I use both Russian and English keyboard layouts on my laptop, and usually don’t think about which one is active.
Until I unlock the laptop.
The KDE Plasma lock screen inherits the last active keyboard layout from the session. So if I was writing something in Russian before closing the lid, I can open the laptop the next morning, put my password in and got locked laptop for a couple of seconds.
Only after a second I notice the RU indicator in the corner.
It’s a small problem, but an annoying one. Especially when using a fingerprint reader: if the fingerprint doesn’t work for some reason and I have to type the password, I don’t want to think about the keyboard layout first.
What I really wanted was a simple rule:
The lock screen should always use the English keyboard layout.
There doesn’t seem to be a KDE setting for this, so I decided to solve it with a small user service.
How KDE handles the layout
KDE exposes its keyboard layout manager over D-Bus. The relevant interface is:
org.kde.keyboard /Layouts
It provides methods for getting the current layout, listing available layouts, and switching between them:
uint org.kde.KeyboardLayouts.getLayout()
a(sss) org.kde.KeyboardLayouts.getLayoutsList()
bool org.kde.KeyboardLayouts.setLayout(uint index)
KDE also sends a signal whenever the screen is locked or unlocked:
org.freedesktop.ScreenSaver.ActiveChanged(boolean)
So the idea is fairly straightforward:
- Wait for
ActiveChanged(true). - Find the index of the
uslayout. - Tell KDE to switch to it.
There is one important detail here: this has to run as a user service, not a system service. The signal is sent over the user’s session D-Bus, so a system-level daemon isn’t the right place to listen for it.
A small Plasma 6 surprise
My first version of the script didn’t work.
The service was running, the D-Bus signal was being received, and setLayout was returning successfully. It was just switching to the wrong layout.
The first problem was the format returned by getLayoutsList.
In Plasma 5, this API returned a simple list of strings. In Plasma 6, it returns an array of structures:
a(sss)
For example:
$ qdbus6 --literal org.kde.keyboard /Layouts org.kde.KeyboardLayouts.getLayoutsList
[Argument: a(sss) {[Argument: (sss) "ru", "", "Russian"], [Argument: (sss) "us", "", "English (US)"]}]
The first value is the short layout name, followed by the variant and the display name.
There is another small difference from older versions: getCurrentLayout is no longer available. The current method is simply getLayout.
So the script needs to account for the Plasma 6 API rather than assuming the old format.
Don’t hardcode the layout index
There was another trap.
setLayout takes an index, not a layout name. The index corresponds to the order of layouts configured in KDE.
On my system the list was:
0 → ru
1 → us
So this:
qdbus6 org.kde.keyboard /Layouts org.kde.KeyboardLayouts.setLayout 0
would happily switch to Russian.
The solution is to look up the us layout at runtime instead of assuming that it is always index 0 or 1.
The script
I put the following script at ~/.local/bin/kde-lock-layout.sh:
#!/usr/bin/env bash
# Force the "us" keyboard layout every time the screen locks,
# so the password prompt always starts in English.
set -u
TARGET="us"
# Plasma 6 ships qdbus6 (qt6-tools); qdbus is the Qt5 name.
qdbus_call() {
if command -v qdbus6 >/dev/null 2>&1; then
qdbus6 "$@"
elif command -v qdbus >/dev/null 2>&1; then
qdbus "$@"
else
return 1
fi
}
# Short names of all layouts, in KDE's list order. Plasma 6 returns
# a(sss) structs - printable only with --literal; Plasma 5 returned a
# plain string list, which the else-branch handles.
list_layout_names() {
local out
out=$(qdbus_call --literal org.kde.keyboard /Layouts org.kde.KeyboardLayouts.getLayoutsList 2>/dev/null)
if [[ "$out" == *'(sss)'* ]]; then
grep -oP '\(sss\) "\K[^"]+' <<<"$out"
else
qdbus_call org.kde.keyboard /Layouts org.kde.KeyboardLayouts.getLayoutsList 2>/dev/null
fi
}
# Index of the target layout in KDE's layout list.
layout_index() {
local i=0 l
while IFS= read -r l; do
[[ "$l" == "$TARGET"* ]] && { echo "$i"; return; }
i=$((i + 1))
done < <(list_layout_names)
echo 0
}
switch_layout() {
local idx
idx=$(layout_index)
# setLayout's boolean reply would otherwise end up in the journal.
qdbus_call org.kde.keyboard /Layouts org.kde.KeyboardLayouts.setLayout "$idx" >/dev/null \
|| busctl --user call org.kde.keyboard /Layouts org.kde.KeyboardLayouts setLayout u "$idx" >/dev/null
}
# KDE emits this signal on the session bus when the screen locks.
dbus-monitor --session "type='signal',interface='org.freedesktop.ScreenSaver',member='ActiveChanged'" |
while IFS= read -r line; do
case "$line" in
*"boolean true"*) switch_layout ;;
esac
done
The script listens to the session bus and does nothing until the screen is actually locked. When ActiveChanged(true) arrives, it finds the us layout and activates it.
I deliberately look up the layout every time instead of caching its index. This means changing the order of layouts in KDE’s settings doesn’t break the script.
Running it with systemd
To keep the script running in the background, I created a user systemd service:
~/.config/systemd/user/kde-lock-layout.service
[Unit]
Description=Force English keyboard layout when the screen locks
After=graphical-session.target
PartOf=graphical-session.target
[Service]
ExecStart=%h/.local/bin/kde-lock-layout.sh
Restart=on-failure
RestartSec=3
[Install]
WantedBy=graphical-session.target
Using graphical-session.target is intentional. The service belongs to the graphical Plasma session, where the session D-Bus is available.
Then enable it:
install -Dm755 kde-lock-layout.sh ~/.local/bin/kde-lock-layout.sh
install -Dm644 kde-lock-layout.service ~/.config/systemd/user/kde-lock-layout.service
systemctl --user daemon-reload
systemctl --user enable --now kde-lock-layout.service
Testing it
The whole thing can be tested without physically closing and opening the laptop.
First, switch to Russian:
$ qdbus6 org.kde.keyboard /Layouts org.kde.KeyboardLayouts.setLayout 0
Then lock the session:
$ qdbus6 org.freedesktop.ScreenSaver /ScreenSaver org.freedesktop.ScreenSaver.Lock
And check the active layout after a moment:
$ qdbus6 org.kde.keyboard /Layouts org.kde.KeyboardLayouts.getLayout
1
On my machine, 1 is us, so the switch worked.
If something goes wrong, the service log is available with:
journalctl --user -u kde-lock-layout.service
One limitation
This only affects the lock screen inside an existing Plasma session.
The SDDM login screen is a separate process with its own keyboard-layout configuration. So if you also want to control the layout shown on the login screen after a reboot, that needs to be configured separately through SDDM.
For my use case, though, this is enough. Now I can switch to Russian, close the lid, open it again, and type the password without first checking which layout KDE decided to restore.