use async_trait::async_trait;
use std::fmt;
use crate::backends::overlap;
use crate::error::{DisplayError, DisplayResult};
use crate::traits::{OutputEditable, UniversalTopology};
use crate::types::{
DisplayId, DisplayRotation, Extent2D, HdrMode, HdrState, OutputState, Point2D, Rect,
};
#[derive(Debug, Clone)]
pub struct KdeOutputState {
pub id: DisplayId,
pub connector_id: crate::types::ConnectorId,
pub adapter_id: crate::types::AdapterId,
pub geometry: Rect,
pub refresh_rate: u32,
pub scale: f64,
pub rotation: DisplayRotation,
pub enabled: bool,
pub is_primary: bool,
pub hdr_state: HdrState,
pub hdr_mode: HdrMode,
pub supported_modes: Vec<crate::types::VideoMode>,
}
impl crate::backends::overlap::OverlapCheckable for KdeOutputState {
fn is_enabled(&self) -> bool {
self.enabled
}
fn geometry(&self) -> Rect {
self.geometry
}
}
pub struct KdeTopology {
pub outputs: Vec<KdeOutputState>,
pub persistence_enabled: bool,
pub dirty: bool,
}
impl fmt::Debug for KdeTopology {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("KdeTopology")
.field("outputs_count", &self.outputs.len())
.field("persistence_enabled", &self.persistence_enabled)
.field("dirty", &self.dirty)
.finish()
}
}
macro_rules! find_kde_output {
($self:expr) => {{
let target_id = $self.target_id.clone();
$self
.topology
.outputs
.iter_mut()
.find(|o| o.id == target_id)
.ok_or_else(|| DisplayError::NotFound(target_id))
}};
}
pub struct KdeOutputEditor<'a> {
topology: &'a mut KdeTopology,
target_id: DisplayId,
}
impl<'a> OutputEditable for KdeOutputEditor<'a> {
fn set_rotation(
&mut self,
rotation: DisplayRotation,
) -> DisplayResult<&mut dyn OutputEditable> {
let out = find_kde_output!(self)?;
if out.rotation != rotation {
out.rotation = rotation;
self.topology.dirty = true;
}
Ok(self)
}
fn set_resolution(&mut self, extent: Extent2D) -> DisplayResult<&mut dyn OutputEditable> {
let out = find_kde_output!(self)?;
if out.geometry.size != extent {
out.geometry.size = extent;
self.topology.dirty = true;
}
Ok(self)
}
fn set_position(&mut self, position: Point2D) -> DisplayResult<&mut dyn OutputEditable> {
let out = find_kde_output!(self)?;
if out.geometry.origin != position {
out.geometry.origin = position;
self.topology.dirty = true;
}
Ok(self)
}
fn set_refresh_rate(&mut self, rate: u32) -> DisplayResult<&mut dyn OutputEditable> {
let out = find_kde_output!(self)?;
if out.refresh_rate != rate {
out.refresh_rate = rate;
self.topology.dirty = true;
}
Ok(self)
}
fn set_primary(&mut self) -> DisplayResult<&mut dyn OutputEditable> {
for out in &mut self.topology.outputs {
out.is_primary = false;
}
let out = find_kde_output!(self)?;
out.is_primary = true;
self.topology.dirty = true;
Ok(self)
}
fn set_hdr(
&mut self,
state: HdrState,
mode: HdrMode,
) -> DisplayResult<&mut dyn OutputEditable> {
let out = find_kde_output!(self)?;
if out.hdr_state != state || out.hdr_mode != mode {
out.hdr_state = state;
out.hdr_mode = mode;
self.topology.dirty = true;
}
Ok(self)
}
fn set_scale(&mut self, scale: f64) -> DisplayResult<&mut dyn OutputEditable> {
let out = find_kde_output!(self)?;
if (out.scale - scale).abs() > f64::EPSILON {
out.scale = scale;
self.topology.dirty = true;
}
Ok(self)
}
fn set_enabled(&mut self, enabled: bool) -> DisplayResult<&mut dyn OutputEditable> {
let out = find_kde_output!(self)?;
if out.enabled != enabled {
out.enabled = enabled;
self.topology.dirty = true;
}
Ok(self)
}
fn clone_from(&mut self, source_id: &DisplayId) -> DisplayResult<&mut dyn OutputEditable> {
let source = self
.topology
.outputs
.iter()
.find(|o| o.id == *source_id)
.ok_or_else(|| DisplayError::NotFound(source_id.clone()))?
.clone();
let dest = find_kde_output!(self)?;
dest.geometry = source.geometry;
dest.refresh_rate = source.refresh_rate;
dest.scale = source.scale;
dest.rotation = source.rotation;
dest.hdr_state = source.hdr_state;
dest.hdr_mode = source.hdr_mode;
self.topology.dirty = true;
Ok(self)
}
fn get_state(&self) -> OutputState {
self.topology
.outputs
.iter()
.find(|o| o.id == self.target_id)
.map(|o| OutputState {
identity: crate::types::DisplayIdentity {
id: o.id.clone(),
connector_id: o.connector_id.clone(),
adapter_id: o.adapter_id.clone(),
hardware_uuid: None,
monitor_name: format!("KDE Output ({})", o.connector_id.0),
},
geometry: o.geometry,
refresh_rate: o.refresh_rate,
rotation: o.rotation,
hdr_state: o.hdr_state,
hdr_mode: o.hdr_mode,
scale: o.scale,
native_resolution: Some(o.geometry.size),
supported_modes: o.supported_modes.clone(),
enabled: o.enabled,
is_primary: o.is_primary,
})
.unwrap_or_default()
}
}
#[async_trait]
impl UniversalTopology for KdeTopology {
fn acquire() -> DisplayResult<Self> {
Ok(Self {
outputs: Vec::new(),
persistence_enabled: false,
dirty: false,
})
}
fn get_outputs(&self) -> Vec<OutputState> {
self.outputs
.iter()
.map(|o| OutputState {
identity: crate::types::DisplayIdentity {
id: o.id.clone(),
connector_id: o.connector_id.clone(),
adapter_id: o.adapter_id.clone(),
hardware_uuid: None,
monitor_name: format!("KDE Output ({})", o.connector_id.0),
},
geometry: o.geometry,
refresh_rate: o.refresh_rate,
rotation: o.rotation,
hdr_state: o.hdr_state,
hdr_mode: o.hdr_mode,
scale: o.scale,
native_resolution: Some(o.geometry.size),
supported_modes: o.supported_modes.clone(),
enabled: o.enabled,
is_primary: o.is_primary,
})
.collect()
}
fn edit_output(&mut self, id: &DisplayId) -> DisplayResult<Box<dyn OutputEditable + '_>> {
if !self.outputs.iter().any(|o| o.id == *id) {
return Err(DisplayError::NotFound(id.clone()));
}
Ok(Box::new(KdeOutputEditor {
topology: self,
target_id: id.clone(),
}))
}
fn set_persistence(&mut self, enabled: bool) -> &mut Self {
self.persistence_enabled = enabled;
self
}
async fn validate(&self) -> DisplayResult<()> {
overlap::check_overlap(
&self
.outputs
.iter()
.map(|o| (o.enabled, o.geometry))
.collect::<Vec<(bool, Rect)>>(),
)
}
async fn commit(&mut self) -> DisplayResult<()> {
if !self.dirty {
return Ok(());
}
self.validate().await?;
self.dirty = false;
Ok(())
}
}