feat(detect): implement modifier state synchronization on Wayland
This commit is contained in:
parent
08e86c7e35
commit
33b9012802
197
Cargo.lock
generated
197
Cargo.lock
generated
|
@ -143,6 +143,16 @@ dependencies = [
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "calloop"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d0a1340115d6bd81e1066469091596a339f68878a2ce3c2f39e546607d22131"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"nix 0.19.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "caps"
|
name = "caps"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
@ -418,12 +428,27 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dlib"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794"
|
||||||
|
dependencies = [
|
||||||
|
"libloading",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "downcast"
|
name = "downcast"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d"
|
checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "downcast-rs"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dtoa"
|
name = "dtoa"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
|
@ -580,6 +605,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"regex",
|
"regex",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
|
"smithay-client-toolkit",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"widestring",
|
"widestring",
|
||||||
]
|
]
|
||||||
|
@ -1031,6 +1057,16 @@ dependencies = [
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libloading"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linked-hash-map"
|
name = "linked-hash-map"
|
||||||
version = "0.5.4"
|
version = "0.5.4"
|
||||||
|
@ -1131,6 +1167,15 @@ version = "2.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memmap2"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "memoffset"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
|
@ -1246,6 +1291,40 @@ version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.19.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.2.1",
|
||||||
|
"cc",
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.2.1",
|
||||||
|
"cc",
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "6.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "normalize-line-endings"
|
name = "normalize-line-endings"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -1329,6 +1408,12 @@ dependencies = [
|
||||||
"objc",
|
"objc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opener"
|
name = "opener"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -1709,6 +1794,12 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped-tls"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -1781,6 +1872,30 @@ version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
|
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smithay-client-toolkit"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec783683499a2cfc85b6df3d04f83b1907b5cbd98a1aed44667dbdf1eac4e64c"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.2.1",
|
||||||
|
"calloop",
|
||||||
|
"dlib",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"memmap2",
|
||||||
|
"nix 0.20.0",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-cursor",
|
||||||
|
"wayland-protocols",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "squote"
|
name = "squote"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -2091,6 +2206,79 @@ version = "0.10.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-client"
|
||||||
|
version = "0.28.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3ab332350e502f159382201394a78e3cc12d0f04db863429260164ea40e0355"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.2.1",
|
||||||
|
"downcast-rs",
|
||||||
|
"libc",
|
||||||
|
"nix 0.20.0",
|
||||||
|
"scoped-tls",
|
||||||
|
"wayland-commons",
|
||||||
|
"wayland-scanner",
|
||||||
|
"wayland-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-commons"
|
||||||
|
version = "0.28.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda"
|
||||||
|
dependencies = [
|
||||||
|
"nix 0.20.0",
|
||||||
|
"once_cell",
|
||||||
|
"smallvec",
|
||||||
|
"wayland-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-cursor"
|
||||||
|
version = "0.28.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a"
|
||||||
|
dependencies = [
|
||||||
|
"nix 0.20.0",
|
||||||
|
"wayland-client",
|
||||||
|
"xcursor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-protocols"
|
||||||
|
version = "0.28.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "286620ea4d803bacf61fa087a4242ee316693099ee5a140796aaba02b29f861f"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.2.1",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-commons",
|
||||||
|
"wayland-scanner",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-scanner"
|
||||||
|
version = "0.28.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce923eb2deb61de332d1f356ec7b6bf37094dc5573952e1c8936db03b54c03f1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote 1.0.9",
|
||||||
|
"xml-rs 0.8.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-sys"
|
||||||
|
version = "0.28.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d841fca9aed7febf9bed2e9796c49bf58d4152ceda8ac949ebe00868d8f0feb8"
|
||||||
|
dependencies = [
|
||||||
|
"dlib",
|
||||||
|
"lazy_static",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "widestring"
|
name = "widestring"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
@ -2272,6 +2460,15 @@ dependencies = [
|
||||||
"winapi-build",
|
"winapi-build",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xcursor"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a9a231574ae78801646617cefd13bfe94be907c0e4fa979cfd8b770aa3c5d08"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xml-rs"
|
name = "xml-rs"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
|
|
|
@ -6,9 +6,11 @@ edition = "2018"
|
||||||
build="build.rs"
|
build="build.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
# TODO: REMOVE!!!
|
||||||
|
default = ["wayland"]
|
||||||
# If the wayland feature is enabled, all X11 dependencies will be dropped
|
# If the wayland feature is enabled, all X11 dependencies will be dropped
|
||||||
# and only EVDEV-based methods will be supported.
|
# and only EVDEV-based methods will be supported.
|
||||||
wayland = []
|
wayland = ["sctk"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
|
@ -24,6 +26,7 @@ widestring = "0.4.3"
|
||||||
[target.'cfg(target_os="linux")'.dependencies]
|
[target.'cfg(target_os="linux")'.dependencies]
|
||||||
libc = "0.2.85"
|
libc = "0.2.85"
|
||||||
scopeguard = "1.1.0"
|
scopeguard = "1.1.0"
|
||||||
|
sctk = { package = "smithay-client-toolkit", version = "0.14.0", optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.0.66"
|
cc = "1.0.66"
|
||||||
|
|
|
@ -5,6 +5,7 @@ use anyhow::Result;
|
||||||
use libc::{input_event, size_t, ssize_t, EWOULDBLOCK, O_CLOEXEC, O_NONBLOCK, O_RDONLY};
|
use libc::{input_event, size_t, ssize_t, EWOULDBLOCK, O_CLOEXEC, O_NONBLOCK, O_RDONLY};
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use scopeguard::ScopeGuard;
|
use scopeguard::ScopeGuard;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::{
|
use std::{
|
||||||
ffi::{c_void, CStr},
|
ffi::{c_void, CStr},
|
||||||
|
@ -13,6 +14,7 @@ use std::{
|
||||||
use std::{fs::File, os::unix::fs::OpenOptionsExt};
|
use std::{fs::File, os::unix::fs::OpenOptionsExt};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use super::sync::ModifiersState;
|
||||||
use super::{
|
use super::{
|
||||||
ffi::{
|
ffi::{
|
||||||
is_keyboard_or_mouse, xkb_key_direction, xkb_keycode_t, xkb_keymap_key_repeats, xkb_state,
|
is_keyboard_or_mouse, xkb_key_direction, xkb_keycode_t, xkb_keymap_key_repeats, xkb_state,
|
||||||
|
@ -185,6 +187,38 @@ impl Device {
|
||||||
|
|
||||||
Some(RawInputEvent::Keyboard(event))
|
Some(RawInputEvent::Keyboard(event))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_key(&mut self, code: u32, pressed: bool) {
|
||||||
|
let direction = if pressed {
|
||||||
|
super::ffi::xkb_key_direction::DOWN
|
||||||
|
} else {
|
||||||
|
super::ffi::xkb_key_direction::UP
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
xkb_state_update_key(self.get_state(), code, direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_modifier_state(&mut self, modifiers_state: &ModifiersState, modifiers_map: &HashMap<String, u32>) {
|
||||||
|
if modifiers_state.alt {
|
||||||
|
self.update_key(*modifiers_map.get("alt").expect("unable to find modifiers key in map"), true);
|
||||||
|
}
|
||||||
|
if modifiers_state.ctrl {
|
||||||
|
self.update_key(*modifiers_map.get("ctrl").expect("unable to find modifiers key in map"), true);
|
||||||
|
}
|
||||||
|
if modifiers_state.meta {
|
||||||
|
self.update_key(*modifiers_map.get("meta").expect("unable to find modifiers key in map"), true);
|
||||||
|
}
|
||||||
|
if modifiers_state.num_lock {
|
||||||
|
self.update_key(*modifiers_map.get("num_lock").expect("unable to find modifiers key in map"), true);
|
||||||
|
}
|
||||||
|
if modifiers_state.shift {
|
||||||
|
self.update_key(*modifiers_map.get("shift").expect("unable to find modifiers key in map"), true);
|
||||||
|
}
|
||||||
|
if modifiers_state.caps_lock {
|
||||||
|
self.update_key(*modifiers_map.get("caps_lock").expect("unable to find modifiers key in map"), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Device {
|
impl Drop for Device {
|
||||||
|
|
|
@ -26,8 +26,10 @@ mod ffi;
|
||||||
mod hotkey;
|
mod hotkey;
|
||||||
mod keymap;
|
mod keymap;
|
||||||
mod state;
|
mod state;
|
||||||
|
mod sync;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use context::Context;
|
use context::Context;
|
||||||
|
@ -37,7 +39,7 @@ use lazycell::LazyCell;
|
||||||
use libc::{
|
use libc::{
|
||||||
__errno_location, close, epoll_ctl, epoll_event, epoll_wait, EINTR, EPOLLIN, EPOLL_CTL_ADD,
|
__errno_location, close, epoll_ctl, epoll_event, epoll_wait, EINTR, EPOLLIN, EPOLL_CTL_ADD,
|
||||||
};
|
};
|
||||||
use log::{error, trace};
|
use log::{debug, error, info, trace};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
|
use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
|
||||||
|
@ -57,6 +59,19 @@ const BTN_MIDDLE: u16 = 0x112;
|
||||||
const BTN_SIDE: u16 = 0x113;
|
const BTN_SIDE: u16 = 0x113;
|
||||||
const BTN_EXTRA: u16 = 0x114;
|
const BTN_EXTRA: u16 = 0x114;
|
||||||
|
|
||||||
|
// Offset between evdev keycodes (where KEY_ESCAPE is 1), and the evdev XKB
|
||||||
|
// keycode set (where ESC is 9).
|
||||||
|
const EVDEV_OFFSET: u32 = 8;
|
||||||
|
|
||||||
|
// List of modifier keycodes, as defined in the "input-event-codes.h" header
|
||||||
|
// TODO: create an option to override them if needed
|
||||||
|
const KEY_CTRL: u32 = 29;
|
||||||
|
const KEY_SHIFT: u32 = 42;
|
||||||
|
const KEY_ALT: u32 = 56;
|
||||||
|
const KEY_META: u32 = 125;
|
||||||
|
const KEY_CAPSLOCK: u32 = 58;
|
||||||
|
const KEY_NUMLOCK: u32 = 69;
|
||||||
|
|
||||||
pub struct EVDEVSource {
|
pub struct EVDEVSource {
|
||||||
devices: Vec<Device>,
|
devices: Vec<Device>,
|
||||||
hotkeys: Vec<HotKey>,
|
hotkeys: Vec<HotKey>,
|
||||||
|
@ -65,11 +80,20 @@ pub struct EVDEVSource {
|
||||||
_context: LazyCell<Context>,
|
_context: LazyCell<Context>,
|
||||||
_keymap: LazyCell<Keymap>,
|
_keymap: LazyCell<Keymap>,
|
||||||
_hotkey_filter: RefCell<HotKeyFilter>,
|
_hotkey_filter: RefCell<HotKeyFilter>,
|
||||||
|
_modifiers_map: HashMap<String, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_without_default)]
|
#[allow(clippy::new_without_default)]
|
||||||
impl EVDEVSource {
|
impl EVDEVSource {
|
||||||
pub fn new(options: SourceCreationOptions) -> EVDEVSource {
|
pub fn new(options: SourceCreationOptions) -> EVDEVSource {
|
||||||
|
let mut modifiers_map = HashMap::new();
|
||||||
|
modifiers_map.insert("ctrl".to_string(), KEY_CTRL + EVDEV_OFFSET);
|
||||||
|
modifiers_map.insert("shift".to_string(), KEY_SHIFT + EVDEV_OFFSET);
|
||||||
|
modifiers_map.insert("alt".to_string(), KEY_ALT + EVDEV_OFFSET);
|
||||||
|
modifiers_map.insert("meta".to_string(), KEY_META + EVDEV_OFFSET);
|
||||||
|
modifiers_map.insert("caps_lock".to_string(), KEY_CAPSLOCK + EVDEV_OFFSET);
|
||||||
|
modifiers_map.insert("num_lock".to_string(), KEY_NUMLOCK + EVDEV_OFFSET);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
devices: Vec::new(),
|
devices: Vec::new(),
|
||||||
hotkeys: options.hotkeys,
|
hotkeys: options.hotkeys,
|
||||||
|
@ -77,6 +101,7 @@ impl EVDEVSource {
|
||||||
_keymap: LazyCell::new(),
|
_keymap: LazyCell::new(),
|
||||||
_keyboard_rmlvo: options.evdev_keyboard_rmlvo,
|
_keyboard_rmlvo: options.evdev_keyboard_rmlvo,
|
||||||
_hotkey_filter: RefCell::new(HotKeyFilter::new()),
|
_hotkey_filter: RefCell::new(HotKeyFilter::new()),
|
||||||
|
_modifiers_map: modifiers_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,8 +128,18 @@ impl Source for EVDEVSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the hotkeys
|
|
||||||
let state = State::new(&keymap)?;
|
let state = State::new(&keymap)?;
|
||||||
|
|
||||||
|
info!("Querying modifier status...");
|
||||||
|
if let Some(modifiers_state) = sync::get_modifiers_state()? {
|
||||||
|
debug!("Updating device modifier state: {:?}", modifiers_state);
|
||||||
|
|
||||||
|
for device in &mut self.devices {
|
||||||
|
device.update_modifier_state(&modifiers_state, &self._modifiers_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the hotkeys
|
||||||
self
|
self
|
||||||
._hotkey_filter
|
._hotkey_filter
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
|
|
39
espanso-detect/src/evdev/sync/mod.rs
Normal file
39
espanso-detect/src/evdev/sync/mod.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct ModifiersState {
|
||||||
|
pub ctrl: bool,
|
||||||
|
pub alt: bool,
|
||||||
|
pub shift: bool,
|
||||||
|
pub caps_lock: bool,
|
||||||
|
pub meta: bool,
|
||||||
|
pub num_lock: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
mod wayland;
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
pub use wayland::get_modifiers_state;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "wayland"))]
|
||||||
|
pub fn get_modifiers_state() -> anyhow::Result<Option<ModifiersState>> {
|
||||||
|
// Fallback for non-wayland systems
|
||||||
|
Ok(None)
|
||||||
|
}
|
252
espanso-detect/src/evdev/sync/wayland.rs
Normal file
252
espanso-detect/src/evdev/sync/wayland.rs
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
// This module was implemented starting from this wonderful example:
|
||||||
|
// https://github.com/Smithay/client-toolkit/blob/master/examples/kbd_input.rs
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::cmp::min;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use log::error;
|
||||||
|
use sctk::reexports::calloop;
|
||||||
|
use sctk::reexports::client::protocol::{wl_keyboard, wl_shm, wl_surface};
|
||||||
|
use sctk::seat::keyboard::{map_keyboard_repeat, Event as KbEvent, RepeatKind};
|
||||||
|
use sctk::shm::AutoMemPool;
|
||||||
|
use sctk::window::{Event as WEvent, FallbackFrame};
|
||||||
|
|
||||||
|
sctk::default_environment!(EspansoModifiersSync, desktop);
|
||||||
|
|
||||||
|
pub fn get_modifiers_state() -> Result<Option<super::ModifiersState>> {
|
||||||
|
let (env, display, queue) = sctk::new_default_environment!(EspansoModifiersSync, desktop)
|
||||||
|
.context("Unable to connect to a Wayland compositor")?;
|
||||||
|
|
||||||
|
let result = Rc::new(RefCell::new(None));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare a calloop event loop to handle key repetion
|
||||||
|
*/
|
||||||
|
// Here `Option<WEvent>` is the type of a global value that will be shared by
|
||||||
|
// all callbacks invoked by the event loop.
|
||||||
|
let mut event_loop = calloop::EventLoop::<Option<WEvent>>::try_new().unwrap();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a buffer with window contents
|
||||||
|
*/
|
||||||
|
|
||||||
|
let mut dimensions = (1u32, 1u32);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init wayland objects
|
||||||
|
*/
|
||||||
|
|
||||||
|
let surface = env.create_surface().detach();
|
||||||
|
|
||||||
|
let mut window = env
|
||||||
|
.create_window::<FallbackFrame, _>(surface, None, dimensions, move |evt, mut dispatch_data| {
|
||||||
|
let next_action = dispatch_data.get::<Option<WEvent>>().unwrap();
|
||||||
|
// Keep last event in priority order : Close > Configure > Refresh
|
||||||
|
let replace = match (&evt, &*next_action) {
|
||||||
|
(_, &None)
|
||||||
|
| (_, &Some(WEvent::Refresh))
|
||||||
|
| (&WEvent::Configure { .. }, &Some(WEvent::Configure { .. }))
|
||||||
|
| (&WEvent::Close, _) => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if replace {
|
||||||
|
*next_action = Some(evt);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.context("Failed to create a window !")?;
|
||||||
|
|
||||||
|
window.set_title("Espanso Sync Tool".to_string());
|
||||||
|
|
||||||
|
let mut pool = env
|
||||||
|
.create_auto_pool()
|
||||||
|
.context("Failed to create a memory pool !")?;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Keyboard initialization
|
||||||
|
*/
|
||||||
|
|
||||||
|
let mut seats = Vec::<(
|
||||||
|
String,
|
||||||
|
Option<(wl_keyboard::WlKeyboard, calloop::RegistrationToken)>,
|
||||||
|
)>::new();
|
||||||
|
|
||||||
|
// first process already existing seats
|
||||||
|
for seat in env.get_all_seats() {
|
||||||
|
if let Some((has_kbd, name)) = sctk::seat::with_seat_data(&seat, |seat_data| {
|
||||||
|
(
|
||||||
|
seat_data.has_keyboard && !seat_data.defunct,
|
||||||
|
seat_data.name.clone(),
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
if has_kbd {
|
||||||
|
let result_clone = result.clone();
|
||||||
|
match map_keyboard_repeat(
|
||||||
|
event_loop.handle(),
|
||||||
|
&seat,
|
||||||
|
None,
|
||||||
|
RepeatKind::System,
|
||||||
|
move |event, _, _| keyboard_event_handler(event, &result_clone),
|
||||||
|
) {
|
||||||
|
Ok((kbd, repeat_source)) => {
|
||||||
|
seats.push((name, Some((kbd, repeat_source))));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to map keyboard on seat {} : {:?}.", name, e);
|
||||||
|
seats.push((name, None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
seats.push((name, None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// then setup a listener for changes
|
||||||
|
let loop_handle = event_loop.handle();
|
||||||
|
let result_clone = result.clone();
|
||||||
|
let _seat_listener = env.listen_for_seats(move |seat, seat_data, _| {
|
||||||
|
let result_clone = result_clone.clone();
|
||||||
|
// find the seat in the vec of seats, or insert it if it is unknown
|
||||||
|
let idx = seats.iter().position(|(name, _)| name == &seat_data.name);
|
||||||
|
let idx = idx.unwrap_or_else(|| {
|
||||||
|
seats.push((seat_data.name.clone(), None));
|
||||||
|
seats.len() - 1
|
||||||
|
});
|
||||||
|
|
||||||
|
let (_, ref mut opt_kbd) = &mut seats[idx];
|
||||||
|
// we should map a keyboard if the seat has the capability & is not defunct
|
||||||
|
if seat_data.has_keyboard && !seat_data.defunct {
|
||||||
|
if opt_kbd.is_none() {
|
||||||
|
// we should initalize a keyboard
|
||||||
|
match map_keyboard_repeat(
|
||||||
|
loop_handle.clone(),
|
||||||
|
&seat,
|
||||||
|
None,
|
||||||
|
RepeatKind::System,
|
||||||
|
move |event, _, _| keyboard_event_handler(event, &result_clone),
|
||||||
|
) {
|
||||||
|
Ok((kbd, repeat_source)) => {
|
||||||
|
*opt_kbd = Some((kbd, repeat_source));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!(
|
||||||
|
"Failed to map keyboard on seat {} : {:?}.",
|
||||||
|
seat_data.name, e
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some((kbd, source)) = opt_kbd.take() {
|
||||||
|
// the keyboard has been removed, cleanup
|
||||||
|
kbd.release();
|
||||||
|
loop_handle.remove(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if !env.get_shell().unwrap().needs_configure() {
|
||||||
|
// initial draw to bootstrap on wl_shell
|
||||||
|
redraw(&mut pool, window.surface(), dimensions).expect("Failed to draw");
|
||||||
|
window.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
sctk::WaylandSource::new(queue)
|
||||||
|
.quick_insert(event_loop.handle())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut next_action = None;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match next_action.take() {
|
||||||
|
Some(WEvent::Close) => break,
|
||||||
|
Some(WEvent::Refresh) => {
|
||||||
|
window.refresh();
|
||||||
|
window.surface().commit();
|
||||||
|
}
|
||||||
|
Some(WEvent::Configure {
|
||||||
|
new_size,
|
||||||
|
states: _,
|
||||||
|
}) => {
|
||||||
|
if let Some((w, h)) = new_size {
|
||||||
|
window.resize(w, h);
|
||||||
|
dimensions = (w, h)
|
||||||
|
}
|
||||||
|
window.refresh();
|
||||||
|
redraw(&mut pool, window.surface(), dimensions).expect("Failed to draw");
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let result_clone= result.clone();
|
||||||
|
let result_ref = result_clone.borrow();
|
||||||
|
|
||||||
|
if let Some(result) = &*result_ref {
|
||||||
|
return Ok(Some(result.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// always flush the connection before going to sleep waiting for events
|
||||||
|
display.flush().unwrap();
|
||||||
|
|
||||||
|
event_loop
|
||||||
|
.dispatch(
|
||||||
|
Some(std::time::Duration::from_millis(10)),
|
||||||
|
&mut next_action,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_event_handler(
|
||||||
|
event: KbEvent,
|
||||||
|
result_clone: &Rc<RefCell<Option<super::ModifiersState>>>,
|
||||||
|
) {
|
||||||
|
if let KbEvent::Modifiers { modifiers } = event {
|
||||||
|
let mut result_mut = (**result_clone).borrow_mut();
|
||||||
|
*result_mut = Some(super::ModifiersState {
|
||||||
|
ctrl: modifiers.ctrl,
|
||||||
|
alt: modifiers.alt,
|
||||||
|
shift: modifiers.shift,
|
||||||
|
caps_lock: modifiers.caps_lock,
|
||||||
|
meta: modifiers.logo,
|
||||||
|
num_lock: modifiers.num_lock,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn redraw(
|
||||||
|
pool: &mut AutoMemPool,
|
||||||
|
surface: &wl_surface::WlSurface,
|
||||||
|
(buf_x, buf_y): (u32, u32),
|
||||||
|
) -> Result<(), ::std::io::Error> {
|
||||||
|
let (canvas, new_buffer) = pool.buffer(
|
||||||
|
buf_x as i32,
|
||||||
|
buf_y as i32,
|
||||||
|
4 * buf_x as i32,
|
||||||
|
wl_shm::Format::Argb8888,
|
||||||
|
)?;
|
||||||
|
for (i, dst_pixel) in canvas.chunks_exact_mut(4).enumerate() {
|
||||||
|
let x = i as u32 % buf_x;
|
||||||
|
let y = i as u32 / buf_x;
|
||||||
|
let r: u32 = min(((buf_x - x) * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
|
||||||
|
let g: u32 = min((x * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
|
||||||
|
let b: u32 = min(((buf_x - x) * 0xFF) / buf_x, (y * 0xFF) / buf_y);
|
||||||
|
let pixel: [u8; 4] = ((0xFF << 24) + (r << 16) + (g << 8) + b).to_ne_bytes();
|
||||||
|
dst_pixel[0] = pixel[0];
|
||||||
|
dst_pixel[1] = pixel[1];
|
||||||
|
dst_pixel[2] = pixel[2];
|
||||||
|
dst_pixel[3] = pixel[3];
|
||||||
|
}
|
||||||
|
surface.attach(Some(&new_buffer), 0, 0);
|
||||||
|
if surface.as_ref().version() >= 4 {
|
||||||
|
surface.damage_buffer(0, 0, buf_x as i32, buf_y as i32);
|
||||||
|
} else {
|
||||||
|
surface.damage(0, 0, buf_x as i32, buf_y as i32);
|
||||||
|
}
|
||||||
|
surface.commit();
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user