summaryrefslogtreecommitdiffstats
path: root/src/x11.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/x11.rs92
1 files changed, 92 insertions, 0 deletions
diff --git a/src/x11.rs b/src/x11.rs
new file mode 100644
index 0000000..27052b9
--- /dev/null
+++ b/src/x11.rs
@@ -0,0 +1,92 @@
1use x11rb::connection::{Connection, RequestConnection};
2use x11rb::protocol::xkb::{self, ConnectionExt as XkbConnectionExt};
3use x11rb::protocol::xproto::{AtomEnum, ConnectionExt};
4use x11rb::rust_connection::RustConnection;
5
6#[derive(Debug, Default)]
7pub struct RuleNames {
8 pub rules: String,
9 pub model: String,
10 pub layout: String,
11 pub variant: String,
12 pub options: String,
13}
14
15pub struct X11Connection {
16 pub conn: RustConnection,
17 pub root: x11rb::protocol::xproto::Window,
18 xkb_ok: bool,
19}
20
21impl X11Connection {
22 pub fn open() -> Option<Self> {
23 let (conn, screen_num) = x11rb::connect(None).ok()?;
24 let root = conn.setup().roots[screen_num].root;
25 let xkb_ok = conn
26 .extension_information(xkb::X11_EXTENSION_NAME)
27 .ok()
28 .flatten()
29 .is_some();
30 if xkb_ok {
31 let _ = conn.xkb_use_extension(1, 0).ok();
32 }
33 Some(Self { conn, root, xkb_ok })
34 }
35
36 pub fn modifier_mask(&self) -> u16 {
37 self.conn
38 .query_pointer(self.root)
39 .ok()
40 .and_then(|c| c.reply().ok())
41 .map(|r| r.mask.into())
42 .unwrap_or(0)
43 }
44
45 pub fn active_group(&self) -> usize {
46 if !self.xkb_ok {
47 return 0;
48 }
49 self.conn
50 .xkb_get_state(0x0100)
51 .ok()
52 .and_then(|c| c.reply().ok())
53 .map(|r| u8::from(r.group) as usize)
54 .unwrap_or(0)
55 }
56}
57
58pub fn get_xkb_rule_names() -> Option<RuleNames> {
59 let (conn, screen_num) = x11rb::connect(None).ok()?;
60 let screen = &conn.setup().roots[screen_num];
61
62 let atom = conn
63 .intern_atom(false, b"_XKB_RULES_NAMES")
64 .ok()?
65 .reply()
66 .ok()?
67 .atom;
68
69 let prop = conn
70 .get_property(false, screen.root, atom, AtomEnum::ANY, 0, 1024)
71 .ok()?
72 .reply()
73 .ok()?;
74
75 if prop.value.is_empty() || prop.format == 0 {
76 return None;
77 }
78
79 let parts: Vec<&str> = prop
80 .value
81 .split(|&b| b == 0)
82 .filter_map(|s| std::str::from_utf8(s).ok())
83 .collect();
84
85 Some(RuleNames {
86 rules: parts.first().map(|s| s.to_string()).unwrap_or_default(),
87 model: parts.get(1).map(|s| s.to_string()).unwrap_or_default(),
88 layout: parts.get(2).map(|s| s.to_string()).unwrap_or_default(),
89 variant: parts.get(3).map(|s| s.to_string()).unwrap_or_default(),
90 options: parts.get(4).map(|s| s.to_string()).unwrap_or_default(),
91 })
92}