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.
Related
In an OS X sprite kit project I can implement the keyDown(_:) and keyUp(_:) methods to listen for key events. How do I find which key was pressed (up arrow, A, 2, space, etc). I can use the characters property for alphabetical keys but how do I check for arrow or space keys. Can I use the keyCode property and will this be different/likely to change with keyboard/mac type.
It depends on the game. If you're developing a game where the characters have meaning (for example, a game where a=attack, p=pause, g=throw grenade), then processing the theEvent.characters makes sense. If you use key codes and the user uses a different keyboard layout (e.g., Dvorak, Colmak), the user would be forced to mentally remap the keys to the QWERTY keyboard layout.
Here's an example of how to convert characters in theEvent.characters to a UTF-16 representation. It's useful to detect if special characters, such as the up/down arrows, were pressed.
override func keyDown(theEvent:NSEvent) {
for codeUnit in theEvent.characters!.utf16 {
println ("Keypressed: \(codeUnit)")
}
}
For other types of games where the location of the keys matter (for example W = up, A = left, D = right, S = down), using the hardware-independent key code property makes more sense.
I have a system with no keyboard. I can connect a keyboard, but ultimately the inputs come from a custom keypad which is not a HID device, it sends serial data which I can interpret and decode to determine if the user pressed Up, Down, Left, Right, or Enter.
Right now all I have is a Fl_Window, with two Fl_Button widgets. Focus is set for one of the buttons and callbacks are defined for these buttons. I know that if I attach a real keyboard, and use the arrow keys I can change focus from button to button. I do have to hit SPACE to activate a button.
My problem is determining how to cause these key presses using code when I decode the outcome form the embedded key pad. Because in deployment, there will be no actual keyboard.
What I've tried is to invoke int Fl_Window::handle(int) and not really had success. I've also tried to invoke int Fl::handle(int, Fl_Window *) and not had success.
Here are code examples:
if((ret = Fl::handle(FL_Left, window)) == 0)
That compiles, but I find that I get zero back, implying that it did not process the event.
if((ret = Fl_Window::handle(FL_Right)) == 0)
That does not compile, informing me that it "cannot call member function virtual int Fl_Window::handle(int) without object"
I'm thinking that the "int event" actually ought to be FL_KEYDOWN.
That logic leaves me to wonder though how I "set" event_key(). For instance, there are API functions to get that when one has a handler, but I do not wish to get that; I wish to cause that event to occur.
Is my only option here to figure out how to emulate a HID or make some type of virtual HID where I then cause the keyboard events to occur?
I do not feel I require a handler function in my application, I'm fine with the default behaviors which occur and cause my callback functions to be invoked. My problem is that I can't "cause" these events to occur.
You need to assign the desired key to e_keysym, then dispatch a FL_KEYDOWN event using Fl::handle_(). (Fl::handle() will not generate the followup FL_SHORTCUT event.)
Fl::e_keysym = FL_Left;
Fl::handle_(FL_KEYDOWN, window);
// sleep() and/or Fl::wait() as appropriate
Fl::handle_(FL_KEYUP, window);
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");
}
}
}
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.
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.