mirror of
https://github.com/k0kubun/xremap
synced 2024-11-02 03:40:25 +00:00
Support multi-purpose keys in modmap
This commit is contained in:
parent
2557edb190
commit
091900500d
14
README.md
14
README.md
@ -15,7 +15,8 @@
|
|||||||
|
|
||||||
* You can remap any keys, e.g. Ctrl or CapsLock.
|
* You can remap any keys, e.g. Ctrl or CapsLock.
|
||||||
* You can remap any key combination to another, even to a key sequence.
|
* You can remap any key combination to another, even to a key sequence.
|
||||||
* You can also remap a key sequence as well. You could do something like Emacs's `C-x C-c`.
|
* You can remap a key sequence as well. You could do something like Emacs's `C-x C-c`.
|
||||||
|
* You can remap a key to two different keys depending on whether it's pressed alone or held.
|
||||||
* Application-specific remapping. Even if it's not supported by your application, xremap can.
|
* Application-specific remapping. Even if it's not supported by your application, xremap can.
|
||||||
|
|
||||||
## Prerequisite
|
## Prerequisite
|
||||||
@ -105,7 +106,12 @@ is supported only in `modmap` since `keymap` handles modifier keys differently.
|
|||||||
modmap:
|
modmap:
|
||||||
- name: Name # Required
|
- name: Name # Required
|
||||||
remap: # Required
|
remap: # Required
|
||||||
KEY_XXX: KEY_YYY
|
KEY_XXX: KEY_YYY # Required
|
||||||
|
# or
|
||||||
|
KEY_XXX:
|
||||||
|
held: KEY_YYY # Required
|
||||||
|
alone: KEY_ZZZ # Required
|
||||||
|
alone_timeout_millis: 1000 # Optional
|
||||||
application: # Optional
|
application: # Optional
|
||||||
not: [Application, ...]
|
not: [Application, ...]
|
||||||
# or
|
# or
|
||||||
@ -116,6 +122,10 @@ For `KEY_XXX` and `KEY_YYY`, use [these names](https://github.com/emberian/evdev
|
|||||||
You can skip `KEY_` and the name is case-insensitive. So `KEY_CAPSLOCK`, `CAPSLOCK`, and `CapsLock` are the same thing.
|
You can skip `KEY_` and the name is case-insensitive. So `KEY_CAPSLOCK`, `CAPSLOCK`, and `CapsLock` are the same thing.
|
||||||
Some [custom aliases](src/config/key.rs) like `SHIFT_R`, `CONTROL_L`, etc. are provided.
|
Some [custom aliases](src/config/key.rs) like `SHIFT_R`, `CONTROL_L`, etc. are provided.
|
||||||
|
|
||||||
|
If you specify a map containing `held` and `alone`, you can use the key for two purposes.
|
||||||
|
The key is considered `alone` if it's pressed and released within `alone_timeout_millis` (default: 1000)
|
||||||
|
before any other key is pressed. Otherwise it's considered `held`.
|
||||||
|
|
||||||
### keymap
|
### keymap
|
||||||
|
|
||||||
`keymap` is for remapping a sequence of key combinations to another sequence of key combinations or other actions.
|
`keymap` is for remapping a sequence of key combinations to another sequence of key combinations or other actions.
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
# modmap:
|
modmap:
|
||||||
# - name: Global
|
- name: SandS
|
||||||
# remap:
|
|
||||||
# a: b
|
|
||||||
keymap:
|
|
||||||
- name: Global
|
|
||||||
application:
|
|
||||||
only: Slack
|
|
||||||
remap:
|
remap:
|
||||||
C-i: C-u
|
Space:
|
||||||
|
held: Shift_L
|
||||||
|
alone: Space
|
||||||
|
alone_timeout_millis: 500
|
||||||
|
@ -7,6 +7,7 @@ use serde::de::{MapAccess, Visitor};
|
|||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
|
// Values in `keymap.remap`
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
KeyPress(KeyPress),
|
KeyPress(KeyPress),
|
||||||
@ -70,7 +71,7 @@ impl<'de> Deserialize<'de> for Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serde_error<'de, V, M>(message: &str) -> Result<V, M::Error>
|
pub fn serde_error<'de, V, M>(message: &str) -> Result<V, M::Error>
|
||||||
where
|
where
|
||||||
M: MapAccess<'de>,
|
M: MapAccess<'de>,
|
||||||
{
|
{
|
||||||
|
102
src/config/key_action.rs
Normal file
102
src/config/key_action.rs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
use crate::config::action::serde_error;
|
||||||
|
use crate::config::key::parse_key;
|
||||||
|
use evdev::Key;
|
||||||
|
use serde::de::{MapAccess, Visitor};
|
||||||
|
use serde::{Deserialize, Deserializer};
|
||||||
|
use std::fmt::Formatter;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
static DEFAULT_ALONE_TIMEOUT_MILLIS: u64 = 1000;
|
||||||
|
|
||||||
|
// Values in `modmap.remap`
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum KeyAction {
|
||||||
|
Key(Key),
|
||||||
|
MultiPurposeKey(MultiPurposeKey),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MultiPurposeKey {
|
||||||
|
pub held: Key,
|
||||||
|
pub alone: Key,
|
||||||
|
pub alone_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for KeyAction {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct KeyActionVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for KeyActionVisitor {
|
||||||
|
type Value = KeyAction;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
|
||||||
|
formatter.write_str("string or map")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
let key = parse_key(value).map_err(serde::de::Error::custom)?;
|
||||||
|
Ok(KeyAction::Key(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
|
||||||
|
where
|
||||||
|
M: MapAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut held: Option<Key> = None;
|
||||||
|
let mut alone: Option<Key> = None;
|
||||||
|
let mut alone_timeout_millis: u64 = DEFAULT_ALONE_TIMEOUT_MILLIS;
|
||||||
|
|
||||||
|
while let Some(key) = map.next_key::<String>()? {
|
||||||
|
match &key[..] {
|
||||||
|
"held" => {
|
||||||
|
let value: String = map.next_value()?;
|
||||||
|
held = Some(parse_key(&value).map_err(serde::de::Error::custom)?)
|
||||||
|
}
|
||||||
|
"alone" => {
|
||||||
|
let value: String = map.next_value()?;
|
||||||
|
alone = Some(parse_key(&value).map_err(serde::de::Error::custom)?)
|
||||||
|
}
|
||||||
|
"alone_timeout_millis" => alone_timeout_millis = map.next_value()?,
|
||||||
|
key => {
|
||||||
|
return serde_error::<Self::Value, M>(&format!(
|
||||||
|
"held, alone, or alone_timeout_ms is expected, but got: {}",
|
||||||
|
key
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let held = match held {
|
||||||
|
Some(held) => held,
|
||||||
|
None => {
|
||||||
|
return serde_error::<Self::Value, M>(
|
||||||
|
"held is not specified in a multi-purpose remap of modmap",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let alone = match alone {
|
||||||
|
Some(alone) => alone,
|
||||||
|
None => {
|
||||||
|
return serde_error::<Self::Value, M>(
|
||||||
|
"alone is not specified in a multi-purpose remap of modmap",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let multi_purpose_key = MultiPurposeKey {
|
||||||
|
held,
|
||||||
|
alone,
|
||||||
|
alone_timeout: Duration::from_millis(alone_timeout_millis),
|
||||||
|
};
|
||||||
|
Ok(KeyAction::MultiPurposeKey(multi_purpose_key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_any(KeyActionVisitor)
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ pub mod action;
|
|||||||
mod actions;
|
mod actions;
|
||||||
pub mod application;
|
pub mod application;
|
||||||
mod key;
|
mod key;
|
||||||
|
pub mod key_action;
|
||||||
pub mod key_press;
|
pub mod key_press;
|
||||||
mod keymap;
|
mod keymap;
|
||||||
mod modmap;
|
mod modmap;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::config::application::Application;
|
use crate::config::application::Application;
|
||||||
use crate::config::key::parse_key;
|
use crate::config::key::parse_key;
|
||||||
|
use crate::config::key_action::KeyAction;
|
||||||
use evdev::Key;
|
use evdev::Key;
|
||||||
use serde::de::{value, Error, MapAccess, Visitor};
|
use serde::de::{value, Error, MapAccess, Visitor};
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
@ -11,18 +12,18 @@ use std::fmt;
|
|||||||
pub struct Modmap {
|
pub struct Modmap {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
#[serde(deserialize_with = "modmap_remap")]
|
#[serde(deserialize_with = "modmap_remap")]
|
||||||
pub remap: HashMap<Key, Key>,
|
pub remap: HashMap<Key, KeyAction>,
|
||||||
pub application: Option<Application>,
|
pub application: Option<Application>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn modmap_remap<'de, D>(deserializer: D) -> Result<HashMap<Key, Key>, D::Error>
|
fn modmap_remap<'de, D>(deserializer: D) -> Result<HashMap<Key, KeyAction>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
struct ModmapRemap;
|
struct ModmapRemap;
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for ModmapRemap {
|
impl<'de> Visitor<'de> for ModmapRemap {
|
||||||
type Value = HashMap<Key, Key>;
|
type Value = HashMap<Key, KeyAction>;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
formatter.write_str("map from string to string")
|
formatter.write_str("map from string to string")
|
||||||
@ -32,13 +33,12 @@ where
|
|||||||
where
|
where
|
||||||
M: MapAccess<'de>,
|
M: MapAccess<'de>,
|
||||||
{
|
{
|
||||||
let remap: HashMap<String, String> = Deserialize::deserialize(value::MapAccessDeserializer::new(map))?;
|
let remap: HashMap<String, KeyAction> = Deserialize::deserialize(value::MapAccessDeserializer::new(map))?;
|
||||||
let mut modmap = HashMap::new();
|
let mut modmap = HashMap::new();
|
||||||
|
|
||||||
for (from, to) in remap.iter() {
|
for (from, to) in remap.into_iter() {
|
||||||
let from_key = parse_key(&from).map_err(M::Error::custom)?;
|
let from_key = parse_key(&from).map_err(M::Error::custom)?;
|
||||||
let to_key = parse_key(&to).map_err(M::Error::custom)?;
|
modmap.insert(from_key, to);
|
||||||
modmap.insert(from_key, to_key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(modmap)
|
Ok(modmap)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::client::{build_client, WMClient};
|
use crate::client::{build_client, WMClient};
|
||||||
use crate::config::action::Action;
|
use crate::config::action::Action;
|
||||||
use crate::config::application::Application;
|
use crate::config::application::Application;
|
||||||
|
use crate::config::key_action::KeyAction;
|
||||||
use crate::config::key_press::{KeyPress, Modifier};
|
use crate::config::key_press::{KeyPress, Modifier};
|
||||||
use crate::Config;
|
use crate::Config;
|
||||||
use evdev::uinput::VirtualDevice;
|
use evdev::uinput::VirtualDevice;
|
||||||
@ -9,10 +10,12 @@ use lazy_static::lazy_static;
|
|||||||
use log::debug;
|
use log::debug;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
pub struct EventHandler {
|
pub struct EventHandler {
|
||||||
device: VirtualDevice,
|
device: VirtualDevice,
|
||||||
wm_client: WMClient,
|
wm_client: WMClient,
|
||||||
|
multi_purpose_keys: HashMap<Key, MultiPurposeKeyState>,
|
||||||
override_remap: Option<HashMap<KeyPress, Vec<Action>>>,
|
override_remap: Option<HashMap<KeyPress, Vec<Action>>>,
|
||||||
application_cache: Option<String>,
|
application_cache: Option<String>,
|
||||||
shift: PressState,
|
shift: PressState,
|
||||||
@ -26,6 +29,7 @@ impl EventHandler {
|
|||||||
EventHandler {
|
EventHandler {
|
||||||
device,
|
device,
|
||||||
wm_client: build_client(),
|
wm_client: build_client(),
|
||||||
|
multi_purpose_keys: HashMap::new(),
|
||||||
override_remap: None,
|
override_remap: None,
|
||||||
application_cache: None,
|
application_cache: None,
|
||||||
shift: PressState::new(false),
|
shift: PressState::new(false),
|
||||||
@ -38,32 +42,31 @@ impl EventHandler {
|
|||||||
// Handle EventType::KEY
|
// Handle EventType::KEY
|
||||||
pub fn on_event(&mut self, event: InputEvent, config: &Config) -> Result<(), Box<dyn Error>> {
|
pub fn on_event(&mut self, event: InputEvent, config: &Config) -> Result<(), Box<dyn Error>> {
|
||||||
self.application_cache = None; // expire cache
|
self.application_cache = None; // expire cache
|
||||||
let mut key = Key::new(event.code());
|
let key = Key::new(event.code());
|
||||||
debug!("=> {}: {:?}", event.value(), &key);
|
debug!("=> {}: {:?}", event.value(), &key);
|
||||||
|
|
||||||
// Apply modmap
|
// Apply modmap
|
||||||
for modmap in &config.modmap {
|
let mut key_values = if let Some(key_action) = self.find_modmap(&config, &key) {
|
||||||
if let Some(modmap_key) = modmap.remap.get(&key) {
|
self.dispatch_keys(key_action, key, event.value())
|
||||||
if let Some(wm_class_matcher) = &modmap.application {
|
} else {
|
||||||
if !self.match_wm_class(wm_class_matcher) {
|
vec![(key, event.value())]
|
||||||
continue;
|
};
|
||||||
}
|
if !self.multi_purpose_keys.is_empty() {
|
||||||
}
|
key_values = self.flush_timeout_keys(key_values);
|
||||||
key = modmap_key.clone();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply keymap
|
// Apply keymap
|
||||||
if MODIFIER_KEYS.contains(&key.code()) {
|
for (key, value) in key_values.into_iter() {
|
||||||
self.update_modifier(key.code(), event.value());
|
if MODIFIER_KEYS.contains(&key.code()) {
|
||||||
} else if let Some(actions) = self.find_keymap(config, &key, event.value()) {
|
self.update_modifier(key.code(), value);
|
||||||
for action in &actions {
|
} else if let Some(actions) = self.find_keymap(config, &key, value) {
|
||||||
self.dispatch_action(action)?;
|
for action in &actions {
|
||||||
|
self.dispatch_action(action)?;
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
return Ok(());
|
self.send_key(&key, value)?;
|
||||||
}
|
}
|
||||||
self.send_key(&key, event.value())?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +82,72 @@ impl EventHandler {
|
|||||||
self.send_event(event)
|
self.send_event(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dispatch_keys(&mut self, key_action: KeyAction, key: Key, value: i32) -> Vec<(Key, i32)> {
|
||||||
|
match key_action {
|
||||||
|
KeyAction::Key(modmap_key) => vec![(modmap_key.clone(), value)],
|
||||||
|
KeyAction::MultiPurposeKey(multi_purpose_key) => {
|
||||||
|
if value == PRESS {
|
||||||
|
self.multi_purpose_keys.insert(
|
||||||
|
key.clone(),
|
||||||
|
MultiPurposeKeyState {
|
||||||
|
held: multi_purpose_key.held,
|
||||||
|
alone: multi_purpose_key.alone,
|
||||||
|
alone_timeout_at: Some(Instant::now() + multi_purpose_key.alone_timeout),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return vec![]; // delay the press
|
||||||
|
} else if value == REPEAT {
|
||||||
|
if let Some(state) = self.multi_purpose_keys.get_mut(&key) {
|
||||||
|
return state.repeat();
|
||||||
|
}
|
||||||
|
} else if value == RELEASE {
|
||||||
|
if let Some(state) = self.multi_purpose_keys.remove(&key) {
|
||||||
|
return state.release();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("unexpected key event value: {}", value);
|
||||||
|
}
|
||||||
|
// fallthrough on state discrepancy
|
||||||
|
vec![(key, value)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush_timeout_keys(&mut self, key_values: Vec<(Key, i32)>) -> Vec<(Key, i32)> {
|
||||||
|
let mut flush = false;
|
||||||
|
for (_, value) in key_values.iter() {
|
||||||
|
if *value == PRESS {
|
||||||
|
flush = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if flush {
|
||||||
|
let mut flushed: Vec<(Key, i32)> = vec![];
|
||||||
|
for (_, state) in self.multi_purpose_keys.iter_mut() {
|
||||||
|
flushed.extend(state.force_held());
|
||||||
|
}
|
||||||
|
flushed.extend(key_values);
|
||||||
|
flushed
|
||||||
|
} else {
|
||||||
|
key_values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_modmap(&mut self, config: &Config, key: &Key) -> Option<KeyAction> {
|
||||||
|
for modmap in &config.modmap {
|
||||||
|
if let Some(key_action) = modmap.remap.get(&key) {
|
||||||
|
if let Some(application_matcher) = &modmap.application {
|
||||||
|
if !self.match_application(application_matcher) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Some(key_action.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn find_keymap(&mut self, config: &Config, key: &Key, value: i32) -> Option<Vec<Action>> {
|
fn find_keymap(&mut self, config: &Config, key: &Key, value: i32) -> Option<Vec<Action>> {
|
||||||
if !is_pressed(value) {
|
if !is_pressed(value) {
|
||||||
return None;
|
return None;
|
||||||
@ -100,8 +169,8 @@ impl EventHandler {
|
|||||||
}
|
}
|
||||||
for keymap in &config.keymap {
|
for keymap in &config.keymap {
|
||||||
if let Some(actions) = keymap.remap.get(&key_press) {
|
if let Some(actions) = keymap.remap.get(&key_press) {
|
||||||
if let Some(wm_class_matcher) = &keymap.application {
|
if let Some(application_matcher) = &keymap.application {
|
||||||
if !self.match_wm_class(wm_class_matcher) {
|
if !self.match_application(application_matcher) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,7 +281,7 @@ impl EventHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_wm_class(&mut self, application_matcher: &Application) -> bool {
|
fn match_application(&mut self, application_matcher: &Application) -> bool {
|
||||||
// Lazily fill the wm_class cache
|
// Lazily fill the wm_class cache
|
||||||
if let None = self.application_cache {
|
if let None = self.application_cache {
|
||||||
match self.wm_client.current_application() {
|
match self.wm_client.current_application() {
|
||||||
@ -255,30 +324,6 @@ impl EventHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct PressState {
|
|
||||||
left: bool,
|
|
||||||
right: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PressState {
|
|
||||||
fn new(pressed: bool) -> PressState {
|
|
||||||
PressState {
|
|
||||||
left: pressed,
|
|
||||||
right: pressed,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_pressed(value: i32) -> bool {
|
|
||||||
value == PRESS || value == REPEAT
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputEvent#value
|
|
||||||
static RELEASE: i32 = 0;
|
|
||||||
static PRESS: i32 = 1;
|
|
||||||
static REPEAT: i32 = 2;
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref MODIFIER_KEYS: [u16; 8] = [
|
static ref MODIFIER_KEYS: [u16; 8] = [
|
||||||
// Shift
|
// Shift
|
||||||
@ -312,3 +357,76 @@ lazy_static! {
|
|||||||
Key::new(Key::KEY_RIGHTMETA.code()),
|
Key::new(Key::KEY_RIGHTMETA.code()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct PressState {
|
||||||
|
left: bool,
|
||||||
|
right: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PressState {
|
||||||
|
fn new(pressed: bool) -> PressState {
|
||||||
|
PressState {
|
||||||
|
left: pressed,
|
||||||
|
right: pressed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_pressed(value: i32) -> bool {
|
||||||
|
value == PRESS || value == REPEAT
|
||||||
|
}
|
||||||
|
|
||||||
|
// InputEvent#value
|
||||||
|
static RELEASE: i32 = 0;
|
||||||
|
static PRESS: i32 = 1;
|
||||||
|
static REPEAT: i32 = 2;
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct MultiPurposeKeyState {
|
||||||
|
held: Key,
|
||||||
|
alone: Key,
|
||||||
|
alone_timeout_at: Option<Instant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MultiPurposeKeyState {
|
||||||
|
fn repeat(&mut self) -> Vec<(Key, i32)> {
|
||||||
|
if let Some(alone_timeout_at) = &self.alone_timeout_at {
|
||||||
|
if Instant::now() < *alone_timeout_at {
|
||||||
|
vec![] // still delay the press
|
||||||
|
} else {
|
||||||
|
self.alone_timeout_at = None; // timeout
|
||||||
|
vec![(self.held.clone(), PRESS)]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec![(self.held.clone(), REPEAT)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn release(&self) -> Vec<(Key, i32)> {
|
||||||
|
if let Some(alone_timeout_at) = &self.alone_timeout_at {
|
||||||
|
if Instant::now() < *alone_timeout_at {
|
||||||
|
// dispatch the delayed press and this release
|
||||||
|
vec![(self.alone.clone(), PRESS), (self.alone.clone(), RELEASE)]
|
||||||
|
} else {
|
||||||
|
// too late. dispatch the held key
|
||||||
|
vec![(self.held.clone(), PRESS), (self.held.clone(), RELEASE)]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec![(self.held.clone(), RELEASE)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn force_held(&mut self) -> Vec<(Key, i32)> {
|
||||||
|
if self.alone_timeout_at.is_some() {
|
||||||
|
self.alone_timeout_at = None;
|
||||||
|
vec![(self.held.clone(), PRESS)]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user