Skip to main content

rdesktop_core/
window_extras.rs

1//! Platform-specific window attributes for deep desktop scenarios.
2//!
3//! Provides [`apply_window_attributes`], called right after a tao window is
4//! built. It realizes the `Wallpaper`/`Overlay` window kinds and click-through
5//! requested in [`crate::config::WindowConfig`].
6//!
7//! - **Click-through**: the window ignores pointer input, which falls through
8//!   to whatever is behind it. Required for wallpaper; optional for overlays.
9//! - **Desktop layer**: the window is reparented beneath the desktop icons
10//!   (Windows: `WorkerW`; macOS: `kCGDesktopWindowLevel`) so it behaves like
11//!   a Wallpaper-Engine-style background.
12
13use crate::config::{WindowConfig, WindowKind};
14use tao::window::Window;
15
16/// Apply `config.kind` / `config.click_through` to a freshly built window.
17pub fn apply_window_attributes(window: &Window, config: &WindowConfig) {
18    let click_through = config.click_through || config.kind == WindowKind::Wallpaper;
19    let is_wallpaper = config.kind == WindowKind::Wallpaper;
20
21    #[cfg(target_os = "windows")]
22    windows::apply(window, click_through, is_wallpaper);
23    #[cfg(target_os = "macos")]
24    macos::apply(window, click_through, is_wallpaper);
25    #[cfg(not(any(target_os = "windows", target_os = "macos")))]
26    {
27        let _ = (window, click_through, is_wallpaper);
28        tracing::debug!("click-through / wallpaper layer not implemented on this platform");
29    }
30}
31
32#[cfg(target_os = "windows")]
33mod windows {
34    use std::ptr;
35
36    use tao::platform::windows::WindowExtWindows;
37    use tao::window::Window;
38    use windows_sys::Win32::Foundation::{BOOL, HWND, LPARAM};
39    use windows_sys::Win32::UI::WindowsAndMessaging::{
40        EnumWindows, FindWindowExW, FindWindowW, GetWindowLongPtrW, SendMessageTimeoutW, SetParent,
41        SetWindowLongPtrW, SetWindowPos, GWL_EXSTYLE, HWND_BOTTOM, SMTO_ABORTIFHUNG,
42        SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, WS_EX_LAYERED, WS_EX_TRANSPARENT,
43    };
44
45    pub(crate) fn apply(window: &Window, click_through: bool, is_wallpaper: bool) {
46        let hwnd = window.hwnd() as HWND;
47        if hwnd == 0 {
48            return;
49        }
50        unsafe {
51            if click_through {
52                let ex = GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
53                let new_ex = ex | (WS_EX_LAYERED as isize) | (WS_EX_TRANSPARENT as isize);
54                SetWindowLongPtrW(hwnd, GWL_EXSTYLE, new_ex);
55            }
56            if is_wallpaper {
57                if let Some(worker) = find_desktop_workerw() {
58                    SetParent(hwnd, worker);
59                }
60                SetWindowPos(
61                    hwnd,
62                    HWND_BOTTOM,
63                    0,
64                    0,
65                    0,
66                    0,
67                    SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE,
68                );
69            }
70        }
71    }
72
73    /// Locate the desktop `WorkerW` window (the host behind the desktop icons)
74    /// by asking Explorer to (re)create it and then finding the sibling
75    /// `WorkerW` that does not own `SHELLDLL_DefView`.
76    unsafe fn find_desktop_workerw() -> Option<HWND> {
77        let progman = FindWindowW(windows_sys::w!("Progman"), ptr::null::<u16>());
78        if progman == 0 {
79            return None;
80        }
81        // Prompt Explorer to create a dedicated wallpaper WorkerW host.
82        SendMessageTimeoutW(
83            progman,
84            0x52C,
85            0,
86            0,
87            SMTO_ABORTIFHUNG,
88            1000,
89            ptr::null_mut::<usize>(),
90        );
91        let mut worker: HWND = 0;
92        EnumWindows(Some(enum_proc), &mut worker as *mut _ as LPARAM);
93        if worker == 0 {
94            None
95        } else {
96            Some(worker)
97        }
98    }
99
100    extern "system" fn enum_proc(hwnd: HWND, lparam: LPARAM) -> BOOL {
101        unsafe {
102            let defview = FindWindowExW(
103                hwnd,
104                0,
105                windows_sys::w!("SHELLDLL_DefView"),
106                ptr::null::<u16>(),
107            );
108            if defview != 0 {
109                let pworker = &mut *(lparam as *mut HWND);
110                *pworker = FindWindowExW(0, hwnd, windows_sys::w!("WorkerW"), ptr::null::<u16>());
111            }
112            windows_sys::Win32::Foundation::TRUE
113        }
114    }
115}
116
117#[cfg(target_os = "macos")]
118mod macos {
119    use core_graphics::window::{CGWindowLevelForKey, CGWindowLevelKey};
120    use objc::{msg_send, runtime::Object};
121    use tao::platform::macos::WindowExtMacOS;
122    use tao::window::Window;
123
124    pub(crate) fn apply(window: &Window, click_through: bool, is_wallpaper: bool) {
125        let ns_win = window.ns_window() as *mut Object;
126        if ns_win.is_null() {
127            return;
128        }
129        unsafe {
130            let _: () = msg_send![ns_win, setIgnoresMouseEvents: click_through || is_wallpaper];
131            if is_wallpaper {
132                let level: i64 =
133                    CGWindowLevelForKey(CGWindowLevelKey::kCGDesktopWindowLevelKey) as i64;
134                let _: () = msg_send![ns_win, setLevel: level];
135                let _: () = msg_send![ns_win, orderBack: ns_win];
136            }
137        }
138    }
139}