1#[cfg(target_os = "windows")]
17pub(super) unsafe fn setup_native_frameless(hwnd: *mut std::ffi::c_void) {
18 use windows_sys::Win32::Graphics::Dwm::DwmExtendFrameIntoClientArea;
19 use windows_sys::Win32::UI::Controls::MARGINS;
20
21 let margins = MARGINS {
22 cxLeftWidth: -1,
23 cxRightWidth: -1,
24 cyTopHeight: -1,
25 cyBottomHeight: -1,
26 };
27 let result = DwmExtendFrameIntoClientArea(hwnd, &margins);
28 if result == 0 {
29 tracing::info!("Applied DwmExtendFrameIntoClientArea to hide resize border");
30 } else {
31 tracing::warn!("DwmExtendFrameIntoClientArea failed: hr={:#x}", result);
32 }
33}
34
35#[cfg(target_os = "windows")]
38static ORIGINAL_WNDPROCS: std::sync::LazyLock<
39 std::sync::Mutex<std::collections::HashMap<usize, isize>>,
40> = std::sync::LazyLock::new(|| std::sync::Mutex::new(std::collections::HashMap::new()));
41
42#[cfg(target_os = "windows")]
48static FOCUS_RESTORE_WNDPROCS: std::sync::LazyLock<
49 std::sync::Mutex<std::collections::HashMap<usize, isize>>,
50> = std::sync::LazyLock::new(|| std::sync::Mutex::new(std::collections::HashMap::new()));
51
52#[cfg(target_os = "windows")]
64pub(super) unsafe fn install_frameless_resize_hook(hwnd: *mut std::ffi::c_void) {
65 use windows_sys::Win32::UI::WindowsAndMessaging::*;
66
67 const RESIZE_BORDER: i32 = 6;
68
69 unsafe extern "system" fn wndproc_hook(
70 hwnd: *mut std::ffi::c_void,
71 msg: u32,
72 wparam: usize,
73 lparam: isize,
74 ) -> isize {
75 match msg {
76 WM_NCCALCSIZE if wparam == 1 => {
79 return 0;
82 }
83
84 WM_NCACTIVATE => {
87 return 1; }
89
90 WM_NCHITTEST => {
91 let x = (lparam & 0xFFFF) as i16 as i32;
92 let y = ((lparam >> 16) & 0xFFFF) as i16 as i32;
93
94 let mut rect = std::mem::zeroed::<windows_sys::Win32::Foundation::RECT>();
95 GetWindowRect(hwnd, &mut rect);
96
97 let left = x - rect.left < RESIZE_BORDER;
98 let right = rect.right - x < RESIZE_BORDER;
99 let top = y - rect.top < RESIZE_BORDER;
100 let bottom = rect.bottom - y < RESIZE_BORDER;
101
102 if top && left { return HTTOPLEFT as isize; }
103 if top && right { return HTTOPRIGHT as isize; }
104 if bottom && left { return HTBOTTOMLEFT as isize; }
105 if bottom && right { return HTBOTTOMRIGHT as isize; }
106 if left { return HTLEFT as isize; }
107 if right { return HTRIGHT as isize; }
108 if top { return HTTOP as isize; }
109 if bottom { return HTBOTTOM as isize; }
110 }
112
113 _ => {}
114 }
115
116 let key = hwnd as usize;
118 let original = ORIGINAL_WNDPROCS
119 .lock()
120 .ok()
121 .and_then(|map| map.get(&key).copied())
122 .unwrap_or(0);
123 if original != 0 {
124 CallWindowProcW(Some(std::mem::transmute(original)), hwnd, msg, wparam, lparam)
125 } else {
126 DefWindowProcW(hwnd, msg, wparam, lparam)
127 }
128 }
129
130 let original = GetWindowLongPtrW(hwnd, GWLP_WNDPROC);
131 if let Ok(mut map) = ORIGINAL_WNDPROCS.lock() {
132 map.insert(hwnd as usize, original);
133 }
134 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, wndproc_hook as isize);
135 tracing::info!("Installed frameless resize hook (WM_NCCALCSIZE + WM_NCHITTEST)");
136}
137
138#[cfg(target_os = "windows")]
155pub(crate) unsafe fn install_top_level_focus_restore_hook(hwnd: *mut std::ffi::c_void) {
156 use std::sync::atomic::Ordering;
157 use windows_sys::Win32::UI::Input::KeyboardAndMouse::SetFocus;
158 use windows_sys::Win32::UI::WindowsAndMessaging::{
159 CallWindowProcW, DefWindowProcW, IsWindow, SetWindowLongPtrW, GWLP_WNDPROC, WM_ACTIVATE,
160 };
161
162 const WA_INACTIVE: u32 = 0;
163
164 unsafe extern "system" fn wndproc_hook(
165 hwnd: *mut std::ffi::c_void,
166 msg: u32,
167 wparam: usize,
168 lparam: isize,
169 ) -> isize {
170 if msg == WM_ACTIVATE {
171 let activation_state = (wparam & 0xFFFF) as u32;
174 if activation_state != WA_INACTIVE {
175 let child = crate::browser_pane::hwnd::LAST_FOCUSED_BY_ROOT
178 .lock()
179 .ok()
180 .and_then(|m| m.get(&(hwnd as usize)).copied())
181 .unwrap_or(0);
182 if child != 0 {
183 let child_hwnd = child as *mut std::ffi::c_void;
184 if IsWindow(child_hwnd) != 0 {
185 crate::browser_pane::hwnd::ALLOW_BROWSER_PANE_FOCUS_ONCE
187 .store(true, Ordering::Relaxed);
188 SetFocus(child_hwnd);
189 tracing::info!(
190 "[focus-restore] WM_ACTIVATE root={:p} state={} -> SetFocus child={:p}",
191 hwnd, activation_state, child_hwnd,
192 );
193 } else {
194 tracing::info!(
195 "[focus-restore] WM_ACTIVATE root={:p} stale child={:p} (IsWindow=0) — no-op",
196 hwnd, child_hwnd,
197 );
198 }
199 } else {
200 tracing::info!(
201 "[focus-restore] WM_ACTIVATE root={:p} no recorded child — no-op",
202 hwnd,
203 );
204 }
205 }
206 }
207
208 let original = FOCUS_RESTORE_WNDPROCS
210 .lock()
211 .ok()
212 .and_then(|m| m.get(&(hwnd as usize)).copied())
213 .unwrap_or(0);
214 if original != 0 {
215 CallWindowProcW(Some(std::mem::transmute(original)), hwnd, msg, wparam, lparam)
216 } else {
217 DefWindowProcW(hwnd, msg, wparam, lparam)
218 }
219 }
220
221 let already_hooked = FOCUS_RESTORE_WNDPROCS
222 .lock()
223 .ok()
224 .map(|m| m.contains_key(&(hwnd as usize)))
225 .unwrap_or(false);
226 if already_hooked {
227 return;
228 }
229 let original = SetWindowLongPtrW(hwnd, GWLP_WNDPROC, wndproc_hook as *const () as isize);
230 if original != 0 {
231 if let Ok(mut map) = FOCUS_RESTORE_WNDPROCS.lock() {
232 map.insert(hwnd as usize, original);
233 }
234 tracing::info!(
235 "[focus-restore] installed WM_ACTIVATE observer on top-level HWND {:p}",
236 hwnd,
237 );
238 } else {
239 tracing::warn!(
240 "[focus-restore] SetWindowLongPtrW returned 0 for HWND {:p} — hook not installed",
241 hwnd,
242 );
243 }
244}
245
246#[cfg(target_os = "windows")]
258pub(super) unsafe fn skip_taskbar(hwnd: *mut std::ffi::c_void) {
259 use windows_sys::Win32::System::Com::{CoCreateInstance, CLSCTX_INPROC_SERVER};
260 use windows_sys::core::GUID;
261
262 const CLSID_TASKBAR_LIST: GUID = GUID {
264 data1: 0x56FDF344,
265 data2: 0xFD6D,
266 data3: 0x11D0,
267 data4: [0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90],
268 };
269 const IID_TASKBAR_LIST: GUID = GUID {
271 data1: 0x56FDF342,
272 data2: 0xFD6D,
273 data3: 0x11D0,
274 data4: [0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90],
275 };
276
277 #[repr(C)]
281 struct ITaskbarList {
282 lp_vtbl: *const ITaskbarListVtbl,
283 }
284 #[repr(C)]
285 struct ITaskbarListVtbl {
286 query_interface: unsafe extern "system" fn(*mut ITaskbarList, *const GUID, *mut *mut core::ffi::c_void) -> i32,
287 add_ref: unsafe extern "system" fn(*mut ITaskbarList) -> u32,
288 release: unsafe extern "system" fn(*mut ITaskbarList) -> u32,
289 hr_init: unsafe extern "system" fn(*mut ITaskbarList) -> i32,
290 add_tab: unsafe extern "system" fn(*mut ITaskbarList, *mut core::ffi::c_void) -> i32,
291 delete_tab: unsafe extern "system" fn(*mut ITaskbarList, *mut core::ffi::c_void) -> i32,
292 activate_tab: unsafe extern "system" fn(*mut ITaskbarList, *mut core::ffi::c_void) -> i32,
293 set_active_alt: unsafe extern "system" fn(*mut ITaskbarList, *mut core::ffi::c_void) -> i32,
294 }
295
296 let mut tbl: *mut ITaskbarList = std::ptr::null_mut();
297 let hr = CoCreateInstance(
298 &CLSID_TASKBAR_LIST as *const GUID,
299 std::ptr::null_mut(),
300 CLSCTX_INPROC_SERVER,
301 &IID_TASKBAR_LIST as *const GUID,
302 &mut tbl as *mut _ as *mut _,
303 );
304 if hr < 0 || tbl.is_null() {
305 tracing::warn!("[skip_taskbar] CoCreateInstance(TaskbarList) failed: hr=0x{:x}", hr);
306 return;
307 }
308
309 let vtbl = &*(*tbl).lp_vtbl;
310 let hr = (vtbl.hr_init)(tbl);
311 if hr < 0 {
312 tracing::warn!("[skip_taskbar] HrInit failed: hr=0x{:x}", hr);
313 (vtbl.release)(tbl);
314 return;
315 }
316 let hr = (vtbl.delete_tab)(tbl, hwnd);
317 if hr < 0 {
318 tracing::warn!("[skip_taskbar] DeleteTab failed: hr=0x{:x}", hr);
319 } else {
320 tracing::info!("[skip_taskbar] hid HWND {:p} from taskbar", hwnd);
321 }
322 (vtbl.release)(tbl);
323}
324
325#[cfg(target_os = "windows")]
329pub(super) unsafe fn set_window_icon(hwnd: *mut std::ffi::c_void) {
330 use windows_sys::Win32::UI::WindowsAndMessaging::*;
331 use windows_sys::Win32::System::LibraryLoader::GetModuleHandleW;
332
333 let hinstance = GetModuleHandleW(std::ptr::null());
334 if hinstance.is_null() {
335 tracing::warn!("set_window_icon: GetModuleHandleW returned null");
336 return;
337 }
338
339 let icon_big = LoadImageW(
341 hinstance,
342 1 as *const u16, IMAGE_ICON,
344 32, 32,
345 LR_SHARED,
346 );
347 if !icon_big.is_null() {
348 SendMessageW(hwnd, WM_SETICON, ICON_BIG as usize, icon_big as isize);
349 }
350
351 let icon_small = LoadImageW(
353 hinstance,
354 1 as *const u16,
355 IMAGE_ICON,
356 16, 16,
357 LR_SHARED,
358 );
359 if !icon_small.is_null() {
360 SendMessageW(hwnd, WM_SETICON, ICON_SMALL as usize, icon_small as isize);
361 }
362
363 if !icon_big.is_null() || !icon_small.is_null() {
364 tracing::info!("Set window icon from embedded resource");
365 } else {
366 tracing::warn!("set_window_icon: no icon found in exe resource");
367 }
368}