Skip to main content

windows_ccd/
lib.rs

1#![cfg_attr(all(doc, not(doctest)), feature(doc_cfg))]
2#![warn(missing_docs)]
3#![doc = include_str!("../README.md")]
4
5use std::mem;
6
7pub use crate::device_id::DeviceId;
8pub use crate::error::{Error, Win32Error};
9use crate::windows::{
10    DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME,
11    DISPLAYCONFIG_DEVICE_INFO_HEADER, DISPLAYCONFIG_DEVICE_INFO_TYPE, DISPLAYCONFIG_MODE_INFO,
12    DISPLAYCONFIG_PATH_INFO, DISPLAYCONFIG_SOURCE_DEVICE_NAME, DISPLAYCONFIG_TARGET_DEVICE_NAME,
13    DisplayConfigGetDeviceInfo, ERROR_INSUFFICIENT_BUFFER, GetDisplayConfigBufferSizes,
14    QUERY_DISPLAY_CONFIG_FLAGS, QueryDisplayConfig, SET_DISPLAY_CONFIG_FLAGS, SetDisplayConfig,
15};
16
17mod device_id;
18#[cfg(feature = "dump")]
19mod dump;
20mod error;
21pub mod util;
22pub mod windows;
23
24/// Result type for this crate.
25pub type Result<T, E = error::Error> = std::result::Result<T, E>;
26
27/// Convenience function for the [QueryDisplayConfig] function.
28///
29/// The call to [GetDisplayConfigBufferSizes] and the sizing of the paths and modes buffers are automatically done.
30///
31/// # Errors
32/// Returns an error when [QueryDisplayConfig] fails.
33///
34/// [QueryDisplayConfig]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-querydisplayconfig
35/// [GetDisplayConfigBufferSizes]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdisplayconfigbuffersizes
36///
37// TODO: currenttopologyid parameter.
38pub fn query_display_config(
39    flags: QUERY_DISPLAY_CONFIG_FLAGS,
40) -> Result<(Vec<DISPLAYCONFIG_PATH_INFO>, Vec<DISPLAYCONFIG_MODE_INFO>)> {
41    loop {
42        let mut path_count = 0;
43        let mut mode_count = 0;
44
45        let result =
46            unsafe { GetDisplayConfigBufferSizes(flags, &raw mut path_count, &raw mut mode_count) };
47        Error::new(result, "GetDisplayConfigBufferSizes").to_result(())?;
48
49        let mut paths = vec![DISPLAYCONFIG_PATH_INFO::default(); path_count as usize];
50        let mut modes = vec![DISPLAYCONFIG_MODE_INFO::default(); mode_count as usize];
51
52        let result = unsafe {
53            QueryDisplayConfig(
54                flags,
55                &raw mut path_count,
56                paths.as_mut_ptr(),
57                &raw mut mode_count,
58                modes.as_mut_ptr(),
59                None,
60            )
61        };
62
63        if result != ERROR_INSUFFICIENT_BUFFER {
64            let error = Error::new(result, "QueryDisplayConfig");
65
66            #[cfg(feature = "dump")]
67            crate::dump::dump_query_display_config(
68                error, flags, &paths, path_count, &modes, mode_count, None,
69            );
70
71            return error.to_result_with(|| {
72                paths.truncate(path_count as usize);
73                modes.truncate(mode_count as usize);
74                (paths, modes)
75            });
76        }
77
78        // Try again.
79    }
80}
81
82/// Convenience function for the [SetDisplayConfig] function.
83///
84/// # Errors
85/// Returns an error when [SetDisplayConfig] fails.
86///
87/// [SetDisplayConfig]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setdisplayconfig
88pub fn set_display_config(
89    paths: Option<&[DISPLAYCONFIG_PATH_INFO]>,
90    modes: Option<&[DISPLAYCONFIG_MODE_INFO]>,
91    flags: SET_DISPLAY_CONFIG_FLAGS,
92) -> Result<()> {
93    let result = unsafe { SetDisplayConfig(paths, modes, flags) };
94    let error = Error::new(result, "SetDisplayConfig");
95
96    #[cfg(feature = "dump")]
97    crate::dump::dump_set_display_config(error, paths, modes, flags);
98
99    error.to_result(())
100}
101
102mod private {
103    cfg_select! {
104        feature = "dump" => {
105            pub trait GetDeviceInfoBase: Default + crate::dump::ToJsonValue {}
106            impl<T: Default + crate::dump::ToJsonValue> GetDeviceInfoBase for T {}
107        }
108        _ => {
109            pub trait GetDeviceInfoBase: Default {}
110            impl<T: Default> GetDeviceInfoBase for T {}
111        }
112    }
113}
114
115/// A trait for the structures that can be returned by the [`display_config_get_device_info`] function.
116pub trait GetDeviceInfo: private::GetDeviceInfoBase {
117    /// The type of the structure.
118    const TYPE: DISPLAYCONFIG_DEVICE_INFO_TYPE;
119
120    /// A reference to the [`DISPLAYCONFIG_DEVICE_INFO_HEADER`].
121    #[cfg(feature = "dump")]
122    fn header(&self) -> &DISPLAYCONFIG_DEVICE_INFO_HEADER;
123
124    /// A mutable reference to the [`DISPLAYCONFIG_DEVICE_INFO_HEADER`].
125    fn header_mut(&mut self) -> &mut DISPLAYCONFIG_DEVICE_INFO_HEADER;
126}
127
128impl GetDeviceInfo for DISPLAYCONFIG_SOURCE_DEVICE_NAME {
129    const TYPE: DISPLAYCONFIG_DEVICE_INFO_TYPE = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
130
131    #[cfg(feature = "dump")]
132    fn header(&self) -> &DISPLAYCONFIG_DEVICE_INFO_HEADER {
133        &self.header
134    }
135
136    fn header_mut(&mut self) -> &mut DISPLAYCONFIG_DEVICE_INFO_HEADER {
137        &mut self.header
138    }
139}
140
141impl GetDeviceInfo for DISPLAYCONFIG_TARGET_DEVICE_NAME {
142    const TYPE: DISPLAYCONFIG_DEVICE_INFO_TYPE = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
143
144    #[cfg(feature = "dump")]
145    fn header(&self) -> &DISPLAYCONFIG_DEVICE_INFO_HEADER {
146        &self.header
147    }
148
149    fn header_mut(&mut self) -> &mut DISPLAYCONFIG_DEVICE_INFO_HEADER {
150        &mut self.header
151    }
152}
153
154/// Convenience function for the [DisplayConfigGetDeviceInfo] function.
155///
156/// The [`DISPLAYCONFIG_DEVICE_INFO_HEADER`] set up is done automatically based on the structure you want to obtain.
157///
158/// Also, the `adapterId` and `id` are automatically obtained from the [`DISPLAYCONFIG_PATH_SOURCE_INFO`].
159///
160/// # Example
161///
162/// To obtain a [`DISPLAYCONFIG_SOURCE_DEVICE_NAME`]:
163///
164/// ```
165/// use windows_ccd::{display_config_get_device_info, DeviceId, Result};
166/// use windows_ccd::windows::{DISPLAYCONFIG_PATH_SOURCE_INFO, DISPLAYCONFIG_SOURCE_DEVICE_NAME};
167///
168/// fn get_device_name(source_info: DISPLAYCONFIG_PATH_SOURCE_INFO) -> Result<DISPLAYCONFIG_SOURCE_DEVICE_NAME> {
169///     display_config_get_device_info(source_info)
170/// }
171/// ```
172///
173/// # Errors
174/// Returns an error when [DisplayConfigGetDeviceInfo] fails.
175///
176/// [`DISPLAYCONFIG_PATH_SOURCE_INFO`]: crate::windows::DISPLAYCONFIG_PATH_SOURCE_INFO
177/// [DisplayConfigGetDeviceInfo]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-displayconfiggetdeviceinfo
178pub fn display_config_get_device_info<T: GetDeviceInfo>(
179    device_id: impl Into<DeviceId>,
180) -> Result<T> {
181    let device_id = device_id.into();
182
183    let mut device_info = T::default();
184    *device_info.header_mut() = DISPLAYCONFIG_DEVICE_INFO_HEADER {
185        r#type: T::TYPE,
186        #[expect(clippy::cast_possible_truncation)]
187        size: mem::size_of::<T>() as u32,
188        adapterId: device_id.adapter_id,
189        id: device_id.id,
190    };
191
192    let result = unsafe { DisplayConfigGetDeviceInfo(device_info.header_mut()) };
193    let error = Error::new(result, "DisplayConfigGetDeviceInfo");
194
195    #[cfg(feature = "dump")]
196    crate::dump::dump_display_config_get_device_info(error, &device_info);
197
198    error.to_result(device_info)
199}