1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
4pub enum WindowRole {
5 Main,
6 #[default]
7 Auxiliary,
8}
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum TaskbarVisibility {
12 Show,
13 Hide,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum ActivationPolicy {
18 Activates,
19 NonActivating,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum WindowZLevel {
24 Normal,
25 AlwaysOnTop,
26}
27
28#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
29#[serde(rename_all = "snake_case")]
30pub enum WindowHitTestRequestV1 {
31 Normal,
33 PassthroughAll,
35 PassthroughRegions { regions: Vec<WindowHitTestRegionV1> },
37}
38
39#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44#[serde(tag = "kind", rename_all = "snake_case")]
45pub enum WindowHitTestRegionV1 {
46 Rect {
47 x: f32,
48 y: f32,
49 width: f32,
50 height: f32,
51 },
52 RRect {
54 x: f32,
55 y: f32,
56 width: f32,
57 height: f32,
58 radius: f32,
59 },
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub struct WindowHitTestRegionsSignatureV1 {
64 pub fingerprint64: u64,
65}
66
67impl WindowHitTestRegionV1 {
68 fn canonicalize_f32(v: f32) -> f32 {
69 if !v.is_finite() {
70 return 0.0;
71 }
72 if v == 0.0 {
73 return 0.0;
75 }
76 v
77 }
78
79 fn quantize_64(v: f32) -> i64 {
80 let v = Self::canonicalize_f32(v);
81 (v * 64.0).round() as i64
82 }
83
84 fn canonical_key(&self) -> (u8, i64, i64, i64, i64, i64) {
85 match *self {
86 WindowHitTestRegionV1::Rect {
87 x,
88 y,
89 width,
90 height,
91 } => (
92 0,
93 Self::quantize_64(x),
94 Self::quantize_64(y),
95 Self::quantize_64(width),
96 Self::quantize_64(height),
97 0,
98 ),
99 WindowHitTestRegionV1::RRect {
100 x,
101 y,
102 width,
103 height,
104 radius,
105 } => (
106 1,
107 Self::quantize_64(x),
108 Self::quantize_64(y),
109 Self::quantize_64(width),
110 Self::quantize_64(height),
111 Self::quantize_64(radius),
112 ),
113 }
114 }
115}
116
117pub fn canonicalize_hit_test_regions_v1(
118 mut regions: Vec<WindowHitTestRegionV1>,
119) -> Vec<WindowHitTestRegionV1> {
120 fn canonical(v: f32) -> f32 {
121 if !v.is_finite() {
122 return 0.0;
123 }
124 if v == 0.0 {
125 return 0.0;
126 }
127 v
128 }
129
130 regions.retain_mut(|r| match r {
131 WindowHitTestRegionV1::Rect {
132 x,
133 y,
134 width,
135 height,
136 } => {
137 *x = canonical(*x);
138 *y = canonical(*y);
139 *width = canonical(*width).max(0.0);
140 *height = canonical(*height).max(0.0);
141 *width > 0.0 && *height > 0.0
142 }
143 WindowHitTestRegionV1::RRect {
144 x,
145 y,
146 width,
147 height,
148 radius,
149 } => {
150 *x = canonical(*x);
151 *y = canonical(*y);
152 *width = canonical(*width).max(0.0);
153 *height = canonical(*height).max(0.0);
154 let w = *width;
155 let h = *height;
156 if w <= 0.0 || h <= 0.0 {
157 return false;
158 }
159 let max_r = 0.5 * w.min(h);
160 *radius = canonical(*radius).clamp(0.0, max_r);
161 true
162 }
163 });
164
165 regions.sort_by_key(|r| r.canonical_key());
166 regions
167}
168
169pub fn hit_test_regions_signature_v1(
170 regions: &[WindowHitTestRegionV1],
171) -> (String, WindowHitTestRegionsSignatureV1) {
172 fn fnv1a_64(bytes: &[u8]) -> u64 {
173 const FNV1A_OFFSET: u64 = 0xcbf29ce484222325;
174 const FNV1A_PRIME: u64 = 0x00000100000001B3;
175 let mut hash = FNV1A_OFFSET;
176 for &b in bytes {
177 hash ^= b as u64;
178 hash = hash.wrapping_mul(FNV1A_PRIME);
179 }
180 hash
181 }
182
183 let mut sig = String::from("hit_test_regions_v1;u64px=1/64;");
184 for r in regions {
185 match *r {
186 WindowHitTestRegionV1::Rect {
187 x,
188 y,
189 width,
190 height,
191 } => {
192 sig.push_str(&format!(
193 "rect(x={},y={},w={},h={});",
194 WindowHitTestRegionV1::quantize_64(x),
195 WindowHitTestRegionV1::quantize_64(y),
196 WindowHitTestRegionV1::quantize_64(width),
197 WindowHitTestRegionV1::quantize_64(height),
198 ));
199 }
200 WindowHitTestRegionV1::RRect {
201 x,
202 y,
203 width,
204 height,
205 radius,
206 } => {
207 sig.push_str(&format!(
208 "rrect(x={},y={},w={},h={},r={});",
209 WindowHitTestRegionV1::quantize_64(x),
210 WindowHitTestRegionV1::quantize_64(y),
211 WindowHitTestRegionV1::quantize_64(width),
212 WindowHitTestRegionV1::quantize_64(height),
213 WindowHitTestRegionV1::quantize_64(radius),
214 ));
215 }
216 }
217 }
218
219 let fingerprint64 = fnv1a_64(sig.as_bytes());
220 (sig, WindowHitTestRegionsSignatureV1 { fingerprint64 })
221}
222
223#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
229pub struct WindowOpacity(pub u8);
230
231impl WindowOpacity {
232 pub fn from_f32(opacity: f32) -> Self {
233 let a = opacity.clamp(0.0, 1.0);
234 let byte = (255.0 * a).round().clamp(0.0, 255.0) as u8;
235 Self(byte)
236 }
237
238 pub fn as_f32(self) -> f32 {
239 (self.0 as f32) / 255.0
240 }
241}
242
243#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
244#[serde(rename_all = "snake_case")]
245pub enum WindowDecorationsRequest {
246 System,
248 None,
250 Server,
252 Client,
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
257#[serde(rename_all = "snake_case")]
258pub enum WindowBackgroundMaterialRequest {
259 None,
261 SystemDefault,
263 Mica,
265 Acrylic,
267 Vibrancy,
269}
270
271#[derive(Debug, Clone, PartialEq, Default)]
272pub struct WindowStyleRequest {
273 pub taskbar: Option<TaskbarVisibility>,
274 pub activation: Option<ActivationPolicy>,
275 pub z_level: Option<WindowZLevel>,
276 pub decorations: Option<WindowDecorationsRequest>,
277 pub resizable: Option<bool>,
278 pub transparent: Option<bool>,
280 pub background_material: Option<WindowBackgroundMaterialRequest>,
282 pub hit_test: Option<WindowHitTestRequestV1>,
284 pub opacity: Option<WindowOpacity>,
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291
292 #[test]
293 fn canonicalize_regions_clamps_and_sorts() {
294 let regions = vec![
295 WindowHitTestRegionV1::RRect {
296 x: 10.0,
297 y: 0.0,
298 width: 10.0,
299 height: 10.0,
300 radius: 999.0,
301 },
302 WindowHitTestRegionV1::Rect {
303 x: f32::NAN,
304 y: 0.0,
305 width: 0.0,
306 height: 10.0,
307 },
308 WindowHitTestRegionV1::Rect {
309 x: 0.0,
310 y: 0.0,
311 width: 10.0,
312 height: 10.0,
313 },
314 ];
315
316 let out = canonicalize_hit_test_regions_v1(regions);
317 assert_eq!(out.len(), 2);
318
319 assert!(matches!(out[0], WindowHitTestRegionV1::Rect { .. }));
321
322 match out[1] {
323 WindowHitTestRegionV1::RRect { radius, .. } => {
324 assert_eq!(radius, 5.0);
326 }
327 _ => panic!("expected rrect"),
328 }
329
330 let (_sig, fp) = hit_test_regions_signature_v1(&out);
331 assert_ne!(fp.fingerprint64, 0);
332 }
333}