Switching keyboard layout with modifier keys
I am using Debian buster/sid with Gnome 3.28.1 (and GNOME Shell 3.26.2). I use 3 keyboard layouts and would like to have dedicated keys for at least the two of them. On Windows I used to use Left Ctrl to switch to English and Right Ctrl to switch to Russian. This way I don't have to remember what is the current layout if I start typing with one of these keys.
These keys don't interfere with any of the application shortcuts. Switching occurs only if Control key was pressed and released without pressing any other keys.
For comparison, default Gnome hotkey (Win + Space) is inconvenient because it requires pressing two keys and is modal (you have to remember what is the current layout). It is good only if you switch layouts rarely.
Gnome Tweaks has an option to use Control keys to switch layouts, but with this option a Control key press gets consumed and combinations like Ctrl + C don't work (it just prints letter "c"). It makes that option unusable. Also, the indicator in the top bar doesn't show layout change if I use those keys.
I am aware that there is a similar bug 865 in X server with XKB extension which has not been fixed for 14 years and bug 36812 in Ubuntu tracker. XKB extension spec says that LockGroup action triggers on keydown and consumes the pressed key. Also, because of this bug people coming from Windows cannot use combinations like Ctrl+Shift which is used on Windows. But Gnome uses Wayland so it is unrelated here.
I tried to write a program that would read key events from /dev/input and call XkbLockGroup()
, but it doesn't switch anything.
So here is what I would like (in order of descending priority):
- use Left/Right Control in GNOME for switching layouts without breaking any hotkeys
- make keyboard indicator show actual layout
- be able to use any other combinations like Ctrl + Shift for switching layouts
- if possible fix the problem globally so that it works with any other desktop environment on Wayland out of the box
I understand C and JS so I could try to make a patch myself if it is not too difficult, but I don't understand what would be the right way to fix it. I couldn't find any documentation about how GNOME deals with layout switching, all I found was outdated and related to X server.
So I looked at the code in KeyboardManager https://gitlab.gnome.org/GNOME/gnome-shell/blob/master/js/misc/keyboardManager.js#L63 and I see that it calls lock_layout_group()
in Mutter library which ends up calling XkbLockGroup()
for X11 backend (that doesn't work on my system) and somewhere inside Clutter for "native backend" (which is, I guess, the name for Wayland). The code inside Clutter calls xkb_state_update_mask() and sets locked group in xkb_state
from xkbcommon library.
I also looked how Gnome Tweaks works (when choosing layout switch options) and found that it changes an option in gsettings, and gnome-shell reacts to that and passes new xkbOptions to xkbcommon.
So, as I understand, gnome-shell has two ways to handle layout switching:
- for Win + Space, it handles switching itself
- for keys chosen in Gnome Tweaks, it uses xkbcommon
To fix the problem globally I assume I have to fix it in xkbcommon library. That might take time and could be too difficult so first I would prefer to find an easier solution.
So can you please answer these questions:
- what would be the best way to fix the problem?
- is there any CLI command or library function that I can call to choose specific keyboard layout? Maybe I could listen to /dev/input events and call it when necessary.
- am I right that to fix the problem globally I will have to fix it in xkbcommon which inherited the problem from XKB?
- how do I fix the problem with an indicator showing the wrong layout? I guess gnome-shell should somehow learn about layout being switched inside xkbcommon? For example, by polling
xkb_state::xkb_state_key_get_layout()
for changes after every key pressed.