From 9c18070b17d69777cb5ee8b4784cd6e30b02f745 Mon Sep 17 00:00:00 2001 From: Momoyama Date: Wed, 27 May 2026 18:44:49 +0800 Subject: [PATCH] fix(macos): avoid non-selectable IME layouts for remote keys macOS input methods can expose non-user-selectable keyboard layouts bundled with the input method rather than the user's real selectable keyboard layout. Translating remote key events through those layouts can send IME punctuation semantics to clients instead of the expected ASCII punctuation. Use the current ASCII-capable keyboard layout when the active input source has no Unicode key layout data and the reported keyboard layout is not directly selectable. Normal selectable keyboard layouts continue to use the existing translation path. This affects macOS servers forwarding keyboard input to remote clients while an input-method-bundled layout is active. --- src/lib/platform/OSXKeyState.cpp | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 487b11161..5cc7b1277 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -173,6 +173,33 @@ bool isModifier(uint8_t virtualKey) return (modifiers.find(virtualKey) != modifiers.end()); } +AutoTISInputSourceRef copyKeyboardLayoutForKeyTranslation() +{ + std::lock_guard lock(g_tisMutex); + + AutoTISInputSourceRef keyboardLayout(TISCopyCurrentKeyboardLayoutInputSource(), CFRelease); + AutoTISInputSourceRef inputSource(TISCopyCurrentKeyboardInputSource(), CFRelease); + + const bool inputSourceHasLayout = + inputSource && TISGetInputSourceProperty(inputSource.get(), kTISPropertyUnicodeKeyLayoutData) != nullptr; + + CFBooleanRef isSelectCapable = nullptr; + if (keyboardLayout) { + isSelectCapable = + (CFBooleanRef)TISGetInputSourceProperty(keyboardLayout.get(), kTISPropertyInputSourceIsSelectCapable); + } + const bool keyboardLayoutIsSelectCapable = isSelectCapable && CFBooleanGetValue(isSelectCapable); + + if (inputSource && keyboardLayout && !inputSourceHasLayout && !keyboardLayoutIsSelectCapable) { + AutoTISInputSourceRef asciiKeyboardLayout(TISCopyCurrentASCIICapableKeyboardLayoutInputSource(), CFRelease); + if (asciiKeyboardLayout) { + return asciiKeyboardLayout; + } + } + + return keyboardLayout; +} + } // namespace // @@ -293,11 +320,10 @@ KeyButton OSXKeyState::mapKeyFromEvent(KeyIDs &ids, KeyModifierMask *maskOut, CG } // get keyboard info - AutoTISInputSourceRef currentKeyboardLayout(nullptr, CFRelease); + AutoTISInputSourceRef currentKeyboardLayout = copyKeyboardLayoutForKeyTranslation(); CFDataRef ref = nullptr; { std::lock_guard lock(g_tisMutex); - currentKeyboardLayout = AutoTISInputSourceRef(TISCopyCurrentKeyboardLayoutInputSource(), CFRelease); if (currentKeyboardLayout) ref = (CFDataRef)TISGetInputSourceProperty(currentKeyboardLayout.get(), kTISPropertyUnicodeKeyLayoutData); }