[entry] Vim like layer for Xorg and Wayland
This commit is contained in:
parent
3dc3ba1d8c
commit
cbb799f0cd
|
@ -0,0 +1,312 @@
|
|||
+++
|
||||
title = "Vim-like Layer for Xorg and Wayland"
|
||||
date = "2020-08-25"
|
||||
author = "Ceda EI"
|
||||
tags = ["xorg", "wayland", "vim"]
|
||||
keywords = ["xorg", "wayland", "vim"]
|
||||
description = "Create a layer of shortcuts with xkb for Xorg and Wayland"
|
||||
showFullContent = false
|
||||
+++
|
||||
|
||||
# The Goal
|
||||
|
||||
## Insert Mode
|
||||
![Insert Mode: A keyboard layout similar to normal QWERTY
|
||||
layout](/images/insert_mode.jpg)
|
||||
## Normal Mode
|
||||
![Normal Mode: A keyboard layout with the alphabet keys replaced with shortcut
|
||||
key](/images/normal_mode.jpg)
|
||||
|
||||
Inspired by vim, I wanted to create a layer on top of my keyboard which worked
|
||||
like a shortcut layer. So, to start off, I found out about XKB[^1]. XKB is the
|
||||
Xorg Keyboard Extension which tells Xorg on how to react to input from
|
||||
keyboard. After reading through some source code, I found out that Xorg has
|
||||
support for function keys F1 - F35[^2]. The general idea here was:
|
||||
|
||||
- Create an insert mode layout for text input.
|
||||
- Replace keys with relevant keys in normal mode (e.g. replace j with Down) and
|
||||
for keys that require executing a command, replace then with a function key
|
||||
above F12 (e.g. replace q with F13).
|
||||
- Bind all the function keys above F12 to the respective functions.
|
||||
|
||||
To start off, I began a fresh Xorg session with nothing modifying the keys
|
||||
(removed `xmodmap` from startup) and first dumped the current layout into a
|
||||
file.
|
||||
|
||||
```bash
|
||||
xkbcomp $DISPLAY ~/.xkb/insert.xkb
|
||||
```
|
||||
|
||||
This was my starting point. I made changes to this file which were common to
|
||||
both Insert and Normal mode. e.g. replaced `Caps Lock` with `Ctrl` and made
|
||||
`Shift+Caps Lock` `Caps Lock`. Also, I unbound `Alt_R` as a modifier so that I
|
||||
could use that as a switch between Normal and Insert Mode.
|
||||
|
||||
Here is a diff between the original layout and Insert mode.
|
||||
|
||||
```
|
||||
1323c1321
|
||||
< key <CAPS> { [ Caps_Lock ] };
|
||||
---
|
||||
> key <CAPS> { [ Control_L, Caps_Lock ] };
|
||||
1551c1549
|
||||
< modifier_map Lock { <CAPS> };
|
||||
---
|
||||
> modifier_map Control { <CAPS> };
|
||||
1555d1552
|
||||
< modifier_map Mod1 { <RALT> };
|
||||
```
|
||||
|
||||
Next, I copied `~/.xkb/insert.xkb` to `~/.xkb/normal.xkb`. I replaced keys as
|
||||
per the plan.
|
||||
|
||||
Here is a diff between Insert mode and Normal mode.
|
||||
|
||||
```diff
|
||||
1200c1200
|
||||
< symbols[Group1]= [ q, Q ]
|
||||
---
|
||||
> symbols[Group1]= [F13]
|
||||
1204c1204
|
||||
< symbols[Group1]= [ w, W ]
|
||||
---
|
||||
> symbols[Group1]= [F14]
|
||||
1208c1208
|
||||
< symbols[Group1]= [ e, E ]
|
||||
---
|
||||
> symbols[Group1]= [F15]
|
||||
1212c1212
|
||||
< symbols[Group1]= [ r, R ]
|
||||
---
|
||||
> symbols[Group1]= [F16]
|
||||
1216c1216
|
||||
< symbols[Group1]= [ t, T ]
|
||||
---
|
||||
> symbols[Group1]= [F17]
|
||||
1220c1220
|
||||
< symbols[Group1]= [ y, Y ]
|
||||
---
|
||||
> symbols[Group1]= [F18]
|
||||
1224c1224
|
||||
< symbols[Group1]= [ u, U ]
|
||||
---
|
||||
> symbols[Group1]= [F19]
|
||||
1228c1228
|
||||
< symbols[Group1]= [ i, I ]
|
||||
---
|
||||
> symbols[Group1]= [Alt_R]
|
||||
1232c1232
|
||||
< symbols[Group1]= [ o, O ]
|
||||
---
|
||||
> symbols[Group1]= [F20]
|
||||
1236c1236
|
||||
< symbols[Group1]= [ p, P ]
|
||||
---
|
||||
> symbols[Group1]= [F21]
|
||||
1244c1244
|
||||
< symbols[Group1]= [ a, A ]
|
||||
---
|
||||
> symbols[Group1]= [F22]
|
||||
1248c1248
|
||||
< symbols[Group1]= [ s, S ]
|
||||
---
|
||||
> symbols[Group1]= [Delete]
|
||||
1252c1252
|
||||
< symbols[Group1]= [ d, D ]
|
||||
---
|
||||
> symbols[Group1]= [BackSpace]
|
||||
1256c1256
|
||||
< symbols[Group1]= [ f, F ]
|
||||
---
|
||||
> symbols[Group1]= [Home]
|
||||
1260c1260
|
||||
< symbols[Group1]= [ g, G ]
|
||||
---
|
||||
> symbols[Group1]= [End]
|
||||
1264c1264
|
||||
< symbols[Group1]= [ h, H ]
|
||||
---
|
||||
> symbols[Group1]= [Left]
|
||||
1268c1268
|
||||
< symbols[Group1]= [ j, J ]
|
||||
---
|
||||
> symbols[Group1]= [Down]
|
||||
1272c1272
|
||||
< symbols[Group1]= [ k, K ]
|
||||
---
|
||||
> symbols[Group1]= [Up]
|
||||
1276c1276
|
||||
< symbols[Group1]= [ l, L ]
|
||||
---
|
||||
> symbols[Group1]= [Right]
|
||||
1285c1285
|
||||
< symbols[Group1]= [ z, Z ]
|
||||
---
|
||||
> symbols[Group1]= [F23]
|
||||
1289c1289
|
||||
< symbols[Group1]= [ x, X ]
|
||||
---
|
||||
> symbols[Group1]= [F24]
|
||||
1293c1293
|
||||
< symbols[Group1]= [ c, C ]
|
||||
---
|
||||
> symbols[Group1]= [F25]
|
||||
1297c1297
|
||||
< symbols[Group1]= [ v, V ]
|
||||
---
|
||||
> symbols[Group1]= [F26]
|
||||
1301c1301
|
||||
< symbols[Group1]= [ b, B ]
|
||||
---
|
||||
> symbols[Group1]= [F27]
|
||||
1305c1305
|
||||
< symbols[Group1]= [ n, N ]
|
||||
---
|
||||
> symbols[Group1]= [Next]
|
||||
1309c1309
|
||||
< symbols[Group1]= [ m, M ]
|
||||
---
|
||||
> symbols[Group1]= [Prior]
|
||||
```
|
||||
|
||||
At this point, `normal.xkb` file defines the following layout.
|
||||
|
||||
![Normal Mode: A keyboard ](/images/normal_mode_unbound.jpg)
|
||||
|
||||
Now, we need a script that switches between layouts. To load an layout in Xorg, we use
|
||||
|
||||
```bash
|
||||
xkbcomp ~/.xkb/normal.xkb "$DISPLAY"
|
||||
```
|
||||
|
||||
Sway supports this via the input command in the following form.
|
||||
|
||||
```bash
|
||||
swaymsg input '*' xkb_file ~/.xkb/normal.xkb
|
||||
```
|
||||
|
||||
The following script cycles through the layouts when it is called. It also
|
||||
allows to add more layouts later (just add them to layouts array and it will
|
||||
cycle in the order of the array).
|
||||
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# Usage: xkb_swapper.sh [layout_name]
|
||||
|
||||
function set_layout() {
|
||||
echo "Setting layout to $1"
|
||||
if [[ -v WAYLAND_DISPLAY ]]; then
|
||||
swaymsg input '*' xkb_file ~/.xkb/"$1".xkb
|
||||
else
|
||||
xkbcomp ~/.xkb/"$1".xkb "$DISPLAY"
|
||||
fi
|
||||
echo "$1" > ~/.cache/xkb-curr-"$DISPLAY"
|
||||
}
|
||||
layouts=(insert normal)
|
||||
current_layout=$(cat ~/.cache/xkb-curr-"$DISPLAY" || echo "")
|
||||
|
||||
if [[ $1 != "" ]]; then
|
||||
set_layout "$1"
|
||||
exit
|
||||
fi
|
||||
if [[ $current_layout == "" ]]; then
|
||||
echo "No current layout found!"
|
||||
set_layout "${layouts[0]}"
|
||||
fi
|
||||
|
||||
i=0
|
||||
while [[ $i -lt ${#layouts[@]} ]]; do
|
||||
if [[ $current_layout == "${layouts[$i]}" ]]; then
|
||||
new_idx="$((i+1))"
|
||||
if [[ $new_idx -eq ${#layouts[@]} ]]; then
|
||||
set_layout "${layouts[0]}"
|
||||
else
|
||||
set_layout "${layouts[$new_idx]}"
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
((i++))
|
||||
done
|
||||
|
||||
echo "Current Layout doesn't exist!"
|
||||
set_layout "${layouts[0]}"
|
||||
```
|
||||
|
||||
The above script works with all Xorg based DE/WMs as well as Sway (wayland
|
||||
compositor). I saved it as `xkb_swapper.sh` in my `PATH`. Calling the script
|
||||
without any argument cycles through the layouts. If arguments are passed, the
|
||||
first argument is taken as layout name and layout is changed to that.
|
||||
|
||||
The last step is binding the function keys and `Alt_R` to commands to execute.
|
||||
Here are some of the parts of my i3 config that bind the function keys.
|
||||
|
||||
```
|
||||
bindsym Alt_R exec xkb_swapper.sh
|
||||
bindsym 0xffca kill
|
||||
bindsym 0xffcf exec volchange -5
|
||||
bindsym 0xffd0 exec volchange +5
|
||||
bindsym 0xffd1 exec brightness -200
|
||||
bindsym 0xffd2 exec brightness +200
|
||||
bindsym 0xffcb exec mpc prev
|
||||
bindsym 0xffcc exec mpc toggle
|
||||
bindsym 0xffcd exec mpc next
|
||||
```
|
||||
|
||||
`i3` doesn't seem to accept `F13` - `F35` as keynames however it accepts the
|
||||
keycodes[^2]. Here is a small list for easy access.
|
||||
|
||||
```
|
||||
0xffbe F1
|
||||
0xffbf F2
|
||||
0xffc0 F3
|
||||
0xffc1 F4
|
||||
0xffc2 F5
|
||||
0xffc3 F6
|
||||
0xffc4 F7
|
||||
0xffc5 F8
|
||||
0xffc6 F9
|
||||
0xffc7 F10
|
||||
0xffc8 F11
|
||||
0xffc9 F12
|
||||
0xffca F13
|
||||
0xffcb F14
|
||||
0xffcc F15
|
||||
0xffcd F16
|
||||
0xffce F17
|
||||
0xffcf F18
|
||||
0xffd0 F19
|
||||
0xffd1 F20
|
||||
0xffd2 F21
|
||||
0xffd3 F22
|
||||
0xffd4 F23
|
||||
0xffd5 F24
|
||||
0xffd6 F25
|
||||
0xffd7 F26
|
||||
0xffd8 F27
|
||||
0xffd9 F28
|
||||
0xffda F29
|
||||
0xffdb F30
|
||||
0xffdc F31
|
||||
0xffdd F32
|
||||
0xffde F33
|
||||
0xffdf F34
|
||||
0xffe0 F35
|
||||
```
|
||||
|
||||
# Bonus: Displaying the current mode in your bar
|
||||
|
||||
The script stores the mode in `~/.cache/xkb-curr-$DISPLAY`. `cat` that and
|
||||
wrap in your bar's config. Here is my config for
|
||||
[i3status-rust](https://github.com/greshake/i3status-rust).
|
||||
|
||||
```toml
|
||||
[[block]]
|
||||
block = "custom"
|
||||
command = "echo -en '\\uf11c '; cat ~/.cache/xkb-curr-$DISPLAY"
|
||||
interval = 0.5
|
||||
```
|
||||
|
||||
|
||||
[^1]: As always, the [Arch Wiki page on XKB](https://wiki.archlinux.org/index.php/X_keyboard_extension) is a nice place to start.
|
||||
[^2]: You can find all the defined keys in `/usr/include/X11/keysymdef.h`.
|
Binary file not shown.
After Width: | Height: | Size: 142 KiB |
Binary file not shown.
After Width: | Height: | Size: 148 KiB |
Binary file not shown.
After Width: | Height: | Size: 193 KiB |
Loading…
Reference in New Issue