Add IPC on Unix systems

This commit is contained in:
Federico Terzi 2019-09-14 20:13:09 +02:00
parent 041f53842f
commit 2b4835171a
7 changed files with 143 additions and 20 deletions

View File

@ -95,7 +95,7 @@ impl <'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIMa
}
}
fn on_toggle(&self, status: bool) {
fn on_enable_update(&self, status: bool) {
let message = if status {
"espanso enabled"
}else{

View File

@ -14,6 +14,8 @@ pub enum ActionType {
Toggle = 1,
Exit = 2,
IconClick = 3,
Enable = 4,
Disable = 5,
}
impl From<i32> for ActionType {
@ -22,6 +24,8 @@ impl From<i32> for ActionType {
1 => ActionType::Toggle,
2 => ActionType::Exit,
3 => ActionType::IconClick,
4 => ActionType::Enable,
5 => ActionType::Disable,
_ => ActionType::Noop,
}
}

View File

@ -6,7 +6,7 @@ use std::sync::mpsc;
use std::sync::mpsc::Receiver;
use std::time::Duration;
use clap::{App, Arg, SubCommand};
use clap::{App, Arg, SubCommand, ArgMatches};
use fs2::FileExt;
use log::{error, info, LevelFilter};
use simplelog::{CombinedLogger, SharedLogger, TerminalMode, TermLogger};
@ -50,6 +50,17 @@ fn main() {
.short("v")
.multiple(true)
.help("Sets the level of verbosity"))
.subcommand(SubCommand::with_name("cmd")
.about("Send a command to the espanso daemon.")
.subcommand(SubCommand::with_name("exit")
.about("Terminate the daemon."))
.subcommand(SubCommand::with_name("enable")
.about("Enable the espanso replacement engine."))
.subcommand(SubCommand::with_name("disable")
.about("Disable the espanso replacement engine."))
.subcommand(SubCommand::with_name("toggle")
.about("Toggle the status of the espanso replacement engine."))
)
.subcommand(SubCommand::with_name("dump")
.about("Prints all current configuration options."))
.subcommand(SubCommand::with_name("detect")
@ -111,6 +122,11 @@ fn main() {
status_main();
return;
}
if let Some(matches) = matches.subcommand_matches("cmd") {
cmd_main(matches);
return;
}
}
/// Daemon subcommand, start the event loop and spawn a background thread worker
@ -153,8 +169,8 @@ fn daemon_main(config_set: ConfigSet) {
daemon_background(receive_channel, config_set);
});
let ipc_manager = protocol::get_ipc_manager(send_channel.clone());
ipc_manager.start_server();
let ipc_server = protocol::get_ipc_server(send_channel.clone());
ipc_server.start();
context.eventloop();
}
@ -273,6 +289,41 @@ fn detect_main() {
}
}
fn cmd_main(matches: &ArgMatches) {
let command = if let Some(matches) = matches.subcommand_matches("exit") {
Some(IPCCommand {
id: String::from("exit"),
payload: String::from(""),
})
}else if let Some(matches) = matches.subcommand_matches("toggle") {
Some(IPCCommand {
id: String::from("toggle"),
payload: String::from(""),
})
}else if let Some(matches) = matches.subcommand_matches("enable") {
Some(IPCCommand {
id: String::from("enable"),
payload: String::from(""),
})
}else if let Some(matches) = matches.subcommand_matches("disable") {
Some(IPCCommand {
id: String::from("disable"),
payload: String::from(""),
})
}else{
None
};
if let Some(command) = command {
let ipc_client = protocol::get_ipc_client();
ipc_client.send_command(command);
exit(0);
}
exit(1);
}
fn acquire_lock() -> Option<File> {
let espanso_dir = context::get_data_dir();
let lock_file_path = espanso_dir.join("espanso.lock");

View File

@ -12,7 +12,7 @@ pub struct Match {
pub trait MatchReceiver {
fn on_match(&self, m: &Match);
fn on_toggle(&self, status: bool);
fn on_enable_update(&self, status: bool);
}
pub trait Matcher : KeyEventReceiver {

View File

@ -37,7 +37,14 @@ impl <'a, R: MatchReceiver, M: ConfigManager<'a>> ScrollingMatcher<'a, R, M> {
let mut is_enabled = self.is_enabled.borrow_mut();
*is_enabled = !(*is_enabled);
self.receiver.on_toggle(*is_enabled);
self.receiver.on_enable_update(*is_enabled);
}
fn set_enabled(&self, enabled: bool) {
let mut is_enabled = self.is_enabled.borrow_mut();
*is_enabled = enabled;
self.receiver.on_enable_update(*is_enabled);
}
}
@ -128,6 +135,12 @@ impl <'a, R: MatchReceiver, M: ConfigManager<'a>> ActionEventReceiver for Scroll
ActionType::Toggle => {
self.toggle();
},
ActionType::Enable => {
self.set_enabled(true);
},
ActionType::Disable => {
self.set_enabled(false);
},
_ => {}
}
}

View File

@ -12,14 +12,20 @@ mod windows;
#[cfg(not(target_os = "windows"))]
mod unix;
pub trait IPCManager {
fn start_server(&self);
pub trait IPCServer {
fn start(&self);
}
pub trait IPCClient {
fn send_command(&self, command: IPCCommand);
}
#[derive(Serialize, Deserialize, Debug)]
pub struct IPCCommand {
id: String,
payload: String,
pub id: String,
#[serde(default)]
pub payload: String,
}
impl IPCCommand {
@ -28,6 +34,15 @@ impl IPCCommand {
"exit" => {
Some(Event::Action(ActionType::Exit))
},
"toggle" => {
Some(Event::Action(ActionType::Toggle))
},
"enable" => {
Some(Event::Action(ActionType::Enable))
},
"disable" => {
Some(Event::Action(ActionType::Disable))
},
_ => None
}
}
@ -35,6 +50,11 @@ impl IPCCommand {
// UNIX IMPLEMENTATION
#[cfg(not(target_os = "windows"))]
pub fn get_ipc_manager(event_channel: Sender<Event>) -> impl IPCManager {
unix::UnixIPCManager::new(event_channel)
pub fn get_ipc_server(event_channel: Sender<Event>) -> impl IPCServer {
unix::UnixIPCServer::new(event_channel)
}
#[cfg(not(target_os = "windows"))]
pub fn get_ipc_client() -> impl IPCClient {
unix::UnixIPCClient::new()
}

View File

@ -1,4 +1,5 @@
use std::io::{BufRead, BufReader, Read};
use std::io::Write;
use std::os::unix::net::{UnixStream,UnixListener};
use std::thread;
use log::{info, error};
@ -11,19 +12,20 @@ use crate::event::*;
const UNIX_SOCKET_NAME : &str = "espanso.sock";
pub struct UnixIPCManager {
pub struct UnixIPCServer {
event_channel: Sender<Event>,
}
impl UnixIPCManager {
pub fn new(event_channel: Sender<Event>) -> UnixIPCManager {
UnixIPCManager{event_channel}
impl UnixIPCServer {
pub fn new(event_channel: Sender<Event>) -> UnixIPCServer {
UnixIPCServer {event_channel}
}
}
impl super::IPCManager for UnixIPCManager {
fn start_server(&self) {
std::thread::spawn(|| {
impl super::IPCServer for UnixIPCServer {
fn start(&self) {
let event_channel = self.event_channel.clone();
std::thread::spawn(move || {
let espanso_dir = context::get_data_dir();
let unix_socket = espanso_dir.join(UNIX_SOCKET_NAME);
@ -44,7 +46,7 @@ impl super::IPCManager for UnixIPCManager {
Ok(command) => {
let event = command.to_event();
if let Some(event) = event {
// TODO: send event to event channel
event_channel.send(event).expect("Broken event channel");
}
},
Err(e) => {
@ -60,4 +62,37 @@ impl super::IPCManager for UnixIPCManager {
}
});
}
}
pub struct UnixIPCClient {
}
impl UnixIPCClient {
pub fn new() -> UnixIPCClient {
UnixIPCClient{}
}
}
impl super::IPCClient for UnixIPCClient {
fn send_command(&self, command: IPCCommand) {
let espanso_dir = context::get_data_dir();
let unix_socket = espanso_dir.join(UNIX_SOCKET_NAME);
// Open the stream
let mut stream = UnixStream::connect(unix_socket);
match stream {
Ok(mut stream) => {
let json_str = serde_json::to_string(&command);
if let Ok(json_str) = json_str {
stream.write_all(json_str.as_bytes()).unwrap_or_else(|e| {
println!("Can't write to IPC socket");
});
}
},
Err(e) => {
println!("Can't connect to daemon: {}", e);
}
}
}
}