diff options
| author | salmonik <raisersoft@proton.me> | 2026-07-11 01:46:59 +0200 |
|---|---|---|
| committer | salmonik <raisersoft@proton.me> | 2026-07-11 01:46:59 +0200 |
| commit | 247d2805db0c1b686ef4c6cc0f786ff416aeb7e3 (patch) | |
| tree | b6dae4a848df5ae75c4d1bc8e73bad7c63d17575 /src/x11.rs | |
| download | xkbpreview-247d2805db0c1b686ef4c6cc0f786ff416aeb7e3.tar.gz xkbpreview-247d2805db0c1b686ef4c6cc0f786ff416aeb7e3.zip | |
init
Diffstat (limited to 'src/x11.rs')
| -rw-r--r-- | src/x11.rs | 92 |
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 @@ | |||
| 1 | use x11rb::connection::{Connection, RequestConnection}; | ||
| 2 | use x11rb::protocol::xkb::{self, ConnectionExt as XkbConnectionExt}; | ||
| 3 | use x11rb::protocol::xproto::{AtomEnum, ConnectionExt}; | ||
| 4 | use x11rb::rust_connection::RustConnection; | ||
| 5 | |||
| 6 | #[derive(Debug, Default)] | ||
| 7 | pub struct RuleNames { | ||
| 8 | pub rules: String, | ||
| 9 | pub model: String, | ||
| 10 | pub layout: String, | ||
| 11 | pub variant: String, | ||
| 12 | pub options: String, | ||
| 13 | } | ||
| 14 | |||
| 15 | pub struct X11Connection { | ||
| 16 | pub conn: RustConnection, | ||
| 17 | pub root: x11rb::protocol::xproto::Window, | ||
| 18 | xkb_ok: bool, | ||
| 19 | } | ||
| 20 | |||
| 21 | impl 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 | |||
| 58 | pub 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 | } | ||
