oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
2,718 bytes raw
1
const std = @import("std");
2
const xlib = @import("../x11/xlib.zig");
3
const config = @import("../config/config.zig");
4
5
pub const max_chord_len: u8 = 4;
6
/// How long (in milliseconds) the user has between key presses within
7
/// a chord before the sequence is abandoned and state is reset.
8
pub const timeout_ms: i64 = 1000;
9
10
/// Tracks the in-progress key-chord sequence.
11
///
12
/// Owned by `WindowManager`. Call `update` on every key press; it returns
13
/// whether the sequence is still live. Call `reset` to abandon the
14
/// current sequence and release the keyboard grab if one is held.
15
pub const ChordState = struct {
16
    keys: [max_chord_len]config.Key_Press = .{config.Key_Press{}} ** max_chord_len,
17
    index: u8 = 0,
18
    last_timestamp: i64 = 0,
19
    keyboard_grabbed: bool = false,
20
21
    /// Push a new key press onto the sequence and update the timestamp.
22
    ///
23
    /// Returns `false` if the sequence is already at maximum length, in which
24
    /// case the caller should call `reset` before retrying.
25
    pub fn push(self: *ChordState, key: config.Key_Press) bool {
26
        if (self.index >= max_chord_len) return false;
27
        self.keys[self.index] = key;
28
        self.index += 1;
29
        self.last_timestamp = std.time.milliTimestamp();
30
31
        return true;
32
    }
33
34
    /// Returns true if the sequence has timed out and should be reset.
35
    pub fn is_timed_out(self: *const ChordState) bool {
36
        if (self.index == 0) return false;
37
        return (std.time.milliTimestamp() - self.last_timestamp) >= timeout_ms;
38
    }
39
40
    /// Clears the sequence and releases the keyboard grab if one is held.
41
    ///
42
    /// `display` may be null only during early startup before the connection
43
    /// is open, in normal operation it should always be provided.
44
    pub fn reset(self: *ChordState, display: ?*xlib.Display) void {
45
        self.keys = .{config.Key_Press{}} ** max_chord_len;
46
        self.index = 0;
47
        self.last_timestamp = 0;
48
49
        if (self.keyboard_grabbed) {
50
            if (display) |dpy| {
51
                _ = xlib.XUngrabKeyboard(dpy, xlib.CurrentTime);
52
            }
53
            self.keyboard_grabbed = false;
54
        }
55
    }
56
57
    /// Try to grab the keyboard for exclusive input during a partial match.
58
    ///
59
    /// Sets `keyboard_grabbed` on success.  Safe to call repeatedly,
60
    /// does nothing if already grabbed.
61
    pub fn grab_keyboard(self: *ChordState, display: *xlib.Display, root: xlib.Window) void {
62
        if (self.keyboard_grabbed) return;
63
64
        const result = xlib.XGrabKeyboard(display, root, xlib.True, xlib.GrabModeAsync, xlib.GrabModeAsync, xlib.CurrentTime);
65
        if (result == xlib.GrabSuccess) {
66
            self.keyboard_grabbed = true;
67
        }
68
    }
69
};