use std::time::Instant;
use super::*;
use ddc_hi::{Ddc, DdcHost, FeatureCode};
use log::*;
const INPUT_SELECT: FeatureCode = 0x60;
static mut DRY_RUN: bool = false;
pub struct Monitor {
ddc_hi_display: ddc_hi::Display,
is_capabilities_updated: bool,
needs_sleep: bool,
}
impl std::fmt::Display for Monitor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.ddc_hi_display.info.id)
}
}
impl std::fmt::Debug for Monitor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Monitor")
.field("info", &self.ddc_hi_display.info)
.finish()
}
}
impl Monitor {
pub fn new(ddc_hi_display: ddc_hi::Display) -> Self {
Monitor {
ddc_hi_display,
is_capabilities_updated: false,
needs_sleep: false,
}
}
pub fn enumerate() -> Vec<Self> {
ddc_hi::Display::enumerate()
.into_iter()
.map(Monitor::new)
.collect()
}
fn is_dry_run() -> bool {
unsafe { DRY_RUN }
}
pub fn set_dry_run(value: bool) {
unsafe { DRY_RUN = value }
}
pub fn update_capabilities(&mut self) -> anyhow::Result<()> {
if self.is_capabilities_updated {
return Ok(());
}
self.is_capabilities_updated = true;
debug!("update_capabilities({self})");
let start_time = Instant::now();
let result = self
.ddc_hi_display
.update_capabilities()
.inspect_err(|e| warn!("{self}: Failed to update capabilities: {e}"));
debug!(
"update_capabilities({self}) elapsed: {:?}",
start_time.elapsed()
);
result
}
pub(crate) fn contains_backend(&self, backend: &str) -> bool {
self.ddc_hi_display
.info
.backend
.to_string()
.contains(backend)
}
pub(crate) fn contains(&self, name: &str) -> bool {
self.ddc_hi_display.info.id.contains(name)
}
fn feature_descriptor(&self, feature_code: FeatureCode) -> Option<&mccs_db::Descriptor> {
self.ddc_hi_display.info.mccs_database.get(feature_code)
}
fn feature_code(&self, feature_code: FeatureCode) -> FeatureCode {
if let Some(feature) = self.feature_descriptor(feature_code) {
return feature.code;
}
feature_code
}
pub fn input_source(&mut self) -> anyhow::Result<InputSourceRaw> {
let feature_code: FeatureCode = self.feature_code(INPUT_SELECT);
Ok(self.ddc_hi_display.handle.get_vcp_feature(feature_code)?.sl)
}
pub fn set_input_source(&mut self, value: InputSourceRaw) -> anyhow::Result<()> {
info!(
"InputSource({self}) = {value}{mode}",
value = InputSource::str_from_raw(value),
mode = if Self::is_dry_run() { " (dry-run)" } else { "" }
);
if Self::is_dry_run() {
return Ok(());
}
let feature_code: FeatureCode = self.feature_code(INPUT_SELECT);
self.ddc_hi_display
.handle
.set_vcp_feature(feature_code, value as u16)
.inspect(|_| self.needs_sleep = true)
}
pub fn input_sources(&mut self) -> Option<Vec<InputSourceRaw>> {
if let Some(feature) = self.feature_descriptor(INPUT_SELECT) {
trace!("INPUT_SELECT({self}) = {feature:?}");
if let mccs_db::ValueType::NonContinuous { values, .. } = &feature.ty {
return Some(values.keys().cloned().collect());
}
}
None
}
pub fn sleep_if_needed(&mut self) {
if self.needs_sleep {
debug!("sleep({self})");
let start_time = Instant::now();
self.needs_sleep = false;
self.ddc_hi_display.handle.sleep();
debug!("sleep({self}) elapsed {:?}", start_time.elapsed());
}
}
pub fn to_long_string(&mut self) -> String {
let mut lines = Vec::new();
lines.push(self.to_string());
let input_source = self.input_source();
lines.push(format!(
"Input Source: {}",
match input_source {
Ok(value) => InputSource::str_from_raw(value),
Err(e) => e.to_string(),
}
));
if let Some(input_sources) = self.input_sources() {
lines.push(format!(
"Input Sources: {}",
input_sources
.iter()
.map(|value| InputSource::str_from_raw(*value))
.collect::<Vec<_>>()
.join(", ")
));
}
if let Some(model) = &self.ddc_hi_display.info.model_name {
lines.push(format!("Model: {model}"));
}
lines.push(format!("Backend: {}", self.ddc_hi_display.info.backend));
lines.join("\n ")
}
}