Map NSEvent keyCode to virtual key code - macos

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.

Related

WinUI 3 keyboard layout for input from SwapChainPanel

I'm working on a WinUI 3 - C++/winRT - desktop application.
The application displays and updates in a window-sized XAML SwapChainPanel through Direct2D and needs to receive keyboard input from the user. With KeyDown and KeyUp on the SwapChainPanel, I can get raw keyboard input. However, this provides only VirtualKeys, ScanCodes, RepeatCount, etc. through the accompanying KeyRoutedEventArgs.
I can find no way to tell WinUI 3 which keyboard layout to use, nor any sort of keyboard management for such things as shift keys, etc (VirtualKeys are only capital ASCII letters).
What I've managed to do is build window messages from the KeyDowns and KeyUps and send them to TranslateMessage then DispatchMessage so they end up as WM_CHARs in the window's message loop.
This takes care of quite a bit of the shift, caps lock, etc. logic and produces Unicode. However, it doesn't take into account the keyboard layout which, in my case, is Canadian Multilingual with four layers of characters on most keys. I can receive some non-ASCII characters (Latin 1) but they aren't the right ones.
This must be a common situation with all the different languages in the world, but I haven't found anything in the way of a keyboard processing function that would receive raw information from the keyboard, process the control keys and output Unicode.
If the window's message pump is the only way to go (for now ?), how to get TranslateMessage to take into account the keyboard layout ?
Thanks for any help with this.

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

Programmatically implement Mac long-press accent popup

The application I'm working on uses a custom text editor. The problem is that it therefore doesn't employ the Mac's now-standard long-press key accent popup, i.e., holding down 'A' will produce "aaaaaaaaa" instead of the "à á â ä..." window.
Anyone know if it's possible to programmatically call/otherwise implement that accent popup?
Properly handling key events so that they interact properly with the system is discussed in the Creating a Custom Text View section of the Cocoa Text Architecture Guide. You should also familiarize yourself with the Handling Key Events chapter of the Cocoa Event Handling Guide (although it's a bit outdated; in particular, it refers to the deprecated NSTextInput protocol which has been replaced by NSTextInputClient).
The basic gist is that you should send key events through either -[NSTextInputContext handleEvent:] or -[NSResponder interpretKeyEvents:], implement action methods on your view, and also have your view class adopt and implement the NSTextInputClient protocol.
You acquire a reference to the appropriate NSTextInputContext object from the inputContext property of NSView.
Sending the key events through one of those handler methods is how that press-and-hold feature gets activated. The NSTextInputClient protocol methods are how it ultimately interacts with your text view's document model. When the user selects a character from the popover, the feature uses that protocol to actually replace the initial character with the final one.
This will also allow your text view to handle Asian input methods.

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.

Pre-define global hotkeys in a cocoa application and the keyboard layout

I'd love to pre-define some global hotkeys in my little cocoa application, and my question is: should I worry about the keyboard layouts?
I mean for example, when I pre-define a global key combo like "Cmd+Opt+A", I can look up key code of 'A' and hard-code it in my program, but i'm not sure if it would be a problem when users using a different keyboard layout rather than mine. Aslo, is it OK to use kVK_ANSI_A etc in this situation? What's the best practice to handle this kind of problem?
Thanks!
Keycodes are designed to be universally. That is why they are used rather than just the letter "A". So yeah, it is safe to do so. Here is a very useful app on the mac app store for finding key codes. They keycode for "A" is 97. So you can register a hotkey using keycode = 97 and modifierFlags = NSCommandKeyMask | NSAlternateKeyMask;

Resources