1use crate::windows::{
4 DISPLAYCONFIG_PATH_CLONE_GROUP_INVALID, DISPLAYCONFIG_PATH_DESKTOP_IMAGE_IDX_INVALID,
5 DISPLAYCONFIG_PATH_INFO, DISPLAYCONFIG_PATH_MODE_IDX_INVALID, DISPLAYCONFIG_PATH_SOURCE_INFO,
6 DISPLAYCONFIG_PATH_SOURCE_MODE_IDX_INVALID, DISPLAYCONFIG_PATH_SUPPORT_VIRTUAL_MODE,
7 DISPLAYCONFIG_PATH_TARGET_INFO, DISPLAYCONFIG_PATH_TARGET_MODE_IDX_INVALID,
8};
9
10pub trait PathInfoExt {
12 fn support_virtual_mode(&self) -> bool;
14
15 fn clone_group_id(&self) -> Option<usize>;
19
20 fn source_mode_idx(&self) -> Option<usize>;
26
27 fn target_mode_idx(&self) -> Option<usize>;
33
34 fn desktop_mode_idx(&self) -> Option<usize>;
38
39 fn set_clone_group_id(&mut self, id: Option<usize>);
45
46 fn set_source_mode_idx(&mut self, idx: Option<usize>);
52
53 fn set_target_mode_idx(&mut self, idx: Option<usize>);
59
60 fn set_desktop_mode_idx(&mut self, idx: Option<usize>);
66}
67
68impl PathInfoExt for DISPLAYCONFIG_PATH_INFO {
69 fn support_virtual_mode(&self) -> bool {
70 self.flags.contains(DISPLAYCONFIG_PATH_SUPPORT_VIRTUAL_MODE)
71 }
72
73 fn clone_group_id(&self) -> Option<usize> {
74 self.sourceInfo.clone_group_id(self.support_virtual_mode())
75 }
76
77 fn source_mode_idx(&self) -> Option<usize> {
78 self.sourceInfo.source_mode_idx(self.support_virtual_mode())
79 }
80
81 fn target_mode_idx(&self) -> Option<usize> {
82 self.targetInfo.target_mode_idx(self.support_virtual_mode())
83 }
84
85 fn desktop_mode_idx(&self) -> Option<usize> {
86 self.targetInfo
87 .desktop_mode_idx(self.support_virtual_mode())
88 }
89
90 fn set_clone_group_id(&mut self, id: Option<usize>) {
91 self.sourceInfo
92 .set_clone_group_id(id, self.support_virtual_mode());
93 }
94
95 fn set_source_mode_idx(&mut self, idx: Option<usize>) {
96 self.sourceInfo
97 .set_source_mode_idx(idx, self.support_virtual_mode());
98 }
99
100 fn set_target_mode_idx(&mut self, idx: Option<usize>) {
101 self.targetInfo
102 .set_target_mode_idx(idx, self.support_virtual_mode());
103 }
104
105 fn set_desktop_mode_idx(&mut self, idx: Option<usize>) {
106 self.targetInfo
107 .set_desktop_mode_idx(idx, self.support_virtual_mode());
108 }
109}
110
111pub trait PathSourceInfoExt {
113 fn source_mode_idx(&self, path_support_virtual_mode: bool) -> Option<usize>;
119
120 fn clone_group_id(&self, path_support_virtual_mode: bool) -> Option<usize>;
122
123 fn set_source_mode_idx(&mut self, idx: Option<usize>, path_support_virtual_mode: bool);
129
130 fn set_clone_group_id(&mut self, id: Option<usize>, path_support_virtual_mode: bool);
134}
135
136impl PathSourceInfoExt for DISPLAYCONFIG_PATH_SOURCE_INFO {
137 fn source_mode_idx(&self, path_support_virtual_mode: bool) -> Option<usize> {
138 self.get_value(path_support_virtual_mode, PathInfoUnionValueKind::Primary)
139 }
140
141 fn clone_group_id(&self, path_support_virtual_mode: bool) -> Option<usize> {
142 self.get_value(path_support_virtual_mode, PathInfoUnionValueKind::Secondary)
143 }
144
145 fn set_source_mode_idx(&mut self, idx: Option<usize>, path_support_virtual_mode: bool) {
146 self.set_value(
147 path_support_virtual_mode,
148 PathInfoUnionValueKind::Primary,
149 idx,
150 );
151 }
152
153 fn set_clone_group_id(&mut self, id: Option<usize>, path_support_virtual_mode: bool) {
154 self.set_value(
155 path_support_virtual_mode,
156 PathInfoUnionValueKind::Secondary,
157 id,
158 );
159 }
160}
161
162pub trait PathTargetInfoExt {
164 fn target_mode_idx(&self, path_support_virtual_mode: bool) -> Option<usize>;
170
171 fn desktop_mode_idx(&self, path_support_virtual_mode: bool) -> Option<usize>;
173
174 fn set_target_mode_idx(&mut self, idx: Option<usize>, path_support_virtual_mode: bool);
180
181 fn set_desktop_mode_idx(&mut self, idx: Option<usize>, path_support_virtual_mode: bool);
185}
186
187impl PathTargetInfoExt for DISPLAYCONFIG_PATH_TARGET_INFO {
188 fn target_mode_idx(&self, path_support_virtual_mode: bool) -> Option<usize> {
189 self.get_value(path_support_virtual_mode, PathInfoUnionValueKind::Primary)
190 }
191
192 fn desktop_mode_idx(&self, path_support_virtual_mode: bool) -> Option<usize> {
193 self.get_value(path_support_virtual_mode, PathInfoUnionValueKind::Secondary)
194 }
195
196 fn set_target_mode_idx(&mut self, idx: Option<usize>, path_support_virtual_mode: bool) {
197 self.set_value(
198 path_support_virtual_mode,
199 PathInfoUnionValueKind::Primary,
200 idx,
201 );
202 }
203
204 fn set_desktop_mode_idx(&mut self, idx: Option<usize>, path_support_virtual_mode: bool) {
205 self.set_value(
206 path_support_virtual_mode,
207 PathInfoUnionValueKind::Secondary,
208 idx,
209 );
210 }
211}
212
213#[derive(Debug, Clone, Copy, PartialEq, Eq)]
214enum PathInfoUnionValueKind {
215 Primary,
216 Secondary,
217}
218
219trait PathInfoUnionValuesAccessor {
220 const INVALID_PRIMARY: u32;
221 const INVALID_SECONDARY: u32;
222
223 const SECONDARY_DESCRIPTION: &'static str;
224
225 fn mode_info_idx(&self) -> u32;
226 fn mode_info_idx_mut(&mut self) -> &mut u32;
227
228 fn bitfield(&self) -> u32;
229 fn bitfield_mut(&mut self) -> &mut u32;
230
231 fn get_value(
232 &self,
233 path_support_virtual_mode: bool,
234 kind: PathInfoUnionValueKind,
235 ) -> Option<usize> {
236 if path_support_virtual_mode {
237 let (value, invalid) = match kind {
238 PathInfoUnionValueKind::Primary => {
239 (self.bitfield().higher_u16(), Self::INVALID_PRIMARY)
240 }
241 PathInfoUnionValueKind::Secondary => {
242 (self.bitfield().lower_u16(), Self::INVALID_SECONDARY)
243 }
244 };
245 (u32::from(value) != invalid).then_some(value as usize)
246 } else {
247 match kind {
248 PathInfoUnionValueKind::Primary => {
249 let value = self.mode_info_idx();
250 (value != DISPLAYCONFIG_PATH_MODE_IDX_INVALID).then_some(value as usize)
251 }
252 PathInfoUnionValueKind::Secondary => None,
253 }
254 }
255 }
256
257 fn set_value(
258 &mut self,
259 path_support_virtual_mode: bool,
260 kind: PathInfoUnionValueKind,
261 value: Option<usize>,
262 ) {
263 if path_support_virtual_mode {
264 #[expect(clippy::cast_possible_truncation)]
265 let value = match value {
266 Some(val) => val as u16,
267 None => Self::INVALID_PRIMARY as u16,
268 };
269
270 match kind {
271 PathInfoUnionValueKind::Primary => {
272 self.bitfield_mut().set_higher_u16(value);
273 }
274 PathInfoUnionValueKind::Secondary => {
275 self.bitfield_mut().set_lower_u16(value);
276 }
277 }
278 } else {
279 match kind {
280 PathInfoUnionValueKind::Primary => {
281 *self.mode_info_idx_mut() = match value {
282 #[expect(clippy::cast_possible_truncation)]
283 Some(val) => val as u32,
284 None => DISPLAYCONFIG_PATH_MODE_IDX_INVALID,
285 };
286 }
287 PathInfoUnionValueKind::Secondary => {
288 assert!(
289 value.is_none(),
290 "{} cannot be set",
291 Self::SECONDARY_DESCRIPTION
292 );
293 }
294 }
295 }
296 }
297}
298impl PathInfoUnionValuesAccessor for DISPLAYCONFIG_PATH_SOURCE_INFO {
299 const INVALID_PRIMARY: u32 = DISPLAYCONFIG_PATH_SOURCE_MODE_IDX_INVALID;
300 const INVALID_SECONDARY: u32 = DISPLAYCONFIG_PATH_CLONE_GROUP_INVALID;
301
302 const SECONDARY_DESCRIPTION: &'static str = "Clone group id";
303
304 fn mode_info_idx(&self) -> u32 {
305 unsafe { self.Anonymous.modeInfoIdx }
306 }
307
308 fn mode_info_idx_mut(&mut self) -> &mut u32 {
309 unsafe { &mut self.Anonymous.modeInfoIdx }
310 }
311
312 fn bitfield(&self) -> u32 {
313 unsafe { self.Anonymous.Anonymous._bitfield }
314 }
315
316 fn bitfield_mut(&mut self) -> &mut u32 {
317 unsafe { &mut self.Anonymous.Anonymous._bitfield }
318 }
319}
320impl PathInfoUnionValuesAccessor for DISPLAYCONFIG_PATH_TARGET_INFO {
321 const INVALID_PRIMARY: u32 = DISPLAYCONFIG_PATH_TARGET_MODE_IDX_INVALID;
322 const INVALID_SECONDARY: u32 = DISPLAYCONFIG_PATH_DESKTOP_IMAGE_IDX_INVALID;
323
324 const SECONDARY_DESCRIPTION: &'static str = "Desktop mode idx";
325
326 fn mode_info_idx(&self) -> u32 {
327 unsafe { self.Anonymous.modeInfoIdx }
328 }
329
330 fn mode_info_idx_mut(&mut self) -> &mut u32 {
331 unsafe { &mut self.Anonymous.modeInfoIdx }
332 }
333
334 fn bitfield(&self) -> u32 {
335 unsafe { self.Anonymous.Anonymous._bitfield }
336 }
337
338 fn bitfield_mut(&mut self) -> &mut u32 {
339 unsafe { &mut self.Anonymous.Anonymous._bitfield }
340 }
341}
342
343pub trait U32Ext {
345 fn contains(self, flag: u32) -> bool;
347
348 fn lower_u16(self) -> u16;
350
351 fn higher_u16(self) -> u16;
353
354 fn set_lower_u16(&mut self, lower: u16);
356
357 fn set_higher_u16(&mut self, higher: u16);
359}
360impl U32Ext for u32 {
361 fn contains(self, flag: u32) -> bool {
362 self & flag == flag
363 }
364
365 fn lower_u16(self) -> u16 {
366 (self & 0x0000_FFFF) as u16
367 }
368
369 fn higher_u16(self) -> u16 {
370 (self >> 16) as u16
371 }
372
373 fn set_lower_u16(&mut self, lower: u16) {
374 *self &= 0xFFFF_0000;
375 *self |= u32::from(lower) & 0x0000_FFFF;
376 }
377
378 fn set_higher_u16(&mut self, higher: u16) {
379 *self &= 0x0000_FFFF;
380 *self |= u32::from(higher) << 16;
381 }
382}
383
384#[must_use]
386pub fn from_windows_string(s: &[u16]) -> String {
387 let len = s.iter().position(|&c| c == 0).unwrap_or(s.len());
388 String::from_utf16_lossy(&s[..len])
389}