1#![deny(missing_docs)]
2
3use crate::error::Error;
4use crate::iokit::CoreDisplay_DisplayCreateInfoDictionary;
5use crate::iokit::IoObject;
6use crate::{arm, intel};
7use core_foundation::base::{CFType, TCFType};
8use core_foundation::data::CFData;
9use core_foundation::dictionary::CFDictionary;
10use core_foundation::string::{CFString, CFStringRef};
11use core_graphics::base::CGError;
12use core_graphics::display::{CGDirectDisplayID, CGDisplay};
13use ddc::{
14 DdcCommand, DdcCommandMarker, DdcCommandRaw, DdcCommandRawMarker, DdcHost, Delay, ErrorCode, I2C_ADDRESS_DDC_CI,
15 SUB_ADDRESS_DDC_CI,
16};
17use std::time::Duration;
18use std::{fmt, iter};
19
20extern "C" {
21 fn CGGetOnlineDisplayList(max_displays: u32, online: *mut CGDirectDisplayID, count: *mut u32) -> CGError;
25}
26
27fn online_display_ids() -> Result<Vec<CGDirectDisplayID>, Error> {
31 let mut count: u32 = 0;
32 let err: CGError = unsafe { CGGetOnlineDisplayList(0, std::ptr::null_mut(), &mut count) };
33 if err != 0 {
34 return Err(Error::from(err));
35 }
36 let mut ids = vec![0 as CGDirectDisplayID; count as usize];
37 let err: CGError = unsafe { CGGetOnlineDisplayList(count, ids.as_mut_ptr(), &mut count) };
38 if err != 0 {
39 return Err(Error::from(err));
40 }
41 ids.truncate(count as usize);
42 Ok(ids)
43}
44
45#[derive(Debug)]
47enum MonitorService {
48 Intel(IoObject),
49 Arm(arm::IOAVService),
50}
51
52#[derive(Debug)]
54pub struct Monitor {
55 monitor: CGDisplay,
56 service: MonitorService,
57 i2c_address: u16,
58 delay: Delay,
59}
60
61impl fmt::Display for Monitor {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 write!(f, "{}", self.description())
64 }
65}
66
67impl Monitor {
68 fn new(monitor: CGDisplay, service: MonitorService, i2c_address: u16) -> Self {
70 Monitor {
71 monitor,
72 service,
73 i2c_address,
74 delay: Default::default(),
75 }
76 }
77
78 pub fn enumerate() -> Result<Vec<Self>, Error> {
80 let monitors = online_display_ids()?
81 .into_iter()
82 .filter_map(|display_id| {
83 let display = CGDisplay::new(display_id);
84 return if let Some(service) = intel::get_io_framebuffer_port(display) {
85 Some(Self::new(display, MonitorService::Intel(service), I2C_ADDRESS_DDC_CI))
86 } else if let Ok((service, i2c_address)) = arm::get_display_av_service(display) {
87 Some(Self::new(display, MonitorService::Arm(service), i2c_address))
88 } else {
89 None
90 };
91 })
92 .collect();
93 Ok(monitors)
94 }
95
96 pub fn description(&self) -> String {
99 self.product_name().unwrap_or(format!(
100 "{:04x}:{:04x}",
101 self.monitor.vendor_number(),
102 self.monitor.model_number()
103 ))
104 }
105
106 pub fn serial_number(&self) -> Option<String> {
108 let serial = self.monitor.serial_number();
109 match serial {
110 0 => None,
111 _ => Some(format!("{}", serial)),
112 }
113 }
114
115 pub fn product_name(&self) -> Option<String> {
117 let info: CFDictionary<CFString, CFType> =
118 unsafe { CFDictionary::wrap_under_create_rule(CoreDisplay_DisplayCreateInfoDictionary(self.monitor.id)) };
119
120 let display_product_name_key = CFString::from_static_string("DisplayProductName");
121 let display_product_names_dict = info.find(&display_product_name_key)?.downcast::<CFDictionary>()?;
122 let (_, localized_product_names) = display_product_names_dict.get_keys_and_values();
123 localized_product_names
124 .first()
125 .map(|name| unsafe { CFString::wrap_under_get_rule(*name as CFStringRef) }.to_string())
126 }
127
128 pub fn edid(&self) -> Option<Vec<u8>> {
130 let info: CFDictionary<CFString, CFType> =
131 unsafe { CFDictionary::wrap_under_create_rule(CoreDisplay_DisplayCreateInfoDictionary(self.monitor.id)) };
132 let display_product_name_key = CFString::from_static_string("IODisplayEDIDOriginal");
133 let edid_data = info.find(&display_product_name_key)?.downcast::<CFData>()?;
134 Some(edid_data.bytes().into())
135 }
136
137 pub fn handle(&self) -> CGDisplay {
139 self.monitor
140 }
141
142 fn encode_command<'a>(&self, data: &[u8], packet: &'a mut [u8]) -> &'a [u8] {
143 packet[0] = SUB_ADDRESS_DDC_CI;
144 packet[1] = 0x80 | data.len() as u8;
145 packet[2..2 + data.len()].copy_from_slice(data);
146 packet[2 + data.len()] =
147 Self::checksum(iter::once((self.i2c_address as u8) << 1).chain(packet[..2 + data.len()].iter().cloned()));
148 &packet[..3 + data.len()]
149 }
150
151 fn decode_response<'a>(&self, response: &'a mut [u8]) -> Result<&'a mut [u8], crate::error::Error> {
152 if response.is_empty() {
153 return Ok(response);
154 };
155 let len = (response[1] & 0x7f) as usize;
156 if len + 2 >= response.len() {
157 return Err(Error::Ddc(ErrorCode::InvalidLength));
158 }
159 let checksum = Self::checksum(
160 iter::once(((self.i2c_address << 1) | 1) as u8)
161 .chain(iter::once(SUB_ADDRESS_DDC_CI))
162 .chain(response[1..2 + len].iter().cloned()),
163 );
164 if response[2 + len] != checksum {
165 return Err(Error::Ddc(ErrorCode::InvalidChecksum));
166 }
167 Ok(&mut response[2..2 + len])
168 }
169}
170
171impl DdcHost for Monitor {
172 type Error = Error;
173
174 fn sleep(&mut self) {
175 self.delay.sleep()
176 }
177}
178
179impl DdcCommandRaw for Monitor {
180 fn execute_raw<'a>(
181 &mut self,
182 data: &[u8],
183 out: &'a mut [u8],
184 response_delay: Duration,
185 ) -> Result<&'a mut [u8], Self::Error> {
186 assert!(data.len() <= 36);
187 let mut packet = [0u8; 36 + 3];
188 let packet = self.encode_command(data, &mut packet);
189 let response = match &self.service {
190 MonitorService::Intel(service) => intel::execute(service, self.i2c_address, packet, out, response_delay),
191 MonitorService::Arm(service) => arm::execute(service, self.i2c_address, packet, out, response_delay),
192 }?;
193 self.decode_response(response)
194 }
195}
196
197impl DdcCommandMarker for Monitor {}
198
199impl DdcCommandRawMarker for Monitor {
200 fn set_sleep_delay(&mut self, delay: Delay) {
201 self.delay = delay;
202 }
203}