Skip to main content

jay_config/keyboard/
mod.rs

1//! Tools affecting the keyboard behavior.
2
3use {
4    crate::keyboard::{mods::Modifiers, syms::KeySym},
5    serde::{Deserialize, Serialize},
6    std::ops::{BitOr, BitOrAssign},
7};
8
9pub mod mods;
10pub mod syms;
11
12/// A keysym with zero or more modifiers
13#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Hash, Debug)]
14pub struct ModifiedKeySym {
15    pub mods: Modifiers,
16    pub sym: KeySym,
17}
18
19impl From<KeySym> for ModifiedKeySym {
20    fn from(sym: KeySym) -> Self {
21        Self {
22            mods: Modifiers(0),
23            sym,
24        }
25    }
26}
27
28impl BitOr<Modifiers> for ModifiedKeySym {
29    type Output = ModifiedKeySym;
30
31    fn bitor(self, rhs: Modifiers) -> Self::Output {
32        ModifiedKeySym {
33            mods: self.mods | rhs,
34            sym: self.sym,
35        }
36    }
37}
38
39impl BitOrAssign<Modifiers> for ModifiedKeySym {
40    fn bitor_assign(&mut self, rhs: Modifiers) {
41        self.mods |= rhs;
42    }
43}
44
45/// A keymap.
46#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)]
47pub struct Keymap(pub u64);
48
49impl Keymap {
50    /// The invalid keymap.
51    pub const INVALID: Self = Self(0);
52
53    /// Returns whether this keymap is valid.
54    pub fn is_valid(self) -> bool {
55        self != Self::INVALID
56    }
57
58    /// Returns whether this keymap is invalid.
59    pub fn is_invalid(self) -> bool {
60        self == Self::INVALID
61    }
62
63    /// Destroys this reference to the keymap.
64    ///
65    /// Seats that are currently using this keymap are unaffected.
66    pub fn destroy(self) {
67        if self.is_valid() {
68            get!().destroy_keymap(self);
69        }
70    }
71}
72
73/// An RMLVO group consisting of a layout and a variant.
74#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)]
75pub struct Group<'a> {
76    /// The layout of the group.
77    pub layout: &'a str,
78    /// The variant of the group. Can be an empty string.
79    pub variant: &'a str,
80}
81
82/// Parses a keymap.
83///
84/// The returned keymap can later be used to set the keymap of a seat. If the keymap cannot
85/// be parsed, returns an invalid keymap. Trying to set a seat's keymap to an invalid keymap
86/// has no effect.
87///
88/// A simple keymap looks as follows:
89///
90/// ```text
91/// xkb_keymap {
92///     xkb_keycodes  { include "evdev+aliases(qwerty)" };
93///     xkb_types     { include "complete" };
94///     xkb_compat    { include "complete" };
95///     xkb_symbols   { include "pc+inet(evdev)+us(basic)" };
96/// };
97/// ```
98///
99/// To use a programmer Dvorak, replace the corresponding line by
100///
101/// ```text
102///     xkb_symbols   { include "pc+inet(evdev)+us(dvp)" };
103/// ```
104///
105/// To use a German keymap, replace the corresponding line by
106///
107/// ```text
108///     xkb_symbols   { include "pc+inet(evdev)+de(basic)" };
109/// ```
110///
111/// You can also use a completely custom keymap that doesn't use any includes. See the
112/// [default][default] Jay keymap for an example.
113///
114/// General information about the keymap format can be found in the [arch wiki][wiki].
115///
116/// [default]: https://github.com/mahkoh/jay/tree/master/src/keymap.xkb
117/// [wiki]: https://wiki.archlinux.org/title/X_keyboard_extension
118pub fn parse_keymap(keymap: &str) -> Keymap {
119    get!(Keymap::INVALID).parse_keymap(keymap)
120}
121
122/// Creates a keymap from RMLVO names.
123///
124/// If a parameter is not given, a value from the environment or a default is used:
125///
126/// | name                   | default                |
127/// | ---------------------- | ---------------------- |
128/// | `XKB_DEFAULT_RULES`    | `evdev`                |
129/// | `XKB_DEFAULT_MODEL`    | `pc105`                |
130/// | `XKB_DEFAULT_LAYOUT`   | `us`                   |
131/// | `XKB_DEFAULT_VARIANTS` |                        |
132/// | `XKB_DEFAULT_OPTIONS`  |                        |
133///
134/// `XKB_DEFAULT_LAYOUT` and `XKB_DEFAULT_VARIANTS` are parsed into the `groups` parameter like this example:
135/// ```
136/// XKB_DEFAULT_LAYOUT = "us,il,ru,de,jp"
137/// XKB_DEFAULT_VARIANTS = ",,phonetic,neo"
138/// ```
139/// produces:
140/// ```
141/// [
142///     Group { layout: "us", variant: "" },
143///     Group { layout: "il", variant: "" },
144///     Group { layout: "ru", variant: "phonetic" },
145///     Group { layout: "de", variant: "neo" },
146///     Group { layout: "jp", variant: "" },
147/// ]
148/// ```
149pub fn keymap_from_names(
150    rules: Option<&str>,
151    model: Option<&str>,
152    groups: Option<&[Group<'_>]>,
153    options: Option<&[&str]>,
154) -> Keymap {
155    get!(Keymap::INVALID).keymap_from_names(rules, model, groups, options)
156}