Первый стабильный выпуск низкоуровневого корректора раскладок в linux «xswitcher»
Спустя год разработки удалось(у меня нашлась пара недель) довести до рабочего состояния задуманное в предыдущей публикации. А спустя ещё пару месяцев я пишу наконец эту статью.
В общем, ура! «Мы строили-строили и наконец построили». И оно даже работает/переключает. Причём, и в gnome3 тоже (не без помощи костыля).
TL.DR
Кому совсем не терпится, идёт на https://github.com/ds-voix/xswitcher и либо собирает по инструкции, либо качает статически слинкованный бинарь из подкаталога "bin/"
. Встроенная конфигурация циклически переключает первые 2 языка по левому CTRL
, включает 1-й язык по левому SHIFT
и 2-й язык по правому SHIFT
. Перепечатывание последнего введённого слова в изменённой раскладке выполняется по нажатию Pause
или F12
(осторожно с браузерами, это я просто не нашёл лучшего для кастрированной клавиатуры своего Lenovo X1).
Пользователи gnome должны страдать использовать хак из подкаталога "gnome3/"
. Тут уже́ встроенной конфигурацией не обойтись. Придётся положить "xswitcher.conf"
в папку "/etc/xswitcher/"
(предварительно её создав), а хэлпер "switch.gnome"
— в папку "/usr/local/bin/"
. И дать права на исполнение.
Напоминаю, что т.к. xswitcher по своей природе троян низкоуровневый кейлоггер в паре с виртуальной клавиатурой, для работы ему нужны права суперпользователя. Я сам запускаю из-под KDE (прописав в автостарт), для этого бинарю нужен suid-бит в разрешениях "chmod u+s вот_этому"
. Альтернативно можно пускать из systemd (upstart|rc.d|etc.), написав соответствующий конфиг (к релизу не прилагается). Вот здесь https://github.com/ds-voix/xswitcher/issues/1 товарищ экспериментировал.
С wayland — увы, пока помочь не могу. Но готов посодействовать, об этом см. ниже «Что делать с wayland».
Детали (что под капотом)
В целом, реализовано почти всё намеченное в «предыдущей части сериала».
За основу я взял гипотезу о том что процесс «алфавитного» ввода текста с клавиатуры может быть описан с использованием только регулярных грамматик. Так как мой моск не в силах «переваривать» цепочки «голых» скен-кодов, я постарался ему в этом помочь. Сделал трансляцию скен-кодов в ±человекочитаемые аббревиатуры клавиш. А чтобы не биться о восприятие всяких жутких последовательностей вида "([0-9A-Z=-]|GRAVE|APOSTROPHE|SEMICOLON|[LR]_BRACE|COMMA|DOT|(BACK)?SLASH|KP[0-9])"
, вынес их задание в именованные шаблоны. Один раз побился над составлением шаблона, и хватит.
Ешё одной задумкой «как сделать удобнее» было вынести «состояние клавиш состояния» (извините за тавтологию) «за скобки» регулярных выражений. Этот приём в итоге почти не понадобился, но в процессе оказалось удобным ввести понятие «виртуальной клавиши состояния». Такая сейчас одна: «WORD». Включается сразу после перепечатывания слова и сохраняет состояние пока не сработает детектор «нового слова». Дальше на сконструированные регулярные выражения вешается набор «детекторов совпадения» («match»). Предусмотрены пара встроенных функций («NewWord», «NewSentence») и неограниченный набор подключаемых «хуков» «Action.Name». Работу с юникодом в конфигурации я не закладывал и не проверял, так что советую использовать только ASCII в именах.
«Хуки» также могут вызывать как несколько встроенных функций, так и друг друга. Таким образом можно описывать схемы без ветвлений (и циклов соответственно). (Я не придумал зачем оно здесь надо. Кому надо, у меня есть https://github.com/ds-voix/VX-PBX/tree/master/VX с примерами из области телефонии. Правда, там perl. Хотя вот прямо сейчас пользуюсь этой штукой для проключения SAN в гипервизоры.)
Встроенная функция «Exec» позволяет запустить внешний бинарь и (опционально) отдать ему в stdin записанные скен-коды из буфера последнего слова или «предложения». Возможно следовало бы также добавить в качестве параметра текущий номер раскладки. Сделаю если кто-нибудь объяснит зачем это ему надо. «Exec» уже́ пригодился, чтобы закостылить гнома.
Основной встроенной функцией, из-за которой весь этот сыр-бор, является «RetypeWord» (забить слово и напечатать ещё раз). Смену раскладки можно полностью вынести наружу, как это и пришлось сделать для gnome. Есть ещё встроенная функция «Respawn». Она перезапускает xswitcher, в процессе переподключаясь ко всем нужным устройствам ввода. Используйте после подключения внешней клавиатуры/мышки (отключится оно само, ругнувшись в консоль).
Форк keybd_event
Для реализации «виртуальной клавиатуры» я не стал изобретать свой велосипед, а взял удобную (как мне показалось) библиотечку "github.com/micmonay/keybd_event"
. В случае с linux она цепляется к "/dev/uinput"
, создаёт «устройство» через syscall "SYS_IOCTL(_UI_DEV_CREATE)"
ну и дальше использует по назначению.
Всё крайне удобно, если бы не одно «но». В погоне за кроссплатформенностью авторы переборщили с уровнем абстракции. Вызовы «нажать» и «отпустить» кнопку скрыты. Вместо них — абстракция «нажать и отпустить» без возможности различать некоторые «клавиши состояния» (правую и левую «WIN»). Я, не долго думая, просто обернул вызовы «нажать» и «отпустить» «публичной» функцией. К сожалению, в таком виде коммит от меня принять не согласны т.к. он не будет работать под Windows. А у меня нет желания делать как-то лучше. Если кому-нибудь интересно посодействовать в улучшении функциональности «keybd_event» — помогайте. А пока, чтобы всё собралось, нужно подложить файл keybd_linux_export.go
в каталог с библиотекой. Или тянуть с меня форк, но это нехорошо т.к. я его в случае чего обновлять не буду.
Совместная работа с IBus/Fcitx
IBus навязывают всем подряд. При том что в большинстве случаев («европейские» азбуки слева направо) это просто ненужная блоатварь. Fcitx не навязывают, его просто используют те кому приходится писа́ть на языках ЮВА. (Я в т.ч.)
Здесь я знаю две проблемы.
IBus: Завязана на gnome и в связке с ней (по идее) должен работать костыль, см. выше «TL.DR.».
Fcitx: При загрузке перед xswitcher он умудряется как-то изменять характеристики клавиатуры. «Отваливаются» некоторые клавиши состояния. Мне он нужен лишь изредка и я его просто не загружаю при старте системы. Если кто-нибудь активно использует и fcitx и »3-й/5-й ряды»/«compose»-клавиши, я прошу потратить немного внимания на изучение данного нюанса. Возможно, проблема выеденного яйца не сто́ит и я просто «не умею готовить».
Что дальше
Текущее состояние xswitcher меня устраивает (оно просто работает и почти не «путается под ногами»). Но это никак не отменяет кучу открытых вопросов. Ниже я перечислю те их них, которые вижу сам. Если в обсуждении будут выдвинуты другие интересные вопросы — дополню текст статьи.
В пакет и на свалку^w^w в репозиторий
Большинство из пользователей linux этим вашим linux просто пользуются. И даже хабра не читают. Запакуйте и опубликуйте пакет для вашего любимого дистрибутива. На словах всё просто, и запаковать-то надо всего пару файлов. Но вопрос в том как это хорошо интегрировать с тем или иным DE.
Графический (GUI) конфигуратор
Вероятно, кому-то (как мне в 90х) интересно писать GUI. Придумать и реализовать GUI для настройки xswitcher — вполне достойная задача. Я, со своей стороны, торжественно обещаю «не ломать API».
Безопасность
«А чего оно рута требует? Это ж не безопасно!»
Я так не считаю т.к. при штатном использовании поверхность атаки сводится к подключению «нехорошего» устройства ввода, что само по себе является привилегированной операцией. Код на GO достаточно компактный и чего-то типа "if (uid = 0)"
туда не особо внедришь. Так что, эта сторона достаточно легко подвергается аудиту. Просто не берите абы-чьи бинарные сборки (мои в т.ч.).
В то же время, приведённые выше соображения ни один (вменяемый) аудитор не примет как доказательства безопасности. В этом и состоит ещё одна (надеюсь, кому-то интересная) задача. — «Что (и как) нужно изменить в схемотехнике/коде, чтобы подобное изделие перестало рассматриваться как топовая угроза безопасности?»
Развитие функциональности
Я сделал хорошо ровно две вещи (ну ладно, три).
Контроль событий клавиатуры (и чуть-чуть мышиных) через регулярные выражения с поддержкой шаблонов (и понятия «состояния»).
Модель для отсекания «последнего введённого слова» (и его перенабор).
Конфигурацию в виде блок-схемы на TOML.
То есть (глядя с другой стороны бесконечности) не сделано почти ничего. Зато есть каркас, на который можно крепить свои конструкции.
А можно перепечатывать целиком строку/предложение?
А может, надо цеплять ввод «как у всех» к «оконным» библиотекам?
А будет автокоррекция (как у punto switcher)?
Хочется принудительно включать определённую раскладку при получении фокуса определённым окном.
Всё можно, если кому-то покажется интересным потратить часть своего времени на реализацию хотя бы одной функции. Я уже́ как-то писа́л что не вижу способа сделать хороший linux-десктоп классическими «коммерческими» приёмами. Потому что «где здесь деньги»? Но можно попробовать вот такой обходной манёвр.
Создаётся «аттрактор» в той области которую желаем улучшить (ну вот как Линус когда-то нащупал способ). Дальше, если каркас вышел удачным, его потихоньку дополняют те кому понравилось. Этот «фокус» работает и с большими коммерческими продуктами («asterisk», «firefox» пока его не принялись толерастить), и с мелкими (в принципе) проектами («far manager»). Попробуем с xswitcher?
Что делать с wayland (он наступает)
Как «хорошо информированный оптимист» предполагаю момент когда поддержку Х порежут «потому что не модно».
С самим wayland — ничего делать не надо (как я понимаю, это «фундамент» для всего остального).
Чтобы работали подобные этой переключалки (ну и кейлоггеры, куда уж без них), нужно обеспечить вывод потоков скен-кодов от подключенных клавиатур в какие-нибудь символьные устройства. (И от мышек/трекболов etc., заодно.)
Это — уровень ядра и должно быть в наличии (если специально не отключать).
Чтобы иметь возможность перепечатать, нужен интерфейс «виртуальная клавиатура» (»/dev/uinput» или »/dev/input/uinput»).
Это опять же уровень ядра и должно быть в наличии (если специально не отключать).
Чтобы переключать язык, нужен API для переключения. Вот его — таки надо. И тут я не помощник.
И ещё хорошо бы получать класс окна при смене фокуса, чтобы регулировать поведение переключалки. Если всё в наличии, просто ткните меня носом в соответствующую документацию/примеры использования. Я обязательно постараюсь помочь в адаптации кода. (В случае с гномом — допускаю, что переключаться можно через описанный выше костыль. А остальным что делать?)
Известные ошибки
Если хорошенько подёргать туда/сюда внешние клавиатуры/мыши, можно «уронить» xswitcher. Если кому-то удастся отдефектовать проблему, я исправлю.
Переключение раскладки по нажатию функциональных клавиш (и иной какой экзотики) чревато феерическими багами. Потому что хswitcher не занимается MITM-порчей событий ввода.
На «недоклавиатурах» лучше придумайте для перепечатывания что-нибудь более нейтральное. «Двойной шифт» к примеру.
Если в вашей программе реализовано автодополнение (не по табулятору), xswitcher может напакостить при перепечатывании. Лучше для таких окон отключать «RetypeWord».
Также можно нарваться в терминале, начав ввод с «Esc» и затем вызвав «RetypeWord».
Это надо переписать на rust
Да хоть на idris. Весь код открыт, заимствуйте как хотите. Замечу, что любой написанный «по мотивам» код не будет подпадать под «антипатент» AGPL, можно лицензировать хоть EULA хоть CPL. Главное, написать работающее.
Благодарности
Отдельное спасибо хочу сказать уважаемым elfmz и unxed за появление моего любимого FAR в linux.
И одновременно
…выразить надежду на скорейшую реализацию https://github.com/elfmz/far2l/issues/892 (Очень хочу выделение мышкой как было под виндой, и ведь уже́ почти всё работает!) Несмотря на ряд шероховатостей far2l, весь код для релиза xswitcher я написал именно в его редакторе. Пользование far2l уже сейчас не кажется актом мазохизма, но две вещи из «старого» FAR хочется увидеть в новом far2l:
Функциональность плагина «trucer»: обрезка пробелов/табуляций в конце строки и «пустых строк» в конце документа
Качественный «автодополнитель» слов (в старом far таким был плагин «Editor Word Completion»). Он имел достаточное окно поиска и не ломал текст «редактированием поверху».
Как всегда, буду крайне признателен за любую «обратную связь».
Удачи!