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}