commit
4bd5f4c6c5
2
.github/scripts/ubuntu/Dockerfile
vendored
2
.github/scripts/ubuntu/Dockerfile
vendored
|
@ -31,7 +31,7 @@ RUN set -eux; \
|
||||||
cargo --version; \
|
cargo --version; \
|
||||||
rustc --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
|
COPY . espanso
|
||||||
|
|
||||||
|
|
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
@ -35,7 +35,7 @@ jobs:
|
||||||
MACOSX_DEPLOYMENT_TARGET: "10.13"
|
MACOSX_DEPLOYMENT_TARGET: "10.13"
|
||||||
- name: Install cargo-make
|
- name: Install cargo-make
|
||||||
run: |
|
run: |
|
||||||
cargo install --force cargo-make
|
cargo install --force cargo-make --version 0.34.0
|
||||||
- name: Run test suite
|
- name: Run test suite
|
||||||
run: cargo make test-binary
|
run: cargo make test-binary
|
||||||
- name: Build
|
- name: Build
|
||||||
|
@ -60,7 +60,7 @@ jobs:
|
||||||
cargo clippy -p espanso --features wayland -- -D warnings
|
cargo clippy -p espanso --features wayland -- -D warnings
|
||||||
- name: Install cargo-make
|
- name: Install cargo-make
|
||||||
run: |
|
run: |
|
||||||
cargo install --force cargo-make
|
cargo install --force cargo-make --version 0.34.0
|
||||||
- name: Run test suite
|
- name: Run test suite
|
||||||
run: cargo make test-binary --env NO_X11=true
|
run: cargo make test-binary --env NO_X11=true
|
||||||
- name: Build
|
- name: Build
|
||||||
|
@ -74,7 +74,7 @@ jobs:
|
||||||
run: rustup update && rustup target add aarch64-apple-darwin
|
run: rustup update && rustup target add aarch64-apple-darwin
|
||||||
- name: Install cargo-make
|
- name: Install cargo-make
|
||||||
run: |
|
run: |
|
||||||
cargo install --force cargo-make
|
cargo install --force cargo-make --version 0.34.0
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cargo make build-macos-arm-binary
|
cargo make build-macos-arm-binary
|
||||||
|
|
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
|
@ -61,7 +61,7 @@ jobs:
|
||||||
echo Using version ${{ needs.extract-version.outputs.espanso_version }}
|
echo Using version ${{ needs.extract-version.outputs.espanso_version }}
|
||||||
- name: Install cargo-make
|
- name: Install cargo-make
|
||||||
run: |
|
run: |
|
||||||
cargo install --force cargo-make
|
cargo install --force cargo-make --version 0.34.0
|
||||||
- name: Test
|
- name: Test
|
||||||
run: cargo make test-binary --profile release
|
run: cargo make test-binary --profile release
|
||||||
- name: Build
|
- name: Build
|
||||||
|
@ -128,7 +128,7 @@ jobs:
|
||||||
echo Using version ${{ needs.extract-version.outputs.espanso_version }}
|
echo Using version ${{ needs.extract-version.outputs.espanso_version }}
|
||||||
- name: Install cargo-make
|
- name: Install cargo-make
|
||||||
run: |
|
run: |
|
||||||
cargo install --force cargo-make
|
cargo install --force cargo-make --version 0.34.0
|
||||||
- name: Test
|
- name: Test
|
||||||
run: cargo make test-binary --profile release
|
run: cargo make test-binary --profile release
|
||||||
env:
|
env:
|
||||||
|
@ -168,7 +168,7 @@ jobs:
|
||||||
run: rustup update && rustup target add aarch64-apple-darwin
|
run: rustup update && rustup target add aarch64-apple-darwin
|
||||||
- name: Install cargo-make
|
- name: Install cargo-make
|
||||||
run: |
|
run: |
|
||||||
cargo install --force cargo-make
|
cargo install --force cargo-make --version 0.34.0
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo make create-bundle --profile release --env BUILD_ARCH=aarch64-apple-darwin
|
run: cargo make create-bundle --profile release --env BUILD_ARCH=aarch64-apple-darwin
|
||||||
- name: Codesign executable
|
- name: Codesign executable
|
||||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -572,7 +572,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "espanso"
|
name = "espanso"
|
||||||
version = "2.0.3-alpha"
|
version = "2.0.4-alpha"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"caps",
|
"caps",
|
||||||
|
|
|
@ -16,7 +16,7 @@ These are the basic tools required to build espanso:
|
||||||
steps. You can install it by running:
|
steps. You can install it by running:
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo install --force cargo-make
|
cargo install --force cargo-make --version 0.34.0
|
||||||
```
|
```
|
||||||
|
|
||||||
# Linux
|
# Linux
|
||||||
|
|
|
@ -156,6 +156,12 @@ pub trait Config: Send + Sync {
|
||||||
// Disabling this option might conflict with the undo feature.
|
// Disabling this option might conflict with the undo feature.
|
||||||
fn win32_exclude_orphan_events(&self) -> bool;
|
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 is_match<'a>(&self, app: &AppProperties<'a>) -> bool;
|
||||||
|
|
||||||
fn pretty_dump(&self) -> String {
|
fn pretty_dump(&self) -> String {
|
||||||
|
@ -187,6 +193,9 @@ pub trait Config: Send + Sync {
|
||||||
show_notifications: {:?}
|
show_notifications: {:?}
|
||||||
secure_input_notification: {:?}
|
secure_input_notification: {:?}
|
||||||
|
|
||||||
|
win32_exclude_orphan_events: {:?}
|
||||||
|
win32_keyboard_layout_cache_interval: {:?}
|
||||||
|
|
||||||
match_paths: {:#?}
|
match_paths: {:#?}
|
||||||
",
|
",
|
||||||
self.label(),
|
self.label(),
|
||||||
|
@ -215,6 +224,9 @@ pub trait Config: Send + Sync {
|
||||||
self.show_notifications(),
|
self.show_notifications(),
|
||||||
self.secure_input_notification(),
|
self.secure_input_notification(),
|
||||||
|
|
||||||
|
self.win32_exclude_orphan_events(),
|
||||||
|
self.win32_keyboard_layout_cache_interval(),
|
||||||
|
|
||||||
self.match_paths(),
|
self.match_paths(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ pub(crate) struct ParsedConfig {
|
||||||
pub show_icon: Option<bool>,
|
pub show_icon: Option<bool>,
|
||||||
pub secure_input_notification: Option<bool>,
|
pub secure_input_notification: Option<bool>,
|
||||||
pub win32_exclude_orphan_events: Option<bool>,
|
pub win32_exclude_orphan_events: Option<bool>,
|
||||||
|
pub win32_keyboard_layout_cache_interval: Option<i64>,
|
||||||
|
|
||||||
pub pre_paste_delay: Option<usize>,
|
pub pre_paste_delay: Option<usize>,
|
||||||
pub restore_clipboard_delay: Option<usize>,
|
pub restore_clipboard_delay: Option<usize>,
|
||||||
|
|
|
@ -109,6 +109,9 @@ pub(crate) struct YAMLConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub win32_exclude_orphan_events: Option<bool>,
|
pub win32_exclude_orphan_events: Option<bool>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
pub win32_keyboard_layout_cache_interval: Option<i64>,
|
||||||
|
|
||||||
// Include/Exclude
|
// Include/Exclude
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub includes: Option<Vec<String>>,
|
pub includes: Option<Vec<String>>,
|
||||||
|
@ -197,6 +200,7 @@ impl TryFrom<YAMLConfig> for ParsedConfig {
|
||||||
paste_shortcut_event_delay: yaml_config.paste_shortcut_event_delay,
|
paste_shortcut_event_delay: yaml_config.paste_shortcut_event_delay,
|
||||||
|
|
||||||
win32_exclude_orphan_events: yaml_config.win32_exclude_orphan_events,
|
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,
|
use_standard_includes: yaml_config.use_standard_includes,
|
||||||
includes: yaml_config.includes,
|
includes: yaml_config.includes,
|
||||||
|
@ -253,6 +257,7 @@ mod tests {
|
||||||
show_notifications: false
|
show_notifications: false
|
||||||
secure_input_notification: false
|
secure_input_notification: false
|
||||||
win32_exclude_orphan_events: false
|
win32_exclude_orphan_events: false
|
||||||
|
win32_keyboard_layout_cache_interval: 300
|
||||||
|
|
||||||
use_standard_includes: true
|
use_standard_includes: true
|
||||||
includes: ["test1"]
|
includes: ["test1"]
|
||||||
|
@ -305,6 +310,7 @@ mod tests {
|
||||||
show_notifications: Some(false),
|
show_notifications: Some(false),
|
||||||
secure_input_notification: Some(false),
|
secure_input_notification: Some(false),
|
||||||
win32_exclude_orphan_events: Some(false),
|
win32_exclude_orphan_events: Some(false),
|
||||||
|
win32_keyboard_layout_cache_interval: Some(300),
|
||||||
|
|
||||||
pre_paste_delay: Some(300),
|
pre_paste_delay: Some(300),
|
||||||
evdev_modifier_delay: Some(40),
|
evdev_modifier_delay: Some(40),
|
||||||
|
|
|
@ -323,6 +323,13 @@ impl Config for ResolvedConfig {
|
||||||
fn evdev_modifier_delay(&self) -> Option<usize> {
|
fn evdev_modifier_delay(&self) -> Option<usize> {
|
||||||
self.parsed.evdev_modifier_delay
|
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 {
|
impl ResolvedConfig {
|
||||||
|
@ -405,6 +412,7 @@ impl ResolvedConfig {
|
||||||
show_notifications,
|
show_notifications,
|
||||||
secure_input_notification,
|
secure_input_notification,
|
||||||
win32_exclude_orphan_events,
|
win32_exclude_orphan_events,
|
||||||
|
win32_keyboard_layout_cache_interval,
|
||||||
includes,
|
includes,
|
||||||
excludes,
|
excludes,
|
||||||
extra_includes,
|
extra_includes,
|
||||||
|
|
|
@ -394,6 +394,10 @@ impl Config for LegacyInteropConfig {
|
||||||
fn evdev_modifier_delay(&self) -> Option<usize> {
|
fn evdev_modifier_delay(&self) -> Option<usize> {
|
||||||
Some(10)
|
Some(10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn win32_keyboard_layout_cache_interval(&self) -> i64 {
|
||||||
|
2000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LegacyMatchGroup {
|
struct LegacyMatchGroup {
|
||||||
|
|
|
@ -6,6 +6,7 @@ use libc::{input_event, size_t, ssize_t, EWOULDBLOCK, O_CLOEXEC, O_NONBLOCK, O_R
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use scopeguard::ScopeGuard;
|
use scopeguard::ScopeGuard;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::os::raw::c_char;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::{
|
use std::{
|
||||||
ffi::{c_void, CStr},
|
ffi::{c_void, CStr},
|
||||||
|
@ -160,16 +161,16 @@ impl Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the utf8 char
|
// Extract the utf8 char
|
||||||
let mut buffer: [u8; 16] = [0; 16];
|
let mut buffer: [c_char; 16] = [0; 16];
|
||||||
unsafe {
|
unsafe {
|
||||||
xkb_state_key_get_utf8(
|
xkb_state_key_get_utf8(
|
||||||
self.get_state(),
|
self.get_state(),
|
||||||
keycode,
|
keycode,
|
||||||
buffer.as_mut_ptr() as *mut i8,
|
buffer.as_mut_ptr(),
|
||||||
std::mem::size_of_val(&buffer),
|
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 content = content_raw.to_string_lossy().to_string();
|
||||||
|
|
||||||
let event = RawKeyboardEvent {
|
let event = RawKeyboardEvent {
|
||||||
|
|
|
@ -66,6 +66,12 @@ pub struct SourceCreationOptions {
|
||||||
// those from espanso, but might need to be disabled when using some software-level keyboards.
|
// those from espanso, but might need to be disabled when using some software-level keyboards.
|
||||||
// Disabling this option might conflict with the undo feature.
|
// Disabling this option might conflict with the undo feature.
|
||||||
pub win32_exclude_orphan_events: bool,
|
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
|
// This struct identifies the keyboard layout that
|
||||||
|
@ -87,6 +93,7 @@ impl Default for SourceCreationOptions {
|
||||||
evdev_keyboard_rmlvo: None,
|
evdev_keyboard_rmlvo: None,
|
||||||
hotkeys: Vec::new(),
|
hotkeys: Vec::new(),
|
||||||
win32_exclude_orphan_events: true,
|
win32_exclude_orphan_events: true,
|
||||||
|
win32_keyboard_layout_cache_interval: 2000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,6 +104,7 @@ pub fn get_source(options: SourceCreationOptions) -> Result<Box<dyn Source>> {
|
||||||
Ok(Box::new(win32::Win32Source::new(
|
Ok(Box::new(win32::Win32Source::new(
|
||||||
&options.hotkeys,
|
&options.hotkeys,
|
||||||
options.win32_exclude_orphan_events,
|
options.win32_exclude_orphan_events,
|
||||||
|
options.win32_keyboard_layout_cache_interval,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use std::os::raw::c_long;
|
||||||
use std::{convert::TryInto, ffi::c_void};
|
use std::{convert::TryInto, ffi::c_void};
|
||||||
|
|
||||||
use lazycell::LazyCell;
|
use lazycell::LazyCell;
|
||||||
|
@ -79,10 +80,19 @@ pub struct RawHotKey {
|
||||||
pub flags: u32,
|
pub flags: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct InitOptions {
|
||||||
|
pub keyboard_layout_cache_interval: c_long,
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(improper_ctypes)]
|
#[allow(improper_ctypes)]
|
||||||
#[link(name = "espansodetect", kind = "static")]
|
#[link(name = "espansodetect", kind = "static")]
|
||||||
extern "C" {
|
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_register_hotkey(window: *const c_void, hotkey: RawHotKey) -> i32;
|
||||||
|
|
||||||
pub fn detect_eventloop(
|
pub fn detect_eventloop(
|
||||||
|
@ -99,24 +109,35 @@ pub struct Win32Source {
|
||||||
hotkeys: Vec<HotKey>,
|
hotkeys: Vec<HotKey>,
|
||||||
|
|
||||||
exclude_orphan_events: bool,
|
exclude_orphan_events: bool,
|
||||||
|
keyboard_layout_cache_interval: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_without_default)]
|
#[allow(clippy::new_without_default)]
|
||||||
impl Win32Source {
|
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 {
|
Self {
|
||||||
handle: std::ptr::null_mut(),
|
handle: std::ptr::null_mut(),
|
||||||
callback: LazyCell::new(),
|
callback: LazyCell::new(),
|
||||||
hotkeys: hotkeys.to_vec(),
|
hotkeys: hotkeys.to_vec(),
|
||||||
exclude_orphan_events,
|
exclude_orphan_events,
|
||||||
|
keyboard_layout_cache_interval,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source for Win32Source {
|
impl Source for Win32Source {
|
||||||
fn initialize(&mut self) -> Result<()> {
|
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 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() {
|
if handle.is_null() {
|
||||||
let error = match error_code {
|
let error = match error_code {
|
||||||
|
|
|
@ -39,8 +39,6 @@
|
||||||
#include <strsafe.h>
|
#include <strsafe.h>
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
// 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 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 |
|
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 |
|
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 {
|
typedef struct {
|
||||||
HKL current_keyboard_layout;
|
HKL current_keyboard_layout;
|
||||||
DWORD last_key_press_tick;
|
DWORD last_key_press_tick;
|
||||||
|
// How many milliseconds must pass between events before refreshing the keyboard layout
|
||||||
|
long keyboard_layout_cache_interval;
|
||||||
|
|
||||||
// Rust interop
|
// Rust interop
|
||||||
void * rust_instance;
|
void * rust_instance;
|
||||||
|
@ -130,7 +130,7 @@ LRESULT CALLBACK detect_window_procedure(HWND window, unsigned int msg, WPARAM w
|
||||||
DWORD currentTick = GetTickCount();
|
DWORD currentTick = GetTickCount();
|
||||||
|
|
||||||
// If enough time has passed between the last keypress and now, refresh the keyboard layout
|
// 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
|
// 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;
|
HWND window = NULL;
|
||||||
|
|
||||||
|
@ -297,6 +297,7 @@ void * detect_initialize(void *_self, int32_t *error_code)
|
||||||
|
|
||||||
// Initialize the default keyboard layout
|
// Initialize the default keyboard layout
|
||||||
variables->current_keyboard_layout = GetKeyboardLayout(0);
|
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
|
// Docs: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw
|
||||||
window = CreateWindowEx(
|
window = CreateWindowEx(
|
||||||
|
|
|
@ -76,9 +76,12 @@ typedef struct {
|
||||||
|
|
||||||
typedef void (*EventCallback)(void * rust_istance, InputEvent data);
|
typedef void (*EventCallback)(void * rust_istance, InputEvent data);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
long keyboard_layout_cache_interval;
|
||||||
|
} InitOptions;
|
||||||
|
|
||||||
// Initialize the Raw Input API and the Window.
|
// 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
|
// Register the given hotkey, return a non-zero code if successful
|
||||||
extern "C" int32_t detect_register_hotkey(void * window, HotKey hotkey);
|
extern "C" int32_t detect_register_hotkey(void * window, HotKey hotkey);
|
||||||
|
|
|
@ -64,8 +64,9 @@ impl Middleware for DisableMiddleware {
|
||||||
|
|
||||||
match &event.etype {
|
match &event.etype {
|
||||||
EventType::Keyboard(m_event) => {
|
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();
|
let mut last_toggle_press = self.last_toggle_press.borrow_mut();
|
||||||
|
if is_toggle_key(m_event, &self.options) {
|
||||||
if let Some(previous_press) = *last_toggle_press {
|
if let Some(previous_press) = *last_toggle_press {
|
||||||
if previous_press.elapsed() < self.options.toggle_key_maximum_window {
|
if previous_press.elapsed() < self.options.toggle_key_maximum_window {
|
||||||
*enabled = !*enabled;
|
*enabled = !*enabled;
|
||||||
|
@ -77,6 +78,11 @@ impl Middleware for DisableMiddleware {
|
||||||
} else {
|
} else {
|
||||||
*last_toggle_press = Some(Instant::now());
|
*last_toggle_press = Some(Instant::now());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EventType::EnableRequest => {
|
EventType::EnableRequest => {
|
||||||
|
@ -115,10 +121,6 @@ impl Middleware for DisableMiddleware {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_toggle_key(event: &KeyboardEvent, options: &DisableOptions) -> bool {
|
fn is_toggle_key(event: &KeyboardEvent, options: &DisableOptions) -> bool {
|
||||||
if event.status != Status::Released {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if options
|
if options
|
||||||
.toggle_key
|
.toggle_key
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use log::error;
|
||||||
|
|
||||||
use super::super::Middleware;
|
use super::super::Middleware;
|
||||||
use crate::event::{effect::HtmlInjectRequest, Event, EventType};
|
use crate::event::{effect::HtmlInjectRequest, Event, EventType};
|
||||||
|
|
||||||
|
@ -37,7 +39,12 @@ impl Middleware for MarkdownMiddleware {
|
||||||
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||||
if let EventType::MarkdownInject(m_event) = &event.etype {
|
if let EventType::MarkdownInject(m_event) = &event.etype {
|
||||||
// Render the markdown into HTML
|
// Render the markdown into HTML
|
||||||
let html = markdown::to_html(&m_event.markdown);
|
// 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();
|
let mut html = html.trim();
|
||||||
|
|
||||||
// Remove the surrounding paragraph
|
// Remove the surrounding paragraph
|
||||||
|
@ -54,6 +61,11 @@ impl Middleware for MarkdownMiddleware {
|
||||||
html: html.to_owned(),
|
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
|
event
|
||||||
|
|
|
@ -191,7 +191,7 @@ fn is_event_of_interest(event_type: &EventType) -> bool {
|
||||||
// In hex, they have the byte 3 = 0xfe
|
// In hex, they have the byte 3 = 0xfe
|
||||||
// See list in "keysymdef.h" file
|
// See list in "keysymdef.h" file
|
||||||
if cfg!(target_os = "linux") {
|
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) {
|
if (65025..=65276).contains(raw_code) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -457,22 +457,5 @@ fn build_native() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
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();
|
build_native();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "espanso"
|
name = "espanso"
|
||||||
version = "2.0.3-alpha"
|
version = "2.0.4-alpha"
|
||||||
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"
|
||||||
|
|
|
@ -126,6 +126,8 @@ pub fn initialize_and_spawn(
|
||||||
),
|
),
|
||||||
hotkeys: match_converter.get_hotkeys(),
|
hotkeys: match_converter.get_hotkeys(),
|
||||||
win32_exclude_orphan_events: default_config.win32_exclude_orphan_events(),
|
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");
|
.expect("failed to initialize detector module");
|
||||||
let exit_source = super::engine::funnel::exit::ExitSource::new(exit_signal, &sequencer);
|
let exit_source = super::engine::funnel::exit::ExitSource::new(exit_signal, &sequencer);
|
||||||
|
|
|
@ -55,9 +55,43 @@ fn convert_fields(fields: &Params) -> HashMap<String, FormField> {
|
||||||
let mut form_field = None;
|
let mut form_field = None;
|
||||||
|
|
||||||
if let Value::Object(params) = field {
|
if let Value::Object(params) = field {
|
||||||
if let Some(Value::String(field_type)) = params.get("type") {
|
form_field = match params.get("type") {
|
||||||
form_field = match field_type.as_str() {
|
Some(Value::String(field_type)) if field_type == "choice" => Some(FormField::Choice {
|
||||||
"text" => Some(FormField::Text {
|
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
|
default: params
|
||||||
.get("default")
|
.get("default")
|
||||||
.and_then(|val| val.as_string())
|
.and_then(|val| val.as_string())
|
||||||
|
@ -68,42 +102,6 @@ fn convert_fields(fields: &Params) -> HashMap<String, FormField> {
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or(false),
|
.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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ fn get_builtin_patches() -> Vec<PatchDefinition> {
|
||||||
patches::linux::urxvt_terminal_x11::patch(),
|
patches::linux::urxvt_terminal_x11::patch(),
|
||||||
patches::linux::xterm_terminal_x11::patch(),
|
patches::linux::xterm_terminal_x11::patch(),
|
||||||
patches::linux::yakuake_terminal_x11::patch(),
|
patches::linux::yakuake_terminal_x11::patch(),
|
||||||
|
patches::linux::virtualbox_x11::patch(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use espanso_config::config::Backend;
|
||||||
|
|
||||||
use crate::patch::patches::{PatchedConfig, Patches};
|
use crate::patch::patches::{PatchedConfig, Patches};
|
||||||
use crate::patch::PatchDefinition;
|
use crate::patch::PatchDefinition;
|
||||||
|
|
||||||
|
@ -33,6 +35,7 @@ pub fn patch() -> PatchDefinition {
|
||||||
name,
|
name,
|
||||||
Patches {
|
Patches {
|
||||||
paste_shortcut: Some(Some("CTRL+SHIFT+V".to_string())),
|
paste_shortcut: Some(Some("CTRL+SHIFT+V".to_string())),
|
||||||
|
backend: Some(Backend::Clipboard),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
|
|
@ -30,6 +30,7 @@ pub mod termite_terminal_x11;
|
||||||
pub mod thunderbird_x11;
|
pub mod thunderbird_x11;
|
||||||
pub mod tilix_terminal_x11;
|
pub mod tilix_terminal_x11;
|
||||||
pub mod urxvt_terminal_x11;
|
pub mod urxvt_terminal_x11;
|
||||||
|
pub mod virtualbox_x11;
|
||||||
pub mod xterm_terminal_x11;
|
pub mod xterm_terminal_x11;
|
||||||
pub mod yakuake_terminal_x11;
|
pub mod yakuake_terminal_x11;
|
||||||
|
|
||||||
|
|
46
espanso/src/patch/patches/linux/virtualbox_x11.rs
Normal file
46
espanso/src/patch/patches/linux/virtualbox_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("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()
|
||||||
|
},
|
||||||
|
))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -49,5 +49,6 @@ generate_patchable_config!(
|
||||||
apply_patch -> bool,
|
apply_patch -> bool,
|
||||||
undo_backspace -> bool,
|
undo_backspace -> bool,
|
||||||
win32_exclude_orphan_events -> bool,
|
win32_exclude_orphan_events -> bool,
|
||||||
|
win32_keyboard_layout_cache_interval -> i64,
|
||||||
keyboard_layout -> Option<RMLVOConfig>
|
keyboard_layout -> Option<RMLVOConfig>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: espanso
|
name: espanso
|
||||||
version: 2.0.3-alpha
|
version: 2.0.4-alpha
|
||||||
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.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user