Skip to content

Commit

Permalink
Merge pull request #3 from andrewdavidmackenzie/main
Browse files Browse the repository at this point in the history
Update edition, fix clippies and add an example
  • Loading branch information
haimgel authored Oct 21, 2023
2 parents 30afd62 + 865e40c commit 0a3b8fe
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 41 deletions.
20 changes: 12 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@
name = "ddc-macos"
version = "0.2.0"
authors = ["Haim Gelfenbeyn <[email protected]>"]
edition = "2018"
description = "DDC/CI monitor control on MacOS"
documentation = "http://haimgel.github.io/ddc-macos-rs/ddc_macos"
readme = "README.md"
repository = "https://github.com/haimgel/ddc-macos-rs"
license = "MIT"
keywords = ["ddc", "mccs", "vcp", "vesa", "macos"]
categories = ["hardware-support", "os::macos-apis"]
edition = "2021"

[dependencies]
core-foundation = "^0.9.1"
core-foundation-sys = "^0.8.1"
core-graphics = "^0.22.1"
ddc = "^0.2.0"
io-kit-sys = "0.1.0"
mach = "^0.3.2"
thiserror = "^1"
core-foundation = "0.9"
core-foundation-sys = "0.8"
core-graphics = "0.23"
ddc = "0.2"
io-kit-sys = "0.3"
mach = "0.3"
thiserror = "1"

[dev-dependencies]
edid-rs = "0.1"
nom = "*"

[badges]
maintenance = { status = "actively-developed" }
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

`ddc-macos` implements the [`ddc`](https://crates.io/crates/ddc) traits for MacOS.

## Examples
You can list external monitors and their description using the provided example using:

`cargo run --example list`

## [Documentation][docs]

See the [documentation][docs] for up to date information.
Expand Down
37 changes: 37 additions & 0 deletions examples/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
extern crate ddc;
extern crate ddc_macos;

use ddc::Ddc;
use ddc_macos::Monitor;

fn main() {
let monitors = Monitor::enumerate()
.expect("Could not enumerate external monitors");

if monitors.is_empty() {
println!("No external monitors found");
} else {
for mut monitor in monitors {
println!("Monitor");
println!("\tDescription: {}", monitor.description());
if let Some(desc) = monitor.product_name() {
println!("\tProduct Name: {}", desc);
}
if let Some(number) = monitor.serial_number() {
println!("\tSerial Number: {}", number);
}
if let Ok(input) = monitor.get_vcp_feature(0x60) {
println!("\tCurrent input: {:04x}", input.value());
}

if let Some(data) = monitor.edid() {
let mut cursor = std::io::Cursor::new(&data);
let mut reader = edid_rs::Reader::new(&mut cursor);
match edid_rs::EDID::parse(&mut reader) {
Ok(edid) => println!("\tEDID Info: {:?}", edid),
_ => println!("\tCould not parse provided EDID information"),
}
}
}
}
}
8 changes: 4 additions & 4 deletions src/iokit/wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ impl From<io_object_t> for IoObject {
}
}

impl Into<io_object_t> for &IoObject {
fn into(self) -> io_object_t {
self.0
impl From<&IoObject> for io_object_t {
fn from(val: &IoObject) -> io_object_t {
val.0
}
}

Expand Down Expand Up @@ -66,7 +66,7 @@ impl IoIterator {
unsafe {
kern_try!(IOServiceGetMatchingServices(kIOMasterPortDefault, dict as _, &mut iter));
}
Ok(Self { 0: iter })
Ok(Self(iter))
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![doc(html_root_url = "http://haimgel.github.io/ddc-macos-rs/")]

//! Implementation of DDC/CI traits on MacOS.
//!
//! # Example
Expand Down
58 changes: 29 additions & 29 deletions src/monitor.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#![deny(missing_docs)]
#![doc(html_root_url = "http://haimgel.github.io/ddc-macos-rs/")]

use crate::iokit::display::*;
use crate::iokit::io2c_interface::*;
Expand Down Expand Up @@ -79,39 +78,40 @@ impl Monitor {
Monitor { monitor, frame_buffer }
}

/// Enumerate all connected physical monitors.
pub fn enumerate() -> std::result::Result<Vec<Self>, Error> {
unsafe {
let displays = CGDisplay::active_displays()
.map_err(Error::from)?
.into_iter()
.map(|display_id| {
let display = CGDisplay::new(display_id);
let frame_buffer = Self::get_io_framebuffer_port(display)?;
Some(Self::new(display, frame_buffer))
/// Enumerate all connected physical monitors returning [Vec<Monitor>]
pub fn enumerate() -> Result<Vec<Self>, Error> {
let monitors = CGDisplay::active_displays()
.map_err(Error::from)?
.into_iter()
.filter_map(|display_id| {
let display = CGDisplay::new(display_id);
let frame_buffer = Self::get_io_framebuffer_port(display)?;
Some(Self::new(display, frame_buffer))
})
.filter_map(|x| x)
.collect();
Ok(displays)
}
.collect();
Ok(monitors)
}

/// Physical monitor description string.
/// Physical monitor description string. If it cannot get the product's name it will use
/// the vendor number and model number to form a description
pub fn description(&self) -> String {
let name = self.product_name().unwrap_or(format!(
self.product_name().unwrap_or(format!(
"{:04x}:{:04x}",
self.monitor.vendor_number(),
self.monitor.model_number()
));
))
}

/// Serial number for this [Monitor]
pub fn serial_number(&self) -> Option<String> {
let serial = self.monitor.serial_number();
if serial != 0 {
format!("{} S/N {}", name, serial)
} else {
name
match serial {
0 => None,
_ => Some(format!("{}", serial))
}
}

/// Product name for this monitor.
/// Product name for this [Monitor], if available
pub fn product_name(&self) -> Option<String> {
let info = Self::display_info_dict(&self.frame_buffer)?;
let display_product_name_key = CFString::from_static_string("DisplayProductName");
Expand All @@ -122,7 +122,7 @@ impl Monitor {
.map(|name| unsafe { CFString::wrap_under_get_rule(*name as CFStringRef) }.to_string())
}

/// Returns EDID for this display as raw bytes data
/// Returns Extended display identification data (EDID) for this [Monitor] as raw bytes data
pub fn edid(&self) -> Option<Vec<u8>> {
let info = Self::display_info_dict(&self.frame_buffer)?;
let display_product_name_key = CFString::from_static_string("IODisplayEDIDOriginal");
Expand All @@ -142,7 +142,7 @@ impl Monitor {
}
}

/// Finds a framebuffer that matches display, returns a properly formatted *unique* display name
// Finds a framebuffer that matches display, returns a properly formatted *unique* display name
fn framebuffer_port_matches_display(port: &IoObject, display: CGDisplay) -> Option<()> {
let mut bus_count: IOItemCount = 0;
unsafe {
Expand Down Expand Up @@ -180,7 +180,7 @@ impl Monitor {
}

// Gets the framebuffer port
unsafe fn get_io_framebuffer_port(display: CGDisplay) -> Option<IoObject> {
fn get_io_framebuffer_port(display: CGDisplay) -> Option<IoObject> {
if display.is_builtin() {
return None;
}
Expand Down Expand Up @@ -249,7 +249,7 @@ impl DdcHost for Monitor {
}

impl DdcCommand for Monitor {
fn execute<C: Command>(&mut self, command: C) -> std::result::Result<<C as Command>::Ok, Self::Error> {
fn execute<C: Command>(&mut self, command: C) -> Result<<C as Command>::Ok, Self::Error> {
// Encode the command into request_data buffer
// 36 bytes is an arbitrary number, larger than any I2C command length.
// Cannot use [0u8; C::MAX_LEN] (associated constants do not work here)
Expand Down Expand Up @@ -284,7 +284,7 @@ impl DdcCommand for Monitor {
}

if request.replyTransactionType == kIOI2CNoTransactionType {
ddc::CommandResult::decode(&[0u8; 0]).map_err(From::from)
CommandResult::decode(&[0u8; 0]).map_err(From::from)
} else {
let reply_length = (reply_data[1] & 0x7f) as usize;
if reply_length + 2 >= reply_data.len() {
Expand All @@ -299,7 +299,7 @@ impl DdcCommand for Monitor {
if reply_data[2 + reply_length] != checksum {
return Err(Error::Ddc(ErrorCode::InvalidChecksum));
}
ddc::CommandResult::decode(&reply_data[2..reply_length + 2]).map_err(From::from)
CommandResult::decode(&reply_data[2..reply_length + 2]).map_err(From::from)
}
}
}
Expand Down
Binary file added tests/edid/dell.edid.bin
Binary file not shown.

0 comments on commit 0a3b8fe

Please sign in to comment.