fix(linux): guard window queries during close to prevent SIGSEGV

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) <noreply@anthropic.com>
This commit is contained in:
julian 2026-03-09 16:25:16 +08:00 committed by Julian
parent ac124c0680
commit f249d102ea
2 changed files with 30 additions and 7 deletions

View file

@ -1768,15 +1768,29 @@ Future<void> 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:

View file

@ -431,7 +431,16 @@ class _DesktopTabState extends State<DesktopTab>
@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");