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,34 +39,28 @@ 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() {
 | 
			
		||||
      if current_layout != layout {
 | 
			
		||||
        debug!(
 | 
			
		||||
          "detected keyboard layout change: from {} to {}",
 | 
			
		||||
          layout, current_layout
 | 
			
		||||
        );
 | 
			
		||||
    let current_layout = espanso_detect::get_active_layout();
 | 
			
		||||
    if current_layout != layout {
 | 
			
		||||
      debug!(
 | 
			
		||||
        "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(()) {
 | 
			
		||||
          error!("unable to send keyboard layout changed event: {}", error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        layout = current_layout;
 | 
			
		||||
      if let Err(error) = watcher_notify.send(()) {
 | 
			
		||||
        error!("unable to send keyboard layout changed event: {}", error);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      error!("keyboard layout watcher couldn't determine active layout");
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
      layout = current_layout;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 let Err(error) = watcher_notify.send(()) {
 | 
			
		||||
        error!("unable to send watcher file changed event: {}", error);
 | 
			
		||||
    if should_reload {
 | 
			
		||||
      if let Err(error) = debounce_tx.send(()) {
 | 
			
		||||
        error!(
 | 
			
		||||
          "unable to send watcher file changed event to debouncer: {}",
 | 
			
		||||
          error
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    last_event_arrival = Instant::now();
 | 
			
		||||
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);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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