regra do udev para carregar automaticamente o layout do teclado quando o teclado USB está conectado

24

Estou tentando carregar um novo layout de teclado quando conecto um teclado USB, mas minha regra do udev não está funcionando.

SUBSISTEMA == "entrada", ATTR {idVendor} == "062a", ATTR {idProduct} == "0201", GOTO = "usb_xmodmap_auto"

LABEL = "usb_xmodmap_auto"
AÇÃO == "adicionar", EXECUTAR + = "/ usr / bin / xmodmap ~ / .usbXmodmap"
AÇÃO == "remover", EXECUTAR + = "/ usr / bin / xmodmap ~ / .pndXmodmap"

Eu recarreguei as regras usando:

> sudo udevadm control --reload-rules

e reiniciando o sistema, mas quando eu conecto o teclado usb, o xmodmap original ainda está carregado e, portanto, o layout do teclado está errado, mas se eu executar o comando no terminal

> / usr / bin / xmodmap ~ / .usbXmodmap
ou
> / usr / bin / xmodmap ~ / .pndXmodmap

eles funcionam muito bem.

espero que alguém possa ajudar.

Editar:

só para ajudar mais, executei alguns testes udevadm:

> udevadm test --action = add /devices/platform/ehci-omap.0/usb1/1-2/1-2.3/1-2.3:1.1/input/input10

saídas:

run_command: chamando: test
udevadm_test: versão 151
Este programa é apenas para depuração, não executa nenhum programa,
especificado por uma chave RUN. Pode mostrar resultados incorretos, porque
alguns valores podem ser diferentes ou não estar disponíveis em uma simulação.

[...]
parse_file: lendo '/etc/udev/rules.d/usb-keyboard.rules' como arquivo de regras
udev_rules_new: as regras usam tokens de 100572 bytes (8381 * 12 bytes), buffer de 21523 bytes
udev_rules_new: índice temporário usado 35380 bytes (1769 * 20 bytes)
udev_device_new_from_syspath: o dispositivo 0x3b4d8 possui devpath '/devices/platform/ehci-omap.0/usb1/1-2/1-2.3/1-2.3:1.1/input/input10'
udev_rules_apply_to_event: EXECUTAR '/ sbin / modprobe -b $ env {MODALIAS}' /etc/udev/rules.d/80-drivers.rules:5
udev_rules_apply_to_event: soquete RUN ': @ / org / freedesktop / hal / udev_event' /etc/udev/rules.d/90-hal.rules:2
udev_rules_apply_to_event: EXECUTAR '/ sbin / modprobe $ env {MODALIAS}' /etc/udev/rules.d/local.rules:31
udev_rules_apply_to_event: RUN 'socket: / org / kernel / udev / monitor' /etc/udev/rules.d/run.rules:2
udev_rules_apply_to_event: EXECUTAR '/ usr / bin / xmodmap ~ / .usbXmodmap' /etc/udev/rules.d/usb-keyboard.rules:4
udevadm_test: UDEV_LOG = 6
udevadm_test: DEVPATH = / devices / platform / ehci-omap.0 / usb1 / 1-2 / 1-2.3 / 1-2.3: 1.1 / input / input10
udevadm_test: PRODUCT = 3 / 62a / 201/110
udevadm_test: NAME = "Teclado compatível com USB"
udevadm_test: PHYS = "usb-ehci-omap.0-2.3 / input1"
udevadm_test: UNIQ = ""
udevadm_test: EV == 1f
udevadm_test: KEY == 837fff 2c3027 bf004444 0 0 1fe3 c04 a27c000 267bfa d941dfed 9e0000 0 0 0
udevadm_test: REL == 143
udevadm_test: ABS == 1 0
udevadm_test: MSC == 10
udevadm_test: MODALIAS = entrada: b0003v062Ap0201e0110-e0,1,2,3,4, k71,72,73,74,77,80,82,83,85,86,87,88,89,8A, 8B, 8C, 8E, 8F, 90,96,98,9B, 9C, 9E, 9F, A1, A3, A4, A5, A6, A7, A8, A9, AB, AC, AD, AE, B1, B2, B5, CE, CF, D0, D1, D2, D5, D9, DB, E2, EA, EB, 100,101,105,106,107,108,109,10A, 10B, 10C, 162,166,16A, 16E, 178,179,17A, 17B, 17C, 17D, 17F, 180,181,182,185,18C, 18D, 192,193,195,1A0,1A1,1A2,1A3,1A4,1A5,1A6,1A7,1A8,1A9,1AA, 1AB, 1AC, 1AD, 1AE, 1B0,1B1,1B7, r0,1,6,8, a20, m4, lsfw
udevadm_test: ACTION = add
udevadm_test: SUBSYSTEM = entrada
udevadm_test: run: '/ sbin / modprobe -b input: b0003v062Ap0201e0110-e0,1,2,3,4, k71,72,73,74,77,80,82,83,85,86,87,88,89 , 8A, 8B, 8C, 8E, 8F, 90,96,98,9B, 9C, 9E, 9F, A1, A3, A4, A5, A6, A7, A8, A9, AB, AC, AD, AE, B1 , B2, B5, CE, CF, D0, D1, D2, D5, D9, DB, E2, EA, EB, 100,101,105,106,107,108,109,10A, 10B, 10C, 162,166,16A, 16E, 178,179,17A, 17B, 17C, 17D , 17F, 180,181,182,185,18C, 18D, 192,193,195,1A0,1A1,1A2,1A3,1A4,1A5,1A6,1A7,1A8,1A9,1AA, 1AB, 1AC, 1AD, 1AE, 1B0,1B1,1B7, r0,1 , 6,8, a20, m4, lsfw »
udevadm_test: run: 'socket: @ / org / freedesktop / hal / udev_event'
udevadm_test: run: '/ sbin / modprobe input: b0003v062Ap0201e0110-e0,1,2,3,4, k71,72,73,74,77,80,82,83,85,86,87,88,8A , 8B, 8C, 8E, 8F, 90,96,98,9B, 9C, 9E, 9F, A1, A3, A4, A5, A6, A7, A8, A9, AB, AC, AD, AE, B1, B2 , B5, CE, CF, D0, D1, D2, D5, D9, DB, E2, EA, EB, 100.101.105.106.107.108.109,10A, 10B, 10C, 162.166,16A, 16E, 178.179,17A, 17B, 17C, 17D, 17F , 180,181,182,185,18C, 18D, 192,193,195,1A0,1A1,1A2,1A3,1A4,1A5,1A6,1A7,1A8,1A9,1AA, 1AB, 1AC, 1AD, 1AE, 1B0,1B1,1B7, r0,1,6 , 8, a20, m4, lsfw '
udevadm_test: run: 'socket: / org / kernel / udev / monitor'
udevadm_test: execute: '/ usr / bin / xmodmap ~ / .usbXmodmap'

e

> udevadm test --action = remove /devices/platform/ehci-omap.0/usb1/1-2/1-2.3/1-2.3:1.1/input/input10

saídas:

run_command: chamando: test
udevadm_test: versão 151
Este programa é apenas para depuração, não executa nenhum programa,
especificado por uma chave RUN. Pode mostrar resultados incorretos, porque
alguns valores podem ser diferentes ou não estar disponíveis em uma simulação.

[...]
parse_file: lendo '/etc/udev/rules.d/usb-keyboard.rules' como arquivo de regras
udev_rules_new: as regras usam tokens de 100572 bytes (8381 * 12 bytes), buffer de 21523 bytes
udev_rules_new: índice temporário usado 35380 bytes (1769 * 20 bytes)
udev_device_new_from_syspath: o dispositivo 0x3b4d8 possui devpath '/devices/platform/ehci-omap.0/usb1/1-2/1-2.3/1-2.3:1.1/input/input10'
udev_rules_apply_to_event: soquete RUN ': @ / org / freedesktop / hal / udev_event' /etc/udev/rules.d/90-hal.rules:2
udev_rules_apply_to_event: RUN 'socket: / org / kernel / udev / monitor' /etc/udev/rules.d/run.rules:2
udev_rules_apply_to_event: EXECUTAR '/ usr / bin / xmodmap ~ / .pndXmodmap' /etc/udev/rules.d/usb-keyboard.rules:5
udevadm_test: UDEV_LOG = 6
udevadm_test: DEVPATH = / devices / platform / ehci-omap.0 / usb1 / 1-2 / 1-2.3 / 1-2.3: 1.1 / input / input10
udevadm_test: PRODUCT = 3 / 62a / 201/110
udevadm_test: NAME = "Teclado compatível com USB"
udevadm_test: PHYS = "usb-ehci-omap.0-2.3 / input1"
udevadm_test: UNIQ = ""
udevadm_test: EV == 1f
udevadm_test: KEY == 837fff 2c3027 bf004444 0 0 1fe3 c04 a27c000 267bfa d941dfed 9e0000 0 0 0
udevadm_test: REL == 143
udevadm_test: ABS == 1 0
udevadm_test: MSC == 10
udevadm_test: MODALIAS = entrada: b0003v062Ap0201e0110-e0,1,2,3,4, k71,72,73,74,77,80,82,83,85,86,87,88,89,8A, 8B, 8C, 8E, 8F, 90,96,98,9B, 9C, 9E, 9F, A1, A3, A4, A5, A6, A7, A8, A9, AB, AC, AD, AE, B1, B2, B5, CE, CF, D0, D1, D2, D5, D9, DB, E2, EA, EB, 100,101,105,106,107,108,109,10A, 10B, 10C, 162,166,16A, 16E, 178,179,17A, 17B, 17C, 17D, 17F, 180,181,182,185,18C, 18D, 192,193,195,1A0,1A1,1A2,1A3,1A4,1A5,1A6,1A7,1A8,1A9,1AA, 1AB, 1AC, 1AD, 1AE, 1B0,1B1,1B7, r0,1,6,8, a20, m4, lsfw
udevadm_test: ACTION = remover
udevadm_test: SUBSYSTEM = entrada
udevadm_test: run: 'socket: @ / org / freedesktop / hal / udev_event'
udevadm_test: run: 'socket: / org / kernel / udev / monitor'
udevadm_test: execute: '/ usr / bin / xmodmap ~ / .pndXmodmap'

o que parece mostrar que deve funcionar, mas não espera que isso ajude a obter uma resposta.

Jake Aitchison
fonte

Respostas:

16

Eu encontrei uma maneira de contornar isso, embora seja um pouco hacky.

Hoje cheguei ao mesmo ponto exato ao tentar configurar dois teclados com udev, setxkbmap e xinput --list e para que eles trabalhem com hotplugging USB. Estou trocando de teclas, não alterando o layout, mas é tudo a mesma coisa, depois de identificar o teclado em um hotplug e poder chamar o setxkbmap de forma condicional, você poderá definir o idioma apenas do teclado especificado . A lista de layouts de teclado pode ser encontrada aqui ls -l /usr/share/kbd/keymaps/i386/e você pode encontrar o nome do seu dispositivo para usar xinput -list.

  1. Você vai querer substituir rizumupelo seu nome de usuário, pois eu achei que não era possível fazer isso sem ser explícito.
  2. Certifique-se de escrever o yournome do teclado.
  3. Use lsusbpara descobrir o ID do hardware que você precisa definir na regra do udev. Meu teclado das se parece com issoBus 002 Device 009: ID 04d9:2013 Holtek Semiconductor, Inc.

Primeiro, configurei a regra do udev para detectar automaticamente o teclado criando uma regra do udev:

No arquivo /etc/udev/rules.d/00-usb-keyboards.rules:

ACTION=="add", ATTRS{idVendor}=="04d9", ATTRS{idProduct}=="2013", RUN+="/home/rizumu/bin/kbd_udev", OWNER="rizumu"

Eu tenho dois arquivos ~ / bin / kbd e ~ / bin / kbd_udev. Verifique se eles têm as permissões corretaschmod 755 ~/bin/kbd*

O ~/bin/kbd_udevscript contém:

#!/bin/bash
/home/rizumu/bin/kbd &

E você notará que tudo o que faz é chamar ~/bin/kbdem segundo plano, para que o udev possa concluir seu processo e ativar o teclado. Dentro do ~/bin/kbdscript, dormimos por um segundo, porque precisamos esperar até que o teclado seja ativado para que possamos obter o ID do dispositivo usando xinput. Para obter este eu definir algumas variáveis e exportado-los para xinput setxkbmap pode fazer thier trabalho: DISPLAY, XAUTHORITY, HOME, e um daskb_idpara o id do meu daskeyboard:

#!/bin/bash
sleep 1
DISPLAY=":0.0"
HOME=/home/rizumu/
XAUTHORITY=$HOME/.Xauthority
export DISPLAY XAUTHORITY HOME
daskb_id=`xinput -list | grep -i 'daskeyboard' | grep -o id=[0-9]. | grep -o [0-9]. | head -1`

xset r rate 200 30
setxkbmap -layout colemak
setxkbmap -option ctrl:nocaps
if [ "${daskb_id}" ]; then
    setxkbmap -device "${daskb_id}" -option altwin:swap_lalt_lwin
fi
Thomas Schreiber
fonte
Muito obrigado por me ajudar a responder minha própria pergunta é AskUbuntu: askubuntu.com/questions/337411/…
Sadi
E eu me pergunto se você também pode me ajudar a adicionar uma mensagem de notificação no final deste script (por exemplo notify-send "USB Keyboard is plugged in and ready for use now." -i gtk-dialog-info -t 1000 -u normal). Como não sei muito sobre scripts, tentei inseri-lo antes ou depois de "fi", mas em ambos os casos a mensagem de notificação continuava aparecendo repetidamente :-(
Sadi
Por que configurar OWNEReste dispositivo?
Limbo Peng
1
O que a xset r rate 200 30linha faz? xsetnão está disponível na minha instalação do Ubuntu 17.04.
21817 kleinfreund
1
Não consigo executar xmodmap $HOME/.Xmodmapusando um script análogo ao seu "/ home / rizumu / bin / kbd". Por que isso seria?
Geremia
5

Dependendo da sua distribuição, você já pode ter uma regra do udev para teclados em /lib/udev/rules.d/64-xorg-xkb.rules. No Ubuntu, isso importa / etc / default / keyboard, que tem opções mais ou menos assim:

XKBMODEL="pc105"
XKBLAYOUT="us"
XKBVARIANT=""
XKBOPTIONS=""

Para minha configuração, descobri que essa regra interna estava sendo executada após minha regra personalizada do udev e estava substituindo minhas configurações. Em vez disso, mudei o XKBOPTIONS em / etc / default / keyboard para:

XKBOPTIONS="-option ctrl:nocaps"

Para obter o comportamento "Caps Lock is Control" que eu queria em todos os teclados.

jsha
fonte
2
Boa ideia! O meu funciona apenas comXBKOPTIONS="ctrl:nocaps"
#
3

Se você estiver executando o GNOME, precisará desativar o plug-in de gerenciamento de teclado para que não substitua as alterações no layout.

gconftool-2 --toggle /apps/gnome_settings_daemon/plugins/keyboard/active

Execute o mesmo comando novamente para ativá-lo conforme desejado.

Ignacio Vazquez-Abrams
fonte
estou correndo angstrom. Isso vai funcionar?
Jake Aitchison 23/02
Você está usando o GNOME no Ångström?
Ignacio Vazquez-Abrams
Estou usando o xfce 4.6.1
Jake Aitchison
1
No meu Ubuntu 13.04, isso está dconfabaixo /org/gnome/settings-daemon/plugins/keyboard/active.
Nh2 16/08
1
E o comando para o Ubuntu 13.04 é:gsettings set org.gnome.settings-daemon.plugins.keyboard active false
Sadi
3

Não funciona porque udeve xmodmapnão tem acesso ao seu monitor X11. Na verdade, udevainda não sabe se há são exibe X11 ativos.

  • Nota: exibe , plural. Ele não pode usar "a" tela X11 porque pode haver mais de um. Por exemplo, se você usar "troca rápida de usuário".
gravidade
fonte
então Como eu poderia fazer isso funcionar?
Jake AITCHISON
Alguém sabe como eu posso consertar isso?
Jake Aitchison
1
Chamei o udev para chamar setxkbmap. A regra do udev chama um script com outro script (para que o udev possa ser concluído). O segundo script faz uma pausa por um segundo, configura as variáveis ​​X11 esperadas e aciona o setxkbmap. Veja minha resposta para a pergunta principal para mais detalhes.
Thomas Schreiber
@rizumu: Ahh, boa sorte para que isso funcione com o GDM.
grawity
3

Eu acho que encontrei uma maneira muito mais limpa de configurar isso, que não requer um hack especial do X11.

A idéia por trás disso é que udevapenas detectará novas entradas do teclado e criará um link simbólico para cada layout, depois inotifyobservará o novo layout no espaço do usuário.

regras do udev

#/etc/udev/rules.d/61-usb-keyboard-layout.rules

# will match my Logitech keyboard with US layout 
SUBSYSTEM=="input", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c31c", GROUP="plugdev", MODE="0660", SYMLINK+="input/by-layout/us"

# will match my Lenovo integrated keyboard with IT layout
SUBSYSTEM=="input", ENV{ID_PATH}=="platform-i8042-serio-0", SYMLINK+="input/by-layout/it"

# force the directory to be recreated, just in case you unplug all input
SUBSYSTEM=="input", RUN="/bin/mkdir -p /dev/input/by-layout"

Com essas regras, eu tenho um diretório em dev ( /dev/input/by-layout) para observar alterações nos scripts do espaço do usuário.

Script de espaço do usuário para o KDE

Por exemplo, ao usar o KDE, tenho este script (automático) em execução:

#!/bin/bash

# In case no link are found, switch to this layout
DEFAULT="it"

switch_layout () {
        [ ! -z "$1" ] || return 0
        /usr/bin/qdbus org.kde.keyboard /Layouts org.kde.KeyboardLayouts.setLayout $1
}

best_layout() {
        local LAYOUT=$(ls -1t /dev/input/by-layout/ | head -n 1)
        if [ -z "$LAYOUT" ] ; then
                LAYOUT=$DEFAULT
        fi
        echo $LAYOUT
}

switch_layout $(best_layout)

while true ; do
        EVENT=$(inotifywait -q -e create -e delete --exclude '.*tmp.*' /dev/input/by-layout/)

        if echo "$EVENT" | grep -qe CREATE ; then
                LAYOUT=${EVENT#?*CREATE }
        fi

        if echo "$EVENT" | grep -qe DELETE ; then
                LAYOUT=$(best_layout)
        fi

        switch_layout $LAYOUT
done

Isso funciona como um encanto para mim. Para alterar o layout do sistema (do qual não preciso no momento), um script semelhante que loadkeyspode ser demonizado usando um script de inicialização do sistema.

giosh94mhz
fonte
Obrigado, isso me fez perceber que posso apenas usar inotifywaitpara executar um script de instalação em qualquer alteração /dev/input, pois o próprio script é idempotente.
Charlie Gorichanaz
3

E a configuração do X.Org? No Gentoo Wiki: X.Org/Input_drivers - udev :

Exemplo: se você possui um teclado Logitech Access para a parte francesa da Suíça, pode usar o seguinte:

Arquivo: /etc/X11/xorg.conf.d/10-keyboard.conf

Section "InputClass"
    Identifier             "evdev keyboard catchall"
    MatchIsKeyboard        "on"
    MatchDevicePath        "/dev/input/event*"
    Driver                 "evdev"
    Option                 "XkbModel" "logiaccess"
    Option                 "XkbLayout" "ch"
    Option                 "XkbVariant" "fr"
EndSection

Para uma explicação detalhada, leia:

man xorg.conf

e:

man evdev

ArchWiki demonstra o uso da mesma sintaxe no xorg.conf, mas observa que "atualmente você deve criar um arquivo de configuração separado, como /etc/X11/xorg.conf.d/90-keyboard-layouts.conf". Eu uso o Arch e configurei meu próprio teclado USB no /etc/X11/xorg.conf.d/vim 10-evdev.conf existente para mim.

@rizumu: Clud Clever, obrigado por compartilhar.

Casey Jones
fonte
1
Eu não tenho um diretório x.org.conf.d no Linux Mint 18.2
Max N
2

Para responder à sua pergunta sobre como acessar a exibição em execução, você pode exportar a variável DISPLAY apropriada no script, assumindo que as permissões para a exibição estejam definidas corretamente. ( man xsetpara permissões de exibição.)

Em muitos casos comuns, você pode simplesmente export DISPLAY=:0usar o comando, pois é a primeira exibição em um sistema de usuário único. Provavelmente é mais fácil executar um script em vez do xmodmap diretamente, pois isso permitirá que você tenha mais controle sobre as variáveis ​​de ambiente e o resto. (Então substitua "/ usr / bin / xmodmap ~ / .usbXmodmap" em sua regra por "/usr/local/bin/keyboard_plug.sh" e coloque os comandos apropriados nesse script junto com a variável DISPLAY.)

Como observado acima, porém, se você assumir DISPLAY =: 0, poderá ter problemas mais tarde se tiver vários usuários ou monitores. Você pode escrever scripts para detectar a exibição apropriada, mas nesse caso você está por conta própria (no que diz respeito a esta resposta). :)

rust_and_moth
fonte
1

Como não consegui que os hacks fizessem funcionar as regras do udev, escrevi um pequeno script Python usando pyudevpara monitorar eventos de entrada.

#! /usr/bin/env python3

import pyudev
import time
import subprocess

ctx = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(ctx)
monitor.filter_by("input")

def defer_xmodmap():
    time.sleep(1) # not sure if there's a race here, but it feels like there could be.
    subprocess.run("xmodmap ~/dotfiles/.xmodmap", shell=True)


for device in iter(monitor.poll, None):
    # there might be a way to add the action condition to the filter, but I couldn't find it
    if device.action != "add":
        continue

    # ensure the KB is initialized -- not sure if this is actually a needed check
    if not device.is_initialized:
        continue

    # my keyboard, from the output of `lsusb`
    if not "045E:07A5" in device.device_path:
        continue

    # it's the keyboard being added.
    defer_xmodmap()

Eu então uso esse arquivo de unidade de usuário systemd para mantê-lo em execução ( systemctl --user enable name_of_service_file):

[Unit]
Description=udev xmodmap monitor

[Service]
ExecStart=/usr/bin/env python3 %h/local/bin/monitor_kb_udev
Restart=always
RestartSec=10

[Install]
WantedBy=default.target

A inotifywaitsolução do @ giosh94mhz é um pouco mais simples e evita a dependência pyudev. No entanto, por algum motivo, achei que o inotifyevento não estava sendo disparado por 10 a 20 segundos depois que meu teclado foi conectado.

Ryan Marcus
fonte