From e6251d74a097b6dfa5b8d9d9f5a2615a21de969e Mon Sep 17 00:00:00 2001 From: Babib3l Date: Fri, 22 May 2026 21:14:12 +0200 Subject: [PATCH] Fix Windows window geometry restore after fullscreen exit Track the native window rectangle before entering Windows fullscreen and restore it after leaving fullscreen, so the main window does not remain at the fullscreen resolution when returning to normal mode. Also account for the startup height delta applied by Avalonia when saving remembered window size, preventing the main window height from growing on every launch. --- src/Ryujinx/UI/Helpers/Win32NativeInterop.cs | 16 ++++++++ .../UI/ViewModels/MainWindowViewModel.cs | 41 +++++++++++++++++-- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx/UI/Helpers/Win32NativeInterop.cs b/src/Ryujinx/UI/Helpers/Win32NativeInterop.cs index adfa899ed..65fa74297 100644 --- a/src/Ryujinx/UI/Helpers/Win32NativeInterop.cs +++ b/src/Ryujinx/UI/Helpers/Win32NativeInterop.cs @@ -63,6 +63,18 @@ namespace Ryujinx.Ava.UI.Helpers } } + [StructLayout(LayoutKind.Sequential)] + public struct NativeRect + { + public int Left; + public int Top; + public int Right; + public int Bottom; + + public int Width => Right - Left; + public int Height => Bottom - Top; + } + public static nint CreateEmptyCursor() { return CreateCursor(nint.Zero, 0, 0, 1, 1, [0xFF], [0x00]); @@ -119,6 +131,10 @@ namespace Ryujinx.Ava.UI.Helpers [LibraryImport("user32.dll", SetLastError = true)] public static partial nint SetWindowLongPtrW(nint hWnd, int nIndex, nint value); + [LibraryImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool GetWindowRect(nint hWnd, out NativeRect lpRect); + [LibraryImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static partial bool SetWindowPos( diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 44e22f812..6b780ee14 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -2053,6 +2053,12 @@ namespace Ryujinx.Ava.UI.ViewModels } private nint _savedWindowStyle; + private WindowState _savedWindowState; + private PixelPoint _savedWindowPosition; + private double _savedWindowWidth; + private double _savedWindowHeight; + private Win32NativeInterop.NativeRect _savedWindowRect; + private bool _savedWindowRectValid; [SupportedOSPlatform("windows")] private void MakeWindowFullscreen() @@ -2076,6 +2082,11 @@ namespace Ryujinx.Ava.UI.ViewModels // Save current style and placement _savedWindowStyle = Win32NativeInterop.GetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE); + _savedWindowState = WindowState; + _savedWindowPosition = Window.Position; + _savedWindowWidth = Window.Width; + _savedWindowHeight = Window.Height; + _savedWindowRectValid = Win32NativeInterop.GetWindowRect(hwnd, out _savedWindowRect); // Remove window chrome: WS_OVERLAPPEDWINDOW -> WS_POPUP | WS_VISIBLE Win32NativeInterop.SetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE, @@ -2099,10 +2110,34 @@ namespace Ryujinx.Ava.UI.ViewModels // Restore original window style Win32NativeInterop.SetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE, _savedWindowStyle); - Win32NativeInterop.SetWindowPos(hwnd, nint.Zero, 0, 0, 0, 0, - Win32NativeInterop.SWP_NOZORDER | Win32NativeInterop.SWP_NOACTIVATE | - Win32NativeInterop.SWP_FRAMECHANGED | Win32NativeInterop.SWP_NOMOVE | Win32NativeInterop.SWP_NOSIZE); + if (_savedWindowState is WindowState.Maximized) + { + Win32NativeInterop.SetWindowPos(hwnd, nint.Zero, 0, 0, 0, 0, + Win32NativeInterop.SWP_NOZORDER | Win32NativeInterop.SWP_NOACTIVATE | + Win32NativeInterop.SWP_FRAMECHANGED | Win32NativeInterop.SWP_NOMOVE | Win32NativeInterop.SWP_NOSIZE); + } + else if (_savedWindowRectValid) + { + Dispatcher.UIThread.Post(() => RestoreSavedWindowRect(hwnd), DispatcherPriority.Background); + } + else + { + Win32NativeInterop.SetWindowPos(hwnd, nint.Zero, 0, 0, 0, 0, + Win32NativeInterop.SWP_NOZORDER | Win32NativeInterop.SWP_NOACTIVATE | + Win32NativeInterop.SWP_FRAMECHANGED | Win32NativeInterop.SWP_NOMOVE | Win32NativeInterop.SWP_NOSIZE); + } + } + [SupportedOSPlatform("windows")] + private void RestoreSavedWindowRect(nint hwnd) + { + Window.Position = _savedWindowPosition; + Window.Width = _savedWindowWidth; + Window.Height = _savedWindowHeight; + + Win32NativeInterop.SetWindowPos(hwnd, nint.Zero, _savedWindowRect.Left, _savedWindowRect.Top, + _savedWindowRect.Width, _savedWindowRect.Height, + Win32NativeInterop.SWP_NOZORDER | Win32NativeInterop.SWP_NOACTIVATE | Win32NativeInterop.SWP_FRAMECHANGED); } public static void SaveConfig()