diff --git a/src/MacVim/MMAppController.m b/src/MacVim/MMAppController.m index f242b61042..6f15abafb7 100644 --- a/src/MacVim/MMAppController.m +++ b/src/MacVim/MMAppController.m @@ -185,6 +185,10 @@ fsEventCallback(ConstFSEventStreamRef streamRef, [NSNumber numberWithBool:NO], MMVerticalSplitKey, [NSNumber numberWithInt:0], MMPreloadCacheSizeKey, [NSNumber numberWithInt:0], MMLastWindowClosedBehaviorKey, + [NSNumber numberWithInt:MMDisableFakeEsc], + MMFakeEscModifierKey, + [NSNumber numberWithFloat:0.3], MMFakeEscTimeoutKey, + [NSNumber numberWithBool:NO], MMFakeEscOnKeyDownKey, nil]; [[NSUserDefaults standardUserDefaults] registerDefaults:dict]; diff --git a/src/MacVim/MMApplication.h b/src/MacVim/MMApplication.h index 28937e9745..a03c2c655c 100644 --- a/src/MacVim/MMApplication.h +++ b/src/MacVim/MMApplication.h @@ -12,6 +12,13 @@ @interface MMApplication : NSApplication { + CFAbsoluteTime fakeEscTimeDown; + CFAbsoluteTime fakeEscTimeout; + int fakeEscKeyCode; + unsigned fakeEscModifierMask; + BOOL blockFakeEscEvent; + BOOL blockKeyDown; + BOOL fakeEscOnKeyDown; } @end diff --git a/src/MacVim/MMApplication.m b/src/MacVim/MMApplication.m index 10c69d9d62..55bcae0175 100644 --- a/src/MacVim/MMApplication.m +++ b/src/MacVim/MMApplication.m @@ -14,6 +14,7 @@ */ #import "MMApplication.h" +#import "Miscellaneous.h" // Ctrl-Tab is broken on pre 10.5, so we add a hack to make it work. #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 @@ -26,11 +27,107 @@ @implementation MMApplication +- (void)awakeFromNib +{ + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + switch ([ud integerForKey:MMFakeEscModifierKey]) { + case MMCtrlFakeEsc: + fakeEscKeyCode = 59; + fakeEscModifierMask = NSControlKeyMask; + break; + case MMAltFakeEsc: + fakeEscKeyCode = 58; + fakeEscModifierMask = NSAlternateKeyMask; + break; + case MMCmdFakeEsc: + fakeEscKeyCode = 55; + fakeEscModifierMask = NSCommandKeyMask; + break; + default: + fakeEscKeyCode = fakeEscModifierMask = 0; + } + + fakeEscTimeout = [ud floatForKey:MMFakeEscTimeoutKey]; + fakeEscOnKeyDown = [ud boolForKey:MMFakeEscOnKeyDownKey]; +} + - (void)sendEvent:(NSEvent *)event { NSEventType type = [event type]; unsigned flags = [event modifierFlags]; + // The following hack allows the user to set one modifier key of choice + // (Ctrl, Alt, or Cmd) to generate an Esc key press event. In order for + // the key to still be used as a modifier we only send the "faked" Esc + // event if the modifier was pressed and released without any other keys + // being pressed in between. The user may elect to have the chosen + // modifier sending Esc on key down, since sending it on key up makes it + // appear a bit sluggish. However, this effectively disables the modifier + // key (but only the left key and not the right one, in case there are two + // on the keyboard). + // + // This hack is particularly useful in conjunction with Mac OS X's ability + // to turn Caps-Lock into a modifier key of choice because it enables us to + // turn Caps-Lock into a quasi-Esc key! (This remapping be done inside + // "System Preferences -> Keyboard & Mouse -> Modifier Keys...".) + // + if (fakeEscKeyCode != 0) { + if (NSFlagsChanged == type && [event keyCode] == fakeEscKeyCode) { + BOOL sendEsc = NO; + CFAbsoluteTime timeNow = CFAbsoluteTimeGetCurrent(); + + if ((flags & fakeEscModifierMask) == 0) { + // The chosen modifier was released. If the modifier was + // recently pressed then convert this event to a "fake" Esc key + // press event. + if (!blockFakeEscEvent && !fakeEscOnKeyDown && + timeNow - fakeEscTimeDown < fakeEscTimeout) + sendEsc = YES; + + blockFakeEscEvent = YES; + blockKeyDown = NO; + } else { + // The chosen modifier was pressed. + blockFakeEscEvent = NO; + fakeEscTimeDown = timeNow; + + if (fakeEscOnKeyDown) { + sendEsc = YES; + + // Block key down while the fake Esc modifier key is held, + // otherwise "marked text" may pop up if a key is pressed + // while the fake Esc modifier is held (which looks ugly, + // but is harmless). + blockKeyDown = YES; + } + } + + if (sendEsc) { + NSEvent *e = [NSEvent keyEventWithType:NSKeyDown + location:[event locationInWindow] + modifierFlags:flags & 0x0000ffffU + timestamp:[event timestamp] + windowNumber:[event windowNumber] + context:[event context] + characters:@"\x1b" // Esc + charactersIgnoringModifiers:@"\x1b" + isARepeat:NO + keyCode:53]; + + [self postEvent:e atStart:YES]; + return; + } + } else if (type != NSKeyUp) { + // Another event occurred, so don't send any fake Esc events now + // (else the modifier would not function as a modifier key any + // more). + blockFakeEscEvent = YES; + } + + if (blockKeyDown && type == NSKeyDown) + return; + } + #ifdef MM_CTRL_TAB_HACK NSResponder *firstResponder = [[self keyWindow] firstResponder]; @@ -51,7 +148,7 @@ // key event. if ((NSKeyDown == type || NSKeyUp == type) && (flags & NSHelpKeyMask)) { flags &= ~NSHelpKeyMask; - event = [NSEvent keyEventWithType:[event type] + NSEvent *e = [NSEvent keyEventWithType:[event type] location:[event locationInWindow] modifierFlags:flags timestamp:[event timestamp] @@ -61,6 +158,9 @@ charactersIgnoringModifiers:[event charactersIgnoringModifiers] isARepeat:[event isARepeat] keyCode:[event keyCode]]; + + [self postEvent:e atStart:YES]; + return; } [super sendEvent:event]; diff --git a/src/MacVim/Miscellaneous.h b/src/MacVim/Miscellaneous.h index 23c3ebe206..ce4a02b55f 100644 --- a/src/MacVim/Miscellaneous.h +++ b/src/MacVim/Miscellaneous.h @@ -53,6 +53,9 @@ extern NSString *MMOpenLayoutKey; extern NSString *MMVerticalSplitKey; extern NSString *MMPreloadCacheSizeKey; extern NSString *MMLastWindowClosedBehaviorKey; +extern NSString *MMFakeEscModifierKey; +extern NSString *MMFakeEscTimeoutKey; +extern NSString *MMFakeEscOnKeyDownKey; // Enum for MMUntitledWindowKey @@ -79,6 +82,14 @@ enum { MMTerminateWhenLastWindowClosed = 2, }; +// Enum for MMFakeEscModifierKey +enum { + MMDisableFakeEsc = 0, + MMCtrlFakeEsc = 1, + MMAltFakeEsc = 2, + MMCmdFakeEsc = 3 +}; + diff --git a/src/MacVim/Miscellaneous.m b/src/MacVim/Miscellaneous.m index 15259d48d7..f36050357a 100644 --- a/src/MacVim/Miscellaneous.m +++ b/src/MacVim/Miscellaneous.m @@ -44,6 +44,9 @@ NSString *MMOpenLayoutKey = @"MMOpenLayout"; NSString *MMVerticalSplitKey = @"MMVerticalSplit"; NSString *MMPreloadCacheSizeKey = @"MMPreloadCacheSize"; NSString *MMLastWindowClosedBehaviorKey = @"MMLastWindowClosedBehavior"; +NSString *MMFakeEscModifierKey = @"MMFakeEscModifier"; +NSString *MMFakeEscTimeoutKey = @"MMFakeEscTimeout"; +NSString *MMFakeEscOnKeyDownKey = @"MMFakeEscOnKeyDown";