penrose 0.4.0

A tiling window manager library inspired by dwm and xmonad
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
//! Setting up and responding to user defined key/mouse bindings
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;

/// Run the xmodmap command to dump the system keymap table.
///
/// This is done in a form that we can load in and convert back to key
/// codes. This lets the user define key bindings in the way that they
/// would expect while also ensuring that it is easy to debug any odd
/// issues with bindings by referring the user to the xmodmap output.
///
/// # Panics
/// This function will panic if it is unable to fetch keycodes using the xmodmap
/// binary on your system or if the output of `xmodmap -pke` is not valid
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(); // keycode <code> = <names ...>
            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(),
        }),
    }
}

/// Parse string format key bindings into [KeyCode] based [KeyBindings] using
/// the command line `xmodmap` utility.
///
/// See [keycodes_from_xmodmap] for details of how `xmodmap` is used.
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()
}

/// Some action to be run by a user key binding
pub trait KeyEventHandler<X>
where
    X: XConn,
{
    /// Call this handler with the current window manager state
    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)
    }
}

/// User defined key bindings
pub type KeyBindings<X> = HashMap<KeyCode, Box<dyn KeyEventHandler<X>>>;

/// An action to be run in response to a mouse event
pub trait MouseEventHandler<X>
where
    X: XConn,
{
    /// Called when the [MouseState] associated with this handler is seen with a button press or
    /// release.
    fn on_mouse_event(&mut self, evt: &MouseEvent, state: &mut State<X>, x: &X) -> Result<()>;

    /// Called when the [ModifierKey]s associated with this handler are seen when the mouse is
    /// moving.
    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(())
    }
}

/// Convert a [KeyEventHandler] to a [MouseEventHandler] that runs on button `Press` events.
///
/// This allows for running pre-existing simple key handlers that do not care about press/release
/// or motion behaviour as a simplified mouse event handler.
///
/// ## Example
/// ```rust
/// use penrose::builtin::actions::floating::sink_all;
/// use penrose::core::bindings::{click_handler, MouseEventHandler};
/// use penrose::x11rb::RustConn;
///
/// let handler: Box<dyn MouseEventHandler<RustConn>> =  click_handler(sink_all());
/// ```
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(())
    }
}

/// User defined mouse bindings
pub type MouseBindings<X> = HashMap<MouseState, Box<dyn MouseEventHandler<X>>>;

/// Abstraction layer for working with key presses
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum KeyPress {
    /// A raw character key
    Utf8(String),
    /// Return / enter key
    Return,
    /// Escape
    Escape,
    /// Tab
    Tab,
    /// Backspace
    Backspace,
    /// Delete
    Delete,
    /// PageUp
    PageUp,
    /// PageDown
    PageDown,
    /// Up
    Up,
    /// Down
    Down,
    /// Left
    Left,
    /// Right
    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()?),
        })
    }
}

/// A u16 X key-code bitmask
pub type KeyCodeMask = u16;

/// A u8 X key-code enum value
pub type KeyCodeValue = u8;

/// A key press and held modifiers
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct KeyCode {
    /// The held modifier mask
    pub mask: KeyCodeMask,
    /// The key code that was held
    pub code: KeyCodeValue,
}

impl KeyCode {
    /// Create a new [KeyCode] from this one that removes the given mask
    pub fn ignoring_modifier(&self, mask: KeyCodeMask) -> KeyCode {
        KeyCode {
            mask: self.mask & !mask,
            code: self.code,
        }
    }
}

/// Known mouse buttons for binding actions
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MouseButton {
    /// 1
    #[default]
    Left,
    /// 2
    Middle,
    /// 3
    Right,
    /// 4
    ScrollUp,
    /// 5
    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 }),
        }
    }
}

/// Known modifier keys for bindings
#[derive(Debug, EnumIter, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ModifierKey {
    /// Control
    Ctrl,
    /// Alt
    Alt,
    /// Shift
    Shift,
    /// Meta / super / windows
    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() }),
        }
    }
}

/// A mouse state specification indicating the button and modifiers held
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MouseState {
    /// The [MouseButton] being held
    pub button: MouseButton,
    /// All [ModifierKey]s being held
    pub modifiers: Vec<ModifierKey>,
}

impl MouseState {
    /// Construct a new MouseState
    pub fn new(button: MouseButton, mut modifiers: Vec<ModifierKey>) -> Self {
        modifiers.sort();
        Self { button, modifiers }
    }

    /// Parse raw mouse state values into a [MouseState]
    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(),
        })
    }

    /// The xcb bitmask for this [MouseState]
    pub fn mask(&self) -> u16 {
        self.modifiers
            .iter()
            .fold(0, |acc, &val| acc | u16::from(val))
    }

    /// The xcb button ID for this [MouseState]
    pub fn button(&self) -> u8 {
        self.button.into()
    }
}

/// The types of mouse events represented by a MouseEvent
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MouseEventKind {
    /// A button was pressed
    Press,
    /// A button was released
    Release,
}

/// Data from a button press or motion-notify event
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MouseEventData {
    /// The ID of the window that was contained the click
    pub id: Xid,
    /// Absolute coordinate of the event
    pub rpt: Point,
    /// Coordinate of the event relative to top-left of the window itself
    pub wpt: Point,
}

/// A mouse movement or button event
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MouseEvent {
    /// The details of which window the event applies to and where the event occurred
    pub data: MouseEventData,
    /// The modifier and button code that was received
    pub state: MouseState,
    /// Was this press or release
    pub kind: MouseEventKind,
}

impl MouseEvent {
    /// Construct a new [MouseEvent] from raw data
    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,
        }
    }
}

/// Mouse motion with a held button and optional modifiers
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MotionNotifyEvent {
    /// The details of which window the event applies to and where the event occurred
    pub data: MouseEventData,
    /// All [ModifierKey]s being held
    pub modifiers: Vec<ModifierKey>,
}

impl MotionNotifyEvent {
    /// Construct a new [MotionNotifyEvent] from raw data
    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,
        }
    }
}