1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! Desktop management for debug/test builds.
//!
//! Functions to create, open, and switch Windows desktops, used by the daemon
//! and test infrastructure so all window operations happen on an isolated
//! desktop instead of the user's real desktop.
//!
//! **Only compiled in debug builds** (`#[cfg(debug_assertions)]`). Excluded from
//! release builds entirely — no desktop code ships in the production binary.
//!
//! # Why isolate?
//!
//! Integration tests create and manipulate windows. Without isolation these test
//! windows would appear on the user's actual desktop, interfere with their work,
//! and potentially break their layout. Switching to a dedicated test desktop
//! keeps all test window operations invisible to the user. The typical flow:
//! test code creates a desktop and sets the test thread onto it, spawns `flowd`
//! (which calls [`switch_to_desktop`], scans existing windows, and starts the
//! hook thread on that desktop), runs the tests, then restores the original
//! desktop and closes the test one.
//!
//! # Handle lifetime
//!
//! - Handles from [`create_desktop`] must be closed via [`close_desktop`].
//! - Handles from [`current_desktop`] must **not** be closed (managed by Windows).
//! - The handle opened by [`switch_to_desktop`] is intentionally **leaked**
//! (see its docs for rationale).
use OsStr;
use OsStrExt;
use ;
use PCWSTR;
/// Access rights used for all desktop operations:
/// - `DESKTOP_READOBJECTS` (0x01) — read window data
/// - `DESKTOP_WRITEOBJECTS` (0x02) — write window data
/// - `DESKTOP_ENUMERATE` (0x04) — enumerate windows
const DESKTOP_ACCESS: u32 = 0x0001 | 0x0002 | 0x0004;
/// Creates a new Windows desktop with the given name.
///
/// Returns a handle to the new desktop. The caller is responsible for closing
/// it via [`close_desktop`] when no longer needed.
///
/// # Errors
///
/// Returns an error if `CreateDesktopW` fails (e.g. name already exists).
/// Opens an existing desktop by name and switches the calling thread to it.
///
/// Used by the daemon and hook thread to join the test desktop.
///
/// # Handle lifetime
///
/// The `OpenDesktopW` handle opened here is **intentionally not closed**.
/// The handle must remain valid for the lifetime of the thread's desktop
/// assignment. If the handle were closed, the thread could be orphaned on
/// a destroyed desktop once all other handles are released. Windows frees
/// the handle when the process exits.
///
/// # Errors
///
/// Returns an error if `OpenDesktopW` or `SetThreadDesktop` fails.
/// Switches the calling thread to the given desktop handle.
///
/// Use this to restore a previously saved desktop via [`current_desktop`].
///
/// # Errors
///
/// Returns an error if `SetThreadDesktop` fails.
/// Returns a handle to the calling thread's current desktop.
///
/// The returned handle should **not** be closed with [`close_desktop`] — it
/// is managed by Windows.
///
/// # Errors
///
/// Returns an error if `GetThreadDesktop` fails (extremely unlikely).
/// Closes a desktop handle obtained from [`create_desktop`].
///
/// The desktop is destroyed when all handles are closed and no threads are
/// assigned to it.
/// Opens an existing desktop by name, returning a handle without switching.
/// Converts a Rust string to a null-terminated UTF-16 vector.