Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup #1

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
[package]
name = "ddc-winapi"
version = "0.3.0"
version = "0.3.1"
authors = ["arcnmx"]
edition = "2021"

description = "DDC/CI monitor control on Windows"
keywords = ["ddc", "mccs", "vcp", "vesa"]
Expand All @@ -17,5 +18,5 @@ maintenance = { status = "passively-maintained" }

[dependencies]
ddc = { version = "^0.2.0" }
winapi = { version = "^0.3.5", features = ["windef", "minwindef", "winuser", "physicalmonitorenumerationapi", "lowlevelmonitorconfigurationapi"] }
widestring = "^0.3.0"
widestring = "^0.4.0"
windows = { version = "0.25", features = ["Win32_Devices_Display", "Win32_Foundation", "Win32_Graphics_Gdi"] }
10 changes: 10 additions & 0 deletions examples/enum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use ddc::Ddc;
use ddc_winapi::Monitor;

fn main() {
let monitors = Monitor::enumerate().unwrap();
for mut m in monitors {
print!("{:?}: ", m);
println!("{:?}", m.get_timing_report());
}
}
215 changes: 102 additions & 113 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//! # Example
//!
//! ```rust,no_run
//! extern crate ddc;
//!
//! # fn main() {
//! use ddc::Ddc;
Expand All @@ -19,18 +18,24 @@
//! # }
//! ```

extern crate winapi;
extern crate ddc;
extern crate widestring;
use std::{fmt, mem, ptr};

use std::{io, ptr, mem, fmt};
use winapi::um::physicalmonitorenumerationapi::*;
use winapi::um::lowlevelmonitorconfigurationapi::*;
use winapi::shared::windef::{HMONITOR, HDC, LPRECT};
use winapi::shared::minwindef::{LPARAM, BYTE, DWORD, BOOL, TRUE};
use winapi::um::winnt::HANDLE;
use ddc::{Ddc, DdcHost, FeatureCode, TimingMessage, VcpValue};
use widestring::WideCString;
use ddc::{Ddc, DdcHost, FeatureCode, VcpValue, TimingMessage};
use windows::{
runtime::{Error as WinError, Result as WinResult},
Win32::{
Devices::Display::{
CapabilitiesRequestAndCapabilitiesReply, DestroyPhysicalMonitor,
GetCapabilitiesStringLength, GetNumberOfPhysicalMonitorsFromHMONITOR,
GetPhysicalMonitorsFromHMONITOR, GetTimingReport, GetVCPFeatureAndVCPFeatureReply,
SaveCurrentSettings, SetVCPFeature, MC_MOMENTARY, MC_SET_PARAMETER, MC_TIMING_REPORT,
MC_VCP_CODE_TYPE, PHYSICAL_MONITOR,
},
Foundation::{BOOL, HANDLE, LPARAM, PSTR, RECT},
Graphics::Gdi::{EnumDisplayMonitors, HDC, HMONITOR},
},
};

// TODO: good luck getting EDID: https://social.msdn.microsoft.com/Forums/vstudio/en-US/efc46c70-7479-4d59-822b-600cb4852c4b/how-to-locate-the-edid-data-folderkey-in-the-registry-which-belongs-to-a-specific-physicalmonitor?forum=wdk

Expand All @@ -41,29 +46,36 @@ pub struct Monitor {

impl Monitor {
/// Create a new monitor from the specified handle.
///
/// # Safety
///
/// The provided `monitor` must contain valid and well-constructed info:
///
/// - `monitor.hPhysicalMonitor` must be a valid HANDLE.
/// - `monitor.szPhysicalMonitorDescription` must contain a null-terminated
/// string.
pub unsafe fn new(monitor: PHYSICAL_MONITOR) -> Self {
Monitor {
monitor: monitor,
}
Monitor { monitor }
}

/// Enumerate all connected physical monitors.
pub fn enumerate() -> io::Result<Vec<Self>> {
enumerate_monitors().and_then(|mon|
mon.into_iter().map(|mon|
get_physical_monitors_from_hmonitor(mon).map(|mon|
mon.into_iter().map(|mon| unsafe { Monitor::new(mon) })
)
).collect::<io::Result<Vec<_>>>()
).map(|v| v.into_iter().flat_map(|mon| mon).collect())
pub fn enumerate() -> WinResult<Vec<Self>> {
enumerate_monitors()
.and_then(|mon| {
mon.into_iter()
.map(|mon| {
get_physical_monitors_from_hmonitor(mon)
.map(|mon| mon.into_iter().map(|mon| unsafe { Monitor::new(mon) }))
})
.collect::<WinResult<Vec<_>>>()
})
.map(|v| v.into_iter().flatten().collect())
}

/// Physical monitor description string.
pub fn description(&self) -> String {
unsafe {
WideCString::from_ptr_str(self.monitor.szPhysicalMonitorDescription.as_ptr())
.to_string_lossy()
}
let str_ptr = ptr::addr_of!(self.monitor.szPhysicalMonitorDescription);
unsafe { WideCString::from_ptr_str(str_ptr as _).to_string_lossy() }
}

/// Physical monitor winapi handle.
Expand All @@ -72,85 +84,71 @@ impl Monitor {
}

/// Retrieves a monitor's horizontal and vertical synchronization frequencies.
pub fn winapi_get_timing_report(&self) -> io::Result<MC_TIMING_REPORT> {
unsafe {
let mut report = mem::zeroed();
if GetTimingReport(self.handle(), &mut report) != TRUE {
Err(io::Error::last_os_error())
} else {
Ok(report)
}
}
pub fn winapi_get_timing_report(&self) -> WinResult<MC_TIMING_REPORT> {
let mut report = Default::default();
BOOL(unsafe { GetTimingReport(self.handle(), &mut report) }).ok()?;
Ok(report)
}

/// Sets the value of a Virtual Control Panel (VCP) code for a monitor.
pub fn winapi_set_vcp_feature(&self, code: BYTE, value: DWORD) -> io::Result<()> {
unsafe {
if SetVCPFeature(self.handle(), code, value) != TRUE {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn winapi_set_vcp_feature(&self, code: u8, value: u32) -> WinResult<()> {
BOOL(unsafe { SetVCPFeature(self.handle(), code, value) }).ok()?;
Ok(())
}

/// Saves the current monitor settings to the display's nonvolatile storage.
pub fn winapi_save_current_settings(&self) -> io::Result<()> {
unsafe {
if SaveCurrentSettings(self.handle()) != TRUE {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn winapi_save_current_settings(&self) -> WinResult<()> {
BOOL(unsafe { SaveCurrentSettings(self.handle()) }).ok()?;
Ok(())
}

/// Retrieves the current value, maximum value, and code type of a Virtual
/// Control Panel (VCP) code for a monitor.
///
/// Returns `(vcp_type, current_value, max_value)`
pub fn winapi_get_vcp_feature_and_vcp_feature_reply(&self, code: BYTE) -> io::Result<(MC_VCP_CODE_TYPE, DWORD, DWORD)> {
unsafe {
let mut ty = 0;
let mut current = 0;
let mut max = 0;
if GetVCPFeatureAndVCPFeatureReply(self.handle(), code, &mut ty, &mut current, &mut max) != TRUE {
Err(io::Error::last_os_error())
} else {
Ok((ty, current, max))
}
}
pub fn winapi_get_vcp_feature_and_vcp_feature_reply(
&self,
code: u8,
) -> WinResult<(MC_VCP_CODE_TYPE, u32, u32)> {
let mut ty = MC_VCP_CODE_TYPE::default();
let mut current = 0;
let mut max = 0;
BOOL(unsafe {
GetVCPFeatureAndVCPFeatureReply(self.handle(), code, &mut ty, &mut current, &mut max)
})
.ok()?;
Ok((ty, current, max))
}

/// Retrieves the length of the buffer to pass to
/// `winapi_capabilities_request_and_capabilities_reply`.
pub fn winapi_get_capabilities_string_length(&self) -> io::Result<DWORD> {
unsafe {
let mut len = 0;
if GetCapabilitiesStringLength(self.handle(), &mut len) != TRUE {
Err(io::Error::last_os_error())
} else {
Ok(len)
}
}
pub fn winapi_get_capabilities_string_length(&self) -> WinResult<u32> {
let mut len = 0;
BOOL(unsafe { GetCapabilitiesStringLength(self.handle(), &mut len) }).ok()?;
Ok(len)
}

/// Retrieves a string describing a monitor's capabilities.
///
/// This string is always ASCII and includes a terminating null character.
pub fn winapi_capabilities_request_and_capabilities_reply(&self, string: &mut [u8]) -> io::Result<()> {
unsafe {
if CapabilitiesRequestAndCapabilitiesReply(self.handle(), string.as_mut_ptr() as *mut _, string.len() as _) != TRUE {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn winapi_capabilities_request_and_capabilities_reply(
&self,
string: &mut [u8],
) -> WinResult<()> {
BOOL(unsafe {
CapabilitiesRequestAndCapabilitiesReply(
self.handle(),
PSTR(string.as_mut_ptr()),
string.len() as _,
)
})
.ok()?;
Ok(())
}
}

impl DdcHost for Monitor {
type Error = io::Error;
type Error = WinError;
}

impl Ddc for Monitor {
Expand Down Expand Up @@ -190,20 +188,17 @@ impl Ddc for Monitor {
}

fn get_timing_report(&mut self) -> Result<TimingMessage, Self::Error> {
self.winapi_get_timing_report()
.map(|timing| TimingMessage {
timing_status: timing.bTimingStatusByte,
horizontal_frequency: timing.dwHorizontalFrequencyInHZ as _,
vertical_frequency: timing.dwVerticalFrequencyInHZ as _,
})
self.winapi_get_timing_report().map(|timing| TimingMessage {
timing_status: timing.bTimingStatusByte,
horizontal_frequency: timing.dwHorizontalFrequencyInHZ as _,
vertical_frequency: timing.dwVerticalFrequencyInHZ as _,
})
}
}

impl Drop for Monitor {
fn drop(&mut self) {
unsafe {
DestroyPhysicalMonitor(self.handle());
}
let _ = unsafe { DestroyPhysicalMonitor(self.handle()) };
}
}

Expand All @@ -217,37 +212,31 @@ impl fmt::Debug for Monitor {
}

/// WinAPI `GetPhysicalMonitorsFromHMONITOR`
pub fn get_physical_monitors_from_hmonitor(monitor: HMONITOR) -> io::Result<Vec<PHYSICAL_MONITOR>> {
unsafe {
let mut len = 0;
if GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, &mut len) != TRUE {
return Err(io::Error::last_os_error())
}
pub fn get_physical_monitors_from_hmonitor(monitor: HMONITOR) -> WinResult<Vec<PHYSICAL_MONITOR>> {
let mut len = 0;
BOOL(unsafe { GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, &mut len) }).ok()?;

let mut monitors = vec![mem::zeroed::<PHYSICAL_MONITOR>(); len as usize];
if GetPhysicalMonitorsFromHMONITOR(monitor, len, monitors.as_mut_ptr()) != TRUE {
Err(io::Error::last_os_error())
} else {
Ok(monitors)
}
}
let mut monitors = vec![PHYSICAL_MONITOR::default(); len as usize];
BOOL(unsafe { GetPhysicalMonitorsFromHMONITOR(monitor, len, monitors.as_mut_ptr()) }).ok()?;

Ok(monitors)
}

/// Enumerates all `HMONITOR`s using the `EnumDisplayMonitors` WinAPI call.
pub fn enumerate_monitors() -> io::Result<Vec<HMONITOR>> {
unsafe extern "system" fn callback(monitor: HMONITOR, _hdc_monitor: HDC, _lprc: LPRECT, userdata: LPARAM) -> BOOL {
pub fn enumerate_monitors() -> WinResult<Vec<HMONITOR>> {
unsafe extern "system" fn callback(
monitor: HMONITOR,
_hdc_monitor: HDC,
_lprc: *mut RECT,
userdata: LPARAM,
) -> BOOL {
let monitors: &mut Vec<HMONITOR> = mem::transmute(userdata);
monitors.push(monitor);
TRUE
BOOL::from(true)
}

let mut monitors = Vec::<HMONITOR>::new();
if unsafe {
let userdata = &mut monitors as *mut _;
winapi::um::winuser::EnumDisplayMonitors(ptr::null_mut(), ptr::null(), Some(callback), userdata as _)
} != TRUE {
Err(io::Error::last_os_error())
} else {
Ok(monitors)
}
let userdata = LPARAM(&mut monitors as *mut _ as _);
unsafe { EnumDisplayMonitors(None, ptr::null(), Some(callback), userdata) }.ok()?;
Ok(monitors)
}