Welcome back to yet another episode of solving extremely obscure Linux problems that probably zero other people worldwide are suffering from! This time, the problem is that on my convertible laptop, I sometimes use both Fcitx 5 for typing Chinese and Maliit Keyboard as a touchscreen keyboard. In Wayland, both of these use the same virtual keyboard protocol and thus only one of them can be active at the same time. I’m not a Wayland expert so this actually might be a limitation in KWin, not in the Wayland protocol. (I have a friend who’s a Wayland expert but that’s because he lives there. Wayland the town, not the protocol.) Fortunately, I only use Fcitx 5 when the laptop is in its normal mode with the physical keyboard accessible, and Maliit Keyboard when the laptop is in tablet mode with the physical keyboard folded back. So there lies the problem: How do I switch the virtual keyboard when entering and exiting tablet mode?
It’s easy to manually do this in the Plasma system settings app. Sure, Plasma is really nice and customizable and for the most part works really well on convertible laptops, but I’m over here tearing my hair out since there’s no easy way to configure it from the command line or from a script! They do provide a CLI utility to edit Plasma config files, for instance, kwriteconfig6 --file kwinrc --group Wayland --key InputMethod /usr/share/applications/fcitx5-wayland-launcher.desktop
to set the virtual keyboard to Fcitx 5, but Plasma (specifically KWin) doesn’t recognize the config change. I suspected the system settings app does some additional D-Bus shenanigans, and after dredging through the output of dbus-monitor
, I found I was right!
signal time=1727303907.040280 sender=:1.403 -> destination=(null destination) serial=113 path=/kwinrc; interface=org.kde.kconfig.notify; member=ConfigChanged
array [
dict entry(
string "Wayland"
array [
array of bytes "InputMethod"
]
)
]
Basically, that particular D-Bus signal tells KWin to peak at the new value of InputMethod
. That translates into the command busctl --user emit /kwinrc org.kde.kconfig.notify ConfigChanged "a{saay}" 1 Wayland 1 11 73 110 112 117 116 77 101 116 104 111 100
. The weird numbers are just InputMethod
in ASCII as an array of bytes. Did you know D-Bus has its own type and serialization system? Well, I had to learn it for that command. It’s also possible to detect entering and exiting tablet mode using D-Bus.
Alright, so now time for a Bash script, in ~/.config/autostart/switchvirtualkeyboard.sh
:
#!/bin/bash
switch() {
echo "Starting $1"
kwriteconfig6 --file kwinrc --group Wayland --key InputMethod "$1"
busctl --user emit /kwinrc org.kde.kconfig.notify ConfigChanged "a{saay}" 1 Wayland 1 11 73 110 112 117 116 77 101 116 104 111 100
}
busctl --user monitor --match "type='signal',interface='org.kde.KWin.TabletModeManager',member='tabletModeChanged'" | \
while read -r line; do
if [[ $line == *"true"* ]]; then
switch /usr/share/applications/com.github.maliit.keyboard.desktop
elif [[ $line == *"false"* ]]; then
switch /usr/share/applications/fcitx5-wayland-launcher.desktop
fi
done
Or alternatively, a Fish version of the same script:
function setvirtualkeyboard
echo "Starting $argv[1]"
kwriteconfig6 --file kwinrc --group Wayland --key InputMethod "$argv[1]"
busctl --user emit /kwinrc org.kde.kconfig.notify ConfigChanged "a{saay}" 1 Wayland 1 11 73 110 112 117 116 77 101 116 104 111 100
end
function switchvirtualkeyboard
busctl --user monitor --match "type='signal',interface='org.kde.KWin.TabletModeManager',member='tabletModeChanged'" | \
while read -l line
if string match -q "*true*" $line
setvirtualkeyboard /usr/share/applications/com.github.maliit.keyboard.desktop
else if string match -q "*false*" $line
setvirtualkeyboard /usr/share/applications/fcitx5-wayland-launcher.desktop
end
end
end
To make Plasma start the Bash script at login, add a file ~/.config/autostart/switchvirtualkeyboard.desktop
. Internally, systemd-xdg-autostart-generator translates the .desktop
file into a systemd service.
[Desktop Entry]
Exec=bash ~/.config/autostart/switchvirtualkeyboard.sh
Name=switchvirtualkeyboard
Type=Application
For the Fish version, you can directly write a systemd service and start and enable it:
[Unit]
Description=Switch the virtual keyboard when entering and exiting tablet mode
[Service]
ExecStart=fish -c switchvirtualkeyboard
[Install]
WantedBy=default.target
As you might expect from all these obscure Linux components, there’s not much documentation about this stuff and the script was really tricky to debug. It works now, but it took me way more time to write this than to the amount of time it would hypothetically take me to manually switch the virtual keyboard in the settings app every time in the future. But hey, I got to learn some cool things about D-Bus and systemd!
Lastly, this is just one way that I found to accomplish this task, but I bet there are better ways. For instance, I considered writing a KWin script instead, but the API seemed a bit limited, since for instance, callDBus
can only call D-Bus methods and can’t send D-Bus signals. If you have any ideas for improvement, I’d love to hear it!