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::collections::HashSet;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::string::FromUtf8Error;
|
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.
|
/// The different kinds of errors that can happen.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -17,6 +23,51 @@ pub enum Error {
|
||||||
/// An error happened while parsing the resolution of a xrandr screen.
|
/// An error happened while parsing the resolution of a xrandr screen.
|
||||||
ParseIntError(ParseIntError),
|
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 {
|
impl From<io::Error> for Error {
|
||||||
|
@ -76,10 +127,10 @@ impl Screen {
|
||||||
let mut output = None;
|
let mut output = None;
|
||||||
|
|
||||||
for element in intersection {
|
for element in intersection {
|
||||||
if let Some(res) = output {
|
match output {
|
||||||
if element > &res {
|
None => output = Some(*element),
|
||||||
output = Some(*element);
|
Some(res) if &res < element => output = Some(*element),
|
||||||
}
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +142,7 @@ impl Screen {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MultiScreen {
|
pub struct MultiScreen {
|
||||||
/// All the contained screens.
|
/// All the contained screens.
|
||||||
pub(crate) screens: Vec<Screen>,
|
screens: Vec<Screen>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MultiScreen {
|
impl MultiScreen {
|
||||||
|
@ -112,6 +163,8 @@ impl MultiScreen {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This will match whether its connected or disconnected
|
||||||
|
// It tells us it is a new screen
|
||||||
if line.contains("connected") {
|
if line.contains("connected") {
|
||||||
|
|
||||||
screens.push(Screen::from_line(line));
|
screens.push(Screen::from_line(line));
|
||||||
|
@ -142,8 +195,21 @@ impl MultiScreen {
|
||||||
Ok(MultiScreen { screens: screens })
|
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.
|
/// 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 primary_found = false;
|
||||||
let mut args = vec![];
|
let mut args = vec![];
|
||||||
|
@ -151,13 +217,13 @@ impl MultiScreen {
|
||||||
for screen in &self.screens {
|
for screen in &self.screens {
|
||||||
|
|
||||||
if ! screen.primary || primary_found {
|
if ! screen.primary || primary_found {
|
||||||
args.push("--output");
|
args.push("--output".to_owned());
|
||||||
args.push(&screen.name);
|
args.push(screen.name.clone());
|
||||||
args.push("--off");
|
args.push("--off".to_owned());
|
||||||
} else {
|
} else {
|
||||||
args.push("--output");
|
args.push("--output".to_owned());
|
||||||
args.push(&screen.name);
|
args.push(screen.name.clone());
|
||||||
args.push("--auto");
|
args.push("--auto".to_owned());
|
||||||
primary_found = true;
|
primary_found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,9 +233,51 @@ impl MultiScreen {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the arguments to duplicate the screens.
|
/// Returns the arguments to duplicate the screens.
|
||||||
pub fn duplicate_args(&self) -> Vec<&str> {
|
pub fn duplicate_args(&self) -> Option<Vec<String>> {
|
||||||
panic!("Not implemented yet");
|
if let Some((ref main, ref second)) = self.main_screens() {
|
||||||
vec![]
|
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.
|
/// 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.
|
/// Calls the xrandr program to duplicate the screens.
|
||||||
pub fn duplicate(&self) -> Result<(), Error> {
|
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.
|
/// 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")
|
Command::new("xrandr")
|
||||||
.args(args)
|
.args(args)
|
||||||
|
|
42
src/tv.rs
42
src/tv.rs
|
@ -1,7 +1,7 @@
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
extern crate tvrs;
|
extern crate tvrs;
|
||||||
|
|
||||||
use clap::{Arg, App, SubCommand};
|
use clap::{Arg, App};
|
||||||
use tvrs::MultiScreen;
|
use tvrs::MultiScreen;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -9,31 +9,33 @@ fn main() {
|
||||||
let matches = App::new("tvrs")
|
let matches = App::new("tvrs")
|
||||||
.author("Thomas Forgione <thomas@forgione.fr>")
|
.author("Thomas Forgione <thomas@forgione.fr>")
|
||||||
.version("0.1.0")
|
.version("0.1.0")
|
||||||
.arg(Arg::with_name("disable")
|
.arg(Arg::with_name("action")
|
||||||
.help("Disable all screens except the primary")
|
.help("Action to do")
|
||||||
.conflicts_with_all(&["enable", "duplicate"]))
|
.takes_value(true)
|
||||||
.arg(Arg::with_name("duplicate")
|
.value_name("ACTION")
|
||||||
.help("Duplicate the primary screen on the second one")
|
.possible_values(&["disable", "duplicate", "enable"])
|
||||||
.conflicts_with_all(&["enable", "disable"]))
|
.required(true))
|
||||||
.arg(Arg::with_name("enable")
|
|
||||||
.help("Enable the second screen")
|
|
||||||
.requires("enable")
|
|
||||||
.conflicts_with_all(&["disable", "duplicate"]))
|
|
||||||
.arg(Arg::with_name("position")
|
.arg(Arg::with_name("position")
|
||||||
.help("Specify the position of the second screen")
|
.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();
|
.get_matches();
|
||||||
|
|
||||||
let screens = MultiScreen::detect()
|
let screens = MultiScreen::detect()
|
||||||
.expect("An error happened while finding screens");
|
.expect("An error happened while finding screens");
|
||||||
|
|
||||||
|
|
||||||
if matches.is_present("disable") {
|
let result = match matches.value_of("action") {
|
||||||
screens.only_primary()
|
Some("disable") => screens.only_primary(),
|
||||||
.expect("An error happenned while executing the command");
|
Some("duplicate") => screens.duplicate(),
|
||||||
} else if matches.is_present("duplicate") {
|
Some("enable") => {
|
||||||
panic!("Not implemented yet");
|
// Can never fail, clap will crash before this
|
||||||
} else if matches.is_present("enable") {
|
let side = matches.value_of("position").unwrap().parse().unwrap();
|
||||||
panic!("Not implemented yet");
|
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