Merge pull request #349 from federico-terzi/dev

Version 0.6.3
This commit is contained in:
Federico Terzi 2020-06-29 20:32:41 +02:00 committed by GitHub
commit 4878bc58bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 404 additions and 84 deletions

12
Cargo.lock generated
View File

@ -371,7 +371,7 @@ dependencies = [
[[package]]
name = "espanso"
version = "0.6.2"
version = "0.6.3"
dependencies = [
"backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
@ -384,6 +384,7 @@ dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log-panics 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"named_pipe 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"notify 4.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -802,6 +803,14 @@ dependencies = [
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "named_pipe"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "native-tls"
version = "0.2.3"
@ -1911,6 +1920,7 @@ dependencies = [
"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23"
"checksum mio-extras 2.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum named_pipe 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad9c443cce91fc3e12f017290db75dde490d685cdaaf508d7159d7cf41f0eb2b"
"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e"
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"

View File

@ -1,6 +1,6 @@
[package]
name = "espanso"
version = "0.6.2"
version = "0.6.3"
authors = ["Federico Terzi <federicoterzi96@gmail.com>"]
license = "GPL-3.0"
description = "Cross-platform Text Expander written in Rust"
@ -36,6 +36,9 @@ notify = "4.0.13"
libc = "0.2.62"
signal-hook = "0.1.15"
[target.'cfg(windows)'.dependencies]
named_pipe = "0.4.1"
[build-dependencies]
cmake = "0.1.31"

View File

@ -77,14 +77,20 @@ xdo_t * xdo_context;
// Callback invoked when a new key event occur.
void event_callback (XPointer, XRecordInterceptData*);
int error_callback(Display *display, XErrorEvent *error);
KeypressCallback keypress_callback;
X11ErrorCallback x11_error_callback;
void * context_instance;
void register_keypress_callback(KeypressCallback callback) {
keypress_callback = callback;
}
void register_error_callback(X11ErrorCallback callback) {
x11_error_callback = callback;
}
int32_t check_x11() {
Display *check_disp = XOpenDisplay(NULL);
@ -156,6 +162,9 @@ int32_t initialize(void * _context_instance) {
xdo_context = xdo_new(NULL);
// Setup a custom error handler
XSetErrorHandler(&error_callback);
/**
* Note: We might never get a MappingNotify event if the
* modifier and keymap information was never cached in Xlib.
@ -272,6 +281,11 @@ void event_callback(XPointer p, XRecordInterceptData *hook)
XRecordFreeData(hook);
}
int error_callback(Display *display, XErrorEvent *error) {
x11_error_callback(context_instance, error->error_code, error->request_code, error->minor_code);
return 0;
}
void release_all_keys() {
char keys[32];
XQueryKeymap(xdo_context->xdpy, keys); // Get the current status of the keyboard

View File

@ -49,7 +49,6 @@ extern "C" void cleanup();
* while the second is the size of the array.
*/
typedef void (*KeypressCallback)(void * self, const char *buffer, int32_t len, int32_t event_type, int32_t key_code);
extern KeypressCallback keypress_callback;
/*
@ -57,6 +56,17 @@ extern KeypressCallback keypress_callback;
*/
extern "C" void register_keypress_callback(KeypressCallback callback);
/*
* Called when a X11 error occurs
*/
typedef void (*X11ErrorCallback)(void * self, char error_code, char request_code, char minor_code);
extern X11ErrorCallback x11_error_callback;
/*
* Register the callback that will be called when an X11 error occurs
*/
extern "C" void register_error_callback(X11ErrorCallback callback);
/*
* Type the given string by simulating Key Presses
*/

View File

@ -566,6 +566,38 @@ void send_multi_vkey_with_delay(int32_t vk, int32_t count, int32_t delay) {
}
}
void trigger_shift_paste() {
std::vector<INPUT> vec;
INPUT input = { 0 };
input.type = INPUT_KEYBOARD;
input.ki.wScan = 0;
input.ki.time = 0;
input.ki.dwExtraInfo = 0;
input.ki.wVk = VK_CONTROL;
input.ki.dwFlags = 0; // 0 for key press
vec.push_back(input);
input.ki.wVk = VK_SHIFT; // SHIFT KEY
vec.push_back(input);
input.ki.wVk = 0x56; // V KEY
vec.push_back(input);
input.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
vec.push_back(input);
input.ki.wVk = VK_SHIFT; // SHIFT KEY
vec.push_back(input);
input.ki.wVk = VK_CONTROL;
vec.push_back(input);
SendInput(vec.size(), vec.data(), sizeof(INPUT));
}
void trigger_paste() {
std::vector<INPUT> vec;

View File

@ -87,6 +87,11 @@ extern "C" void delete_string(int32_t count, int32_t delay);
*/
extern "C" void trigger_paste();
/*
* Send the Paste keyboard shortcut (CTRL+SHIFT+V)
*/
extern "C" void trigger_shift_paste();
/*
* Send the copy keyboard shortcut (CTRL+C)
*/

View File

@ -82,11 +82,24 @@ def build_windows(package_info):
msvc_dirs = glob.glob("C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\*\\VC\\Redist\\MSVC\\*")
print("Found Redists: ", msvc_dirs)
print("Determining best redist...")
if len(msvc_dirs) == 0:
raise Exception("Cannot find redistributable dlls")
msvc_dir = msvc_dirs[-1] # Take the most recent version of the toolchain
print("Using: ",msvc_dir)
msvc_dir = None
for curr_dir in msvc_dirs:
dll_files = glob.glob(curr_dir + "\\x64\\*CRT\\*.dll")
print("Found dlls", dll_files, "in", curr_dir)
if any("vcruntime140_1.dll" in x.lower() for x in dll_files):
msvc_dir = curr_dir
break
if msvc_dir is None:
raise Exception("Cannot find redist with VCRUNTIME140_1.dll")
print("Using: ", msvc_dir)
dll_files = glob.glob(msvc_dir + "\\x64\\*CRT\\*.dll")

View File

@ -1,5 +1,5 @@
name: espanso
version: 0.6.2
version: 0.6.3
summary: A Cross-platform Text Expander written in Rust
description: |
espanso is a Cross-platform, Text Expander written in Rust.

View File

@ -32,6 +32,14 @@ extern "C" {
pub fn get_active_window_class(buffer: *mut c_char, size: i32) -> i32;
pub fn get_active_window_executable(buffer: *mut c_char, size: i32) -> i32;
pub fn is_current_window_special() -> i32;
pub fn register_error_callback(
cb: extern "C" fn(
_self: *mut c_void,
error_code: c_char,
request_code: c_char,
minor_code: c_char,
),
);
// Keyboard
pub fn register_keypress_callback(

View File

@ -65,6 +65,7 @@ extern "C" {
pub fn send_multi_vkey(vk: i32, count: i32);
pub fn delete_string(count: i32, delay: i32);
pub fn trigger_paste();
pub fn trigger_shift_paste();
pub fn trigger_copy();
// PROCESSES

87
src/cli.rs Normal file
View File

@ -0,0 +1,87 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2020 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::config::ConfigSet;
use crate::matcher::{Match, MatchContentType};
use serde::Serialize;
pub fn list_matches(config_set: ConfigSet, onlytriggers: bool, preserve_newlines: bool) {
let matches = filter_matches(config_set);
for m in matches {
for trigger in m.triggers.iter() {
if onlytriggers {
println!("{}", trigger);
} else {
match m.content {
MatchContentType::Text(ref text) => {
let replace = if preserve_newlines {
text.replace.to_owned()
} else {
text.replace.replace("\n", " ")
};
println!("{} - {}", trigger, replace)
}
MatchContentType::Image(_) => {
// Skip image matches for now
}
}
}
}
}
}
#[derive(Debug, Serialize)]
struct JsonMatchEntry {
triggers: Vec<String>,
replace: String,
}
pub fn list_matches_as_json(config_set: ConfigSet) {
let matches = filter_matches(config_set);
let mut entries = Vec::new();
for m in matches {
match m.content {
MatchContentType::Text(ref text) => entries.push(JsonMatchEntry {
triggers: m.triggers,
replace: text.replace.clone(),
}),
MatchContentType::Image(_) => {
// Skip image matches for now
}
}
}
let output = serde_json::to_string(&entries);
println!("{}", output.unwrap_or_default())
}
fn filter_matches(config_set: ConfigSet) -> Vec<Match> {
let mut output = Vec::new();
output.extend(config_set.default.matches);
// TODO: consider specific matches by class, title or exe path
// for specific in config_set.specific {
// output.extend(specific.matches)
// }
output
}

View File

@ -129,7 +129,7 @@ fn default_show_notifications() -> bool {
true
}
fn default_auto_restart() -> bool {
true
true
}
fn default_show_icon() -> bool {
true

View File

@ -21,7 +21,7 @@ use crate::bridge::linux::*;
use crate::config::Configs;
use crate::event::KeyModifier::*;
use crate::event::*;
use log::{debug, error};
use log::{debug, error, warn};
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};
use std::process::exit;
@ -59,6 +59,7 @@ impl LinuxContext {
let context_ptr = &*context as *const LinuxContext as *const c_void;
register_keypress_callback(keypress_callback);
register_error_callback(error_callback);
let res = initialize(context_ptr);
if res <= 0 {
@ -155,3 +156,15 @@ extern "C" fn keypress_callback(
}
}
}
extern "C" fn error_callback(
_self: *mut c_void,
error_code: c_char,
request_code: c_char,
minor_code: c_char,
) {
warn!(
"X11 reported an error code: {}, request_code: {} and minor_code: {}",
error_code, request_code, minor_code
);
}

View File

@ -173,8 +173,7 @@ extern "C" fn keypress_callback(
}
} else if event_type == 1 {
// Modifier event
if is_key_down == 1 {
// Keyup event
if is_key_down == 0 {
let modifier: Option<KeyModifier> = match (key_code, variant) {
(0x5B, _) => Some(LEFT_META),
(0x5C, _) => Some(RIGHT_META),

View File

@ -132,22 +132,28 @@ impl<
None
}
}
}
lazy_static! {
static ref VAR_REGEX: Regex = Regex::new("\\{\\{\\s*(?P<name>\\w+)\\s*\\}\\}").unwrap();
}
fn find_match_by_trigger(&self, trigger: &str) -> Option<Match> {
let config = self.config_manager.active_config();
impl<
'a,
S: KeyboardManager,
C: ClipboardManager,
M: ConfigManager<'a>,
U: UIManager,
R: Renderer,
> MatchReceiver for Engine<'a, S, C, M, U, R>
{
fn on_match(&self, m: &Match, trailing_separator: Option<char>, trigger_offset: usize) {
if let Some(m) = config
.matches
.iter()
.find(|m| m.triggers.iter().any(|t| t == trigger))
{
Some(m.clone())
} else {
None
}
}
fn inject_match(
&self,
m: &Match,
trailing_separator: Option<char>,
trigger_offset: usize,
skip_delete: bool,
) {
let config = self.config_manager.active_config();
if !config.enable_active {
@ -163,7 +169,9 @@ impl<
m.triggers[trigger_offset].chars().count() as i32 + 1 // Count also the separator
};
self.keyboard_manager.delete_string(&config, char_count);
if !skip_delete {
self.keyboard_manager.delete_string(&config, char_count);
}
let mut previous_clipboard_content: Option<String> = None;
@ -287,6 +295,24 @@ impl<
// Re-allow espanso to interpret actions
self.is_injecting.store(false, Release);
}
}
lazy_static! {
static ref VAR_REGEX: Regex = Regex::new("\\{\\{\\s*(?P<name>\\w+)\\s*\\}\\}").unwrap();
}
impl<
'a,
S: KeyboardManager,
C: ClipboardManager,
M: ConfigManager<'a>,
U: UIManager,
R: Renderer,
> MatchReceiver for Engine<'a, S, C, M, U, R>
{
fn on_match(&self, m: &Match, trailing_separator: Option<char>, trigger_offset: usize) {
self.inject_match(m, trailing_separator, trigger_offset, false);
}
fn on_enable_update(&self, status: bool) {
let message = if status {
@ -432,6 +458,15 @@ impl<
self.ui_manager.notify(&message);
}
}
SystemEvent::Trigger(trigger) => {
let m = self.find_match_by_trigger(&trigger);
match m {
Some(m) => {
self.inject_match(&m, None, 0, true);
}
None => warn!("No match found with trigger: {}", trigger),
}
}
}
}
}

View File

@ -130,6 +130,9 @@ pub enum SystemEvent {
// Notification
NotifyRequest(String),
// Trigger an expansion from IPC
Trigger(String),
}
// Receivers

View File

@ -88,7 +88,8 @@ impl super::Extension for ScriptExtension {
match output {
Ok(output) => {
let mut output_str = String::from_utf8_lossy(output.stdout.as_slice()).to_string();
let mut output_str =
String::from_utf8_lossy(output.stdout.as_slice()).to_string();
let error_str = String::from_utf8_lossy(output.stderr.as_slice());
let error_str = error_str.to_string();
let error_str = error_str.trim();
@ -103,7 +104,7 @@ impl super::Extension for ScriptExtension {
let should_trim = if let Some(value) = trim_opt {
let val = value.as_bool();
val.unwrap_or(true)
}else{
} else {
true
};
@ -154,10 +155,7 @@ mod tests {
Value::from("args"),
Value::from(vec!["echo", "hello world"]),
);
params.insert(
Value::from("trim"),
Value::from(false),
);
params.insert(Value::from("trim"), Value::from(false));
let extension = ScriptExtension::new();
let output = extension.calculate(&params, &vec![]);

View File

@ -34,6 +34,7 @@ pub enum Shell {
Cmd,
Powershell,
WSL,
WSL2,
Bash,
Sh,
}
@ -45,27 +46,32 @@ impl Shell {
let mut command = Command::new("cmd");
command.args(&["/C", &cmd]);
command
},
}
Shell::Powershell => {
let mut command = Command::new("powershell");
command.args(&["-Command", &cmd]);
command
},
}
Shell::WSL => {
let mut command = Command::new("bash");
command.args(&["-c", &cmd]);
command
}
Shell::WSL2 => {
let mut command = Command::new("wsl");
command.args(&["bash", "-c", &cmd]);
command
},
}
Shell::Bash => {
let mut command = Command::new("bash");
command.args(&["-c", &cmd]);
command
},
}
Shell::Sh => {
let mut command = Command::new("sh");
command.args(&["-c", &cmd]);
command
},
}
};
// Inject the $CONFIG variable
@ -79,6 +85,7 @@ impl Shell {
"cmd" => Some(Shell::Cmd),
"powershell" => Some(Shell::Powershell),
"wsl" => Some(Shell::WSL),
"wsl2" => Some(Shell::WSL2),
"bash" => Some(Shell::Bash),
"sh" => Some(Shell::Sh),
_ => None,
@ -169,7 +176,7 @@ impl super::Extension for ShellExtension {
let should_trim = if let Some(value) = trim_opt {
let val = value.as_bool();
val.unwrap_or(true)
}else{
} else {
true
};

View File

@ -51,6 +51,9 @@ impl super::KeyboardManager for WindowsKeyboardManager {
trigger_paste();
}
},
PasteShortcut::CtrlShiftV => {
trigger_shift_paste();
},
_ => {
error!("Windows backend does not support this Paste Shortcut, please open an issue on GitHub if you need it.")
}

View File

@ -53,6 +53,7 @@ use crate::ui::UIManager;
mod bridge;
mod check;
mod cli;
mod clipboard;
mod config;
mod context;
@ -158,6 +159,39 @@ fn main() {
.subcommand(SubCommand::with_name("default")
.about("Print the default configuration file path."))
)
.subcommand(SubCommand::with_name("match")
.about("List and execute matches from the CLI")
.subcommand(SubCommand::with_name("list")
.about("Print all matches to standard output")
.arg(Arg::with_name("json")
.short("j")
.long("json")
.help("Return the matches as json")
.required(false)
.takes_value(false)
)
.arg(Arg::with_name("onlytriggers")
.short("t")
.long("onlytriggers")
.help("Print only triggers without replacement")
.required(false)
.takes_value(false)
)
.arg(Arg::with_name("preservenewlines")
.short("n")
.long("preservenewlines")
.help("Preserve newlines when printing replacements")
.required(false)
.takes_value(false)
)
)
.subcommand(SubCommand::with_name("exec")
.about("Triggers the expansion of the given match")
.arg(Arg::with_name("trigger")
.help("The trigger of the match to be expanded")
)
)
)
// Package manager
.subcommand(SubCommand::with_name("package")
.about("Espanso package manager commands")
@ -274,6 +308,11 @@ fn main() {
return;
}
if let Some(matches) = matches.subcommand_matches("match") {
match_main(config_set, matches);
return;
}
if let Some(matches) = matches.subcommand_matches("package") {
if let Some(matches) = matches.subcommand_matches("install") {
install_main(config_set, matches);
@ -480,18 +519,14 @@ fn register_signals(_: Configs) {}
fn register_signals(config: Configs) {
// On Unix, also listen for signals so that we can terminate the
// worker if the daemon receives a signal
use signal_hook::{iterator::Signals, SIGTERM, SIGINT};
use signal_hook::{iterator::Signals, SIGINT, SIGTERM};
let signals = Signals::new(&[SIGTERM, SIGINT]).expect("unable to register for signals");
thread::Builder::new()
.name("signal monitor".to_string())
.spawn(move || {
for signal in signals.forever() {
info!("Received signal: {:?}, terminating worker", signal);
send_command_or_warn(
Service::Worker,
config,
IPCCommand::exit_worker(),
);
send_command_or_warn(Service::Worker, config, IPCCommand::exit_worker());
std::thread::sleep(Duration::from_millis(200));
@ -1027,7 +1062,13 @@ fn install_main(_config_set: ConfigSet, matches: &ArgMatches) {
exit(1);
});
let repository = matches.value_of("repository_url").unwrap_or("hub");
let mut repository = matches.value_of("repository_url").unwrap_or("hub");
// Remove trailing .git string if present
// See: https://github.com/federico-terzi/espanso/issues/326
if repository.ends_with(".git") {
repository = repository.trim_end_matches(".git")
}
let package_resolver = Box::new(ZipPackageResolver::new());
@ -1223,6 +1264,31 @@ fn path_main(_config_set: ConfigSet, matches: &ArgMatches) {
}
}
fn match_main(config_set: ConfigSet, matches: &ArgMatches) {
if let Some(matches) = matches.subcommand_matches("list") {
let json = matches.is_present("json");
let onlytriggers = matches.is_present("onlytriggers");
let preserve_newlines = matches.is_present("preservenewlines");
if !json {
crate::cli::list_matches(config_set, onlytriggers, preserve_newlines);
} else {
crate::cli::list_matches_as_json(config_set);
}
} else if let Some(matches) = matches.subcommand_matches("exec") {
let trigger = matches.value_of("trigger").unwrap_or_else(|| {
eprintln!("missing trigger");
exit(1);
});
send_command_or_warn(
Service::Worker,
config_set.default.clone(),
IPCCommand::trigger(trigger),
);
}
}
fn edit_main(matches: &ArgMatches) {
// Determine which is the file to edit
let config = matches.value_of("config").unwrap_or("default");

View File

@ -67,6 +67,7 @@ impl IPCCommand {
"notify" => Some(Event::System(SystemEvent::NotifyRequest(
self.payload.clone(),
))),
"trigger" => Some(Event::System(SystemEvent::Trigger(self.payload.clone()))),
_ => None,
}
}
@ -101,6 +102,10 @@ impl IPCCommand {
id: "notify".to_owned(),
payload: message,
}),
Event::System(SystemEvent::Trigger(trigger)) => Some(IPCCommand {
id: "trigger".to_owned(),
payload: trigger,
}),
_ => None,
}
}
@ -125,6 +130,13 @@ impl IPCCommand {
payload: "".to_owned(),
}
}
pub fn trigger(trigger: &str) -> IPCCommand {
Self {
id: "trigger".to_owned(),
payload: trigger.to_owned(),
}
}
}
fn process_event<R: Read, E: Error>(event_channel: &Sender<Event>, stream: Result<R, E>) {
@ -200,13 +212,13 @@ pub fn get_ipc_client(service: Service, _: Configs) -> impl IPCClient {
#[cfg(target_os = "windows")]
pub fn get_ipc_server(
service: Service,
config: Configs,
_: Configs,
event_channel: Sender<Event>,
) -> impl IPCServer {
windows::WindowsIPCServer::new(service, config, event_channel)
windows::WindowsIPCServer::new(service, event_channel)
}
#[cfg(target_os = "windows")]
pub fn get_ipc_client(service: Service, config: Configs) -> impl IPCClient {
windows::WindowsIPCClient::new(service, config)
pub fn get_ipc_client(service: Service, _: Configs) -> impl IPCClient {
windows::WindowsIPCClient::new(service)
}

View File

@ -23,32 +23,33 @@ use std::net::{TcpListener, TcpStream};
use std::sync::mpsc::Sender;
use crate::config::Configs;
use crate::context;
use crate::event::*;
use crate::protocol::{process_event, send_command, Service};
use named_pipe::{PipeClient, PipeOptions, PipeServer};
use std::io::Error;
use std::path::PathBuf;
const DAEMON_WIN_PIPE_NAME: &str = "\\\\.\\pipe\\espansodaemon";
const WORKER_WIN_PIPE_NAME: &str = "\\\\.\\pipe\\espansoworker";
const CLIENT_TIMEOUT: u32 = 2000;
pub struct WindowsIPCServer {
service: Service,
config: Configs,
event_channel: Sender<Event>,
}
fn to_port(config: &Configs, service: &Service) -> u16 {
let port = match service {
Service::Daemon => config.ipc_server_port,
Service::Worker => config.worker_ipc_server_port,
};
port as u16
fn get_pipe_name(service: &Service) -> String {
match service {
Service::Daemon => DAEMON_WIN_PIPE_NAME.to_owned(),
Service::Worker => WORKER_WIN_PIPE_NAME.to_owned(),
}
}
impl WindowsIPCServer {
pub fn new(
service: Service,
config: Configs,
event_channel: Sender<Event>,
) -> WindowsIPCServer {
pub fn new(service: Service, event_channel: Sender<Event>) -> WindowsIPCServer {
WindowsIPCServer {
service,
config,
event_channel,
}
}
@ -57,20 +58,20 @@ impl WindowsIPCServer {
impl super::IPCServer for WindowsIPCServer {
fn start(&self) {
let event_channel = self.event_channel.clone();
let server_port = to_port(&self.config, &self.service);
let pipe_name = get_pipe_name(&self.service);
std::thread::Builder::new()
.name("ipc_server".to_string())
.spawn(move || {
let listener = TcpListener::bind(format!("127.0.0.1:{}", server_port))
.expect("Error binding to IPC server port");
let options = PipeOptions::new(&pipe_name);
info!(
"Binded to IPC tcp socket: {}",
listener.local_addr().unwrap().to_string()
);
info!("Binding to named pipe: {}", pipe_name);
for stream in listener.incoming() {
process_event(&event_channel, stream);
loop {
let server = options
.single()
.expect("unable to initialize IPC named pipe");
let pipe_server = server.wait();
process_event(&event_channel, pipe_server);
}
})
.expect("Unable to spawn IPC server thread");
@ -79,20 +80,19 @@ impl super::IPCServer for WindowsIPCServer {
pub struct WindowsIPCClient {
service: Service,
config: Configs,
}
impl WindowsIPCClient {
pub fn new(service: Service, config: Configs) -> WindowsIPCClient {
WindowsIPCClient { service, config }
pub fn new(service: Service) -> WindowsIPCClient {
WindowsIPCClient { service }
}
}
impl super::IPCClient for WindowsIPCClient {
fn send_command(&self, command: IPCCommand) -> Result<(), String> {
let port = to_port(&self.config, &self.service);
let stream = TcpStream::connect(("127.0.0.1", port));
let pipe_name = get_pipe_name(&self.service);
let client = PipeClient::connect_ms(pipe_name, CLIENT_TIMEOUT);
send_command(command, stream)
send_command(command, client)
}
}

View File

@ -106,11 +106,12 @@ impl MacSystemManager {
if let Ok(path) = string {
if !path.trim().is_empty() {
let process = path.trim().to_string();
let app_name = if let Some(name) = Self::get_app_name_from_path(&process) {
name
} else {
process.to_owned()
};
let app_name =
if let Some(name) = Self::get_app_name_from_path(&process) {
name
} else {
process.to_owned()
};
return Some((app_name, process));
}
@ -138,14 +139,15 @@ impl MacSystemManager {
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_app_name_from_path() {
let app_name = MacSystemManager::get_app_name_from_path("/Applications/iTerm.app/Contents/MacOS/iTerm2");
let app_name = MacSystemManager::get_app_name_from_path(
"/Applications/iTerm.app/Contents/MacOS/iTerm2",
);
assert_eq!(app_name.unwrap(), "iTerm")
}
@ -161,4 +163,3 @@ mod tests {
assert_eq!(app_name.unwrap(), "SecurityAgent")
}
}