commit
4878bc58bd
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -371,7 +371,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "espanso"
|
name = "espanso"
|
||||||
version = "0.6.2"
|
version = "0.6.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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 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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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]]
|
[[package]]
|
||||||
name = "native-tls"
|
name = "native-tls"
|
||||||
version = "0.2.3"
|
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 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 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 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 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 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"
|
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "espanso"
|
name = "espanso"
|
||||||
version = "0.6.2"
|
version = "0.6.3"
|
||||||
authors = ["Federico Terzi <federicoterzi96@gmail.com>"]
|
authors = ["Federico Terzi <federicoterzi96@gmail.com>"]
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
description = "Cross-platform Text Expander written in Rust"
|
description = "Cross-platform Text Expander written in Rust"
|
||||||
|
@ -36,6 +36,9 @@ notify = "4.0.13"
|
||||||
libc = "0.2.62"
|
libc = "0.2.62"
|
||||||
signal-hook = "0.1.15"
|
signal-hook = "0.1.15"
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
named_pipe = "0.4.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cmake = "0.1.31"
|
cmake = "0.1.31"
|
||||||
|
|
||||||
|
|
|
@ -77,14 +77,20 @@ xdo_t * xdo_context;
|
||||||
|
|
||||||
// Callback invoked when a new key event occur.
|
// Callback invoked when a new key event occur.
|
||||||
void event_callback (XPointer, XRecordInterceptData*);
|
void event_callback (XPointer, XRecordInterceptData*);
|
||||||
|
int error_callback(Display *display, XErrorEvent *error);
|
||||||
|
|
||||||
KeypressCallback keypress_callback;
|
KeypressCallback keypress_callback;
|
||||||
|
X11ErrorCallback x11_error_callback;
|
||||||
void * context_instance;
|
void * context_instance;
|
||||||
|
|
||||||
void register_keypress_callback(KeypressCallback callback) {
|
void register_keypress_callback(KeypressCallback callback) {
|
||||||
keypress_callback = callback;
|
keypress_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void register_error_callback(X11ErrorCallback callback) {
|
||||||
|
x11_error_callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
int32_t check_x11() {
|
int32_t check_x11() {
|
||||||
Display *check_disp = XOpenDisplay(NULL);
|
Display *check_disp = XOpenDisplay(NULL);
|
||||||
|
|
||||||
|
@ -156,6 +162,9 @@ int32_t initialize(void * _context_instance) {
|
||||||
|
|
||||||
xdo_context = xdo_new(NULL);
|
xdo_context = xdo_new(NULL);
|
||||||
|
|
||||||
|
// Setup a custom error handler
|
||||||
|
XSetErrorHandler(&error_callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: We might never get a MappingNotify event if the
|
* Note: We might never get a MappingNotify event if the
|
||||||
* modifier and keymap information was never cached in Xlib.
|
* modifier and keymap information was never cached in Xlib.
|
||||||
|
@ -272,6 +281,11 @@ void event_callback(XPointer p, XRecordInterceptData *hook)
|
||||||
XRecordFreeData(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() {
|
void release_all_keys() {
|
||||||
char keys[32];
|
char keys[32];
|
||||||
XQueryKeymap(xdo_context->xdpy, keys); // Get the current status of the keyboard
|
XQueryKeymap(xdo_context->xdpy, keys); // Get the current status of the keyboard
|
||||||
|
|
|
@ -49,7 +49,6 @@ extern "C" void cleanup();
|
||||||
* while the second is the size of the array.
|
* 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);
|
typedef void (*KeypressCallback)(void * self, const char *buffer, int32_t len, int32_t event_type, int32_t key_code);
|
||||||
|
|
||||||
extern KeypressCallback keypress_callback;
|
extern KeypressCallback keypress_callback;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -57,6 +56,17 @@ extern KeypressCallback keypress_callback;
|
||||||
*/
|
*/
|
||||||
extern "C" void register_keypress_callback(KeypressCallback 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
|
* Type the given string by simulating Key Presses
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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() {
|
void trigger_paste() {
|
||||||
std::vector<INPUT> vec;
|
std::vector<INPUT> vec;
|
||||||
|
|
||||||
|
|
|
@ -87,6 +87,11 @@ extern "C" void delete_string(int32_t count, int32_t delay);
|
||||||
*/
|
*/
|
||||||
extern "C" void trigger_paste();
|
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)
|
* Send the copy keyboard shortcut (CTRL+C)
|
||||||
*/
|
*/
|
||||||
|
|
17
packager.py
17
packager.py
|
@ -82,11 +82,24 @@ def build_windows(package_info):
|
||||||
msvc_dirs = glob.glob("C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\*\\VC\\Redist\\MSVC\\*")
|
msvc_dirs = glob.glob("C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\*\\VC\\Redist\\MSVC\\*")
|
||||||
print("Found Redists: ", msvc_dirs)
|
print("Found Redists: ", msvc_dirs)
|
||||||
|
|
||||||
|
print("Determining best redist...")
|
||||||
|
|
||||||
if len(msvc_dirs) == 0:
|
if len(msvc_dirs) == 0:
|
||||||
raise Exception("Cannot find redistributable dlls")
|
raise Exception("Cannot find redistributable dlls")
|
||||||
|
|
||||||
msvc_dir = msvc_dirs[-1] # Take the most recent version of the toolchain
|
msvc_dir = None
|
||||||
print("Using: ",msvc_dir)
|
|
||||||
|
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")
|
dll_files = glob.glob(msvc_dir + "\\x64\\*CRT\\*.dll")
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: espanso
|
name: espanso
|
||||||
version: 0.6.2
|
version: 0.6.3
|
||||||
summary: A Cross-platform Text Expander written in Rust
|
summary: A Cross-platform Text Expander written in Rust
|
||||||
description: |
|
description: |
|
||||||
espanso is a Cross-platform, Text Expander written in Rust.
|
espanso is a Cross-platform, Text Expander written in Rust.
|
||||||
|
|
|
@ -32,6 +32,14 @@ extern "C" {
|
||||||
pub fn get_active_window_class(buffer: *mut c_char, size: i32) -> i32;
|
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 get_active_window_executable(buffer: *mut c_char, size: i32) -> i32;
|
||||||
pub fn is_current_window_special() -> 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
|
// Keyboard
|
||||||
pub fn register_keypress_callback(
|
pub fn register_keypress_callback(
|
||||||
|
|
|
@ -65,6 +65,7 @@ extern "C" {
|
||||||
pub fn send_multi_vkey(vk: i32, count: i32);
|
pub fn send_multi_vkey(vk: i32, count: i32);
|
||||||
pub fn delete_string(count: i32, delay: i32);
|
pub fn delete_string(count: i32, delay: i32);
|
||||||
pub fn trigger_paste();
|
pub fn trigger_paste();
|
||||||
|
pub fn trigger_shift_paste();
|
||||||
pub fn trigger_copy();
|
pub fn trigger_copy();
|
||||||
|
|
||||||
// PROCESSES
|
// PROCESSES
|
||||||
|
|
87
src/cli.rs
Normal file
87
src/cli.rs
Normal 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
|
||||||
|
}
|
|
@ -129,7 +129,7 @@ fn default_show_notifications() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
fn default_auto_restart() -> bool {
|
fn default_auto_restart() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
fn default_show_icon() -> bool {
|
fn default_show_icon() -> bool {
|
||||||
true
|
true
|
||||||
|
|
|
@ -21,7 +21,7 @@ use crate::bridge::linux::*;
|
||||||
use crate::config::Configs;
|
use crate::config::Configs;
|
||||||
use crate::event::KeyModifier::*;
|
use crate::event::KeyModifier::*;
|
||||||
use crate::event::*;
|
use crate::event::*;
|
||||||
use log::{debug, error};
|
use log::{debug, error, warn};
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::os::raw::{c_char, c_void};
|
use std::os::raw::{c_char, c_void};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
@ -59,6 +59,7 @@ impl LinuxContext {
|
||||||
let context_ptr = &*context as *const LinuxContext as *const c_void;
|
let context_ptr = &*context as *const LinuxContext as *const c_void;
|
||||||
|
|
||||||
register_keypress_callback(keypress_callback);
|
register_keypress_callback(keypress_callback);
|
||||||
|
register_error_callback(error_callback);
|
||||||
|
|
||||||
let res = initialize(context_ptr);
|
let res = initialize(context_ptr);
|
||||||
if res <= 0 {
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -173,8 +173,7 @@ extern "C" fn keypress_callback(
|
||||||
}
|
}
|
||||||
} else if event_type == 1 {
|
} else if event_type == 1 {
|
||||||
// Modifier event
|
// Modifier event
|
||||||
if is_key_down == 1 {
|
if is_key_down == 0 {
|
||||||
// Keyup event
|
|
||||||
let modifier: Option<KeyModifier> = match (key_code, variant) {
|
let modifier: Option<KeyModifier> = match (key_code, variant) {
|
||||||
(0x5B, _) => Some(LEFT_META),
|
(0x5B, _) => Some(LEFT_META),
|
||||||
(0x5C, _) => Some(RIGHT_META),
|
(0x5C, _) => Some(RIGHT_META),
|
||||||
|
|
|
@ -132,22 +132,28 @@ impl<
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
lazy_static! {
|
fn find_match_by_trigger(&self, trigger: &str) -> Option<Match> {
|
||||||
static ref VAR_REGEX: Regex = Regex::new("\\{\\{\\s*(?P<name>\\w+)\\s*\\}\\}").unwrap();
|
let config = self.config_manager.active_config();
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
if let Some(m) = config
|
||||||
'a,
|
.matches
|
||||||
S: KeyboardManager,
|
.iter()
|
||||||
C: ClipboardManager,
|
.find(|m| m.triggers.iter().any(|t| t == trigger))
|
||||||
M: ConfigManager<'a>,
|
{
|
||||||
U: UIManager,
|
Some(m.clone())
|
||||||
R: Renderer,
|
} else {
|
||||||
> MatchReceiver for Engine<'a, S, C, M, U, R>
|
None
|
||||||
{
|
}
|
||||||
fn on_match(&self, m: &Match, trailing_separator: Option<char>, trigger_offset: usize) {
|
}
|
||||||
|
|
||||||
|
fn inject_match(
|
||||||
|
&self,
|
||||||
|
m: &Match,
|
||||||
|
trailing_separator: Option<char>,
|
||||||
|
trigger_offset: usize,
|
||||||
|
skip_delete: bool,
|
||||||
|
) {
|
||||||
let config = self.config_manager.active_config();
|
let config = self.config_manager.active_config();
|
||||||
|
|
||||||
if !config.enable_active {
|
if !config.enable_active {
|
||||||
|
@ -163,7 +169,9 @@ impl<
|
||||||
m.triggers[trigger_offset].chars().count() as i32 + 1 // Count also the separator
|
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;
|
let mut previous_clipboard_content: Option<String> = None;
|
||||||
|
|
||||||
|
@ -287,6 +295,24 @@ impl<
|
||||||
// Re-allow espanso to interpret actions
|
// Re-allow espanso to interpret actions
|
||||||
self.is_injecting.store(false, Release);
|
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) {
|
fn on_enable_update(&self, status: bool) {
|
||||||
let message = if status {
|
let message = if status {
|
||||||
|
@ -432,6 +458,15 @@ impl<
|
||||||
self.ui_manager.notify(&message);
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,6 +130,9 @@ pub enum SystemEvent {
|
||||||
|
|
||||||
// Notification
|
// Notification
|
||||||
NotifyRequest(String),
|
NotifyRequest(String),
|
||||||
|
|
||||||
|
// Trigger an expansion from IPC
|
||||||
|
Trigger(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receivers
|
// Receivers
|
||||||
|
|
|
@ -88,7 +88,8 @@ impl super::Extension for ScriptExtension {
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
Ok(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 = String::from_utf8_lossy(output.stderr.as_slice());
|
||||||
let error_str = error_str.to_string();
|
let error_str = error_str.to_string();
|
||||||
let error_str = error_str.trim();
|
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 should_trim = if let Some(value) = trim_opt {
|
||||||
let val = value.as_bool();
|
let val = value.as_bool();
|
||||||
val.unwrap_or(true)
|
val.unwrap_or(true)
|
||||||
}else{
|
} else {
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -154,10 +155,7 @@ mod tests {
|
||||||
Value::from("args"),
|
Value::from("args"),
|
||||||
Value::from(vec!["echo", "hello world"]),
|
Value::from(vec!["echo", "hello world"]),
|
||||||
);
|
);
|
||||||
params.insert(
|
params.insert(Value::from("trim"), Value::from(false));
|
||||||
Value::from("trim"),
|
|
||||||
Value::from(false),
|
|
||||||
);
|
|
||||||
|
|
||||||
let extension = ScriptExtension::new();
|
let extension = ScriptExtension::new();
|
||||||
let output = extension.calculate(¶ms, &vec![]);
|
let output = extension.calculate(¶ms, &vec![]);
|
||||||
|
|
|
@ -34,6 +34,7 @@ pub enum Shell {
|
||||||
Cmd,
|
Cmd,
|
||||||
Powershell,
|
Powershell,
|
||||||
WSL,
|
WSL,
|
||||||
|
WSL2,
|
||||||
Bash,
|
Bash,
|
||||||
Sh,
|
Sh,
|
||||||
}
|
}
|
||||||
|
@ -45,27 +46,32 @@ impl Shell {
|
||||||
let mut command = Command::new("cmd");
|
let mut command = Command::new("cmd");
|
||||||
command.args(&["/C", &cmd]);
|
command.args(&["/C", &cmd]);
|
||||||
command
|
command
|
||||||
},
|
}
|
||||||
Shell::Powershell => {
|
Shell::Powershell => {
|
||||||
let mut command = Command::new("powershell");
|
let mut command = Command::new("powershell");
|
||||||
command.args(&["-Command", &cmd]);
|
command.args(&["-Command", &cmd]);
|
||||||
command
|
command
|
||||||
},
|
}
|
||||||
Shell::WSL => {
|
Shell::WSL => {
|
||||||
|
let mut command = Command::new("bash");
|
||||||
|
command.args(&["-c", &cmd]);
|
||||||
|
command
|
||||||
|
}
|
||||||
|
Shell::WSL2 => {
|
||||||
let mut command = Command::new("wsl");
|
let mut command = Command::new("wsl");
|
||||||
command.args(&["bash", "-c", &cmd]);
|
command.args(&["bash", "-c", &cmd]);
|
||||||
command
|
command
|
||||||
},
|
}
|
||||||
Shell::Bash => {
|
Shell::Bash => {
|
||||||
let mut command = Command::new("bash");
|
let mut command = Command::new("bash");
|
||||||
command.args(&["-c", &cmd]);
|
command.args(&["-c", &cmd]);
|
||||||
command
|
command
|
||||||
},
|
}
|
||||||
Shell::Sh => {
|
Shell::Sh => {
|
||||||
let mut command = Command::new("sh");
|
let mut command = Command::new("sh");
|
||||||
command.args(&["-c", &cmd]);
|
command.args(&["-c", &cmd]);
|
||||||
command
|
command
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inject the $CONFIG variable
|
// Inject the $CONFIG variable
|
||||||
|
@ -79,6 +85,7 @@ impl Shell {
|
||||||
"cmd" => Some(Shell::Cmd),
|
"cmd" => Some(Shell::Cmd),
|
||||||
"powershell" => Some(Shell::Powershell),
|
"powershell" => Some(Shell::Powershell),
|
||||||
"wsl" => Some(Shell::WSL),
|
"wsl" => Some(Shell::WSL),
|
||||||
|
"wsl2" => Some(Shell::WSL2),
|
||||||
"bash" => Some(Shell::Bash),
|
"bash" => Some(Shell::Bash),
|
||||||
"sh" => Some(Shell::Sh),
|
"sh" => Some(Shell::Sh),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -169,7 +176,7 @@ impl super::Extension for ShellExtension {
|
||||||
let should_trim = if let Some(value) = trim_opt {
|
let should_trim = if let Some(value) = trim_opt {
|
||||||
let val = value.as_bool();
|
let val = value.as_bool();
|
||||||
val.unwrap_or(true)
|
val.unwrap_or(true)
|
||||||
}else{
|
} else {
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,9 @@ impl super::KeyboardManager for WindowsKeyboardManager {
|
||||||
trigger_paste();
|
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.")
|
error!("Windows backend does not support this Paste Shortcut, please open an issue on GitHub if you need it.")
|
||||||
}
|
}
|
||||||
|
|
80
src/main.rs
80
src/main.rs
|
@ -53,6 +53,7 @@ use crate::ui::UIManager;
|
||||||
|
|
||||||
mod bridge;
|
mod bridge;
|
||||||
mod check;
|
mod check;
|
||||||
|
mod cli;
|
||||||
mod clipboard;
|
mod clipboard;
|
||||||
mod config;
|
mod config;
|
||||||
mod context;
|
mod context;
|
||||||
|
@ -158,6 +159,39 @@ fn main() {
|
||||||
.subcommand(SubCommand::with_name("default")
|
.subcommand(SubCommand::with_name("default")
|
||||||
.about("Print the default configuration file path."))
|
.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
|
// Package manager
|
||||||
.subcommand(SubCommand::with_name("package")
|
.subcommand(SubCommand::with_name("package")
|
||||||
.about("Espanso package manager commands")
|
.about("Espanso package manager commands")
|
||||||
|
@ -274,6 +308,11 @@ fn main() {
|
||||||
return;
|
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("package") {
|
||||||
if let Some(matches) = matches.subcommand_matches("install") {
|
if let Some(matches) = matches.subcommand_matches("install") {
|
||||||
install_main(config_set, matches);
|
install_main(config_set, matches);
|
||||||
|
@ -480,18 +519,14 @@ fn register_signals(_: Configs) {}
|
||||||
fn register_signals(config: Configs) {
|
fn register_signals(config: Configs) {
|
||||||
// On Unix, also listen for signals so that we can terminate the
|
// On Unix, also listen for signals so that we can terminate the
|
||||||
// worker if the daemon receives a signal
|
// 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");
|
let signals = Signals::new(&[SIGTERM, SIGINT]).expect("unable to register for signals");
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("signal monitor".to_string())
|
.name("signal monitor".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
for signal in signals.forever() {
|
for signal in signals.forever() {
|
||||||
info!("Received signal: {:?}, terminating worker", signal);
|
info!("Received signal: {:?}, terminating worker", signal);
|
||||||
send_command_or_warn(
|
send_command_or_warn(Service::Worker, config, IPCCommand::exit_worker());
|
||||||
Service::Worker,
|
|
||||||
config,
|
|
||||||
IPCCommand::exit_worker(),
|
|
||||||
);
|
|
||||||
|
|
||||||
std::thread::sleep(Duration::from_millis(200));
|
std::thread::sleep(Duration::from_millis(200));
|
||||||
|
|
||||||
|
@ -1027,7 +1062,13 @@ fn install_main(_config_set: ConfigSet, matches: &ArgMatches) {
|
||||||
exit(1);
|
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());
|
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) {
|
fn edit_main(matches: &ArgMatches) {
|
||||||
// Determine which is the file to edit
|
// Determine which is the file to edit
|
||||||
let config = matches.value_of("config").unwrap_or("default");
|
let config = matches.value_of("config").unwrap_or("default");
|
||||||
|
|
|
@ -67,6 +67,7 @@ impl IPCCommand {
|
||||||
"notify" => Some(Event::System(SystemEvent::NotifyRequest(
|
"notify" => Some(Event::System(SystemEvent::NotifyRequest(
|
||||||
self.payload.clone(),
|
self.payload.clone(),
|
||||||
))),
|
))),
|
||||||
|
"trigger" => Some(Event::System(SystemEvent::Trigger(self.payload.clone()))),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,6 +102,10 @@ impl IPCCommand {
|
||||||
id: "notify".to_owned(),
|
id: "notify".to_owned(),
|
||||||
payload: message,
|
payload: message,
|
||||||
}),
|
}),
|
||||||
|
Event::System(SystemEvent::Trigger(trigger)) => Some(IPCCommand {
|
||||||
|
id: "trigger".to_owned(),
|
||||||
|
payload: trigger,
|
||||||
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,6 +130,13 @@ impl IPCCommand {
|
||||||
payload: "".to_owned(),
|
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>) {
|
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")]
|
#[cfg(target_os = "windows")]
|
||||||
pub fn get_ipc_server(
|
pub fn get_ipc_server(
|
||||||
service: Service,
|
service: Service,
|
||||||
config: Configs,
|
_: Configs,
|
||||||
event_channel: Sender<Event>,
|
event_channel: Sender<Event>,
|
||||||
) -> impl IPCServer {
|
) -> impl IPCServer {
|
||||||
windows::WindowsIPCServer::new(service, config, event_channel)
|
windows::WindowsIPCServer::new(service, event_channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub fn get_ipc_client(service: Service, config: Configs) -> impl IPCClient {
|
pub fn get_ipc_client(service: Service, _: Configs) -> impl IPCClient {
|
||||||
windows::WindowsIPCClient::new(service, config)
|
windows::WindowsIPCClient::new(service)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,32 +23,33 @@ use std::net::{TcpListener, TcpStream};
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
use crate::config::Configs;
|
use crate::config::Configs;
|
||||||
|
use crate::context;
|
||||||
use crate::event::*;
|
use crate::event::*;
|
||||||
use crate::protocol::{process_event, send_command, Service};
|
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 {
|
pub struct WindowsIPCServer {
|
||||||
service: Service,
|
service: Service,
|
||||||
config: Configs,
|
|
||||||
event_channel: Sender<Event>,
|
event_channel: Sender<Event>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_port(config: &Configs, service: &Service) -> u16 {
|
fn get_pipe_name(service: &Service) -> String {
|
||||||
let port = match service {
|
match service {
|
||||||
Service::Daemon => config.ipc_server_port,
|
Service::Daemon => DAEMON_WIN_PIPE_NAME.to_owned(),
|
||||||
Service::Worker => config.worker_ipc_server_port,
|
Service::Worker => WORKER_WIN_PIPE_NAME.to_owned(),
|
||||||
};
|
}
|
||||||
port as u16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowsIPCServer {
|
impl WindowsIPCServer {
|
||||||
pub fn new(
|
pub fn new(service: Service, event_channel: Sender<Event>) -> WindowsIPCServer {
|
||||||
service: Service,
|
|
||||||
config: Configs,
|
|
||||||
event_channel: Sender<Event>,
|
|
||||||
) -> WindowsIPCServer {
|
|
||||||
WindowsIPCServer {
|
WindowsIPCServer {
|
||||||
service,
|
service,
|
||||||
config,
|
|
||||||
event_channel,
|
event_channel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,20 +58,20 @@ impl WindowsIPCServer {
|
||||||
impl super::IPCServer for WindowsIPCServer {
|
impl super::IPCServer for WindowsIPCServer {
|
||||||
fn start(&self) {
|
fn start(&self) {
|
||||||
let event_channel = self.event_channel.clone();
|
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()
|
std::thread::Builder::new()
|
||||||
.name("ipc_server".to_string())
|
.name("ipc_server".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
let listener = TcpListener::bind(format!("127.0.0.1:{}", server_port))
|
let options = PipeOptions::new(&pipe_name);
|
||||||
.expect("Error binding to IPC server port");
|
|
||||||
|
|
||||||
info!(
|
info!("Binding to named pipe: {}", pipe_name);
|
||||||
"Binded to IPC tcp socket: {}",
|
|
||||||
listener.local_addr().unwrap().to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
for stream in listener.incoming() {
|
loop {
|
||||||
process_event(&event_channel, stream);
|
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");
|
.expect("Unable to spawn IPC server thread");
|
||||||
|
@ -79,20 +80,19 @@ impl super::IPCServer for WindowsIPCServer {
|
||||||
|
|
||||||
pub struct WindowsIPCClient {
|
pub struct WindowsIPCClient {
|
||||||
service: Service,
|
service: Service,
|
||||||
config: Configs,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowsIPCClient {
|
impl WindowsIPCClient {
|
||||||
pub fn new(service: Service, config: Configs) -> WindowsIPCClient {
|
pub fn new(service: Service) -> WindowsIPCClient {
|
||||||
WindowsIPCClient { service, config }
|
WindowsIPCClient { service }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::IPCClient for WindowsIPCClient {
|
impl super::IPCClient for WindowsIPCClient {
|
||||||
fn send_command(&self, command: IPCCommand) -> Result<(), String> {
|
fn send_command(&self, command: IPCCommand) -> Result<(), String> {
|
||||||
let port = to_port(&self.config, &self.service);
|
let pipe_name = get_pipe_name(&self.service);
|
||||||
let stream = TcpStream::connect(("127.0.0.1", port));
|
let client = PipeClient::connect_ms(pipe_name, CLIENT_TIMEOUT);
|
||||||
|
|
||||||
send_command(command, stream)
|
send_command(command, client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,11 +106,12 @@ impl MacSystemManager {
|
||||||
if let Ok(path) = string {
|
if let Ok(path) = string {
|
||||||
if !path.trim().is_empty() {
|
if !path.trim().is_empty() {
|
||||||
let process = path.trim().to_string();
|
let process = path.trim().to_string();
|
||||||
let app_name = if let Some(name) = Self::get_app_name_from_path(&process) {
|
let app_name =
|
||||||
name
|
if let Some(name) = Self::get_app_name_from_path(&process) {
|
||||||
} else {
|
name
|
||||||
process.to_owned()
|
} else {
|
||||||
};
|
process.to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
return Some((app_name, process));
|
return Some((app_name, process));
|
||||||
}
|
}
|
||||||
|
@ -138,14 +139,15 @@ impl MacSystemManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_app_name_from_path() {
|
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")
|
assert_eq!(app_name.unwrap(), "iTerm")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,4 +163,3 @@ mod tests {
|
||||||
assert_eq!(app_name.unwrap(), "SecurityAgent")
|
assert_eq!(app_name.unwrap(), "SecurityAgent")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user