Skip to content

Commit 9f1b7bb

Browse files
author
liuchuancong
committed
fix(full screen)
1 parent 958a056 commit 9f1b7bb

2 files changed

Lines changed: 65 additions & 131 deletions

File tree

lib/player/utils/fullscreen.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class WindowService {
9595

9696
Future<void> doExitWindowFullScreen() async {
9797
if (Platform.isWindows) {
98-
WinFullscreen.exitFullscreen();
98+
WinFullscreen.exitSpecialMode();
9999
WinFullscreen.stopEscListener();
100100
return;
101101
}

lib/player/utils/win32_window.dart

Lines changed: 64 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,44 @@ import 'package:win32/win32.dart';
66

77
class 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

Comments
 (0)