Описание "действий".

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

Описание действия похоже на объявление функции в языке C, то есть

имя_действия '(' список_аргументов ');'
Но отличие в том, что в списке аргументов указываются не просто значения, а пары имя-значение:
имя_аргумента '=' значение_аргумента
Например
 MovePointer(x=10, y=10, repeat=False);

Кстати программа xkbcomp, которая компилирует файлы конфигурации, во многих случаях понимает разные названия для одного и того же действия и разные имена для одного и того же аргумента.

Внутри XKB действия представляют собой некую структуру которая содержит

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

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

SomeAction(x=10, y=10)
означает абсолютные значения для x и y, а
SomeAction(x=+10, y=+10)
означает положительные приращения для тех же переменных.

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

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

И еще несколько слов о том, как в описании действия указываются флаги. Хотя они предствляют собой отдельные биты в одном и том же поле внутренней структуры XKB, но в описании указываются как отдельные переменные типа boolean (то есть, их значением может быть только "да" или "нет"). При этом xkbcomp понимает в качестве значений логической переменной и другие слова - в качестве "да" вы можете писать yes, on, true, а качестве "нет" - no, off, false.

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

Например следующие строчки полностью эквивалентны

SomeAction(..., SomeFlag=yes,  ...);
SomeAction(..., SomeFlag=on,   ...);
SomeAction(..., SomeFlag=true, ...);
SomeAction(..., SomeFlag,      ...);
и следующие строчки тоже эквивалентны
SomeAction(..., SomeFlag=no,    ...);
SomeAction(..., SomeFlag=off,   ...);
SomeAction(..., SomeFlag=false, ...);
SomeAction(..., !SomeFlag,      ...);
SomeAction(..., ~SomeFlag,      ...);

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

Действия, изменяющие состояние XKB.

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

Изменение набора модификаторов (modifiers).

Как я уже сказал, набор модификаторов распределен по трем переменным - base modifiers, latched modifiers и locked modifiers.
Соответственно действия для их изменения:

Основной аргумент у всех трех действий - modifiers (другое имя - mods). А его значение - название виртуального или реального модификатора. Если одно действие меняет сразу несколько модификаторов, их можно перечислить через знак "+". Например

SetMods(mods=Shift+Control);

Вместо названия модификатора можно указать специальное значение UseModMapMods (или просто ModMapMods). Это будет означать, что сами модификаторы надо взять из списка виртуальных модификаторов, связаных с этой клавишей (modmap и vmodmap).

Надо также заметить, что эти три действия отличаются не только тем, какую переменную они меняют. Они по разному работают в момент нажатия и в момент отпускания клавиши. Вспомните как отличается работа клавиш Shift и CapsLock. Первая должна действовать только пока ее удерживают в нажатом состоянии, то есть при ее нажатии модификатор Shift должен появиться, а при отпускании - автоматически исчезнуть. А вот CapsLock должна действовать долговременно - при первом нажатии ее модификатор должен стать активным и оставаться в таком состоянии даже после того как вы отпустите клавишу. А вот по повторному нажатию/отпусканию - убраться.

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

А вот действие LockMods при первом исполнении только добавляет модификатор в locked modifiers, но не удаляет его при отпускании клавиши, а вот если модификатор уже установлен (то есть это уже повторное нажатие то же клавши), то при нажатии клавиши модификатор наоборот - убирается из locked modifiers.

Обратите внимание, что на самом деле совсем необязательно, чтобы вы использовали для модификатора Shift действие типа SetMods, а для модификатора Lock - LockMods. Вы можете "залокировать" Shift или наоборот - делать Lock активным только на время удержания клавши. Но это уже зависит от того, что вы собственно хотите этим добится. :-)

Также поведение первых двух действий могут слегка изменяться с помощью двух флагов - clearLocks и latchToLock.

Поэтому полное описание всех деталей этих действий выглядит так

Действие При нажатии При отпускании
SetMods Добавляет модификаторы в base modifiers
  • Убирает свои модификаторы из base modifiers
  • если clearLocks=yes и между нажатием и отпусканием этой клавиши вы не нажимали другие клавиши, то эти же модификаторы вычищаются и из locked modifiers
LatchMods Добавляет модификаторы в latched modifiers
  • Убирает свои модификаторы из latched modifiers
  • если clearLocks=yes и между нажатием и отпусканием этой клавиши вы не нажимали другие клавиши, то эти же модификаторы вычищаются и из locked modifiers
  • если latchToLock=yes, то те же модификаторы запоминаются в locked modifiers
LockMods
  • Добавляет модификаторы в base modifiers
  • если этих модификаторов нет в locked modifiers, то добавляет их туда, в противном случае наоборот - убирает
  • Убирает свои модификаторы из base modifiers
  • locked modifiers не меняется.

Изменение номер группы.

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