mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-05-11 15:30:35 +00:00
Fix Windows fullscreen gap when toggling from maximized (#80)
This PR aims to Fix the Windows fullscreen gap when toggling from maximized state by using canonical Win32 fullscreen approach. When entering fullscreen, the window style is saved and replaced with WS_POPUP | WS_VISIBLE to remove all window chrome (title bar, borders), then SetWindowPos sizes the window to cover the full screen with SWP_FRAMECHANGED to force Win32 to recalculate the frame. On exit, the original window style is restored. This eliminates the few-pixel gap at the top that occurred because Avalonia's WindowState = FullScreen transition from a maximized state retained the title bar non-client area, leaving visible space at the top of the screen (Fullscreen resolution would be 1920 x 1072p on a 1080p monitor, it now correctly renders at 1920 x 1080p) Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/80
This commit is contained in:
parent
e8cc252d9a
commit
bab160d650
2 changed files with 82 additions and 2 deletions
|
|
@ -8,6 +8,12 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||
internal partial class Win32NativeInterop
|
||||
{
|
||||
internal const int GWLP_WNDPROC = -4;
|
||||
internal const int GWL_STYLE = -16;
|
||||
internal const int GWL_EXSTYLE = -20;
|
||||
|
||||
internal const uint WS_OVERLAPPEDWINDOW = 0x00CF0000;
|
||||
internal const uint WS_POPUP = 0x80000000;
|
||||
internal const uint WS_VISIBLE = 0x10000000;
|
||||
|
||||
[Flags]
|
||||
public enum ClassStyles : uint
|
||||
|
|
@ -107,9 +113,29 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||
nint hInstance,
|
||||
nint lpParam);
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLongPtrW")]
|
||||
public static partial nint GetWindowLongPtrW(nint hWnd, int nIndex);
|
||||
|
||||
[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 SetWindowPos(
|
||||
nint hWnd,
|
||||
nint hWndInsertAfter,
|
||||
int x,
|
||||
int y,
|
||||
int cx,
|
||||
int cy,
|
||||
uint uFlags);
|
||||
|
||||
internal const uint SWP_NOZORDER = 0x0004;
|
||||
internal const uint SWP_NOACTIVATE = 0x0010;
|
||||
internal const uint SWP_FRAMECHANGED = 0x0020;
|
||||
internal const uint SWP_NOMOVE = 0x0002;
|
||||
internal const uint SWP_NOSIZE = 0x0001;
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
public static partial ushort GetAsyncKeyState(int nVirtKey);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Avalonia.Input;
|
|||
using Avalonia.Media;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using System.Runtime.Versioning;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using DynamicData;
|
||||
|
|
@ -2014,7 +2015,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
|
||||
LastFullscreenToggle = Environment.TickCount64;
|
||||
|
||||
if (WindowState is not WindowState.Normal)
|
||||
if (WindowState is WindowState.FullScreen)
|
||||
{
|
||||
WindowState = WindowState.Normal;
|
||||
Window.TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowOldUI;
|
||||
|
|
@ -2023,21 +2024,74 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
{
|
||||
ShowMenuAndStatusBar = true;
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
RestoreWindowFromFullscreen();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowState = WindowState.FullScreen;
|
||||
Window.TitleBar.ExtendsContentIntoTitleBar = true;
|
||||
|
||||
if (IsGameRunning)
|
||||
{
|
||||
ShowMenuAndStatusBar = false;
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
MakeWindowFullscreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowState = WindowState.FullScreen;
|
||||
}
|
||||
}
|
||||
|
||||
IsFullScreen = WindowState is WindowState.FullScreen;
|
||||
}
|
||||
|
||||
private nint _savedWindowStyle;
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
private void MakeWindowFullscreen()
|
||||
{
|
||||
nint hwnd = Window.TryGetPlatformHandle()?.Handle ?? nint.Zero;
|
||||
if (hwnd == nint.Zero) return;
|
||||
|
||||
// Save current style and placement
|
||||
_savedWindowStyle = Win32NativeInterop.GetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE);
|
||||
|
||||
// Remove window chrome: WS_OVERLAPPEDWINDOW -> WS_POPUP | WS_VISIBLE
|
||||
Win32NativeInterop.SetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE,
|
||||
unchecked((nint)(Win32NativeInterop.WS_POPUP | Win32NativeInterop.WS_VISIBLE)));
|
||||
|
||||
Avalonia.Platform.Screen? screen = Window.Screens.ScreenFromVisual(Window);
|
||||
int w = screen?.Bounds.Width ?? 0;
|
||||
int h = screen?.Bounds.Height ?? 0;
|
||||
|
||||
Win32NativeInterop.SetWindowPos(hwnd, nint.Zero, 0, 0, w, h,
|
||||
Win32NativeInterop.SWP_NOZORDER | Win32NativeInterop.SWP_NOACTIVATE | Win32NativeInterop.SWP_FRAMECHANGED);
|
||||
|
||||
WindowState = WindowState.FullScreen;
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
private void RestoreWindowFromFullscreen()
|
||||
{
|
||||
nint hwnd = Window.TryGetPlatformHandle()?.Handle ?? nint.Zero;
|
||||
if (hwnd == nint.Zero) return;
|
||||
|
||||
// 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);
|
||||
|
||||
}
|
||||
|
||||
public static void SaveConfig()
|
||||
{
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
|
|
|
|||
Loading…
Reference in a new issue