diff --git a/sources/MacOSKeyCodes.swift b/sources/MacOSKeyCodes.swift index dd2ec3f7d..c9cc8cc04 100644 --- a/sources/MacOSKeyCodes.swift +++ b/sources/MacOSKeyCodes.swift @@ -65,6 +65,30 @@ struct SquirrelKeycode { return UInt32(XK_VoidSymbol) } + static let modifierKeycodes: Set = [ + UInt16(kVK_Shift), UInt16(kVK_RightShift), + UInt16(kVK_CapsLock), + UInt16(kVK_Control), UInt16(kVK_RightControl), + UInt16(kVK_Option), UInt16(kVK_RightOption), + UInt16(kVK_Command), UInt16(kVK_RightCommand), + UInt16(kVK_Function) + ] + + static func inferModifierKeycode(from changes: NSEvent.ModifierFlags) -> UInt16? { + if changes.contains(.capsLock) { + return UInt16(kVK_CapsLock) + } else if changes.contains(.shift) { + return UInt16(kVK_Shift) + } else if changes.contains(.control) { + return UInt16(kVK_Control) + } else if changes.contains(.option) { + return UInt16(kVK_Option) + } else if changes.contains(.command) { + return UInt16(kVK_Command) + } + return nil + } + private static let keycodeMappings: [Int: Int32] = [ // modifiers kVK_CapsLock: XK_Caps_Lock, diff --git a/sources/SquirrelInputController.swift b/sources/SquirrelInputController.swift index bad53d9f1..398a469af 100644 --- a/sources/SquirrelInputController.swift +++ b/sources/SquirrelInputController.swift @@ -62,9 +62,22 @@ final class SquirrelInputController: IMKInputController { } // print("[DEBUG] FLAGSCHANGED client: \(sender ?? "nil"), modifiers: \(modifiers)") var rimeModifiers: UInt32 = SquirrelKeycode.osxModifiersToRime(modifiers: modifiers) - // For flags-changed event, keyCode is available since macOS 10.15 - // (#715) - let rimeKeycode: UInt32 = SquirrelKeycode.osxKeycodeToRime(keycode: event.keyCode, keychar: nil, shift: false, caps: false) + // For flags-changed event, keyCode is available since macOS 10.15 (#715) + // Some remote desktop software (e.g. Parsec) sends flagsChanged events with + // keyCode defaulting to 0 (kVK_ANSI_A) instead of the actual modifier keycode, + // causing a ghost 'a' keypress. Validate and infer the correct keycode from + // the changed modifier flags when necessary. (#825) + var keyCode = event.keyCode + if !SquirrelKeycode.modifierKeycodes.contains(keyCode) { + guard let inferred = SquirrelKeycode.inferModifierKeycode(from: changes) else { + lastModifiers = modifiers + rimeUpdate() + handled = true + break + } + keyCode = inferred + } + let rimeKeycode: UInt32 = SquirrelKeycode.osxKeycodeToRime(keycode: keyCode, keychar: nil, shift: false, caps: false) if changes.contains(.capsLock) { // NOTE: rime assumes XK_Caps_Lock to be sent before modifier changes,