Fix: restore keyboard/mouse input on iPadOS after app returns from background

When the user switches to another app and returns to RustDesk on iPadOS,
all input (keyboard, trackpad, touch) stops working until the app is
restarted.

Root cause: iOS silently drops the Flutter focus node when the app goes
to background. On resume, nobody re-requests the focus so input events
never reach the session.

Fix: call _restoreInputOnResume() from didChangeAppLifecycleState when
state == resumed. This restarts the mouse listener and re-requests
physical keyboard focus with a short delay (same pattern already used
in onSoftKeyboardChanged for the iOS virtual keyboard workaround).

Tested on iPad Pro with Magic Keyboard Folio running iPadOS 26.3.1,
connecting to macOS via self-hosted RustDesk server over Tailscale.

Fixes: #4570, #8768

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Nelson Ordóñez 2026-03-30 18:05:59 +02:00
parent 010a54d1c9
commit 8370656fab

View file

@ -154,9 +154,29 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
trySyncClipboard();
_restoreInputOnResume();
}
}
// Fix: restore keyboard/mouse input after returning from background on iPadOS.
// Bug: switching apps on iPadOS loses all input (keyboard, trackpad, touch).
// Root cause: iOS drops the focus node when app goes to background; nobody re-requests it.
// Fix: on resume, restart mouse listener and re-request physical focus with a small
// delay (same pattern already used in onSoftKeyboardChanged for iOS keyboard workaround).
void _restoreInputOnResume() {
if (!isIOS) return;
gFFI.inputModel.listenToMouse(true);
_iosKeyboardWorkaroundTimer?.cancel();
_iosKeyboardWorkaroundTimer = Timer(Duration(milliseconds: 100), () {
if (!mounted) return;
_physicalFocusNode.unfocus();
_iosKeyboardWorkaroundTimer = Timer(Duration(milliseconds: 50), () {
if (!mounted) return;
_physicalFocusNode.requestFocus();
});
});
}
// For client side
// When swithing from other app to this app, try to sync clipboard.
void trySyncClipboard() {