Индикатор раскладки клавиатуры для tint2 [Часть 2]
17.04.2022 linux, tint2, xkb, debian, клавиатура, bash, c langВ первой части я рассказал, как сделать индикатор раскладки клавиатуры используя данные индикатора CapsLock и командный язык Bash. В этой записи я продолжу тему индикации. Теперь будем реализовывать её используя и Bash, и C (Си).
Индикатор раскладки клавиатуры также будет работать только под X11. Я просто не использую другую оконную систему.
Недостаток реализации предыдущего индикатора раскладки клавиатуры заключался в том, что мы завязывались на данных индикатора CapsLock. В случае, если мы не используем CapsLock для переключения раскладки, или же у нас больше трех языков, то данное решение бесполезно. В добавок, у нас не было возможности переключать раскладку по левому клику мыши по иконки в tint2. Для её реализации требовалось бы прибегнуть к хаку в виде пакета xdotool, который может триггерить событие устройств ввода, тем самым эмулируя физическое переключение раскладки клавиатуры. Всё это привело к тому, что я решил вспомнить былое и реализовать утилиту на языке C, которая бы возвращался код раскладки и могла переключать её.
Перед тем, как приступить к реализации своей версии, я посмотрел разные варианты уже имеющихся, где только смог найти. Но мне хотелось сделать все по-своему. Заодно написать что-нибудь на Си.
В итоге я быстро накидал первую версию. Проверил в течении пары дней, что все работает. И сделал рефакторинг, уменьшив количество кода и упростив реализацию. До этого было много обработок различных ошибок, которые я решил просто выбрасывать в stderr
и возвращать bool
. Ну и так по мелочи.
Утилиту я назвал xkbklu
, что в переводе означает XKB Keyboard layout Utitlity.
Исходник:
// main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XKBrules.h>
Display *dpy;
/**
* @param[out] l_code A language code of current keyboard layout.
* @param[out] l_pos A layout position in XKB group list.
* @param[out] t_layout Total number of layouts in XKB group list.
* @return "true" if executed sucessully, "false" if an
* error occured.
*/
bool get_status(char *l_code[], int *l_pos, int *t_layout) {
XkbStateRec state;
XkbGetState(dpy, XkbUseCoreKbd, &state);
XkbRF_VarDefsRec rules;
if (!XkbRF_GetNamesProp(dpy, NULL, &rules)) {
fprintf(stderr, "ERR: Cannot get XKB-rules.\n");
return false;
}
char *layout = strtok(rules.layout, ",");
char *cur_layout = NULL;
int layout_size = 0;
while (layout != NULL) {
if (layout_size == state.group) {
cur_layout = layout;
}
++layout_size;
layout = strtok(NULL, ",");
}
if (cur_layout == NULL) {
fprintf(stderr, "ERR: Undefined current layout.\n");
return false;
}
if (l_code != NULL) {
*l_code = cur_layout;
}
if (l_pos != NULL) {
*l_pos = (int)state.group;
}
if (t_layout != NULL) {
*t_layout = layout_size;
}
return true;
}
/**
* @return "true" if executed sucessully, "false" if an
* error occured.
*/
bool switch_layout(void) {
int l_pos = 0;
int t_layout = 0;
if (!get_status(NULL, &l_pos, &t_layout)) {
return false;
}
int next_layout = l_pos + 1 >= t_layout ? 0 : l_pos + 1;
if (!XkbLockGroup(dpy, XkbUseCoreKbd, next_layout)) {
fprintf(stderr, "ERR: Cannot switch keyboard layout.\n");
return false;
}
return true;
}
void show_help(void) {
printf("xkbklu - The helper for xkb* utilities.\n");
printf("\n");
printf("Commands:\n");
printf("switch - Switch layout of keyboard.\n");
printf("status - Return layout code of keyboard.\n");
}
int main(int argc, char *argv[]) {
dpy = XOpenDisplay(NULL);
if (dpy == NULL) {
fprintf(stderr, "ERR: Cannot open display.\n");
return EXIT_FAILURE;
}
int exit_code = EXIT_FAILURE;
char *command = argc > 1 ? argv[1] : "";
if (strcmp(command, "status") == 0) {
char *lang;
if (get_status(&lang, NULL, NULL)) {
printf("%s\n", lang);
exit_code = EXIT_SUCCESS;
}
} else if (strcmp(command, "switch") == 0) {
if (switch_layout()) {
exit_code = EXIT_SUCCESS;
}
} else if (
strcmp(command, "") == 0
|| strcmp(command, "--help") == 0
|| strcmp(command, "-h") == 0
) {
show_help();
exit_code = EXIT_SUCCESS;
} else {
fprintf(stderr, "ERR: Command is not found.\n");
}
XCloseDisplay(dpy);
return exit_code;
}
Зависимости:
- X11;
- xkbfile.
Сборка:
$ gcc -Wall -o ./xkbklu -I /usr/include ./main.c -lX11 -lxkbfile
Использование:
xkbklu - The helper for xkb* utilities.
Commands:
switch - Switch layout of keyboard.
status - Return layout code of keyboard.
Теперь можно доработать наш индикатор для tint2. Для этого перенесем полученный после сборки бинарник xkbklu в ~/.local/bin/
, и внесем правки в Bash скрипт keyboard-layout:
#!/usr/bin/env bash
ICON_PATH="/home/$(whoami)/.local/share/keyboard-layout"
get_status() {
local code=$(xkbklu status)
local icon="${ICON_PATH}/flag-${code}.svg"
if [ -f $icon ]; then
echo $icon
else
echo "${ICON_PATH}/no-flag.svg"
fi
}
switch_layout() {
xkbklu switch
}
case "$1" in
'switch')
switch_layout
;;
'status')
get_status
;;
*)
# do nothing
;;
esac
exit 0
И конечно же, результат работы:
Репозиторий утилиты на Github: https://github.com/bupy7/xkbklu