From 058b8f901bf38e14b42638897a414d63385312b0 Mon Sep 17 00:00:00 2001 From: marton Date: Wed, 13 May 2026 23:10:05 -0400 Subject: [PATCH] fix(macos): preserve modifiers through Apple input reflectors --- src/platform/macos/input.cpp | 80 ++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/src/platform/macos/input.cpp b/src/platform/macos/input.cpp index bf29b80d7d6..de721819e25 100644 --- a/src/platform/macos/input.cpp +++ b/src/platform/macos/input.cpp @@ -11,6 +11,7 @@ #include #import #include +#include #include // local includes @@ -36,6 +37,7 @@ namespace platf { CGEventSourceRef source {}; // keyboard related stuff + CGEventSourceRef keyboard_source {}; CGEventFlags kb_flags {}; // mouse related stuff @@ -245,6 +247,43 @@ const KeyCodeMap kKeyCodesMap[] = { return temp_map->mac_keycode; } + struct modifier_flags_t { + CGEventFlags generic {}; + CGEventFlags device {}; + CGEventFlags all_devices {}; + }; + + bool modifier_flags_for_key(int key, modifier_flags_t &flags) { + switch (key) { + case kVK_Shift: + flags = {kCGEventFlagMaskShift, NX_DEVICELSHIFTKEYMASK, NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK}; + return true; + case kVK_RightShift: + flags = {kCGEventFlagMaskShift, NX_DEVICERSHIFTKEYMASK, NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK}; + return true; + case kVK_Command: + flags = {kCGEventFlagMaskCommand, NX_DEVICELCMDKEYMASK, NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK}; + return true; + case kVK_RightCommand: + flags = {kCGEventFlagMaskCommand, NX_DEVICERCMDKEYMASK, NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK}; + return true; + case kVK_Option: + flags = {kCGEventFlagMaskAlternate, NX_DEVICELALTKEYMASK, NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK}; + return true; + case kVK_RightOption: + flags = {kCGEventFlagMaskAlternate, NX_DEVICERALTKEYMASK, NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK}; + return true; + case kVK_Control: + flags = {kCGEventFlagMaskControl, NX_DEVICELCTLKEYMASK, NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK}; + return true; + case kVK_RightControl: + flags = {kCGEventFlagMaskControl, NX_DEVICERCTLKEYMASK, NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK}; + return true; + default: + return false; + } + } + void keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) { auto key = keysym(modcode); @@ -256,43 +295,28 @@ const KeyCodeMap kKeyCodesMap[] = { auto macos_input = ((macos_input_t *) input.get()); CGEventRef event = nullptr; + modifier_flags_t modifier_flags; - if (key == kVK_Shift || key == kVK_RightShift || - key == kVK_Command || key == kVK_RightCommand || - key == kVK_Option || key == kVK_RightOption || - key == kVK_Control || key == kVK_RightControl) { - event = CGEventCreate(macos_input->source); + if (modifier_flags_for_key(key, modifier_flags)) { + event = CGEventCreateKeyboardEvent(macos_input->keyboard_source, key, !release); if (!event) { return; } CGEventSetIntegerValueField(event, kCGKeyboardEventKeycode, key); - CGEventFlags mask; - - switch (key) { - case kVK_Shift: - case kVK_RightShift: - mask = kCGEventFlagMaskShift; - break; - case kVK_Command: - case kVK_RightCommand: - mask = kCGEventFlagMaskCommand; - break; - case kVK_Option: - case kVK_RightOption: - mask = kCGEventFlagMaskAlternate; - break; - case kVK_Control: - case kVK_RightControl: - mask = kCGEventFlagMaskControl; - break; + if (release) { + macos_input->kb_flags &= ~modifier_flags.device; + if ((macos_input->kb_flags & modifier_flags.all_devices) == 0) { + macos_input->kb_flags &= ~modifier_flags.generic; + } + } else { + macos_input->kb_flags |= modifier_flags.generic | modifier_flags.device; } - macos_input->kb_flags = release ? macos_input->kb_flags & ~mask : macos_input->kb_flags | mask; CGEventSetType(event, kCGEventFlagsChanged); } else { - event = CGEventCreateKeyboardEvent(macos_input->source, key, !release); + event = CGEventCreateKeyboardEvent(macos_input->keyboard_source, key, !release); if (!event) { return; } @@ -301,7 +325,7 @@ const KeyCodeMap kKeyCodesMap[] = { } CGEventSetFlags(event, macos_input->kb_flags); - CGEventPost(kCGHIDEventTap, event); + CGEventPost(kCGSessionEventTap, event); CFRelease(event); } @@ -567,6 +591,7 @@ const KeyCodeMap kKeyCodesMap[] = { CFRelease(mode); macos_input->source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); + macos_input->keyboard_source = CGEventSourceCreate(kCGEventSourceStatePrivate); macos_input->kb_flags = 0; @@ -584,6 +609,7 @@ const KeyCodeMap kKeyCodesMap[] = { const auto *input = static_cast(p); CFRelease(input->source); + CFRelease(input->keyboard_source); CFRelease(input->mouse_event); delete input;