Полезная информация

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Однако, никто не запрещает использовать программы, которые были "слинкованы" со старой библиотекой Xlib, "не подозревающей" о существовании XKB.

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

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

Для каждого скан-кода в XKB хранится двумерная таблица (массив) символов. "Координатами" в этой таблицы являются "номер группы" или просто - "группа" (group) и "уровень" (shift level). Обычно, разные группы используются для разных языков (а точнее - алфавитов), а уровень разделяет символы на "большие/маленькие" буквы (ну, то что меняется при нажатии Shift).
Хотя, никто не мешает использовать деление на "группы" и "уровни" как-нибудь по другому.

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

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

Заметим, что в core-модуле каждому скан-коду соответсвует одномерный "вектор" символов (длиной до 256). И, хотя понятие группы в нем также существует, но...
Группа состоит только из двух уровней. И строго определены только две группы.
В "векторе символов" первые два символа относятся к первой группе (два уровня одной группы), вторые два - относятся ко второй группе.

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

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

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

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

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

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

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

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

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

Естественно, при таком суммировании может получиться число, больше чем количество заданных групп. Чтобы получить из него какой-нибудь допустимый номер группы, 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 существует в трех экземплярах (в трех переменных)

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

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

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

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

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

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

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

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

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

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

Тип клавиши.

"Тип клавиши" указывает - к какому типу относится данная клавиша. То есть - по какому правилу вычислять shift level для данной клавиши.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Индикаторы.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Набор управляющих флагов (XKB Controls).

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

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

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

Расширенные возможности "пищалки" (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