diff --git a/.github/scripts/ubuntu/Dockerfile b/.github/scripts/ubuntu/Dockerfile index 6fbba6e..b3c7eee 100644 --- a/.github/scripts/ubuntu/Dockerfile +++ b/.github/scripts/ubuntu/Dockerfile @@ -31,7 +31,7 @@ RUN set -eux; \ cargo --version; \ rustc --version; -RUN mkdir espanso && cargo install --force cargo-make +RUN mkdir espanso && cargo install --force cargo-make --version 0.34.0 COPY . espanso diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 519e97f..b881035 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: MACOSX_DEPLOYMENT_TARGET: "10.13" - name: Install cargo-make run: | - cargo install --force cargo-make + cargo install --force cargo-make --version 0.34.0 - name: Run test suite run: cargo make test-binary - name: Build @@ -60,7 +60,7 @@ jobs: cargo clippy -p espanso --features wayland -- -D warnings - name: Install cargo-make run: | - cargo install --force cargo-make + cargo install --force cargo-make --version 0.34.0 - name: Run test suite run: cargo make test-binary --env NO_X11=true - name: Build @@ -74,7 +74,7 @@ jobs: run: rustup update && rustup target add aarch64-apple-darwin - name: Install cargo-make run: | - cargo install --force cargo-make + cargo install --force cargo-make --version 0.34.0 - name: Build run: | cargo make build-macos-arm-binary diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d41c8b9..9674a1f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -61,7 +61,7 @@ jobs: echo Using version ${{ needs.extract-version.outputs.espanso_version }} - name: Install cargo-make run: | - cargo install --force cargo-make + cargo install --force cargo-make --version 0.34.0 - name: Test run: cargo make test-binary --profile release - name: Build @@ -128,7 +128,7 @@ jobs: echo Using version ${{ needs.extract-version.outputs.espanso_version }} - name: Install cargo-make run: | - cargo install --force cargo-make + cargo install --force cargo-make --version 0.34.0 - name: Test run: cargo make test-binary --profile release env: @@ -168,7 +168,7 @@ jobs: run: rustup update && rustup target add aarch64-apple-darwin - name: Install cargo-make run: | - cargo install --force cargo-make + cargo install --force cargo-make --version 0.34.0 - name: Build run: cargo make create-bundle --profile release --env BUILD_ARCH=aarch64-apple-darwin - name: Codesign executable diff --git a/Cargo.lock b/Cargo.lock index 0c06a1c..716e52c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -572,7 +572,7 @@ dependencies = [ [[package]] name = "espanso" -version = "2.0.3-alpha" +version = "2.0.4-alpha" dependencies = [ "anyhow", "caps", diff --git a/Compilation.md b/Compilation.md index 8b936ed..860ffac 100644 --- a/Compilation.md +++ b/Compilation.md @@ -16,7 +16,7 @@ These are the basic tools required to build espanso: steps. You can install it by running: ``` -cargo install --force cargo-make +cargo install --force cargo-make --version 0.34.0 ``` # Linux diff --git a/espanso-config/src/config/mod.rs b/espanso-config/src/config/mod.rs index d3d12da..dd88929 100644 --- a/espanso-config/src/config/mod.rs +++ b/espanso-config/src/config/mod.rs @@ -156,6 +156,12 @@ pub trait Config: Send + Sync { // Disabling this option might conflict with the undo feature. fn win32_exclude_orphan_events(&self) -> bool; + // The maximum interval (in milliseconds) for which a keyboard layout + // can be cached. If switching often between different layouts, you + // could lower this amount to avoid the "lost detection" effect described + // in this issue: https://github.com/federico-terzi/espanso/issues/745 + fn win32_keyboard_layout_cache_interval(&self) -> i64; + fn is_match<'a>(&self, app: &AppProperties<'a>) -> bool; fn pretty_dump(&self) -> String { @@ -187,6 +193,9 @@ pub trait Config: Send + Sync { show_notifications: {:?} secure_input_notification: {:?} + win32_exclude_orphan_events: {:?} + win32_keyboard_layout_cache_interval: {:?} + match_paths: {:#?} ", self.label(), @@ -215,6 +224,9 @@ pub trait Config: Send + Sync { self.show_notifications(), self.secure_input_notification(), + self.win32_exclude_orphan_events(), + self.win32_keyboard_layout_cache_interval(), + self.match_paths(), } } diff --git a/espanso-config/src/config/parse/mod.rs b/espanso-config/src/config/parse/mod.rs index 134afc0..bd39ad4 100644 --- a/espanso-config/src/config/parse/mod.rs +++ b/espanso-config/src/config/parse/mod.rs @@ -45,6 +45,7 @@ pub(crate) struct ParsedConfig { pub show_icon: Option, pub secure_input_notification: Option, pub win32_exclude_orphan_events: Option, + pub win32_keyboard_layout_cache_interval: Option, pub pre_paste_delay: Option, pub restore_clipboard_delay: Option, diff --git a/espanso-config/src/config/parse/yaml.rs b/espanso-config/src/config/parse/yaml.rs index 81e884d..824429c 100644 --- a/espanso-config/src/config/parse/yaml.rs +++ b/espanso-config/src/config/parse/yaml.rs @@ -109,6 +109,9 @@ pub(crate) struct YAMLConfig { #[serde(default)] pub win32_exclude_orphan_events: Option, + #[serde(default)] + pub win32_keyboard_layout_cache_interval: Option, + // Include/Exclude #[serde(default)] pub includes: Option>, @@ -197,6 +200,7 @@ impl TryFrom for ParsedConfig { paste_shortcut_event_delay: yaml_config.paste_shortcut_event_delay, win32_exclude_orphan_events: yaml_config.win32_exclude_orphan_events, + win32_keyboard_layout_cache_interval: yaml_config.win32_keyboard_layout_cache_interval, use_standard_includes: yaml_config.use_standard_includes, includes: yaml_config.includes, @@ -253,6 +257,7 @@ mod tests { show_notifications: false secure_input_notification: false win32_exclude_orphan_events: false + win32_keyboard_layout_cache_interval: 300 use_standard_includes: true includes: ["test1"] @@ -305,6 +310,7 @@ mod tests { show_notifications: Some(false), secure_input_notification: Some(false), win32_exclude_orphan_events: Some(false), + win32_keyboard_layout_cache_interval: Some(300), pre_paste_delay: Some(300), evdev_modifier_delay: Some(40), diff --git a/espanso-config/src/config/resolve.rs b/espanso-config/src/config/resolve.rs index ea74240..afcad9b 100644 --- a/espanso-config/src/config/resolve.rs +++ b/espanso-config/src/config/resolve.rs @@ -323,6 +323,13 @@ impl Config for ResolvedConfig { fn evdev_modifier_delay(&self) -> Option { self.parsed.evdev_modifier_delay } + + fn win32_keyboard_layout_cache_interval(&self) -> i64 { + self + .parsed + .win32_keyboard_layout_cache_interval + .unwrap_or(2000) + } } impl ResolvedConfig { @@ -405,6 +412,7 @@ impl ResolvedConfig { show_notifications, secure_input_notification, win32_exclude_orphan_events, + win32_keyboard_layout_cache_interval, includes, excludes, extra_includes, diff --git a/espanso-config/src/legacy/mod.rs b/espanso-config/src/legacy/mod.rs index 4efbb56..674b08a 100644 --- a/espanso-config/src/legacy/mod.rs +++ b/espanso-config/src/legacy/mod.rs @@ -394,6 +394,10 @@ impl Config for LegacyInteropConfig { fn evdev_modifier_delay(&self) -> Option { Some(10) } + + fn win32_keyboard_layout_cache_interval(&self) -> i64 { + 2000 + } } struct LegacyMatchGroup { diff --git a/espanso-detect/src/evdev/device.rs b/espanso-detect/src/evdev/device.rs index 2dd0b25..818578d 100644 --- a/espanso-detect/src/evdev/device.rs +++ b/espanso-detect/src/evdev/device.rs @@ -6,6 +6,7 @@ use libc::{input_event, size_t, ssize_t, EWOULDBLOCK, O_CLOEXEC, O_NONBLOCK, O_R use log::trace; use scopeguard::ScopeGuard; use std::collections::HashMap; +use std::os::raw::c_char; use std::os::unix::io::AsRawFd; use std::{ ffi::{c_void, CStr}, @@ -160,16 +161,16 @@ impl Device { } // Extract the utf8 char - let mut buffer: [u8; 16] = [0; 16]; + let mut buffer: [c_char; 16] = [0; 16]; unsafe { xkb_state_key_get_utf8( self.get_state(), keycode, - buffer.as_mut_ptr() as *mut i8, + buffer.as_mut_ptr(), std::mem::size_of_val(&buffer), ) }; - let content_raw = unsafe { CStr::from_ptr(buffer.as_ptr() as *mut i8) }; + let content_raw = unsafe { CStr::from_ptr(buffer.as_ptr()) }; let content = content_raw.to_string_lossy().to_string(); let event = RawKeyboardEvent { diff --git a/espanso-detect/src/lib.rs b/espanso-detect/src/lib.rs index 393b40e..6d210ed 100644 --- a/espanso-detect/src/lib.rs +++ b/espanso-detect/src/lib.rs @@ -66,6 +66,12 @@ pub struct SourceCreationOptions { // those from espanso, but might need to be disabled when using some software-level keyboards. // Disabling this option might conflict with the undo feature. pub win32_exclude_orphan_events: bool, + + // The maximum interval (in milliseconds) for which a keyboard layout + // can be cached. If switching often between different layouts, you + // could lower this amount to avoid the "lost detection" effect described + // in this issue: https://github.com/federico-terzi/espanso/issues/745 + pub win32_keyboard_layout_cache_interval: i64, } // This struct identifies the keyboard layout that @@ -87,6 +93,7 @@ impl Default for SourceCreationOptions { evdev_keyboard_rmlvo: None, hotkeys: Vec::new(), win32_exclude_orphan_events: true, + win32_keyboard_layout_cache_interval: 2000, } } } @@ -97,6 +104,7 @@ pub fn get_source(options: SourceCreationOptions) -> Result> { Ok(Box::new(win32::Win32Source::new( &options.hotkeys, options.win32_exclude_orphan_events, + options.win32_keyboard_layout_cache_interval, ))) } diff --git a/espanso-detect/src/win32/mod.rs b/espanso-detect/src/win32/mod.rs index d25e528..dbde573 100644 --- a/espanso-detect/src/win32/mod.rs +++ b/espanso-detect/src/win32/mod.rs @@ -17,6 +17,7 @@ * along with espanso. If not, see . */ +use std::os::raw::c_long; use std::{convert::TryInto, ffi::c_void}; use lazycell::LazyCell; @@ -79,10 +80,19 @@ pub struct RawHotKey { pub flags: u32, } +#[repr(C)] +pub struct InitOptions { + pub keyboard_layout_cache_interval: c_long, +} + #[allow(improper_ctypes)] #[link(name = "espansodetect", kind = "static")] extern "C" { - pub fn detect_initialize(_self: *const Win32Source, error_code: *mut i32) -> *mut c_void; + pub fn detect_initialize( + _self: *const Win32Source, + options: *const InitOptions, + error_code: *mut i32, + ) -> *mut c_void; pub fn detect_register_hotkey(window: *const c_void, hotkey: RawHotKey) -> i32; pub fn detect_eventloop( @@ -99,24 +109,35 @@ pub struct Win32Source { hotkeys: Vec, exclude_orphan_events: bool, + keyboard_layout_cache_interval: i64, } #[allow(clippy::new_without_default)] impl Win32Source { - pub fn new(hotkeys: &[HotKey], exclude_orphan_events: bool) -> Win32Source { + pub fn new( + hotkeys: &[HotKey], + exclude_orphan_events: bool, + keyboard_layout_cache_interval: i64, + ) -> Win32Source { Self { handle: std::ptr::null_mut(), callback: LazyCell::new(), hotkeys: hotkeys.to_vec(), exclude_orphan_events, + keyboard_layout_cache_interval, } } } impl Source for Win32Source { fn initialize(&mut self) -> Result<()> { + let options = InitOptions { + keyboard_layout_cache_interval: self.keyboard_layout_cache_interval.try_into().unwrap(), + }; + let mut error_code = 0; - let handle = unsafe { detect_initialize(self as *const Win32Source, &mut error_code) }; + let handle = + unsafe { detect_initialize(self as *const Win32Source, &options, &mut error_code) }; if handle.is_null() { let error = match error_code { diff --git a/espanso-detect/src/win32/native.cpp b/espanso-detect/src/win32/native.cpp index 1ac9d41..c54c396 100644 --- a/espanso-detect/src/win32/native.cpp +++ b/espanso-detect/src/win32/native.cpp @@ -39,8 +39,6 @@ #include #include -// How many milliseconds must pass between events before refreshing the keyboard layout -const long DETECT_REFRESH_KEYBOARD_LAYOUT_INTERVAL = 2000; const wchar_t *const DETECT_WINCLASS = L"EspansoDetect"; const USHORT MOUSE_DOWN_FLAGS = RI_MOUSE_LEFT_BUTTON_DOWN | RI_MOUSE_RIGHT_BUTTON_DOWN | RI_MOUSE_MIDDLE_BUTTON_DOWN | RI_MOUSE_BUTTON_1_DOWN | RI_MOUSE_BUTTON_2_DOWN | RI_MOUSE_BUTTON_3_DOWN | @@ -52,6 +50,8 @@ const USHORT MOUSE_UP_FLAGS = RI_MOUSE_LEFT_BUTTON_UP | RI_MOUSE_RIGHT_BUTTON_UP typedef struct { HKL current_keyboard_layout; DWORD last_key_press_tick; + // How many milliseconds must pass between events before refreshing the keyboard layout + long keyboard_layout_cache_interval; // Rust interop void * rust_instance; @@ -130,7 +130,7 @@ LRESULT CALLBACK detect_window_procedure(HWND window, unsigned int msg, WPARAM w DWORD currentTick = GetTickCount(); // If enough time has passed between the last keypress and now, refresh the keyboard layout - if ((currentTick - variables->last_key_press_tick) > DETECT_REFRESH_KEYBOARD_LAYOUT_INTERVAL) + if ((currentTick - variables->last_key_press_tick) > variables->keyboard_layout_cache_interval) { // Because keyboard layouts on windows are Window-specific, to get the current @@ -269,7 +269,7 @@ LRESULT CALLBACK detect_window_procedure(HWND window, unsigned int msg, WPARAM w } } -void * detect_initialize(void *_self, int32_t *error_code) +void * detect_initialize(void *_self, InitOptions * options, int32_t *error_code) { HWND window = NULL; @@ -297,6 +297,7 @@ void * detect_initialize(void *_self, int32_t *error_code) // Initialize the default keyboard layout variables->current_keyboard_layout = GetKeyboardLayout(0); + variables->keyboard_layout_cache_interval = options->keyboard_layout_cache_interval; // Docs: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw window = CreateWindowEx( diff --git a/espanso-detect/src/win32/native.h b/espanso-detect/src/win32/native.h index a93b9f7..7e187d3 100644 --- a/espanso-detect/src/win32/native.h +++ b/espanso-detect/src/win32/native.h @@ -76,9 +76,12 @@ typedef struct { typedef void (*EventCallback)(void * rust_istance, InputEvent data); +typedef struct { + long keyboard_layout_cache_interval; +} InitOptions; // Initialize the Raw Input API and the Window. -extern "C" void * detect_initialize(void * rust_istance, int32_t *error_code); +extern "C" void * detect_initialize(void * rust_istance, InitOptions * options, int32_t *error_code); // Register the given hotkey, return a non-zero code if successful extern "C" int32_t detect_register_hotkey(void * window, HotKey hotkey); diff --git a/espanso-engine/src/process/middleware/disable.rs b/espanso-engine/src/process/middleware/disable.rs index 77bd0e8..5b7e633 100644 --- a/espanso-engine/src/process/middleware/disable.rs +++ b/espanso-engine/src/process/middleware/disable.rs @@ -64,18 +64,24 @@ impl Middleware for DisableMiddleware { match &event.etype { EventType::Keyboard(m_event) => { - if is_toggle_key(m_event, &self.options) { + if m_event.status == Status::Released { let mut last_toggle_press = self.last_toggle_press.borrow_mut(); - if let Some(previous_press) = *last_toggle_press { - if previous_press.elapsed() < self.options.toggle_key_maximum_window { - *enabled = !*enabled; - *last_toggle_press = None; - has_status_changed = true; + if is_toggle_key(m_event, &self.options) { + if let Some(previous_press) = *last_toggle_press { + if previous_press.elapsed() < self.options.toggle_key_maximum_window { + *enabled = !*enabled; + *last_toggle_press = None; + has_status_changed = true; + } else { + *last_toggle_press = Some(Instant::now()); + } } else { *last_toggle_press = Some(Instant::now()); } } else { - *last_toggle_press = Some(Instant::now()); + // If another key is pressed (not the toggle key), we should reset the window + // For more information, see: https://github.com/federico-terzi/espanso/issues/815 + *last_toggle_press = None; } } } @@ -115,10 +121,6 @@ impl Middleware for DisableMiddleware { } fn is_toggle_key(event: &KeyboardEvent, options: &DisableOptions) -> bool { - if event.status != Status::Released { - return false; - } - if options .toggle_key .as_ref() diff --git a/espanso-engine/src/process/middleware/markdown.rs b/espanso-engine/src/process/middleware/markdown.rs index abebc54..7deec85 100644 --- a/espanso-engine/src/process/middleware/markdown.rs +++ b/espanso-engine/src/process/middleware/markdown.rs @@ -17,6 +17,8 @@ * along with espanso. If not, see . */ +use log::error; + use super::super::Middleware; use crate::event::{effect::HtmlInjectRequest, Event, EventType}; @@ -37,23 +39,33 @@ impl Middleware for MarkdownMiddleware { fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event { if let EventType::MarkdownInject(m_event) = &event.etype { // Render the markdown into HTML - let html = markdown::to_html(&m_event.markdown); - let mut html = html.trim(); + // NOTE: we wrap the `to_html` call between catch_unwind because if the markdown is malformed, + // the library panics. Ideally, the library would return a Result::Err in that case, but + // for now it doesn't, so we employ that workaround. + // See also: https://github.com/federico-terzi/espanso/issues/759 + let html = std::panic::catch_unwind(|| markdown::to_html(&m_event.markdown)); + if let Ok(html) = html { + let mut html = html.trim(); - // Remove the surrounding paragraph - if html.starts_with("

") { - html = html.trim_start_matches("

"); - } - if html.ends_with("

") { - html = html.trim_end_matches("

"); - } + // Remove the surrounding paragraph + if html.starts_with("

") { + html = html.trim_start_matches("

"); + } + if html.ends_with("

") { + html = html.trim_end_matches("

"); + } - return Event::caused_by( - event.source_id, - EventType::HtmlInject(HtmlInjectRequest { - html: html.to_owned(), - }), - ); + return Event::caused_by( + event.source_id, + EventType::HtmlInject(HtmlInjectRequest { + html: html.to_owned(), + }), + ); + } else { + error!("unable to convert markdown to HTML, is it malformed?"); + + return Event::caused_by(event.source_id, EventType::NOOP); + } } event diff --git a/espanso-engine/src/process/middleware/matcher.rs b/espanso-engine/src/process/middleware/matcher.rs index 92f49ea..d16276d 100644 --- a/espanso-engine/src/process/middleware/matcher.rs +++ b/espanso-engine/src/process/middleware/matcher.rs @@ -191,7 +191,7 @@ fn is_event_of_interest(event_type: &EventType) -> bool { // In hex, they have the byte 3 = 0xfe // See list in "keysymdef.h" file if cfg!(target_os = "linux") { - if let Key::Other(raw_code) = &keyboard_event.key { + if let (Key::Other(raw_code), None) = (&keyboard_event.key, &keyboard_event.value) { if (65025..=65276).contains(raw_code) { return false; } diff --git a/espanso-modulo/build.rs b/espanso-modulo/build.rs index 31646ca..86bc21d 100644 --- a/espanso-modulo/build.rs +++ b/espanso-modulo/build.rs @@ -457,22 +457,5 @@ fn build_native() { } fn main() { - println!("cargo:rerun-if-changed=src/x11/native/native.h"); - println!("cargo:rerun-if-changed=src/sys/interop/interop.h"); - println!("cargo:rerun-if-changed=src/sys/form/form.cpp"); - println!("cargo:rerun-if-changed=src/sys/common/mac.h"); - println!("cargo:rerun-if-changed=src/sys/common/mac.mm"); - println!("cargo:rerun-if-changed=src/sys/common/common.h"); - println!("cargo:rerun-if-changed=src/sys/common/common.cpp"); - println!("cargo:rerun-if-changed=src/sys/welcome/welcome_gui.h"); - println!("cargo:rerun-if-changed=src/sys/welcome/welcome_gui.cpp"); - println!("cargo:rerun-if-changed=src/sys/welcome/welcome.cpp"); - println!("cargo:rerun-if-changed=src/sys/troubleshooting/troubleshooting_gui.h"); - println!("cargo:rerun-if-changed=src/sys/troubleshooting/troubleshooting_gui.cpp"); - println!("cargo:rerun-if-changed=src/sys/troubleshooting/troubleshooting.cpp"); - println!("cargo:rerun-if-changed=src/sys/search/search.cpp"); - println!("cargo:rerun-if-changed=src/sys/wizard/wizard.cpp"); - println!("cargo:rerun-if-changed=src/sys/wizard/wizard_gui.cpp"); - println!("cargo:rerun-if-changed=src/sys/wizard/wizard_gui.h"); build_native(); } diff --git a/espanso/Cargo.toml b/espanso/Cargo.toml index 60cd2cf..a31cae3 100644 --- a/espanso/Cargo.toml +++ b/espanso/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "espanso" -version = "2.0.3-alpha" +version = "2.0.4-alpha" authors = ["Federico Terzi "] license = "GPL-3.0" description = "Cross-platform Text Expander written in Rust" diff --git a/espanso/src/cli/worker/engine/mod.rs b/espanso/src/cli/worker/engine/mod.rs index 249db13..f68309a 100644 --- a/espanso/src/cli/worker/engine/mod.rs +++ b/espanso/src/cli/worker/engine/mod.rs @@ -126,6 +126,8 @@ pub fn initialize_and_spawn( ), hotkeys: match_converter.get_hotkeys(), win32_exclude_orphan_events: default_config.win32_exclude_orphan_events(), + win32_keyboard_layout_cache_interval: default_config + .win32_keyboard_layout_cache_interval(), }) .expect("failed to initialize detector module"); let exit_source = super::engine::funnel::exit::ExitSource::new(exit_signal, &sequencer); diff --git a/espanso/src/cli/worker/engine/process/middleware/render/extension/form.rs b/espanso/src/cli/worker/engine/process/middleware/render/extension/form.rs index dd9fbe7..fb0510b 100644 --- a/espanso/src/cli/worker/engine/process/middleware/render/extension/form.rs +++ b/espanso/src/cli/worker/engine/process/middleware/render/extension/form.rs @@ -55,55 +55,53 @@ fn convert_fields(fields: &Params) -> HashMap { let mut form_field = None; if let Value::Object(params) = field { - if let Some(Value::String(field_type)) = params.get("type") { - form_field = match field_type.as_str() { - "text" => Some(FormField::Text { - default: params - .get("default") - .and_then(|val| val.as_string()) - .cloned(), - multiline: params - .get("multiline") - .and_then(|val| val.as_bool()) - .cloned() - .unwrap_or(false), - }), - "choice" => Some(FormField::Choice { - default: params - .get("default") - .and_then(|val| val.as_string()) - .cloned(), - values: params - .get("values") - .and_then(|val| val.as_array()) - .map(|arr| { - arr - .iter() - .flat_map(|choice| choice.as_string()) - .cloned() - .collect() - }) - .unwrap_or_default(), - }), - "list" => Some(FormField::List { - default: params - .get("default") - .and_then(|val| val.as_string()) - .cloned(), - values: params - .get("values") - .and_then(|val| val.as_array()) - .map(|arr| { - arr - .iter() - .flat_map(|choice| choice.as_string()) - .cloned() - .collect() - }) - .unwrap_or_default(), - }), - _ => None, - } + form_field = match params.get("type") { + Some(Value::String(field_type)) if field_type == "choice" => Some(FormField::Choice { + default: params + .get("default") + .and_then(|val| val.as_string()) + .cloned(), + values: params + .get("values") + .and_then(|val| val.as_array()) + .map(|arr| { + arr + .iter() + .flat_map(|choice| choice.as_string()) + .cloned() + .collect() + }) + .unwrap_or_default(), + }), + Some(Value::String(field_type)) if field_type == "list" => Some(FormField::List { + default: params + .get("default") + .and_then(|val| val.as_string()) + .cloned(), + values: params + .get("values") + .and_then(|val| val.as_array()) + .map(|arr| { + arr + .iter() + .flat_map(|choice| choice.as_string()) + .cloned() + .collect() + }) + .unwrap_or_default(), + }), + // By default, it's considered type 'text' + _ => Some(FormField::Text { + default: params + .get("default") + .and_then(|val| val.as_string()) + .cloned(), + multiline: params + .get("multiline") + .and_then(|val| val.as_bool()) + .cloned() + .unwrap_or(false), + }), } } diff --git a/espanso/src/patch/mod.rs b/espanso/src/patch/mod.rs index e499181..f1ce634 100644 --- a/espanso/src/patch/mod.rs +++ b/espanso/src/patch/mod.rs @@ -55,6 +55,7 @@ fn get_builtin_patches() -> Vec { patches::linux::urxvt_terminal_x11::patch(), patches::linux::xterm_terminal_x11::patch(), patches::linux::yakuake_terminal_x11::patch(), + patches::linux::virtualbox_x11::patch(), ]; } diff --git a/espanso/src/patch/patches/linux/alacritty_terminal_x11.rs b/espanso/src/patch/patches/linux/alacritty_terminal_x11.rs index f02ea34..039d15c 100644 --- a/espanso/src/patch/patches/linux/alacritty_terminal_x11.rs +++ b/espanso/src/patch/patches/linux/alacritty_terminal_x11.rs @@ -19,6 +19,8 @@ use std::sync::Arc; +use espanso_config::config::Backend; + use crate::patch::patches::{PatchedConfig, Patches}; use crate::patch::PatchDefinition; @@ -33,6 +35,7 @@ pub fn patch() -> PatchDefinition { name, Patches { paste_shortcut: Some(Some("CTRL+SHIFT+V".to_string())), + backend: Some(Backend::Clipboard), ..Default::default() }, )) diff --git a/espanso/src/patch/patches/linux/mod.rs b/espanso/src/patch/patches/linux/mod.rs index d145f16..3d42011 100644 --- a/espanso/src/patch/patches/linux/mod.rs +++ b/espanso/src/patch/patches/linux/mod.rs @@ -30,6 +30,7 @@ pub mod termite_terminal_x11; pub mod thunderbird_x11; pub mod tilix_terminal_x11; pub mod urxvt_terminal_x11; +pub mod virtualbox_x11; pub mod xterm_terminal_x11; pub mod yakuake_terminal_x11; diff --git a/espanso/src/patch/patches/linux/virtualbox_x11.rs b/espanso/src/patch/patches/linux/virtualbox_x11.rs new file mode 100644 index 0000000..9db735f --- /dev/null +++ b/espanso/src/patch/patches/linux/virtualbox_x11.rs @@ -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 . + */ + +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("VirtualBox Machine"), + apply: |base, name| { + Arc::new(PatchedConfig::patch( + base, + name, + Patches { + backend: Some(Backend::Inject), + key_delay: Some(Some(10)), + inject_delay: Some(Some(15)), + disable_x11_fast_inject: Some(true), + ..Default::default() + }, + )) + }, + } +} diff --git a/espanso/src/patch/patches/mod.rs b/espanso/src/patch/patches/mod.rs index 5fddc89..c6dd4f7 100644 --- a/espanso/src/patch/patches/mod.rs +++ b/espanso/src/patch/patches/mod.rs @@ -49,5 +49,6 @@ generate_patchable_config!( apply_patch -> bool, undo_backspace -> bool, win32_exclude_orphan_events -> bool, + win32_keyboard_layout_cache_interval -> i64, keyboard_layout -> Option ); diff --git a/snapcraft.yaml b/snapcraft.yaml index 51f22b0..92a5f11 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -1,5 +1,5 @@ name: espanso -version: 2.0.3-alpha +version: 2.0.4-alpha summary: A Cross-platform Text Expander written in Rust description: | espanso is a Cross-platform, Text Expander written in Rust.