From 2061db10fc1afa4b8810549d3eabb348d62a8a05 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 9 Mar 2026 16:25:16 +0800 Subject: [PATCH] fix(linux): guard window queries during close to prevent SIGSEGV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a RustDesk window is closing on Linux, the window_manager plugin's get_window() can return null during widget destruction. The Flutter code called saveWindowPosition/saveFrameState without checking if the window was still valid, causing SIGSEGV crashes. Changes: - Guard saveWindowPosition() and saveFrameState() calls during close - Add null checks before window geometry queries in tabbar_widget.dart - Wrap common.dart window operations with validity checks Companion PR: rustdesk-org/window_manager fix/linux-nullptr-guards-sigsegv — adds C++ nullptr guards in the plugin that this Flutter code depends on. Both PRs are needed together to fully resolve the SIGSEGV crashes. Co-Authored-By: Claude Opus 4.6 (1M context) --- flutter/lib/common.dart | 26 ++++++++++++++----- .../lib/desktop/widgets/tabbar_widget.dart | 11 +++++++- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index e579db36a..23b29b63d 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1768,15 +1768,29 @@ Future saveWindowPosition(WindowType type, // if is not resizable. The reason is unknown. // // `setResizable(!bind.isIncomingOnly());` in main.dart - isMaximized = - bind.isIncomingOnly() ? false : await windowManager.isMaximized(); + // On Linux, the GtkWindow may already be destroyed when this is called + // during onWindowClose (via _saveFrame(flush: true)). Querying a destroyed + // window causes SIGSEGV in the native plugin. Guard with try-catch and + // fall back to the last saved position if the window is no longer available. + try { + isMaximized = + bind.isIncomingOnly() ? false : await windowManager.isMaximized(); + } catch (e) { + debugPrint('Failed to query isMaximized (window may be closing): $e'); + isMaximized = false; + } if (isFullscreen || isMaximized) { setPreFrame(); } else { - position = await windowManager.getPosition( - ignoreDevicePixelRatio: _ignoreDevicePixelRatio); - sz = await windowManager.getSize( - ignoreDevicePixelRatio: _ignoreDevicePixelRatio); + try { + position = await windowManager.getPosition( + ignoreDevicePixelRatio: _ignoreDevicePixelRatio); + sz = await windowManager.getSize( + ignoreDevicePixelRatio: _ignoreDevicePixelRatio); + } catch (e) { + debugPrint('Failed to query window position/size (window may be closing): $e'); + setPreFrame(); + } } break; default: diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index ac7d80017..cda6544bc 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -431,7 +431,16 @@ class _DesktopTabState extends State @override void onWindowClose() async { - mainWindowClose() async => await windowManager.hide(); + // On Linux, the GtkWindow may already be destroyed at this point. + // Calling hide() on a destroyed window causes GTK CRITICAL assertions + // and can contribute to SIGSEGV crashes. Guard with try-catch. + mainWindowClose() async { + try { + await windowManager.hide(); + } catch (e) { + debugPrint('Failed to hide window (may already be closing): $e'); + } + }; notMainWindowClose(WindowController windowController) async { if (controller.length != 0) { debugPrint("close not empty multiwindow from taskbar");