Alt-Shift-Toggle is a fix for the cluncky behavior in language (keyboard layout) switching in linux using Alt+Shift combo. Both Alt and Shift are modifier keys and the combo can be used to switch keyboard layouts alongside other keyboard shortcut (for example Alt+Shift+F in code editors usully formats the code). To achieve this we use 4 states: alt_state and shift_state reflects whether Alt or Shift key is pressed. The group_state becomes True when both Alt and Shift keys are pressed at the same time. The fail_state prevents language switch if another key is pressed alongside Alt and Shift. The language switch occurs on key up, and not on key down when group_state is True and fail_state is False. The fail_state is reset only when both Alt and Shift are released.
So, to switch the language, the user must:
- Press
AltorShiftkey; - Press
Shift(ifAlton step 1), or pressAlt(ifShifton step 1); - Release either
AltorShiftkey without pressing any other key; - To switch again, press the released key in step 3 and go to step 3;
To use keyboard shortcut with Alt and Shift (for example Alt+Shift+F) and switch language after that:
- Press
AltandShift; - Press the
Fkey to useAlt+Shift+F; - Release all pressed keys;
- Go to step 1 in language switch;
Pressing Alt and Shift before fail_state resets will not switch the language. This allow Alt+Shift combo to switch the language without interference with keyboard shortcuts.
The linux is famously clunky for international users used to Windows-like language switching. This is 20+ years problem in XKB, not considered a bug, since there is no way to encode keyboard shortcuts with fail state. This is an attempt to add this functionality non-intrusively, without modifying the XKB itself (although this problem should be fixed in XKB).
The solution consists of two individual programs communicating through signals SIGUSR1 and SIGUSR2.
This program is notifier that reads keyboard keys from dev/input/event* files and sends SIGUSR1 to any subscribed processes when the above requirements for switching language/keyboard layout is met. The program operates as systemd service as root.
In this version, the application is rewritten in python, allowing simpler code. You should examine this code (as any code meant to run as root) before install it. The code reads keyboard keys, so you should not blindly trust programs that do so.
- Completely rewritten in python.
- Detection of keyboard-related
/dev/input/event*can happen not only during the initialization, but also when a new input device is plugged-in. This allow language switch to occur from newly plugged USB keyboard without resetting the service. - Waiting for signal happens in the main thread using
sigwaitinfoinstead ofsigaction, allowing signals to be observed without interrupting thereadsyscall from the keyboard observing threads. - Each detected keyboard have its own thread now.
This program perform keyboard layout/group switch whenever it receives SIGUSR1 from the expected process. This operates as non-root and it is added by the GUI/X startup system. The reason for separate program is:
- X window system has completely different lifetime compared to
systemd; - The connection to X window system passes through
.Xauthoritycookie;
Therefore, detecing when X is finally available and connecting to it from systemd service is significantly more challenging task compared to having separate program that is started by the X window system itself. As a result, a separate program is easier solution, but it requires the two program to communicate, without exposing the system to security risk. As a result, alt-shift-notify notifies only for Alt+Shift combo described above, and sends SIGUSR1 only to subscribed processes and only when language switch must occur. The xkb_next_layout can only subscribe as non-root if the executable has cap_kill capability (kill(2) can be used to send any signal, not just SIGKILL).
- The application now uses
inotifyto observe when thealt-shift-notifyPID file will appear. This allow thealt-shift-notifyservice to be reset without resettingxkb_next_layout. Whenever the PID file changes,SIGUSR1is send to the new PID. On exit,SIGUSR2is send if the current PID is not zero. - The application now uses
sigwaitinfoin separate thread, instead ofsigactioncallback, so signals does not interruptread()frominotify.
- Install
python3andlibudev. - Run
python3 install.py <install-dir>or./install.py <install-dir>under root. In case of fatal errors, check if any requirements are missing and install them. Some errors fromwheelare expected, but do not prevent installingpyudev, so you can ignore them. - Optional: Modify
${INSTALL_DIR}/alt-shift-notify.service, if necessary. - Run
systemctl enable alt-shift-notify.service - Reboot or run
service alt-shift-notify start
For more options run ${INSTALL_DIR}/venv/bin/python main.py --help. This allow switching the PID file location (see --pid-file) and print more information in stdout (see --verbose);
- Install
cmake; - Create a build directory;
- Run
cmake <source-directory>; - Run
make; - Run
make install(might requiresudo); - Add
xkb_next_layoutto theStartup Applicationin your GUI environment;
The installation automatically adds cap_kill capability to the installed xkb_next_layout;
You can also run xkb_next_layout in bash to test and observe its behavior.
- Add
--verboseoption and print only when verbose;