1use std::time::Instant;
2
3use super::*;
4use ddc_hi::{Ddc, DdcHost, FeatureCode};
5use log::*;
6
7const INPUT_SELECT: FeatureCode = 0x60;
9
10static mut DRY_RUN: bool = false;
11
12pub struct Monitor {
20 ddc_hi_display: ddc_hi::Display,
21 is_capabilities_updated: bool,
22 needs_sleep: bool,
23}
24
25impl std::fmt::Display for Monitor {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 write!(f, "{}", self.ddc_hi_display.info.id)
28 }
29}
30
31impl std::fmt::Debug for Monitor {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 f.debug_struct("Monitor")
34 .field("info", &self.ddc_hi_display.info)
35 .finish()
36 }
37}
38
39impl Monitor {
40 pub fn new(ddc_hi_display: ddc_hi::Display) -> Self {
42 Monitor {
43 ddc_hi_display,
44 is_capabilities_updated: false,
45 needs_sleep: false,
46 }
47 }
48
49 pub fn enumerate() -> Vec<Self> {
52 ddc_hi::Display::enumerate()
53 .into_iter()
54 .map(Monitor::new)
55 .collect()
56 }
57
58 fn is_dry_run() -> bool {
59 unsafe { DRY_RUN }
60 }
61
62 pub fn set_dry_run(value: bool) {
66 unsafe { DRY_RUN = value }
67 }
68
69 pub fn update_capabilities(&mut self) -> anyhow::Result<()> {
73 if self.is_capabilities_updated {
74 return Ok(());
75 }
76 self.is_capabilities_updated = true;
77 debug!("update_capabilities({self})");
78 let start_time = Instant::now();
79 let result = self
80 .ddc_hi_display
81 .update_capabilities()
82 .inspect_err(|e| warn!("{self}: Failed to update capabilities: {e}"));
83 debug!(
84 "update_capabilities({self}) elapsed: {:?}",
85 start_time.elapsed()
86 );
87 result
88 }
89
90 pub(crate) fn contains_backend(&self, backend: &str) -> bool {
91 self.ddc_hi_display
92 .info
93 .backend
94 .to_string()
95 .contains(backend)
96 }
97
98 pub(crate) fn contains(&self, name: &str) -> bool {
99 self.ddc_hi_display.info.id.contains(name)
100 }
101
102 fn feature_descriptor(&self, feature_code: FeatureCode) -> Option<&mccs_db::Descriptor> {
103 self.ddc_hi_display.info.mccs_database.get(feature_code)
104 }
105
106 fn feature_code(&self, feature_code: FeatureCode) -> FeatureCode {
107 if let Some(feature) = self.feature_descriptor(feature_code) {
111 return feature.code;
112 }
113 feature_code
114 }
115
116 pub fn input_source(&mut self) -> anyhow::Result<InputSourceRaw> {
126 let feature_code: FeatureCode = self.feature_code(INPUT_SELECT);
127 Ok(self.ddc_hi_display.handle.get_vcp_feature(feature_code)?.sl)
128 }
129
130 pub fn set_input_source(&mut self, value: InputSourceRaw) -> anyhow::Result<()> {
145 info!(
146 "InputSource({self}) = {value}{mode}",
147 value = InputSource::str_from_raw(value),
148 mode = if Self::is_dry_run() { " (dry-run)" } else { "" }
149 );
150 if Self::is_dry_run() {
151 return Ok(());
152 }
153 let feature_code: FeatureCode = self.feature_code(INPUT_SELECT);
154 self.ddc_hi_display
155 .handle
156 .set_vcp_feature(feature_code, value as u16)
157 .inspect(|_| self.needs_sleep = true)
158 }
159
160 pub fn input_sources(&mut self) -> Option<Vec<InputSourceRaw>> {
163 if let Some(feature) = self.feature_descriptor(INPUT_SELECT) {
164 trace!("INPUT_SELECT({self}) = {feature:?}");
165 if let mccs_db::ValueType::NonContinuous { values, .. } = &feature.ty {
166 return Some(values.keys().cloned().collect());
167 }
168 }
169 None
170 }
171
172 pub fn sleep_if_needed(&mut self) {
175 if self.needs_sleep {
176 debug!("sleep({self})");
177 let start_time = Instant::now();
178 self.needs_sleep = false;
179 self.ddc_hi_display.handle.sleep();
180 debug!("sleep({self}) elapsed {:?}", start_time.elapsed());
181 }
182 }
183
184 pub fn to_long_string(&mut self) -> String {
186 let mut lines = Vec::new();
187 lines.push(self.to_string());
188 let input_source = self.input_source();
189 lines.push(format!(
190 "Input Source: {}",
191 match input_source {
192 Ok(value) => InputSource::str_from_raw(value),
193 Err(e) => e.to_string(),
194 }
195 ));
196 if let Some(input_sources) = self.input_sources() {
197 lines.push(format!(
198 "Input Sources: {}",
199 input_sources
200 .iter()
201 .map(|value| InputSource::str_from_raw(*value))
202 .collect::<Vec<_>>()
203 .join(", ")
204 ));
205 }
206 if let Some(model) = &self.ddc_hi_display.info.model_name {
207 lines.push(format!("Model: {model}"));
208 }
209 lines.push(format!("Backend: {}", self.ddc_hi_display.info.backend));
210 lines.join("\n ")
211 }
212}