wallpaper_app/lib.rs
1use std::default::Default;
2use core::ptr::null_mut;
3use std::sync::Mutex;
4
5use winapi::ctypes::c_int;
6use winapi::shared::minwindef::BOOL;
7
8use winapi::um::errhandlingapi::GetLastError;
9use winapi::um::libloaderapi::GetModuleHandleW;
10
11use winapi::um::winuser::{
12 WNDCLASSW,
13 WNDPROC,
14 MSG,
15 IDC_ARROW,
16 SMTO_NORMAL,
17
18 PM_REMOVE,
19 WS_POPUP,
20 WS_VISIBLE,
21 SM_CXSCREEN,
22 SM_CYSCREEN,
23 SWP_NOZORDER,
24 SWP_NOOWNERZORDER,
25};
26
27use winapi::um::winuser::{
28 RegisterClassW,
29 CreateWindowExW,
30 ShowWindow,
31 EnumWindows,
32 FindWindowW,
33 FindWindowExW,
34 SendMessageTimeoutW,
35
36 GetSystemMetrics,
37 SetWindowPos,
38 SetParent,
39
40 LoadCursorW,
41};
42
43use winapi::um::winuser::{
44 PeekMessageW,
45 TranslateMessage,
46 DispatchMessageW,
47 SystemParametersInfoW,
48};
49
50use winapi::shared::minwindef::{
51 HINSTANCE,
52 LPARAM,
53};
54
55use winapi::um::winuser::SW_SHOW;
56use winapi::shared::windef::HWND;
57
58/// Used in [`FindWindowExW()`]. Is the name of the window class that is the parent of the desktop window:
59///
60/// --- Window ... SHELLDLL_DefView
61///
62/// ------ Window ... WorkerW
63///
64/// --------- OurWindow ... OurWindowClass
65///
66/// See more: <https://learn.microsoft.com/en-us/archive/msdn-magazine/2004/march/c-q-a-list-view-mode-setforegroundwindow-and-class-protection>
67/// <https://www.codeproject.com/Articles/856020/Draw-Behind-Desktop-Icons-in-Windows-plus>
68pub const SHELLDLL_DEF_VIEW_STR : &str = "SHELLDLL_DefView";
69
70/// Used in [`FindWindowExW()`]. Any application that needs to listen to window messages call this Api to create a worker window.
71/// Is the name of the window class we are looking for to put our window into as a child:
72///
73/// --- Window ... SHELLDLL_DefView
74///
75/// ------ Window ... WorkerW
76///
77/// --------- OurWindow ... OurWindowClass
78///
79/// See more: <https://learn.microsoft.com/en-us/archive/msdn-magazine/2004/march/c-q-a-list-view-mode-setforegroundwindow-and-class-protection>
80/// <https://www.codeproject.com/Articles/856020/Draw-Behind-Desktop-Icons-in-Windows-plus>
81pub const WORKER_W_STR : &str = "WorkerW";
82
83pub mod drawing;
84
85/// Handle to desktop window app. Any application that needs to listen to window messages call this Api to create a worker window.
86static mut WORKER_W : Mutex::<HWND> = Mutex::new(null_mut());
87
88/// Create WNDCLASSW and handle to it with custom name and WNDPROC.
89///
90/// <i>window_procedure</i> - A callback function, which you define in your application, that processes messages sent to a window.
91///
92/// Read more:
93///
94/// WNDCLASSW - <https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassw>
95///
96/// WNDPROC - <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-wndproc>
97///
98/// Example:
99/// ```
100/// fn main() {
101/// let class_name = wide_null("My app window Class");
102/// let window_name = wide_null("My app window");
103/// let (window_class, h_instance) = create_window_class(&class_name, window_procedure);
104/// let window_handle = create_window_handle(&window_class, &class_name, &window_name, h_instance);
105/// create_window(window_handle);
106/// }
107/// ```
108///
109/// Procedure example:
110/// ```
111/// pub unsafe extern "system" fn window_procedure(hwnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM,) -> LRESULT {
112/// match msg {
113/// WM_NCCREATE => {
114/// println!("NC Create");
115/// let createstruct: *mut CREATESTRUCTW = l_param as *mut _;
116/// if createstruct.is_null() {
117/// return 0;
118/// }
119/// let boxed_i32_ptr = (*createstruct).lpCreateParams;
120/// SetWindowLongPtrW(hwnd, GWLP_USERDATA, boxed_i32_ptr as LONG_PTR);
121/// return 1;
122/// }
123/// WM_CREATE => println!("WM Create"),
124/// WM_CLOSE => drop(DestroyWindow(hwnd)),
125/// WM_DESTROY => {
126/// let ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut i32;
127/// drop(Box::from_raw(ptr));
128/// println!("Cleaned up the box.");
129/// PostQuitMessage(0);
130/// }
131/// WM_ERASEBKGND => return 1,
132/// WM_PAINT => your_paint_func(hwnd),
133/// _ => return DefWindowProcW(hwnd, msg, w_param, l_param),
134/// }
135///
136/// 0
137/// }
138/// ```
139pub fn create_window_class(name: &Vec<u16>, window_procedure: WNDPROC) -> (WNDCLASSW, HINSTANCE) {
140 let h_instance = unsafe { GetModuleHandleW(core::ptr::null()) };
141
142 let mut wc = WNDCLASSW::default();
143 wc.lpfnWndProc = window_procedure;
144 wc.hInstance = h_instance;
145 wc.lpszClassName = name.as_ptr();
146 wc.hCursor = unsafe { LoadCursorW(null_mut(), IDC_ARROW) };
147 (wc, h_instance)
148}
149
150/// Create window handle for window class (WNDCLASSW) with <i>window_name</i>
151///
152/// <i>wc</i> and <i>h_instance</i> - can be results o the [`create_window_class()`] func
153///
154/// Example:
155/// ```
156/// fn main() {
157/// let class_name = wide_null("My app window Class");
158/// let window_name = wide_null("My app window");
159/// let (window_class, h_instance) = create_window_class(&class_name, window_procedure);
160/// let window_handle = create_window_handle(&window_class, &class_name, &window_name, h_instance);
161/// create_window(window_handle);
162/// }
163/// ```
164pub fn create_window_handle(wc: &WNDCLASSW, wc_name: &Vec<u16>, window_name: &Vec<u16>, h_instance: HINSTANCE, ) -> HWND {
165 let atom = unsafe { RegisterClassW(wc) };
166 if atom == 0 {
167 let last_error = unsafe { GetLastError() };
168 panic!("Could not register the window class, error code: {}", last_error);
169 }
170
171 let lparam: *mut i32 = Box::leak(Box::new(5_i32));
172 let hwnd = unsafe {
173 CreateWindowExW(
174 0,
175 wc_name.as_ptr(),
176 window_name.as_ptr(),
177 WS_POPUP | WS_VISIBLE,
178 0,
179 0,
180 0,
181 0,
182 core::ptr::null_mut(),
183 core::ptr::null_mut(),
184 h_instance,
185 lparam.cast(),
186 )
187 };
188 if hwnd.is_null() {
189 panic!("Failed to create a window.");
190 }
191
192 hwnd
193}
194
195/// Create window using window <i>handle</i>.
196///
197/// <i>handle</i> - can be result of [`create_window_handle()`] func
198///
199/// Example:
200/// ```
201/// fn main() {
202/// let class_name = wide_null("My app window Class");
203/// let window_name = wide_null("My app window");
204/// let (window_class, h_instance) = create_window_class(&class_name, window_procedure);
205/// let window_handle = create_window_handle(&window_class, &class_name, &window_name, h_instance);
206/// create_window(window_handle);
207/// }
208/// ```
209pub fn create_window(handle: HWND) {
210 let _previously_visible = unsafe { ShowWindow(handle, SW_SHOW) };
211}
212
213/// Find `Progman` and get handle. Progman requires for [`try_spawn_worker_w()`] func
214///
215/// Example:
216/// ```
217/// fn main() {
218/// let progman_h = get_progman_handle();
219/// if try_spawn_worker_w(progman_h).is_err() {
220/// panic!("`Progman` failed to spawn WorkerW!");
221/// };
222/// }
223/// ```
224pub fn get_progman_handle() -> HWND {
225 let h_progman = unsafe { FindWindowW(wide_null("Progman").as_ptr(), null_mut()) };
226 h_progman
227}
228
229/// Message to `Progman` to spawn a `WorkerW`
230///
231/// Send 0x052C to Progman. This message directs Progman to spawn a
232/// WorkerW behind the desktop icons. If it is already there, nothing
233/// happens.
234///
235/// Example:
236/// ```
237/// fn main() {
238/// let progman_h = get_progman_handle();
239/// if try_spawn_worker_w(progman_h).is_err() {
240/// panic!("`Progman` failed to spawn WorkerW!");
241/// };
242/// }
243/// ```
244pub fn try_spawn_worker_w(progman_handle: HWND) -> Result<(), &'static str> {
245 // Requare all for support all windows versions!
246 let send_message_results = unsafe { [
247 SendMessageTimeoutW(progman_handle, 0x052C, 0, 0, SMTO_NORMAL, 1000, null_mut()),
248 SendMessageTimeoutW(progman_handle, 0x052C, 0x0d, 0, SMTO_NORMAL, 1000, null_mut()),
249 SendMessageTimeoutW(progman_handle, 0x052C, 0x0d, 1, SMTO_NORMAL, 1000, null_mut())
250 ] };
251
252 if send_message_results.iter().all(|r| *r == 0) {
253 return Err("`Progman` failed to spawn WorkerW!");
254 }
255
256 Ok(())
257}
258
259/// Find the newly created `WorkerW`
260/// Example:
261/// ```
262/// fn main() {
263/// let class_name = wide_null("My app window Class");
264/// let window_name = wide_null("My app window");
265/// let (window_class, h_instance) = create_window_class(&class_name, window_procedure);
266/// let window_handle = create_window_handle(&window_class, &class_name, &window_name, h_instance);let worker_w_handle = find_worker_w();
267/// pull_window_to_desktop(window_handle, worker_w_handle);
268/// let worker_w_handle = find_worker_w();
269/// pull_window_to_desktop(window_handle, worker_w_handle);
270/// }
271/// ```
272pub fn find_worker_w() -> HWND {
273 unsafe {
274 EnumWindows(Some(enum_windows_proc), 0);
275 return WORKER_W.lock().unwrap().clone();
276 };
277}
278
279/// Sets worker_w_handle as parent to handle and set window size to [`winapi::um::winuser::SM_CXSCREEN`] x [`winapi::um::winuser::SM_CYSCREEN`]
280///
281/// Used flags:
282///
283/// <b>SWP_NOOWNERZORDER</b> - Does not change the owner window's position in the Z order.
284///
285/// <b>SWP_NOZORDER</b> - Retains the current Z order (ignores the hWndInsertAfter parameter).
286///
287/// Example:
288/// ```
289/// fn main() {
290/// let class_name = wide_null("My app window Class");
291/// let window_name = wide_null("My app window");
292/// let (window_class, h_instance) = create_window_class(&class_name, window_procedure);
293/// let window_handle = create_window_handle(&window_class, &class_name, &window_name, h_instance);let worker_w_handle = find_worker_w();
294/// pull_window_to_desktop(window_handle, worker_w_handle);
295/// }
296/// ```
297pub fn pull_window_to_desktop(handle: HWND, worker_w_handle: HWND) {
298 unsafe { SetParent(handle, worker_w_handle) };
299 unsafe {
300 SetWindowPos(
301 handle,
302 null_mut(),
303 0,
304 0,
305 GetSystemMetrics(SM_CXSCREEN) as c_int,
306 GetSystemMetrics(SM_CYSCREEN) as c_int,
307 SWP_NOOWNERZORDER | SWP_NOZORDER
308 )
309 };
310
311 unsafe { SystemParametersInfoW(20, 0, null_mut(), 0x1) };
312}
313
314/// It receives top-level window handles and find windows with class [`SHELLDLL_DEF_VIEW_STR`] + child with [`WORKER_W_STR`] class
315///
316/// Read more: <https://learn.microsoft.com/ru-ru/previous-versions/windows/desktop/legacy/ms633498(v=vs.85)>
317pub unsafe extern "system" fn enum_windows_proc(hwnd: HWND, _l_param: LPARAM) -> BOOL {
318 let shelldll_def_view_name = wide_null(SHELLDLL_DEF_VIEW_STR);
319 let cur_hwnd = unsafe { FindWindowExW(hwnd, null_mut(), shelldll_def_view_name.as_ptr(), null_mut()) };
320
321 if !cur_hwnd.is_null()
322 {
323 println!("{} window found!", SHELLDLL_DEF_VIEW_STR);
324 let worker_w_name = wide_null(WORKER_W_STR);
325 // Gets the WorkerW Window after the current one.
326 let mut worker = WORKER_W.lock().unwrap();
327 unsafe { *worker = FindWindowExW(null_mut(), hwnd, worker_w_name.as_ptr(), null_mut()) };
328 if !worker.is_null() {
329 println!("{} window found!", WORKER_W_STR);
330 }
331 }
332
333 return 1;
334}
335
336/// A simple function to handle window messages.
337/// You can use it, or define your own. It use PeekMessageW() (<https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-peekmessagew>)
338///
339/// Returns TRUE if the message was received and processed
340///
341/// Example:
342/// ```
343/// let msg = MSG::default();
344/// loop {
345/// if handle_window_messages(msg) {
346/// println!("Message received and processed!");
347/// }
348/// else {
349/// std::thread::sleep(std::time::Duration::from_micros(100));
350/// }
351/// }
352/// ```
353pub fn handle_window_messages(mut msg: MSG) -> bool {
354 let message_return = unsafe { PeekMessageW(&mut msg, null_mut(), 0, 0, PM_REMOVE) };
355 if message_return == 0 {
356 return false;
357 } else if message_return == -1 {
358 let last_error = unsafe { GetLastError() };
359 panic!("Error with `GetMessageW`, error code: {}", last_error);
360 } else {
361 unsafe {
362 TranslateMessage(&msg);
363 DispatchMessageW(&msg);
364 }
365 }
366
367 true
368}
369
370/// Combines low-lewel methods for simplify create window at desktop!
371///
372/// <i>name</i> - name of new window
373///
374/// <i>window_procedure</i> - A callback function, which you define in your application, that processes messages sent to a window.
375///
376/// Read more about WNDPROC - <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-wndproc>
377///
378/// Usage example:
379/// ```
380/// fn main() {
381/// unsafe { SetProcessDPIAware(); }
382/// let window_handle = create_desktop_window_fast("My app window", Some(window_procedure));
383/// loop_graphics(window_handle);
384/// }
385/// ```
386///
387/// Procedure example:
388/// ```
389/// pub unsafe extern "system" fn window_procedure(hwnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM,) -> LRESULT {
390/// match msg {
391/// WM_NCCREATE => {
392/// println!("NC Create");
393/// let createstruct: *mut CREATESTRUCTW = l_param as *mut _;
394/// if createstruct.is_null() {
395/// return 0;
396/// }
397/// let boxed_i32_ptr = (*createstruct).lpCreateParams;
398/// SetWindowLongPtrW(hwnd, GWLP_USERDATA, boxed_i32_ptr as LONG_PTR);
399/// return 1;
400/// }
401/// WM_CREATE => println!("WM Create"),
402/// WM_CLOSE => drop(DestroyWindow(hwnd)),
403/// WM_DESTROY => {
404/// let ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut i32;
405/// drop(Box::from_raw(ptr));
406/// println!("Cleaned up the box.");
407/// PostQuitMessage(0);
408/// }
409/// WM_ERASEBKGND => return 1,
410/// WM_PAINT => your_paint_func(hwnd),
411/// _ => return DefWindowProcW(hwnd, msg, w_param, l_param),
412/// }
413///
414/// 0
415/// }
416/// ```
417pub fn create_desktop_window_fast(name: &str, window_procedure: WNDPROC) -> HWND {
418 let class_name = wide_null(format!("{} Class", name).as_str());
419 let window_name = wide_null(name);
420 let (window_class, h_instance) = create_window_class(&class_name, window_procedure);
421 let window_handle = create_window_handle(&window_class, &class_name, &window_name, h_instance);
422 create_window(window_handle);
423
424 let progman_h = get_progman_handle();
425 if try_spawn_worker_w(progman_h).is_err() {
426 panic!("`Progman` failed to spawn WorkerW!");
427 };
428
429 let worker_w_handle = find_worker_w();
430 pull_window_to_desktop(window_handle, worker_w_handle);
431
432 window_handle
433}
434
435/// Convert string to windows friedly format.
436///
437/// !!! With this strings use W-functions !!!
438///
439/// For example: [`winapi::um::libloaderapi::GetModuleHandleW()`] or [`winapi::um::winuser::RegisterClassW`]
440///
441/// Usage example:
442/// ```
443/// fn main() {
444/// let class_name = wide_null("My app window Class");
445/// let (window_class, h_instance) = create_window_class(&class_name, window_procedure);
446/// }
447/// ```
448pub fn wide_null(s: &str) -> Vec<u16> {
449 s.encode_utf16().chain(Some(0)).collect()
450}