diff options
| author | ppggff <pgf00a@gmail.com> | 2026-07-05 08:25:47 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-05 03:25:47 +0300 |
| commit | 2bc1724c2dae72184a5d2ec070e30c70dc519f9b (patch) | |
| tree | 573a960e82f832455d303ca97ea1783587482ad6 /src/platform | |
| parent | e713949ca51facf1e40c584b271f812f527bc522 (diff) | |
fix: switch prefix ASCII input source on the foreground client (#1016)
* fix: switch prefix ASCII input source on the foreground client
The macOS prefix ASCII-switch ran in the headless server, whose
TISCopyCurrentKeyboardInputSource read is a stale per-process cache
(refreshed only via a main-run-loop notification the server never pumps),
so prefix could leave the terminal on the wrong input source.
Forward it to the foreground client, like clipboard: sync_prefix_input_source
emits AppEvent::PrefixInputSource as an intent; the server forwards it to the
foreground client and the monolithic app applies it in-process via
RealPrefixInputSource, which pumps its main run loop before the stale TIS read.
Key it on Mode::wants_ascii_input so multi-level prefix commands keep ASCII
until returning to the terminal, and restore the IME for rename text entry.
Adds ServerMessage::PrefixInputSource to the already-unreleased protocol 15.
refs #774
* fix: keep prefix input-source switch out of the headless server process
An App-internal drain (the exhaustive drain at the top of
handle_api_request, reachable from prefix-key runtime mutations) can
consume a queued PrefixInputSource intent before the server's
forwarding drain sees it, applying the TIS switch in the headless
server process and stranding the restore state. Gate the in-process
switch on App.local_input_source_switch, disabled by the headless
server alongside the other local side effects, so a swallowed intent
degrades to a skipped switch. Widen the forwarding-bypass maintenance
test to the handle_internal_event_with_prefix_sync wrapper.
refs #774
* docs: clarify prefix input-source comments and flag docs
refs #774
* fix: bump protocol for prefix input source
refs #774
---------
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>
Diffstat (limited to 'src/platform')
| -rw-r--r-- | src/platform/macos.rs | 28 | ||||
| -rw-r--r-- | src/platform/mod.rs | 6 |
2 files changed, 34 insertions, 0 deletions
diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 3770a3be..e72d32fe 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -97,6 +97,34 @@ extern "C" { buffer_size: CfIndex, encoding: u32, ) -> Boolean; + + #[link_name = "kCFRunLoopDefaultMode"] + static CF_RUN_LOOP_DEFAULT_MODE: CfStringRef; + + #[link_name = "CFRunLoopRunInMode"] + fn cf_run_loop_run_in_mode( + mode: CfStringRef, + seconds: f64, + return_after_source_handled: Boolean, + ) -> libc::c_int; +} + +/// Pump the main thread's run loop once (non-blocking) so the process receives the +/// `kTISNotifySelectedKeyboardInputSourceChanged` notification and refreshes the per-process cache +/// that `TISCopyCurrentKeyboardInputSource` reads. That notification arrives only via the main +/// thread's run loop, so a process that never runs a CFRunLoop (the headless server) reads a stale +/// source. Must run on the main thread. +pub(crate) fn pump_input_source_runloop() { + debug_assert!( + // SAFETY: `pthread_main_np` is always safe to call. + unsafe { libc::pthread_main_np() } != 0, + "pump_input_source_runloop must run on the main thread" + ); + // SAFETY: `CFRunLoopRunInMode` is thread-safe; a 0-second call drains the ready sources and + // returns immediately (no blocking). `CF_RUN_LOOP_DEFAULT_MODE` is a framework-owned constant. + unsafe { + let _ = cf_run_loop_run_in_mode(CF_RUN_LOOP_DEFAULT_MODE, 0.0, 0); + } } #[derive(Debug)] diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 169b7c1c..889c4ce9 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -154,6 +154,9 @@ pub(crate) fn switch_to_ascii_input_source() -> Option<InputSourceRestore> { None } +#[cfg(not(target_os = "macos"))] +pub(crate) fn pump_input_source_runloop() {} + /// Switches the host keyboard input source while prefix mode is active. /// /// `App` drives this through a trait so the prefix-mode transitions can be @@ -179,6 +182,9 @@ pub(crate) struct RealPrefixInputSource { impl PrefixInputSource for RealPrefixInputSource { fn switch_to_ascii(&mut self) { if self.restore.is_none() { + // Drain pending input-source-change notifications so the read below is fresh (see + // `pump_input_source_runloop`); a no-op on non-macOS. + pump_input_source_runloop(); self.restore = switch_to_ascii_input_source(); } } |
