Skip to main content

display_profile_lib/
lib.rs

1#![cfg_attr(all(doc, not(doctest)), feature(doc_cfg))]
2#![expect(clippy::missing_errors_doc)]
3#![doc = include_str!("../README.md")]
4
5pub use error::Error;
6pub use get_profile::get_profile;
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9pub use set_profile::{SetProfileAction, set_profile};
10use windows_ccd::windows::DISPLAYCONFIG_2DREGION;
11
12mod error;
13mod get_profile;
14mod set_profile;
15mod util;
16
17const VIRTUAL_MODE_AWARE: bool = true;
18
19pub type Result<T, E = Error> = std::result::Result<T, E>;
20
21macro_rules! define_windows_mapped_struct {
22    (
23        $name:ident => $win_name:ident in $($win_path:ident)::+ {
24            $(
25                $field:ident => $win_field:ident : $field_type:ty ,
26            )*
27        }
28    ) => {
29        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
30        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31        pub struct $name {
32            $(
33                pub $field: $field_type,
34            )*
35        }
36
37        impl From<$($win_path::)+ $win_name> for $name {
38            fn from(value: $($win_path::)+ $win_name) -> Self {
39                $name {
40                    $(
41                        $field: value.$win_field,
42                    )*
43                }
44            }
45        }
46
47        impl From<$name> for $($win_path::)+ $win_name {
48            fn from(value: $name) -> Self {
49                $($win_path::)+ $win_name {
50                    $(
51                        $win_field: value.$field,
52                    )*
53                }
54            }
55        }
56    };
57}
58
59macro_rules! define_windows_mapped_enum {
60    (
61        $name:ident => $win_type:ident in $($win_path:ident)::+ {
62            $(
63                $( #[ $attribute:meta ] )?
64                $variant:ident => $win_val:ident ,
65            )*
66        }
67    ) => {
68        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
69        #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
70        pub enum $name {
71            $(
72                $( #[ $attribute ] )?
73                $variant,
74            )*
75        }
76
77        impl From<$($win_path::)+ $win_type> for $name {
78            fn from(value: $($win_path::)+ $win_type) -> Self {
79                use $($win_path)::+ as WinPath;
80                match value {
81                    $( WinPath::$win_val => $name::$variant, )*
82                    _ => unreachable!(),
83                }
84            }
85        }
86
87        impl From<$name> for $($win_path::)+ $win_type {
88            fn from(value: $name) -> Self {
89                use $($win_path)::+ as WinPath;
90                match value {
91                    $( $name::$variant => WinPath::$win_val, )*
92                }
93            }
94        }
95    };
96}
97
98pub type Profile = Vec<Monitor>;
99
100#[derive(Debug, Clone, PartialEq, Eq)]
101#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
102pub struct Monitor {
103    pub friendly_device_name: String,
104    pub source_device_name: String,
105    pub device_path: String,
106    pub dimensions: Dimensions,
107    pub pixel_format: PixelFormat,
108    pub position: Position,
109    pub rotation: Rotation,
110    pub scaling: Scaling,
111    pub refresh_rate: Rational,
112    pub path_source_size: Position,
113    pub desktop_image_region: Rect,
114    pub desktop_image_clip: Rect,
115}
116
117#[derive(Debug, Clone, PartialEq, Eq)]
118#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
119pub struct Dimensions {
120    pub width: u32,
121    pub height: u32,
122}
123impl From<Dimensions> for DISPLAYCONFIG_2DREGION {
124    fn from(value: Dimensions) -> Self {
125        DISPLAYCONFIG_2DREGION {
126            cx: value.width,
127            cy: value.height,
128        }
129    }
130}
131
132define_windows_mapped_enum!(
133    PixelFormat => DISPLAYCONFIG_PIXELFORMAT in windows_ccd::windows {
134        #[cfg_attr(feature = "serde", serde(rename = "8BPP"))]
135        BitsPerPixel8 => DISPLAYCONFIG_PIXELFORMAT_8BPP,
136        #[cfg_attr(feature = "serde", serde(rename = "16BPP"))]
137        BitsPerPixel16 => DISPLAYCONFIG_PIXELFORMAT_16BPP,
138        #[cfg_attr(feature = "serde", serde(rename = "24BPP"))]
139        BitsPerPixel24 => DISPLAYCONFIG_PIXELFORMAT_24BPP,
140        #[cfg_attr(feature = "serde", serde(rename = "32BPP"))]
141        BitsPerPixel32 => DISPLAYCONFIG_PIXELFORMAT_32BPP,
142        NONGDI => DISPLAYCONFIG_PIXELFORMAT_NONGDI,
143    }
144);
145
146define_windows_mapped_struct!(
147    Position => POINTL in windows_ccd::windows {
148        x => x: i32,
149        y => y: i32,
150    }
151);
152
153define_windows_mapped_enum!(
154    Rotation => DISPLAYCONFIG_ROTATION in windows_ccd::windows {
155        IDENTITY => DISPLAYCONFIG_ROTATION_IDENTITY,
156        ROTATE90 => DISPLAYCONFIG_ROTATION_ROTATE90,
157        ROTATE180 => DISPLAYCONFIG_ROTATION_ROTATE180,
158        ROTATE270 => DISPLAYCONFIG_ROTATION_ROTATE270,
159    }
160);
161
162define_windows_mapped_enum!(
163    Scaling => DISPLAYCONFIG_SCALING in windows_ccd::windows {
164        IDENTITY => DISPLAYCONFIG_SCALING_IDENTITY,
165        CENTERED => DISPLAYCONFIG_SCALING_CENTERED,
166        STRETCHED => DISPLAYCONFIG_SCALING_STRETCHED,
167        ASPECTRATIOCENTEREDMAX => DISPLAYCONFIG_SCALING_ASPECTRATIOCENTEREDMAX,
168        CUSTOM => DISPLAYCONFIG_SCALING_CUSTOM,
169        PREFERRED => DISPLAYCONFIG_SCALING_PREFERRED,
170    }
171);
172
173define_windows_mapped_struct!(
174    Rational => DISPLAYCONFIG_RATIONAL in windows_ccd::windows {
175        numerator => Numerator: u32,
176        denominator => Denominator: u32,
177    }
178);
179
180define_windows_mapped_struct!(
181    Rect => RECTL in windows_ccd::windows {
182        left => left: i32,
183        top => top: i32,
184        right => right: i32,
185        bottom => bottom: i32,
186    }
187);