#![cfg(target_os = "windows")]
#![allow(clippy::upper_case_acronyms)]
use std::ffi::c_void;
use std::sync::{Mutex, Once};
type BOOL = i32;
type HWND = *mut c_void;
#[repr(C)]
#[derive(Default)]
struct RECT {
left: i32,
top: i32,
right: i32,
bottom: i32,
}
#[repr(C)]
struct POINT {
x: i32,
y: i32,
}
#[repr(C)]
struct MSG {
hwnd: HWND,
message: u32,
w_param: usize,
l_param: isize,
time: u32,
pt: POINT,
private: u32,
}
type WndProc = unsafe extern "system" fn(HWND, u32, usize, isize) -> isize;
#[repr(C)]
struct WNDCLASSW {
style: u32,
lpfn_wnd_proc: WndProc,
cb_cls_extra: i32,
cb_wnd_extra: i32,
h_instance: *mut c_void,
h_icon: *mut c_void,
h_cursor: *mut c_void,
hbr_background: *mut c_void,
lpsz_menu_name: *const u16,
lpsz_class_name: *const u16,
}
#[link(name = "user32")]
unsafe extern "system" {
fn FindWindowW(lpClassName: *const u16, lpWindowName: *const u16) -> HWND;
fn FindWindowExW(
hWndParent: HWND,
hWndChildAfter: HWND,
lpszClass: *const u16,
lpszWindow: *const u16,
) -> HWND;
fn SendMessageTimeoutW(
hWnd: HWND,
Msg: u32,
wParam: usize,
lParam: isize,
fuFlags: u32,
uTimeout: u32,
lpdwResult: *mut usize,
) -> isize;
fn SetParent(hWndChild: HWND, hWndNewParent: HWND) -> HWND;
fn EnumWindows(
lpEnumFunc: unsafe extern "system" fn(HWND, isize) -> BOOL,
lParam: isize,
) -> BOOL;
fn IsWindow(hWnd: HWND) -> BOOL;
fn GetWindowRect(hWnd: HWND, lpRect: *mut RECT) -> BOOL;
fn ScreenToClient(hWnd: HWND, lpPoint: *mut POINT) -> BOOL;
fn SetWindowPos(
hWnd: HWND,
hWndInsertAfter: HWND,
X: i32,
Y: i32,
cx: i32,
cy: i32,
uFlags: u32,
) -> BOOL;
fn RegisterWindowMessageW(lpString: *const u16) -> u32;
fn RegisterClassW(lpWndClass: *const WNDCLASSW) -> u16;
fn CreateWindowExW(
dwExStyle: u32,
lpClassName: *const u16,
lpWindowName: *const u16,
dwStyle: u32,
X: i32,
Y: i32,
nWidth: i32,
nHeight: i32,
hWndParent: HWND,
hMenu: *mut c_void,
hInstance: *mut c_void,
lpParam: *mut c_void,
) -> HWND;
fn DefWindowProcW(hWnd: HWND, Msg: u32, wParam: usize, lParam: isize) -> isize;
fn GetMessageW(lpMsg: *mut MSG, hWnd: HWND, wMsgFilterMin: u32, wMsgFilterMax: u32) -> BOOL;
fn DispatchMessageW(lpMsg: *const MSG) -> isize;
}
#[link(name = "kernel32")]
unsafe extern "system" {
fn GetLastError() -> u32;
fn SetLastError(dwErrCode: u32);
fn GetModuleHandleW(lpModuleName: *const u16) -> *mut c_void;
}
const SMTO_NORMAL: u32 = 0;
const WM_SPAWN_WORKER: u32 = 0x052C;
const SWP_NOSIZE: u32 = 0x0001;
const SWP_NOZORDER: u32 = 0x0004;
const SWP_NOACTIVATE: u32 = 0x0010;
const WS_EX_TOOLWINDOW: u32 = 0x0000_0080;
const WS_POPUP: u32 = 0x8000_0000;
static ATTACHED: Mutex<Vec<isize>> = Mutex::new(Vec::new());
static WATCHER: Once = Once::new();
fn wide(s: &str) -> Vec<u16> {
s.encode_utf16().chain(std::iter::once(0)).collect()
}
unsafe extern "system" fn enum_windows_proc(hwnd: HWND, lparam: isize) -> BOOL {
let workerw = &mut *(lparam as *mut HWND);
let shelldll = wide("SHELLDLL_DefView");
let def = FindWindowExW(
hwnd,
std::ptr::null_mut(),
shelldll.as_ptr(),
std::ptr::null(),
);
if !def.is_null() {
let worker = wide("WorkerW");
*workerw = FindWindowExW(
std::ptr::null_mut(),
hwnd,
worker.as_ptr(),
std::ptr::null(),
);
return 0;
}
1
}
fn desktop_parent() -> Option<(HWND, HWND)> {
unsafe {
let progman_class = wide("Progman");
let progman = FindWindowW(progman_class.as_ptr(), std::ptr::null());
if progman.is_null() {
return None;
}
let mut result: usize = 0;
SendMessageTimeoutW(
progman,
WM_SPAWN_WORKER,
0xD,
0x1,
SMTO_NORMAL,
1000,
&mut result,
);
let shelldll = wide("SHELLDLL_DefView");
let defview = FindWindowExW(
progman,
std::ptr::null_mut(),
shelldll.as_ptr(),
std::ptr::null(),
);
if !defview.is_null() {
return Some((progman, defview));
}
let mut workerw: HWND = std::ptr::null_mut();
EnumWindows(enum_windows_proc, &mut workerw as *mut HWND as isize);
(!workerw.is_null()).then_some((workerw, std::ptr::null_mut()))
}
}
pub fn find_workerw() -> Option<isize> {
match desktop_parent()? {
(worker, after) if after.is_null() => Some(worker as isize),
_ => None,
}
}
fn reparent(hwnd: HWND) -> crate::Result<()> {
let (parent, after) = desktop_parent()
.ok_or_else(|| crate::Error::new("desktop layer: Progman / WorkerW not found"))?;
unsafe {
let mut rect = RECT::default();
GetWindowRect(hwnd, &mut rect);
SetLastError(0);
if SetParent(hwnd, parent).is_null() && GetLastError() != 0 {
return Err(crate::Error::new(format!(
"desktop layer: SetParent failed ({})",
GetLastError()
)));
}
let mut pt = POINT {
x: rect.left,
y: rect.top,
};
ScreenToClient(parent, &mut pt);
let z = if after.is_null() { SWP_NOZORDER } else { 0 };
SetWindowPos(
hwnd,
after,
pt.x,
pt.y,
0,
0,
SWP_NOSIZE | SWP_NOACTIVATE | z,
);
}
Ok(())
}
pub fn attach_to_desktop(hwnd: isize) -> crate::Result<()> {
reparent(hwnd as HWND)?;
{
let mut list = ATTACHED.lock().unwrap();
if !list.contains(&hwnd) {
list.push(hwnd);
}
}
WATCHER.call_once(|| {
std::thread::spawn(watch_explorer);
});
Ok(())
}
#[deprecated(note = "use attach_to_desktop")]
pub fn attach_to_workerw(child_hwnd: isize) -> bool {
attach_to_desktop(child_hwnd).is_ok()
}
pub fn attach_window<R: tauri::Runtime>(win: &tauri::WebviewWindow<R>) -> crate::Result<()> {
let hwnd = win
.hwnd()
.map_err(|e| crate::Error::new(format!("desktop layer: hwnd: {e}")))?;
attach_to_desktop(hwnd.0 as isize)
}
static TASKBAR_CREATED: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
unsafe extern "system" fn watcher_proc(hwnd: HWND, msg: u32, w: usize, l: isize) -> isize {
let tc = TASKBAR_CREATED.load(std::sync::atomic::Ordering::Relaxed);
if tc != 0 && msg == tc {
let live: Vec<isize> = {
let mut list = ATTACHED.lock().unwrap();
list.retain(|&h| IsWindow(h as HWND) != 0);
list.clone()
};
for h in live {
if let Err(e) = reparent(h as HWND) {
log::warn!("desktop layer re-attach after Explorer restart: {e}");
}
}
return 0;
}
DefWindowProcW(hwnd, msg, w, l)
}
fn watch_explorer() {
unsafe {
TASKBAR_CREATED.store(
RegisterWindowMessageW(wide("TaskbarCreated").as_ptr()),
std::sync::atomic::Ordering::Relaxed,
);
let class = wide("TauriWidgetsExplorerWatch");
let instance = GetModuleHandleW(std::ptr::null());
let wc = WNDCLASSW {
style: 0,
lpfn_wnd_proc: watcher_proc,
cb_cls_extra: 0,
cb_wnd_extra: 0,
h_instance: instance,
h_icon: std::ptr::null_mut(),
h_cursor: std::ptr::null_mut(),
hbr_background: std::ptr::null_mut(),
lpsz_menu_name: std::ptr::null(),
lpsz_class_name: class.as_ptr(),
};
RegisterClassW(&wc);
let hwnd = CreateWindowExW(
WS_EX_TOOLWINDOW,
class.as_ptr(),
class.as_ptr(),
WS_POPUP,
0,
0,
0,
0,
std::ptr::null_mut(),
std::ptr::null_mut(),
instance,
std::ptr::null_mut(),
);
if hwnd.is_null() {
log::warn!(
"desktop layer: Explorer watcher window failed ({})",
GetLastError()
);
return;
}
let mut msg: MSG = std::mem::zeroed();
while GetMessageW(&mut msg, std::ptr::null_mut(), 0, 0) > 0 {
DispatchMessageW(&msg);
}
}
}