Elmord's Magic Valley

Computers, languages, and computer languages. Às vezes em Português, sometimes in English.

Migrando para o XKB

2013-02-23 21:48 -0300. Tags: comp, unix, kbd, em-portugues

(Se você não quer saber de preliminares, clique aqui.)

Depois de muita enrolação, decidi migrar meu keymap do xmodmap para o XKB. Sobrevivi à experiência.

O objetivo deste post é relatar parte do que eu aprendi no processo. Para uma referência mais ou menos completa da configuração do XKB, você pode consultar o mui excelente An Unreliable Guide to XKB Configuration, ou algum outro dos links listados no final do post sobre o xmodmap. Neste post, pretendo cobrir o básico necessário para migrar do xmodmap para o XKB (talvez um pouco mais do que o básico, mas enfim).

Little rant

O XKB é um pequeno monstro. Ele foi feito para resolver todos os problemas que ninguém teve antes dele. Por exemplo: existe uma seção do keymap, xkb_geometry, cujo objetivo é descrever a disposição física das teclas no teclado. Afinal, algum programa pode querer desenhar o teclado na tela (o comando xkbprint faz isso), ou saber que tecla fica do lado de qual. Nunca se sabe. Existem pelo menos três maneiras de descrever um layout pré-pronto (XkbKeymap, XkbRules, ou descrevendo componente por componente do keymap), e pelo menos três meios de ativar uma configuração (setxkbmap, xkbcomp ou pelo arquivo de configuração do X). E como todo programa no Unix, o XKB usa um arquivo de configuração com sua própria sintaxe idiossincrática, com seu próprio parser que emite mensagens de erro extremamente prestativas, como esta diante da falta de um ; na linha 21:

syntax error: line 23 of /root/.xkb/default
last scanned symbol is: modifier_map
Errors encountered in /root/.xkb/default; not compiled.

Alguém podia voltar no tempo e colocar um leitor de S-expressions na libc original.

Dito isso, muito da complexidade do XKB pode ser ignorada para tarefas mais simples de edição de keymap (leia-se: tudo o que era possível com o xmodmap). Além disso, o fato de o povo do X ter conseguido implementar as loucuras do XKB sem quebrar compatibilidade com os programas mais antigos é digno de três estrelinhas (pelo que eu entendo o próprio xmodmap continuou funcionando intacto depois da adição do XKB ao X, ainda que com ocasionais comportamentos estranhos).

But that's enough talk. Have at you!

Conceitos básicos

Antes de mais nada, gostaria de esclarecer que XKB não é um programa, e sim uma infraestrutura de configuração de keymap. O XKB é composto por uma extensão do servidor X e um conjunto de funções da biblioteca Xlib que torna as funcionalidades do XKB acessíveis aos clientes X. Além disso, o X vem com alguns programas utilitários, tais como xkbcomp, setxkbmap e xkbprint, que utilizam a infraestrutura do XKB para ler e manipular o layout. (O xmodmap, por outro lado, é um programa que utiliza a o "core protocol" do X para manipular o teclado; a função do xmodmap é vagamente similar à do xkbcomp e setxkbmap.)

Outra diferença entre o xmodmap/core-protocol e o XKB é que no XKB não existe (aparentemente) a noção de alterar o keybinding de teclas individuais do keymap; é necessário sempre carregar um keymap completo. Por outro lado, os arquivos de configuração do XKB suportam uma diretiva include, que nos permite incluir um keymap pré-pronto em um arquivo de configuração e especificar apenas o que queremos de diferente do default.

O programa principal de controle do XKB é o xkbcomp. A função principal desse programa é ler um arquivo texto descrevendo um keymap e compilá-lo para um formato binário que o X é capaz de digerir. A sintaxe básica é:

xkbcomp [opções] origem destino

Porém, o xkbcomp é uma criatura bastante flexível. Você pode especificar como destino algo como :0, instruindo o xkbcomp a compilar o keymap e ativá-lo no display :0, sem necessidade de criar um arquivo binário intermediário. Além disso, você pode especificar :0 como origem para fazer um dump do keymap atual para um arquivo texto especificado como destino (você pode usar - para imprimir para a stdout). O dump do xkbcomp é enorme (1844 linhas na minha máquina; arquivos de keymap na prática usam includes, o que os torna muito menores), mas é útil para vermos quais são as diretivas de configuração possíveis e qual é a sintaxe de cada uma.

A função primária do setxkbmap é pôr em efeito um keymap pré-pronto. A sintaxe básica é:

setxkbmap [opções] [layout [variant [option...]]]

Onde layout, variant e option são uma das três maneiras de descrever um keymap no X (XkbRules). Exemplos:

setxkbmap br abnt2                 # Layout ABNT-2
setxkbmap us intl                  # Layout US internacional (com acentos)
setxkbmap br abnt2 ctrl:swapcaps   # Layout ABNT-2 com Ctrl e CapsLock invertidos
setxkbmap br abnt2 ctrl:nocaps     # Layout ABNT-2, CapsLock se comporta como Ctrl

Como mencionado anteriormente, você pode atribuir keymaps diferentes a teclados diferentes usando a opção -device id, onde id é o id do dispositivo tal como exibido pelo comando xinput list.

O esquema de rules é muito bonito quando queremos usar um keymap pronto, mas não quando queremos fazer modificações ao keymap. Nesse caso, teremos que criar nosso próprio arquivo de keymap. Como não é possível aplicar um keymap parcial sobre o keymap existente com o XKB, e não queremos ter que descrever todo o keymap só para alterar meia dúzia de teclas, o ideal é usar includes. Felizmente, o setxkbmap suporta uma opção -print, que imprime um keymap do XKB que descreve o keymap atual (ou um especificado na linha de comando) em termos de includes:

# setxkbmap -print -option "" br abnt2
xkb_keymap {
        xkb_keycodes  { include "evdev+aliases(qwerty)" };
        xkb_types     { include "complete"      };
        xkb_compat    { include "complete"      };
        xkb_symbols   { include "pc+br(abnt2)+inet(evdev)"      };
        xkb_geometry  { include "pc(pc105)"     };
};

(O -option "" serve para resetar as options do XKB (tais como ctrl:nocaps e ctrl:swapcaps). Por padrão, o setxkbmap usa as opções ativas atualmente, mesmo que elas não sejam especificadas na linha de comando. Você também pode executar simplesmente setxkbmap -print, sem especificar um keymap, para usar o keymap atual (ou pelo menos o que o setxkbmap pensa que é o keymap atual).)

Eis um keymap completo! Se você salvar esse keymap em um arquivo, você pode usar o comando xkbcomp arquivo :0 para pôr o keymap em efeito.

Componentes e includes

Como podemos ver na saída do setxkbmap -print, um keymap é composto por cinco componentes:

Basicamente a única parte que nos interessa do keymap é a xkb_symbols; podemos ignorar todo o resto e simplesmente copiar a configuração emitida pelo setxkbmap.

A diretiva include instrui o xkbcomp a procurar os componentes especificados em seu caminho de pesquisa. Por padrão, o xkbcomp procurará no diretório /usr/share/X11/xkb. Dentro desse diretório você encontrará subdiretórios para cada componente (keycodes, symbols, etc.). O xkbcomp procurará no subdiretório apropriado dependendo da seção em que a diretiva include ocorrer.

Cada um desses arquivos pode conter mais de uma versão do mesmo componente. Uma diretiva da forma include "br(abnt2)" indica o componente chamado abnt2 dentro do arquivo br no diretório apropriado. Também é possível incluir múltiplos componentes, usando a sintaxe include "componente1+componente2".

Você pode adicionar outros diretórios ao caminho de pesquisa do xkbcomp usando a opção -Idiretório (sem espaço após o -I). O xkbcomp esperará encontrar os componentes a incluir dentro dos subdiretórios apropriados no diretório especificado (e.g., diretório/symbols, etc.).

Símbolos

Vamos ao que interessa: fazer com o XKB o que a essas alturas já teríamos feito com o xmodmap. Eis um arquivo de keymap de exemplo:

xkb_keymap {
    xkb_keycodes  { include "evdev+aliases(qwerty)" };
    xkb_types     { include "complete"      };
    xkb_compat    { include "complete"      };
    xkb_symbols   {
         include "pc+br(abnt2)+inet(evdev)"
         key <LatA> { [ a, A, aacute, Aacute ] };
         key <AE02> { [ 3, numbersign, threesuperior ] };
         key <CAPS> { [ Control_L ] };
         modifier_map Control { <CAPS> };
    };
    xkb_geometry  { include "pc(pc105)"     };
};

O trecho em negrito são os comandos que adicionamos; o resto foi gerado pelo comando setxkbmap -print -option "" br abnt2. Note que não vai ponto-e-vírgula após o include.

O principal comando da seção xkb_symbols é o key. A sintaxe básica é:

key <nome-da-tecla> { [ level1, level2, level3, level4 ] };

Onde <nome-da-tecla> é o nome simbólico da tecla definido na seção xkb_keycodes (uma maneira "prática" de descobrir os nomes das teclas é olhar a seção xkb_symbols do keymap atual com o comando xkbcomp :0 - | less), e level1~level4 são (basicamente) os símbolos emitidos pela tecla pura, Shift+tecla, AltGr+tecla e AltGr+Shift+tecla. Você pode especificar menos que quatro níveis, se desejar.

O comando modifier_map associa teclas a modificadores, e tem basicamente a mesma função do comando add no xmodmap, com a diferença de que no XKB ele funciona direito. A sintaxe é:

modifier_map ModifierName { <nome-da-tecla-1>, <nome-da-tecla-2>, ... };

Onde ModifierName é algo como Control, Shift, Lock, Mod1, etc. (i.e., os mesmos modifiers do xmodmap), e <nome-da-tecla-1> é um nome simbólico de tecla, como <CAPS> ou <LCTL> (left Control) ou <RALT> (right Alt).

É possível especificar mais de um comando modifier_map com o mesmo modificador (o que é ótimo, pois o arquivo em que estamos dando include contém também suas próprias definições de modifier_map). Aparentemente não é possível desassociar um modificador de uma tecla (i.e., aparentemente não existe algo equiparável aos comandos remove e clear do xmodmap), mas o XKB parece ser esperto o suficiente para limpar uma tecla automaticamente quando ela é redefinida pelo comando key (i.e., não é necessário fazer os malabarismos de clear e add que tínhamos que fazer com o xmodmap para remapear Control e CapsLock).

Você pode salvar esse keymap e executar xkbcomp arquivo :0 para pô-lo em vigor.

Simples, hã? Se tudo o que você queria era fazer com o XKB o que você fazia com o xmodmap, você já tem toda a informação de que precisa. (Exceto o comando pointer do xmodmap, cujo equivalente no XKB, se houver, eu não conheço.)

Levels e Groups

Se você fizer um dump do keymap atual com xkbcomp :0 -, verá que a sintaxe que ele usa para o comando key é um pouco mais verbosa:

key <AC01> {
    type= "FOUR_LEVEL_ALPHABETIC",
    symbols[Group1]= [ a, A, aacute, Aacute ]
};

Comecemos pelo fim. A sintaxe symbols[Group1] = [ ... ] indica que os quatro símbolos estão sendo associados aos quatro níveis do grupo Group1 da tecla. Basicamente, o nível de uma tecla é afetado pelas teclas Shift, AltGr e CapsLock. É por isso que, em versões modernas do X, a tecla AltGr é associada ao símbolo ISO_Level3_Shift, como discutido no post sobre o xmodmap.

Um grupo, por sua vez, é uma maneira de atribuir diversos conjuntos de símbolos para uma mesma tecla. Por exemplo, se você costuma escrever em russo, você pode ter um layout ABNT-2 no grupo Group1 e um layout russo no grupo Group2, e atribuir uma tecla para alternar entre os grupos. A vantagem de se usar, por exemplo, dois grupos de quatro níveis ao invés de um grupo de oito níveis, é que cada grupo pode ser definido em um arquivo separado. Se você usar uma diretiva do tipo:

include "pc+br(abnt2)+us(intl):2"

você está instruindo o xkbcomp a incluir o layout br(abnt2) como o primeiro grupo e o layout us(intl) como o segundo*. Você pode então definir uma tecla para alternar entre os dois grupos: o símbolo Mode_switch alterna para o próximo grupo enquanto a tecla é pressionada, e ISO_Next_Group, ISO_Prev_Group e outros alternam permanentemente o grupo atual (como se fosse um CapsLock para grupos).

Ao invés de usar a sintaxe symbols[Group1] = [ ... ], symbols[Group2] = [ ... ], você pode simplesmente especificar mais de um grupo envolvido em [ ... ] dentro do comando key:

key <AC01> { [ a, A, aacute, Aacute ], [ ae, AE, aring, Aring ] };

Agora voltemos ao começo. type= "FOUR_LEVEL_ALPHABETIC" diz que o tipo da tecla A é FOUR_LEVEL_ALPHABETIC. Se nós olharmos o conteúdo da seção xkb_types do dump do xkbcomp, encontraremos a seguinte definição:

type "FOUR_LEVEL_ALPHABETIC" {
    modifiers= Shift+Lock+LevelThree;
    map[Shift]= Level2;
    map[Lock]= Level2;
    map[LevelThree]= Level3;
    map[Shift+LevelThree]= Level4;
    map[Lock+LevelThree]= Level4;
    map[Shift+Lock+LevelThree]= Level3;
    level_name[Level1]= "Base";
    level_name[Level2]= "Shift";
    level_name[Level3]= "Alt Base";
    level_name[Level4]= "Shift Alt";
};

O que essa definição nos diz é que uma tecla do tipo FOUR_LEVEL_ALPHABETIC é afetada pelos modificadores Shift, Lock e LevelThree (i.e., AltGr), e que diferentes combinações desses modificadores ativam diferentes níveis da tecla. As teclas de pontuação, por outro lado, são do tipo FOUR_LEVEL. A definição de FOUR_LEVEL é similar à de FOUR_LEVEL_ALPHABETIC, mas não inclui o Lock entre os modificadores que afetam a tecla. As teclas do keypad numérico, por sua vez, são do tipo FOUR_LEVEL_KEYPAD, que é análogo ao FOUR_LEVEL_ALPHABETIC, mas usa o modificador NumLock ao invés de Lock para escolher os níveis superiores. Há diversos outros tipos de teclas, definidos de maneira similar.

End of Game

Por hoje ficamos por aqui. O XKB possui inúmeras outras features, sobre as quais talvez eu venha escrever no futuro. Para mais informações, consulte os links já referidos, e dê uma olhada na saída do xkbcomp :0 -.

____

* Três estrelinhas para o primeiro que achar essa informação documentada em algum lugar; eu descobri olhando o /usr/share/X11/xkb/rules/xorg, chutando o significado do :n e testando. By the way, aparentemente simplesmente não existe documentação oficial, que dirá uma especificação, da sintaxe desses arquivos. Para mais informações, consulte o código-fonte da libxkbfile. (E não, a gramática não é especificada por um arquivo yacc da vida. Vire-se no parser escrito à mão em C, se sua paciência for capaz.)

Comentários / Comments (2)

Marcus Aurelius, 2015-12-01 16:39:49 -0200 #

Finalmente começando a fazer uso deste repositório de conhecimento sobre o XKB. Eu sabia que um dia seria útil; nem pesquisei na internet, vim direto para cá.


Vítor De Araújo, 2015-12-02 10:11:12 -0200 #

Bom saber que esse post vai servir para alguém além de mim mesmo. :P

Se descobrir alguma coisa interessante sobre o XKB nessas aventuras, posta aí (ou no teu blog). :)


Deixe um comentário / Leave a comment

Main menu

Recent posts

Recent comments

Tags

em-portugues (213) comp (148) prog (71) in-english (62) life (49) unix (38) pldesign (37) lang (32) random (28) about (28) mind (26) lisp (25) fenius (22) mundane (22) web (20) ramble (18) img (13) rant (12) hel (12) scheme (10) privacy (10) freedom (8) esperanto (7) music (7) lash (7) bash (7) academia (7) copyright (7) home (6) mestrado (6) shell (6) android (5) conlang (5) misc (5) emacs (5) latex (4) editor (4) etymology (4) php (4) worldly (4) book (4) politics (4) network (3) c (3) tour-de-scheme (3) security (3) kbd (3) film (3) wrong (3) cook (2) treta (2) poem (2) physics (2) x11 (2) audio (2) comic (2) lows (2) llvm (2) wm (2) philosophy (2) perl (1) wayland (1) ai (1) german (1) en-esperanto (1) golang (1) translation (1) kindle (1) pointless (1) old-chinese (1)

Elsewhere

Quod vide


Copyright © 2010-2024 Vítor De Araújo
O conteúdo deste blog, a menos que de outra forma especificado, pode ser utilizado segundo os termos da licença Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International.

Powered by Blognir.