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::{Icon, Window};
15
16/// Convert a framework icon into the platform window icon type.
17pub fn window_icon(config: &WindowConfig) -> Option<Icon> {
18    let icon = config.icon.as_ref()?;
19    match Icon::from_rgba(icon.rgba.clone(), icon.width, icon.height) {
20        Ok(icon) => Some(icon),
21        Err(error) => {
22            tracing::warn!(%error, "invalid rdesktop window icon; continuing without icon");
23            None
24        }
25    }
26}
27
28/// Apply `config.kind` / `config.click_through` to a freshly built window.
29pub fn apply_window_attributes(window: &Window, config: &WindowConfig) {
30    let click_through = config.click_through || config.kind == WindowKind::Wallpaper;
31    let is_wallpaper = config.kind == WindowKind::Wallpaper;
32
33    #[cfg(target_os = "windows")]
34    windows::apply(window, click_through, is_wallpaper);
35    #[cfg(target_os = "macos")]
36    macos::apply(window, click_through, is_wallpaper);
37    #[cfg(not(any(target_os = "windows", target_os = "macos")))]
38    {
39        let _ = (window, click_through, is_wallpaper);
40        tracing::debug!("click-through / wallpaper layer not implemented on this platform");
41    }
42}
43
44#[cfg(target_os = "windows")]
45mod windows {
46    use std::ptr;
47
48    use tao::platform::windows::WindowExtWindows;
49    use tao::window::Window;
50    use windows_sys::Win32::Foundation::{BOOL, HWND, LPARAM};
51    use windows_sys::Win32::UI::WindowsAndMessaging::{
52        EnumWindows, FindWindowExW, FindWindowW, GetWindowLongPtrW, GWL_EXSTYLE, HWND_BOTTOM,
53        SendMessageTimeoutW, SetParent, SetWindowLongPtrW, SetWindowPos, SMTO_ABORTIFHUNG,
54        SWP_NOMOVE, SWP_NOSIZE, SWP_NOACTIVATE, WS_EX_LAYERED, WS_EX_TRANSPARENT,
55    };
56
57    pub(crate) fn apply(window: &Window, click_through: bool, is_wallpaper: bool) {
58        let hwnd = window.hwnd() as HWND;
59        if hwnd == 0 {
60            return;
61        }
62        unsafe {
63            if click_through {
64                let ex = GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
65                let new_ex = ex | (WS_EX_LAYERED as isize) | (WS_EX_TRANSPARENT as isize);
66                SetWindowLongPtrW(hwnd, GWL_EXSTYLE, new_ex);
67            }
68            if is_wallpaper {
69                if let Some(worker) = find_desktop_workerw() {
70                    SetParent(hwnd, worker);
71                }
72                SetWindowPos(
73                    hwnd,
74                    HWND_BOTTOM,
75                    0,
76                    0,
77                    0,
78                    0,
79                    SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE,
80                );
81            }
82        }
83    }
84
85    /// Locate the desktop `WorkerW` window (the host behind the desktop icons)
86    /// by asking Explorer to (re)create it and then finding the sibling
87    /// `WorkerW` that does not own `SHELLDLL_DefView`.
88    unsafe fn find_desktop_workerw() -> Option<HWND> {
89        let progman = FindWindowW(windows_sys::w!("Progman"), ptr::null::<u16>());
90        if progman == 0 {
91            return None;
92        }
93        // Prompt Explorer to create a dedicated wallpaper WorkerW host.
94        SendMessageTimeoutW(
95            progman,
96            0x52C,
97            0,
98            0,
99            SMTO_ABORTIFHUNG,
100            1000,
101            ptr::null_mut::<usize>(),
102        );
103        let mut worker: HWND = 0;
104        EnumWindows(Some(enum_proc), &mut worker as *mut _ as LPARAM);
105        if worker == 0 { None } else { Some(worker) }
106    }
107
108    extern "system" fn enum_proc(hwnd: HWND, lparam: LPARAM) -> BOOL {
109        unsafe {
110            let defview = FindWindowExW(hwnd, 0, windows_sys::w!("SHELLDLL_DefView"), ptr::null::<u16>());
111            if defview != 0 {
112                let pworker = &mut *(lparam as *mut HWND);
113                *pworker = FindWindowExW(0, hwnd, windows_sys::w!("WorkerW"), ptr::null::<u16>());
114            }
115            windows_sys::Win32::Foundation::TRUE
116        }
117    }
118}
119
120#[cfg(target_os = "macos")]
121mod macos {
122    use core_graphics::window::{CGWindowLevelForKey, CGWindowLevelKey};
123    use objc::{msg_send, runtime::Object};
124    use tao::platform::macos::WindowExtMacOS;
125    use tao::window::Window;
126
127    pub(crate) fn apply(window: &Window, click_through: bool, is_wallpaper: bool) {
128        let ns_win = window.ns_window() as *mut Object;
129        if ns_win.is_null() {
130            return;
131        }
132        unsafe {
133            let _: () = msg_send![ns_win, setIgnoresMouseEvents: click_through || is_wallpaper];
134            if is_wallpaper {
135                let level: i64 = CGWindowLevelForKey(CGWindowLevelKey::kCGDesktopWindowLevelKey) as i64;
136                let _: () = msg_send![ns_win, setLevel: level];
137                let _: () = msg_send![ns_win, orderBack: ns_win];
138            }
139        }
140    }
141}