mirror of
https://github.com/emersion/kanshi
synced 2024-11-23 00:02:16 +01:00
Add exec
to execute commands when a configuration is matched
This commit is contained in:
parent
6910b76ff4
commit
b707965878
@ -17,7 +17,7 @@ pub struct MatchedOutput<'a> {
|
||||
}
|
||||
|
||||
pub trait Frontend {
|
||||
fn apply_configuration(&self, Option<Vec<MatchedOutput>>) -> Result<(), Box<Error>>;
|
||||
fn apply_configuration<'a>(&self, Option<&'a [MatchedOutput<'a>]>) -> Result<(), Box<Error>>;
|
||||
}
|
||||
|
||||
pub struct SwayFrontend {
|
||||
@ -31,7 +31,7 @@ impl SwayFrontend {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_commands(&self, config: Option<Vec<MatchedOutput>>) -> Vec<String> {
|
||||
fn get_commands<'a>(&self, config: Option<&'a [MatchedOutput<'a>]>) -> Vec<String> {
|
||||
if let Some(config) = config {
|
||||
let mut cmds = Vec::with_capacity(config.len());
|
||||
for MatchedOutput{connected, saved} in config {
|
||||
@ -61,7 +61,7 @@ impl SwayFrontend {
|
||||
}
|
||||
}
|
||||
|
||||
fn print_configuration(&self, config: Option<Vec<MatchedOutput>>) -> Result<(), Box<Error>> {
|
||||
fn print_configuration<'a>(&self, config: Option<&'a [MatchedOutput<'a>]>) -> Result<(), Box<Error>> {
|
||||
let cmds = self.get_commands(config);
|
||||
let mut w = io::stdout();
|
||||
for cmd in cmds {
|
||||
@ -73,7 +73,7 @@ impl SwayFrontend {
|
||||
}
|
||||
|
||||
impl Frontend for SwayFrontend {
|
||||
fn apply_configuration(&self, config: Option<Vec<MatchedOutput>>) -> Result<(), Box<Error>> {
|
||||
fn apply_configuration<'a>(&self, config: Option<&'a [MatchedOutput<'a>]>) -> Result<(), Box<Error>> {
|
||||
let cmds = self.get_commands(config);
|
||||
let mut conn = I3Connection::connect()?;
|
||||
for cmd in cmds {
|
||||
|
42
src/main.rs
42
src/main.rs
@ -14,10 +14,17 @@ use std::env;
|
||||
use getopts::Options;
|
||||
|
||||
use backend::{ConnectedOutput, Backend, SysFsBackend};
|
||||
use store::{SavedOutput, Store, GnomeStore, KanshiStore};
|
||||
use store::{SavedOutput, SavedConfiguration, Store, GnomeStore, KanshiStore};
|
||||
use frontend::{MatchedOutput, Frontend, SwayFrontend};
|
||||
use notifier::{Notifier, UdevNotifier};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::process::Command;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MatchedConfiguration<'a> {
|
||||
pub outputs: Vec<MatchedOutput<'a>>,
|
||||
pub saved: &'a SavedConfiguration,
|
||||
}
|
||||
|
||||
fn connector_type(name: &str) -> Option<String> {
|
||||
let name = name.to_lowercase();
|
||||
@ -156,12 +163,12 @@ fn main() {
|
||||
let connected_outputs = &connected_outputs;
|
||||
let configuration = configurations.iter()
|
||||
.filter_map(|config| {
|
||||
let n_saved = config.len();
|
||||
let n_saved = config.outputs.len();
|
||||
if n_saved != connected_outputs.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let matched = config.into_iter()
|
||||
let matched = config.outputs.iter()
|
||||
.filter_map(|saved| {
|
||||
connected_outputs.iter()
|
||||
.find(|connected| **connected == *saved)
|
||||
@ -173,19 +180,34 @@ fn main() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(matched)
|
||||
Some(MatchedConfiguration{outputs: matched, saved: config})
|
||||
})
|
||||
.nth(0);
|
||||
|
||||
writeln!(&mut stderr, "Matching configuration: {:?}", &configuration).unwrap();
|
||||
|
||||
match frontend.apply_configuration(configuration) {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
writeln!(&mut stderr, "Error: cannot apply configuration: {}", err).unwrap();
|
||||
std::process::exit(1);
|
||||
{
|
||||
let outputs = configuration.as_ref().map(|config| config.outputs.as_ref());
|
||||
match frontend.apply_configuration(outputs) {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
writeln!(&mut stderr, "Error: cannot apply configuration: {}", err).unwrap();
|
||||
std::process::exit(1);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
match configuration.map(|config| &config.saved.exec) {
|
||||
Some(exec) => {
|
||||
for e in exec {
|
||||
let cmd = e.get(0).unwrap();
|
||||
let args = e.get(1..).unwrap();
|
||||
writeln!(&mut stderr, "Executing command: {:?}", &cmd).unwrap();
|
||||
Command::new(cmd).args(args).spawn().unwrap();
|
||||
}
|
||||
},
|
||||
};
|
||||
None => (),
|
||||
}
|
||||
|
||||
match rx {
|
||||
Some(ref rx) => {
|
||||
|
64
src/store.rs
64
src/store.rs
@ -33,8 +33,14 @@ pub struct SavedOutput {
|
||||
pub scale: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SavedConfiguration {
|
||||
pub outputs: Vec<SavedOutput>,
|
||||
pub exec: Vec<Vec<String>>,
|
||||
}
|
||||
|
||||
pub trait Store {
|
||||
fn list_configurations(&self) -> Result<Vec<Vec<SavedOutput>>, Box<Error>>;
|
||||
fn list_configurations(&self) -> Result<Vec<SavedConfiguration>, Box<Error>>;
|
||||
}
|
||||
|
||||
fn xdg_config_home() -> PathBuf {
|
||||
@ -50,7 +56,7 @@ fn parse_bool(s: &str) -> bool {
|
||||
pub struct GnomeStore;
|
||||
|
||||
impl Store for GnomeStore {
|
||||
fn list_configurations(&self) -> Result<Vec<Vec<SavedOutput>>, Box<Error>> {
|
||||
fn list_configurations(&self) -> Result<Vec<SavedConfiguration>, Box<Error>> {
|
||||
let monitors_path = xdg_config_home().join("monitors.xml");
|
||||
|
||||
let monitors = xmltree::Element::parse(File::open(&monitors_path)?).unwrap();
|
||||
@ -59,7 +65,7 @@ impl Store for GnomeStore {
|
||||
.map(|e| {
|
||||
// TODO: <clone> support
|
||||
|
||||
e.children.iter()
|
||||
let outputs = e.children.iter()
|
||||
.filter(|e| e.name == "output")
|
||||
.map(|e| {
|
||||
let mut o = SavedOutput{
|
||||
@ -93,7 +99,9 @@ impl Store for GnomeStore {
|
||||
|
||||
o
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
SavedConfiguration{outputs, exec: Vec::new()}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@ -202,27 +210,61 @@ named!(parse_scale<&[u8], OutputArg>, do_parse!(
|
||||
>> (OutputArg::Scale(f))
|
||||
));
|
||||
|
||||
named!(parse_argument<&[u8], OutputArg>, alt!(
|
||||
named!(parse_output_arg<&[u8], OutputArg>, alt!(
|
||||
parse_vendor | parse_product | parse_serial |
|
||||
parse_disable | parse_resolution | parse_position | parse_scale
|
||||
));
|
||||
|
||||
named!(parse_output<&[u8], SavedOutput>, do_parse!(
|
||||
enum ConfigurationArg {
|
||||
Output(SavedOutput),
|
||||
Exec(Vec<String>),
|
||||
}
|
||||
|
||||
fn parse_configuration_with_args(args: Vec<ConfigurationArg>) -> SavedConfiguration {
|
||||
let mut c = SavedConfiguration::default();
|
||||
|
||||
for arg in args {
|
||||
match arg {
|
||||
ConfigurationArg::Output(o) => c.outputs.push(o),
|
||||
ConfigurationArg::Exec(e) => c.exec.push(e),
|
||||
}
|
||||
}
|
||||
|
||||
c
|
||||
}
|
||||
|
||||
named!(parse_output<&[u8], ConfigurationArg>, do_parse!(
|
||||
tag!("output")
|
||||
>> parse_space
|
||||
>> name: parse_string
|
||||
>> args: many0!(preceded!(parse_space, parse_argument))
|
||||
>> (parse_output_with_args(name, args))
|
||||
>> args: many0!(preceded!(parse_space, parse_output_arg))
|
||||
>> (ConfigurationArg::Output(parse_output_with_args(name, args)))
|
||||
));
|
||||
|
||||
named!(parse_configuration<&[u8], Vec<SavedOutput>>, delimited!(tag!("{"), many0!(ws!(parse_output)), tag!("}")));
|
||||
named!(parse_exec<&[u8], ConfigurationArg>, do_parse!(
|
||||
tag!("exec")
|
||||
>> cmd: many1!(preceded!(parse_space, parse_string))
|
||||
>> (ConfigurationArg::Exec(cmd))
|
||||
));
|
||||
|
||||
named!(parse_configuration_list<&[u8], Vec<Vec<SavedOutput>>>, many0!(ws!(parse_configuration)));
|
||||
named!(parse_configuration_arg<&[u8], ConfigurationArg>, alt!(parse_output | parse_exec));
|
||||
|
||||
named!(parse_configuration_args<&[u8], Vec<ConfigurationArg>>, delimited!(
|
||||
tag!("{"),
|
||||
many0!(ws!(parse_configuration_arg)),
|
||||
tag!("}")
|
||||
));
|
||||
|
||||
named!(parse_configuration<&[u8], SavedConfiguration>, map!(
|
||||
parse_configuration_args, parse_configuration_with_args
|
||||
));
|
||||
|
||||
named!(parse_configuration_list<&[u8], Vec<SavedConfiguration>>, many0!(ws!(parse_configuration)));
|
||||
|
||||
pub struct KanshiStore;
|
||||
|
||||
impl Store for KanshiStore {
|
||||
fn list_configurations(&self) -> Result<Vec<Vec<SavedOutput>>, Box<Error>> {
|
||||
fn list_configurations(&self) -> Result<Vec<SavedConfiguration>, Box<Error>> {
|
||||
let config_path = xdg_config_home().join("kanshi").join("config");
|
||||
|
||||
let mut buf = Vec::new();
|
||||
|
Loading…
Reference in New Issue
Block a user