xkb_data/
lib.rs

1// Copyright 2023 System76 <info@system76.com>
2// SPDX-License-Identifier: MPL-2.0
3
4use serde::Deserialize;
5use serde_xml_rs as xml;
6use std::fs::File;
7use std::io::{self, BufReader};
8
9const X11_BASE_RULES: &str = "/usr/share/X11/xkb/rules/base.xml";
10const X11_EXTRAS_RULES: &str = "/usr/share/X11/xkb/rules/base.extras.xml";
11
12/// A list of keyboard layouts parsed from `/usr/share/X11/xkb/rules/base.xml`.
13#[derive(Debug, Deserialize, Clone)]
14pub struct KeyboardLayouts {
15    #[serde(rename = "layoutList")]
16    pub layout_list: LayoutList,
17}
18
19impl KeyboardLayouts {
20    /// Fetch the layouts from the layout list.
21    pub fn layouts(&self) -> &[KeyboardLayout] { &self.layout_list.layout }
22
23    /// Fetch the layouts from the layout list.
24    pub fn layouts_mut(&mut self) -> &mut [KeyboardLayout] { &mut self.layout_list.layout }
25}
26
27/// A list of keyboard layouts.
28#[derive(Debug, Deserialize, Clone)]
29pub struct LayoutList {
30    pub layout: Vec<KeyboardLayout>,
31}
32
33/// A keyboard layout, which contains an optional list of variants, a name, and a description.
34#[derive(Debug, Deserialize, Clone)]
35pub struct KeyboardLayout {
36    #[serde(rename = "configItem")]
37    pub config_item:  ConfigItem,
38    #[serde(rename = "variantList")]
39    pub variant_list: Option<VariantList>,
40}
41
42impl KeyboardLayout {
43    /// Fetches the name of the keyboard layout.
44    pub fn name(&self) -> &str { &self.config_item.name }
45
46    /// Fetches a description of the layout.
47    pub fn description(&self) -> &str { &self.config_item.description }
48
49    /// Fetches a list of possible layout variants.
50    pub fn variants(&self) -> Option<&Vec<KeyboardVariant>> {
51        self.variant_list.as_ref().and_then(|x| x.variant.as_ref())
52    }
53}
54
55/// Contains the name and description of a keyboard layout.
56#[derive(Debug, Deserialize, Clone)]
57pub struct ConfigItem {
58    pub name:              String,
59    #[serde(rename = "shortDescription")]
60    pub short_description: Option<String>,
61    pub description:       String,
62}
63
64/// A list of possible variants of a keyboard layout.
65#[derive(Debug, Deserialize, Clone)]
66pub struct VariantList {
67    pub variant: Option<Vec<KeyboardVariant>>,
68}
69
70/// A variant of a keyboard layout.
71#[derive(Debug, Deserialize, Clone)]
72pub struct KeyboardVariant {
73    #[serde(rename = "configItem")]
74    pub config_item: ConfigItem,
75}
76
77impl KeyboardVariant {
78    /// The name of this variant of a keybaord layout.
79    pub fn name(&self) -> &str { &self.config_item.name }
80
81    /// A description of this variant of a keyboard layout.
82    pub fn description(&self) -> &str { &self.config_item.description }
83}
84
85/// Fetches a list of keyboard layouts from a path.
86pub fn get_keyboard_layouts(path: &str) -> io::Result<KeyboardLayouts> {
87    xml::from_reader(BufReader::new(File::open(path)?))
88        .map_err(|why| io::Error::new(io::ErrorKind::InvalidData, format!("{}", why)))
89}
90
91/// Fetches a list of keyboard layouts from `/usr/share/X11/xkb/rules/base.xml` or the file defined in the X11_BASE_RULES_XML environment variable.
92pub fn keyboard_layouts() -> io::Result<KeyboardLayouts> {
93    if let Ok(x11_base_rules_xml) = std::env::var("X11_BASE_RULES_XML") {
94        get_keyboard_layouts(&x11_base_rules_xml)
95    }
96    else {
97        get_keyboard_layouts(X11_BASE_RULES)
98    }
99}
100
101/// Fetches a list of keyboard layouts from `/usr/share/X11/xkb/rules/base.extras.xml` or the file defined in the X11_EXTRA_RULES_XML environment variable.
102pub fn extra_keyboard_layouts() -> io::Result<KeyboardLayouts> {
103    if let Ok(x11_extra_rules_xml) = std::env::var("X11_EXTRA_RULES_XML") {
104        get_keyboard_layouts(&x11_extra_rules_xml)
105    }
106    else {
107        get_keyboard_layouts(X11_EXTRAS_RULES)
108    }
109}
110
111/// Fetches a list of keyboard layouts from `/usr/share/X11/xkb/rules/base.xml` and
112/// extends them with the list of keyboard layouts from `/usr/share/X11/xkb/rules/base.extras.xml`.
113pub fn all_keyboard_layouts() -> io::Result<KeyboardLayouts> {
114    let base_rules = keyboard_layouts();
115    let extras_rules = extra_keyboard_layouts();
116
117    match (base_rules, extras_rules,) {
118        (Ok(base_rules), Ok(extras_rules)) => return Ok(merge_rules(base_rules, extras_rules)),
119        (Err(why), _) => return Err(io::Error::new(io::ErrorKind::InvalidData, format!("{}", why))),
120        (_, Err(why)) => return Err(io::Error::new(io::ErrorKind::InvalidData, format!("{}", why))),
121    }
122}
123
124fn merge_rules(base: KeyboardLayouts, extras: KeyboardLayouts) -> KeyboardLayouts {
125    KeyboardLayouts {
126        layout_list: concat_layout_lists(vec![base.layout_list, extras.layout_list])
127    }
128}
129
130fn concat_layout_lists(layouts: Vec<LayoutList>) -> LayoutList {
131    let mut new_layouts = vec![];
132    for layout_list in layouts.into_iter() {
133        new_layouts.extend(layout_list.layout);
134    }
135    return LayoutList { layout: new_layouts }
136}