Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add mouse wheel events to defsrc #592

Merged
merged 16 commits into from
Nov 4, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions parser/src/cfg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,34 @@ fn parse_action_atom(ac_span: &Spanned<String>, s: &ParsedState) -> Result<&'sta
s.a.sref(s.a.sref_slice(CustomAction::MouseTap(Btn::Backward))),
)))
}
"mwu" | "mousewheelup" => {
return Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
CustomAction::MWheelNotch {
direction: MWheelDirection::Up,
},
)))))
}
"mwd" | "mousewheeldown" => {
return Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
CustomAction::MWheelNotch {
direction: MWheelDirection::Down,
},
)))))
}
"mwl" | "mousewheelleft" => {
return Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
CustomAction::MWheelNotch {
direction: MWheelDirection::Left,
},
)))))
}
"mwr" | "mousewheelright" => {
return Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
CustomAction::MWheelNotch {
direction: MWheelDirection::Right,
},
)))))
}
"rpt" | "repeat" | "rpt-key" => {
return Ok(s.a.sref(Action::Custom(
s.a.sref(s.a.sref_slice(CustomAction::Repeat)),
Expand Down
19 changes: 19 additions & 0 deletions parser/src/custom_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use anyhow::{anyhow, Result};
use kanata_keyberon::key_code::KeyCode;

use crate::keys::OsCode;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CustomAction {
Cmd(Vec<String>),
Expand All @@ -29,6 +31,9 @@ pub enum CustomAction {
interval: u16,
distance: u16,
},
MWheelNotch {
direction: MWheelDirection,
},
MoveMouse {
direction: MoveDirection,
interval: u16,
Expand Down Expand Up @@ -101,6 +106,20 @@ pub enum MWheelDirection {
Right,
}

impl TryFrom<OsCode> for MWheelDirection {
type Error = ();
fn try_from(value: OsCode) -> Result<Self, Self::Error> {
use OsCode::*;
Ok(match value {
MouseWheelUp | MouseWheelUpHiRes => MWheelDirection::Up,
MouseWheelDown | MouseWheelDownHiRes => MWheelDirection::Down,
MouseWheelLeft | MouseWheelLeftHiRes => MWheelDirection::Left,
MouseWheelRight | MouseWheelRightHiRes => MWheelDirection::Right,
_ => return Err(()),
})
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MoveDirection {
Up,
Expand Down
25 changes: 25 additions & 0 deletions parser/src/keys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,16 @@ pub fn str_to_oscode(s: &str) -> Option<OsCode> {
"mfwd" | "mouseforward" => OsCode::BTN_EXTRA,
"mbck" | "mousebackward" => OsCode::BTN_SIDE,

// NOTE: these are linux-only right now due to missing implementation for windows
#[cfg(any(target_os = "linux", target_os = "unknown"))]
"mwu" | "mousewheelup" => OsCode::MouseWheelUp,
#[cfg(any(target_os = "linux", target_os = "unknown"))]
"mwd" | "mousewheeldown" => OsCode::MouseWheelDown,
#[cfg(any(target_os = "linux", target_os = "unknown"))]
"mwl" | "mousewheelleft" => OsCode::MouseWheelLeft,
#[cfg(any(target_os = "linux", target_os = "unknown"))]
"mwr" | "mousewheelright" => OsCode::MouseWheelRight,

"hmpg" | "homepage" => OsCode::KEY_HOMEPAGE,
"mdia" | "media" => OsCode::KEY_MEDIA,
"mail" => OsCode::KEY_MAIL,
Expand Down Expand Up @@ -1018,6 +1028,21 @@ pub enum OsCode {
BTN_TRIGGER_HAPPY39 = 742,
BTN_TRIGGER_HAPPY40 = 743,
BTN_MAX = 744,

// Mouse wheel events are not a part of EV_KEY, so they technically
// shouldn't be there, but they're still there, because this way
// it's easier to implement allowing to add them to defsrc without
// making tons of changes all over the codebase.
MouseWheelUp = 745,
MouseWheelDown = 746,
MouseWheelLeft = 747,
MouseWheelRight = 748,

MouseWheelUpHiRes = 749,
MouseWheelDownHiRes = 750,
MouseWheelLeftHiRes = 751,
MouseWheelRightHiRes = 752,

KEY_MAX = 767,
}

Expand Down
87 changes: 77 additions & 10 deletions src/kanata/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl Kanata {
let key_event = match KeyEvent::try_from(in_event) {
Ok(ev) => ev,
_ => {
// Pass-through non-key events
// Pass-through non-key and non-scroll events
let mut kanata = kanata.lock();
kanata
.kbd_out
Expand All @@ -51,15 +51,82 @@ impl Kanata {

check_for_exit(&key_event);

// Check if this keycode is mapped in the configuration. If it hasn't been mapped, send
// it immediately.
if !MAPPED_KEYS.lock().contains(&key_event.code) {
let mut kanata = kanata.lock();
kanata
.kbd_out
.write_key(key_event.code, key_event.value)
.map_err(|e| anyhow!("failed write key: {}", e))?;
continue;
let KeyEvent { code, value } = key_event;

if value == KeyValue::Tap {
// Scroll event for sure. Only scroll events produce Tap.

let direction: MWheelDirection = code.try_into().unwrap();

match code
.try_into()
.expect("scroll event OsCode should not fail this")
{
ScrollEventKind::Standard => {
if kanata.lock().scroll_wheel_mapped {
if MAPPED_KEYS.lock().contains(&code) {
// Send this event to processing loop.
} else {
// We can't simply passthough with `write_raw`, because hi-res events will not
// be passed-through when scroll_wheel_mapped==true. If we just used
// `write_raw` here, some of the scrolls issued by kanata would be
// REL_WHEEL_HI_RES + REL_HWHEEL and some just REL_HWHEEL and an issue
// like this one would happen: https://github.com/jtroo/kanata/issues/395
//
// So to fix this case, we need to use `scroll`
// which will also send hi-res scrolls along normal scrolls.

let scroll_distance = in_event.value().unsigned_abs() as u16;

let mut kanata = kanata.lock();
kanata
.kbd_out
.scroll(
direction,
scroll_distance * HI_RES_SCROLL_UNITS_IN_LO_RES,
)
.map_err(|e| anyhow!("failed write: {}", e))?;
continue;
}
} else {
// Passthrough if none of the scroll wheel events are mapped
// in the configuration.
let mut kanata = kanata.lock();
kanata
.kbd_out
.write_raw(in_event)
.map_err(|e| anyhow!("failed write: {}", e))?;
continue;
}
}
ScrollEventKind::HiRes => {
// Don't passthrough hi-res mouse wheel events when scroll wheel is remapped,
if kanata.lock().scroll_wheel_mapped {
continue;
}
// Passthrough if none of the scroll wheel events are mapped
// in the configuration.
let mut kanata = kanata.lock();
kanata
.kbd_out
.write_raw(in_event)
.map_err(|e| anyhow!("failed write: {}", e))?;
continue;
}
}
} else {
// Handle normal keypresses.

// Check if this keycode is mapped in the configuration.
// If it hasn't been mapped, send it immediately.
if !MAPPED_KEYS.lock().contains(&code) {
let mut kanata = kanata.lock();
kanata
.kbd_out
.write_raw(in_event)
.map_err(|e| anyhow!("failed write: {}", e))?;
continue;
};
}

// Send key events to the processing loop
Expand Down
30 changes: 25 additions & 5 deletions src/kanata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ pub struct Kanata {
/// gets stored in this buffer and if the next movemouse action is opposite axis
/// than the one stored in the buffer, both events are outputted at the same time.
movemouse_buffer: Option<(Axis, CalculatedMouseMove)>,
scroll_wheel_mapped: bool,
}

#[derive(PartialEq, Clone, Copy)]
Expand Down Expand Up @@ -333,6 +334,11 @@ impl Kanata {
.map(|s| !FALSE_VALUES.contains(&s.to_lowercase().as_str()))
.unwrap_or(true);

let scroll_wheel_mapped = cfg.mapped_keys.contains(&OsCode::MouseWheelUp)
|| cfg.mapped_keys.contains(&OsCode::MouseWheelDown)
|| cfg.mapped_keys.contains(&OsCode::MouseWheelLeft)
|| cfg.mapped_keys.contains(&OsCode::MouseWheelRight);

*MAPPED_KEYS.lock() = cfg.mapped_keys;

Ok(Self {
Expand Down Expand Up @@ -392,6 +398,7 @@ impl Kanata {
waiting_for_idle: HashSet::default(),
ticks_since_idle: 0,
movemouse_buffer: None,
scroll_wheel_mapped,
})
}

Expand Down Expand Up @@ -436,14 +443,18 @@ impl Kanata {
.get("movemouse-inherit-accel-state")
.map(|s| TRUE_VALUES.contains(&s.to_lowercase().as_str()))
.unwrap_or_default();
self.scroll_wheel_mapped = cfg.mapped_keys.contains(&OsCode::MouseWheelUp)
|| cfg.mapped_keys.contains(&OsCode::MouseWheelDown)
|| cfg.mapped_keys.contains(&OsCode::MouseWheelLeft)
|| cfg.mapped_keys.contains(&OsCode::MouseWheelRight);
*MAPPED_KEYS.lock() = cfg.mapped_keys;
Kanata::set_repeat_rate(&cfg.items)?;
log::info!("Live reload successful");
Ok(())
}

/// Update keyberon layout state for press/release, handle repeat separately
fn handle_key_event(&mut self, event: &KeyEvent) -> Result<()> {
fn handle_input_event(&mut self, event: &KeyEvent) -> Result<()> {
log::debug!("process recv ev {event:?}");
let evc: u16 = event.code.into();
self.ticks_since_idle = 0;
Expand All @@ -466,6 +477,11 @@ impl Kanata {
let ret = self.handle_repeat(event);
return ret;
}
KeyValue::Tap => {
self.layout.bm().event(Event::Press(0, evc));
self.layout.bm().event(Event::Release(0, evc));
return Ok(());
}
};
self.layout.bm().event(kbrn_ev);
Ok(())
Expand Down Expand Up @@ -990,6 +1006,10 @@ impl Kanata {
})
}
},
CustomAction::MWheelNotch { direction } => {
self.kbd_out
.scroll(*direction, HI_RES_SCROLL_UNITS_IN_LO_RES)?;
}
CustomAction::MoveMouse {
direction,
interval,
Expand Down Expand Up @@ -1600,8 +1620,8 @@ impl Kanata {
// are not cleared.
if (now - k.last_tick) > time::Duration::from_secs(60) {
log::debug!(
"clearing keyberon normal key states due to blocking for a while"
);
"clearing keyberon normal key states due to blocking for a while"
);
k.layout.bm().states.retain(|s| {
!matches!(
s,
Expand All @@ -1627,7 +1647,7 @@ impl Kanata {
#[cfg(feature = "perf_logging")]
let start = std::time::Instant::now();

if let Err(e) = k.handle_key_event(&kev) {
if let Err(e) = k.handle_input_event(&kev) {
break e;
}

Expand Down Expand Up @@ -1662,7 +1682,7 @@ impl Kanata {
#[cfg(feature = "perf_logging")]
let start = std::time::Instant::now();

if let Err(e) = k.handle_key_event(&kev) {
if let Err(e) = k.handle_input_event(&kev) {
break e;
}

Expand Down
32 changes: 31 additions & 1 deletion src/kanata/windows/interception.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,14 @@ impl Kanata {
};
KeyEvent { code, value }
}
ic::Stroke::Mouse { state, .. } => {
ic::Stroke::Mouse { state, rolling, .. } => {
if let Some(hwid) = mouse_to_intercept_hwid {
log::trace!("checking mouse stroke {:?}", strokes[i]);
if let Some(event) = mouse_state_to_event(
dev,
&hwid,
state,
rolling,
&intrcptn,
&mut is_dev_interceptable,
) {
Expand Down Expand Up @@ -123,6 +124,7 @@ fn mouse_state_to_event(
input_dev: ic::Device,
allowed_hwid: &[u8; HWID_ARR_SZ],
state: ic::MouseState,
rolling: i16,
intrcptn: &ic::Interception,
is_dev_interceptable: &mut HashMap<ic::Device, bool>,
) -> Option<KeyEvent> {
Expand Down Expand Up @@ -191,6 +193,34 @@ fn mouse_state_to_event(
code: OsCode::BTN_EXTRA,
value: KeyValue::Release,
})
} else if state.contains(ic::MouseState::WHEEL) {
let osc = if rolling >= 0 {
OsCode::MouseWheelUp
} else {
OsCode::MouseWheelDown
};
if !MAPPED_KEYS.lock().contains(&osc) {
Some(KeyEvent {
code: osc,
value: KeyValue::Tap,
})
} else {
None
}
} else if state.contains(ic::MouseState::HWHEEL) {
let osc = if rolling >= 0 {
OsCode::MouseWheelRight
} else {
OsCode::MouseWheelLeft
};
if !MAPPED_KEYS.lock().contains(&osc) {
Some(KeyEvent {
code: osc,
value: KeyValue::Tap,
})
} else {
None
}
} else {
None
}
Expand Down
Loading