use serde::Serialize;
use std::collections::BTreeMap;
use std::fmt::Formatter;
use std::hash::Hash;
use serde_repr::{Deserialize_repr, Serialize_repr};
use serde_tuple::{Deserialize_tuple, Serialize_tuple};
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Deserialize_repr, Serialize_repr)]
#[repr(u16)]
pub enum Rotation {
Zero = 0,
Ninety = 90,
OneEighty = 180,
TwoSeventy = 270,
}
impl Rotation {
pub const VALUES: [Rotation; 4] = {
use Rotation::*;
[Zero, Ninety, OneEighty, TwoSeventy]
};
}
impl From<Rotation> for f64 {
fn from(value: Rotation) -> Self {
(value as i16) as f64
}
}
impl From<Rotation> for i32 {
fn from(value: Rotation) -> Self {
value as i32
}
}
impl std::fmt::Display for Rotation {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", (*self as i32))
}
}
impl TryFrom<f64> for Rotation {
type Error = String;
fn try_from(value: f64) -> Result<Self, Self::Error> {
Rotation::VALUES
.into_iter()
.find(|&rotation| {
let fvalue: f64 = rotation.into();
value == fvalue
})
.ok_or(format!(
"{} is not currently an allowed Rotation value.",
value
))
}
}
#[derive(Debug, Hash, PartialEq, Eq, Clone, Deserialize_tuple, Serialize_tuple)]
pub struct Point {
pub x: i64,
pub y: i64,
}
impl std::fmt::Display for Point {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
#[derive(Debug, Clone)]
pub enum Error {
UnknownUUID(String),
DuplicateConfiguration(String),
InvalidBrightness(f32),
InvalidTransactionState,
Poisoned(String),
Internal(String),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
use Error::*;
match self {
UnknownUUID(uuid) => {
write!(
f,
"Attempted to configure a non-existent display with UUID {}",
uuid
)
}
DuplicateConfiguration(uuid) => {
write!(
f,
"Attempted to change a setting more than once on display \
with UUID {}",
uuid
)
}
InvalidBrightness(value) => {
write!(
f,
"Invalid brightness value: {}. Must be between 0.0 and 1.0.",
value
)
}
InvalidTransactionState => {
write!(
f,
"While attempting to configure displays the configuration \
transaction became invalid."
)
}
Poisoned(msg) => {
write!(f, "Lock poison error: {}", msg)
}
Internal(msg) => write!(f, "{}", msg),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
pub trait DisplayMode: Clone + std::fmt::Debug + Serialize {
fn scaled(&self) -> bool;
fn color_depth(&self) -> usize;
fn frequency(&self) -> usize;
fn extents(&self) -> &Point;
fn match_pattern(&self, pattern: &DisplayModePattern) -> bool {
pattern.scaled.iter().all(|&s| s == self.scaled())
&& pattern.color_depth.iter().all(|&d| d == self.color_depth())
&& pattern.frequency.iter().all(|&f| f == self.frequency())
&& pattern.extents.iter().all(|p| p == self.extents())
}
}
#[derive(Debug, Clone)]
pub struct DisplayModePattern {
pub scaled: Option<bool>,
pub color_depth: Option<usize>,
pub frequency: Option<usize>,
pub extents: Option<Point>,
}
pub trait Display: std::fmt::Debug {
fn uuid(&self) -> &str;
fn mirror_of(&self) -> Option<&str>;
fn enabled(&self) -> bool;
fn origin(&self) -> &Point;
fn rotation(&self) -> Rotation;
fn brightness(&self) -> Option<f32>;
type DisplayModeType: DisplayMode;
fn current_mode(&self) -> &Self::DisplayModeType;
fn possible_modes(&self) -> &[Self::DisplayModeType];
fn matching_modes(&self, pattern: &DisplayModePattern) -> Vec<Self::DisplayModeType> {
self.possible_modes()
.iter()
.flat_map(|m| {
if m.match_pattern(pattern) {
Some(m.clone())
} else {
None
}
})
.collect()
}
}
pub trait DisplayConfigTransaction {
type DisplayModeType: DisplayMode;
fn set_mirroring(&mut self, uuid: &str, mirror_of_uuid: Option<&str>) -> Result<(), Error>;
fn set_mode(&mut self, uuid: &str, mode: &Self::DisplayModeType) -> Result<(), Error>;
fn set_rotation(&mut self, uuid: &str, rotation: Rotation) -> Result<(), Error>;
fn set_brightness(&mut self, uuid: &str, brightness: f32) -> Result<(), Error>;
fn set_origin(&mut self, uuid: &str, point: &Point) -> Result<(), Error>;
fn set_enabled(&mut self, uuid: &str, enabled: bool) -> Result<(), Error>;
fn commit(self) -> Result<(), Error>;
}
pub trait DisplayState: Sized {
fn current() -> Result<Self, Error>;
type DisplayModeType: DisplayMode;
type DisplayType: Display<DisplayModeType = Self::DisplayModeType>;
type DisplayConfigTransactionType: DisplayConfigTransaction<
DisplayModeType = Self::DisplayModeType,
>;
fn get_displays(&self) -> &BTreeMap<String, Self::DisplayType>;
fn configure(&self) -> Result<Self::DisplayConfigTransactionType, Error>;
}