8

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?

NateS
  • 5,751
  • 4
  • 49
  • 59
  • For someone coming here with a similar problem. Have a look at [KeyNaming](https://github.com/JensAyton/KeyNaming) library. – Hari Mahadevan Feb 25 '19 at 03:35

2 Answers2

21

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.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Ah, thanks for explaining virtual key codes from the Mac perspective, it seems to differ from Windows. It seems easiest to test charactersIgnoringModifiers for characters my app cares about. – NateS Aug 24 '13 at 13:51
  • What are the API calls as of now, since Carbon Core and Carbon in general got deprecated? –  Apr 09 '18 at 20:07
  • @Julien, not all of Carbon is deprecated. The functions I mention here are not. – Ken Thomases Apr 09 '18 at 21:21
  • Hm, interesting. The function itself is not marked as deprecated, but the whole module instead, without mentioning it in the function documentation. –  Apr 09 '18 at 21:24
1

According to the NSEvent documentation, -[NSEvent keyCode] returns the hardware-independent virtual key code.

JWWalker
  • 22,385
  • 6
  • 55
  • 76
  • Hmm, you're right, for some reason I read that as hardware-dependent, I guess because that seems to be the behavior in my app. I guess I have more investigating to do. Nothing to see here, carry on... – NateS Aug 23 '13 at 19:33