Elmord's Magic Valley

Software, lingüística e rock'n'roll. Às vezes em Português, sometimes in English.

Múltiplos keymaps no X

2013-02-09 03:50 -0200. Tags: comp, unix, mundane, kbd, em-portugues

(O script desenvolvido neste post pode ser encontrado aqui.)

Em um episódio anterior deste blog, vimos como usar o xmodmap para alterar o keymap do X. Também vimos que embora o xmodmap seja muito mais simples de usar do que o XKB, ele está semi-obsoleto e não lida muito bem com certos recursos mais modernos do XKB. Recentemente eu descobri mais um: o xmodmap não funciona direito com múltiplos teclados (e.g., um teclado builtin de notebook e um teclado externo). Especificamente:

  1. O xmodmap altera os layouts de todos os teclados conectados; não é possível atribuir um layout US para um teclado e um ABNT-2 para outro, por exemplo;
  2. O xmodmap altera apenas os layouts dos teclados conectados; se você ligar um novo teclado, ele assumirá o layout default, e você terá que rodar o xmodmap novamente para afetá-lo.

Sendo assim, temos dois problemas a resolver: detectar quando um teclado é conectado (o que independe de usarmos xmodmap ou XKB), e atribuir um layout a ele (o que exige o XKB se quisermos usar um layout específico para cada teclado; se quisermos apenas propagar o layout corrente para o novo teclado, podemos usar o xmodmap).

Detectando o teclado

A maneira mais palatável que eu encontrei de detectar o teclado* é monitorando o arquivo de log do X (/var/log/Xorg.n.log, onde n é o número do display). As linhas relevantes do arquivo têm a forma:

[ 10848.216] (II) XINPUT: Adding extended input device "GASIA GASIA USB KB Pro" (type: KEYBOARD)

Podemos fazer um script que monitora esse arquivo com o comando tail -f, filtra apenas os dados que nos interessam, e fica em um laço aguardando que novas linhas sejam escritas no arquivo:

#!/bin/bash

[[ $DISPLAY == :* ]] || echo "$0: Oops, remote display?" >&2
display="${DISPLAY##*:}"
display="${display%%.*}"

logfile="/var/log/Xorg.$display.log"

tail -n +1 -f "$logfile" |
    sed -une 's/.*XINPUT: Adding extended input device "\([^"]*\)" (type: \([^)]*\)).*/\2\t\1/p' |
    while IFS=$'\t' read type name; do

        echo "Dispositivo detectado: $name"
        # comandos...

    done

Salve esse script em algum local apropriado (e.g., ~/bin/xinput-monitor) e dê-lhe permissão de execução (chmod +x ~/bin/xinput-monitor). Você pode executá-lo como está para testar se os dispositivos estão sendo realmente detectados (experimente conectar um teclado externo com o script rodando).

A opção -n +1 faz com que o tail imprima o conteúdo do arquivo desde a primeira linha antes de começar a monitorá-lo; isso faz com que o script "detecte" mesmo os dispositivos que já estavam conectados antes de ele rodar. Se você não desejar esse comportamento, pode trocar o -n +1 por -n 0.

Se tudo o que você quer é rodar o xmodmap (ou qualquer outro comando) quando um dispositivo é conectado, basta colocar o comando no trecho indicado no while. Feito isso, basta pôr o script para rodar na inicialização de seu ambiente gráfico (colocando uma linha do tipo ~/bin/xinput-monitor & no seu ~/.xinitrc ou ~/.xsession, ou em algum arquivo de configuração do seu ambiente gráfico favorito).

Note que nem todos os dispositivos listados são teclados (nem mesmo todos os dispositivos com type: KEYBOARD são teclados); se você não quiser executar o xmodmap uma vez para cada um desses dispositivos (o que em tese é inofensivo, mas nunca se sabe), você pode usar um if ou case para ignorar os dispositivos que não lhe interessam. Por exemplo:

    ...
    while IFS=$'\t' read type name; do
        echo "Dispositivo detectado: $name"

        case "$name" in
            'Power Button'|'Video Bus'|'Integrated Webcam'|*'Synaptics'*)
                # Não faz nada.
                ;;
            *)
                # Os comandos que desejamos executar quando um teclado é conectado.
                xmodmap ~/.xmodmaprc
                xset r rate 250
                xkbset exp =m
                ;;
        esac
    done

Setando um layout específico para cada teclado

Para atribuir um layout individual para cada teclado conectado, teremos que abandonar o bom[citation needed] e velho xmodmap e entrar no maravilhoso[dubious – discuss] mundo do XKB. Felizmente, usar um layout pronto com o XKB é bem simples: basta executar um comando como:

# Layout ABNT-2.
setxkbmap -device device_id br abnt2

# Layout US internacional (com acentos).
setxkbmap -device device_id us intl

A opção -device device_id indica qual teclado o comando deve afetar. Se ela não for especificada, o comando afeta todos os teclados.

O grande problema é determinar o device_id de cada teclado; ele pode variar dependendo dos dispositivos presentes e da ordem em que o X os encontra. Aparentemente o id não aparece no arquivo de log do X. A solução é usar o xinput (que vem no pacote xinput no Debian, Ubuntu e companhia), um comando que permite ver e modificar diversas configurações de dispositivos de entrada no X (tais como os dez mil parâmetros que controlam velocidade, aceleração, gestures e outras firulas de touchpads), entre outras coisas. O comando xinput list produz uma listagem dos dispositivos presentes e respectivos ids, com a seguinte cara:

# xinput list
⎡ Virtual core pointer                          \tid=2\t[master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                \tid=4\t[slave  pointer  (2)]
⎜   ↳ SynPS/2 Synaptics TouchPad                \tid=13\t[slave  pointer  (2)]
⎜   ↳ GASIA GASIA USB KB Pro                    \tid=10\t[slave  pointer  (2)]
⎣ Virtual core keyboard                         \tid=3\t[master keyboard (2)]
    ↳ Virtual core XTEST keyboard               \tid=5\t[slave  keyboard (3)]
    ↳ Power Button                              \tid=6\t[slave  keyboard (3)]
    ↳ Video Bus                                 \tid=7\t[slave  keyboard (3)]
    ↳ Power Button                              \tid=8\t[slave  keyboard (3)]
    ↳ Integrated Webcam                         \tid=11\t[slave  keyboard (3)]
    ↳ AT Translated Set 2 keyboard              \tid=12\t[slave  keyboard (3)]
    ↳ GASIA GASIA USB KB Pro                    \tid=9\t[slave  keyboard (3)]

(Os \t representam TABs.)

Um problema visível nessa saída é que pode haver mais de um dispositivo com o mesmo nome (temos dois Power Button e dois GASIA GASIA USB KB Pro (sendo que um deles é um mouse (!))). Como chamar o setxkbmap sobre um não-teclado parece não ter efeito algum, podemos simplesmente tentar os dois ids e nos darmos por satisfeitos.

O primeiro passo é obter os ids a partir dos nomes. Para isso, podemos recorrer ao sed:

        device_ids="$(xinput list | sed -n "s|.*↳ $name *\tid=\([^\t]*\)\t.*|\1|p")"

Com os ids à mão, basta executar o setxkbmap para cada id, usando os parâmetros apropriados dependendo do nome do dispositivo detectado (altere as cláusulas do case para "configurar" o script para os seus teclados e layouts):

        for id in $device_ids; do
            case "$name" in
                'AT Translated Set 2 keyboard') setxkbmap -device "$id" us intl ;;
                'Some weird keyboard')          setxkbmap -device "$id" is ;;
                *)                              setxkbmap -device "$id" br abnt2 ;;
            esac
        done

'Tis over

E era isso. Eis uma versão completa do script. Ficou faltando resolver o problema de portar um xmodmaprc personalizado (caso você tenha um) para o XKB, mas isso fica para um próximo post.

(Este foi o 100-ésimo post do blog, por sinal.)

____

* Outra maneira freqüentemente citada pelo povo da Internet é alterar as configurações do udev para rodar um script quando o teclado é conectado. Além de exigir direitos de root, o script vai ter que descobrir o nome que o X atribui ao teclado por vodu, vai rodar como root (a menos que você tome a precaução de fazer o script trocar para o usuário "dono" da sessão X, o que implica descobrir quem é o dono), e você vai ter que tomar precauções para o caso de haver mais de um display X rodando (por exemplo, se você estiver usando a função "trocar de usuário" de certos ambientes gráficos). Just say no.

Comentários / Comments (0)

Deixe um comentário / Leave a comment

Main menu

Posts recentes

Comentários recentes

Tags

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

Elsewhere

Quod vide


Copyright © 2010-2020 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.