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.
This commit is contained in:
Babib3l 2026-05-22 21:14:12 +02:00
parent 929c70003f
commit e6251d74a0
2 changed files with 54 additions and 3 deletions

View file

@ -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(

View file

@ -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()