Снова об автопереключении раскладки клавиатуры.

Опубликовано 11.03.2024 в OpenBSD

Не так давно я писал, как имея только софт из репозитория OpenBSD... да что там, как имея только компилятор и базовую систему можно софтверно управлять раскладками клавиатуры. Тогда я рассмотрел реализацию четырех полезняшек:

  • переключение на латиницу при переходе на определенный рабочий стол i3 (где у меня терминалы открытые всегда висят, и в подавляющем большинстве случаев я туда переключаюсь вовсе не для ввода чего-нибудь по-русски, а совсем даже наоборот);
  • переключение на латиницу же при блокировке экрана (по бездействию или при уходе в suspend/hibernate), чтобы, значит, не ошибаться раскладкой при вводе пароля для разблокировки;
  • переключению на не менее латинскую раскладку при старте запускалки программ а-ля dmenu (в моем случае - rofi );
  • ну и вот хоткеи самого оконного менеджера i3 должны, безусловно, срабатывать независимо от активной раскладки, но это совсем уж просто (используй в конфиге bindcode вместо bindsym там, где нужна такая универсальность, а коды клавиш смотри старым добрым xev);

Этим, однако же, удобство не исчерпывается, и захотелось мне попробовать еще пару-тройку улучшайзингов, а именно:

Автопереключение раскладки для каждого окна в i3

Любое окружение рабочего стола (DE) предоставляет такую популярную настройку, как "запоминать раскладку клавиатуры для каждого окна". Я как-то не был фанатом этой опции никогда, вечно её отключал повсюду, а тут вот поймал себя на том, что при написании текста (да хоть вот этого) часто переключаюсь между окном текстового редактора (где вводится русский текст) и тем же терминалом, но не им одним - и файловым менеджером (где русский нужен лишь иногда), браузером (где вообще по-разному случается), и прочее в том же духе. И когда такие переключения часты - перещелкивание раскладки малость утомляет. Так не попробовать ли мне запоминать раскладки пооконно?

Тут, правда, есть жирный нюанс: я не пользуюсь DE, я конструирую себе рабочее окружение сам, а в качестве оконного менеджера использую i3 - и начал я уже было даже задумываться, как бы мне скриптами обвязать все оконные переходы в i3 и... понял, что такая идея наверняка уже кому-то приходила в голову. И ведь верно! Есть замечательный проект xkb-switch-i3, являющийся форком не менее замечательного минималистичного переключателя раскладок xkb-switch. Правда, оных утилит нет в портах OpenBSD, но это совершенно не мешает собрать их самостоятельно, как описано на страничке самого проекта (я, увы, не настолько еще крут, чтобы самостоятельно ваять порты). Соответственно, git clone нам в руки, качаем и компиляем - там что-то такое совсем незначительное при сборке было, include заголовок какой-то не находился, но победить это так легко, что я и патча-то не слепил даже, так, ручками путь дописал... Соответственно, мы получаем на выходе вариацию утилиты xkb-switch, умеющую, помимо показа текущей раскладки и установки нужной, еще и пооконное запоминание и переключение раскладок делать, для чего нужно лишь в конфиг i3 вписать что-нибудь вроде exec --no-startup-id xkb-switch --i3. Проверено лично, работает отлично!

Автопереключение раскладки в редакторе Neovim

И есть у упомянутого xkb-switch еще одно замечательное свойство, очень нам вскорости пригодившееся - он озолотил нашу систему еще и библиотекой /usr/local/lib/libxkbswitch.so с ABI, которую можно вызывать и из других интересных мест. Скажем, редактор neovim - со всей своей модальной сущностью управляется ассортиментом хоткеев и клавиатурных комбинаций, зачастую не очень логичных и мнемоничных, но привычных до боли за годы вимерства... И раузмеется, эти сочетания и комбинации из коробки в не-латинской раскладке не работают. И есть чертова уйма способов как заставить их работать с теми или иными издержками и ограничениями. И есть готовые плагины. Но я пойду своим, самобытным путем!

Итак, что мне нужно: не-латинская раскладка мне нужна, разумеется, практически исключительно в режиме ввода. А при переходе из режима ввода в любую другую модальность - хотелось бы мне, чтоб раскладка автоматически переключалась на латиницу. НО! В идеале чтоб при возвращении в режим ввода она переключалась вновь - на ту, что была до выхода, сохраненную (будь она хоть не-латиницей, хоть даже и латницей - но чтоб вернуться к редактированию прям как будто и не прерывался, без лишних телодвижений).

Но начнем мы даже не с этого, а с маленькой такой несущественной опции, изрядно меня, впрочем, озадачившей. Выход из "режима ввода" в "нормальный режим" по умолчанию в vim, neovim, а также всяческих вимоидах (вот только те, что из любопытства щупал я: helix, kakoune, vis - все неюзабельная чушь пока что) - осуществляется по клавише ESC, до которой довольно далеко тянуться. Можно еще Ctrl-[, конечно, но это не намного более удобная раскоряка. Поэтому частым выбором вимеров является забиндить ESC на клаившу CapsLock - удобно доступную левому мизинцу, крупную, целкую, бесполезную в смысле основного своего назначения. Но именно по этим, блин, причинам у меня повсюду и всегда на CapsLock уже повешено то самое ручное переключение раскладки (варианта) ввода! Занято!

Есть еще одна популярная опция: выход из режима ввода вешают на сочетание клавиш "jj", "kj", "jk", ну а чтоб и по-русски работало - на русские же "оо", "ло", "ол" - чертовски удобно, они всегда под рукой, только вот... только вот "координаты" или, там "сообщество" будет вводить трудновато, если на "оо" повешено это самое, не говоря уж о том, сколько в русском слов со слогами "ол" и "ло". Не, может в пиндосском слов с сочетанием "jj", "kj", "jk" и нету вовсе, или они там большая редкость - но с учетом русского меня это не устраивает. Тем более, что есть вариант лучше! В русском языке, знаете ли, весьма затруднительно найти слова, в которых было бы сочетание "шш" (в латинской раскладке оно же "ii" - слова с таким буквосочетанием в пиндосском есть, но не так чтоб много и сильно употребимые). Выбор, как по мне, близок к идеальному - нажатием "i" мы входим в режим вставки (редактирования), нажатием же "ii" (в русском "шш") - выходим из него, дотянуться легко, забыть невозможно!

Но есть подводный камень: ввод слов, содержащих "ш" и "i" станет маленько некомфортным - потому что редактор после ввода первой буквы будет ожидать сколько-то миллисекунд (чтоб определить, как ему обработать ввод - как начало управляющей последовательности или как просто ввод буквы) и эти вот мини-задержки раздражают. Да, длительность задержки можно конфигурировать - но всё равно бесит. К счастью, решение есть: давно уже существуют плагины и для vim, и для neovim, позволяющие оную задержку убирать полностью. В моем случае впишем в ~/.config/nvim/init.lua что-то понятное вашему вимовскому пакетному менеджеру (у меня это Lazy) для загрузки нужного плагина:

require("lazy").setup({
    { "max397574/better-escape.nvim" }
})

и ниже туда же, собственно, запуск плагина с нужными настройками (внимание на нижнее подчеркивание!):

require('better_escape').setup({
        mapping = {"шш", "ii"},
        timeout = 200,
})

Указанный таймаут тут совершенно опционален - если не указать, по используется вимосвская настройка timeoutlen, равная по дефолту 300-м, что-ли, миллисекундам.

Теперь к самому прекрасному: автопереключение на латиницу при выходе из режима редактирования и обратно при возвращении к редактированию. Для этого я методом копипейст-программирования написал собственный модуль на lua (я крут, от меня все мрут), следующего вида:

local M = {}
-- nvim_create_autocmd shortcut
local autocmd = vim.api.nvim_create_autocmd

local xkb_switch_lib = '/usr/local/lib/libxkbswitch.so'

local function get_current_layout()
    return vim.fn.libcall(xkb_switch_lib, 'Xkb_Switch_getXkbLayout', '')
end

local saved_layout = get_current_layout()

function M.setup()
    -- When leaving Insert Mode:
    -- 1. Save the current layout
    -- 2. Switch to the US layout
    autocmd(
        'InsertLeave',
        {
            pattern = "*",
            callback = function()
                vim.schedule(function()
                    saved_layout = get_current_layout()
                    vim.fn.libcall(xkb_switch_lib, 'Xkb_Switch_setXkbLayout', 'us')
                end)
            end
        }
    )

    -- When Neovim loses focus
    -- When entering Insert Mode:
    -- 1. Switch to the previously saved layout
    autocmd(
        {'InsertEnter'},
        {
            pattern = "*",
            callback = function()
                vim.schedule(function()
                    vim.fn.libcall(xkb_switch_lib, 'Xkb_Switch_setXkbLayout', saved_layout)
                end)
            end
        }
    )


end

return M

Традиционное "вуаля!", всё вроде бы работает сообразно выдвинутым требованиям, ура!

... и даже в Firefox!

P.S. Вишенка на тортике: с некоторых пор я фанат расширения для Firefox под названием Vimium - рву на себе волосы. что осознал всю его благодатность так преступно поздно, мог бы годы уже использовать с наслаждением! Так вот, там нужная нам опция "хоткеи должны работать независимо от того, какая раскладка клавиатуры сейчас активна" реализована одним чекбоксом в настройках расширения. И работает!