@@ -6,24 +6,44 @@ import 'package:win32/win32.dart';
66
77class WinFullscreen {
88 static int ? _hwnd;
9-
109 static Timer ? _escListener;
1110
12- /// 缓存进入全屏前的窗口状态
11+ /// Cache for restoring window state
1312 static int _originalX = 0 ;
1413 static int _originalY = 0 ;
1514 static int _originalWidth = 800 ;
1615 static int _originalHeight = 600 ;
17-
1816 static bool _originalMaximized = false ;
1917 static bool _originalSaved = false ;
2018
19+ // ignore: constant_identifier_names
20+ static const DWMWA_WINDOW_CORNER_PREFERENCE = 33 ;
21+ // ignore: constant_identifier_names
22+ static const DWMWCP_DONOTROUND = 1 ;
23+ // ignore: constant_identifier_names
24+ static const DWMWA_USE_IMMERSIVE_DARK_MODE = 20 ;
25+
26+ /// HELPER: Detect if the OS is Windows 11 (Build 22000+)
27+ static bool get isWindows11 {
28+ if (! Platform .isWindows) return false ;
29+
30+ final versionInfo = calloc <OSVERSIONINFOEX >()..ref.dwOSVersionInfoSize = sizeOf <OSVERSIONINFOEX >();
31+
32+ try {
33+ if (GetVersionEx (versionInfo.cast ()) != 0 ) {
34+ // Windows 11 is version 10.0 with Build >= 22000
35+ return versionInfo.ref.dwMajorVersion == 10 && versionInfo.ref.dwBuildNumber >= 22000 ;
36+ }
37+ } finally {
38+ calloc.free (versionInfo);
39+ }
40+ return false ;
41+ }
42+
2143 static void startEscListener (Function onEsc) {
2244 _escListener? .cancel ();
23-
2445 _escListener = Timer .periodic (const Duration (milliseconds: 100 ), (timer) {
25- final state = GetAsyncKeyState (VK_ESCAPE );
26-
46+ int state = GetAsyncKeyState (0x1B ); // VK_ESCAPE
2747 if ((state & 0x8000 ) != 0 ) {
2848 timer.cancel ();
2949 onEsc ();
@@ -40,206 +60,121 @@ class WinFullscreen {
4060 return _hwnd! ;
4161 }
4262
43- /// 保存进入全屏前的窗口状态
44- static void saveOriginalWindowRect () {
45- if (! Platform .isWindows || _originalSaved) {
46- return ;
63+ /// Adaptive UI Fix for Win10 vs Win11
64+ static void _applyVersionSpecificFix (int hWnd, {bool isFullscreen = true }) {
65+ final pvAttribute = calloc <Int32 >();
66+
67+ if (isWindows11) {
68+ pvAttribute.value = isFullscreen ? DWMWCP_DONOTROUND : 0 ;
69+ DwmSetWindowAttribute (hWnd, DWMWA_WINDOW_CORNER_PREFERENCE , pvAttribute, sizeOf <Int32 >());
4770 }
4871
49- final hWnd = _getHwnd ();
72+ pvAttribute.value = 1 ;
73+ DwmSetWindowAttribute (hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE , pvAttribute, sizeOf <Int32 >());
5074
51- /// 是否最大化
52- _originalMaximized = IsZoomed (hWnd) != 0 ;
75+ calloc. free (pvAttribute);
76+ }
5377
78+ static void saveOriginalWindowRect () {
79+ if (! Platform .isWindows || _originalSaved) return ;
80+ final hWnd = _getHwnd ();
81+ _originalMaximized = IsZoomed (hWnd) != 0 ;
5482 final rect = calloc <RECT >();
55-
5683 if (GetWindowRect (hWnd, rect) != 0 ) {
5784 _originalX = rect.ref.left;
5885 _originalY = rect.ref.top;
59-
6086 _originalWidth = rect.ref.right - rect.ref.left;
61-
6287 _originalHeight = rect.ref.bottom - rect.ref.top;
63-
6488 _originalSaved = true ;
6589 }
66-
6790 calloc.free (rect);
6891 }
6992
70- /// 真正无边框全屏(修复 Win11 白边/毛刺)
7193 static void enterFullscreen () {
72- if (! Platform .isWindows) {
73- return ;
74- }
75-
94+ if (! Platform .isWindows) return ;
7695 final hWnd = _getHwnd ();
77-
7896 saveOriginalWindowRect ();
7997
80- /// 去除窗口边框
8198 int style = GetWindowLongPtr (hWnd, GWL_STYLE );
82-
83- style & = ~ WS_CAPTION ;
84- style & = ~ WS_THICKFRAME ;
85- style & = ~ WS_SYSMENU ;
86- style & = ~ WS_MINIMIZEBOX ;
87- style & = ~ WS_MAXIMIZEBOX ;
88-
99+ style & = ~ (WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU );
89100 style | = WS_POPUP ;
90-
91101 SetWindowLongPtr (hWnd, GWL_STYLE , style);
92102
93- /// 去除扩展边框
94103 int exStyle = GetWindowLongPtr (hWnd, GWL_EXSTYLE );
95-
96- exStyle & = ~ WS_EX_WINDOWEDGE ;
97- exStyle & = ~ WS_EX_DLGMODALFRAME ;
98- exStyle & = ~ WS_EX_CLIENTEDGE ;
99- exStyle & = ~ WS_EX_STATICEDGE ;
100-
104+ exStyle & = ~ (WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_DLGMODALFRAME );
101105 SetWindowLongPtr (hWnd, GWL_EXSTYLE , exStyle);
102106
103- final monitor = MonitorFromWindow (hWnd, MONITOR_DEFAULTTONEAREST );
107+ // Apply specific fixes based on detected version
108+ _applyVersionSpecificFix (hWnd, isFullscreen: true );
104109
110+ final monitor = MonitorFromWindow (hWnd, MONITOR_DEFAULTTONEAREST );
105111 final info = calloc <MONITORINFO >()..ref.cbSize = sizeOf <MONITORINFO >();
106-
107112 GetMonitorInfo (monitor, info);
108113
109- /// 修复 Win11 fullscreen 白边毛刺
110- ///
111- /// fullscreen 实际缩进去 1px
112- /// 避免 DWM / SwapChain 边缘抗锯齿问题
113-
114114 SetWindowPos (
115115 hWnd,
116116 HWND_TOP ,
117117 info.ref.rcMonitor.left,
118118 info.ref.rcMonitor.top,
119119 info.ref.rcMonitor.right - info.ref.rcMonitor.left,
120120 info.ref.rcMonitor.bottom - info.ref.rcMonitor.top,
121- SWP_SHOWWINDOW | SWP_FRAMECHANGED ,
121+ SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOSENDCHANGING ,
122122 );
123-
124- ShowWindow (hWnd, SW_SHOW );
125-
126123 SetForegroundWindow (hWnd);
127-
128124 SetFocus (hWnd);
129-
130125 calloc.free (info);
131126 }
132127
133- /// 退出全屏
134- static void exitFullscreen () {
135- if (! Platform .isWindows) {
136- return ;
137- }
138-
139- final hWnd = _getHwnd ();
140-
141- /// 恢复窗口样式
142- int style = GetWindowLongPtr (hWnd, GWL_STYLE );
143-
144- style | = WS_CAPTION ;
145- style | = WS_THICKFRAME ;
146- style | = WS_SYSMENU ;
147- style | = WS_MINIMIZEBOX ;
148- style | = WS_MAXIMIZEBOX ;
128+ static void exitFullscreen () => exitSpecialMode ();
149129
150- style & = ~ WS_POPUP ;
151-
152- SetWindowLongPtr (hWnd, GWL_STYLE , style);
153-
154- /// 恢复扩展样式
155- int exStyle = GetWindowLongPtr (hWnd, GWL_EXSTYLE );
156-
157- exStyle | = WS_EX_WINDOWEDGE ;
158-
159- SetWindowLongPtr (hWnd, GWL_EXSTYLE , exStyle);
160-
161- if (_originalMaximized) {
162- ShowWindow (hWnd, SW_MAXIMIZE );
163- } else {
164- SetWindowPos (
165- hWnd,
166- HWND_TOP ,
167- _originalX,
168- _originalY,
169- _originalWidth,
170- _originalHeight,
171- SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOZORDER ,
172- );
173- }
174-
175- _originalSaved = false ;
176- }
177-
178- /// 进入 PiP 模式
179130 static void enterPipMode ({required double width, required double height, required double x, required double y}) {
180- if (! Platform .isWindows) {
181- return ;
182- }
183-
131+ if (! Platform .isWindows) return ;
184132 final hWnd = _getHwnd ();
185-
186133 saveOriginalWindowRect ();
187134
188135 int style = GetWindowLongPtr (hWnd, GWL_STYLE );
189-
190- style & = ~ WS_CAPTION ;
191- style & = ~ WS_THICKFRAME ;
192- style & = ~ WS_SYSMENU ;
193- style & = ~ WS_MAXIMIZEBOX ;
194- style & = ~ WS_MINIMIZEBOX ;
195-
136+ style & = ~ (WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU );
196137 style | = WS_POPUP ;
197-
198138 SetWindowLongPtr (hWnd, GWL_STYLE , style);
199139
200140 int exStyle = GetWindowLongPtr (hWnd, GWL_EXSTYLE );
201-
202141 exStyle | = WS_EX_TOPMOST ;
203-
204142 SetWindowLongPtr (hWnd, GWL_EXSTYLE , exStyle);
205143
144+ _applyVersionSpecificFix (hWnd, isFullscreen: false );
145+
206146 SetWindowPos (
207147 hWnd,
208148 HWND_TOPMOST ,
209149 x.toInt (),
210150 y.toInt (),
211151 width.toInt (),
212152 height.toInt (),
213- SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOACTIVATE ,
153+ SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOSENDCHANGING ,
214154 );
215155 }
216156
217- /// 退出 PiP / 特殊模式
218157 static void exitSpecialMode () {
219- if (! Platform .isWindows) {
220- return ;
221- }
222-
158+ if (! Platform .isWindows || ! _originalSaved) return ;
223159 final hWnd = _getHwnd ();
224160
225161 int style = GetWindowLongPtr (hWnd, GWL_STYLE );
226-
227- style | = WS_CAPTION ;
228- style | = WS_THICKFRAME ;
229- style | = WS_SYSMENU ;
230- style | = WS_MINIMIZEBOX ;
231- style | = WS_MAXIMIZEBOX ;
232-
162+ style | = (WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU );
233163 style & = ~ WS_POPUP ;
234-
235164 SetWindowLongPtr (hWnd, GWL_STYLE , style);
236165
237166 int exStyle = GetWindowLongPtr (hWnd, GWL_EXSTYLE );
238-
239167 exStyle & = ~ WS_EX_TOPMOST ;
240-
168+ exStyle | = WS_EX_WINDOWEDGE ;
241169 SetWindowLongPtr (hWnd, GWL_EXSTYLE , exStyle);
242170
171+ // Restore default rounding on exit (Win11 only)
172+ if (isWindows11) {
173+ final pvAttribute = calloc <Int32 >()..value = 0 ; // DWMWCP_DEFAULT
174+ DwmSetWindowAttribute (hWnd, DWMWA_WINDOW_CORNER_PREFERENCE , pvAttribute, sizeOf <Int32 >());
175+ calloc.free (pvAttribute);
176+ }
177+
243178 if (_originalMaximized) {
244179 ShowWindow (hWnd, SW_MAXIMIZE );
245180 } else {
@@ -253,7 +188,6 @@ class WinFullscreen {
253188 SWP_SHOWWINDOW | SWP_FRAMECHANGED ,
254189 );
255190 }
256-
257191 _originalSaved = false ;
258192 }
259193}
0 commit comments