display-profile-lib 0.1.0

Provides functions and types to get and set a Windows display profile.
#![cfg_attr(all(doc, not(doctest)), feature(doc_cfg))]
#![expect(clippy::missing_errors_doc)]
#![doc = include_str!("../README.md")]

pub use error::Error;
pub use get_profile::get_profile;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub use set_profile::{SetProfileAction, set_profile};
use windows_ccd::windows::DISPLAYCONFIG_2DREGION;

mod error;
mod get_profile;
mod set_profile;
mod util;

const VIRTUAL_MODE_AWARE: bool = true;

pub type Result<T, E = Error> = std::result::Result<T, E>;

macro_rules! define_windows_mapped_struct {
    (
        $name:ident => $win_name:ident in $($win_path:ident)::+ {
            $(
                $field:ident => $win_field:ident : $field_type:ty ,
            )*
        }
    ) => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
        pub struct $name {
            $(
                pub $field: $field_type,
            )*
        }

        impl From<$($win_path::)+ $win_name> for $name {
            fn from(value: $($win_path::)+ $win_name) -> Self {
                $name {
                    $(
                        $field: value.$win_field,
                    )*
                }
            }
        }

        impl From<$name> for $($win_path::)+ $win_name {
            fn from(value: $name) -> Self {
                $($win_path::)+ $win_name {
                    $(
                        $win_field: value.$field,
                    )*
                }
            }
        }
    };
}

macro_rules! define_windows_mapped_enum {
    (
        $name:ident => $win_type:ident in $($win_path:ident)::+ {
            $(
                $( #[ $attribute:meta ] )?
                $variant:ident => $win_val:ident ,
            )*
        }
    ) => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
        pub enum $name {
            $(
                $( #[ $attribute ] )?
                $variant,
            )*
        }

        impl From<$($win_path::)+ $win_type> for $name {
            fn from(value: $($win_path::)+ $win_type) -> Self {
                use $($win_path)::+ as WinPath;
                match value {
                    $( WinPath::$win_val => $name::$variant, )*
                    _ => unreachable!(),
                }
            }
        }

        impl From<$name> for $($win_path::)+ $win_type {
            fn from(value: $name) -> Self {
                use $($win_path)::+ as WinPath;
                match value {
                    $( $name::$variant => WinPath::$win_val, )*
                }
            }
        }
    };
}

pub type Profile = Vec<Monitor>;

#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Monitor {
    pub friendly_device_name: String,
    pub source_device_name: String,
    pub device_path: String,
    pub dimensions: Dimensions,
    pub pixel_format: PixelFormat,
    pub position: Position,
    pub rotation: Rotation,
    pub scaling: Scaling,
    pub refresh_rate: Rational,
    pub path_source_size: Position,
    pub desktop_image_region: Rect,
    pub desktop_image_clip: Rect,
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Dimensions {
    pub width: u32,
    pub height: u32,
}
impl From<Dimensions> for DISPLAYCONFIG_2DREGION {
    fn from(value: Dimensions) -> Self {
        DISPLAYCONFIG_2DREGION {
            cx: value.width,
            cy: value.height,
        }
    }
}

define_windows_mapped_enum!(
    PixelFormat => DISPLAYCONFIG_PIXELFORMAT in windows_ccd::windows {
        #[cfg_attr(feature = "serde", serde(rename = "8BPP"))]
        BitsPerPixel8 => DISPLAYCONFIG_PIXELFORMAT_8BPP,
        #[cfg_attr(feature = "serde", serde(rename = "16BPP"))]
        BitsPerPixel16 => DISPLAYCONFIG_PIXELFORMAT_16BPP,
        #[cfg_attr(feature = "serde", serde(rename = "24BPP"))]
        BitsPerPixel24 => DISPLAYCONFIG_PIXELFORMAT_24BPP,
        #[cfg_attr(feature = "serde", serde(rename = "32BPP"))]
        BitsPerPixel32 => DISPLAYCONFIG_PIXELFORMAT_32BPP,
        NONGDI => DISPLAYCONFIG_PIXELFORMAT_NONGDI,
    }
);

define_windows_mapped_struct!(
    Position => POINTL in windows_ccd::windows {
        x => x: i32,
        y => y: i32,
    }
);

define_windows_mapped_enum!(
    Rotation => DISPLAYCONFIG_ROTATION in windows_ccd::windows {
        IDENTITY => DISPLAYCONFIG_ROTATION_IDENTITY,
        ROTATE90 => DISPLAYCONFIG_ROTATION_ROTATE90,
        ROTATE180 => DISPLAYCONFIG_ROTATION_ROTATE180,
        ROTATE270 => DISPLAYCONFIG_ROTATION_ROTATE270,
    }
);

define_windows_mapped_enum!(
    Scaling => DISPLAYCONFIG_SCALING in windows_ccd::windows {
        IDENTITY => DISPLAYCONFIG_SCALING_IDENTITY,
        CENTERED => DISPLAYCONFIG_SCALING_CENTERED,
        STRETCHED => DISPLAYCONFIG_SCALING_STRETCHED,
        ASPECTRATIOCENTEREDMAX => DISPLAYCONFIG_SCALING_ASPECTRATIOCENTEREDMAX,
        CUSTOM => DISPLAYCONFIG_SCALING_CUSTOM,
        PREFERRED => DISPLAYCONFIG_SCALING_PREFERRED,
    }
);

define_windows_mapped_struct!(
    Rational => DISPLAYCONFIG_RATIONAL in windows_ccd::windows {
        numerator => Numerator: u32,
        denominator => Denominator: u32,
    }
);

define_windows_mapped_struct!(
    Rect => RECTL in windows_ccd::windows {
        left => left: i32,
        top => top: i32,
        right => right: i32,
        bottom => bottom: i32,
    }
);