[Interest] Qt/Mac low-level hacking: key mapping and NSMenuFunctionKey

René J.V. Bertin rjvbertin at gmail.com
Sun Jan 29 15:28:29 CET 2017


Not long ago I asked about support (on Mac) for opening context menus via the keyboard. I was told there is none, so I dug around in the Qt sources for a bit myself. I discovered quite quickly that there is a NSMenuFunctionKey (defined in the system SDKs) that is taken into account in the Cocoa platform plugin, and mapped to Qt::Key_Menu .

I have now tinkered with the idea of intercepting keyboard events with a local NSEvent monitor (set in a style plugin for now), which catches events corresponding to a press of the right Option (Alt) key and replaces them with a synthetic event emulating a press of the Menu key:

                const unichar menuKeyCode = static_cast<unichar>(NSMenuFunctionKey);
                NSString *menuKeyString = [NSString stringWithCharacters:&menuKeyCode length:1];
                NSEvent *menuKeyEvent = [NSEvent keyEventWithType:NSKeyDown
                    location:[event locationInWindow] modifierFlags:([event modifierFlags] & ~NSAlternateKeyMask)
                    timestamp:[event timestamp] windowNumber:[event windowNumber]
                    context:nil characters:menuKeyString charactersIgnoringModifiers:menuKeyString isARepeat:NO
                    // the keyCode must be an 8-bit value so not to be confounded with the Unicode value.
                    // Judging from Carbon/Events.h 0x7f is unused.
                qWarning() << "new event:" << QString::fromNSString([menuKeyEvent description]);
                return menuKeyEvent;

The principle seems to work when I emulate a common function key like Home, but it doesn't work for NSMenuFunctionKey. Part of the problem is probably that I have no way of knowing what keycode to insert, but there's something else which appears to go wrong in qcocoakeymapper.mm

With the event created as shown above, QCocoaKeyMapper::updateKeyMap() is called as follows:

    qWarning("updateKeyMap for virtual key = 0x%02x unicodeKey=0x%04x!", (uint)macVirtualKey, unicodeKey);
> updateKeyMap for virtual key = 0x7f unicodeKey=0x1000055!

That corresponds to my event (enum Key in qnamespace.h). Evidently (?), UCKeyTranslate() fails for this combination, so qt_mac_get_key() is called with the input unicodeKey and macVirtualKey parameters. However, that function returns the letter 'U' for this input, and with DEBUG_KEY_BINDINGS set we can see why:

> **Mapping key: 85 (0x0055) - 61 (0x003d)
> 287: got key: 20

But why would qWarning("%04x", unicodeKey) print 0x1000055 and qWarning("%04x", unicodeKey.unicode()) print 0x0055 if a QChar's actual value is stored as `ushort ucs`??

By contrast, this is the output for a real event when pressing F1:

> updateKeyMap for virtual key = 0x7a unicodeKey=0x1000030!
> **Mapping key: 16 (0x0010) - 122 (0x007a)
> 319: got key: Qt::Key_F1

In short, I must manage to get qt_mac_get_key() to fall through to its final loop where it checks "if they belong to key codes in private unicode range". Any suggestions how I can get there without patching qcocoakeymapper.mm ? Has anyone ever been able to test this particular case/configuration with a keyboard that does have a Menu key, or with a system keymap that generates actual NSMenuFunctionKey strokes (or a properly configured ~/Library/KeyBindings/DefaultKeyBinding.dict)?


More information about the Interest mailing list