Skip to content

Commit

Permalink
feat: Added options to switch display input
Browse files Browse the repository at this point in the history
  • Loading branch information
uttarayan21 committed Feb 7, 2025
1 parent 43b0aac commit 08a922b
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 4 deletions.
1 change: 1 addition & 0 deletions ddcutil-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub fn main() {
let out_dir = std::path::PathBuf::from(std::env::var_os("OUT_DIR").expect("OUT_DIR"));

let bindings = bindgen::Builder::default()
.impl_debug(true)
.header("headers/ddcutil.h")
.header("headers/version.h")
// .allowlist_file("*.c_api\\.h")
Expand Down
34 changes: 32 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::str::FromStr;

use crate::error::*;
use clap::*;

#[derive(Debug, Parser)]
Expand All @@ -6,9 +9,36 @@ pub struct Args {
pub op: Op,
#[arg(short, long, action = clap::ArgAction::Count)]
pub verbosity: u8,
#[arg(short, long)]
pub bus: Option<u8>,
}
#[derive(Debug, Subcommand)]
pub enum Op {
Set { brightness: u8 },
Get,
SetBrightness { brightness: u8 },
GetBrightnexs,
SetInput { bus: u8, input: crate::ddc::Input },
GetInput { bus: Option<u8> },
}

impl ValueEnum for crate::ddc::Input {
fn value_variants<'a>() -> &'a [Self] {
&[
Self::HDMI(1),
Self::HDMI(2),
Self::DP(1),
Self::DP(2),
Self::TYPEC(1),
]
}

fn to_possible_value(&self) -> Option<builder::PossibleValue> {
Some(match self {
Self::HDMI(1) => builder::PossibleValue::new("HDMI-1"),
Self::HDMI(2) => builder::PossibleValue::new("HDMI-2"),
Self::DP(1) => builder::PossibleValue::new("DP-1"),
Self::DP(2) => builder::PossibleValue::new("DP-2"),
Self::TYPEC(1) => builder::PossibleValue::new("TYPEC"),
_ => return None,
})
}
}
103 changes: 103 additions & 0 deletions src/ddc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,37 @@ use crate::error::*;
use core::ptr::{null_mut, NonNull};
use ddcutil_sys::bindings::*;

#[derive(Debug, Clone, Copy)]
pub enum Input {
HDMI(u8),
DP(u8),
TYPEC(u8),
}

impl From<Input> for u8 {
fn from(input: Input) -> u8 {
match input {
Input::HDMI(1) => 0x11,
Input::HDMI(2) => 0x12,
Input::DP(1) => 0x0f,
Input::DP(2) => 0x10,
_ => todo!(),
}
}
}
impl TryFrom<u8> for Input {
type Error = DDCError;
fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
Ok(match value {
0x11 => Self::HDMI(1),
0x12 => Self::HDMI(2),
0x0f => Self::DP(1),
0x10 => Self::DP(2),
_ => Err(DDCError::new(DdcutilErrorKind::Other))?,
})
}
}

pub struct DisplayList {
list: NonNull<DDCA_Display_Info_List>,
len: usize,
Expand Down Expand Up @@ -58,6 +89,7 @@ impl<'i> Iterator for DisplayListIter<'i> {
}
}

#[derive(Debug)]
pub struct DisplayInfo<'info> {
info: &'info DDCA_Display_Info,
}
Expand All @@ -67,19 +99,25 @@ impl DisplayInfo<'_> {
Display::open(self)
}

pub fn io_path(&self) -> IOPath {
self.info.path.into()
}

pub fn model(&self) -> &str {
unsafe { core::ffi::CStr::from_ptr(self.info.model_name.as_ptr()) }
.to_str()
.unwrap()
}
}

#[derive(Debug)]
pub struct Display {
handle: DDCA_Display_Handle,
}

impl Display {
const BACKLIGHT: u8 = 0x10;
const INPUT: u8 = 0x60;
pub fn open(info: &DisplayInfo) -> Result<Self> {
let dref = info.info.dref;
let mut dh = null_mut();
Expand Down Expand Up @@ -114,10 +152,75 @@ impl Display {
current: u16::from_be_bytes([out.sh, out.sl]),
})
}

pub fn input(&self) -> Result<Input> {
let mut out = DDCA_Non_Table_Vcp_Value {
mh: 0,
ml: 0,
sh: 0,
sl: 0,
};
let rc = unsafe { ddca_get_non_table_vcp_value(self.handle, Self::INPUT, &mut out) };
LibDDCUtilError::from_rc(rc)?;
Ok(match (out.ml, out.sl) {
(_, 0x0f) => Input::DP(1), // DP-1
(_, 0x10) => Input::DP(2), // DP-2
(_, 0x11) => Input::HDMI(1), // HDMI-1
(_, 0x12) => Input::HDMI(2), // HDMI-2
_ => todo!(),
})
}
pub fn set_input(&self, input: Input) -> Result<()> {
let value: u8 = input.into();
let rc = unsafe { ddca_set_non_table_vcp_value(self.handle, Self::INPUT, 0, value) };
LibDDCUtilError::from_rc(rc)?;
Ok(())
}
}

#[derive(Debug)]
pub struct Backlight {
pub current: u16,
pub max: u16,
}

#[test]
fn test_input() {
let list = DisplayList::probe(true).unwrap();
for dinfo in list.iter() {
tracing::info!("Found display: {}", dinfo.model());
let display = dinfo.open().unwrap();
dbg!(dinfo);
dbg!(&display);
let input = display.input().unwrap();
dbg!(input);
}
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum IOPath {
I2C(i32),
USB(i32),
}

impl core::fmt::Display for IOPath {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::I2C(bus) => write!(f, "/dev/i2c-{}", bus),
Self::USB(dev) => write!(f, "/dev/hiddev{}", dev),
}
}
}

impl From<DDCA_IO_Path> for IOPath {
fn from(path: DDCA_IO_Path) -> Self {
let discriminant = path.io_mode;
if discriminant == DDCA_IO_Mode_DDCA_IO_I2C {
unsafe { Self::I2C(path.path.i2c_busno) }
} else if discriminant == DDCA_IO_Mode_DDCA_IO_USB {
unsafe { Self::USB(path.path.hiddev_devno) }
} else {
unreachable!("DDCUTIL returned an unknown IOPath");
}
}
}
2 changes: 2 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub enum DdcutilErrorKind {
UnknownHandle,
#[error("Out of Range")]
OutOfRange,
#[error("Other error")]
Other,
}

#[derive(Debug, Error)]
Expand Down
39 changes: 39 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
mod ascii;
mod ddc;
mod error;
use core::marker::PhantomData;
pub use ddc::*;
use error::Result;

/// The main entry point for the library.
/// This contains the main struct that will be used to interact with all the monitors
pub struct DDC {}

/// The ddc driver that will be used to interact with the monitors
pub enum DDCDriver {
Windows(WindowsDDC),
Linux(LinuxDDC),
}

/// Uses windows native functions to interact
pub struct WindowsDDC {}

/// Uses ddcutil to interact with the monitors
pub struct LinuxDDC {}

/// A display identifier
/// Placeholder
pub struct DisplayIdent {
__marker: PhantomData<()>,
}

/// Placeholder
pub struct VCPFeature {
__marker: PhantomData<()>,
}

pub trait DDCDriverTrait {
fn probe(&self) -> Result<DisplayList>;
fn get_vcp(&self, display: &DisplayIdent, vcp: u8) -> Result<VCPFeature>;
fn set_vcp(&self, display: &DisplayIdent, vcp: u8) -> Result<()>;
}
34 changes: 32 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fn main() -> Result<()> {
}

match cli.op {
Op::Get => {
Op::GetBrightnexs => {
let list = DisplayList::probe(true)?;
for dinfo in list.iter() {
tracing::info!("Found display: {}", dinfo.model());
Expand All @@ -45,7 +45,7 @@ fn main() -> Result<()> {
);
}
}
Op::Set { brightness } => {
Op::SetBrightness { brightness } => {
let list = DisplayList::probe(true)?;
for dinfo in list.iter() {
tracing::info!("Found display: {}", dinfo.model());
Expand All @@ -60,6 +60,36 @@ fn main() -> Result<()> {
);
}
}
Op::GetInput { bus } => {
let list = DisplayList::probe(true)?;
for dinfo in list.iter().filter(|info| {
bus.map(|bus| info.io_path() == IOPath::I2C(bus as i32))
.unwrap_or(true)
}) {
tracing::info!("Found display: {} ({})", dinfo.model(), dinfo.io_path());
let display = dinfo.open()?;
let input = display.input()?;
println!(
"{}: {:?}: {}",
dinfo.model().green(),
input,
dinfo.io_path()
);
}
}
Op::SetInput { bus, input } => {
let list = DisplayList::probe(true)?;
if let Some(dinfo) = list
.iter()
.find(|info| info.io_path() == IOPath::I2C(bus as i32))
{
tracing::info!("Found display: {}", dinfo.model());
let display = dinfo.open()?;
display.set_input(input)?;
let input = display.input()?;
println!("{}: {:?}", dinfo.model().blue(), input);
}
}
}
Ok(())
}

0 comments on commit 08a922b

Please sign in to comment.