From b6c06f31be1e8124ed12cc2ff5361752df1e4634 Mon Sep 17 00:00:00 2001 From: Bjorn Winckler Date: Tue, 30 Sep 2008 22:06:30 +0200 Subject: [PATCH] Modifier key sends Esc Adds possibility to make the left Ctrl, Alt, or Cmd key function as a second Esc key. The key will still function as a modifier if held down in conjunction with another key. Enable by setting the user default "MMFakeEscModifier" to: 1 = Left Ctrl 2 = Left Alt 3 = Left Cmd any other number disables this functionality By remapping Caps-Lock to one of the above modifier keys this enables the use of Caps-Lock as a second (and easy to reach) Esc key. Caps-Lock can be remapped inside "System Preferences -> Keyboard & Mouse -> Modifier Keys...". The Esc event is sent when the modifier key is released. If the key is not released within a predefined timeout, then no Esc event is generated. The timeout can be changed by setting the user default MMFakeEscTimeout (a float, specifying the timeout in seconds). The fact that the Esc event is sent on release makes it feel somewhat sluggish. It is possible to have the event sent when the modifier key is pressed by setting the user default "MMFakeEscOnKeyDown" but then the left modifier key can only be used as an Esc key. --- src/MacVim/MMAppController.m | 4 ++ src/MacVim/MMApplication.h | 7 +++ src/MacVim/MMApplication.m | 102 ++++++++++++++++++++++++++++++++++++++++++- src/MacVim/Miscellaneous.h | 11 +++++ src/MacVim/Miscellaneous.m | 3 ++ 5 files changed, 126 insertions(+), 1 deletion(-) diff --git a/src/MacVim/MMAppController.m b/src/MacVim/MMAppController.m index f242b610..6f15abaf 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 28937e97..a03c2c65 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 10c69d9d..55bcae01 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 23c3ebe2..ce4a02b5 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 15259d48..f3605035 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"; -- 2.11.4.GIT