From 61ddade049dd048415e8fca9ba2c81cba13e5139 Mon Sep 17 00:00:00 2001 From: palmoni5 Date: Fri, 21 Aug 2026 12:07:25 +0300 Subject: [PATCH] fix(windows): restore keyboard focus when the cursor re-enters the remote image (#15880) * fix(windows): restore keyboard focus when the cursor re-enters the remote image On Windows the raw key focus node is unfocused on window blur and nothing requests it back, so returning to an already connected session left the keyboard dead until the remote image was clicked. Request focus from enterView(), gated on the window being active, the tab being selected and no blocking overlay, so a background window cannot grab system keys. enterOrLeave(true) is still driven by RawKeyFocusScope's onFocusChange, so it is not called twice. * fix(windows): refocus on window focus when the cursor already hovers the image Alt+Tab or a taskbar click returns focus without a PointerEnter, so enterView() cannot restore the keyboard. Reuse _cursorOverImage, gated on the selected tab and no blocking overlay. * refactor(windows): share one focus predicate for every requestFocus path The relative-mouse-mode restore on window focus could hand remote input to this page while a blocking dialog was up or the tab was not selected. --- flutter/lib/desktop/pages/remote_page.dart | 27 ++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 79f382249..3e98418b1 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -273,6 +273,11 @@ class _RemotePageState extends State tabState.tabs[selected].key == widget.id; } + // Every Windows requestFocus() must pass this, or a blocking dialog or an + // inactive tab could hand remote input to this page. + bool get _windowsCanFocusRemoteInput => + _isSelectedTab && _blockableOverlayState.middleBlocked.isFalse; + bool get _isMacOSKeyboardContextActive { return stateGlobal.isFocused.value && !_isWindowBlur && _isSelectedTab; } @@ -513,6 +518,15 @@ class _RemotePageState extends State _queueMacOSKeyboardAfterFullScreen(allowHiddenLifecycle: true); } + // Refocus without PointerEnter: the cursor already hovers the image when + // focus returns (Alt+Tab, taskbar), so enterView() never fires again. + if (isWindows && + _cursorOverImage.value && + _windowsCanFocusRemoteInput && + !_rawKeyFocusNode.hasFocus) { + _rawKeyFocusNode.requestFocus(); + } + // Restore relative mouse mode constraints when window regains focus. if (_ffi.inputModel.relativeMouseMode.value) { if (isMacOS) { @@ -523,7 +537,7 @@ class _RemotePageState extends State _cursorOverImage.value = true; _macOSLocalFocusLost = false; } - } else { + } else if (!isWindows || _windowsCanFocusRemoteInput) { _rawKeyFocusNode.requestFocus(); } _ffi.inputModel.onWindowFocus(); @@ -835,7 +849,16 @@ class _RemotePageState extends State _macOSLocalFocusLost = false; stateGlobal.getInputSource(force: true); _syncMacOSKeyboardGrab(reassert: true, allowInactiveLifecycle: true); - } else if (!isWindows) { + } else if (isWindows) { + // Blur unfocuses this node and nothing restores it, so the keyboard stayed + // dead until a click. Focus only while the window is really active, or a + // background window would grab system keys. onFocusChange does enterOrLeave. + if (!_isWindowBlur && + _windowsCanFocusRemoteInput && + !_rawKeyFocusNode.hasFocus) { + _rawKeyFocusNode.requestFocus(); + } + } else { if (!_rawKeyFocusNode.hasFocus) { _rawKeyFocusNode.requestFocus(); }