Elmord's Magic Valley

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

Diversão com keymaps no X

2012-08-20 04:35 -0300. Tags: comp, unix, mundane, kbd, em-portugues

Em que o poeta compartilha seus nem tão vastos conhecimentos sobre alteração de keymaps no X. Em um post futuro, pretendo falar sobre as modificações que fiz em meu keymap e que achei úteis.

Introdução ao caos

Keymaps no X funcionam da seguinte maneira: A cada tecla está associado um keycode, um número único para cada tecla. Ao pressionar (ou soltar) uma tecla, o X envia o keycode ao cliente que tem o foco do teclado. O cliente, então, pode fazer o que bem entender com esse keycode. Na maior parte dos casos, entretanto, o cliente utiliza-se da keymap table que o servidor X armazena para converter um keycode em um keysym, que dá o significado da tecla em questão. Por exemplo, ao pressionar a tecla Enter, o X repassa para o cliente o keycode 36 (em um teclado comum de PC), que normalmente está associado a um keysym Return na tabela de keymap. Você pode alterar a keymap table usando um programinha chamado xmodmap. Alternativamente, você pode usar alguma outra interface para realizar essa tarefa, como o programa gráfico XKeyCaps (que altera a keymap table em tempo real e gera um arquivo de configuração para o xmodmap).

A cada keycode está associado mais de um keysym; o keysym apropriado depende dos modificadores que estão ativos no momento em que a tecla é pressionada. Em geral, as teclas Shift e AltGr estão associadas a modificadores que influenciam na seleção do keysym (e.g., em um teclado ABNT2, o keycode 10 está associado aos keysyms 1, exclam (Shift+1) e onesuperior (AltGr+1)). [O Caps_Lock também controla um modificador, que não é o mesmo do Shift; mais informação adiante.]

Insatisfeitos com os poderes do xmodmap, o povo do X inventou o Xkb, um mecanismo mais flexível para a especificação de keymaps. Em tese, a criação do Xkb torna o xmodmap obsoleto, mas como disse um sábio:

In principle, Xkb is supposed to take over. In practice, there are only three people in the known universe who understand Xkb, and nobody is quite sure who they are.

O xmodmap, embora apresente o ocasional bug, é bem mais simples de usar do que o Xkb. (Os relatos de experiências com a migração do xmodmap para o Xkb que eu vejo por aí não são muito animadores.) Ainda não estudei o Xkb direito para poder dar instruções; talvez eu poste algo sobre ele no futuro. Para mais informações, vide links. Neste post, focarei no xmodmap.

Fundamentos de xmodmap

Usualmente, o xmodmap lê de um arquivo uma série de comandos, um por linha, que indicam as alterações que se deseja realizar no keymap. O tipo de comando mais simples é da forma:

keycode número = keysym1 keysym2 ...

Esse comando associa os keysyms ao keycode especificado. Até 8 keysyms podem ser especificados em um comando keycode. O primeiro keysym é usado quando a tecla é pressionada sozinha. O segundo é usado quando a tecla é pressionada enquanto o Shift está ativo. O terceiro é usado quando Mode_switch está ativo (já chegamos lá); o quarto com Mode_switch+Shift; o quinto com ISO_Level3_Shift; o sexto com ISO_Level3_Shift+Shift.

Agora, que diabos são Mode_switch e ISO_Level3_Shift?

No começo dos tempos, Mode_switch correspondia ao AltGr. Os quatro primeiros keysyms correspondiam à tecla pura, Shift, AltGr e AltGr+Shift. E o mundo era feliz.

Por algum diabo, em algum ponto da história o AltGr passou a ser mapeado como ISO_Level3_Shift na maior parte dos keymaps padrão do X. A conseqüência mais visível disso é que o terceiro e quarto keysym da tabela do xmodmap não fazem nada no keymap padrão. Conseqüentemente, se você pretende atribuir um keysym diferente para AltGr+alguma-coisa, você deve preencher as colunas 3 e 4 com algum valor tapa-buraco (e.g., NoSymbol, ou uma cópia das colunas 5 e 6, ou qualquer coisa).

Se o problema fosse só esse, seria apenas um pequeno inconveniente. O problema é que o tal ISO_Level3_Shift é uma invenção do Xkb que não interage muito bem com o xmodmap. Se uma tecla não possui um valor associado a ISO_Level3_Shift+tecla no keymap padrão, não é possível associar um valor a essa combinação via xmodmap; o X simplesmente ignorará o valor das colunas 5 e 6. Por exemplo, se você está usando um keymap us-intl (US Internacional) com AltGr mapeado para ISO_Level3_Shift, você conseguirá alterar o keysym da combinação AltGr+a, pois essa combinação existe no keymap original, mas você não conseguirá atribuir efetivamente um keysym a AltGr+j, pois essa combinação não possui mapeamento no us-intl. Da mesma forma, você não conseguirá mapear AltGr-↑.

A solução mais simples para esse problema é simplesmente remapear o AltGr para Mode_switch e ser feliz para sempre. O problema é que nesse caso você perde todos os mapeamentos com AltGr que possam existir originalmente no seu keymap padrão, pois eles estão associados ao ISO_Level3_Shift. Por exemplo, se você remapear o AltGr para Mode_switch em um layout ABNT2, você perderá o mapeamento padrão de AltGr+1 para ¹ e terá que remapeá-lo manualmente (ou com um script; vejamos mais adiante) se desejá-lo.

Evidentemente, isso só é um problema se você deseja usar o AltGr com alguma tecla não prevista no layout original.

A solução de remapear o AltGr não resolve o problema se você deseja ter tanto o Mode_switch quanto o ISO_Level3_Shift no mesmo teclado (permitindo associar seis keysyms por tecla). Nesse caso, você vai ter que arranjar um keymap do Xkb que mapeie todas as combinações desejadas com ISO_Level3_Shift. O keymap br(abnt2) parece servir bem para esse propósito. Em último caso, você pode experimentar alterar os arquivos /usr/share/X11/xkb/symbols/us e companhia e adicionar o que falta (mas nesse caso não teria mais por que usar o xmodmap, em tese (exceto pelo fato de que é mais prático alterar o teclado posteriormente via xmodmap do que alterando os arquivos de sistema)).

Quanto às colunas 7 e 8, elas aparentemente não servem para nada. Dizem as más línguas que elas deveriam conter o keysym associado à combinação Mode_switch+ISO_Level3_Shift+Shift+tecla, mas nunca consegui fazer funcionar. Tentar criar um modificador ISO_Level4_Shift ou ISO_Level5_Shift também não foi eficaz. (Por sinal, o keysym ISO_Level4_Shift não existe, mas ISO_Level5_Shift sim. Go figure.)

E como eu descubro os keycodes e keysyms?

O X vem com um programinha supimpa chamado xev. Esse programa cria uma janela e reporta pela stdout (o terminal por onde você abriu o xev) todos os eventos que essa janela recebe. Assim, ao pressionar uma tecla, o xev imprimirá algo do tipo:

KeyPress event, serial 33, synthetic NO, window 0x2200001,
root 0x69, subw 0x0, time 39264530, (38,371), root:(1137,799),
state 0x0, keycode 98 (keysym 0xff52, Up), same_screen YES,
XLookupString gives 0 bytes: 
XmbLookupString gives 0 bytes: 
XFilterEvent returns: False

Com isso, você consegue descobrir o keycode da tecla (para poder atribuir keysyms) e o keysym que ela gera atualmente (para poder atribuí-lo a outra tecla).

Se você deseja atribuir a uma tecla um keysym que ainda não existe no seu keymap e que você não sabe o nome (e.g., se você quiser atribuir o caractere ĉ à combinação Mode_switch+c), você pode:

  1. usar Uxxxx, onde xxxx é o código Unicode hexadecimal do caractere desejado (no caso de o símbolo desejado ser um caractere, e não um XF86AudioStop da vida); ou
  2. olhar uma tabela de keysyms, tal como a tabela incompleta em /usr/share/X11/XKeysymDB (que normalmente acompanha o X) ou a tabela completa em /usr/include/X11/keysymdef.h (descartando o prefixo XK_; esse arquivo vem no pacote x11proto-core-dev no Debian); ou
  3. adivinhar o nome do símbolo. Nomes típicos incluem:
    • dead_acute, dead_diaresis, dead_circumflex, dead_tilde, dead_macron, dead_breve, dead_abovedot, dead_cedilla, dead_caron, etc.: acentos e diacríticos de todos os sabores (uma deadkey é uma tecla que não faz nada até que seja digitado o caractere seguinte);
    • aacute, Aacute, ccedilla, Ccedilla, etc.: caracteres acentuados prontos;
    • XF86AudioMute, XF86AudioLowerVolume, XF86AudioRaiseVolume, XF86AudioPlay, XF86AudioStop, XF86AudioPrev, XF86AudioNext, XF86PowerOff, etc.: teclas "multimídia" e firulas relacionadas;

O comando keysym

Em alguns casos você não precisa saber o keycode de uma tecla para mapeá-la. O xmodmap reconhece o comando:

keysym keysym_velho = keysym1 keysym2 ...

Nesse caso, o xmodmap procura por todos keycodes que atualmente estão associados ao keysym_velho, e lhes atribui os valores novos. Isso é particularmente útil quando se deseja apenas associar valores novos às combinações com AltGr, sem alterar o valor puro da tecla:

! O primeiro par ccedilla/Ccedilla para o Mode_switch, o segundo para o ISO_Level3_Shift.
keysym c = c C ccedilla Ccedilla ccedilla Ccedilla

Para modificações que alteram o valor puro da tecla, entretanto, usar o comando keysym pode não ser uma boa idéia. Suponha que você deseja trocar os parênteses e os colchetes de lugar no teclado:

keysym 9 = 9 bracketleft
keysym 0 = 0 bracketright
keysym bracketleft = parenleft braceleft
keysym bracketright = parenright braceright

A aplicação dessas modificações no keymap original funciona (o xmodmap é esperto o suficiente para primeiro avaliar todos os lados esquerdos do =, e depois efetuar as mudanças, de modo que a alteração das duas primeiras linhas não interfere no funcionamento das duas últimas numa mesma execução). Porém, se esse arquivo de keymap for carregado uma segunda vez, catástrofes acontecem: as duas primeiras linhas fazem o que deveriam, mas as duas últimas associam parênteses a todas as teclas que atualmente estão mapeadas para colchetes, ou seja, as teclas 9 e 0! Como os comandos são avaliados na ordem, as duas últimas linhas sobrepõem o mapeamento de 9 e 0, de modo que tanto 9 e 0 quanto as teclas de colchetes agora contêm parênteses!

Evidentemente, o problema só surge se você carregar o arquivo duas vezes. Porém, enquanto você está experimentando com keymaps, você provavelmente vai querer fazer alterações e recarregar o arquivo de keymap diversas vezes sem ter que reiniciar o X. Em qualquer caso, parece melhor escrever um keymap que faz a coisa certa quando é carregado múltiplas vezes do que encontrar surpresas no futuro e levar mil instantes para entender o que está acontecendo.

Moral da história: só use o keysym para alterar valores secundários das teclas, e isso só se você tiver certeza de que keysym_velho só ocorre uma vez no keymap, ou que você queira realmente alterar todas as teclas associadas ao keysym_velho.

Modifiers

Remapear modificadores (Ctrl, Alt, Shift, Caps_Lock, JanelinhaFeliz, etc.) envolve alguns detalhes mais.

O X mantém uma tabela de modificadores; se você executar xmodmap sem parâmetros, ele imprimirá essa tabela. Essa tabela associa keycodes a modificadores. Os modificadores reconhecidos são control, shift (auto-explicativos), lock (correspondente ao Caps_Lock), e mod1 até mod5 (que não têm significado pré-definido). Sempre que você altera o mapeamento de uma tecla associada a modificadores, você deve lembrar de reajustar a tabela de modificadores também. Para controlar essa tabela, o xmodmap provê os comandos add, remove e clear.

add modifier_name = keysym ... adiciona as teclas associadas aos keysyms especificados à lista de teclas que ativam o modificador. Se você executar um comando do tipo add shift = a, o efeito será que a tecla A, além de funcionar como um A normal, também passará a funcionar como um Shift: se você segurar a tecla A enquanto digita outras teclas, o efeito será o mesmo de segurar Shift ao mesmo tempo que as outras teclas.

remove modifier_name = keysym ... desassocia todas as teclas associadas aos keysyms especificados do modificador em questão. clear modifier_name desassocia todas as teclas do modificador. O fato de que os comandos trabalham com keysyms mas a tabela trabalha com keycodes gera todo tipo de bizarrice na manipulação de keymaps. No geral, o mais simples, prático e indolor é, sempre que se remapeia teclas associadas a modificadores, limpar os modificadores em questão, fazer o remapeamento e adicionar as teclas novamente.

Exemplo: para inverter a posição usual do Ctrl da esquerda e do Caps_Lock:

clear lock
clear control
! Keycodes do Ctrl e Caps_Lock obtidos com o xev. Os valores podem ser diferentes no
! seu teclado / versão do X.
keycode 66 = Control_L
keycode 37 = Caps_Lock
add lock = Caps_Lock
! Não esqueça de readicionar o Ctrl da direita, que foi apagado no clear.
add control = Control_L Control_R

Opções de linha de comando do xmodmap

Além do xmodmap arquivo, para carregar um arquivo com comandos, o xmodmap suporta algumas outras opções. Em primeiro lugar, usando - como arquivo, o xmodmap lê da stdin. Isso é primariamente útil se por alguma razão você quiser gerar um keymap por script e alimentá-lo ao xmodmap. Além disso, o xmodmap suporta uma opção -e comando, que permite executar um comando de keymap diretamente a partir da linha de comando. Por exemplo:

xmodmap -e 'keysym a = a A ae AE'

Cada comando consiste de apenas um comando de keymap, mas a opção -e pode ser repetida.

Outro comando útil é xmodmap -pke, que imprime o keymap atual no formato que o xmodmap lê. Assim, você pode salvar o keymap atual com xmodmap -pke >.xmodmaprc e carregá-lo posteriormente (em outra sessão do X) com xmodmap .xmodmaprc. Isso é útil se você fez diversas modificações no layout pela linha de comando e deseja salvar o estado atual sem ter que encontrar todos os comandos dados e agrupá-los em um arquivo. (O xmodmap -pke não imprime linhas add, remove e clear, entretanto.) Isso também é útil para salvar o keymap atual antes de começar a bagunçá-lo, para poder retornar a um estado consistente sem ter que reiniciar o X. Para isso, é bom criar um meio de invocar o xmodmap seu-teclado-original sem ter que usar o teclado (e.g., com um atalho no seu ambiente gráfico favorito).

Carregando o keymap na inicialização

Todas as modificações feitas na keymap table duram apenas até o servidor X morrer; na próxima inicialização, a tabela volta a ser o que era. Para tornar as modificações permanentes, você precisa carregá-las toda vez que sua sessão X inicia. Para isso, você deverá colocar o comando xmodmap ~/seu-arquivo-com-comandos-de-keymap em algum lugar que seja executado pelo seu ambiente gráfico na inicialização. Se você usa o xinit para carregar a interface gráfica, o arquivo é o .xinitrc, no seu home. Alguns ambientes gráficos carregam o .xsessionrc. Alguns permitem que você adicione comandos para serem executados na inicialização em alguma configuração do ambiente. Finalmente, se não estou enganado, o GNOME detecta por conta arquivos de nome .xmodmaprc e assemelhados na inicialização e pergunta se você deseja carregá-los na inicialização da primeira vez que os encontra.

Notas soltas

Na transição do driver de teclado kbd para evdev (i.e., da época do Debian 4 para o Debian 5, embora ainda seja possível usar o driver kbd hoje em dia, desde que seja instalado o pacote xserver-xorg-input-kbd), algumas teclas mudaram de keycode. Em particular, o AltGr mudou de 113 para 108, e as setas e algumas outras teclas especiais também mudaram. Isso pode causar algum problema na hora de migrar de uma versão velha do X para uma mais recente.

Eu prometi links com informações sobre o Xkb. Eis o que eu encontrei (e ainda não li, mostly):

EOF.

Comentários / Comments (3)

pj, 2012-08-30 20:30:57 -0300 #

Vitor, realmente vc é o cara!!! eu optei pelo awesome para usar com o debian e estava procurando uma documentação+softwares sobre configuração de teclados para hotkyes do meu net, e achei massa essa sua idéia de fazer um blog na sua home no public_html! há tempos tb que quero fazer um blog, eu cheguei até a criar um no softwarelivre.org, mas dessisti... eu acho que vc te imitar... =-P

Parabéns cara!!!


Vítor De Araújo, 2012-08-31 12:47:08 -0300 #

Obrigado! :D


Bruno Wagner, 2016-11-11 21:04:33 -0200 #

Muito obrigado, aprendi muito aqui.


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.