Переключение раскладки в X11 командой из скрипта
Опубликовано 23.02.2024 в OpenBSD
Вот настроили мы по-старинке переключение раскладок в X11, например, добавив куда-нибудь в ~/.xsession
что-то по своему вкусу, у меня так: setxkbmap -layout us,ru -option grp:caps_toggle,compose=ralt
. Ну или даже, чтоб олдскулы свело, прописали это же самое в формате конфига для иксов, как деды воевали^W конфигурировали.
И есть у нас простое и незатейливое требование: из скрипта командою переключать в некоторых случаях раскладку на заведомо нам нужную. Ну, например, так: в моем привычном до боли тайловом менеджере окон i3 на первом рабочем столе (workspace) традиционно висят открытыми пара терминалов, привык я так. И удобно мне весьма при переключении на этот самый первый workspace и раскладку клавиатуры переключать на язык потенциального^W состоявшегося противника, ну, чтобы не начать вводить команды кириллицей. Чего проще, забиндить в ~/.config/i3/config
на соответствующую комбинацию клавиш не только переход на первый workspace, но и переключение на нужную раскладку? Или вот, ещё нагляднее: мы блокируем экран, скажем, при бездействии 10 минут и для разблокировки его требуем ввода пароля. Было бы весьма удобно перед блокировкой экрана и клавиатуру сразу переключить на ту раскладку, на которой предстоит потом вводить пароль.
Взгуглив, вы легко найдете решение, предлагаемое на каждом углу: а используйте-ка что-то вроде setxkbmap layout en
и будет вам счастье. Да, это сработает, но есть нюанс - таким образом, вы включите единственную раскладку (layout), а все те настройки и способ переключения, что описаны в первом абзаце, просто перестанут работать. И если ко всем прочим радостям вы пользуетесь каким-то индикатором раскладки, тот вовсе станет показывать нерелевантную чушь.
Заметим, однако, что в иксах всё нам нужное уже заложено из коробки: есть чудесные клавиши с keysym = ISO_First_Group для переключения на первую раскладку, ISO_Last_Group для переключения, соответственно, на последнюю, и ISO_Next_Group для переключения в цикле. И всё, что нам нужно - это эмулировать нажатие ISO_First_Group, мы достигнем искомого. А что у нас есть такого старого, доброго и привычного для эмуляции всякого нажатия и движения в иксах? Правильно, xdotool
.
И тут мы встречаем первый в нашей истории жирный такой нюанс: в xdotool
есть давно известный... то ли баг, то ли так и было задумано. В общем, xdotool перед эмуляцией клавиш программно сбрасывает все "модификаторы ввода", к коим относят и всё связанное с раскладкой. Так что эмулировать ISO_First_Group легче лёгкого, да только вот эффект это даст ровно нулевой. И исправлять это поведение никто, кажется, не планирует.
Ладно, не огорчаемся, ловим рабочий способ. Есть ведь другой эмулятор, работоспособный: виртуальная экранная клавиатура xvkbd
, которую мы поставим (посредством doas pkg_add xvkbd
) и добьемся искомого поведения вызовом: xvkbd -text '\[ISO_Next_Group]'
(будучи вызванной с опцией -text эта программа никаких экранных клавиатур не рисует, а просто эмулирует ввод переданного текста). Собственно, всё, решение найдено, на этом повествование можно было бы заканчивать. Вписывайте в ~/.config/i3/config
строчку вроде bindsym $mod+1 workspace number $ws1; exec "xvkbd -text '\[ISO_First_Group]'"
и вуаля. Ну для полного вуаля можно еще добавить оное к комбинации клавиш для запуска программ, у меня вот так:
bindcode $mod+40 exec "xvkbd -text '\[ISO_First_Group]'; rofi -show combi -modes combi -combi-modes 'drun,run,window' -show-icons -icon-theme 'Adwaita' -drun-match-fields 'name,exec' -font 'FantasqueSansM Nerd Font Regular 18'"
И в скрипт для блокировки экрана тоже можно вписать xvkbd -text '\[ISO_First_Group]'
. Всё.
Но как быть пуристам и минималистам, которые не хотят тащить в систему пусть крохотную, но все-таки избыточную программу? А давайте напишем сами мини-переключалку, которая будет делать ровно то, что нам надо и ничего больше, использовать исключительно X11 API и не иметь никаких зависимостей, окромя самих иксов? Ловите:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <X11/XKBlib.h>
void printHelpMessage() {
printf("syntax : \n --help - this message \n");
printf(" --set N - set layout N, 0 = first, 1 = second... \n\n");
return;
}
void setKbdLayout(int layoutId) {
/* Вызовом функции XOpenDisplay подключаемся к Х-серверу.
В качестве аргумента displayName передаем NULL - это оз-
начает, что будет выбран сервер по умолчанию, данные о
котором хранятся в переменной окружения DISPLAY */
Display* _display;
char* displayName = "";
_display = XOpenDisplay(displayName);
/* Используя XksUseCoreKeyboard мы указываем основную кла-
виатуру без необходимости определения ее идентификатора */
int _deviceId = XkbUseCoreKbd;
/* Устанавливаем заданную раскладку и закрываем соединение
с X-сервером */
XkbLockGroup(_display, _deviceId, layoutId);
XCloseDisplay(_display);
return;
}
int main(int argc, char **argv) {
/* Займемся обработкой параметров командной строки. */
if (argc <= 1) {
printHelpMessage();
}
else if (!strcmp(argv[1], "--set")) {
if(argc <= 2) {
printf("'set' operation requires a parameter.\n");
} else {
int res = atoi(argv[2]);
setKbdLayout(res);
}
}
else if (!strcmp(argv[1], "--help")) {
printHelpMessage();
}
else {
printf("\nUnknown parameter!\n");
printHelpMessage();
}
return 0;
}
Сохраним этот код в файлик с названием вроде swxkb.c и скомпилируем в OpenBSD так: clang -o swxkb swxkb.c -L/usr/X11R6/lib -lX11
. Впрочем, с тем же успехом оно скомпилируется и в любом Linux-дистрибутиве при помощи gcc (разве что, может потребоваться установка пакета libx11-dev или чего-то наподобие, в OpenBSD оно идет из коробки с иксами). По использованию - там в коде хелп виднеется. Куда вызов вписать - выше по тексту указано.
Ну и под занавес. Если вдруг потребуется скриптом раскладку не только устанавливать, но и узнавать текущуюю, то сделать это можно вот так:
#!/bin/sh
COMMAND=$(xset -q | grep LED | awk '{ print $10 }')
case "$COMMAND" in
"00000000"|"00000001") LAYOUT="en" ;;
"00000010"|"00000011") LAYOUT="ru" ;;
*) LAYOUT="??" ;;
esac
echo $LAYOUT
Наслаждайтесь.
P.S. Ностальгия... лет, наверное, не менее 12-ти назад я уже решал подобную задачу - настраивая под себя i3 (или даже вовсе wmii) под Arch Linux на Asus EeePC 901. Тогда я нашел готовую реализацию, что-то минималистичное в AUR. До чего же приятно иногда окунуться в прошлое!