#![deny(missing_docs)]
#![deny(unsafe_code)]
#![warn(clippy::all)]
pub mod ddc_backends;
pub mod ddc_trait;
pub mod ddc_types;
pub mod error;
use crate::ddc_trait::DisplayDevice;
use log::{debug, warn};
pub fn list_monitors() -> Vec<DisplayDevice> {
let mut list: Vec<DisplayDevice> = Vec::new();
#[cfg(target_os = "windows")]
{
debug!("Enumerating DDC/CI monitors on Windows via EnumDisplayMonitors");
#[allow(unsafe_code)]
mod windows_ffi {
use crate::ddc_trait::DisplayDevice;
use log::{debug, warn};
use windows::Win32::Devices::Display::*;
use windows::Win32::Foundation::{BOOL, LPARAM, RECT};
use windows::Win32::Graphics::Gdi::*;
pub fn enumerate_windows_monitors() -> Vec<DisplayDevice> {
let mut list = Vec::new();
unsafe extern "system" fn callback(
h: HMONITOR,
_: HDC,
_: *mut RECT,
lp: LPARAM,
) -> BOOL {
let l = &mut *(lp.0 as *mut Vec<DisplayDevice>);
let mut count = 0;
if GetNumberOfPhysicalMonitorsFromHMONITOR(h, &mut count).is_ok() {
let mut physical_monitors =
vec![PHYSICAL_MONITOR::default(); count as usize];
if GetPhysicalMonitorsFromHMONITOR(h, &mut physical_monitors).is_ok() {
for monitor in physical_monitors {
let desc = monitor.szPhysicalMonitorDescription;
let len = desc.iter().position(|&c| c == 0).unwrap_or(desc.len());
if let Some(handle) =
std::ptr::NonNull::new(monitor.hPhysicalMonitor.0)
{
let info = String::from_utf16_lossy(&desc[..len]);
debug!("Found DDC/CI monitor: {}", info);
l.push(DisplayDevice {
info,
inner: Box::new(
crate::ddc_backends::ddc_win::WindowsBackend { handle },
),
});
} else {
warn!("Monitor had null physical handle, skipping");
}
}
}
}
BOOL::from(true)
}
unsafe {
let _ = EnumDisplayMonitors(
None,
None,
Some(callback),
LPARAM(&mut list as *mut _ as isize),
);
}
list
}
}
list.extend(windows_ffi::enumerate_windows_monitors());
}
#[cfg(target_os = "linux")]
{
use crate::ddc_backends::ddc_linux::LinuxBackend;
use crate::ddc_trait::DdcControl;
use std::fs;
debug!("Enumerating DDC/CI monitors on Linux via /dev/i2c-* scan");
if let Ok(entries) = fs::read_dir("/dev") {
for entry in entries.flatten() {
let file_name = entry.file_name().to_string_lossy().into_owned();
if file_name.starts_with("i2c-") {
let path = format!("/dev/{}", file_name);
if file_name.contains("smbus") {
continue;
}
let backend = LinuxBackend::new(path.clone());
if backend.get_vcp_feature(0x10).is_ok() {
debug!("Found DDC/CI monitor on bus: {}", file_name);
list.push(DisplayDevice {
info: format!("I2C Display Bus ({})", file_name),
inner: Box::new(backend),
});
}
}
}
}
}
list
}