Skip to content

install.sh

Files / keyboard-layout / install.sh

Files in keyboard-layout/

DownloadRawShell script ยท 98 lines ยท 4.8 KB

From Dvorak with QWERTY's brackets on KDE Plasma (Wayland). MIT licence: LICENSE.

keyboard-layout/install.sh
#!/usr/bin/env bash
# Installs the dvorak-qwerty keyboard layout (Dvorak, with QWERTY's [ ] { } - _ = + keys, and QWERTY
# letters while Ctrl or Meta is held) into /etc/xkb and adds it after your current KDE layouts. Turns
# on the XKB option custom:types for its key types, and sets Meta+Alt+K (Switch to Next Keyboard
# Layout) when that shortcut is empty. KWin only searches /etc/xkb if it existed when KWin started,
# so on the first install, log out and back in. Run as yourself, not with sudo: it asks for the
# password once. Safe to run again. KDE Plasma 6 on Wayland.
set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
name=dvorak-qwerty
fail() { echo "๐Ÿ”ด $*"; echo "๐Ÿ”ด FAILED"; exit 1; }
[[ $EUID -ne 0 ]] || fail "run it as yourself, not root: it changes your KDE settings"
for f in "$name" custom-types evdev.xml; do [[ -f "$here/$f" ]] || fail "missing $here/$f (keep it next to this script)"; done
# 1. Compile check
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT
mkdir "$tmp/symbols" "$tmp/types" && cp "$here/$name" "$tmp/symbols/" && cp "$here/custom-types" "$tmp/types/custom"
cat > "$tmp/km.xkb" <<EOF
xkb_keymap {
xkb_keycodes { include "evdev+aliases(qwerty)" };
xkb_types { include "complete+custom" };
xkb_compat { include "complete" };
xkb_symbols { include "pc+us+$name:2+inet(evdev)" };
};
EOF
xkbcomp -w0 -I"$tmp" -xkb "$tmp/km.xkb" "$tmp/out.xkb" || fail "layout doesn't compile"
echo "๐ŸŸข Layout compiles"
# 2. KWin reads /etc/xkb/symbols and /etc/xkb/types; System Settings lists layouts from
# /etc/xkb/rules/evdev.xml. types/custom is a shared name: don't overwrite someone else's.
if [[ -f /etc/xkb/types/custom ]] && ! grep -q DVQ_ALPHA /etc/xkb/types/custom; then
fail "/etc/xkb/types/custom exists and isn't this layout's: merge custom-types into it by hand"
fi
sudo install -Dm644 "$here/$name" "/etc/xkb/symbols/$name"
sudo install -Dm644 "$here/custom-types" /etc/xkb/types/custom
sudo install -Dm644 "$here/evdev.xml" /etc/xkb/rules/evdev.xml
echo "๐ŸŸข Copied to /etc/xkb"
# 3. KDE settings in kxkbrc. --notify makes KWin reload, but only when a value changes.
kr() { kreadconfig6 --file kxkbrc --group Layout --key "$1"; }
kw() { kwriteconfig6 --notify --file kxkbrc --group Layout --key "$1" "$2"; }
# 3a. Option custom:types (loads types/custom). KWin only uses Options when ResetOldOptions is true;
# otherwise it uses the system's (localectl), so start from those.
options="$(kr Options)"
[[ "$(kr ResetOldOptions)" == true ]] || options="$(localectl status | sed -n 's/^ *X11 Options: //p')"
if [[ ",$options," == *",custom:types,"* ]]; then
echo "๐ŸŸข Option custom:types already on"
else
options="${options:+$options,}custom:types"
kw ResetOldOptions true
kw Options "$options"
echo "๐ŸŸข Options now: $options"
fi
# 3b. Add the layout after the current ones. With no LayoutList yet, KWin uses the system layout.
layouts="$(kr LayoutList)"; variants="$(kr VariantList)"
if [[ -z "$layouts" ]]; then
layouts="$(localectl status | sed -n 's/^ *X11 Layout: //p')"
variants="$(localectl status | sed -n 's/^ *X11 Variant: //p')"
layouts="${layouts:-us}"
fi
if [[ ",$layouts," == *",$name,"* ]]; then
echo "๐ŸŸข Already in your layouts: $layouts"
else
IFS=, read -ra L <<< "$layouts"; IFS=, read -ra V <<< "$variants"; IFS=, read -ra N <<< "$(kr DisplayNames)"
for i in "${!L[@]}"; do V[i]="${V[i]:-}"; N[i]="${N[i]:-}"; done
L+=("$name"); V+=(""); N+=("dvq")
kw Use true
kw VariantList "$(IFS=,; echo "${V[*]}")"
kw DisplayNames "$(IFS=,; echo "${N[*]}")"
kw LayoutList "$(IFS=,; echo "${L[*]}")"
echo "๐ŸŸข Layouts now: $(IFS=,; echo "${L[*]}")"
fi
for _ in 1 2 3 4 5; do
sleep 1
loaded="$(qdbus6 --literal org.kde.keyboard /Layouts org.kde.KeyboardLayouts.getLayoutsList)"
[[ "$loaded" == *"\"$name\""* ]] && break
done
if [[ "$loaded" == *"\"$name\""* ]]; then
echo "๐ŸŸข KWin has loaded it"
else
# The layout compiled in step 1, so it's KWin's search paths: journalctl -b _COMM=kwin_wayland | grep XKB-338
echo "๐ŸŸ  KWin hasn't loaded it yet: it only searches /etc/xkb if that existed when KWin started. Log out and back in"
fi
# 4. Shortcut: kglobalaccel keys are a(ai), Meta+Alt+K = 0x10000000 + 0x08000000 + 0x4b
action=("KDE Keyboard Layout Switcher" "Switch to Next Keyboard Layout" "Keyboard Layout Switcher" "Switch to Next Keyboard Layout")
gacc=(busctl --user call org.kde.kglobalaccel /kglobalaccel org.kde.KGlobalAccel)
case "$("${gacc[@]}" shortcutKeys as 4 "${action[@]}")" in
"a(ai) 0" | "a(ai) 1 4 0 0 0 0")
"${gacc[@]}" setForeignShortcutKeys 'asa(ai)' 4 "${action[@]}" 1 4 402653259 0 0 0
echo "๐ŸŸข Shortcut set: Meta+Alt+K" ;;
*) echo "๐ŸŸข Layout shortcut already set, left as is" ;;
esac
echo "๐ŸŸข SUCCESS: switch layouts with Meta+Alt+K or the tray icon (dvq), after a log-out if KWin hasn't loaded it"

Text, photos and drawings: CC BY 4.0. Code: MIT. Share and adapt them, crediting HomeLab Guides (blog.jacq.cc). Licence and privacy