diff --git a/src/blocks/keyboard_layout.rs b/src/blocks/keyboard_layout.rs index 57fdd90ba6..c7f0b34ef9 100644 --- a/src/blocks/keyboard_layout.rs +++ b/src/blocks/keyboard_layout.rs @@ -2,6 +2,7 @@ //! //! Four drivers are available: //! - `setxkbmap` which polls setxkbmap to get the current layout +//! - `xkbswitch` which utilizes [XkbSwitch](https://github.com/grwlf/xkb-switch) to monitor and retrieve the current layout and variant //! - `localebus` which can read asynchronous updates from the systemd `org.freedesktop.locale1` D-Bus path //! - `kbddbus` which uses [kbdd](https://github.com/qnikst/kbdd) to monitor per-window layout changes via DBus //! - `sway` which can read asynchronous updates from the sway IPC @@ -12,7 +13,7 @@ //! //! Key | Values | Default //! ----|--------|-------- -//! `driver` | One of `"setxkbmap"`, `"localebus"`, `"kbddbus"` or `"sway"`, depending on your system. | `"setxkbmap"` +//! `driver` | One of `"setxkbmap"`, `"xkbswitch"`, "localebus"`, `"kbddbus"` or `"sway"`, depending on your system. | `"setxkbmap"` //! `interval` | Update interval, in seconds. Only used by the `"setxkbmap"` driver. | `60` //! `format` | A string to customise the output of this block. See below for available placeholders. | `" $layout "` //! `sway_kb_identifier` | Identifier of the device you want to monitor, as found in the output of `swaymsg -t get_inputs`. | Defaults to first input found @@ -34,6 +35,15 @@ //! interval = 15 //! ``` //! +//! Check `xkbswitch` every 15 seconds +//! +//! ```toml +//! [[block]] +//! block = "keyboard_layout" +//! driver = "xkbswitch" +//! interval = 15 +//! ``` +//! //! Listen to D-Bus for changes: //! //! ```toml @@ -82,6 +92,9 @@ mod set_xkb_map; use set_xkb_map::SetXkbMap; +mod xkb_switch; +use xkb_switch::XkbSwitch; + mod locale_bus; use locale_bus::LocaleBus; @@ -109,6 +122,7 @@ pub struct Config { pub enum KeyboardLayoutDriver { #[default] SetXkbMap, + XkbSwitch, LocaleBus, KbddBus, Sway, @@ -119,6 +133,7 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> { let mut backend: Box = match config.driver { KeyboardLayoutDriver::SetXkbMap => Box::new(SetXkbMap::new(config.interval)), + KeyboardLayoutDriver::XkbSwitch => Box::new(XkbSwitch::new(config.interval)), KeyboardLayoutDriver::LocaleBus => Box::new(LocaleBus::new().await?), KeyboardLayoutDriver::KbddBus => Box::new(KbddBus::new().await?), KeyboardLayoutDriver::Sway => Box::new(Sway::new(config.sway_kb_identifier.clone()).await?), diff --git a/src/blocks/keyboard_layout/xkb_switch.rs b/src/blocks/keyboard_layout/xkb_switch.rs new file mode 100644 index 0000000000..dfe96587d0 --- /dev/null +++ b/src/blocks/keyboard_layout/xkb_switch.rs @@ -0,0 +1,44 @@ +use super::*; +use tokio::process::Command; + +pub(super) struct XkbSwitch(Seconds); + +impl XkbSwitch { + pub(super) fn new(update_interval: Seconds) -> Self { + Self(update_interval) + } +} + +#[async_trait] +impl Backend for XkbSwitch { + async fn get_info(&mut self) -> Result { + // This command output is in the format of "layout(variant)" or "layout" + let output = Command::new("xkb-switch") + .arg("-p") + .output() + .await + .error("Failed to execute 'xkb-switch -p'")?; + + let output = + String::from_utf8(output.stdout).error("xkb-switch produces a non-UTF8 output")?; + + let mut components = output.trim_end().split('('); + + let layout = components + .next() + .error("Could not find layout entry in xkb-switch")? + .to_string(); + + let variant = components + .last() + // Remove the trailing parenthesis ")" + .map(|variant_str| variant_str.split_at(variant_str.len() - 1).0.to_string()); + + Ok(Info { layout, variant }) + } + + async fn wait_for_change(&mut self) -> Result<()> { + sleep(self.0 .0).await; + Ok(()) + } +}