Наши партнеры








Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

Немного о "внутренностях" XKB.

Основные термины - коды и символы.

Основное назначение модуля XKB — преобразовывать скан-коды нажимаемых клавиш в коды символов.

Обычно в документации XKB скан-коды называются keycodes, а коды символов - просто "символы" (symbols).

Естественно, "символы" - это не обязательно коды "печатных" символов (букв, цифр, знаков препинания и т. п.). Это могут быть также "управляющие" коды (Esc, Enter, Backspace и т. п.) или коды, которые никак не отображаются, но влияют на внутренне состояние самого XKB (переключение РУС/ЛАТ, Control, Shift, Alt и т.п.) и, следовательно, на то, какой символ будет выбран при нажатии обычных "буквенных" клавиш.

Две части XKB и "проблема совместимости".

Прежде всего следует сказать несколько слов о том, как происходит преобразование скан-кодов в символы при работе X Window System без модуля XKB.

Сам X-сервер переводом скан-кода в код символа не занимается. При нажатии или отпускании кнопки клавиатурный модуль X-сервера посылает сообщение о событии (event) прикладной программе.

В этом сообщении указывается только скан-код нажатой кнопки и "состояние клавиатуры" - набор битовых флагов, который отражает состояние клавиш-модификаторов (Shift, Control, Alt, CapsLock и т. п.).

Клиентская программа должна сама решить, какой символ соответствует скан-коду при таком сочетании битов-модификаторов.

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

Разумеется, при создании программ никто не пишет каждый раз процедуру для интерпретации скан-кодов. Для этих целей существуют специальные подпрограммы в библиотеке Xlib.
Конечно, Xlib используется не только для этого. Это библиотека низкоуровневых процедур для работы с X-сервером. Даже если программа пользуется высокоуровневыми библиотеками-"тулкитами" (Xaw, Motif, Qt, gtk и т. п.), библиотека Xlib служит "базовой" для этих тулкитов.

Итак, процедуры из Xlib, зная скан-код и состояние клавиатуры, в соответствии с таблицей символов, полученной от сервера при старте, выбирают подходящий символ.

Модуль XKB точно так же сообщает программе только скан-код и свое "состояние". В отличие от "старого" модуля (обычно говорят - "core protocol", будем его называть для краткости - "core-модуль"), XKB имеет более сложную таблицу символов, другой набор модификаторов и некоторые дополнительные параметры "состояния клавиатуры" (обо всех этих отличиях мы поговорим по ходу рассмотрения "внутренностей" XKB).

Для полноценной работы с XKB библиотека Xlib должна содержать процедуры преобразования скан-кодов, "знакомые с Xkb".
Естественно, "иксы", у которых X-сервер "укомплектован" модулем XKB, имеют и соответствующую библиотеку Xlib.

Таким образом, XKB фактически делится на две части - модуль, встроенный в X-сервер, и набор подпрограмм, входящих в библиотеку Xlib.

Однако же, никто не запрещает использовать программы, которые были собраны со старой библиотекой Xlib, "не подозревающей" о существовании XKB: при этом и возникает "проблема совместимости". Модуль XKB должен уметь общаться как со "своей" Xlib, так и со старой, работающей в соответствии с core protocol.
Естественно, "общение" не ограничивается только передачей сообщений о нажатии/отпускании клавиш. Процедуры Xlib могут обращаться к X-серверу с различными запросами (например, взять таблицу символов) и командами (например, поменять в этой таблице расположение символов).
На все эти запросы и команды модуль XKB должен реагировать так, чтобы даже "старая" Xlib могла работать правильно (насколько это возможно).

Таблица символов

Что же собой представляет таблица символов, связывающая символы со скан-кодами и состоянием модификаторов? Рассмотрим сначала "традиционную" таблицу символов, которая используется в core protocol.

Как и во многих других клавиатурных модулях, ее можно представить как обычную двумерную таблицу, где каждая строка соответствует скан-коду, а столбцы - модификаторам или комбинации модификаторов.

Прежде всего надо заметить, что в "сообщении о нажатии/отпускании клавиши" под биты-модификаторы отводится один байт (октет) и соответственно, существует восемь битов-модификаторов. Первые три называются - Shift, Lock, Control, остальные "безымянные" - Mod1, Mod2, Mod3, Mod4, Mod5.
Замечу, что хотя названия первых трех модификаторов явно намекают на то, каким клавишам они должны соответствовать, на самом деле они могут быть "привязаны" (то есть, менять свое значение при нажатии/отпускании клавиши) к любым другим кнопкам.

В данном случае нам важно, что восемь модификаторов могут составить 256 различных комбинаций. Поэтому теоретически таблица символов может содержать до 256 столбцов. В то же время, в core protocol'е строго определены только первые четыре колонки, в смысле - каким комбинациям модификаторов они соответствуют. Легко догадаться, что в этих комбинациях используются только два модификатора - Shift и Mode_switch.
(Вы можете заметить, что среди модификаторов нет бита с именем Mode_switch. Совершенно верно. Его роль выполняет один из "безымянных", модификаторов - Mod1-Mod5. А Mode_switch - это название одного из служебных символов. Когда приложение запрашивает у сервера таблицу символов, оно также выясняет, какой из безымянных модификаторов связан с этим символом, и в дальнейшем по состоянию этого модификатора догадывается о том, нажал ли пользователь клавишу, соответствующую символу Mode_switch.)

Итак. Первые четыре колонки в таблице соответствуют "состояниям" -

"никаких модификаторов" Shift Mode_switch Mode_switch+Shift


В терминах core protocol можно сказать, что состояние Mode_switch выбирает одну из двух групп (не надо путать их с XKB группами, о которых мы поговорим позднее), а состояние Shift - одну из двух колонок внутри группы.
Как вы скорее всего знаете, разные группы обычно используются для разных языков (алфавитов), а Shift выбирает строчные или прописные буквы в пределах одного алфавита.

  Group 1 Group 2
  Shift Mode_switch Mode_switch+Shift
...
keycode 38 a A Cyrillic_ef Cyrillic_EF
keycode 39 s S Cyrillic_yeru Cyrillic_YERU
keycode 40 d D Cyrillic_ve Cyrillic_VE
...

Заметьте также, что ни Lock, ни Control в выборе символа из таблицы не участвуют. Если эти модификаторы активны, то отдельные подпрограммки Xlib после выбора символа из таблицы делают соответствующие преобразования этих символов.

Вернемся к XKB. Как видите, недостаток "традиционной" таблицы - ее "негибкость". Хотя колонок может быть до 256, "стандартно" обрабатываются только первые четыре, причем их зависимость от состояния модификаторов жестко "зашита" в алгоритмах Xlib.

Поэтому одно из основных усовершенствований, которые были внесены при разработке XKB - большая гибкость в построении таблицы.

  • Во-первых, в XKB колонки не связаны жестко с конкретными модификаторами. В одном из файлов конфигурации XKB описывается зависимость номера колонки от произвольного набора модификаторов. Естественно, эти зависимости можно изменять и добавлять простым редактированием соответствующего файла. (Подробнее о них смотри ниже: Вычисление "уровня" (shift level). Типы клавиш.)
  • Во-вторых, в одной и той же "раскладке клавиатуры" разные клавиши могут соответствовать разному количеству символов в зависимости от модификаторов. Например,
    • клавиша Enter вообще не зависит от модификаторов, то есть в соответствующей строке имеет смысл только одна колонка;
    • клавиша с символами '1' и '!' содержит две колонки, выбор конкретной зависит только от Shift,
    • а клавиша с символами '-','_'и '=' может иметь три колонки, выбор которых зависит от двух модификаторов - Shift и Mod1
  • При таком подходе понятие "группа" сильно меняет свой смысл. Мы уже не можем определять группу как "пачку" колонок, поскольку для разных клавиш размер этой "пачки" будет разным.
    Вообще-то, при той гибкости в связывании колонок с модификаторами, которую дает XKB, можно было бы вообще забыть о группах или просто говорить, что группа —это группа колонок, объединенных модификатором (тем же Mode_switch). Но...

Делить таблицу на группы удобнее. Во-первых, так легче провести границу между разными алфавитами (если, конечно, таковые используются в раскладке). Заметьте, что в некоторых случаях хотелось бы иметь не два разных алфавита, а больше. Причем раскладки для разных алфавитов могли бы составлять разные люди, независимо друг от друга
Во-вторых, чем больше модификаторов участвуют в выборе колонок, тем сложнее и запутаннее становятся соответствующие описания зависимостей.

Поэтому, авторы XKB использовали радикальный подход - в одной и той же раскладке может быть несколько (до четырех) отдельных таблиц. Вот эти таблицы и называются группами XKB (или просто groups).

Вообще-то, правильнее сказать не "несколько двумерных таблиц в одной раскладке", а - у каждого скан-кода (keycode) может быть до четырех "однострочных" таблиц - групп.
Потому, что ...

  • Назначение некоторых клавиш (той же Enter) не меняется в разных алфавитах, поэтому для них не требуется деление таблицы на несколько отдельных групп.
  • Даже в пределах одной группы (алфавита) разные клавиши могут иметь разное количество колонок. То есть, "ширина" таблицы меняется не только "от группы к группе" но и "от скан-кода к скан-коду". Поэтому удобнее описывать каждую отдельную группу для каждого отдельного скан-кода.
  • И, наконец, с каждым скан-кодом связаны еще некоторые параметры (см. ниже ), которые не зависят от номера группы. Поэтому, если бы мы описывали один и тот же скан-код в разных таблицах-группах, эта информация могла бы оказаться противоречивой.

Итак.

  • Для каждого скан-кода (keycode) в XKB "раскладке" хранится несколько однострочных таблиц символов, которые называются "группами".
  • Каждая такая таблица может делиться на несколько колонок - "уровней" (shift level)
  • Какая из таблиц-строчек актуальна в данный момент, определяется текущим номером группы (или просто - group). Текущий номер группы хранится в X-сервере и сообщается приложению в событии о нажатии/отпускании клавиши вместе со скан-кодом и набором модификаторов.
  • Выбор нужной колонки (shift level) определяется состоянием модификаторов.
  • Разные keycode могут иметь разное количество групп.
  • В разных группах даже одного и того же keycode может быть разное количество shift level.
keycode номер группы количество level'ов  
36 1 одна колонка Enter  
38 1 две колонки a A  
2 две колонки Cyrillic_ef Cyrillic_EF  
3 две колонки Greek_alpha Greek_ALPHA  
21 1 две колонки + =  
2 четыре колонки + = \ ;
...

Наконец, надо сказать, что групп может быть от одной до четырех, а уровней - до 64.

Таблица действий.

Кроме "таблицы символов" к скан-коду может быть "привязана" аналогичная "таблица действий" (actions). Эта таблица также делится на подтаблицы (группы) и колонки (уровни).

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

В ее ячейках располагаются вызовы внутренних процедур XKB, которые меняют его состояние - текущую группу, состояние модификаторов и внутренних флагов XKB.

Точнее - действия, выполняемые actions, не ограничиваются измениями состояния XKB. Они также используются для

  • эмуляции событий мыши (перемещения указателя и нажатия mouse buttons)
  • генерации специальных events для приложений
  • переключения экранов
  • выключения X-сервера
  • и т.п.

Немного подробнее о всех возможных actions написано в разделе Описание действий.

Если на клавише для группы и позиции в группе определено действие, то для этой же группы и позиции в группе для той же клавиши должен быть определен символ (обычно служебный).

Заметим, что в core-модуле понятия "действие" вообще нет.

Состояние XKB: номер группы.

Текуший номер группы хранится в "таблице состояния" XKB.
Точнее, внутри XKB имеются три ячейки

  • locked group - некий аналог нажатия клавиши CapsLock, но для групп
  • latched group - аналог действия клавиши Shift, но, опять же, для групп
  • base group - базовое смещение номера группы
Обычно содержимое этих переменных меняется с помощью действий (actions), которые связываются с подходящими клавишами (их скан-кодами).

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

Метод выравнивания номера группы.

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

  • Wrap ("заворачивание") - получившееся число делится на количество групп и берется остаток от деления. Этот метод используется по умолчанию.
  • Clamp ("удержание в границах") - если получилось число большее чем номер последней группы, то берется этот самый "номер последней группы", если же получилось число меньше, чем номер самой первой группы, то берется первая группа.
  • Redirect ("перенаправление" или, точнее - "замена") - если используется этот метод, то в "состоянии XKB" должен быть определен еще один дополнительный параметр - номер группы "куда перенаправить" (или - "чем заменить"). Если получившееся число выходит за пределы допустимых значений для групп, оно просто заменяется этой самой "чем заменить". Кстати, если и этот номер не уложится в допустимые границы, то XKB подставит первую группу.

Модификаторы.

Кроме переменных, содержащих номера групп, "состояние XKB" определяется "флажками" - модификаторами. Эти битовые флаги меняются при нажатии клавиш Shift, CapsLock, Alt, Control и т. п. и, естественно, влияют на выбор подходящего символа из таблиц.

Как я уже упоминал, "core protocol" (протокол "общения" между "традиционным" клавиатурным модулем и Xlib) тоже имеет "набор модификаторов" (который и сообщается прикладной программе при нажатии кнопок). Эти модификаторы (8 бит) называются Shift, Lock, Control, Mod1-Mod5. В зависимости от их состояния Xlib выбирает подходящий символ и может выполнять дополнительные действия — делать из обычных символов управляющие, менять прописные/строчные буквы и т. п.
Кроме того, программа может сама (помимо Xlib) по-своему интерпретировать эти модификаторы и менять свое поведение.

Поэтому, хотя XKB имеет больше модификаторов и его поведение (действия, выполняемые при определенной комбинации модификаторов) можно гибко перестраивать (как при старте, так и в процессе работы), модуль XKB вынужден эмулировать "классический" набор модификаторов, специально для старых клиентских программ (и старой Xlib).

Итак. Модификаторы core protocol'а в XKB называются "реальными" (real) модификаторами и их названия такие же, как в core protocol.

Кроме того, XKB имеет еще 16 своих внутренних модификаторов, которые называются "виртуальными" (virtual) модификаторами.

К сожалению, "проблема совместимости" заключается еще и в том, что в формате "сообщения о клавише" для модификаторов отведено только восемь бит. Поэтому XKB при всем желании не может сообщить приложению все свои шестнадцать виртуальных модификаторов и вынужден в любом случае отображать их в "реальные" (даже для приложений "знакомых с XKB"). Единственным утешением является то, что можно сколько угодно виртуальных модификаторов отобразить в один реальный и это отображение легко перенастраивается.

Итак. Эти модификаторы выполняют несколько функций

  • по их состоянию "вычисляется" shift level для выбора нужного символа и/или "действия";
  • по их состоянию "вычисляется" состояние индикаторов (см. ниже);
  • и, кроме того, они могут влиять на выполнение некоторых других действий XKB (вычисление номера группы и т.п.).

Состояние XKB: модификаторы.

Так же как и номер группы, набор битов-модификаторов внутри XKB существует в трех экземплярах (в трех переменных)

  • locked modifiers
  • latched modifiers
  • base modifiers
Содержимое этих переменных (так же как и "групповых") может меняться с помощью действий (actions).

Как и для номеров групп, для модификаторов существует "фактический" набор модификаторов, который определяется как логическая сумма (побитная операция ИЛИ) трех указанных переменных. Соответственно, никаких дополнительных "выравниваний", как для значения номера группы, не требуется.

Вычисление "уровня" (shift level). Типы клавиш.

В отличие от номера группы, уровень не запоминается в состоянии XKB, а вычисляется из состояния модификаторов. При этом для различных клавиш зависимость уровня от модификаторов может различаться.

Для того чтобы обеспечить такую гибкость, в XKB существут понятие "тип клавиши".

В каждом описании типа клавиши задается некая функция - какие модификаторы принимать к рассмотрению и какой уровень должен получится при определенном сочетании модификаторов.
Соответственно, у каждой клавиши (скан-кода) в каждой под-таблице (группе) указывается свой "идентификатор типа".

Естественно, при нажатии клавиши XKB (точнее, процедуры Xlib) определяет, к какому типу она относится и по описанию типа и текущему состоянию модификаторов вычисляет, из какого уровня следует выбрать символ для этого скан-кода.

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

Какая еще информация хранится для каждого скан-кода.

Кроме таблицы символов (и, возможно, "действий") с каждым скан-кодом связаны еще несколько переменных

  • метод "выравнивания" номера группы, специфичный для данной клавиши;
  • "поведение" клавиши;
  • набор "исключений";
  • реальный модификатор (modmap) и виртуальный модификатор (vmodmap), связанные с этой клавишей.

Метод "выравнивания" номера группы.

Поскольку разные клавиши могут иметь разное количество групп, может возникнуть ситуация, когда вычисленный и "выровненный" эффективный номер группы все-таки не попадает в дапазон допустимых групп для данной клавиши.

В этом случае эффективный номер группы может дополнительно "выравниваться", специально для данной клавиши.

Возможные методы точно такие же, как и "глобальные". По умолчанию используется метод Wrap.

"Поведение" клавиши.

Эта переменная состоит из двух частей - набора флагов и дополнительного аргумента. Обычно аргумент не нужен. Но некоторые флаги подразумевают дполнительный числовой аргумент, а смысл этого аргумента зависит от флага.

Флаги определяют

  • нужен ли "автоповтор" для этой клавиши (Строго говоря, флаг "автоповтор" хранится в другом месте, а не в переменной "поведение". Но в данном случае это не так уж важно.)
  • обычная эта клавиша или "залипающая" (то есть, по первому нажатию клавша "залипает" и отпускается при повторном нажатии). При этом "залипание" клавиши может отрабатываться самим "железом" или эмулироваться модулем XKB.
  • принадлежность этой клавиши к какой-нибудь "радио-группе". При этом дополнительный аргумент указывает номер такой "радио-группы".
  • принадлежит ли эта клавиша к "перекрывающейся группе" (overlay). Таких групп может быть всего две. Какая группа в данный момент активна, определяется "управляющими флагами" overlay1 и overlay2. Соответственно в "поведении клавиши" есть два флага которые и определяют принадлежность клавши к той или другой группе, а дополнительный аргумент указывает - какой скан-код должна эмулировать клавиша, если включен "режим перекрытия".

Набор "исключений".

Это битовая маска, которая указывает - какая информация, связанная с клавишей, "задана точно" (explicit) и не должна изменяться в некоторых случаях. Дело в том, что в core protocol определены команды, с помощью которых программы могут менять "раскладку клавиатуры" внутри клавиатурного модуля X-сервера. Естественно, эти комады меняют только "привязку" символов, поскольку другие "свойства" скан-кодов в core protocol'е не определены.

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

Так вот. Набор "исключений" может защитить информацию, связанную с конкретным скан-кодом, именно от таких косвенных изменений.
Если прикладная программа будет пользоваться соответствующим запросами XKB модуля (а не core protocol), такой защиты не требуется.

Итак, с помощью этой маски можно запретить

  • изменение типа клавиши (количество уровней) для каждой группы (можно установить запрет для каждой группы поотдельности);
  • изменения, которые могут произойти при применнии "интерпретации", при этом можно запретить -
    • вообще все изменения, вызываемые "интерпретацией";
    • изменение флагов "автоповтор" и "залипание";
    • изменение набора виртуальных модификаторов.

Реальный и виртуальный модификаторы.

Обратите внимание, что с каждым скан-кодом связаны две разные переменные (modmap и vmodmap), одна для реальных модификаторов, другая - для виртуальных.

Набор реальных модификаторов служит для эмуляции "традиционного" набора модификаторов (в соответствии с core protocol), а набор виртуальных модификаторов может служить аргументами для "действий", связанных с этой клавишей.

Надо заметить, что изменения набора виртуальных модификаторов (base, locked и latched) делается с помощью соответствующих "действий". Естественно, одним из аргуметов этого "действия" должен быть модификатор (или несколько модификаторов), которые нужно установить/сбросить в соответствующих "переменных состояния". Так вот, при описании "действия", модификаторы можно указать явно, а можно просто сослаться на vmodmap (то есть - "установить витруальные модификаторы, привязанные к этой клавише").

А вот изменение модификатора в "эмулируемом наборе модификаторов" происходит автоматически при нажатии/отпускании клавиши. Естественно, устанавливаются/сбрасываются именно те модификаторы, которые указаны в наборе реальных модификаторов (modmap).

Кроме того, эти два набора служат для установления соответствия между реальными и виртуальными модификаторами. Надо заметить, что виртуальный модификатор не имеет никакого смысла, если не связан с каким-нибудь реальным модификатором.

Состояние XKB: Набор управляющих флагов (XKB Controls).

Кроме номера группы и набора модификаторов, в XKB существует аналогичный набор для "управляющих флагов". Правда, в отличии от номера группы и набора модификаторов, которые "размазаны" по трем переменным (base, locked, latched), набор управляющих флагов только один.

В "управляющие флаги" входят все те "флажки", которые не попали в "набор модификаторов". Они не сообщаются приложению, а используются XKB для переключения его внутренних режимов.
Эти флаги управляют

"Управляющие флаги" (как и номера групп и модификаторы) меняются соответствущими действиями, привязанными к подходящим клавишам.

Индикаторы.

Как известно, на клавиатуре кроме "кнопок" имеется несколько "лампочек"- индикаторов. Управление этими индикаторами тоже входит в обязанности XKB.
В XKB может существовать до 32 индикаторов.
Естественно, на клавиатуре отображаются только первые 3-4 из них. Эти индикаторы называются в XKB "физическими", а остальные - "виртуальными". Состояние виртуальных модификаторов может быть считано из XKB и отображаться на экране специальными программами (xkbvleds, mxkbledpanel).

Каждый индикатор может быть связан с компонентом "состояниея XKB" (модификатором, номером группы, "управляющим флагом") и, соответственно, состояние индикатора будет автоматически отражать состояние своего "компонента состояния XKB".

Причем, в XKB существуют специальные запросы, которыми прикладная программа может управлять состянием какого-нибудь индикатора ("зажигать"/"гасить" его). Заметьте, речь идет о том, что программа может просто включать/выключать "лампочку", а не менять состояние клавиатуры, которое "отслеживается" этой "лампочкой".

Поэтому, с каждым индикатором связан набор флагов, который определяет

  • можно ли прикладной программе управлять самим этим индикатором (или для его включения/выключения обязательно надо изменить "состояние клавиатуры";
  • "привязан" ли этот индикатор к "состоянию клавиатуры" или он включается/выключается только прикладными программами;
  • и, наконец, будет ли такое включение/выключение иметь "обратную связь". То есть, можно настроить индикаторы так, что включение/выключение "лампочки" прикладной программой будет вызывать автоматически соответствующие изменения "состояния клавиатуры" (модификаторов, номера группы, управляющих флагов).

Таблица "совместимости".

Как уже упоминалось, XKB вынужден решать "проблему совместимости" с программами, которые не подозревают о существовании XKB и обращаются с запросами к "core-модулю" X-сервера.

Естественно, модуль XKB может обработать эти запросы, но основная проблема, возникающая при этом, состоит в том, что в модуле XKB вводятся некоторые новые понятия (новые относительно core protocol'а), которые никак не отражены в "традиционных" запросах к клавиатурному модулю.

  • Во-первых, в XKB больше модификаторов и их поведение может гибко перестраиваться. Поэтому, как уже упоминалось, для совместимости модулю XKB приходится поддерживать восемь "традиционных" модификаторов и осуществлять отображение (преобразование) своих модификаторов в "традиционные".

    Более того. XKB вынужден поддерживать "традиционный" формат "сообщения о нажатии/отпускании" клавиши, в котором передается "состояние" клавиатурного модуля, состоящее из набора реальных модификаторов. Даже если модуль XKB общается с XKB-совместимой Xlib, он вынужден, для передачи ей информации о состоянии своих виртуальных модификаторов, "превращать" их в реальные.
    (Поэтому, кстати, виртуальный модификатор не оказывает никакого эффекта, если он не "отображен" в какой-нибудь реальный).

  • Во-вторых, в core protocol "номер группы" имеет другой смысл. Как уже говорилось - групп всего две, переключаются они символом Mode_Switch и в "состоянии клавиатуры" переключение на "дополнительную" группу отображается одним из модификаторов Mod1-Mod5 - тем, который связан с символом Mode_Switch.

    Таким образом, в "традиционном" протоколе номер группы отображается одним из модификаторов - "основная группа/альтернативная группа", а в модуле XKB, в "состоянии клавиатуры" явно существует двухбитное поле - "номер группы" (которое, к тому же, не "перекрывается" с набором модификаторов).

    Кроме того, в "традиционном" модуле клавиатуры каждая группа состоит ровно из двух "уровней". Хотя при этом допускается, что каждому скан-коду может быть сопоставлен одномерный массив символов длиной до 256. Первые четыре ячейки в нем соответствуют "двум группам, в каждой по два уровня". Остальные ячейки "клиентская" программа может выбирать сама, в зависимости от состояния модификаторов Mod1-Mod5.

    Поэтому, для совместимости модуль XKB, при общении со "старыми" программами, вынужден ...

    • Во-первых, "размазывать" свои подтаблицы-группы в одну строчку "традиционной" таблицы символов (честно говоря, я так и не понял общий алгоритм этого действия, кроме тривиального случая, когда скан-коду соответствует две группы, каждая из двух уровней).
    • Во-вторых, отображать свой "номер группы" в состояние какого-нибудь "реального" модификатора.
  • Наконец, в core-модуле отсутствуют "действия" - основной механизм, обеспечивающий "гибкость поведения" XKB. В связи с этим возникает проблема - "старая" программа может поменять "привязку" символов к скан-кодам, но, поскольку она не подозревает о том, что к скан-кодам могут быть "привязаны" также и "действия", может возникнуть ситуация, когда такая программа переместить, например, символ "переключатель РУС/ЛАТ" на другую клавишу, а соответствующее "действие" (которое в XKB реально осуществляет это переключение) останется на старом месте.

    Для решения этой проблемы в XKB хранится таблица "интерпретаций" (interpretation) управляющих символов. Эта таблица связывает коды символов и вызовы соответствующих "действий" (actions). Естественно, в этой таблице присутствуют только специальные "управляющие" символы ( Caps_Lock, Shift, Num_Lock, "переключатель РУС/ЛАТ" и т.п.).

    Кроме самих символов и "действий" в таблице интерпретаций хранится также дополнительная информация - список "реальных модификаторов" и "критерий соответствия" ("любой из модификаторов", "все указанные модификаторы", "только указанные модификаторы" и т.п.).

    Каждый раз при изменении "привязки" символа (учтите, что это изменение может происходить при старте X-сервера, как часть "начальной настройки" сервера) модуль XKB проверяет - присутствует ли в этот символ в "таблице интерпретаций". И если символ там есть, то XKB, "присвоив" этот символ требуему скан-коду, добавляет этому скан-коду и соответствующее "действие". Дополнительная информация (модификаторы и "критерий соответствия") тоже может использоваться при "поиске" подходящего места для "действия".

    Кроме того, применение "интерпретации" может поменять некоторую другую информацию, связанную со скан-кодом - флаги, отвечающие за "автоповтор" и "залипание" клавиши и "список виртуальных модификаторов".

    Напомню, что у каждого скан-кода может быть указан набор "исключений", который защитит информацию, связанную со скан-кодом от применения "интерпретации".

    Надо отметить, что помещать все "действия" в "таблицу интерпретаций", а не присваивать их непосредственно скан-кодам - является "хорошим тоном". Поскольку в "таблице интерпретаций" "действие" связывается с символом, а не со скан-кодом, и, к тому же, "перенос" "действий" из "таблицы интерпретаций" в соответствуюшие таблицы "действий" скан-кодов XKB выполняет каждый раз при загрузке конфигурации (в том числе и при старте X-сервера), модуль XKB сам найдет подходящее место для соответствующего "действия" и, таким образом, корректно "свяжет" символы и действия (в таблицах, назначенных скан-кодам).

Еще несколько определений.

Радио-группы. (Radio Groups)

Модуль XKB позволяет объединять несколько клавиш в "радио-группу". То есть, клавиши одной ради-огруппы являются взаимозависимыми. При нажатии одно из клавиш группы все остальные из этой группы "отжимаются".

Естественно, нажатая клавиша остается "залипшей", пока не будет нажата другая клавиша из этой группы. Принадлежность конкретной клавиши к некоторой радио-группе задается в поле "поведение клавиши".

Там же можно определить дополнительное свойство - можно ли "отжать" все клавиши радио-группы. Обычное определение радио-группы подразумевает, что одна из клавиш группы должна быть нажата, а чтобы отжать ее, надо нажать другую клавишу в группе (естественно, теперь она будет "залипшей"). Если задано свойство "могут быть отжаты все", то "залипшую" клавишу группы можно "отжать" просто повторным нажатием (при этом никакая другая клавиша группы не окажется "залипшей").

В XKB можно объявить до 127 радио-групп (хотя это может зависить от конкретной реализации XKB).

Перекрытия (Overlay).

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

Такая группа называется "перекрытием" (overlay). Их может быть всего две. Принадлежность конкретной клавиши к конкретной группе-"перекрытию" и "альтернативный" скан-код задаются в поле "поведение клавиши".

Это свойство используется для клавиатур, у которых очень маленький набор клавиш (например в "лаптопах"). Например, на такой клавиатуре может полностью отсутствовать "дополнительная цифровая клавиатура" (keypad). Поскольку некоторые приложения могут потребовать, чтобы какие-то действия в них вызывались нажатием кнопок именно на keypad, модуль XKB может эмулировать эту дополнительную клавиатуру, используя, например, "цифровые" кнопки на основной клавиатуре.

AccessX.Дополнительные услуги для людей с ограниченными физическими возможностями.

Имеются ввиду люди у которых подвижность рук (пальцев) ограничена или они вынуждены пользоваться какими-либо маханическими приспособлениями для работы с клавиатурой.

Проблемы, которые могут возникнуть при этом:

  • Невозможно нажимать одновременно несколько клавиш (например Shift+"буква"). Для решения этой проблемы в XKB предусмотрен режим StickyKeys ("прилипающие клавиши"). В этом режиме вместо одновременного нажатия нескольких клавиш можно нажимать их последовательно. Например, вместо Shift+Control+C можно последовательно нажать клавиши Shift, Control и C. Обратие внимание - не все клавиши должны "слипаться". Правило здесь простое - если клавиша является модификатором и без других кнопок ее нажатие не имеет смысла, то после нажатия такой клавиши XKB ждет нажатия других клавиш, чтобы "слепить" в одно событие. Если и последующие кнопки являются модификаторами (как Control после Shift в нашем примере), то XKB остается в состянии ожидания. И только после нажатия обычной (буквенной клавиши) генерируется event и XKB возвращается в нормальное состяние.
    Кроме того заметьте, что этот режим сам может вызвать проблему если клавиша модификатор нажата по ошибке. Любое следующее нажатие вызовет не то действие, которое хотелось бы. Поэтому в режиме StickyKeys предусмотрена специальная задержка (это настраиваемый параметр) в течении которой XKB ждет следующую клавишу. Если за это время другого нажатия не последовало, то клавиша отпускается.
  • "Дребезг" клавиши при нажатии. Для решения этой проблемы в XKB предусмотрен режим BounceKeys ("прыгающие клавиши"). В этом режиме после первого нажатия на клавишу XKB на некоторое время становится "нечувствительным", то есть не обращает внимание на изменение состяния клавиши и таким образом игнорирует лишние нажатия. Естественно интервал времени "нечувствительности" можно менять.
  • Ошибочное "задевание" клавиш. Имеется ввиду, что человек при перемещении руки от одной клавиши к другой может случайно задеть несколько клавшь, которые нажимать не собирался. Для решения этой проблемы предусмотрен режим SlowKeys ("медленные клавиши"). Смысл его в том, что клавиша считается реально нажатой если она остается в таком состоянии некоторое время (опять же это время можно настраивать). Другими словами, если клавишу нажать и быстро отпустить, то XKB игнорирут такое скоротечное нажатие.
  • Невозможность управлять "мышью". Для решения этой проблемы в XKB предусморена возможность эмулировать события мыши с помощью клавиатуры. Подробнее об этом режиме мы поговорим ниже.

Все эти режимы осуществляются частью XKB модуля, которая называется AccessX. Все режимы можно включать и выключать по отдельности изменяя состяние одноименных "управляющих флагов XKB" (XKB Controls). Кроме того всю подсистему AcceessX можно выключить одним из флагов (AccessXKeys). (Естественно, чтобы заработал хотя бы один из режимов нужно включить и AccessX, и нужный режим).

Обратите внимание, что само включение режимов AccessX может быть проблемой. (Представьте себе, что человеку нужен режим StickyKeys, а для его активизации нужно нажать сложную комбинация из нескольких клавиш). Поэтому для включения некоторых режимов используются специальные действия:

  • Если нажать клавишу Shift и удерживать ее нажатой в течении 8 секунд, то включается режим SlowKeys.
  • Если нажать тот же Shift пять раз подряд, то включется режим StickyKeys.
Правда для этого должен быть включен сам AccessX. Его можно включить
  • либо указав соответствующий ключ при запуске X-сервера,
  • либо установив нужный флаг в "файле конфигурации XKB",
  • или включить тот же флаг с помощью клавиши с нужным action (по умолчанию такой клавиши нет, но ее можно предусмотреть в раскладке клавиатуры).

С другой стороны, если один из режимов AccessX включен, а компьютер используется разными пользователями, то человку не имеющему ограничений он может наоборот мешать. Поэтому в XKB предусмотрено автоматическое отключение режимов AccessX, если в течении некоторого времени клавиатура никак не использовалась. Кроме того, режим StickyKeys (эмуляция одновременного нажатия через последовательное) автоматически выключиться, если вы нажмете несколько клавиш действительно одновременно.
Саму возможность автоматического выключения AccessX по таймауту можно (как и другие режимы) включить/выключить соответствующим флагом (AccessXTimeout) в XKB Controls.

И наконец - в AccessX предусмотрен дополнительный режим звуковой индикации всех происходящих событий - начало и окончание всевозможных таймаутов, а также включение и выключение LED'ов. Для того, чтобы лучше ориентироваться в этх событиях (особенно если включено несколько разных режимов AccessX) XKB старается озвучивать их звуками разной длительности и тона (насколько позволяет "железо").
Этот режим, как и другие может быть включен/выключен соответсвующим флагом (AccessXFeedback) в XKB Controls. Кроме того, можно по отдельности включить/выключить озвучивание отдельных событий.

Эмуляция "мыши"

XKB может эмулировать события мыши с помощью клавиатуры. То есть, его можно настроить так, что при нажатии определенных клавиш X-сервер будет посылать приложению сообщения (events) не о нажатии/отжатии кнопок, а о перемещении мыши и нажатии кнопок на мышке.

Делается это с помощью соответсвующих действий (actions) - перемещение мыши, нажатие кнопки мыши, выбор кнопки "по умолчанию".

Рассмотрим их немного подробнее.

  • В action "перемещение" можно задавать сразу обе координаты (или их приращения). Координаты могут быть как абсолютные (это означает, что при выполнении такого действия указатель мыши должен встать в заданную точку экрана), так и относительные - собственно перемещение на несколько точек в нужном направлении. Обычно для удобства делают несколько разных action для одного направления, но с разными приращениями - на одну точку, на десять точек и т.д. Эти action можно привязать к одной кнопке, но в разных уровнях. Получится, что при обычном нажатии кнопки указатель смещается, на одну точку, а нажатие той же кнопки с Shift'ом перемещение будет на несколько точек при одном нажатии.
  • В action "нажатии кнопки мыши" можно кроме самой кнопки указать и число повторов. То есть, одиночное нажатие клавиши может эмулировать "двойной клик" (или тройной, четверной и т.п.).
  • Кроме того, существует разновидность этих действий, которая не просто "нажимает кнопку мыши" по нажатии клавиши, а оставляет ее нажатой после отпускания клавши (как все Lock клавши). То есть, вы можете с помощью такого действия "прижать кнопку мыши", переместить указатель и повторным нажатием клавиши "отпустить кнопку мыши". Естественно, это выглядит так, как будто вы двигали реальную мышь с нажатой кнопкой.
  • И наконец, обратите внимание, что кнопок мыши может быть до пяти, а с учетом "многократных кликов" и Lock'ирующих нажатий может получится, что под "мышинные кнопки" придется занять довольно много клавиш. Для того, чтобы сократить это количество в XKB предусмотрено, что одну из кнопок можно объявить "кнопкой по умолчанию" (default button). Для такого назначения и его изменения предусмотрены специальные действия, в которых можно опять же указать, как конкретную кнопку (по номеру), так и относительное изменение номера "кнопки по умолчания". Комбинируя эти действия, можно одной клавишей перебирать циклически кнопки (делая их по очереди "умолчательными"), а в действиях "нажимающих" кнопки указать, что они относятся к той кнопке, которая сейчас выбрана как default button. (Проблема только в том, как не запутаться и не забыть - какая кнопка сейчас выбрана :-)

Кроме того, существует два режима (mode) перемещения мыши - простой и "с ускорением" (accelerated). При одиночном нажатии клавиши разница между ними незаметна. А вот если вы держите клавишу нажатой и работает автоповтор, тот в обычном режиме на каждый автоповтор указатель смещается на одно и то же количество точек (заданое в аргументах действия). А в accelerated mode указатель начинает ускоряться, то есть с каждым новым автоповтором размер одиночного шага растет.

Весь процесс ускорения описывается несколькими дополнительными параметрами, которые хранятся во внутренних переменных XKB (являются частью "состояния" XKB). (Надо заметить, что режим ускорения имеет свои параметры автоповтора - задержку между реальным нажатием клавиши и первым повтором и интервал между повторорами.) Итак, эти параметры (числовые) описывают

  • delay - интервал (в миллисекундах) от нажатия клавиши (эмулирующей движение мыши) до первого автоповтора.
  • interval - интервал (в миллисекундах) между повторами
  • maxspeed - максимальная скорость (в точках на один повтор)
  • timetomax - количество повторов, за которое достигается максимальная скорость движения. Как видите, здесь нет в явном виде величины ускорения. XKB сам вычисляет его из начальной величины смещения (задается в конкретном действии), maxspeed и timetomax.
  • curve - "степень кривизны" ускорения. Дело в том, что скорость может расти не линейно. В общем виде приращение скорости пропорционально функции X^(1 + curve/1000). То есть, при curve=0 скорость растет линейно, а при других значениях (от -1000 до +1000) с некоторой "кривизной".
По умолчанию используется именно режим с ускорением.

Включение и выключение эмуляции мыши и выбор режима движения выбирается двумя управляющими флагами XKB (XKB Controls) - MouseKeys и MouseKeysAccel.

Надо заметить, что по умолчанию все нужные действия описаны и привязаны к кнопкам "дополнительной цифровой клавиатуры" (NUMPAD). Для включения и выключения режима эмуляции мыши используется комбинация Shift+NumLock.

Расширенные возможности "пищалки" (Bell).

Вообще-то, эта часть XKB имеет малое отношение к клавиатуре.
Более того, для пользователей компьютеров, в которых клавиатура, дисплей и "спикер" - независимые устройства, такое совмещение может показаться очень странным. Но поскольку, в некоторых архитектурах динамик "пищалки" находится в клавиатуре и издает звук (key_click) при каждом нажатии клавиши, в "иксах" считается, что управление "пищалкой" - дело клавиатурного модуля.

Надо отметить, что управление "пищалкой" заложено еще в традиционном (core) модуле клавиатуры. С помощью стандартных запросов к X-серверу, приложение может регулировать параметры key_click'а (высоту тона, длительность и громкость), а также вызвать этот click при необходимости.

В XKB разработчики пошли еще дальше и предусмотрели возможность вместо простого "писка" проигрывать "музыкальные фрагменты". Естественно, обеспечить такое "музыкальное сопровождение" для клавиатурного модуля слишком сложная задача. Во-первых нужна "база данных" различных звуков (хотя бы просто в виде набора аудио-файлов), а во-вторых музыка может воспроизводится различными "железками" (звуковыми картами), каждая из которых требует "особого подхода" (драйвера).

Поэтому, по замыслу разработчиков, выбором конкретного звука и его воспроизведением должна заниматься отдельная программа (назовем ее - "музыкальная приставка"). А модуль XKB вместо "писка" просто генерирует специальное сообщение (event), которое так же как и обычные event'ы X-сервера могут быть доставлены любой программе. "Музыкальная приставка" только должна при старте сообщить X-серверу, что ее интересует определенная разновидность event'ов (в данном случае - bell-event'ы от модуля XKB).

Естественно, если такая "музыкальная приставка" существует, ее возможности не ограничиваются воспроизведением "писков" разной частоты и длительности. Она может проигрывать множество разных музыкальных фрагментов из своей "базы данных".
Поэтому, в сообщениях XKB указывается не "параметры писка", а просто некое "имя звука" (bell name). Естественно, "музыкальная приставка" должна иметь какой-то конфигурационный файл, в котором для каждого "имени звука" назначен свой музыкальный фрагмент.

Соответственно, теперь любое приложение с помощью соответствующих запросов к X-серверу (теперь уже не в core-формате, а в формате XKB), может заказать не просто "писк" с определенным тоном и длительностью, а любой звук, указав его имя (bell name).

При этом XKB собственно является просто "ретранслятором". Он даже не анализирует правильность "имени звука", а просто, получив запрос от любого приложения, передает его в виде своего event'а "музыкальной приставке".

Естественно, в такой схеме XKB кажется даже лишним. Приложение могло бы и само общаться с "музыкальной приставкой". Но не забудьте, что сама идеология "иксов" допускает, что приложение, X-сервер и "музыкальная приставка" могут быть запущены на различных машинах. Поэтому, нормальное приложение должно было бы, кроме соединения с X-сервером, найти еще и "музыкальную приставку" и установить с ней контакт.

В схеме с XKB, любое приложение отправляет свои запросы тому же самому X-серверу (с которым ему и так приходится общаться), а уж администратор сервера определяет - кто и как будет обслуживать эти запросы. Кроме того, при этом вводится некоторый стандарт на общение между приложением и "звуковой приставкой" (разве что, кроме самих "имен звуков").

Надо заметить также, что XKB не только ретранслирует запросы, но и сам может "заказать звук" при некоторых изменениях своего внутреннего состояния (состояния индикаторов, флагов и т.п.).


Иван Паскаль pascal@tsu.ru