commit
f436a9757d
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -572,7 +572,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "espanso"
|
||||
version = "2.0.1"
|
||||
version = "2.0.2-alpha"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"caps",
|
||||
|
@ -787,7 +787,7 @@ dependencies = [
|
|||
"test-case",
|
||||
"thiserror",
|
||||
"walkdir",
|
||||
"yaml-rust 0.4.5 (git+https://github.com/federico-terzi/yaml-rust)",
|
||||
"yaml-rust 0.4.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2399,7 +2399,7 @@ dependencies = [
|
|||
"dtoa",
|
||||
"linked-hash-map",
|
||||
"serde",
|
||||
"yaml-rust 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"yaml-rust 0.4.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3312,8 +3312,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "git+https://github.com/federico-terzi/yaml-rust#b1a195252fcdabf743f68d03f4d84d151a5a3f62"
|
||||
version = "0.4.6"
|
||||
source = "git+https://github.com/federico-terzi/yaml-rust#454221bebabc93307bbf7aa7f556407dd3027363"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
|
|
@ -13,7 +13,7 @@ regex = "1.4.3"
|
|||
lazy_static = "1.4.0"
|
||||
dunce = "1.0.1"
|
||||
walkdir = "2.3.1"
|
||||
yaml-rust = { git = "https://github.com/federico-terzi/yaml-rust" }
|
||||
yaml-rust = { version = "0.4.6", git = "https://github.com/federico-terzi/yaml-rust" }
|
||||
path-slash = "0.1.4"
|
||||
tempdir = "0.3.7"
|
||||
fs_extra = "1.2.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "espanso"
|
||||
version = "2.0.1"
|
||||
version = "2.0.2-alpha"
|
||||
authors = ["Federico Terzi <federicoterzi96@gmail.com>"]
|
||||
license = "GPL-3.0"
|
||||
description = "Cross-platform Text Expander written in Rust"
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
use anyhow::Result;
|
||||
use crossbeam::channel::Sender;
|
||||
use log::{debug, error};
|
||||
use log::{debug, error, warn};
|
||||
|
||||
const WATCHER_INTERVAL: u64 = 1000;
|
||||
|
||||
|
@ -39,23 +39,21 @@ pub fn initialize_and_spawn(watcher_notify: Sender<()>) -> Result<()> {
|
|||
}
|
||||
|
||||
fn watcher_main(watcher_notify: &Sender<()>) {
|
||||
let layout = espanso_detect::get_active_layout();
|
||||
let mut layout = espanso_detect::get_active_layout();
|
||||
|
||||
if layout.is_none() {
|
||||
error!("unable to start keyboard layout watcher, as espanso couldn't determine active layout.");
|
||||
return;
|
||||
warn!("keyboard layout watcher couldn't determine active layout.")
|
||||
}
|
||||
|
||||
let mut layout = layout.expect("missing active layout");
|
||||
|
||||
loop {
|
||||
std::thread::sleep(std::time::Duration::from_millis(WATCHER_INTERVAL));
|
||||
|
||||
if let Some(current_layout) = espanso_detect::get_active_layout() {
|
||||
let current_layout = espanso_detect::get_active_layout();
|
||||
if current_layout != layout {
|
||||
debug!(
|
||||
"detected keyboard layout change: from {} to {}",
|
||||
layout, current_layout
|
||||
"detected keyboard layout change: from '{}' to '{}'",
|
||||
layout.as_deref().unwrap_or_default(),
|
||||
current_layout.as_deref().unwrap_or_default(),
|
||||
);
|
||||
|
||||
if let Err(error) = watcher_notify.send(()) {
|
||||
|
@ -64,9 +62,5 @@ fn watcher_main(watcher_notify: &Sender<()>) {
|
|||
|
||||
layout = current_layout;
|
||||
}
|
||||
} else {
|
||||
error!("keyboard layout watcher couldn't determine active layout");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,36 +17,42 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::{
|
||||
path::Path,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use std::{path::Path, time::Duration};
|
||||
|
||||
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
|
||||
|
||||
use anyhow::Result;
|
||||
use crossbeam::channel::Sender;
|
||||
use crossbeam::{channel::Sender, select};
|
||||
use log::{error, info, warn};
|
||||
|
||||
const WATCHER_DEBOUNCE_DURATION: u64 = 1;
|
||||
const WATCHER_NOTIFY_DELAY_MS: u64 = 500;
|
||||
const WATCHER_DEBOUNCE_DURATION_MS: u64 = 1000;
|
||||
|
||||
pub fn initialize_and_spawn(config_dir: &Path, watcher_notify: Sender<()>) -> Result<()> {
|
||||
let config_dir = config_dir.to_path_buf();
|
||||
|
||||
let (debounce_tx, debounce_rx) = crossbeam::channel::unbounded();
|
||||
|
||||
std::thread::Builder::new()
|
||||
.name("watcher".to_string())
|
||||
.spawn(move || {
|
||||
watcher_main(&config_dir, &watcher_notify);
|
||||
watcher_main(&config_dir, debounce_tx);
|
||||
})?;
|
||||
|
||||
std::thread::Builder::new()
|
||||
.name("watcher-debouncer".to_string())
|
||||
.spawn(move || {
|
||||
debouncer_main(debounce_rx, &watcher_notify);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn watcher_main(config_dir: &Path, watcher_notify: &Sender<()>) {
|
||||
fn watcher_main(config_dir: &Path, debounce_tx: crossbeam::channel::Sender<()>) {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
let mut watcher: RecommendedWatcher =
|
||||
Watcher::new(tx, Duration::from_secs(WATCHER_DEBOUNCE_DURATION))
|
||||
Watcher::new(tx, Duration::from_millis(WATCHER_NOTIFY_DELAY_MS))
|
||||
.expect("unable to create file watcher");
|
||||
|
||||
watcher
|
||||
|
@ -55,8 +61,6 @@ fn watcher_main(config_dir: &Path, watcher_notify: &Sender<()>) {
|
|||
|
||||
info!("watching for changes in path: {:?}", config_dir);
|
||||
|
||||
let mut last_event_arrival = Instant::now();
|
||||
|
||||
loop {
|
||||
let should_reload = match rx.recv() {
|
||||
Ok(event) => {
|
||||
|
@ -92,16 +96,35 @@ fn watcher_main(config_dir: &Path, watcher_notify: &Sender<()>) {
|
|||
}
|
||||
};
|
||||
|
||||
// Send only one event, otherwise we could run the risk of useless reloads or even race conditions.
|
||||
if should_reload
|
||||
&& last_event_arrival.elapsed() > std::time::Duration::from_secs(WATCHER_DEBOUNCE_DURATION)
|
||||
{
|
||||
if should_reload {
|
||||
if let Err(error) = debounce_tx.send(()) {
|
||||
error!(
|
||||
"unable to send watcher file changed event to debouncer: {}",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn debouncer_main(debounce_rx: crossbeam::channel::Receiver<()>, watcher_notify: &Sender<()>) {
|
||||
let mut has_received_event = false;
|
||||
|
||||
loop {
|
||||
select! {
|
||||
recv(debounce_rx) -> _ => {
|
||||
has_received_event = true;
|
||||
},
|
||||
default(Duration::from_millis(WATCHER_DEBOUNCE_DURATION_MS)) => {
|
||||
if has_received_event {
|
||||
if let Err(error) = watcher_notify.send(()) {
|
||||
error!("unable to send watcher file changed event: {}", error);
|
||||
}
|
||||
}
|
||||
|
||||
last_event_arrival = Instant::now();
|
||||
has_received_event = false;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,7 +94,8 @@ fn launcher_main(args: CliModuleArgs) -> i32 {
|
|||
},
|
||||
});
|
||||
|
||||
let is_auto_start_page_enabled = !preferences.has_selected_auto_start_option();
|
||||
let is_auto_start_page_enabled =
|
||||
!preferences.has_selected_auto_start_option() && !cfg!(target_os = "linux");
|
||||
let preferences_clone = preferences.clone();
|
||||
let auto_start_handler = Box::new(move |auto_start| {
|
||||
preferences_clone.set_has_selected_auto_start_option(true);
|
||||
|
|
|
@ -95,6 +95,8 @@ fn service_main(args: CliModuleArgs) -> i32 {
|
|||
return start_main(&paths, &paths_overrides, sub_args);
|
||||
} else if cli_args.subcommand_matches("stop").is_some() {
|
||||
return stop_main(&paths);
|
||||
} else if cli_args.subcommand_matches("status").is_some() {
|
||||
return status_main(&paths);
|
||||
} else if let Some(sub_args) = cli_args.subcommand_matches("restart") {
|
||||
stop_main(&paths);
|
||||
return start_main(&paths, &paths_overrides, sub_args);
|
||||
|
@ -152,3 +154,15 @@ fn stop_main(paths: &Paths) -> i32 {
|
|||
|
||||
SERVICE_SUCCESS
|
||||
}
|
||||
|
||||
fn status_main(paths: &Paths) -> i32 {
|
||||
let lock_file = acquire_worker_lock(&paths.runtime);
|
||||
if lock_file.is_some() {
|
||||
error_eprintln!("espanso is not running");
|
||||
return SERVICE_NOT_RUNNING;
|
||||
}
|
||||
drop(lock_file);
|
||||
|
||||
info_println!("espanso is running");
|
||||
SERVICE_SUCCESS
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ pub const WORKER_SUCCESS: i32 = 0;
|
|||
pub const WORKER_ALREADY_RUNNING: i32 = 1;
|
||||
pub const WORKER_GENERAL_ERROR: i32 = 2;
|
||||
pub const WORKER_LEGACY_ALREADY_RUNNING: i32 = 3;
|
||||
pub const WORKER_EXIT_ALL_PROCESSES: i32 = 101;
|
||||
pub const WORKER_RESTART: i32 = 102;
|
||||
pub const WORKER_EXIT_ALL_PROCESSES: i32 = 50;
|
||||
pub const WORKER_RESTART: i32 = 51;
|
||||
|
||||
pub const DAEMON_SUCCESS: i32 = 0;
|
||||
pub const DAEMON_ALREADY_RUNNING: i32 = 1;
|
||||
|
@ -36,7 +36,7 @@ pub const MIGRATE_LEGACY_INSTANCE_RUNNING: i32 = 2;
|
|||
pub const MIGRATE_USER_ABORTED: i32 = 3;
|
||||
pub const MIGRATE_CLEAN_FAILURE: i32 = 50;
|
||||
pub const MIGRATE_DIRTY_FAILURE: i32 = 51;
|
||||
pub const MIGRATE_UNEXPECTED_FAILURE: i32 = 101;
|
||||
pub const MIGRATE_UNEXPECTED_FAILURE: i32 = 52;
|
||||
|
||||
pub const ADD_TO_PATH_SUCCESS: i32 = 0;
|
||||
pub const ADD_TO_PATH_FAILURE: i32 = 1;
|
||||
|
|
|
@ -85,6 +85,10 @@ lazy_static! {
|
|||
subcommand: "stop".to_owned(),
|
||||
forward_into: "service".to_owned(),
|
||||
},
|
||||
CliAlias {
|
||||
subcommand: "status".to_owned(),
|
||||
forward_into: "service".to_owned(),
|
||||
},
|
||||
CliAlias {
|
||||
subcommand: "install".to_owned(),
|
||||
forward_into: "package".to_owned(),
|
||||
|
@ -171,6 +175,8 @@ fn main() {
|
|||
.about("Restart the espanso service")
|
||||
.name("restart");
|
||||
let stop_subcommand = SubCommand::with_name("stop").about("Stop espanso service");
|
||||
let status_subcommand =
|
||||
SubCommand::with_name("status").about("Check if the espanso daemon is running or not.");
|
||||
|
||||
let mut clap_instance = App::new("espanso")
|
||||
.version(VERSION)
|
||||
|
@ -299,12 +305,6 @@ For example, specifying 'email' is equivalent to 'match/email.yml'."#))
|
|||
),
|
||||
),
|
||||
)
|
||||
// .subcommand(SubCommand::with_name("start")
|
||||
// .about("Start the daemon spawning a new process in the background."))
|
||||
// .subcommand(SubCommand::with_name("stop")
|
||||
// .about("Stop the espanso daemon."))
|
||||
// .subcommand(SubCommand::with_name("restart")
|
||||
// .about("Restart the espanso daemon."))
|
||||
// .subcommand(SubCommand::with_name("status")
|
||||
// .about("Check if the espanso daemon is running or not."))
|
||||
.subcommand(
|
||||
|
@ -346,11 +346,13 @@ For example, specifying 'email' is equivalent to 'match/email.yml'."#))
|
|||
.subcommand(start_subcommand.clone())
|
||||
.subcommand(restart_subcommand.clone())
|
||||
.subcommand(stop_subcommand.clone())
|
||||
.subcommand(status_subcommand.clone())
|
||||
.about("Register and manage 'espanso' as a system service."),
|
||||
)
|
||||
.subcommand(start_subcommand)
|
||||
.subcommand(restart_subcommand)
|
||||
.subcommand(stop_subcommand)
|
||||
.subcommand(status_subcommand)
|
||||
// .subcommand(SubCommand::with_name("match")
|
||||
// .about("List and execute matches from the CLI")
|
||||
// .subcommand(SubCommand::with_name("list")
|
||||
|
|
|
@ -50,6 +50,7 @@ fn get_builtin_patches() -> Vec<PatchDefinition> {
|
|||
patches::linux::simple_terminal_2_x11::patch(),
|
||||
patches::linux::terminator_terminal_x11::patch(),
|
||||
patches::linux::termite_terminal_x11::patch(),
|
||||
patches::linux::thunderbird_x11::patch(),
|
||||
patches::linux::tilix_terminal_x11::patch(),
|
||||
patches::linux::urxvt_terminal_x11::patch(),
|
||||
patches::linux::xterm_terminal_x11::patch(),
|
||||
|
|
|
@ -27,6 +27,7 @@ pub mod simple_terminal_2_x11;
|
|||
pub mod simple_terminal_x11;
|
||||
pub mod terminator_terminal_x11;
|
||||
pub mod termite_terminal_x11;
|
||||
pub mod thunderbird_x11;
|
||||
pub mod tilix_terminal_x11;
|
||||
pub mod urxvt_terminal_x11;
|
||||
pub mod xterm_terminal_x11;
|
||||
|
|
46
espanso/src/patch/patches/linux/thunderbird_x11.rs
Normal file
46
espanso/src/patch/patches/linux/thunderbird_x11.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 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 std::sync::Arc;
|
||||
|
||||
use espanso_config::config::Backend;
|
||||
|
||||
use crate::patch::patches::{PatchedConfig, Patches};
|
||||
use crate::patch::PatchDefinition;
|
||||
|
||||
pub fn patch() -> PatchDefinition {
|
||||
PatchDefinition {
|
||||
name: module_path!().split(':').last().unwrap_or("unknown"),
|
||||
is_enabled: || cfg!(target_os = "linux") && !super::util::is_wayland(),
|
||||
should_patch: |app| app.class.unwrap_or_default().contains("Thunderbird"),
|
||||
apply: |base, name| {
|
||||
Arc::new(PatchedConfig::patch(
|
||||
base,
|
||||
name,
|
||||
Patches {
|
||||
paste_shortcut: Some(Some("CTRL+SHIFT+V".to_string())),
|
||||
backend: Some(Backend::Clipboard),
|
||||
key_delay: Some(Some(15)),
|
||||
inject_delay: Some(Some(15)),
|
||||
..Default::default()
|
||||
},
|
||||
))
|
||||
},
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user