use crate::{
core::{State, Xid},
pure::geometry::Point,
x::XConn,
Error, Result,
};
#[cfg(feature = "keysyms")]
use penrose_keysyms::XKeySym;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, convert::TryFrom, fmt, process::Command};
use strum::{EnumIter, IntoEnumIterator};
use tracing::trace;
pub fn keycodes_from_xmodmap() -> Result<HashMap<String, u8>> {
let output = Command::new("xmodmap").arg("-pke").output()?;
let m = String::from_utf8(output.stdout)?
.lines()
.flat_map(|l| {
let mut words = l.split_whitespace(); let key_code: u8 = match words.nth(1) {
Some(word) => match word.parse() {
Ok(val) => val,
Err(e) => panic!("{}", e),
},
None => panic!("unexpected output format from xmodmap -pke"),
};
words.skip(1).map(move |name| (name.into(), key_code))
})
.collect();
Ok(m)
}
fn parse_binding(pattern: &str, known_codes: &HashMap<String, u8>) -> Result<KeyCode> {
let mut parts: Vec<&str> = pattern.split('-').collect();
let name = parts.remove(parts.len() - 1);
match known_codes.get(name) {
Some(code) => {
let mask = parts
.iter()
.map(|&s| ModifierKey::try_from(s))
.try_fold(0, |acc, v| v.map(|inner| acc | u16::from(inner)))?;
trace!(?pattern, mask, code, "parsed keybinding");
Ok(KeyCode { mask, code: *code })
}
None => Err(Error::UnknownKeyName {
name: name.to_owned(),
}),
}
}
pub fn parse_keybindings_with_xmodmap<S, X>(
str_bindings: HashMap<S, Box<dyn KeyEventHandler<X>>>,
) -> Result<KeyBindings<X>>
where
S: AsRef<str>,
X: XConn,
{
let m = keycodes_from_xmodmap()?;
str_bindings
.into_iter()
.map(|(s, v)| parse_binding(s.as_ref(), &m).map(|k| (k, v)))
.collect()
}
pub trait KeyEventHandler<X>
where
X: XConn,
{
fn call(&mut self, state: &mut State<X>, x: &X) -> Result<()>;
}
impl<X: XConn> fmt::Debug for Box<dyn KeyEventHandler<X>> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("KeyEventHandler").finish()
}
}
impl<F, X> KeyEventHandler<X> for F
where
F: FnMut(&mut State<X>, &X) -> Result<()>,
X: XConn,
{
fn call(&mut self, state: &mut State<X>, x: &X) -> Result<()> {
(self)(state, x)
}
}
pub type KeyBindings<X> = HashMap<KeyCode, Box<dyn KeyEventHandler<X>>>;
pub trait MouseEventHandler<X>
where
X: XConn,
{
fn on_mouse_event(&mut self, evt: &MouseEvent, state: &mut State<X>, x: &X) -> Result<()>;
fn on_motion(&mut self, evt: &MotionNotifyEvent, state: &mut State<X>, x: &X) -> Result<()>;
}
impl<X: XConn> fmt::Debug for Box<dyn MouseEventHandler<X>> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MouseEventHandler").finish()
}
}
impl<F, X> MouseEventHandler<X> for F
where
F: FnMut(&mut State<X>, &X) -> Result<()>,
X: XConn,
{
fn on_mouse_event(&mut self, evt: &MouseEvent, state: &mut State<X>, x: &X) -> Result<()> {
if evt.kind == MouseEventKind::Press {
(self)(state, x)
} else {
Ok(())
}
}
fn on_motion(&mut self, _: &MotionNotifyEvent, _: &mut State<X>, _: &X) -> Result<()> {
Ok(())
}
}
pub fn click_handler<X: XConn + 'static>(
kh: Box<dyn KeyEventHandler<X>>,
) -> Box<dyn MouseEventHandler<X>> {
Box::new(MouseWrapper { inner: kh })
}
struct MouseWrapper<X: XConn> {
inner: Box<dyn KeyEventHandler<X>>,
}
impl<X: XConn> MouseEventHandler<X> for MouseWrapper<X> {
fn on_mouse_event(&mut self, evt: &MouseEvent, state: &mut State<X>, x: &X) -> Result<()> {
if evt.kind == MouseEventKind::Press {
self.inner.call(state, x)
} else {
Ok(())
}
}
fn on_motion(&mut self, _: &MotionNotifyEvent, _: &mut State<X>, _: &X) -> Result<()> {
Ok(())
}
}
pub type MouseBindings<X> = HashMap<MouseState, Box<dyn MouseEventHandler<X>>>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum KeyPress {
Utf8(String),
Return,
Escape,
Tab,
Backspace,
Delete,
PageUp,
PageDown,
Up,
Down,
Left,
Right,
}
#[cfg(feature = "keysyms")]
impl TryFrom<XKeySym> for KeyPress {
type Error = std::string::FromUtf8Error;
fn try_from(s: XKeySym) -> std::result::Result<KeyPress, Self::Error> {
Ok(match s {
XKeySym::XK_Return | XKeySym::XK_KP_Enter | XKeySym::XK_ISO_Enter => KeyPress::Return,
XKeySym::XK_Escape => KeyPress::Escape,
XKeySym::XK_Tab | XKeySym::XK_ISO_Left_Tab | XKeySym::XK_KP_Tab => KeyPress::Tab,
XKeySym::XK_BackSpace => KeyPress::Backspace,
XKeySym::XK_Delete | XKeySym::XK_KP_Delete => KeyPress::Delete,
XKeySym::XK_Page_Up | XKeySym::XK_KP_Page_Up => KeyPress::PageUp,
XKeySym::XK_Page_Down | XKeySym::XK_KP_Page_Down => KeyPress::PageDown,
XKeySym::XK_Up | XKeySym::XK_KP_Up => KeyPress::Up,
XKeySym::XK_Down | XKeySym::XK_KP_Down => KeyPress::Down,
XKeySym::XK_Left | XKeySym::XK_KP_Left => KeyPress::Left,
XKeySym::XK_Right | XKeySym::XK_KP_Right => KeyPress::Right,
s => KeyPress::Utf8(s.as_utf8_string()?),
})
}
}
pub type KeyCodeMask = u16;
pub type KeyCodeValue = u8;
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct KeyCode {
pub mask: KeyCodeMask,
pub code: KeyCodeValue,
}
impl KeyCode {
pub fn ignoring_modifier(&self, mask: KeyCodeMask) -> KeyCode {
KeyCode {
mask: self.mask & !mask,
code: self.code,
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MouseButton {
#[default]
Left,
Middle,
Right,
ScrollUp,
ScrollDown,
}
impl From<MouseButton> for u8 {
fn from(b: MouseButton) -> u8 {
match b {
MouseButton::Left => 1,
MouseButton::Middle => 2,
MouseButton::Right => 3,
MouseButton::ScrollUp => 4,
MouseButton::ScrollDown => 5,
}
}
}
impl TryFrom<u8> for MouseButton {
type Error = Error;
fn try_from(n: u8) -> Result<Self> {
match n {
1 => Ok(Self::Left),
2 => Ok(Self::Middle),
3 => Ok(Self::Right),
4 => Ok(Self::ScrollUp),
5 => Ok(Self::ScrollDown),
_ => Err(Error::UnknownMouseButton { button: n }),
}
}
}
#[derive(Debug, EnumIter, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ModifierKey {
Ctrl,
Alt,
Shift,
Meta,
}
impl ModifierKey {
fn was_held(&self, mask: u16) -> bool {
mask & u16::from(*self) > 0
}
}
impl From<ModifierKey> for u16 {
fn from(m: ModifierKey) -> u16 {
(match m {
ModifierKey::Shift => 1 << 0,
ModifierKey::Ctrl => 1 << 2,
ModifierKey::Alt => 1 << 3,
ModifierKey::Meta => 1 << 6,
}) as u16
}
}
impl TryFrom<&str> for ModifierKey {
type Error = Error;
fn try_from(s: &str) -> std::result::Result<Self, Self::Error> {
match s {
"C" => Ok(Self::Ctrl),
"A" => Ok(Self::Alt),
"S" => Ok(Self::Shift),
"M" => Ok(Self::Meta),
_ => Err(Error::UnknownModifier { name: s.to_owned() }),
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MouseState {
pub button: MouseButton,
pub modifiers: Vec<ModifierKey>,
}
impl MouseState {
pub fn new(button: MouseButton, mut modifiers: Vec<ModifierKey>) -> Self {
modifiers.sort();
Self { button, modifiers }
}
pub fn from_detail_and_state(detail: u8, state: u16) -> Result<Self> {
Ok(Self {
button: MouseButton::try_from(detail)?,
modifiers: ModifierKey::iter().filter(|m| m.was_held(state)).collect(),
})
}
pub fn mask(&self) -> u16 {
self.modifiers
.iter()
.fold(0, |acc, &val| acc | u16::from(val))
}
pub fn button(&self) -> u8 {
self.button.into()
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MouseEventKind {
Press,
Release,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MouseEventData {
pub id: Xid,
pub rpt: Point,
pub wpt: Point,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MouseEvent {
pub data: MouseEventData,
pub state: MouseState,
pub kind: MouseEventKind,
}
impl MouseEvent {
pub fn new(
id: Xid,
rx: i16,
ry: i16,
ex: i16,
ey: i16,
state: MouseState,
kind: MouseEventKind,
) -> Self {
MouseEvent {
data: MouseEventData {
id,
rpt: Point::new(rx as i32, ry as i32),
wpt: Point::new(ex as i32, ey as i32),
},
state,
kind,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MotionNotifyEvent {
pub data: MouseEventData,
pub modifiers: Vec<ModifierKey>,
}
impl MotionNotifyEvent {
pub fn new(id: Xid, rx: i16, ry: i16, ex: i16, ey: i16, modifiers: Vec<ModifierKey>) -> Self {
MotionNotifyEvent {
data: MouseEventData {
id,
rpt: Point::new(rx as i32, ry as i32),
wpt: Point::new(ex as i32, ey as i32),
},
modifiers,
}
}
}