Commits

Brodie Rao committed be41cfb

Reorganized code into separate files, changed swizzle on load to only go through if methods for both classes are found, removed usage of swizzle error, made #defines for some constants, replaced non-standard \e with \033, made TOGGLE_MOUSE handling more correct, disabled alternate screen scrolling for now (has issues), cleaned up formatting, added more comments

  • Participants
  • Parent commits b4c513b

Comments (0)

Files changed (5)

 CFLAGS=-bundle -framework Cocoa
-OBJECTS=JRSwizzle.m MouseTerm.m
+OBJECTS=JRSwizzle.m MouseTerm.m Terminal.m
 TARGET=MouseTerm.bundle/Contents/MacOS/MouseTerm
 
 all: build
+// Possible mouse modes
+typedef enum
+{
+    NO_MODE = -1,
+    NORMAL_MODE,
+    HILITE_MODE,
+    BUTTON_MODE,
+    ALL_MODE
+} MouseMode;
+
+// Control codes
+
+// Normal control codes
+#define UP_ARROW "\033[A"
+#define DOWN_ARROW "\033[B"
+// Control codes for application keypad mode
+#define UP_ARROW_APP "\033OA"
+#define DOWN_ARROW_APP "\033OB"
+#define ARROW_LEN (sizeof(UP_ARROW) - 1)
+
+#define TOGGLE_MOUSE "\033[?100" // Excludes mode and toggle flag
+#define TOGGLE_MOUSE_LEN (sizeof(TOGGLE_MOUSE) - 1)
+
+// X11 mouse button values
+typedef enum
+{
+    MOUSE_BUTTON1 = 0,
+    MOUSE_BUTTON2,
+    MOUSE_BUTTON3,
+    MOUSE_RELEASE,
+    MOUSE_WHEEL_UP,
+    MOUSE_WHEEL_DOWN
+} MouseButton;
+
+// X11 mouse reporting responses
+#define MOUSE_RESPONSE "\033[M%c%c%c"
+#define MOUSE_RESPONSE_LEN (sizeof(MOUSE_RESPONSE) - 1)
+
+// Returns a control code for a mouse movement (from iTerm)
+inline NSData* mousePress(MouseButton button, unsigned int modflag, int x, int y)
+{
+    char buf[MOUSE_RESPONSE_LEN];
+    char cb;
+
+    cb = button % 3;
+    if (button > MOUSE_RELEASE) // Wheel movement
+        cb += 64;
+    if (modflag & NSControlKeyMask)
+        cb += 16;
+    if (modflag & NSShiftKeyMask)
+        cb += 4;
+    if (modflag & NSAlternateKeyMask) // Alt/option
+        cb += 8;
+
+    snprintf(buf, MOUSE_RESPONSE_LEN, MOUSE_RESPONSE, 32 + cb, 32 + x + 1,
+             32 + y + 1);
+    return [NSData dataWithBytes: buf length: MOUSE_RESPONSE_LEN];
+}
+#import <Cocoa/Cocoa.h>
+
+// Classes from Terminal.app being overridden
+
+typedef struct
+{
+    unsigned int y;
+    unsigned int x;
+} Position;
+
+@interface TTView: NSScrollView
+- (Position) displayPositionForPoint: (NSPoint) point;
+@end
+
+@interface TTTabController: NSObject
+@end
+
+// Custom instance variables
+extern NSMutableDictionary* MouseTerm_ivars = nil;
 
 #import "JRSwizzle.h"
 
-// Classes we're overriding
-
-typedef struct
-{
-    unsigned int y;
-    unsigned int x;
-} Position;
-
-@interface TTView: NSScrollView
-- (Position) displayPositionForPoint: (NSPoint) point;
-@end
-
+// Dummy implementations to fix 64-bit linking
 @implementation TTView
 - (Position) displayPositionForPoint: (NSPoint) point {}
 @end
 
-@interface TTTabController: NSObject
-@end
-
 @implementation TTTabController
 @end
 
-// For custom instance variables
-static NSMutableDictionary* MouseTerm_ivars = nil;
-
-#define IVAR(obj, name) \
-    [[MouseTerm_ivars objectForKey: [NSValue valueWithPointer: obj]] \
-                      objectForKey: name]
-#define SET_IVAR(obj, name, value) \
-    [[MouseTerm_ivars objectForKey: [NSValue valueWithPointer: obj]] \
-                      setObject: value forKey: name]
-
-// Possible mouse modes
-typedef enum
-{
-    NO_MODE = -1,
-    NORMAL_MODE,
-    HILITE_MODE,
-    BUTTON_MODE,
-    ALL_MODE
-} MouseMode;
-
-// Returns a control code for a mouse movement (from iTerm)
-NSData* mousePress(int button, unsigned int modflag, int x, int y)
-{
-    static char buf[7];
-    char cb;
-
-    cb = button % 3;
-    if (button > 3)
-        cb += 64;
-    if (modflag & NSControlKeyMask)
-        cb += 16;
-    if (modflag & NSShiftKeyMask)
-        cb += 4;
-    if (modflag & NSAlternateKeyMask)
-        cb += 8;
-
-    snprintf(buf, sizeof(buf), "\033[M%c%c%c", 32 + cb, 32 + x + 1,
-             32 + y + 1);
-    return [NSData dataWithBytes: buf length: sizeof(buf) - 1];
-}
-
-@implementation TTTabController (MouseTermTTTabController)
-
-// We intercept all shell output to look for mouse reporting control codes
-- (void) MouseTerm_shellDidReceiveData: (NSData*) data
-{
-    // FIXME: What if the data's split up over method calls?
-    char* pos;
-    if (pos = strnstr([data bytes], "\e[?100", [data length]))
-    {
-        // FIXME: Possible pointer arithmetic issues? Generates warnings...
-
-        // Is there enough data in the buffer for the next two characters?
-        if ([data length] >= (pos + sizeof("\e]?100") - 1 -
-                              (const char*) [data bytes]) + 2)
-        {
-            char mode = pos[6];
-            char flag = pos[7];
-            MouseMode mouseMode = NO_MODE;
-
-            if (flag == 'h')
-            {
-                switch (mode)
-                {
-                case '0':
-                    mouseMode = NORMAL_MODE;
-                    break;
-                case '2':
-                    mouseMode = BUTTON_MODE;
-                    break;
-                case '3': 
-                    mouseMode = ALL_MODE;
-                    break;
-                }
-                
-                if (mouseMode != NO_MODE)
-                    SET_IVAR([self view], @"mouseMode",
-                             [NSNumber numberWithInt: mouseMode]);
-            }
-            // FIXME: Should it turn off reporting for any mode?
-            else if (flag == 'l')
-                SET_IVAR([self view], @"mouseMode",
-                         [NSNumber numberWithInt: NO_MODE]);
-        }
-    }
-
-    [self MouseTerm_shellDidReceiveData: data];
-}
-
-@end
-
-@implementation TTView (MouseTermTTView)
-
-// FIXME: These need to be implemented!
-#if 0
-- (void) MouseTerm_mouseDown: (NSEvent*) event
-{
-    return [self MouseTerm_mouseDown: event];
-}
-
-- (void) MouseTerm_mouseUp: (NSEvent*) event
-{
-    return [self MouseTerm_mouseUp: event];
-}
-
-- (void) MouseTerm_mouseDragged: (NSEvent*) event
-{
-    return [self MouseTerm_mouseDragged: event];
-}
-
-- (void) MouseTerm_mouseEntered: (NSEvent*) event
-{
-    return [self MouseTerm_mouseEntered: event];
-}
-
-- (void) MouseTerm_otherMouseDown: (NSEvent*) event
-{
-    return [self MouseTerm_otherMouseDown: event];
-}
-
-- (void) MouseTerm_otherMouseUp: (NSEvent*) event
-{
-    return [self MouseTerm_otherMouseUp: event];
-}
-
-- (void) MouseTerm_otherMouseDragged: (NSEvent*) event
-{
-    return [self MouseTerm_otherMouseDragged: event];
-}
-#endif
-
-// Intercepts all scroll wheel movements (one wheel "tick" at a time)
-- (void) MouseTerm_scrollWheel: (NSEvent*) event
-{
-    NSPoint windowloc = [event locationInWindow];
-    NSPoint viewloc = [self convertPoint: windowloc fromView: nil];
-    NSRect visible = [[self enclosingScrollView] documentVisibleRect];
-    NSUInteger modflags = [event modifierFlags];
-
-    // FIXME: Is this necessary?
-    if (viewloc.y <= visible.origin.y || modflags & NSAlternateKeyMask)
-        goto ignored;
-
-    NSObject* logicalScreen = [self logicalScreen];
-
-    switch ([(NSNumber*) IVAR(self, @"mouseMode") intValue])
-    {
-        case NO_MODE: {
-            // FIXME: This screws up screen. Should probably take a closer
-            // look at http://bugzilla.gnome.org/show_bug.cgi?id=424184
-            if ((BOOL) [logicalScreen isAlternateScreenActive])
-            {
-                // FIXME: Need some way to account for scrolling acceleration
-                NSData* data = [NSData dataWithBytes: ([event deltaY] > 0 ?
-                                "\eOA\eOA\eOA" : "\eOB\eOB\eOB") length: 9];
-                [(NSObject*) [[self controller] shell] writeData: data];
-                return;
-            }
-            else
-                goto ignored;
-        }
-        case NORMAL_MODE:
-        case BUTTON_MODE:
-        case ALL_MODE: {
-            Position pos = [self displayPositionForPoint: viewloc];
-
-        [(NSObject*) [[self controller] shell] writeData: mousePress(
-                ([event deltaY] > 0 ? 5 : 4),
-                [event modifierFlags], pos.x, pos.y)];
-        }
-    }
-
-handled:
-    return;
-ignored:
-    [self MouseTerm_scrollWheel: event];
-}
-
-// Initializes instance variables
-- (TTView*) MouseTerm_initWithFrame: (NSRect) frame
-{
-    [MouseTerm_ivars setObject: [NSMutableDictionary dictionary]
-                     forKey: [NSValue valueWithPointer: self]];
-    SET_IVAR(self, @"mouseMode", [NSNumber numberWithInt: NO_MODE]);
-    return [self MouseTerm_initWithFrame: frame];
-}
-
-// Deletes instance variables
-- (void) MouseTerm_dealloc
-{
-    [MouseTerm_ivars removeObjectForKey: [NSValue valueWithPointer: self]];
-
-    [self MouseTerm_dealloc];
-}
-
-@end
-
-#undef RESPONSE
-#undef IVAR
-#undef SET_IVAR
+NSMutableDictionary* MouseTerm_ivars = nil;
 
 @interface MouseTerm: NSObject
 @end
 
 @implementation MouseTerm
 
-// FIXME: What happens when only some methods are swizzled?
+// FIXME: Revert swizzled methods when a swizzle fails
 + (void) load
 {
-    Class cls = NSClassFromString(@"TTTabController");
-    if (!cls)
+    Class cl1 = NSClassFromString(@"TTTabController");
+    if (!cl1)
     {
         NSLog(@"[MouseTerm] ERROR: Got nil Class for TTTabController");
         return;
     }
-    if (!class_getInstanceMethod(cls, @selector(shellDidReceiveData:)))
+    if (!class_getInstanceMethod(cl1, @selector(shellDidReceiveData:)))
     {
         NSLog(@"[MouseTerm] ERROR: Got nil Method for [TTTabController "
                "shellDidReceiveData:]");
         return;
     }
 
-    NSError* error = nil;
-
-    if (![cls jr_swizzleMethod: @selector(shellDidReceiveData:)
-              withMethod: @selector(MouseTerm_shellDidReceiveData:)
-              error: &error])
-    {
-        NSLog(@"[MouseTerm] ERROR: Failed to swizzle "
-               "[TTTabController shellDidReceiveData:]: %@", error);
-        return;
-    }
-
-    cls = NSClassFromString(@"TTView");
-    if (!cls)
+    Class cl2 = NSClassFromString(@"TTView");
+    if (!cl2)
     {
         NSLog(@"[MouseTerm] ERROR: Got nil Class for TTView");
         return;
     }
-    if (!class_getInstanceMethod(cls, @selector(initWithFrame:)))
+    if (!class_getInstanceMethod(cl2, @selector(initWithFrame:)))
     {
         NSLog(@"[MouseTerm] ERROR: Got nil Method for [TTView "
                "initWithFrame:]");
         return;
     }
-    if (!class_getInstanceMethod(cls, @selector(dealloc)))
+    if (!class_getInstanceMethod(cl2, @selector(dealloc)))
     {
         NSLog(@"[MouseTerm] ERROR: Got nil Method for [TTView dealloc]");
         return;
     }
-    if (!class_getInstanceMethod(cls, @selector(scrollWheel:)))
+    if (!class_getInstanceMethod(cl2, @selector(scrollWheel:)))
     {
         NSLog(@"[MouseTerm] ERROR: Got nil Method for [TTView scrollWheel:]");
         return;
     }
 
-    error = nil;
+    // Initialize instance vars before any swizzling so nothing bad happens
+    // if some methods are swizzled but not others.
+    MouseTerm_ivars = [[NSMutableDictionary alloc] init];
 
-    if (![cls jr_swizzleMethod: @selector(initWithFrame:)
+    if (![cl2 jr_swizzleMethod: @selector(initWithFrame:)
               withMethod: @selector(MouseTerm_initWithFrame:)
-              error: &error])
+              error: nil])
     {
         NSLog(@"[MouseTerm] ERROR: Failed to swizzle [TTView "
-               "initWithFrame:]: %@", error);
+               "initWithFrame:]");
         return;
     }
 
-    if (![cls jr_swizzleMethod: @selector(dealloc)
+    if (![cl2 jr_swizzleMethod: @selector(dealloc)
               withMethod: @selector(MouseTerm_dealloc)
-              error: &error])
+              error: nil])
     {
-        NSLog(@"[MouseTerm] ERROR: Failed to swizzle [TTView dealloc]: %@",
-              error);
+        NSLog(@"[MouseTerm] ERROR: Failed to swizzle [TTView dealloc]");
         return;
     }
 
-    if (![cls jr_swizzleMethod: @selector(scrollWheel:)
+    if (![cl2 jr_swizzleMethod: @selector(scrollWheel:)
               withMethod: @selector(MouseTerm_scrollWheel:)
-              error: &error])
+              error: nil])
     {
-        NSLog(@"[MouseTerm] ERROR: Failed to swizzle "
-               "[TTView scrollWheel:]: %@", error);
+        NSLog(@"[MouseTerm] ERROR: Failed to swizzle [TTView scrollWheel:]");
         return;
     }
 
-    MouseTerm_ivars = [[NSMutableDictionary alloc] init];
+    if (![cl1 jr_swizzleMethod: @selector(shellDidReceiveData:)
+              withMethod: @selector(MouseTerm_shellDidReceiveData:)
+              error: nill])
+    {
+        NSLog(@"[MouseTerm] ERROR: Failed to swizzle "
+               "[TTTabController shellDidReceiveData:]");
+        return;
+    }
 }
 
 // Deletes instance variables dictionary
+#import <Cocoa/Cocoa.h>
+
+#import "MouseTerm.h"
+#import "Mouse.h"
+
+#define IVAR(obj, name) \
+    [[MouseTerm_ivars objectForKey: [NSValue valueWithPointer: obj]] \
+                      objectForKey: name]
+#define SET_IVAR(obj, name, value) \
+    [[MouseTerm_ivars objectForKey: [NSValue valueWithPointer: obj]] \
+                      setObject: value forKey: name]
+
+@implementation TTTabController (MouseTermTTTabController)
+
+// Intercepts all shell output to look for mouse reporting control codes
+- (void) MouseTerm_shellDidReceiveData: (NSData*) data
+{
+    // FIXME: What if the data's split up over method calls?
+    NSUInteger length = [data length];
+    const char* chars = [data bytes];
+    const char* pos;
+
+    if (pos = strnstr(chars, TOGGLE_MOUSE, length))
+    {
+        // Is there enough data in the buffer for the next two characters?
+        if ([data length] >= (NSUInteger) &pos[TOGGLE_MOUSE_LEN] - chars + 2)
+        {
+            char mode = pos[TOGGLE_MOUSE_LEN];
+            char flag = pos[TOGGLE_MOUSE_LEN + 1];
+            MouseMode mouseMode = NO_MODE;
+
+            switch (mode)
+            {
+            case '0':
+                mouseMode = NORMAL_MODE;
+                break;
+            case '1':
+                mouseMode = HILITE_MODE;
+                break;
+            case '2':
+                mouseMode = BUTTON_MODE;
+                break;
+            case '3': 
+                mouseMode = ALL_MODE;
+                break;
+            }
+            
+            if (mouseMode != NO_MODE)
+            {
+                if (flag == 'h')
+                    SET_IVAR([self view], @"mouseMode",
+                             [NSNumber numberWithInt: mouseMode]);
+                else if (flag == 'l')
+                    SET_IVAR([self view], @"mouseMode",
+                             [NSNumber numberWithInt: NO_MODE]);
+            }
+        }
+    }
+
+    [self MouseTerm_shellDidReceiveData: data];
+}
+
+@end
+
+@implementation TTView (MouseTermTTView)
+
+// FIXME: These need to be implemented!
+#if 0
+- (void) MouseTerm_mouseDown: (NSEvent*) event
+{
+    return [self MouseTerm_mouseDown: event];
+}
+
+- (void) MouseTerm_mouseUp: (NSEvent*) event
+{
+    return [self MouseTerm_mouseUp: event];
+}
+
+- (void) MouseTerm_mouseDragged: (NSEvent*) event
+{
+    return [self MouseTerm_mouseDragged: event];
+}
+
+- (void) MouseTerm_mouseEntered: (NSEvent*) event
+{
+    return [self MouseTerm_mouseEntered: event];
+}
+
+- (void) MouseTerm_otherMouseDown: (NSEvent*) event
+{
+    return [self MouseTerm_otherMouseDown: event];
+}
+
+- (void) MouseTerm_otherMouseUp: (NSEvent*) event
+{
+    return [self MouseTerm_otherMouseUp: event];
+}
+
+- (void) MouseTerm_otherMouseDragged: (NSEvent*) event
+{
+    return [self MouseTerm_otherMouseDragged: event];
+}
+#endif
+
+// Intercepts all scroll wheel movements (one wheel "tick" at a time)
+- (void) MouseTerm_scrollWheel: (NSEvent*) event
+{
+    NSPoint windowloc = [event locationInWindow];
+    NSPoint viewloc = [self convertPoint: windowloc fromView: nil];
+    NSRect visible = [[self enclosingScrollView] documentVisibleRect];
+    NSUInteger modflags = [event modifierFlags];
+
+    // FIXME: Is comparing viewloc.y and visible.origin.y necessary?
+    if (viewloc.y <= visible.origin.y || modflags & NSAlternateKeyMask)
+        goto ignored;
+
+    NSObject* logicalScreen = [self logicalScreen];
+
+    switch ([(NSNumber*) IVAR(self, @"mouseMode") intValue])
+    {
+        case NO_MODE:
+        {
+            // FIXME: This screws up screen. Should probably take a closer
+            // look at http://bugzilla.gnome.org/show_bug.cgi?id=424184
+            //
+            // Maybe it should only be used in application keypad mode?
+#if 0
+            if ((BOOL) [logicalScreen isAlternateScreenActive])
+            {
+                // FIXME: Need some way to account for scrolling acceleration
+                const char* data;
+                if (is application mode ...)
+                    data = [event deltaY] > 0 ? UP_ARROW_APP UP_ARROW_APP :
+                           DOWN_ARROW_APP DOWN_ARROW_APP;
+                else
+                    data = [event deltaY] > 0 ? UP_ARROW UP_ARROW :
+                           DOWN_ARROW DOWN_ARROW;
+                NSData* data = [NSData dataWithBytes: data
+                                       length: ARROW_LEN * 2];
+                [(NSObject*) [[self controller] shell] writeData: data];
+                goto handled;
+            }
+            else
+                goto ignored;
+#endif
+            goto ignored;
+        }
+        // FIXME: Unhandled at the moment
+        case HILITE_MODE:
+            goto ignored;
+        case NORMAL_MODE:
+        case BUTTON_MODE:
+        case ALL_MODE:
+        {
+            int button = [event deltaY] > 0 ? MOUSE_WHEEL_UP : MOUSE_WHEEL_DOWN;
+            Position pos = [self displayPositionForPoint: viewloc];
+            [(NSObject*) [[self controller] shell] writeData: mousePress(
+                                button, [event modifierFlags],
+                                pos.x, pos.y)];
+            goto handled;
+        }
+    }
+
+handled:
+    return;
+ignored:
+    [self MouseTerm_scrollWheel: event];
+}
+
+// Initializes instance variables
+- (TTView*) MouseTerm_initWithFrame: (NSRect) frame
+{
+    [MouseTerm_ivars setObject: [NSMutableDictionary dictionary]
+                     forKey: [NSValue valueWithPointer: self]];
+    SET_IVAR(self, @"mouseMode", [NSNumber numberWithInt: NO_MODE]);
+    return [self MouseTerm_initWithFrame: frame];
+}
+
+// Deletes instance variables
+- (void) MouseTerm_dealloc
+{
+    [MouseTerm_ivars removeObjectForKey: [NSValue valueWithPointer: self]];
+
+    [self MouseTerm_dealloc];
+}
+
+@end