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 {
|
let message = if status {
|
||||||
"espanso enabled"
|
"espanso enabled"
|
||||||
}else{
|
}else{
|
||||||
|
|
|
@ -14,6 +14,8 @@ pub enum ActionType {
|
||||||
Toggle = 1,
|
Toggle = 1,
|
||||||
Exit = 2,
|
Exit = 2,
|
||||||
IconClick = 3,
|
IconClick = 3,
|
||||||
|
Enable = 4,
|
||||||
|
Disable = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<i32> for ActionType {
|
impl From<i32> for ActionType {
|
||||||
|
@ -22,6 +24,8 @@ impl From<i32> for ActionType {
|
||||||
1 => ActionType::Toggle,
|
1 => ActionType::Toggle,
|
||||||
2 => ActionType::Exit,
|
2 => ActionType::Exit,
|
||||||
3 => ActionType::IconClick,
|
3 => ActionType::IconClick,
|
||||||
|
4 => ActionType::Enable,
|
||||||
|
5 => ActionType::Disable,
|
||||||
_ => ActionType::Noop,
|
_ => 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::sync::mpsc::Receiver;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use clap::{App, Arg, SubCommand};
|
use clap::{App, Arg, SubCommand, ArgMatches};
|
||||||
use fs2::FileExt;
|
use fs2::FileExt;
|
||||||
use log::{error, info, LevelFilter};
|
use log::{error, info, LevelFilter};
|
||||||
use simplelog::{CombinedLogger, SharedLogger, TerminalMode, TermLogger};
|
use simplelog::{CombinedLogger, SharedLogger, TerminalMode, TermLogger};
|
||||||
|
@ -50,6 +50,17 @@ fn main() {
|
||||||
.short("v")
|
.short("v")
|
||||||
.multiple(true)
|
.multiple(true)
|
||||||
.help("Sets the level of verbosity"))
|
.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")
|
.subcommand(SubCommand::with_name("dump")
|
||||||
.about("Prints all current configuration options."))
|
.about("Prints all current configuration options."))
|
||||||
.subcommand(SubCommand::with_name("detect")
|
.subcommand(SubCommand::with_name("detect")
|
||||||
|
@ -111,6 +122,11 @@ fn main() {
|
||||||
status_main();
|
status_main();
|
||||||
return;
|
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
|
/// 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);
|
daemon_background(receive_channel, config_set);
|
||||||
});
|
});
|
||||||
|
|
||||||
let ipc_manager = protocol::get_ipc_manager(send_channel.clone());
|
let ipc_server = protocol::get_ipc_server(send_channel.clone());
|
||||||
ipc_manager.start_server();
|
ipc_server.start();
|
||||||
|
|
||||||
context.eventloop();
|
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> {
|
fn acquire_lock() -> Option<File> {
|
||||||
let espanso_dir = context::get_data_dir();
|
let espanso_dir = context::get_data_dir();
|
||||||
let lock_file_path = espanso_dir.join("espanso.lock");
|
let lock_file_path = espanso_dir.join("espanso.lock");
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub struct Match {
|
||||||
|
|
||||||
pub trait MatchReceiver {
|
pub trait MatchReceiver {
|
||||||
fn on_match(&self, m: &Match);
|
fn on_match(&self, m: &Match);
|
||||||
fn on_toggle(&self, status: bool);
|
fn on_enable_update(&self, status: bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Matcher : KeyEventReceiver {
|
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();
|
let mut is_enabled = self.is_enabled.borrow_mut();
|
||||||
*is_enabled = !(*is_enabled);
|
*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 => {
|
ActionType::Toggle => {
|
||||||
self.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"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
mod unix;
|
mod unix;
|
||||||
|
|
||||||
pub trait IPCManager {
|
pub trait IPCServer {
|
||||||
fn start_server(&self);
|
fn start(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IPCClient {
|
||||||
|
fn send_command(&self, command: IPCCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct IPCCommand {
|
pub struct IPCCommand {
|
||||||
id: String,
|
pub id: String,
|
||||||
payload: String,
|
|
||||||
|
#[serde(default)]
|
||||||
|
pub payload: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IPCCommand {
|
impl IPCCommand {
|
||||||
|
@ -28,6 +34,15 @@ impl IPCCommand {
|
||||||
"exit" => {
|
"exit" => {
|
||||||
Some(Event::Action(ActionType::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
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +50,11 @@ impl IPCCommand {
|
||||||
|
|
||||||
// UNIX IMPLEMENTATION
|
// UNIX IMPLEMENTATION
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
pub fn get_ipc_manager(event_channel: Sender<Event>) -> impl IPCManager {
|
pub fn get_ipc_server(event_channel: Sender<Event>) -> impl IPCServer {
|
||||||
unix::UnixIPCManager::new(event_channel)
|
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::{BufRead, BufReader, Read};
|
||||||
|
use std::io::Write;
|
||||||
use std::os::unix::net::{UnixStream,UnixListener};
|
use std::os::unix::net::{UnixStream,UnixListener};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use log::{info, error};
|
use log::{info, error};
|
||||||
|
@ -11,19 +12,20 @@ use crate::event::*;
|
||||||
|
|
||||||
const UNIX_SOCKET_NAME : &str = "espanso.sock";
|
const UNIX_SOCKET_NAME : &str = "espanso.sock";
|
||||||
|
|
||||||
pub struct UnixIPCManager {
|
pub struct UnixIPCServer {
|
||||||
event_channel: Sender<Event>,
|
event_channel: Sender<Event>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnixIPCManager {
|
impl UnixIPCServer {
|
||||||
pub fn new(event_channel: Sender<Event>) -> UnixIPCManager {
|
pub fn new(event_channel: Sender<Event>) -> UnixIPCServer {
|
||||||
UnixIPCManager{event_channel}
|
UnixIPCServer {event_channel}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::IPCManager for UnixIPCManager {
|
impl super::IPCServer for UnixIPCServer {
|
||||||
fn start_server(&self) {
|
fn start(&self) {
|
||||||
std::thread::spawn(|| {
|
let event_channel = self.event_channel.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
let espanso_dir = context::get_data_dir();
|
let espanso_dir = context::get_data_dir();
|
||||||
let unix_socket = espanso_dir.join(UNIX_SOCKET_NAME);
|
let unix_socket = espanso_dir.join(UNIX_SOCKET_NAME);
|
||||||
|
|
||||||
|
@ -44,7 +46,7 @@ impl super::IPCManager for UnixIPCManager {
|
||||||
Ok(command) => {
|
Ok(command) => {
|
||||||
let event = command.to_event();
|
let event = command.to_event();
|
||||||
if let Some(event) = event {
|
if let Some(event) = event {
|
||||||
// TODO: send event to event channel
|
event_channel.send(event).expect("Broken event channel");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -61,3 +63,36 @@ 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