Add support for running commands on key press (#566)

Based on option `command` in key binding section in config, e.g.

    - { key: N,        mods: Control|Shift,    command: alacritty }
    # or
    - {
        key: N,
        mods: Control|Shift,
        command: {
          program: "alacritty",
          args: ["-e", "vttest"],
    }}

specified command will be run in the background on key press. Alacritty
doesn't wait for its result nor block IO.
This commit is contained in:
Dominik Miedziński 2017-05-25 18:19:00 +02:00 committed by Joe Wilm
parent 112abf385f
commit 5977776874
2 changed files with 54 additions and 10 deletions

View File

@ -378,6 +378,17 @@ impl de::Deserialize for ActionWrapper {
}
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum CommandWrapper {
Just(String),
WithArgs {
program: String,
#[serde(default)]
args: Vec<String>,
},
}
use ::term::{mode, TermMode};
struct ModeWrapper {
@ -517,7 +528,8 @@ impl de::Deserialize for RawBinding {
Mode,
Action,
Chars,
Mouse
Mouse,
Command,
}
impl de::Deserialize for Field {
@ -527,7 +539,7 @@ impl de::Deserialize for RawBinding {
struct FieldVisitor;
static FIELDS: &'static [&'static str] = &[
"key", "mods", "mode", "action", "chars", "mouse"
"key", "mods", "mode", "action", "chars", "mouse", "command",
];
impl Visitor for FieldVisitor {
@ -547,6 +559,7 @@ impl de::Deserialize for RawBinding {
"action" => Ok(Field::Action),
"chars" => Ok(Field::Chars),
"mouse" => Ok(Field::Mouse),
"command" => Ok(Field::Command),
_ => Err(E::unknown_field(value, FIELDS)),
}
}
@ -577,6 +590,7 @@ impl de::Deserialize for RawBinding {
let mut mode: Option<TermMode> = None;
let mut not_mode: Option<TermMode> = None;
let mut mouse: Option<::glutin::MouseButton> = None;
let mut command: Option<CommandWrapper> = None;
use ::serde::de::Error;
@ -626,17 +640,32 @@ impl de::Deserialize for RawBinding {
}
mouse = Some(visitor.visit_value::<MouseButton>()?.into_inner());
}
},
Field::Command => {
if command.is_some() {
return Err(<V::Error as Error>::duplicate_field("command"));
}
command = Some(visitor.visit_value::<CommandWrapper>()?);
},
}
}
let action = match (action, chars) {
(Some(_), Some(_)) => {
return Err(V::Error::custom("must specify only chars or action"));
let action = match (action, chars, command) {
(Some(action), None, None) => action,
(None, Some(chars), None) => Action::Esc(chars),
(None, None, Some(cmd)) => {
match cmd {
CommandWrapper::Just(program) => {
Action::Command(program, vec![])
},
CommandWrapper::WithArgs { program, args } => {
Action::Command(program, args)
},
}
},
(Some(action), _) => action,
(_, Some(chars)) => Action::Esc(chars),
_ => return Err(V::Error::custom("must specify chars or action"))
(None, None, None) => return Err(V::Error::custom("must specify chars, action or command")),
_ => return Err(V::Error::custom("must specify only chars, action or command")),
};
let mode = mode.unwrap_or_else(TermMode::empty);
@ -659,7 +688,7 @@ impl de::Deserialize for RawBinding {
}
const FIELDS: &'static [&'static str] = &[
"key", "mods", "mode", "action", "chars", "mouse"
"key", "mods", "mode", "action", "chars", "mouse", "command",
];
deserializer.deserialize_struct("RawBinding", FIELDS, RawBindingVisitor)

View File

@ -20,6 +20,7 @@
//! determine what to do when a non-modifier key is pressed.
use std::borrow::Cow;
use std::mem;
use std::process::Command;
use std::time::Instant;
use copypasta::{Clipboard, Load, Buffer};
@ -144,6 +145,9 @@ pub enum Action {
/// Paste contents of selection buffer
PasteSelection,
/// Run given command
Command(String, Vec<String>),
/// Quits Alacritty.
Quit,
}
@ -174,6 +178,17 @@ impl Action {
warn!("Error loading data from clipboard. {}", Red(err));
});
},
Action::Command(ref program, ref args) => {
trace!("running command: {} {:?}", program, args);
match Command::new(program).args(args).spawn() {
Ok(child) => {
debug!("spawned new proc with pid: {}", child.id());
},
Err(err) => {
warn!("couldn't run command: {}", err);
},
}
},
Action::Quit => {
// FIXME should do a more graceful shutdown
::std::process::exit(0);