From 01348c762c5fd59b631ed2745cffb43ae619cc94 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 26 Sep 2026 14:53:46 +0800 Subject: [PATCH] fix: prevent lost input after remote session shortcuts Avoid synthetic Alt presses that activate the Windows system menu and swallow the next character. Trigger restart, switch-sides, and chat actions on key release so focus changes cannot leave subsequent shortcut presses ignored. Signed-off-by: fufesou --- .../keyboard_shortcuts/shortcut_constants.dart | 5 ++++- flutter/test/fixtures/key_up_shortcut_actions.json | 5 ++++- src/keyboard/shortcuts.rs | 11 +++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/flutter/lib/common/widgets/keyboard_shortcuts/shortcut_constants.dart b/flutter/lib/common/widgets/keyboard_shortcuts/shortcut_constants.dart index f2163900d..002644601 100644 --- a/flutter/lib/common/widgets/keyboard_shortcuts/shortcut_constants.dart +++ b/flutter/lib/common/widgets/keyboard_shortcuts/shortcut_constants.dart @@ -18,7 +18,7 @@ const kShortcutActionResetCanvas = 'reset_canvas'; const kShortcutActionSwitchTabNext = 'switch_tab_next'; const kShortcutActionSwitchTabPrev = 'switch_tab_prev'; -/// Actions that move keyboard focus to another session. They run when the +/// Actions that move keyboard focus to another session or a dialog. They run when the /// key that fired them is released, not when it is pressed, so the matcher /// that consumed the press also sees its repeats and its release. On Linux a /// legacy-mode session matches in Dart while a map-mode session matches in @@ -29,6 +29,9 @@ const kShortcutActionsRunOnKeyUp = { kShortcutActionCloseTab, kShortcutActionSwitchTabNext, kShortcutActionSwitchTabPrev, + kShortcutActionRestartRemote, + kShortcutActionSwitchSides, + kShortcutActionToggleChat, }; const kShortcutActionToggleMute = 'toggle_mute'; const kShortcutActionPinToolbar = 'pin_toolbar'; diff --git a/flutter/test/fixtures/key_up_shortcut_actions.json b/flutter/test/fixtures/key_up_shortcut_actions.json index 4bf77dc8d..e916844d7 100644 --- a/flutter/test/fixtures/key_up_shortcut_actions.json +++ b/flutter/test/fixtures/key_up_shortcut_actions.json @@ -1,5 +1,8 @@ [ "close_tab", "switch_tab_next", - "switch_tab_prev" + "switch_tab_prev", + "restart_remote", + "switch_sides", + "toggle_chat" ] diff --git a/src/keyboard/shortcuts.rs b/src/keyboard/shortcuts.rs index 1574c80ee..924bee045 100644 --- a/src/keyboard/shortcuts.rs +++ b/src/keyboard/shortcuts.rs @@ -338,7 +338,7 @@ lazy_static::lazy_static! { > = Default::default(); } -/// Actions that move keyboard focus to another session. They run when the +/// Actions that move keyboard focus to another session or a dialog. They run when the /// key that fired them is released, not when it is pressed, so the matcher /// that consumed the press also sees its repeats and its release. The next /// session may route its keys through the other matcher (Flutter's legacy @@ -349,6 +349,9 @@ const RELEASE_ACTION_IDS: &[&str] = &[ action_id::CLOSE_TAB, action_id::SWITCH_TAB_NEXT, action_id::SWITCH_TAB_PREV, + action_id::RESTART_REMOTE, + action_id::SWITCH_SIDES, + action_id::TOGGLE_CHAT, ]; pub fn runs_on_release(action_id: &str) -> bool { @@ -538,9 +541,9 @@ fn release_remote_keys( }; for (key, mut event) in to_release { let mut types = vec![EventType::KeyRelease(key)]; - // Same as `keyboard::release_remote_keys_for_events`: a lone Alt - // release can leave Alt held on the controlled side. - if key == Key::Alt || key == Key::AltGr { + // Keep the focus-loss workaround for other peers. Tapping Alt on + // Windows activates its system menu and consumes the next character. + if peer != "windows" && (key == Key::Alt || key == Key::AltGr) { types.push(EventType::KeyPress(key)); types.push(EventType::KeyRelease(key)); }