use crate::action::Action;
use crate::event::Event;
use crate::event::{KeyEvent, KeyValue};
use crate::tests::{assert_actions, get_input_device_info, EventHandlerForTest};
use evdev::KeyCode as Key;
use indoc::indoc;
use std::thread::sleep;
use std::time::Duration;
#[test]
fn test_multipurpose_is_not_emitted_on_press() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: A
alone: B
"},
vec![Event::KeyEvent(
get_input_device_info(),
KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press),
)],
vec![],
);
}
#[test]
fn test_multipurpose_is_interrupted() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: SHIFT_L
alone: CAPSLOCK
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Release)),
],
);
}
#[test]
fn test_multipurpose_released_without_interuption() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: SHIFT_L
alone: CAPSLOCK
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
)
}
#[test]
fn test_multipurpose_is_repeated_in_hold_preferred_state() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: SHIFT_L
alone: CAPSLOCK
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Repeat)),
],
vec![],
)
}
#[test]
fn test_multipurpose_released_after_timeout() {
let mut handler = EventHandlerForTest::new(indoc! {"
modmap:
- remap:
CAPSLOCK:
held: SHIFT_L
alone: CAPSLOCK
alone_timeout_millis: 10
"});
handler.assert(
vec![Event::KeyEvent(
get_input_device_info(),
KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press),
)],
vec![],
);
sleep(Duration::from_millis(20));
handler.assert(
vec![Event::KeyEvent(
get_input_device_info(),
KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release),
)],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Release)),
],
);
}
#[test]
fn test_multipurpose_is_repeated_after_timeout() {
let mut handler = EventHandlerForTest::new(indoc! {"
modmap:
- remap:
CAPSLOCK:
# modifiers will be sorted first/last
held: [A, SHIFT_L]
alone: CAPSLOCK
alone_timeout_millis: 10
"});
handler.assert(
vec![Event::KeyEvent(
get_input_device_info(),
KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press),
)],
vec![],
);
sleep(Duration::from_millis(20));
handler.assert(
vec![Event::KeyEvent(
get_input_device_info(),
KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Repeat),
)],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press)),
],
);
handler.assert(
vec![Event::KeyEvent(
get_input_device_info(),
KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Repeat),
)],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Repeat)),
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Repeat)),
],
);
handler.assert(
vec![Event::KeyEvent(
get_input_device_info(),
KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release),
)],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Release)),
],
);
}
#[test]
fn test_free_hold_does_not_repeat_hold_action_before_decision() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: SHIFT_L
alone: X
free_hold: true
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Repeat)),
],
vec![],
);
}
#[test]
fn test_free_hold_repeats_hold_action_after_decision() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: X
alone: Y
free_hold: true
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Repeat)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Repeat)),
],
);
}
#[test]
fn test_multipurpose_press_all_keys_then_release_all_keys_1() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: [X, Y]
alone: A
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_1, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_1, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_Y, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_1, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_1, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_Y, KeyValue::Release)),
],
);
}
#[test]
fn test_multipurpose_press_all_keys_then_release_all_keys_2() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: [X, Y]
alone: A
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_1, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_2, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_2, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_1, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_Y, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_1, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_2, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_2, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_1, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_Y, KeyValue::Release)),
],
);
}
#[test]
fn test_multipurpose_press_all_keys_then_release_all_keys_3() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: X
alone: [A, B]
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Release)),
],
)
}
#[test]
fn test_key_release_will_not_trigger_held() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: X
alone: Y
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_1, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_1, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_Y, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_Y, KeyValue::Release)),
],
);
}
#[test]
fn test_the_multipurpose_output_is_used_in_keymap_1() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: X
alone: Y
free_hold: true
keymap:
- remap:
X: KEY_1
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_1, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_1, KeyValue::Release)),
Action::Delay(Duration::from_nanos(0)),
Action::Delay(Duration::from_nanos(0)),
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)),
],
);
}
#[test]
fn test_the_multipurpose_output_is_used_in_keymap_2() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: X
alone: Y
free_hold: true
keymap:
- remap:
Y: KEY_1
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_1, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_1, KeyValue::Release)),
Action::Delay(Duration::from_nanos(0)),
Action::Delay(Duration::from_nanos(0)),
Action::KeyEvent(KeyEvent::new(Key::KEY_Y, KeyValue::Release)),
],
);
}
#[test]
fn test_the_multipurpose_output_is_used_in_keymap_3() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: X
alone: Y
free_hold: true
keymap:
- remap:
Y: KEY_1
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_1, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_1, KeyValue::Release)),
Action::Delay(Duration::from_nanos(0)),
Action::Delay(Duration::from_nanos(0)),
Action::KeyEvent(KeyEvent::new(Key::KEY_Y, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Release)),
],
);
}
#[test]
fn test_the_multipurpose_output_is_used_in_keymap_4() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: X
alone: Y
free_hold: true
keymap:
- remap:
Y: KEY_1
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_1, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_1, KeyValue::Release)),
Action::Delay(Duration::from_nanos(0)),
Action::Delay(Duration::from_nanos(0)),
Action::KeyEvent(KeyEvent::new(Key::KEY_Y, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)),
],
);
}
#[test]
fn test_alone_is_not_optional() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: X
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
);
}
#[test]
fn test_held_is_not_optional() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
alone: X
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
);
}
#[test]
fn test_output_key_used_as_trigger() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: X
alone: Y
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)),
],
);
}
#[test]
fn test_modifiers_are_sorted_first_and_last_held() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: [X, Control_L, Y]
alone: A
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_Y, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_Y, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)),
],
);
}
#[test]
fn test_modifiers_are_sorted_first_and_last_alone() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: A
alone: [X, Control_L, Y]
"},
vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release)),
],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_Y, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_Y, KeyValue::Release)),
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)),
],
);
}
#[test]
fn test_spurious_release_multipurpose_key() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: A
alone: B
"},
vec![Event::KeyEvent(
get_input_device_info(),
KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release),
)],
vec![Action::KeyEvent(KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Release))],
);
}
#[test]
fn test_spurious_repeat_multipurpose_key() {
assert_actions(
indoc! {"
modmap:
- remap:
CAPSLOCK:
held: A
alone: B
"},
vec![Event::KeyEvent(
get_input_device_info(),
KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Repeat),
)],
vec![Action::KeyEvent(KeyEvent::new(Key::KEY_CAPSLOCK, KeyValue::Repeat))],
);
}