Merge pull request #431 from yasunogithub/fix-ime-ctrl-fastpath

Fix IME key events blocked by ctrl fast path
This commit is contained in:
Lawrence Chen 2026-02-25 02:46:12 -08:00 committed by GitHub
commit a46ee2ba3b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 38 additions and 1 deletions

View file

@ -40,6 +40,7 @@
A5001303 /* SurfaceSearchOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001301 /* SurfaceSearchOverlay.swift */; }; A5001303 /* SurfaceSearchOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001301 /* SurfaceSearchOverlay.swift */; };
A50012F1 /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50012F0 /* Backport.swift */; }; A50012F1 /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50012F0 /* Backport.swift */; };
A50012F3 /* KeyboardShortcutSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50012F2 /* KeyboardShortcutSettings.swift */; }; A50012F3 /* KeyboardShortcutSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50012F2 /* KeyboardShortcutSettings.swift */; };
A50012F5 /* KeyboardLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50012F4 /* KeyboardLayout.swift */; };
A5001521 /* PostHogAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001520 /* PostHogAnalytics.swift */; }; A5001521 /* PostHogAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001520 /* PostHogAnalytics.swift */; };
A5001201 /* UpdateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001211 /* UpdateController.swift */; }; A5001201 /* UpdateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001211 /* UpdateController.swift */; };
A5001202 /* UpdateDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001212 /* UpdateDelegate.swift */; }; A5001202 /* UpdateDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001212 /* UpdateDelegate.swift */; };
@ -171,6 +172,7 @@
A5001301 /* SurfaceSearchOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Find/SurfaceSearchOverlay.swift; sourceTree = "<group>"; }; A5001301 /* SurfaceSearchOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Find/SurfaceSearchOverlay.swift; sourceTree = "<group>"; };
A50012F0 /* Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backport.swift; sourceTree = "<group>"; }; A50012F0 /* Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backport.swift; sourceTree = "<group>"; };
A50012F2 /* KeyboardShortcutSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardShortcutSettings.swift; sourceTree = "<group>"; }; A50012F2 /* KeyboardShortcutSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardShortcutSettings.swift; sourceTree = "<group>"; };
A50012F4 /* KeyboardLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardLayout.swift; sourceTree = "<group>"; };
A5001211 /* UpdateController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Update/UpdateController.swift; sourceTree = "<group>"; }; A5001211 /* UpdateController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Update/UpdateController.swift; sourceTree = "<group>"; };
A5001212 /* UpdateDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Update/UpdateDelegate.swift; sourceTree = "<group>"; }; A5001212 /* UpdateDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Update/UpdateDelegate.swift; sourceTree = "<group>"; };
A5001213 /* UpdateDriver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Update/UpdateDriver.swift; sourceTree = "<group>"; }; A5001213 /* UpdateDriver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Update/UpdateDriver.swift; sourceTree = "<group>"; };
@ -322,6 +324,7 @@
B9000017A1B2C3D4E5F60719 /* WindowDragHandleView.swift */, B9000017A1B2C3D4E5F60719 /* WindowDragHandleView.swift */,
A50012F0 /* Backport.swift */, A50012F0 /* Backport.swift */,
A50012F2 /* KeyboardShortcutSettings.swift */, A50012F2 /* KeyboardShortcutSettings.swift */,
A50012F4 /* KeyboardLayout.swift */,
A5001013 /* TabManager.swift */, A5001013 /* TabManager.swift */,
A5001511 /* UITestRecorder.swift */, A5001511 /* UITestRecorder.swift */,
A5001520 /* PostHogAnalytics.swift */, A5001520 /* PostHogAnalytics.swift */,
@ -559,6 +562,7 @@
B9000018A1B2C3D4E5F60719 /* WindowDragHandleView.swift in Sources */, B9000018A1B2C3D4E5F60719 /* WindowDragHandleView.swift in Sources */,
A50012F1 /* Backport.swift in Sources */, A50012F1 /* Backport.swift in Sources */,
A50012F3 /* KeyboardShortcutSettings.swift in Sources */, A50012F3 /* KeyboardShortcutSettings.swift in Sources */,
A50012F5 /* KeyboardLayout.swift in Sources */,
A5001003 /* TabManager.swift in Sources */, A5001003 /* TabManager.swift in Sources */,
A5001501 /* UITestRecorder.swift in Sources */, A5001501 /* UITestRecorder.swift in Sources */,
A5001521 /* PostHogAnalytics.swift in Sources */, A5001521 /* PostHogAnalytics.swift in Sources */,

View file

@ -2918,7 +2918,10 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations {
"ign=\(cmuxScalarHex(event.charactersIgnoringModifiers)) mods=\(event.modifierFlags.rawValue)" "ign=\(cmuxScalarHex(event.charactersIgnoringModifiers)) mods=\(event.modifierFlags.rawValue)"
) )
#endif #endif
return // If Ghostty handled the key (action/encoding), we're done.
// If not (e.g. `ignore` keybind), fall through to interpretKeyEvents
// so the IME gets a chance to process this event.
if handled { return }
} }
let action = event.isARepeat ? GHOSTTY_ACTION_REPEAT : GHOSTTY_ACTION_PRESS let action = event.isARepeat ? GHOSTTY_ACTION_REPEAT : GHOSTTY_ACTION_PRESS
@ -2973,9 +2976,25 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations {
// so we can detect when composition ends. // so we can detect when composition ends.
let markedTextBefore = markedText.length > 0 let markedTextBefore = markedText.length > 0
// Capture the keyboard layout ID before interpretation so we can
// detect if an IME changed it (e.g. toggling input methods).
// We only check when not already in a preedit state.
let keyboardIdBefore: String? = if (!markedTextBefore) {
KeyboardLayout.id
} else {
nil
}
// Let the input system handle the event (for IME, dead keys, etc.) // Let the input system handle the event (for IME, dead keys, etc.)
interpretKeyEvents([translationEvent]) interpretKeyEvents([translationEvent])
// If the keyboard layout changed, an input method grabbed the event.
// Sync preedit and return without sending the key to Ghostty.
if !markedTextBefore, let kbBefore = keyboardIdBefore, kbBefore != KeyboardLayout.id {
syncPreedit(clearIfNeeded: markedTextBefore)
return
}
// Sync the preedit state with Ghostty so it can render the IME // Sync the preedit state with Ghostty so it can render the IME
// composition overlay (e.g. for Korean, Japanese, Chinese input). // composition overlay (e.g. for Korean, Japanese, Chinese input).
syncPreedit(clearIfNeeded: markedTextBefore) syncPreedit(clearIfNeeded: markedTextBefore)

View file

@ -0,0 +1,14 @@
import Carbon
class KeyboardLayout {
/// Return a string ID of the current keyboard input source.
static var id: String? {
if let source = TISCopyCurrentKeyboardInputSource()?.takeRetainedValue(),
let sourceIdPointer = TISGetInputSourceProperty(source, kTISPropertyInputSourceID) {
let sourceId = Unmanaged<CFString>.fromOpaque(sourceIdPointer).takeUnretainedValue()
return sourceId as String
}
return nil
}
}