I agree with Williham: Why would you use NSButtons to capture key events? This is pretty unusual. You capture key events by actually "capturing" them. E.g. by subclassing NSView.
Please exclude the code below being very compact, but I don't want to waste more space than necessary:
#import <Cocoa/Cocoa.h>
enum {
upKeyPressed = 1,
leftKeyPressed = 2,
downKeyPressed = 4,
rightKeyPressed = 8
};
@interface MyView : NSView
{
uint32_t pressedKeys_;
}
@end
@implementation MyView
- (void)makeMyWindowKey:(NSTimer *)timer
{
// Force app to foreground
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
// Make window key and display on screen
[[timer userInfo] makeKeyAndOrderFront:self];
}
- (BOOL)acceptsFirstResponder
{
// YES, we want to be first responder
return YES;
}
- (void)printPressedKeys
{
printf("Keys currently pressed: ");
if (!pressedKeys_) {
printf("NONE");
} else {
if (pressedKeys_ & upKeyPressed) {
printf("UP ");
}
if (pressedKeys_ & downKeyPressed) {
printf("DOWN ");
}
if (pressedKeys_ & leftKeyPressed) {
printf("LEFT ");
}
if (pressedKeys_ & rightKeyPressed) {
printf("RIGHT ");
}
}
printf("\n");
}
- (void)keyUp:(NSEvent *)anEvent;
{
BOOL printPressedKeys = YES;
switch ([anEvent keyCode]) {
case 126: // UP
pressedKeys_ &= ~upKeyPressed;
break;
case 123: // LEFT
pressedKeys_ &= ~leftKeyPressed;
break;
case 125: // DOWN
pressedKeys_ &= ~downKeyPressed;
break;
case 124: // RIGHT
pressedKeys_ &= ~rightKeyPressed;
break;
default:
// Ignore this key
printPressedKeys = NO;
}
if (printPressedKeys) {
[self printPressedKeys];
}
}
- (void)keyDown:(NSEvent *)anEvent;
{
BOOL printPressedKeys = YES;
switch ([anEvent keyCode]) {
case 126: // UP
pressedKeys_ |= upKeyPressed;
break;
case 123: // LEFT
pressedKeys_ |= leftKeyPressed;
break;
case 125: // DOWN
pressedKeys_ |= downKeyPressed;
break;
case 124: // RIGHT
pressedKeys_ |= rightKeyPressed;
break;
case 53: // ESC
// QUIT
[[NSApplication sharedApplication] terminate:self];
// Fallthrough
default:
printPressedKeys = NO;
}
if (printPressedKeys) {
[self printPressedKeys];
}
}
@end
int main (
int argc,
char ** argv
) {
// We need a pool, since we won't call NSApplicationMain
NSAutoreleasePool * pool = [NSAutoreleasePool new];
// W/o calling NSApplicationMain, we must do this before drawing UI
[NSApplication sharedApplication];
NSWindow * win = [[NSWindow alloc]
initWithContentRect:NSMakeRect(100, 100, 500, 500)
styleMask:NSTitledWindowMask | NSClosableWindowMask
backing:NSBackingStoreBuffered
defer:YES
];
MyView * mv = [[MyView alloc] init];
[win setContentView:mv];
// Retained by window anyway
[mv release];
// Since we are no real UI application by now and since we have
// no Info.plist, we must programmatically become a UI process,
// which is in fact possible
ProcessSerialNumber myProcess = { 0, kCurrentProcess };
TransformProcessType(
&myProcess,
kProcessTransformToForegroundApplication
);
// The window we just created is our main window
[win becomeMainWindow];
// Our view is the first responder
[win setInitialFirstResponder:[win contentView]];
// We want the window to become key as soon as main runloop is running
[NSTimer
scheduledTimerWithTimeInterval:0.1
target:mv
selector:@selector(makeMyWindowKey:)
userInfo:win
repeats:NO
];
[[NSApplication sharedApplication] run];
// We should never get here
// Remove window from screen and discard
[win orderOut:nil];
[win release];
[pool release];
printf("Quitting\n");
return 0;
}
Store in test.m, compile using
gcc -framework Cocoa -o test test.m
run using
./test
Monitor the output in Terminal while pressing arrow keys, quit by pressing ESC key. As you can see, it will print UP LEFT if both, up arrow and left arrow key are pressed and it will say DOWN RIGHT if down and right arrow key are pressed.