From: http://www.ideawu.com/blog/2013/04/how-to-capture-esc-key-in-a-cocoa-application.html
http://forums.macrumors.com/showthread.php?t=780577
You may want to capture(or intercept, detect) the ESC key press in a Cocoa application, capture the ESC key down in Application scope for the whole app, or just in Window scope for some certain windows.
The NSApplicationDelegate is not a subclass of NSResponder, so you can not implement the keyDown: method int app delegate. The NSWindowController is a subclass of NSResponder, you may implement keyDown: method in it, but it will not get invoked when user press the ESC key(with exceptions) and not when user is typing in a text input.
The solution is to use a Event Monitor.
Here’s the sample codes:
1 @interface MyController : NSWindowController{
2 id eventMonitor;
3 }
4 @end
5
6 @implementation MyController
7 - (void)windowDidLoad{
8 NSEvent* (^handler)(NSEvent*) = ^(NSEvent *theEvent) {
9 NSWindow *targetWindow = theEven.window;
10 if (targetWindow != self.window) {
11 return theEvent;
12 }
13
14 NSEvent *result = theEvent;
15 NSLog(@"event monitor: %@", theEvent);
16 if (theEven.keyCode == 53) {
17 [self myProcessEscKeyDown];
18 result = nil;
19 }
20
21 return result;
22 };
23 eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:handler];
24 }
25
26 - (void)windowWillClose:(NSNotification *)notification{
27 [NSEvent removeMonitor:eventMonitor];
28 }
@end
All the stuff is down in a NSWindowController subclass, if you want to implement a key capture in application scope, put these codes in the application delegate, replace windowDidLoad: withapplicationDidFinishLaunching:, windowWillClose: with applicationWillTerminate:.
7 - (void)windowDidLoad{
Register a keyboard event handler just as window is loaded.
10 if (targetWindow != self.window) { 11 return theEvent; 12 }
Let events in other windows pass, we only handle events of the window this controller managed.
16 if (theEven.keyCode == 53) { 17 [self myProcessEscKeyDown: theEvent]; 18 result = nil; 19 }
Detect ESC key down event, process it, and drop the event(return nil) so the responder chain will never know.
23 eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:handler];
Besides NSKeyDown, there are NSKeyUp, NSLeftMouseUp, NSLeftMouseDown, etc. But we only monitor key down event in this example, so set the mask to NSKeyDownMask.
26 - (void)windowWillClose:(NSNotification *)notification{ 27 [NSEvent removeMonitor:eventMonitor]; 28 }
Deattach the event monitor when window close.
Discussions on other approaches
You can achieve it by implementing the sendEvent: of a NSWindow subclass, and you need to use this NSWindow subclass to build up UI in Interface Builder, not the default NSWindow class. I don’t recommend you to do in this way, because it will add two extra files in you project.
A NSWindowController’s keyDown: will get a ESC press event if the first responder is a WebView, I don’t know what have the WebView done to hand over the event to the controller even there are still other views in the view hierachy. Please let me know if you know the reason.
Cocoa Event Architecture and Responder Chain
NSEvent keyCode list
/*
* Summary:
* Virtual keycodes
*
* Discussion:
* These constants are the virtual keycodes defined originally in
* Inside Mac Volume V, pg. V-191. They identify physical keys on a
* keyboard. Those constants with "ANSI" in the name are labeled
* according to the key position on an ANSI-standard US keyboard.
* For example, kVK_ANSI_A indicates the virtual keycode for the key
* with the letter 'A' in the US keyboard layout. Other keyboard
* layouts may have the 'A' key label on a different physical key;
* in this case, pressing 'A' will generate a different virtual
* keycode.
*/
enum {
kVK_ANSI_A = 0x00,
kVK_ANSI_S = 0x01,
kVK_ANSI_D = 0x02,
kVK_ANSI_F = 0x03,
kVK_ANSI_H = 0x04,
kVK_ANSI_G = 0x05,
kVK_ANSI_Z = 0x06,
kVK_ANSI_X = 0x07,
kVK_ANSI_C = 0x08,
kVK_ANSI_V = 0x09,
kVK_ANSI_B = 0x0B,
kVK_ANSI_Q = 0x0C,
kVK_ANSI_W = 0x0D,
kVK_ANSI_E = 0x0E,
kVK_ANSI_R = 0x0F,
kVK_ANSI_Y = 0x10,
kVK_ANSI_T = 0x11,
kVK_ANSI_1 = 0x12,
kVK_ANSI_2 = 0x13,
kVK_ANSI_3 = 0x14,
kVK_ANSI_4 = 0x15,
kVK_ANSI_6 = 0x16,
kVK_ANSI_5 = 0x17,
kVK_ANSI_Equal = 0x18,
kVK_ANSI_9 = 0x19,
kVK_ANSI_7 = 0x1A,
kVK_ANSI_Minus = 0x1B,
kVK_ANSI_8 = 0x1C,
kVK_ANSI_0 = 0x1D,
kVK_ANSI_RightBracket = 0x1E,
kVK_ANSI_O = 0x1F,
kVK_ANSI_U = 0x20,
kVK_ANSI_LeftBracket = 0x21,
kVK_ANSI_I = 0x22,
kVK_ANSI_P = 0x23,
kVK_ANSI_L = 0x25,
kVK_ANSI_J = 0x26,
kVK_ANSI_Quote = 0x27,
kVK_ANSI_K = 0x28,
kVK_ANSI_Semicolon = 0x29,
kVK_ANSI_Backslash = 0x2A,
kVK_ANSI_Comma = 0x2B,
kVK_ANSI_Slash = 0x2C,
kVK_ANSI_N = 0x2D,
kVK_ANSI_M = 0x2E,
kVK_ANSI_Period = 0x2F,
kVK_ANSI_Grave = 0x32,
kVK_ANSI_KeypadDecimal = 0x41,
kVK_ANSI_KeypadMultiply = 0x43,
kVK_ANSI_KeypadPlus = 0x45,
kVK_ANSI_KeypadClear = 0x47,
kVK_ANSI_KeypadDivide = 0x4B,
kVK_ANSI_KeypadEnter = 0x4C,
kVK_ANSI_KeypadMinus = 0x4E,
kVK_ANSI_KeypadEquals = 0x51,
kVK_ANSI_Keypad0 = 0x52,
kVK_ANSI_Keypad1 = 0x53,
kVK_ANSI_Keypad2 = 0x54,
kVK_ANSI_Keypad3 = 0x55,
kVK_ANSI_Keypad4 = 0x56,
kVK_ANSI_Keypad5 = 0x57,
kVK_ANSI_Keypad6 = 0x58,
kVK_ANSI_Keypad7 = 0x59,
kVK_ANSI_Keypad8 = 0x5B,
kVK_ANSI_Keypad9 = 0x5C
};
/* keycodes for keys that are independent of keyboard layout*/
enum {
kVK_Return = 0x24,
kVK_Tab = 0x30,
kVK_Space = 0x31,
kVK_Delete = 0x33,
kVK_Escape = 0x35,
kVK_Command = 0x37,
kVK_Shift = 0x38,
kVK_CapsLock = 0x39,
kVK_Option = 0x3A,
kVK_Control = 0x3B,
kVK_RightShift = 0x3C,
kVK_RightOption = 0x3D,
kVK_RightControl = 0x3E,
kVK_Function = 0x3F,
kVK_F17 = 0x40,
kVK_VolumeUp = 0x48,
kVK_VolumeDown = 0x49,
kVK_Mute = 0x4A,
kVK_F18 = 0x4F,
kVK_F19 = 0x50,
kVK_F20 = 0x5A,
kVK_F5 = 0x60,
kVK_F6 = 0x61,
kVK_F7 = 0x62,
kVK_F3 = 0x63,
kVK_F8 = 0x64,
kVK_F9 = 0x65,
kVK_F11 = 0x67,
kVK_F13 = 0x69,
kVK_F16 = 0x6A,
kVK_F14 = 0x6B,
kVK_F10 = 0x6D,
kVK_F12 = 0x6F,
kVK_F15 = 0x71,
kVK_Help = 0x72,
kVK_Home = 0x73,
kVK_PageUp = 0x74,
kVK_ForwardDelete = 0x75,
kVK_F4 = 0x76,
kVK_End = 0x77,
kVK_F2 = 0x78,
kVK_PageDown = 0x79,
kVK_F1 = 0x7A,
kVK_LeftArrow = 0x7B,
kVK_RightArrow = 0x7C,
kVK_DownArrow = 0x7D,
kVK_UpArrow = 0x7E
};
/* ISO keyboards only*/
enum {
kVK_ISO_Section = 0x0A
};
/* JIS keyboards only*/
enum {
kVK_JIS_Yen = 0x5D,
kVK_JIS_Underscore = 0x5E,
kVK_JIS_KeypadComma = 0x5F,
kVK_JIS_Eisu = 0x66,
kVK_JIS_Kana = 0x68
};