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: 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(|d| Monitor::new(d))
55 .collect()
56 }
57
58 fn is_dry_run() -> bool {
59 unsafe { return 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 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 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 current_input_source(&mut self) -> anyhow::Result<InputSourceRaw> {
118 let feature_code: FeatureCode = self.feature_code(INPUT_SELECT);
119 Ok(self.ddc_hi_display.handle.get_vcp_feature(feature_code)?.sl)
120 }
121
122 pub fn set_current_input_source(&mut self, value: InputSourceRaw) -> anyhow::Result<()> {
124 info!(
125 "InputSource({self}) = {value}{mode}",
126 value = InputSource::str_from_raw(value),
127 mode = if Self::is_dry_run() { " (dry-run)" } else { "" }
128 );
129 if Self::is_dry_run() {
130 return Ok(());
131 }
132 let feature_code: FeatureCode = self.feature_code(INPUT_SELECT);
133 self.ddc_hi_display
134 .handle
135 .set_vcp_feature(feature_code, value as u16)
136 .inspect(|_| self.needs_sleep = true)
137 }
138
139 pub fn input_sources(&mut self) -> Option<Vec<InputSourceRaw>> {
142 if let Some(feature) = self.feature_descriptor(INPUT_SELECT) {
143 trace!("INPUT_SELECT({self}) = {feature:?}");
144 if let mccs_db::ValueType::NonContinuous { values, .. } = &feature.ty {
145 return Some(values.keys().cloned().collect());
146 }
147 }
148 None
149 }
150
151 pub fn sleep_if_needed(&mut self) {
154 if self.needs_sleep {
155 debug!("sleep({self})");
156 let start_time = Instant::now();
157 self.needs_sleep = false;
158 self.ddc_hi_display.handle.sleep();
159 debug!("sleep({self}) elapsed {:?}", start_time.elapsed());
160 }
161 }
162
163 pub fn to_long_string(&mut self) -> String {
165 let mut lines = Vec::new();
166 lines.push(self.to_string());
167 let input_source = self.current_input_source();
168 lines.push(format!(
169 "Input Source: {}",
170 match input_source {
171 Ok(value) => InputSource::str_from_raw(value),
172 Err(e) => e.to_string(),
173 }
174 ));
175 if let Some(input_sources) = self.input_sources() {
176 lines.push(format!(
177 "Input Sources: {}",
178 input_sources
179 .iter()
180 .map(|value| InputSource::str_from_raw(*value))
181 .collect::<Vec<_>>()
182 .join(", ")
183 ));
184 }
185 if let Some(model) = &self.ddc_hi_display.info.model_name {
186 lines.push(format!("Model: {}", model));
187 }
188 lines.push(format!("Backend: {}", self.ddc_hi_display.info.backend));
189 return lines.join("\n ");
190 }
191}