1
0
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:
emersion 2018-11-03 14:28:46 +01:00
parent 6910b76ff4
commit b707965878
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
3 changed files with 89 additions and 25 deletions

@ -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 {

@ -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) => {

@ -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();