SpriteKit OS X get which key was pressed - macos

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.

Related

Safe KLID For 'Custom' Keyboard Layout?

I need to install several 'custom' keyboard layouts on Windows 10.
These are not MKLC generated layouts.
What is a 'safe' KLID to use for my layouts?
axxxxxxx seems to be used by MKLC.
Dxxxxxxx seems to be utilized by the Layouts PreLoad / Substitues
I have several keyboard layouts to install, I.e, ????0409, ????0407, ????040e, .....
Any ideas for a relatively 'safe' value for '????' ?
I am concerned about running into some one else's keyboard layout.
Thanks
KLID — a keyboard layout identifier. Traditionally pronounced "Kay-El-Eye-Dee" because some people in the USA get very uptight about certain homonyms (you can catch me slipping on this point from time to time). It's also sometimes called the input locale identifier since the name for HKL has been updated (see the HKL definiteion for info on why that is incorrect since the HKL is for something different). The KLID can be retrieved for the currently selected keyboard layout in a thread through the GetKeyboardLayoutName API (note the pswzKLID parameter), though that is not true of any other selected or installed keyboard layout. Every keyboard layout on the system has one of these. Each KLID is 32 bits (thus 8 hex digits), and they can all be found in the registry as the subkeys under HKLM\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\. The bottom half of the KLID is a LANGID, and the top half is something device-specific. By convention, the first hex digit is usually as follows:
0 — Most keyboard layouts
A — Keyboard layouts defined by MSKLC
B — Keyboard layouts defined by KbdEdit
D — Some non-CJK input methods that have been defined by the Text Services Framework (note: reported to me; I have never seen one of these!)
E — CJK input methods, also known as IMEs (deprecated and AFAIK not used since Windows 8)
Looks like you can use 1..9 or B or F hex as prefix.
Source: http://archives.miloush.net/michkap/archive/2005/04/17/409032.html

How to determine if a numpad is present and how to obtain the scan code for the numpad enter key?

I would like to add certain behavior to my program that binds a function to the numpad enter key, if it is present, or bind an alternate key if it is not.
According to Microsoft:
The scan code is the value that the keyboard hardware generates when
the user presses a key. It is a device-dependent value that identifies
the key pressed, as opposed to the character represented by the key.
An application typically ignores scan codes. Instead, it uses the
device-independent virtual-key codes to interpret keystroke messages.
(source)
I know that on my keyboard it is 0x9C (156), but this is not guaranteed to hold true for all keyboards.
I can't use MapVirtualKey() with VK_RETURN and MAPVK_VK_TO_VSC as this always returns the scan code for the primary return key in the center of the keyboard.
How can I obtain this information without any intervention on the part of the user?
My language is C/C++ and this is for Win32 only.
The scan code depends on the hardware, it might not be the same on a different system. There can be more than one scan code that maps to a virtual key. Virtual keys are supposed to be somewhat generic and not tied to the hardware.
You can tell the difference in WM_KEYDOWN and WM_KEYUP; WPARAM is VK_RETURN and bit 24 is set in LPARAM when the Enter key on the numpad is used:
Indicates whether the key is an extended key, such as the right-hand ALT and CTRL keys that appear on an enhanced 101- or 102-key keyboard. The value is 1 if it is an extended key; otherwise, it is 0.
GetKeyboardType can tell you some information about "the keyboard" but since there can be more than one keyboard connected these days you would have to go deeper to find out if there are any keyboards that have the properties you are looking for. Perhaps the SetupAPI knows.
For Numpad Enter to be recognized as either kind of Enter, the keyboard hardware must send a scan code that maps to VK_RETURN within the current keyboard layout. Keyboard layout is determined by system settings and the window which is receiving keyboard input (or the user), not by the physical keyboard. It is quite possible that the virtual keyboard layout does not match the physical keyboard (i.e. the labels on the keys don't match their functions).
There are two strategies for allowing different physical keyboard layouts to function correctly:
Change the labels visible on the keys but keep the scan codes in the same physical positions. For the function of each key to match its label, the user must choose, install or create the correct keyboard layout in software.
Physically move keys, or assign pre-established scan codes to different physical positions. The OS doesn't know or care where any key is physically; it just maps scan codes to virtual keycodes based on the current keyboard layout.
Both Enter and Numpad Enter are mapped to VK_RETURN; there is no virtual key code reserved for Numpad Enter, so no way for it to have different scan codes on different keyboard layouts. With strategy #1, any key can be turned into Enter, but not specifically Numpad Enter. With strategy #2, Numpad Enter still has the same scan code as usual.
At the hardware level, Enter sends 0x1C while Numpad Enter sends 0xE0 0x1C (source: my own observations and a document by Andries Brouwer). Windows has different ways of reporting this: with bit 24 of WM_KEYDOWN's lParam, the LLKHF_EXTENDED flag for low level keyboard hooks, the RI_KEY_E0 flag for Raw Input, and possibly more.
In short, it is safe to assume that Numpad Enter is 0x1C plus the extended-key flag, since in any other case, it is impossible to identify.
I know that on my keyboard it is 0x9C (156)
I assume that you received this value from DirectInput, which defines an enum constant DIK_NUMPADENTER with value 0x9C. This value is not a scan code.
Anders makes a good point - I don't know of a way to tell if that key is present on (one of the) keyboard (s) present on any particular system. Also, don't forget about the Onscreen Keyboard and touch devices in general.
Why not simply bind your function to both keys regardless? Do you have a good reason not to do this?
As an addition to #Lexikos great answer:
The scan code is the value that the keyboard hardware generates when
the user presses a key. It is a device-dependent value that identifies
the key pressed, as opposed to the character represented by the key.
This was true in encient days. At least since Windows NT system uses PS/2 Scan Code Set 1 for all keyboard APIs. With some bugs that are specifically supported for backwards compatibility (for example NumLock and Pause scan codes are swapped. They are special.).
Under all Microsoft operating systems, all keyboards actually transmit
Scan Code Set 2 values down the wire from the keyboard to the keyboard
port. These values are translated to Scan Code Set 1 by the i8042 port
chip. The rest of the operating system, and all applications that
handle scan codes expect the values to be from Scan Code Set 1. Scan
Code Set 3 is not used or required for operation of Microsoft
operating systems. (Keyboard Scan Code Specification Revision 1.3a — March 16, 2000)
Because of this some API docs are reffering to scan codes as to virtual scan codes.
Modern USB or Bluetooth keyboard are using HID protocol with its HID Usage IDs to report key presses (see 10 Keyboard/Keypad Page (0x07) in HID Usage Tables spec for a list of possible keyboard key Usage IDs).
These HID Usages get converted to PS/2 Scan Code Set 1 by kbdclass driver (HID client mapper driver for keyboards) via call to HidP_TranslateUsagesToI8042ScanCodes API. It works according to published spec. So we actually have a published scan code list that is used in Windows. If you're interested in history behind this scan code mess - there is a good page.
There is no way to detect if keypad Enter button is present on particular keyboard hardware.

mac Swift multiple keyDown events

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.

Map NSEvent keyCode to virtual key code

NSEvent keyCode gives a keyboard scan code, which is a hardware specific code representing the physical key. I want to convert the scan code to a virtual key code, which is the logical key based on the users keyboard layout (QWERTY, AZERTY, etc).
In Windows I can do this via MapVirtualKey. What is the OS X equivalent?
The virtual key code is precisely not based on the user's keyboard layout. It indicates which key was pressed, not what character that key would produce nor how it's labeled.
For example, kVK_ANSI_A (from Carbon/HIToolbox/Events.h, value 0x00) does not mean the key which produces the 'A' character, it means the key which is in the position that the 'A' key is in an ANSI standard keyboard. If a French keyboard layout is active, that key will produce 'Q'. If the physical keyboard is a French keyboard, that key will probably be labeled 'Q', too.
So, the virtual key code is sort of akin to a scan code, but from an idealized, standard keyboard. It is, as noted, hardware-independent. It is also independent of the keyboard layout.
To translate from the virtual key code to a character, you can use UCKeyTranslate(). You need the 'uchr' data for the current keyboard layout. You can get that using TISCopyCurrentKeyboardLayoutInputSource() and then TISGetInputSourceProperty() with kTISPropertyUnicodeKeyLayoutData as the property key.
You also need the keyboard type code. I believe it's still supported to use LMGetKbdType() to get that, even though it's no longer documented except in the legacy section. If you don't like that, you can obtain a CGEvent from the NSEvent, create a CGEventSource from that using CGEventCreateSourceFromEvent(), and then use CGEventSourceGetKeyboardType()and call CGEventGetIntegerValueField() with kCGKeyboardEventKeyboardType to get the keyboard type.
Of course, it's much easier to simply use -[NSEvent characters] or -[NSEvent charactersIgnoringModifiers]. Or, if you're implementing a text view, send key-down events to -[NSResponder interpretKeyEvents:] (as discussed in Cocoa Event Handling Guide: Handling Key Events) or -[NSTextInputContext handleEvent:] (as discussed in Cocoa Text Architecture Guide:Text Editing). Either of those will call back to the view with the appropriate action selector, like moveBackward:, or with -insertText: if the keystroke (in context of recent events and the input source) would produce text.
According to the NSEvent documentation, -[NSEvent keyCode] returns the hardware-independent virtual key code.

Cocoa Won't Capture Shift Modifier?

I have an application in which I'm trying to capture the shift key modifier to perform an action, however when I run the program and press and normal key without the shift key modifier I get a beep and the modifier and key are not sent to my keyDown event. The relevant code is:
NSString* eventChars = [theEvent charactersIgnoringModifiers];
if ([eventChars isEqualTo:#"w"]) {
newPlayerRow++;
direction = eUp;
} else if ([eventChars isEqualTo:#"x"]) {
newPlayerRow--;
direction = eDown;
} else if ([eventChars isEqualTo:#"a"]) {
newPlayerCol--;
direction = eLeft;
} else if ([eventChars isEqualTo:#"d"]) {
newPlayerCol++;
direction = eRight;
} else {
[super keyDown:theEvent];
return;
}
// handle the player firing a bullet
if (([theEvent modifierFlags] & (NSShiftKeyMask | NSAlphaShiftKeyMask)) != 0) {
NSLog(#"Shift key");
[self fireBulletAtColumn:newPlayerCol row:newPlayerRow inDirection:direction];
[self setNeedsDisplay:YES];
} else {
...
}
I'm not sure what is causing this, but I'd like to be able to capture shift key presses. Thanks in advance for any help with this problem.
EDIT: Also I'm using a MacBook keyboard if that makes any difference.
EDIT: This is definitely a shift-centric problem as changing (NSShiftKeyMask | NSAlphaShiftKeyMask) to NSControlKeyMask does have the desired effect.
First, -charactersIgnoringModifiers doesn't ignore the shift key, so you will still get shifted characters (i.e UPPERCASE and !%#$%^&*) returned from it. What's probably happening in your function is: You press shift-w, your -isEqualTo: returns false because you're comparing a lowercase 'w' and an uppercase 'W', and so you return before getting to the shift-detection code at the bottom. The simplest solution is to just check for both.
However, if you want, for example, Arabic keyboardists to be able to easily use your app, you really shouldn't hardcode characters that may not even appear on the user's keyboard. The value returned by -keyCode refers to a key's position on the keyboard, not the represented character. For starters, the constants beginning in 'kVK_ANSI_' and 'kVK_' in Events.h (you may have to link to Carbon.framework and #include <Carbon/Carbon.h> to use those constants) can be compared to what -keyCode returns, and they refer to the key positions a QWERTY-using USian expects. So you can be (pretty) sure that, regardless of keyboard layout, the keycodes for 'wasd' (kVK_ANSI_W, kVK_ANSI_A, etc.) will refer to that triangle in the top left of your user's keyboard.
If you just want to be notified of modifier key presses without a character key you should be overriding the NSResponder method flagsChanged.
- (void)flagsChanged:(NSEvent *)theEvent
Have you tried logging the values of eventChars and modifierFlags using NSLog? Perhaps it's something other than what you expected it to be. If the log statement doesn't show up in your output, then this code is not running at all and you have a problem somewhere else.
NSLog(#"Chars: %#, modifier flags: 0x%x", eventChars, [theEvent modifierFlags]);
Some other things worth noting:
isEqualTo: is the AppleScript equality operator. It should work, but won't be as efficient because it goes through compare:. The proper method is either isEqualToString: or the more-generic isEqual:, either of which may do a straight equality comparison.
The traditional set of movement keys in the QWERTY letter region is wsad, not wxad. This uses the inverted-T arrangement. You may also want to support the arrow keys, as many of us do have keyboards with their arrows in the inverted T arrangement.
Furthermore, you should make these configurable by the user, and you should respond to key codes rather than letters because different layouts (French, Dvorak, etc.) will generate different letters. I have a table of key codes, adapted from Inside Macintosh, on my website. Use characters only for display purposes.
Shouldn't it be “fireBulletFromColumn:row:inDirection:”? I don't know about you, but saying that the method fires a bullet at a cell tells me (and will tell you, after you haven't looked at the code for a year) that it fires the bullet toward that cell; replacing “At” with “From” avoids this moment of confusion.
From your code it looks like the code that checks for the modifier is never reached.
Anything that is not "w,x,a,d" is sent to the superclass (see the else branch).
You probably want to move "[super keyDown:theEvent]; return;" at the end of your keyDown method.
Solving the immediate problem:
Building on what Boaz said, the -charactersIgnoringModifiers does not include the Shift key (read the documentation) and will return capitalized characters when the Shift key is down. That is why the Control key worked and the Shift key didn't. Switch the string to lowercase before you test it:
eventChars = [[theEvent charactersIgnoringModifiers] lowercaseString];
After that, (as Peter suggested) change all the isEqualTo:'s to isEqualToString: (besides being correct, it's also documented as being faster).
However:
If you are planning on releasing this app to other people, you should really be using key codes (as Peter and Boaz suggested) so it will work on as many different keyboards and setups as possible.
Create a view in your preferences with an input for each command you have and record the key code for the key the user presses. Store these in your NSUserDefaults and check against those instead of your hard coded strings. You can create a default set for your current "wasd" keys.
See projects like Shortcut Recorder for examples of how to do that (or search your friend).

Resources