Переключение раскладки в X11 командой из скрипта
Казалось бы, такая простая задача... но придется программировать на С!
Опубликовано: 2024-02-23
Категория: OpenBSD
Теги: хауту x11 setxkbmap xvkbd layout
Вот настроили мы по-старинке переключение раскладок в 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#include #include #include #include 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. До чего же приятно иногда окунуться в прошлое!