Add IPC on Unix systems
This commit is contained in:
parent
041f53842f
commit
2b4835171a
|
@ -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{
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
57
src/main.rs
57
src/main.rs
|
@ -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");
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user