use x11rb::connection::{Connection, RequestConnection}; use x11rb::protocol::xkb::{self, ConnectionExt as XkbConnectionExt}; use x11rb::protocol::xproto::{AtomEnum, ConnectionExt}; use x11rb::rust_connection::RustConnection; #[derive(Debug, Default)] pub struct RuleNames { pub rules: String, pub model: String, pub layout: String, pub variant: String, pub options: String, } pub struct X11Connection { pub conn: RustConnection, pub root: x11rb::protocol::xproto::Window, xkb_ok: bool, } impl X11Connection { pub fn open() -> Option { let (conn, screen_num) = x11rb::connect(None).ok()?; let root = conn.setup().roots[screen_num].root; let xkb_ok = conn .extension_information(xkb::X11_EXTENSION_NAME) .ok() .flatten() .is_some(); if xkb_ok { let _ = conn.xkb_use_extension(1, 0).ok(); } Some(Self { conn, root, xkb_ok }) } pub fn modifier_mask(&self) -> u16 { self.conn .query_pointer(self.root) .ok() .and_then(|c| c.reply().ok()) .map(|r| r.mask.into()) .unwrap_or(0) } pub fn active_group(&self) -> usize { if !self.xkb_ok { return 0; } self.conn .xkb_get_state(0x0100) .ok() .and_then(|c| c.reply().ok()) .map(|r| u8::from(r.group) as usize) .unwrap_or(0) } } pub fn get_xkb_rule_names() -> Option { let (conn, screen_num) = x11rb::connect(None).ok()?; let screen = &conn.setup().roots[screen_num]; let atom = conn .intern_atom(false, b"_XKB_RULES_NAMES") .ok()? .reply() .ok()? .atom; let prop = conn .get_property(false, screen.root, atom, AtomEnum::ANY, 0, 1024) .ok()? .reply() .ok()?; if prop.value.is_empty() || prop.format == 0 { return None; } let parts: Vec<&str> = prop .value .split(|&b| b == 0) .filter_map(|s| std::str::from_utf8(s).ok()) .collect(); Some(RuleNames { rules: parts.first().map(|s| s.to_string()).unwrap_or_default(), model: parts.get(1).map(|s| s.to_string()).unwrap_or_default(), layout: parts.get(2).map(|s| s.to_string()).unwrap_or_default(), variant: parts.get(3).map(|s| s.to_string()).unwrap_or_default(), options: parts.get(4).map(|s| s.to_string()).unwrap_or_default(), }) }