Skip to main content

jay_config/keyboard/
mod.rs

1//! Tools affecting the keyboard behavior.
2
3use {
4    crate::{
5        _private::{KeymapBuildParamsV1, KeymapBuildParamsV1Kind},
6        keyboard::{mods::Modifiers, syms::KeySym},
7    },
8    serde::{Deserialize, Serialize},
9    std::ops::{BitOr, BitOrAssign},
10};
11
12pub mod mods;
13pub mod syms;
14
15/// A keysym with zero or more modifiers
16#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Hash, Debug)]
17pub struct ModifiedKeySym {
18    pub mods: Modifiers,
19    pub sym: KeySym,
20}
21
22impl From<KeySym> for ModifiedKeySym {
23    fn from(sym: KeySym) -> Self {
24        Self {
25            mods: Modifiers(0),
26            sym,
27        }
28    }
29}
30
31impl BitOr<Modifiers> for ModifiedKeySym {
32    type Output = ModifiedKeySym;
33
34    fn bitor(self, rhs: Modifiers) -> Self::Output {
35        ModifiedKeySym {
36            mods: self.mods | rhs,
37            sym: self.sym,
38        }
39    }
40}
41
42impl BitOrAssign<Modifiers> for ModifiedKeySym {
43    fn bitor_assign(&mut self, rhs: Modifiers) {
44        self.mods |= rhs;
45    }
46}
47
48/// A keymap.
49#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)]
50pub struct Keymap(pub u64);
51
52impl Keymap {
53    /// The invalid keymap.
54    pub const INVALID: Self = Self(0);
55
56    /// Returns whether this keymap is valid.
57    pub fn is_valid(self) -> bool {
58        self != Self::INVALID
59    }
60
61    /// Returns whether this keymap is invalid.
62    pub fn is_invalid(self) -> bool {
63        self == Self::INVALID
64    }
65
66    /// Destroys this reference to the keymap.
67    ///
68    /// Seats that are currently using this keymap are unaffected.
69    pub fn destroy(self) {
70        if self.is_valid() {
71            get!().destroy_keymap(self);
72        }
73    }
74
75    /// Creates a keymap builder.
76    pub fn builder<'a>() -> KeymapBuilder<'a> {
77        KeymapBuilder {
78            v1: KeymapBuildParamsV1 {
79                kind: None,
80                shortcuts_group: None,
81            },
82        }
83    }
84}
85
86/// An RMLVO group consisting of a layout and a variant.
87#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)]
88pub struct Group<'a> {
89    /// The layout of the group.
90    pub layout: &'a str,
91    /// The variant of the group. Can be an empty string.
92    pub variant: &'a str,
93}
94
95/// Parses a keymap.
96///
97/// The returned keymap can later be used to set the keymap of a seat. If the keymap cannot
98/// be parsed, returns an invalid keymap. Trying to set a seat's keymap to an invalid keymap
99/// has no effect.
100///
101/// A simple keymap looks as follows:
102///
103/// ```text
104/// xkb_keymap {
105///     xkb_keycodes  { include "evdev+aliases(qwerty)" };
106///     xkb_types     { include "complete" };
107///     xkb_compat    { include "complete" };
108///     xkb_symbols   { include "pc+inet(evdev)+us(basic)" };
109/// };
110/// ```
111///
112/// To use a programmer Dvorak, replace the corresponding line by
113///
114/// ```text
115///     xkb_symbols   { include "pc+inet(evdev)+us(dvp)" };
116/// ```
117///
118/// To use a German keymap, replace the corresponding line by
119///
120/// ```text
121///     xkb_symbols   { include "pc+inet(evdev)+de(basic)" };
122/// ```
123///
124/// You can also use a completely custom keymap that doesn't use any includes. See the
125/// [default][default] Jay keymap for an example.
126///
127/// General information about the keymap format can be found in the [arch wiki][wiki].
128///
129/// See also [`Keymap::builder`] for a more general interface.
130///
131/// [default]: https://github.com/mahkoh/jay/tree/master/src/keymap.xkb
132/// [wiki]: https://wiki.archlinux.org/title/X_keyboard_extension
133pub fn parse_keymap(keymap: &str) -> Keymap {
134    get!(Keymap::INVALID).parse_keymap(keymap)
135}
136
137/// Creates a keymap from RMLVO names.
138///
139/// If a parameter is not given, a value from the environment or a default is used:
140///
141/// | name                   | default                |
142/// | ---------------------- | ---------------------- |
143/// | `XKB_DEFAULT_RULES`    | `evdev`                |
144/// | `XKB_DEFAULT_MODEL`    | `pc105`                |
145/// | `XKB_DEFAULT_LAYOUT`   | `us`                   |
146/// | `XKB_DEFAULT_VARIANTS` |                        |
147/// | `XKB_DEFAULT_OPTIONS`  |                        |
148///
149/// `XKB_DEFAULT_LAYOUT` and `XKB_DEFAULT_VARIANTS` are parsed into the `groups` parameter like this example:
150/// ```
151/// XKB_DEFAULT_LAYOUT = "us,il,ru,de,jp"
152/// XKB_DEFAULT_VARIANTS = ",,phonetic,neo"
153/// ```
154/// produces:
155/// ```
156/// [
157///     Group { layout: "us", variant: "" },
158///     Group { layout: "il", variant: "" },
159///     Group { layout: "ru", variant: "phonetic" },
160///     Group { layout: "de", variant: "neo" },
161///     Group { layout: "jp", variant: "" },
162/// ]
163/// ```
164///
165/// See also [`Keymap::builder`] for a more general interface.
166pub fn keymap_from_names(
167    rules: Option<&str>,
168    model: Option<&str>,
169    groups: Option<&[Group<'_>]>,
170    options: Option<&[&str]>,
171) -> Keymap {
172    get!(Keymap::INVALID).keymap_from_names(rules, model, groups, options)
173}
174
175/// A keymap builder.
176pub struct KeymapBuilder<'a> {
177    pub(crate) v1: KeymapBuildParamsV1<'a>,
178}
179
180impl<'a> KeymapBuilder<'a> {
181    /// Builds the keymap.
182    pub fn build(self) -> Keymap {
183        get!(Keymap::INVALID).parse_keymap_2(self)
184    }
185
186    /// Sets the XKB map to parse.
187    ///
188    /// See [`parse_keymap`] for details about the format.
189    pub fn map(mut self, map: &'a str) -> Self {
190        self.v1.kind = Some(KeymapBuildParamsV1Kind::Map(map));
191        self
192    }
193
194    /// Sets the RMLVO names to parse.
195    ///
196    /// See [`keymap_from_names`] for details about the format.
197    pub fn names(
198        mut self,
199        rules: Option<&'a str>,
200        model: Option<&'a str>,
201        groups: Option<&'a [Group<'a>]>,
202        options: Option<&'a [&'a str]>,
203    ) -> Self {
204        self.v1.kind = Some(KeymapBuildParamsV1Kind::Names {
205            rules,
206            model,
207            groups: groups.map(|v| v.to_vec()),
208            options: options.map(|v| v.to_vec()),
209        });
210        self
211    }
212
213    /// Sets the 0-based group index to use for shortcuts.
214    ///
215    /// By default, the group that is active at the time of the key press is used. For
216    /// example, if the keymap has two groups, US and RU, and the active group is 1,
217    /// shortcuts are looked up against the Russian group.
218    ///
219    /// By calling `shortcuts_group(0)`, shortcuts are instead always looked up against
220    /// the English group.
221    pub fn shortcuts_group(mut self, group: u32) -> Self {
222        self.v1.shortcuts_group = Some(group);
223        self
224    }
225}