I'm using a custom subclass of NSView and receiving keyboard events through the keyDown/keyUp methods, everything works fine except when the "Cmd ⌘" key is pressed, keyDown events are fired as normal but the keyUp event never comes.
In our case we use the arrow keys on their own to move an image left/right/up/down and if the user holds down "Cmd ⌘" while pressing left/right it will rotate the image instead. Since we get the keyDown event the image starts rotating but it never stops as keyUp never comes. Other modifiers don't have this issue (e.g. if shift, ctrl or alt are held down while another key is pressed we get the keyUp as expected). One option is to use a different modifier but it would be nice to keep it consistent with the PC version (Cmd is used as a substitute for when Ctrl is used on Windows, keeps it consistent with standard copy/paste commands etc).
Does anyone know why it does this? It feels like a bug but is probably just strange "correct behaviour", any ideas how to get around it (other than using an alternative modifier or using the likes of direct HID access).
Thanks.
You can use flagsChanged to be notified when modifier keys are down/up, see: Technical Q&A QA1519
Detecting the Caps Lock Key which is applicable to other modifier keys. See Carbon/Frameworks/HIToolbox/Events.h for more modifier key codes.
- (void)flagsChanged:(NSEvent *)event
{
if (event.keyCode == 0x37) { // kVK_Command
if (event.modifierFlags & NSCommandKeyMask) {
NSLog(#"Command key is down");
} else {
NSLog(#"Command key is up");
}
}
}
Related
I am in the process of creating a 2D tile-based platform game for Mac OSX in swift. I have come across problems with the controls when more than one key is pushed at a time. For example; if 'D' makes the character move to the right, and 'W' makes him jump; if I hold 'D' to make the character run, then tap 'W' to make him jump, he will stop once he lands. The only way for him to continue to move to the right is to retouch 'D' again, which is certainly undesirable. It appears as though when the 'W' key is pressed, it cancels the 'D' keyDown event. If anyone has any suggestions on how I can make it so that multiple keys can be pressed without canceling other similar events I sure would appreciate it.
This is how my KeyDown events looks:
func returnChar(theEvent: NSEvent!) -> Character?{
let s: String = theEvent.characters!
for char in s {
return char}
return nil
}
override func keyDown(theEvent: NSEvent) {
let s: String = String(self.returnChar(theEvent)!)
switch(s){
case "w":
player.jump()
case "s":
// nothing yet
case "d":
player.moveLeftOrRight(CGVector(dx: 1.0, dy: 0.0))
case "a":
player.moveLeftOrRight(CGVector(dx: -1.0, dy: 0.0))
default:
println("default")
}
}
The repeated keydown events that occur when a key is held down are generated by the keyboard hardware. On the PC, the protocol definition says:
If you press a key, its make code is sent to the computer. When you press and hold down a key, that key becomes typematic, which means the keyboard will keep sending that key's make code until the key is released or another key is pressed. To verify this, open a text editor and hold down the "A" key. ... Typematic data is not buffered within the keyboard. In the case where more than one key is held down, only the last key pressed becomes typematic. Typematic repeat then stops when that key is released, even though other keys may be held down.
Since both PCs and Macs can use the same USB keyboards (modulo some key remapping), I'm pretty sure they both use the same protocol.
In the case you describe, the problem occurs because even though you're holding down D, when you hit W, the keyboard stops repeating the D key. The sequence of events is:
Keydown D
Keydown D
...
Keydown W
Keyup W
and at this point the keyboard events stop. Notice that the Keyup D has not occurred, so you actually know that the D key is still down, but now it is up to you to simulate the repeated keypresses.
I suggest you research how to disable autorepeat altogether in the keyboard hardware (or just ignore the key repeat events) and generate the desired game behavior yourself. You could easily do this by tracking the state of each key using Keydown and Keyup events, ignoring repeated Keydown.
Jim Garrison's answer seems correct. A workaround in this case, could be to have the character move right when you press the D key down, and continue to move right using a loop until the D key is released, which you can check using the func keyUp(theEvent: NSEvent)
If using SpritKit, you can do the character movement in the update method while using a bool that is set in the keyDown and keyUp method.
I'm having a problem with OS X detecting keystrokes. I need to detect a key down AND a key release or key up whenever a keyboard key is pressed. This is fairly straight-forward when intercepting the application's event handling chain with [ NSEvent addLocalMonitorForEventsMatchingMask: handler: ]. That lets you intercept and modify NSEvents for a variety of event types, including NSKeyUp and NSKeyDown for the regular printing keys, as well as NSFlagsChanged which can be used to detect the shift, ctrl, alt, and cmd keys. In fact, because the modifier flags change on both the key up and key down for shift, ctrl, alt, and cmd keys, NSFlagsChanged can be used as a key up and key down event for those keys by checking the [NSEvent modifierFlags] along with the [NSEvent keyCode].
Capslock is different, though. Because the capslock modifier really only acts on a key down, when you press capslock you only get a NSFlagsChanged with capslock is pressed, not when it's released. And NSKeyUp and NSKeyDown aren't emitted with modifier keys like capslock and shift and ctrl, etc.
Can anyone suggest a way, maybe even a lower-level interface, for getting capslock key up events? Am I going to have to resort to using kqueues or something?
IOHIDLib seems to be the only way of doing this. I (or a teammate actually) used IOHIDManager to set up an event callback that successfully intercepted capslock key-up and key-down events.
The awful thing about this is that this is completely outside the Cocoa/CoreFoundation event dispatch mechanism, which means that you get events even when your application is out of focus. You end up having to do a lot of window management and focus detection yourself.
But it does work.
i need to know what key code of Command button in mac keyboard, do somebody know how to get it programmatically?
can i get the key code if user tap button Command + X (cut shortcut)? thank you for the suggestion
I'm going to assume here that you're dealing with NSEvents generated by the AppKit framework.
In NSEvent's documentation, take a look at the modifierFlags method.
And the flag you're lookng for is specifically the NSCommandKeyMask.
Now, to get it, if you have a NSView view in focus... it inherits from NSResponder. One of the methods in NSResponder is keyDown. So add a keyDown method to your subclassed view and the parameter it takes is a NSEvent. And that is how you would get your command button key.
BTW & FYI, if you just want to get the command key by itself (which is what I suspect), that's a bigger trick and I'm not sure if it's possible with AppKit. Because the command key, like the control and shift keys, are modifier keys and that means when events get generated, NSEvent is expecting two keys to be pressed at the same time (e.g. Command + Q for Quit, Control + C for interrupt, Shift + A for capital A).
I use onKeyDown in a Custom NSView in detect keydown events, it works well when I input the normal keys, like "a, b, c", but it does not invoke onkeydown function when I press ESC, I want to exit my application when user press ESC.
How to do that?
OK, it turns out my keyboard is broken, only for ESC key, I changed with a keyboard, it works now. Thank you anyway.
I just dropped in a KeyDown handler into a custom NSView in one of my test apps and the ESC key is hitting KeyDown perfectly fine.
Seems like something to do with the way you're calling KeyDown, I think? Maybe the focus isn't set correctly.
In any event, another thing you can do is to implement cancelOperation: in your custom NSView.
Here is the documentation for [NSResponder cancelOperation:]. This also responds to the Escape key and to the Macintosh standard Command + . key combination.
Also check out the Handling Key Events section of the Cocoa Event-Handling Guide, which is where I was looking for answers for you. Hope this helps!
Ola Folks,
Once again I want to drink from the pool of knowledge shared by people using SO.
I have written a small app for OSX that sends key events to an application. I am targeting OSX 10.5.x and newer. However, the problem exists when I build for 10.6.x as well. Everything works fine except when I send only the modifier keys; Alt, Command, Control and Shift.
The problem is that on the two MacBooks, the events for the modifier keys appear to be cleared as soon as the testers move the cursor using the mouse or touch the touchpad.
On a desktop with XCode installed, everything works fine. Just like it should. On two different MacBooks, the problem occurs. The desktop has a standard 101 key keyboard and a multi-button mouse attached.
When a mouse is connected to the MacBooks, a two button mouse with scrollwheel is used. However, the problem exists when no peripherals are attached and the touchpad is used.
What I expect to happen is that the Modifier Key event is sent to the target application, the user moves the cursor using the mouse / touchpad, press buttons on the mouse / touchpad and/or press keys on the keyboard with the modifier key down event 'active'. Then, when they finish, the key up event for the modifier key is sent.
Here is how I am sending key down events, the Shift key for this example:
case ModKeyShiftDown:
xEventSource = CGEventSourceCreate(kCGEventSourceStatePrivate);
xTheCommand = CGEventCreateKeyboardEvent(xEventSource, kVK_Shift, true);
CGEventSetFlags(xTheCommand, kCGEventFlagMaskAlternate);
CGEventPost(kCGHIDEventTap, xTheCommand);
//CGEventPost(kCGSessionEventTap, xTheCommand);
//CGEventPost(kCGAnnotatedSessionEventTap, xTheCommand);
CFRelease(xTheCommand);
CFRelease(xEventSource);
break;
I have used all three flags for creating the event source (kCGEventSourceStatePrivate, kCGEventSourceStateCombinedSessionState and kCGEventSourceStateHIDSystemState).
I have tried Creating the Keyboard Event with the Event Source as well as null as the first parameter.
I have tried with and without the appropriate flags on the event.
I have tried various combinations of posting the event; kCGHIDEventTap, kCGSessionEventTap and kCGAnnotatedSessionEventTap.
For completeness, here is how I send an up event for the Shift key:
case ModKeyShiftUp:
xEventSource = CGEventSourceCreate(kCGEventSourceStatePrivate);
xTheCommand = CGEventCreateKeyboardEvent(xEventSource, kVK_Shift, false);
CGEventSetFlags(xTheCommand, 0);
CGEventPost(kCGHIDEventTap, xTheCommand);
//CGEventPost(kCGSessionEventTap, xTheCommand);
//CGEventPost(kCGAnnotatedSessionEventTap, xTheCommand);
CFRelease(xTheCommand);
CFRelease(xEventSource);
break;
When the testers trigger a modifier key down event, they can see the cursor change as expected. This lets me know the event is being processed by the target application. However, as soon as they touch the mouse or touchpad, the cursor changes back to a standard cursor and the mouse events are processed as if no modifier key events are active.
I would like to know if there is a problem with the way I am sending the events. I would also like to know if there is an alternate way to send Modifier Key events that is Going To Work.
Sorry if I overtalked this. My excuse is that I only slept a couple of hours. :P
Thanx
-isdi-
Ola,
Interestingly enough, using AXUIElementRef and AXUIElementPostKeyboardEvent seem to work.
For whatever reason, sending key events using the Accessibility object and method above solves the problem for the testers.