Adds RELATIVE event catcher (#187)

close #180
pull/239/head
Perseus 1 year ago committed by GitHub
parent 1986c0b7f3
commit 17d50df194
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,13 +2,17 @@ use std::time::Duration;
use evdev::InputEvent;
use crate::event::KeyEvent;
use crate::event::{KeyEvent, RelativeEvent};
// Input to ActionDispatcher. This should only contain things that are easily testable.
#[derive(Debug)]
pub enum Action {
// InputEvent (EventType::KEY) sent to evdev
KeyEvent(KeyEvent),
// InputEvent (EventType::RELATIVE, NOT mouse movement events) sent to evdev
RelativeEvent(RelativeEvent),
// InputEvent (EventType::RELATIVE, ONLY mouse movement events) a collection of mouse movement sent to evdev
MouseMovementEventCollection(Vec<RelativeEvent>),
// InputEvent of any event types. It's discouraged to use this for testing because
// we don't have full control over timeval and it's not pattern-matching friendly.
InputEvent(InputEvent),

@ -8,6 +8,7 @@ use nix::sys::signal;
use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet};
use std::process::{exit, Command, Stdio};
use crate::event::RelativeEvent;
use crate::{action::Action, event::KeyEvent};
pub struct ActionDispatcher {
@ -29,6 +30,23 @@ impl ActionDispatcher {
pub fn on_action(&mut self, action: Action) -> anyhow::Result<()> {
match action {
Action::KeyEvent(key_event) => self.on_key_event(key_event)?,
Action::RelativeEvent(relative_event) => self.on_relative_event(relative_event)?,
Action::MouseMovementEventCollection(mouse_movement_events) => {
// Sending all mouse movement events at once, unseparated by synchronization events.
self.send_mousemovement_event_batch(mouse_movement_events)?;
// Mouse movement events need to be sent all at once because they would otherwise be separated by a synchronization event¹,
// which the OS handles differently from two unseparated mouse movement events.
// For example,
// a REL_X event², followed by a SYNCHRONIZATION event, followed by a REL_Y event³, followed by a SYNCHRONIZATION event,
// will move the mouse cursor by a different amount than
// a REL_X event followed by a REL_Y event followed by a SYNCHRONIZATION event.
// ¹Because Xremap usually sends events one by one through evdev's "emit" function, which adds a synchronization event during each call.
// ²Mouse movement along the X (horizontal) axis.
// ³Mouse movement along the Y (vertical) axis.
}
Action::InputEvent(event) => self.send_event(event)?,
Action::Command(command) => self.run_command(command),
Action::Delay(duration) => thread::sleep(duration),
@ -41,6 +59,24 @@ impl ActionDispatcher {
self.send_event(event)
}
fn on_relative_event(&mut self, event: RelativeEvent) -> std::io::Result<()> {
let event = InputEvent::new_now(EventType::RELATIVE, event.code, event.value);
self.send_event(event)
}
// a function that takes mouse movement events to send in a single batch, unseparated by synchronization events.
fn send_mousemovement_event_batch(&mut self, eventbatch: Vec<RelativeEvent>) -> std::io::Result<()> {
let mut mousemovementbatch: Vec<InputEvent> = Vec::new();
for mouse_movement in eventbatch {
mousemovementbatch.push(InputEvent::new_now(
EventType::RELATIVE,
mouse_movement.code,
mouse_movement.value,
));
}
self.device.emit(&mousemovementbatch)
}
fn send_event(&mut self, event: InputEvent) -> std::io::Result<()> {
if event.event_type() == EventType::KEY {
debug!("{}: {:?}", event.value(), Key::new(event.code()))

@ -1,3 +1,4 @@
use crate::event_handler::DISGUISED_EVENT_OFFSETTER;
use evdev::Key;
use serde::{Deserialize, Deserializer};
use std::error::Error;
@ -47,6 +48,79 @@ pub fn parse_key(input: &str) -> Result<Key, Box<dyn Error>> {
"SUPER_L" => Key::KEY_LEFTMETA,
"WIN_R" => Key::KEY_RIGHTMETA,
"WIN_L" => Key::KEY_LEFTMETA,
// Custom aliases used in config files to represent scancodes for disguised relative events.
// Relative events are disguised into key events with those scancodes,
// and are then sent through modmap and keymap.
//
// These custom aliases are used in config files, like other aliases.
// The difference here is that since these scancodes don't map to any existing name,
// (on purpose, to avoid conflating disguised events and actual key events)
// we need to define them using scancodes instead of existing names.
//
// The DISGUISED_EVENT_OFFSETTER const is used here to make it easy to change the scancodes should it ever be necessary.
// Because configs use name and custom aliases, changing their assigned value doesn't change how to write configs;
// In other words, a config that works when DISGUISED_EVENT_OFFSETTER == 59974
// will work exactly the same way if DISGUISED_EVENT_OFFSETTER == 46221
//
// DISGUISED_EVENT_OFFSETTER is also used in tests.rs::verify_disguised_relative_events(),
// to prevent its modification to a number too low or too big.
//
// Cursor movement
"XRIGHTCURSOR" => Key(DISGUISED_EVENT_OFFSETTER), // Cursor right
"XLEFTCURSOR" => Key(DISGUISED_EVENT_OFFSETTER + 1), // Cursor left
"XDOWNCURSOR" => Key(DISGUISED_EVENT_OFFSETTER + 2), // Cursor down
"XUPCURSOR" => Key(DISGUISED_EVENT_OFFSETTER + 3), // Cursor up
// Cursor... forward and backwards?
"XREL_Z_AXIS_1" => Key(DISGUISED_EVENT_OFFSETTER + 4),
"XREL_Z_AXIS_2" => Key(DISGUISED_EVENT_OFFSETTER + 5),
//
// Rotative cursor movement?
"XREL_RX_AXIS_1" => Key(DISGUISED_EVENT_OFFSETTER + 6), // horizontal
"XREL_RX_AXIS_2" => Key(DISGUISED_EVENT_OFFSETTER + 7),
"XREL_RY_AXIS_1" => Key(DISGUISED_EVENT_OFFSETTER + 8), // vertical
"XREL_RY_AXIS_2" => Key(DISGUISED_EVENT_OFFSETTER + 9),
"XREL_RZ_AXIS_1" => Key(DISGUISED_EVENT_OFFSETTER + 10), // Whatever the third dimensional axis is called
"XREL_RZ_AXIS_2" => Key(DISGUISED_EVENT_OFFSETTER + 11),
//
"XRIGHTSCROLL" => Key(DISGUISED_EVENT_OFFSETTER + 12), // Rightscroll
"XLEFTSCROLL" => Key(DISGUISED_EVENT_OFFSETTER + 13), // Leftscroll
//
// ???
"XREL_DIAL_1" => Key(DISGUISED_EVENT_OFFSETTER + 14),
"XREL_DIAL_2" => Key(DISGUISED_EVENT_OFFSETTER + 15),
//
"XUPSCROLL" => Key(DISGUISED_EVENT_OFFSETTER + 16), // Upscroll
"XDOWNSCROLL" => Key(DISGUISED_EVENT_OFFSETTER + 17), // Downscroll
//
// Something?
"XREL_MISC_1" => Key(DISGUISED_EVENT_OFFSETTER + 18),
"XREL_MISC_2" => Key(DISGUISED_EVENT_OFFSETTER + 19),
"XREL_RESERVED_1" => Key(DISGUISED_EVENT_OFFSETTER + 20),
"XREL_RESERVED_2" => Key(DISGUISED_EVENT_OFFSETTER + 21),
//
// High resolution version of scroll events, sent just after their non-high resolution version.
"XHIRES_UPSCROLL" => Key(DISGUISED_EVENT_OFFSETTER + 22),
"XHIRES_DOWNSCROLL" => Key(DISGUISED_EVENT_OFFSETTER + 23),
"XHIRES_RIGHTSCROLL" => Key(DISGUISED_EVENT_OFFSETTER + 24),
"XHIRES_LEFTSCROLL" => Key(DISGUISED_EVENT_OFFSETTER + 25),
/* Original Relative events and their values for quick reference.
REL_X = 0x00,
REL_Y = 0x01,
REL_Z = 0x02,
REL_RX = 0x03,
REL_RY = 0x04,
REL_RZ = 0x05,
REL_HWHEEL = 0x06,
REL_DIAL = 0x07,
REL_WHEEL = 0x08,
REL_MISC = 0x09,
REL_RESERVED = 0x0a,
REL_WHEEL_HI_RES = 0x0b,
REL_HWHEEL_HI_RES = 0x0c,
*/
// End of custom scancodes
// else
_ => Key::KEY_RESERVED,
};

@ -5,6 +5,10 @@ use evdev::{EventType, InputEvent, Key};
pub enum Event {
// InputEvent (EventType::KEY) sent from evdev
KeyEvent(KeyEvent),
// InputEvent (EventType::Relative) sent from evdev
RelativeEvent(RelativeEvent),
// Any other InputEvent type sent from evdev
OtherEvents(InputEvent),
// Timer for nested override reached its timeout
OverrideTimeout,
}
@ -15,21 +19,27 @@ pub struct KeyEvent {
value: KeyValue,
}
#[derive(Debug)]
pub struct RelativeEvent {
pub code: u16,
pub value: i32,
}
#[derive(Debug)]
pub enum KeyValue {
Press,
Release,
Repeat,
}
impl Event {
// Convert evdev's raw InputEvent to xremap's internal Event
pub fn new(event: InputEvent) -> Option<Event> {
pub fn new(event: InputEvent) -> Event {
let event = match event.event_type() {
EventType::KEY => Event::KeyEvent(KeyEvent::new_with(event.code(), event.value())),
_ => return None,
EventType::RELATIVE => Event::RelativeEvent(RelativeEvent::new_with(event.code(), event.value())),
_ => Event::OtherEvents(event),
};
Some(event)
event
}
}
@ -55,6 +65,13 @@ impl KeyEvent {
}
}
// constructor for relative events.
impl RelativeEvent {
pub fn new_with(code: u16, value: i32) -> RelativeEvent {
RelativeEvent { code, value }
}
}
impl KeyValue {
fn new(value: i32) -> Option<KeyValue> {
let event_value = match value {

@ -6,7 +6,7 @@ use crate::config::keymap::{build_override_table, OverrideEntry};
use crate::config::keymap_action::KeymapAction;
use crate::config::modmap_action::{ModmapAction, MultiPurposeKey, PressReleaseKey};
use crate::config::remap::Remap;
use crate::event::{Event, KeyEvent};
use crate::event::{Event, KeyEvent, RelativeEvent};
use crate::Config;
use evdev::Key;
use lazy_static::lazy_static;
@ -17,6 +17,12 @@ use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::time::{Duration, Instant};
// This const is a value used to offset RELATIVE events' scancodes
// so that they correspond to the custom aliases created in config::key::parse_key.
// This offset also prevents resulting scancodes from corresponding to non-Xremap scancodes,
// to prevent conflating disguised relative events with other events.
pub const DISGUISED_EVENT_OFFSETTER: u16 = 59974;
pub struct EventHandler {
// Currently pressed modifier keys
modifiers: HashSet<Key>,
@ -73,16 +79,32 @@ impl EventHandler {
}
// Handle an Event and return Actions. This should be the only public method of EventHandler.
pub fn on_event(&mut self, event: &Event, config: &Config) -> Result<Vec<Action>, Box<dyn Error>> {
match event {
Event::KeyEvent(key_event) => self.on_key_event(key_event, config),
Event::OverrideTimeout => self.timeout_override(),
}?;
pub fn on_events(&mut self, events: &Vec<Event>, config: &Config) -> Result<Vec<Action>, Box<dyn Error>> {
// a vector to collect mouse movement events to be able to send them all at once as one MouseMovementEventCollection.
let mut mouse_movement_collection: Vec<RelativeEvent> = Vec::new();
for event in events {
match event {
Event::KeyEvent(key_event) => {
self.on_key_event(key_event, config)?;
()
}
Event::RelativeEvent(relative_event) => {
self.on_relative_event(relative_event, &mut mouse_movement_collection, config)?
}
Event::OtherEvents(event) => self.send_action(Action::InputEvent(*event)),
Event::OverrideTimeout => self.timeout_override()?,
};
}
// if there is at least one mouse movement event, sending all of them as one MouseMovementEventCollection
if mouse_movement_collection.len() > 0 {
self.send_action(Action::MouseMovementEventCollection(mouse_movement_collection));
}
Ok(self.actions.drain(..).collect())
}
// Handle EventType::KEY
fn on_key_event(&mut self, event: &KeyEvent, config: &Config) -> Result<(), Box<dyn Error>> {
fn on_key_event(&mut self, event: &KeyEvent, config: &Config) -> Result<bool, Box<dyn Error>> {
self.application_cache = None; // expire cache
let key = Key::new(event.code());
debug!("=> {}: {:?}", event.value(), &key);
@ -98,6 +120,7 @@ impl EventHandler {
key_values = self.flush_timeout_keys(key_values);
}
let mut send_original_relative_event = false;
// Apply keymap
for (key, value) in key_values.into_iter() {
if config.virtual_modifiers.contains(&key) {
@ -113,8 +136,100 @@ impl EventHandler {
continue;
}
}
// checking if there's a "disguised" key version of a relative event,
// (scancodes equal to and over DISGUISED_EVENT_OFFSETTER are only "disguised" custom events)
// and also if it's the same "key" and value as the one that came in.
if key.code() >= DISGUISED_EVENT_OFFSETTER && (key.code(), value) == (event.code(), event.value()) {
// if it is, setting send_original_relative_event to true to later tell on_relative_event to send the original event.
send_original_relative_event = true;
continue;
}
self.send_key(&key, value);
}
// Using the Ok() to send a boolean to on_relative_event, which will be used to decide whether to send the original relative event.
// (True = send the original relative event, false = don't send it.)
Ok(send_original_relative_event)
}
// Handle EventType::RELATIVE
fn on_relative_event(
&mut self,
event: &RelativeEvent,
mouse_movement_collection: &mut Vec<RelativeEvent>,
config: &Config,
) -> Result<(), Box<dyn Error>> {
// Because a "full" RELATIVE event is only one event,
// it doesn't translate very well into a KEY event (because those have a "press" event and an "unpress" event).
// The solution used here is to send two events for each relative event :
// one for the press "event" and one for the "unpress" event.
// These consts are used because 'RELEASE'/'PRESS' are better than '0'/'1' at indicating a button release/press.
const RELEASE: i32 = 0;
const PRESS: i32 = 1;
// All relative events (except maybe those i haven't found information about (REL_DIAL, REL_MISC and REL_RESERVED))
// can have either a positive value or a negative value.
// A negative value is associated with a different action than the positive value.
// Specifically, negative values are associated with the opposite of the action that would emit a positive value.
// For example, a positive value for a scroll event (REL_WHEEL) comes from an upscroll, while a negative value comes from a downscroll.
let key = match event.value {
// Positive and negative values can be really high because the events are relative,
// so their values are variable, meaning we have to match with all positive/negative values.
// Not sure if there is any relative event with a fixed value.
1..=i32::MAX => (event.code * 2) + DISGUISED_EVENT_OFFSETTER,
// While some events may appear to have a fixed value,
// events like scrolling will have higher values with more "agressive" scrolling.
// *2 to create a "gap" between events (since multiplying by two means that all resulting values will be even, the odd numbers between will be missing),
// +1 if the event has a negative value to "fill" the gap (since adding one shifts the parity from even to odd),
// and adding DISGUISED_EVENT_OFFSETTER,
// so that the total as a keycode corresponds to one of the custom aliases that
// are created in config::key::parse_key specifically for these "disguised" relative events.
i32::MIN..=-1 => (event.code * 2) + 1 + DISGUISED_EVENT_OFFSETTER,
0 => {
println!("This event has a value of zero : {:?}", event);
// A value of zero would be unexpected for a relative event,
// since changing something by zero is kinda useless.
// Just in case it can actually happen (and also because match arms need the same output type),
// we'll just act like the value of the event was a positive.
(event.code * 2) + DISGUISED_EVENT_OFFSETTER
}
};
// Sending a RELATIVE event "disguised" as a "fake" KEY event press to on_key_event.
match self.on_key_event(&KeyEvent::new_with(key, PRESS), config)? {
// the boolean value is from a variable at the end of on_key_event from event_handler,
// used to indicate whether the event got through unchanged.
true => {
// Sending the original RELATIVE event if the "press" version of the "fake" KEY event got through on_key_event unchanged.
let action = RelativeEvent::new_with(event.code, event.value);
if event.code <= 2 {
// If it's a mouse movement event (event.code <= 2),
// it is added to mouse_movement_collection to later be sent alongside all other mouse movement event,
// as a single MouseMovementEventCollection instead of potentially multiple RelativeEvent .
// Mouse movement events need to be sent all at once because they would otherwise be separated by a synchronization event¹,
// which the OS handles differently from two unseparated mouse movement events.
// For example, a REL_X event², followed by a SYNCHRONIZATION event, followed by a REL_Y event³, followed by a SYNCHRONIZATION event,
// will move the mouse cursor by a different amount than a REL_X followed by a REL_Y followed by a SYNCHRONIZATION.
// ¹Because Xremap usually sends events one by one through evdev's "emit" function, which adds a synchronization event during each call.
// ²Mouse movement along the X (horizontal) axis.
// ³Mouse movement along the Y (vertical) axis.
mouse_movement_collection.push(action);
} else {
// Otherwise, the event is directly sent as a relative event, to be dispatched like other events.
self.send_action(Action::RelativeEvent(action));
}
}
false => {}
}
// Sending the "unpressed" version of the "fake" KEY event.
self.on_key_event(&KeyEvent::new_with(key, RELEASE), config)?;
Ok(())
}
@ -140,7 +255,7 @@ impl EventHandler {
}
fn send_key(&mut self, key: &Key, value: i32) {
//let event = InputEvent::new(EventType::KEY, key.code(), value);
// let event = InputEvent::new(EventType::KEY, key.code(), value);
let event = KeyEvent::new_with(key.code(), value);
self.send_action(Action::KeyEvent(event));
}
@ -570,7 +685,7 @@ lazy_static! {
];
}
//---
// ---
fn is_pressed(value: i32) -> bool {
value == PRESS || value == REPEAT
@ -581,7 +696,7 @@ static RELEASE: i32 = 0;
static PRESS: i32 = 1;
static REPEAT: i32 = 2;
//---
// ---
#[derive(Debug)]
struct MultiPurposeKeyState {

@ -1,7 +1,6 @@
use crate::config::Config;
use crate::device::{device_watcher, get_input_devices, output_device};
use crate::event_handler::EventHandler;
use action::Action;
use action_dispatcher::ActionDispatcher;
use anyhow::{anyhow, bail, Context};
use clap::{AppSettings, ArgEnum, IntoApp, Parser};
@ -136,7 +135,9 @@ fn main() -> anyhow::Result<()> {
match 'event_loop: loop {
let readable_fds = select_readable(input_devices.values(), &watchers, timer_fd)?;
if readable_fds.contains(timer_fd) {
if let Err(error) = handle_event(&mut handler, &mut dispatcher, &mut config, Event::OverrideTimeout) {
if let Err(error) =
handle_events(&mut handler, &mut dispatcher, &mut config, vec![Event::OverrideTimeout])
{
println!("Error on remap timeout: {error}")
}
}
@ -224,28 +225,28 @@ fn handle_input_events(
Err((Some(ENODEV), _)) => Ok(false),
Err((_, error)) => Err(error).context("Error fetching input events"),
Ok(events) => {
let mut input_events: Vec<Event> = Vec::new();
for event in events {
if let Some(event) = Event::new(event) {
handle_event(handler, dispatcher, config, event)?;
} else {
dispatcher.on_action(Action::InputEvent(event))?;
}
let event = Event::new(event);
input_events.push(event);
}
handle_events(handler, dispatcher, config, input_events)?;
Ok(true)
}
}
}
// Handle an Event with EventHandler, and dispatch Actions with ActionDispatcher
fn handle_event(
fn handle_events(
handler: &mut EventHandler,
dispatcher: &mut ActionDispatcher,
config: &mut Config,
event: Event,
events: Vec<Event>,
) -> anyhow::Result<()> {
let actions = handler
.on_event(&event, config)
.map_err(|e| anyhow!("Failed handling {event:?}:\n {e:?}"))?;
.on_events(&events, config)
.map_err(|e| anyhow!("Failed handling {events:?}:\n {e:?}"))?;
for action in actions {
dispatcher.on_action(action)?;
}

@ -1,3 +1,5 @@
use evdev::EventType;
use evdev::InputEvent;
use evdev::Key;
use indoc::indoc;
use nix::sys::timerfd::{ClockId, TimerFd, TimerFlags};
@ -7,7 +9,7 @@ use crate::client::{Client, WMClient};
use crate::{
action::Action,
config::{keymap::build_keymap_table, Config},
event::{Event, KeyEvent, KeyValue},
event::{Event, KeyEvent, KeyValue, RelativeEvent},
event_handler::EventHandler,
};
@ -48,6 +50,198 @@ fn test_basic_modmap() {
)
}
/* Table to see which scancodes/custom key events correspond to which relative events
Original RELATIVE event | scancode | Custom keyname if | Info
| | positive value (+) | negative value (-) |
REL_X | 0 | XRIGHTCURSOR | XLEFTCURSOR | Cursor right and left
REL_Y | 1 | XDOWNCURSOR | XUPCURSOR | Cursor down and up
REL_Z | 2 | XREL_Z_AXIS_1 | XREL_Z_AXIS_2 | Cursor... forward and backwards?
REL_RX | 3 | XREL_RX_AXIS_1 | XREL_RX_AXIS_2 | Horizontally rotative cursor movement?
REL_RY | 4 | XREL_RY_AXIS_1 | XREL_RY_AXIS_2 | Vertical rotative cursor movement?
REL_RZ | 5 | XREL_RZ_AXIS_1 | XREL_RZ_AXIS_2 | "Whatever the third dimensional axis is called" rotative cursor movement?
REL_HWHEEL | 6 | XRIGHTSCROLL | XLEFTSCROLL | Rightscroll and leftscroll
REL_DIAL | 7 | XREL_DIAL_1 | XREL_DIAL_2 | ???
REL_WHEEL | 8 | XUPSCROLL | XDOWNSCROLL | Upscroll and downscroll
REL_MISC | 9 | XREL_MISC_1 | XREL_MISC_2 | Something?
REL_RESERVED | 10 | XREL_RESERVED_1 | XREL_RESERVED_2 | Something?
REL_WHEEL_HI_RES | 11 | XHIRES_UPSCROLL | XHIRES_DOWNSCROLL | High resolution downscroll and upscroll, sent just after their non-high resolution version
REL_HWHEEL_HI_RES | 12 | XHIRES_RIGHTSCROLL | XHIRES_LEFTSCROLL | High resolution rightcroll and leftscroll, sent just after their non-high resolution version
*/
const _POSITIVE: i32 = 1;
const _NEGATIVE: i32 = -1;
const _REL_X: u16 = 0;
const _REL_Y: u16 = 1;
const _REL_Z: u16 = 2;
const _REL_RX: u16 = 3;
const _REL_RY: u16 = 4;
const _REL_RZ: u16 = 5;
const _REL_HWHEEL: u16 = 6;
const _REL_DIAL: u16 = 7;
const _REL_WHEEL: u16 = 8;
const _REL_MISC: u16 = 9;
const _REL_RESERVED: u16 = 10;
const _REL_WHEEL_HI_RES: u16 = 11;
const _REL_HWHEEL_HI_RES: u16 = 12;
#[test]
fn test_relative_events() {
assert_actions(
indoc! {"
modmap:
- remap:
XRIGHTCURSOR: b
"},
vec![Event::RelativeEvent(RelativeEvent::new_with(_REL_X, _POSITIVE))],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Release)),
],
)
}
#[test]
fn verify_disguised_relative_events() {
use crate::event_handler::DISGUISED_EVENT_OFFSETTER;
// Verifies that the event offsetter used to "disguise" relative events into key event
// is a bigger number than the biggest one a scancode had at the time of writing this (26 december 2022)
assert!(0x2e7 < DISGUISED_EVENT_OFFSETTER);
// and that it's not big enough that one of the "disguised" events's scancode would overflow.
// (the largest of those events is equal to DISGUISED_EVENT_OFFSETTER + 25)
assert!(DISGUISED_EVENT_OFFSETTER <= u16::MAX - 25)
}
#[test]
fn test_mouse_movement_event_accumulation() {
// Tests that mouse movement events correctly get collected to be sent as one MouseMovementEventCollection,
// which is necessary to avoid separating mouse movement events with synchronization events,
// because such a separation would cause a bug with cursor movement.
// Please refer to test_cursor_behavior_1 and test_cursor_behavior_2 for more information on said bug.
assert_actions(
indoc! {""},
vec![
Event::RelativeEvent(RelativeEvent::new_with(_REL_X, _POSITIVE)),
Event::RelativeEvent(RelativeEvent::new_with(_REL_Y, _POSITIVE)),
],
vec![Action::MouseMovementEventCollection(vec![
RelativeEvent::new_with(_REL_X, _POSITIVE),
RelativeEvent::new_with(_REL_Y, _POSITIVE),
])],
)
}
#[test]
#[ignore]
// The OS interprets a REL_X event¹ combined with a REL_Y event² differently if they are separated by synchronization event.
// This test and test_cursor_behavior_2 are meant to be run to demonstrate that fact.
// ¹Mouse movement along the X (horizontal) axis.
// ²Mouse movement along the Y (vertical) axis.
// The only difference between test_cursor_behavior_1 and test_cursor_behavior_2 is that
// test_cursor_behavior_1 adds a synchronization event between REL_X and REL_Y events that would not normally be there.
// In other words, test_cursor_behavior_2 represents what would occur without Xremap intervention.
// Here's how to proceed :
// 1 - Move your mouse cursor to the bottom left of your screen.
// 2 - either run this test with sudo privileges or while your environnment is properly set up (https:// github.com/k0kubun/xremap#running-xremap-without-sudo),
// so that your keyboard and/or mouse may be captured.
// 3 - Press any button (don't move the mouse).
// 4 - Note where the cursor ended up.
// 5 - Repeat steps 1 through 4 for test_cursor_behavior_2.
// 6 - Notice that the mouse cursor often ends up in a different position than when running test_cursor_behavior_1.
//
// Notes :
// - Because emitting an event automatcially adds a synchronization event afterwards (see https:// github.com/emberian/evdev/blob/1d020f11b283b0648427a2844b6b980f1a268221/src/uinput.rs#L167),
// Mouse movement events should be batched together when emitted,
// to avoid separating them with a synchronization event.
//
// - Because a mouse will only ever send a maximum of one REL_X and one REL_Y (and maybe one REL_Z for 3D mice?) at once,
// the only point where a synchronization event can be added where it shouldn't by Xremap is between those events,
// meaning this bug is exclusive to diagonal mouse movement.
//
// - The call to std::thread::sleep for five milliseconds is meant to emulate
// the interval between events from a mouse with a frequency of ~200 Hz.
// A lower time interval between events (which would correspond to a mouse with a higher frequency)
// would cause the difference between test_cursor_behavior_1 and test_cursor_behavior_2 to become less noticeable.
// Conversely, a higher time interval would make the difference more noticeable.
//
fn test_cursor_behavior_1() {
use crate::device::InputDevice;
use crate::device::{get_input_devices, output_device};
// Setup to be able to send events
let mut input_devices = match get_input_devices(&[String::from("/dev/input/event25")], &[], true, false) {
Ok(input_devices) => input_devices,
Err(e) => panic!("Failed to prepare input devices: {}", e),
};
let mut output_device = match output_device(input_devices.values().next().map(InputDevice::bus_type)) {
Ok(output_device) => output_device,
Err(e) => panic!("Failed to prepare an output device: {}", e),
};
for input_device in input_devices.values_mut() {
let _unused = input_device.fetch_events().unwrap();
}
// Looping 400 times amplifies the difference between test_cursor_behavior_1 and test_cursor_behavior_2 to visible levels.
for _ in 0..400 {
output_device
.emit(&[
InputEvent::new_now(EventType::RELATIVE, _REL_X, _POSITIVE),
//
// This line is the only difference between test_cursor_behavior_1 and test_cursor_behavior_2.
InputEvent::new(EventType::SYNCHRONIZATION, 0, 0),
//
InputEvent::new_now(EventType::RELATIVE, _REL_Y, _NEGATIVE),
])
.unwrap();
// Creating a time interval between mouse movement events to simulate a mouse with a frequency of ~200 Hz.
// The smaller the time interval, the smaller the difference between test_cursor_behavior_1 and test_cursor_behavior_2.
std::thread::sleep(Duration::from_millis(5));
}
}
#[test]
#[ignore]
// The OS interprets a REL_X event combined with a REL_Y event differently if they are separated by synchronization event.
// This test and test_cursor_behavior_1 are meant to be run to demonstrate that fact.
// Please refer to the comment above test_cursor_behavior_1 for information on how to run these tests.
fn test_cursor_behavior_2() {
use crate::device::InputDevice;
use crate::device::{get_input_devices, output_device};
// Setup to be able to send events
let mut input_devices = match get_input_devices(&[String::from("/dev/input/event25")], &[], true, false) {
Ok(input_devices) => input_devices,
Err(e) => panic!("Failed to prepare input devices: {}", e),
};
let mut output_device = match output_device(input_devices.values().next().map(InputDevice::bus_type)) {
Ok(output_device) => output_device,
Err(e) => panic!("Failed to prepare an output device: {}", e),
};
for input_device in input_devices.values_mut() {
let _unused = input_device.fetch_events().unwrap();
}
// Looping 400 times amplifies the difference between test_cursor_behavior_1 and test_cursor_behavior_2 to visible levels.
for _ in 0..400 {
output_device
.emit(&[
InputEvent::new_now(EventType::RELATIVE, _REL_X, _POSITIVE),
InputEvent::new_now(EventType::RELATIVE, _REL_Y, _NEGATIVE),
])
.unwrap();
// Creating a time interval between mouse movement events to simulate a mouse with a frequency of ~200 Hz.
// The smaller the time interval, the smaller the difference between test_cursor_behavior_1 and test_cursor_behavior_2.
std::thread::sleep(Duration::from_millis(5));
}
}
#[test]
fn test_interleave_modifiers() {
assert_actions(
@ -389,8 +583,8 @@ fn assert_actions_with_current_application(
WMClient::new("static", Box::new(StaticClient { current_application })),
);
let mut actual: Vec<Action> = vec![];
for event in &events {
actual.append(&mut event_handler.on_event(event, &config).unwrap());
}
actual.append(&mut event_handler.on_events(&events, &config).unwrap());
assert_eq!(format!("{:?}", actions), format!("{:?}", actual));
}

Loading…
Cancel
Save