Skip to content

Commit

Permalink
Add support for the xkb-switch keyboard layout reader. (greshake#1386)
Browse files Browse the repository at this point in the history
* Add support for the xkb-switch keyboard layout reader.

Closes greshake#1384 at greshake#1384

If you set the xkbmap layout to a multilingual string with variants, such as:

```
setxkbmap -layout us,es,ru
setxkbmap -variant dvorak,,phonetic
```

the `keyboard_layout` block will not show the current layout nor the current variant. It will only show the entire multilingual layout string. This is because `i3status-rust` uses the command [`setxkbmap -query`](https://github.com/greshake/i3status-rust/blob/b6e1b0b755de9f4520f1ecf17086d62fc5f190fe/src/blocks/keyboard_layout.rs#L63) to get the current layout.

```
$ setxkbmap -query                 ☸ 0007-04
rules:      evdev
model:      pc105
layout:     us,es,ru
variant:    dvorak,,phonetic
options:    grp:ctrls_toggle
```

This commit uses [`xkb-switch`](https://github.com/grwlf/xkb-switch) to read the current layout and keyboard variant.

Example usage:

```
[[block]]
block = "keyboard_layout"
driver = "xkbswitch"
format = "{layout} {variant}"
interval = 1
```

* Fix formatting and linter errors.

- Less newlines.
- Use unwrap_or_else instead of running unwrap_or with a function call.
  This improves efficiency by making the function call lazy.
- Use a character instead of a single-character string for split().

* Add documentation
  • Loading branch information
roguh authored and MaxVerevkin committed Jan 16, 2022
1 parent b168bb0 commit 57c14b4
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 0 deletions.
12 changes: 12 additions & 0 deletions doc/blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,7 @@ Four drivers are available:
- `setxkbmap` which polls setxkbmap to get the current layout
- `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
- `xkbswitch` which uses [xkb-switch](https://github.com/grwlf/xkb-switch) to show the current layout and variant. This works when `setxkbmap` is used to set a comma separated list of layouts, such as `us,es,fr`.
- `sway` which can read asynchronous updates from the sway IPC

Which of these methods is appropriate will depend on your system setup.
Expand Down Expand Up @@ -985,6 +986,17 @@ block = "keyboard_layout"
driver = "kbddbus"
```

Poll `xkb-switch` for current layout and variant:

```toml
[[block]]
block = "keyboard_layout"
driver = "xkbswitch"
on_click = "xkb-switch -n"
format = "{layout} {variant}"
interval = 1
```

Listen to sway for changes:

```toml
Expand Down
55 changes: 55 additions & 0 deletions src/blocks/keyboard_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub enum KeyboardLayoutDriver {
SetXkbMap,
LocaleBus,
KbddBus,
XkbSwitch,
Sway,
}

Expand Down Expand Up @@ -411,6 +412,59 @@ impl KeyboardLayoutMonitor for Sway {
}
}

pub struct XkbSwitch;

impl XkbSwitch {
pub fn new() -> Result<XkbSwitch> {
Command::new("xkb-switch")
.output()
.block_error("keyboard_layout", "Failed to find xkb-switch in PATH")
.map(|_| XkbSwitch)
}
}

fn xkb_switch_show_layout_and_variant() -> Result<(String, Option<String>)> {
// This command should return a string like "layout(variant)" or "layout"
Command::new("xkb-switch")
.args(&["-p"])
.output()
.block_error("keyboard_layout", "Failed to execute `xkb-switch -p`.")
.and_then(|raw| {
String::from_utf8(raw.stdout).block_error("keyboard_layout", "Non-UTF8 input.")
})
.and_then(|layout_and_variant| {
let mut components = layout_and_variant.trim_end().split('(');

let layout = components
.next()
.ok_or("")
.block_error("keyboard_layout", "Unable to find keyboard layout")?
.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((layout, variant))
})
}

impl KeyboardLayoutMonitor for XkbSwitch {
fn keyboard_layout(&self) -> Result<String> {
xkb_switch_show_layout_and_variant().map(|layout_and_variant| layout_and_variant.0)
}

fn keyboard_variant(&self) -> Result<String> {
xkb_switch_show_layout_and_variant()
.map(|layout_and_variant| layout_and_variant.1.unwrap_or_else(|| "".into()))
}

fn must_poll(&self) -> bool {
true
}
}

#[derive(Deserialize, Debug, Clone)]
#[serde(default, deny_unknown_fields)]
pub struct KeyboardLayoutConfig {
Expand Down Expand Up @@ -468,6 +522,7 @@ impl ConfigBlock for KeyboardLayout {
monitor.monitor(id, send);
Box::new(monitor)
}
KeyboardLayoutDriver::XkbSwitch => Box::new(XkbSwitch::new()?),
KeyboardLayoutDriver::Sway => {
let monitor = Sway::new(block_config.sway_kb_identifier)?;
monitor.monitor(id, send);
Expand Down

0 comments on commit 57c14b4

Please sign in to comment.