Working
This commit is contained in:
parent
55f46bcc50
commit
9e40498be6
155
src/lib.rs
155
src/lib.rs
|
@ -2,8 +2,14 @@ use std::io;
|
|||
use std::collections::HashSet;
|
||||
use std::process::Command;
|
||||
use std::num::ParseIntError;
|
||||
use std::str::FromStr;
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
/// Converts a resolution to a string.
|
||||
pub fn resolution_to_string(res: (u32, u32)) -> String {
|
||||
format!("{}x{}", res.0, res.1).to_owned()
|
||||
}
|
||||
|
||||
/// The different kinds of errors that can happen.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
|
@ -17,6 +23,51 @@ pub enum Error {
|
|||
/// An error happened while parsing the resolution of a xrandr screen.
|
||||
ParseIntError(ParseIntError),
|
||||
|
||||
/// Not enough screens detected.
|
||||
NoScreenDetected,
|
||||
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
/// The relative location of the second screen compared to the first one.
|
||||
pub enum Side {
|
||||
/// On the right of the main screen.
|
||||
Right,
|
||||
|
||||
/// On the left of the main screen.
|
||||
Left,
|
||||
|
||||
/// Above the main screen.
|
||||
Above,
|
||||
|
||||
/// Below the main screen.
|
||||
Below,
|
||||
}
|
||||
|
||||
impl Side {
|
||||
/// Returns the corresponding xrandr argument.
|
||||
pub fn to_argument(&self) -> String {
|
||||
match *self {
|
||||
Side::Right => "--right-of",
|
||||
Side::Left => "--left-of",
|
||||
Side::Above => "--above",
|
||||
Side::Below => "--below",
|
||||
}.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Side {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Side, Self::Err> {
|
||||
match s {
|
||||
"left" => Ok(Side::Left),
|
||||
"right" => Ok(Side::Right),
|
||||
"above" => Ok(Side::Above),
|
||||
"below" => Ok(Side::Below),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
|
@ -76,10 +127,10 @@ impl Screen {
|
|||
let mut output = None;
|
||||
|
||||
for element in intersection {
|
||||
if let Some(res) = output {
|
||||
if element > &res {
|
||||
output = Some(*element);
|
||||
}
|
||||
match output {
|
||||
None => output = Some(*element),
|
||||
Some(res) if &res < element => output = Some(*element),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,7 +142,7 @@ impl Screen {
|
|||
#[derive(Debug)]
|
||||
pub struct MultiScreen {
|
||||
/// All the contained screens.
|
||||
pub(crate) screens: Vec<Screen>,
|
||||
screens: Vec<Screen>,
|
||||
}
|
||||
|
||||
impl MultiScreen {
|
||||
|
@ -112,6 +163,8 @@ impl MultiScreen {
|
|||
continue;
|
||||
}
|
||||
|
||||
// This will match whether its connected or disconnected
|
||||
// It tells us it is a new screen
|
||||
if line.contains("connected") {
|
||||
|
||||
screens.push(Screen::from_line(line));
|
||||
|
@ -142,8 +195,21 @@ impl MultiScreen {
|
|||
Ok(MultiScreen { screens: screens })
|
||||
}
|
||||
|
||||
/// Returns a pair of screen corresponding to the two main screens.
|
||||
pub fn main_screens(&self) -> Option<(&Screen, &Screen)> {
|
||||
if let Some(main) = self.screens.iter().filter(|x| x.primary).next() {
|
||||
if let Some(second) = self.screens.iter().filter(|x| !x.primary && x.connected).next() {
|
||||
Some((main, second))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the arguments of xrandr that disable all screens except the primary.
|
||||
pub fn only_primary_args(&self) -> Vec<&str> {
|
||||
pub fn only_primary_args(&self) -> Vec<String> {
|
||||
|
||||
let mut primary_found = false;
|
||||
let mut args = vec![];
|
||||
|
@ -151,13 +217,13 @@ impl MultiScreen {
|
|||
for screen in &self.screens {
|
||||
|
||||
if ! screen.primary || primary_found {
|
||||
args.push("--output");
|
||||
args.push(&screen.name);
|
||||
args.push("--off");
|
||||
args.push("--output".to_owned());
|
||||
args.push(screen.name.clone());
|
||||
args.push("--off".to_owned());
|
||||
} else {
|
||||
args.push("--output");
|
||||
args.push(&screen.name);
|
||||
args.push("--auto");
|
||||
args.push("--output".to_owned());
|
||||
args.push(screen.name.clone());
|
||||
args.push("--auto".to_owned());
|
||||
primary_found = true;
|
||||
}
|
||||
|
||||
|
@ -167,9 +233,51 @@ impl MultiScreen {
|
|||
}
|
||||
|
||||
/// Returns the arguments to duplicate the screens.
|
||||
pub fn duplicate_args(&self) -> Vec<&str> {
|
||||
panic!("Not implemented yet");
|
||||
vec![]
|
||||
pub fn duplicate_args(&self) -> Option<Vec<String>> {
|
||||
if let Some((ref main, ref second)) = self.main_screens() {
|
||||
if let Some(best_resolution) = main.best_common_resolution(second) {
|
||||
|
||||
Some(vec![
|
||||
"--output".to_owned(),
|
||||
main.name.clone(),
|
||||
"--mode".to_owned(),
|
||||
resolution_to_string(best_resolution),
|
||||
|
||||
"--output".to_owned(),
|
||||
second.name.clone(),
|
||||
"--mode".to_owned(),
|
||||
resolution_to_string(best_resolution),
|
||||
"--same-as".to_owned(),
|
||||
main.name.clone(),
|
||||
])
|
||||
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the arguments for two screens.
|
||||
pub fn two_screen_args(&self, side: Side) -> Option<Vec<String>> {
|
||||
if let Some((ref main, ref second)) = self.main_screens() {
|
||||
|
||||
Some(vec![
|
||||
"--output".to_owned(),
|
||||
main.name.clone(),
|
||||
"--auto".to_owned(),
|
||||
|
||||
"--output".to_owned(),
|
||||
second.name.clone(),
|
||||
"--auto".to_owned(),
|
||||
side.to_argument(),
|
||||
main.name.clone(),
|
||||
])
|
||||
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls the xrandr program to disable all screens except the primary.
|
||||
|
@ -179,11 +287,24 @@ impl MultiScreen {
|
|||
|
||||
/// Calls the xrandr program to duplicate the screens.
|
||||
pub fn duplicate(&self) -> Result<(), Error> {
|
||||
self.run_command(self.duplicate_args())
|
||||
if let Some(args) = self.duplicate_args() {
|
||||
self.run_command(args)
|
||||
} else {
|
||||
Err(Error::NoScreenDetected)
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls the xrandr program to set two screens.
|
||||
pub fn two_screens(&self, side: Side) -> Result<(), Error> {
|
||||
if let Some(args) = self.two_screen_args(side){
|
||||
self.run_command(args)
|
||||
} else {
|
||||
Err(Error::NoScreenDetected)
|
||||
}
|
||||
}
|
||||
|
||||
/// Run an xrandr command with its args.
|
||||
pub fn run_command(&self, args: Vec<&str>) -> Result<(), Error> {
|
||||
pub fn run_command(&self, args: Vec<String>) -> Result<(), Error> {
|
||||
|
||||
Command::new("xrandr")
|
||||
.args(args)
|
||||
|
|
42
src/tv.rs
42
src/tv.rs
|
@ -1,7 +1,7 @@
|
|||
extern crate clap;
|
||||
extern crate tvrs;
|
||||
|
||||
use clap::{Arg, App, SubCommand};
|
||||
use clap::{Arg, App};
|
||||
use tvrs::MultiScreen;
|
||||
|
||||
fn main() {
|
||||
|
@ -9,31 +9,33 @@ fn main() {
|
|||
let matches = App::new("tvrs")
|
||||
.author("Thomas Forgione <thomas@forgione.fr>")
|
||||
.version("0.1.0")
|
||||
.arg(Arg::with_name("disable")
|
||||
.help("Disable all screens except the primary")
|
||||
.conflicts_with_all(&["enable", "duplicate"]))
|
||||
.arg(Arg::with_name("duplicate")
|
||||
.help("Duplicate the primary screen on the second one")
|
||||
.conflicts_with_all(&["enable", "disable"]))
|
||||
.arg(Arg::with_name("enable")
|
||||
.help("Enable the second screen")
|
||||
.requires("enable")
|
||||
.conflicts_with_all(&["disable", "duplicate"]))
|
||||
.arg(Arg::with_name("action")
|
||||
.help("Action to do")
|
||||
.takes_value(true)
|
||||
.value_name("ACTION")
|
||||
.possible_values(&["disable", "duplicate", "enable"])
|
||||
.required(true))
|
||||
.arg(Arg::with_name("position")
|
||||
.help("Specify the position of the second screen")
|
||||
.possible_values(&["left", "right", "top", "bottom"]))
|
||||
.possible_values(&["left", "right", "above", "below"])
|
||||
.required_if("action", "enable"))
|
||||
.get_matches();
|
||||
|
||||
let screens = MultiScreen::detect()
|
||||
.expect("An error happened while finding screens");
|
||||
|
||||
|
||||
if matches.is_present("disable") {
|
||||
screens.only_primary()
|
||||
.expect("An error happenned while executing the command");
|
||||
} else if matches.is_present("duplicate") {
|
||||
panic!("Not implemented yet");
|
||||
} else if matches.is_present("enable") {
|
||||
panic!("Not implemented yet");
|
||||
}
|
||||
let result = match matches.value_of("action") {
|
||||
Some("disable") => screens.only_primary(),
|
||||
Some("duplicate") => screens.duplicate(),
|
||||
Some("enable") => {
|
||||
// Can never fail, clap will crash before this
|
||||
let side = matches.value_of("position").unwrap().parse().unwrap();
|
||||
screens.two_screens(side)
|
||||
},
|
||||
// Can never happen, clap will crash before this
|
||||
_ => Ok(())
|
||||
};
|
||||
|
||||
result.expect("An error happenned while executing the command");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue