use std::cell::RefMut;
use color_eyre::eyre::{Result, eyre};
use displayz::{
DisplaySettings, Frequency, Orientation, Position, Resolution, query_displays, refresh,
};
use structopt::{StructOpt, clap::ArgGroup};
#[derive(StructOpt, Debug)]
#[structopt(
name = "display-cli",
about = "Allows changing display settings on Windows using the CLI."
)]
struct Opts {
#[structopt(subcommand)]
cmd: SubCommands,
#[structopt(short, long, global = true)]
verbose: bool,
}
#[derive(StructOpt, Debug)]
enum SubCommands {
#[structopt(alias = "i")]
Info {
#[structopt(short, long)]
id: Option<usize>,
#[cfg(feature = "json")]
#[structopt(long)]
json: bool,
},
#[structopt(alias = "sp")]
SetPrimary {
#[structopt(short, long)]
id: usize,
},
#[structopt(alias = "p")]
Primary {
#[structopt(flatten)]
properties: PropertiesOpt,
},
#[structopt(alias = "props")]
Properties {
#[structopt(short, long)]
id: usize,
#[structopt(flatten)]
properties: PropertiesOpt,
},
}
#[derive(StructOpt, Debug)]
#[structopt(group = ArgGroup::with_name("prop").required(true).multiple(true))]
struct PropertiesOpt {
#[structopt(
group = "prop",
short,
long,
long_help = "Set the position of the display. Expected format: `<x>,<y>`"
)]
position: Option<Position>,
#[structopt(
group = "prop",
short,
long,
long_help = "Sets the resolution of the display. Expected format: `<width>x<height>`."
)]
resolution: Option<Resolution>,
#[structopt(
group = "prop",
short("t"),
long,
long_help = "Sets the refresh rate of the display. Expected format: `<n>`."
)]
frequency: Option<Frequency>,
#[structopt(
group = "prop",
short,
long,
long_help = "Sets the orientation of the display. Expected format: `landscape`, `portrait`, `landscape_flipped`, or `portrait_flipped`."
)]
orientation: Option<Orientation>,
}
fn main() -> Result<()> {
color_eyre::install()?;
let opts = Opts::from_args();
let log_level = if opts.verbose {
log::LevelFilter::Trace
} else {
log::LevelFilter::Info
};
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(log_level.as_str()))
.init();
log::debug!("Parsed Opts:\n{:#?}", opts);
let display_set = query_displays()?;
log::debug!("Discovered displays:\n{}", display_set);
match opts.cmd {
SubCommands::Info {
id,
#[cfg(feature = "json")]
json,
} => {
#[cfg(feature = "json")]
let output_json = json;
#[cfg(not(feature = "json"))]
let output_json = false;
if output_json {
#[cfg(feature = "json")]
{
use displayz::json;
match id {
Some(id) => {
let display = display_set
.get(id)
.ok_or_else(|| eyre!("Display with id {} not found", id))?;
let json_output = json::display_to_json(&display);
println!("{}", serde_json::to_string_pretty(&json_output)?);
}
None => {
let displays_json: Vec<json::DisplayInfoJson> = display_set
.displays()
.map(|d| json::display_to_json(&d))
.collect();
println!("{}", serde_json::to_string_pretty(&displays_json)?);
}
}
}
} else {
match id {
Some(id) => {
let display = display_set
.get(id)
.ok_or_else(|| eyre!("Display with id {} not found", id))?;
println!("Display ID: {}", display.index());
println!("Windows Display Number: {}", display.index() + 1);
println!("Name: {}", display.name());
println!("String: {}", display.string());
println!("Key: {}", display.key());
println!("Primary: {}", display.is_primary());
if let Some(connector) = display.connector_type() {
println!("Connector: {}", connector);
}
println!("Available: {}", display.target_available());
if let Some(settings) = display.settings() {
let settings = settings.borrow();
println!("\nSettings:");
println!(" Position: {}", settings.position);
println!(" Resolution: {}", settings.resolution);
println!(" Frequency: {} Hz", settings.frequency);
println!(" Orientation: {}", settings.orientation);
println!(" Scaling: {}", settings.scaling);
println!(" Bit Depth: {}", settings.bit_depth);
println!(" Scanline Ordering: {}", settings.scanline_ordering);
} else {
println!("\nSettings: None (Inactive)");
}
}
None => {
println!("All Displays:");
println!();
for display in display_set.displays() {
println!("Display ID: {}", display.index());
println!("Windows Display Number: {}", display.index() + 1);
println!("Name: {}", display.name());
println!("String: {}", display.string());
println!("Key: {}", display.key());
println!("Primary: {}", display.is_primary());
if let Some(connector) = display.connector_type() {
println!("Connector: {}", connector);
}
println!("Available: {}", display.target_available());
if let Some(settings) = display.settings() {
let settings = settings.borrow();
println!("Settings:");
println!(" Position: {}", settings.position);
println!(" Resolution: {}", settings.resolution);
println!(" Frequency: {} Hz", settings.frequency);
println!(" Orientation: {}", settings.orientation);
println!(" Scaling: {}", settings.scaling);
println!(" Bit Depth: {}", settings.bit_depth);
println!(" Scanline Ordering: {}", settings.scanline_ordering);
} else {
println!("Settings: None (Inactive)");
}
println!();
}
}
}
}
}
SubCommands::SetPrimary { id } => {
let display = display_set
.get(id)
.ok_or_else(|| eyre!("Display with id {} not found", id))?;
display.set_primary()?;
display_set.apply()?;
refresh()?;
log::info!("Display settings changed");
}
SubCommands::Primary { properties } => {
let display = display_set.primary();
if let Some(settings) = display.settings() {
let mut settings = settings.borrow_mut();
set_properties(&properties, &mut settings);
} else {
Err(eyre!("Primary display has no settings"))?;
}
display_set.apply()?;
refresh()?;
log::info!("Display settings changed");
}
SubCommands::Properties { id, properties } => {
let display = display_set
.get(id)
.ok_or_else(|| eyre!("Display with id {} not found", id))?;
if let Some(settings) = display.settings() {
let mut settings = settings.borrow_mut();
set_properties(&properties, &mut settings)
} else {
Err(eyre!("Display has no settings"))?;
}
display_set.apply()?;
refresh()?;
log::info!("Display settings changed");
}
}
Ok(())
}
macro_rules! assign_if_ok {
($properties:expr_2021, $settings:expr_2021, $name:ident) => {
if let Some(value) = $properties.$name {
$settings.$name = value;
}
};
}
fn set_properties(properties: &PropertiesOpt, settings: &mut RefMut<DisplaySettings>) {
assign_if_ok!(properties, settings, position);
assign_if_ok!(properties, settings, resolution);
assign_if_ok!(properties, settings, frequency);
assign_if_ok!(properties, settings, orientation);
}