1use crate::{
3 core::{State, Xid},
4 pure::geometry::Point,
5 x::XConn,
6 Error, Result,
7};
8#[cfg(feature = "keysyms")]
9use penrose_keysyms::XKeySym;
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12use std::{collections::HashMap, convert::TryFrom, fmt, process::Command};
13use strum::{EnumIter, IntoEnumIterator};
14use tracing::trace;
15
16pub fn keycodes_from_xmodmap() -> Result<HashMap<String, u8>> {
27 let output = Command::new("xmodmap").arg("-pke").output()?;
28 let m = String::from_utf8(output.stdout)?
29 .lines()
30 .flat_map(|l| {
31 let mut words = l.split_whitespace(); let key_code: u8 = match words.nth(1) {
33 Some(word) => match word.parse() {
34 Ok(val) => val,
35 Err(e) => panic!("{}", e),
36 },
37 None => panic!("unexpected output format from xmodmap -pke"),
38 };
39 words.skip(1).map(move |name| (name.into(), key_code))
40 })
41 .collect();
42
43 Ok(m)
44}
45
46fn parse_binding(pattern: &str, known_codes: &HashMap<String, u8>) -> Result<KeyCode> {
47 let mut parts: Vec<&str> = pattern.split('-').collect();
48 let name = parts.remove(parts.len() - 1);
49
50 match known_codes.get(name) {
51 Some(code) => {
52 let mask = parts
53 .iter()
54 .map(|&s| ModifierKey::try_from(s))
55 .try_fold(0, |acc, v| v.map(|inner| acc | u16::from(inner)))?;
56
57 trace!(?pattern, mask, code, "parsed keybinding");
58 Ok(KeyCode { mask, code: *code })
59 }
60
61 None => Err(Error::UnknownKeyName {
62 name: name.to_owned(),
63 }),
64 }
65}
66
67pub fn parse_keybindings_with_xmodmap<S, X>(
72 str_bindings: HashMap<S, Box<dyn KeyEventHandler<X>>>,
73) -> Result<KeyBindings<X>>
74where
75 S: AsRef<str>,
76 X: XConn,
77{
78 let m = keycodes_from_xmodmap()?;
79
80 str_bindings
81 .into_iter()
82 .map(|(s, v)| parse_binding(s.as_ref(), &m).map(|k| (k, v)))
83 .collect()
84}
85
86pub trait KeyEventHandler<X>
88where
89 X: XConn,
90{
91 fn call(&mut self, state: &mut State<X>, x: &X) -> Result<()>;
93}
94
95impl<X: XConn> fmt::Debug for Box<dyn KeyEventHandler<X>> {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 f.debug_struct("KeyEventHandler").finish()
98 }
99}
100
101impl<F, X> KeyEventHandler<X> for F
102where
103 F: FnMut(&mut State<X>, &X) -> Result<()>,
104 X: XConn,
105{
106 fn call(&mut self, state: &mut State<X>, x: &X) -> Result<()> {
107 (self)(state, x)
108 }
109}
110
111pub type KeyBindings<X> = HashMap<KeyCode, Box<dyn KeyEventHandler<X>>>;
113
114pub trait MouseEventHandler<X>
116where
117 X: XConn,
118{
119 fn on_mouse_event(&mut self, evt: &MouseEvent, state: &mut State<X>, x: &X) -> Result<()>;
122
123 fn on_motion(&mut self, evt: &MotionNotifyEvent, state: &mut State<X>, x: &X) -> Result<()>;
126}
127
128impl<X: XConn> fmt::Debug for Box<dyn MouseEventHandler<X>> {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 f.debug_struct("MouseEventHandler").finish()
131 }
132}
133
134impl<F, X> MouseEventHandler<X> for F
135where
136 F: FnMut(&mut State<X>, &X) -> Result<()>,
137 X: XConn,
138{
139 fn on_mouse_event(&mut self, evt: &MouseEvent, state: &mut State<X>, x: &X) -> Result<()> {
140 if evt.kind == MouseEventKind::Press {
141 (self)(state, x)
142 } else {
143 Ok(())
144 }
145 }
146
147 fn on_motion(&mut self, _: &MotionNotifyEvent, _: &mut State<X>, _: &X) -> Result<()> {
148 Ok(())
149 }
150}
151
152pub fn click_handler<X: XConn + 'static>(
166 kh: Box<dyn KeyEventHandler<X>>,
167) -> Box<dyn MouseEventHandler<X>> {
168 Box::new(MouseWrapper { inner: kh })
169}
170
171struct MouseWrapper<X: XConn> {
172 inner: Box<dyn KeyEventHandler<X>>,
173}
174
175impl<X: XConn> MouseEventHandler<X> for MouseWrapper<X> {
176 fn on_mouse_event(&mut self, evt: &MouseEvent, state: &mut State<X>, x: &X) -> Result<()> {
177 if evt.kind == MouseEventKind::Press {
178 self.inner.call(state, x)
179 } else {
180 Ok(())
181 }
182 }
183
184 fn on_motion(&mut self, _: &MotionNotifyEvent, _: &mut State<X>, _: &X) -> Result<()> {
185 Ok(())
186 }
187}
188
189pub type MouseBindings<X> = HashMap<MouseState, Box<dyn MouseEventHandler<X>>>;
191
192#[derive(Debug, Clone, PartialEq, Eq)]
194pub enum KeyPress {
195 Utf8(String),
197 Return,
199 Escape,
201 Tab,
203 Backspace,
205 Delete,
207 PageUp,
209 PageDown,
211 Up,
213 Down,
215 Left,
217 Right,
219}
220
221#[cfg(feature = "keysyms")]
222impl TryFrom<XKeySym> for KeyPress {
223 type Error = std::string::FromUtf8Error;
224
225 fn try_from(s: XKeySym) -> std::result::Result<KeyPress, Self::Error> {
226 Ok(match s {
227 XKeySym::XK_Return | XKeySym::XK_KP_Enter | XKeySym::XK_ISO_Enter => KeyPress::Return,
228 XKeySym::XK_Escape => KeyPress::Escape,
229 XKeySym::XK_Tab | XKeySym::XK_ISO_Left_Tab | XKeySym::XK_KP_Tab => KeyPress::Tab,
230 XKeySym::XK_BackSpace => KeyPress::Backspace,
231 XKeySym::XK_Delete | XKeySym::XK_KP_Delete => KeyPress::Delete,
232 XKeySym::XK_Page_Up | XKeySym::XK_KP_Page_Up => KeyPress::PageUp,
233 XKeySym::XK_Page_Down | XKeySym::XK_KP_Page_Down => KeyPress::PageDown,
234 XKeySym::XK_Up | XKeySym::XK_KP_Up => KeyPress::Up,
235 XKeySym::XK_Down | XKeySym::XK_KP_Down => KeyPress::Down,
236 XKeySym::XK_Left | XKeySym::XK_KP_Left => KeyPress::Left,
237 XKeySym::XK_Right | XKeySym::XK_KP_Right => KeyPress::Right,
238 s => KeyPress::Utf8(s.as_utf8_string()?),
239 })
240 }
241}
242
243pub type KeyCodeMask = u16;
245
246pub type KeyCodeValue = u8;
248
249#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
251#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
252pub struct KeyCode {
253 pub mask: KeyCodeMask,
255 pub code: KeyCodeValue,
257}
258
259impl KeyCode {
260 pub fn ignoring_modifier(&self, mask: KeyCodeMask) -> KeyCode {
262 KeyCode {
263 mask: self.mask & !mask,
264 code: self.code,
265 }
266 }
267}
268
269#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
271#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
272pub enum MouseButton {
273 #[default]
275 Left,
276 Middle,
278 Right,
280 ScrollUp,
282 ScrollDown,
284}
285
286impl From<MouseButton> for u8 {
287 fn from(b: MouseButton) -> u8 {
288 match b {
289 MouseButton::Left => 1,
290 MouseButton::Middle => 2,
291 MouseButton::Right => 3,
292 MouseButton::ScrollUp => 4,
293 MouseButton::ScrollDown => 5,
294 }
295 }
296}
297
298impl TryFrom<u8> for MouseButton {
299 type Error = Error;
300
301 fn try_from(n: u8) -> Result<Self> {
302 match n {
303 1 => Ok(Self::Left),
304 2 => Ok(Self::Middle),
305 3 => Ok(Self::Right),
306 4 => Ok(Self::ScrollUp),
307 5 => Ok(Self::ScrollDown),
308 _ => Err(Error::UnknownMouseButton { button: n }),
309 }
310 }
311}
312
313#[derive(Debug, EnumIter, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
315#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
316pub enum ModifierKey {
317 Ctrl,
319 Alt,
321 Shift,
323 Meta,
325}
326
327impl ModifierKey {
328 fn was_held(&self, mask: u16) -> bool {
329 mask & u16::from(*self) > 0
330 }
331}
332
333impl From<ModifierKey> for u16 {
334 fn from(m: ModifierKey) -> u16 {
335 (match m {
336 ModifierKey::Shift => 1 << 0,
337 ModifierKey::Ctrl => 1 << 2,
338 ModifierKey::Alt => 1 << 3,
339 ModifierKey::Meta => 1 << 6,
340 }) as u16
341 }
342}
343
344impl TryFrom<&str> for ModifierKey {
345 type Error = Error;
346
347 fn try_from(s: &str) -> std::result::Result<Self, Self::Error> {
348 match s {
349 "C" => Ok(Self::Ctrl),
350 "A" => Ok(Self::Alt),
351 "S" => Ok(Self::Shift),
352 "M" => Ok(Self::Meta),
353 _ => Err(Error::UnknownModifier { name: s.to_owned() }),
354 }
355 }
356}
357
358#[derive(Debug, PartialEq, Eq, Hash, Clone)]
360#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
361pub struct MouseState {
362 pub button: MouseButton,
364 pub modifiers: Vec<ModifierKey>,
366}
367
368impl MouseState {
369 pub fn new(button: MouseButton, mut modifiers: Vec<ModifierKey>) -> Self {
371 modifiers.sort();
372 Self { button, modifiers }
373 }
374
375 pub fn from_detail_and_state(detail: u8, state: u16) -> Result<Self> {
377 Ok(Self {
378 button: MouseButton::try_from(detail)?,
379 modifiers: ModifierKey::iter().filter(|m| m.was_held(state)).collect(),
380 })
381 }
382
383 pub fn mask(&self) -> u16 {
385 self.modifiers
386 .iter()
387 .fold(0, |acc, &val| acc | u16::from(val))
388 }
389
390 pub fn button(&self) -> u8 {
392 self.button.into()
393 }
394}
395
396#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
398#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
399pub enum MouseEventKind {
400 Press,
402 Release,
404}
405
406#[derive(Debug, Clone, PartialEq, Eq, Hash)]
408#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
409pub struct MouseEventData {
410 pub id: Xid,
412 pub rpt: Point,
414 pub wpt: Point,
416}
417
418#[derive(Debug, Clone, PartialEq, Eq, Hash)]
420#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
421pub struct MouseEvent {
422 pub data: MouseEventData,
424 pub state: MouseState,
426 pub kind: MouseEventKind,
428}
429
430impl MouseEvent {
431 pub fn new(
433 id: Xid,
434 rx: i16,
435 ry: i16,
436 ex: i16,
437 ey: i16,
438 state: MouseState,
439 kind: MouseEventKind,
440 ) -> Self {
441 MouseEvent {
442 data: MouseEventData {
443 id,
444 rpt: Point::new(rx as i32, ry as i32),
445 wpt: Point::new(ex as i32, ey as i32),
446 },
447 state,
448 kind,
449 }
450 }
451}
452
453#[derive(Debug, Clone, PartialEq, Eq, Hash)]
455#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
456pub struct MotionNotifyEvent {
457 pub data: MouseEventData,
459 pub modifiers: Vec<ModifierKey>,
461}
462
463impl MotionNotifyEvent {
464 pub fn new(id: Xid, rx: i16, ry: i16, ex: i16, ey: i16, modifiers: Vec<ModifierKey>) -> Self {
466 MotionNotifyEvent {
467 data: MouseEventData {
468 id,
469 rpt: Point::new(rx as i32, ry as i32),
470 wpt: Point::new(ex as i32, ey as i32),
471 },
472 modifiers,
473 }
474 }
475}