Еще несколько "переключателей".

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

Причем, опять же, первая группа - "латиница". А остальные три - варианты "русской" раскладки (например - koi8, alt, cp1251).

И опять же, нам хочется, чтобы основной переключтель имел только два состояниия - "лат"/"какой-нибудь рус".

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

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

Как это можно сделать?

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

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

Я сделал такую кнопку из клавиши "1".


key <AE01> {	[   1, exclam ],

		[   2, exclam ],

		[   3, exclam ],

		[   4, exclam ]  };

(значение клавиши отражает текущий номер группы :-).

Итак.

Вариант первый - "выделенные скан-коды".

Возьмем за основу метод, рассмотренный в предыдущих примерах - две переменные для номера группы.

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

Тогда пусть основной переключатель манипулирует значением в locked group.
Он должен

Для вычисления "добавки" на время перейдем к "внутренним представлениям номера группы" - 0,1,2...
Тогда

"Добавки"

Возвращаясь к обозначениям групп из "конфигов", описание клавиши - основного переключателя получится


key <CAPS> {	[NoSymbol], [NoSymbol], [NoSymbol], [NoSymbol],

		actions[Group1]=[ LockGroup(group=1) ],

		actions[Group2]=[ LockGroup(group=4) ],

		actions[Group3]=[ LockGroup(group=3) ],

		actions[Group4]=[ LockGroup(group=2) ] };

Все хорошо. Но что нам делать с дополнительным переключателем (переключателями)?

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

Если мы нажали кнопку, ответственную за первую "рус", она должна записать свое значение в base group и "залипнуть". Если мы после этого нажмем кнопку, отвечающую за вторую "рус", она должна

Аналогично должна работать и третья кнопка (из дополнительных переключателей).

Но то, что я только что описал, точь в точь описывает поведение кнопок, принадлежащих одной "радио-группе".

Ну и отлично! Объединим наши три дополнительных переключателя в радио-группу, подвесим на них SetGroup с соответствующими аргументами, и... все.

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

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

Я предлагаю взять кнопки F10, F11, F12. Они стоят рядом и используются сравнительно редко.

Можно составлять их описание (это скан-коды <FK10>,<FK11>,<FK12>)


key <FK10> { 	radiogroup=2, [NoSymbol],

		actions[Group1]=[ SetGroup(group=2) ] };

key <FK11> { 	radiogroup=2, [NoSymbol],

		actions[Group1]=[ SetGroup(group=3) ] };

key <FK12> { 	radiogroup=2, [NoSymbol],

		actions[Group1]=[ SetGroup(group=4) ] };

Обратите внимание, что номер радио-группы должен быть больше единицы. Вообще-то, это "баг" XKB, тем более неприятный, что xkbcomp не будет "возражать" если вы объявите радио-группу номер 1, а вот X-сервер потом "сдуреет".

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

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

Вариант второй - "перекрытия".

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

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

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

Напомню, что если кнопка принадлежит к "группе перекрытия" (overlay), она может эмулировать нажатие другой кнопки (с другим скан-кодом), естественно, эта другая кнопка не обязана физически присутствовать на клавиатуре и може быть вполне "виртуальной". К тому же, та кнопка, которая будет эмулировать "виртуальную" не обязана заниматься этим все время. Для того, чтобы он перешла в такой режим, нужно "поднять" "управляющий флаг" Overlay1 ( или Overlay2), а все остальное время она может ваполнять свои "основные обязанности".

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

Итак. Давайте сначала объявим скан-коды "виртуальных" кнопок. Заглянем в файл типа xkb_keycodes (скорее всего у вас это keycodes/xfree86).

Можно заметить, что под реальные кнопки задействованы скан-коды от 9 до 120 (ну, на самом деле - даже меньше, в зависимости от типа клавиатуры).
То есть, мы для своих нужд можем спокойно взять скан-коды, например - 121, 122, 123.

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


xkb_keycodes {

   <RUS1> = 121;

   <RUS2> = 122;

   <RUS3> = 123;

};

(напомню, что "название скан-кода" может быть произвольное, но не длинее 4 символов).

Теперь мы можем наши "действия" убрать с реальных кнопок <FK10> - <FK12> и "повесить" на наши виртуальные (просто поменяем названия скан-кодов) -


key <RUS1> { 	radiogroup=2, [NoSymbol],

		actions[Group1]=[ SetGroup(group=2) ] };

key <RUS2> { 	radiogroup=2, [NoSymbol],

		actions[Group1]=[ SetGroup(group=3) ] };

key <RUS3> { 	radiogroup=2, [NoSymbol],

		actions[Group1]=[ SetGroup(group=4) ] };

Дальше нам надо решить - на какие реальные кнопки мы возложим задачу эмулировать наши виртуальные клавиши.

Поскольку это никак не "ущемляет" реальную кнопку (ее основные обязанности), выбирать мы можем так - как нам будет удобнее. Можно для этого выбрать "цифровые" кнопки - 1,2,3. А можно - какие-нибудь "буквенные", например - K (koi8), A (alt), W (Windows).
Я остановлюсь на первом варианте.

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


key <AE01> {	overlay1 = <RUS1>,

		[	1,  exclam ],

		[  exclam,       1 ] };



key <AE02> {	overlay1 = <RUS2>,

		[	2,	at ],

		[ quotedbl,	 2 ] };



key <AE03> {	overlay1 = <RUS3>,

		[   3,  numbersign ],

		[ apostrophe,	 3 ] };

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

Я остановлюсь на той же <MENU>.
Нам нужно "подвесить" на нее "действие" SetControls(controls=overlay1). Напомню, что это "действие" "держит" флаг только пока кнопка нажата. То есть, кнопки 1-2-3 нажатые вместе с Menu (Menu+1, Menu+2, Menu+3) будут действовать как дополнительные переключатели. А если кнопку Menu не трогать, то эти клавиши работают как обычно.


key <MENU> {	[NoSymbol],

		actions[Group1]=[ SetControls(controls=overlay1) ]};

Все. Можно попробовать.
(Если это не будет работать, попробуйте заменить SetControls на LockControls. Естественно, в этом случае клавишу <MENU> не надо удерживать в нажатом состоянии. По первому нажатию она будет "включать" управляющий флаг overlay1 и кнопки 1-2-3 превратятся в переключатели групп, а по повторному нажатию флаг overlay1 будет "сбрасываться" и кнопки 1-2-3 вернутся к своей "основной работе").

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

Но, основные механизмы (и "подводные камни") я описал. Все остальное зависит от вашей фантазии.


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


Banner.Novgorod.Ru