Script de bash que altera automaticamente os layouts de mouse e teclado com base na janela ativa

0

UPDATE - Script totalmente funcional, localizado na parte inferior da minha resposta para quem não está interessado no processo de chegar lá.

Eu tenho tentado escrever um script bash que faz uso de xbindkeys, xkb e xinput set-button-map para alterar automaticamente os layouts do meu teclado Razer Tartarus e do mouse Logitech G502 Proteus com base no ativo atual janela. Eu escolhi fazer isso com o script sendo executado constantemente em segundo plano em um loop infinito que verifica se a janela ativa é diferente da última verificação. Já vi outros sugerirem que o seu .xbindkeysrc execute um script diferente para cada combinação de teclas / chaves que verifique a janela ativa antes de decidir qual comando enviar, mas com um mouse de 13 botões e um teclado de 21 botões, o número de scripts necessários rapidamente sairia do controle, especialmente quando eu começar a adicionar combinações.

autoProfileSwitch:

#!/bin/bash

Last=""

proteus_id=$(
    xinput list | 
    sed -n 's/.*G502.*id=\([0-9]*\).*pointer.*//p'
)
[ "$proteus_id" ] || exit

tartarus_id=$(
    xinput list |
    sed -n 's/.*Tartarus.*id=\([0-9]*\).*keyboard.*//p'
)
[ "$tartarus_id" ] || exit

tartarus_profile="default"
proteus_profile="1 2 3 4 5 6 7 8 9 10 11 12 13"
xbindkeys_profile=".xbindkeysrc"

while true; do
  Class='xprop -id \'xprop -root |nawk '/_NET_ACTIVE_WINDOW/ {print $5;       exit;}'\' |nawk -F = '/WM_CLASS/ {N=split($2, A, ", ");  gsub(/\"/,"",A[2]); print A[2]; exit;}''

  if [ "$Class" != "$Last" ]
  then

    case $Class in
        "Dwarf_Fortress")   
            tartarus_profile="dwarfFortress"
            proteus_profile="1 3 2 4 5 6 7 8 9 10 11 12 13"
            xbindkeys_profile="dwarfFortress";;

        "Firefox")          
            tartarus_profile="default"
            proteus_profile="1 2 3 4 5 6 7 8 9 10 11 12 13"
            xbindkeys_profile=".xbindkeysrc";;

        "")                 
            tartarus_profile="default"
            proteus_profile="1 2 3 4 5 6 7 8 9 10 11 12 13"
            xbindkeys_profile=".xbindkeysrc";;

        *)                  
            tartarus_profile="default"
            proteus_profile="1 2 3 4 5 6 7 8 9 10 11 12 13"
            xbindkeys_profile=".xbindkeysrc";;
    esac

    if pgrep -x "xbindkeys" > /dev/null
    then
        killall xbindkeys
    fi

    xbindkeys -f $HOME/xbindkeys\ profiles/$xbindkeys_profile

    tartarusProfile -p $tartarus_profile
    #setxkbmap -device $tartarus_id -print | 
        #sed 's/\(xkb_symbols.*\)"/+tartarus('$tartarus_profile')"/' | 
        #xkbcomp -I$HOME/xbindkeys\ profiles/xkb -i $tartarus_id -synch -$DISPLAY 2>/dev/null

    for i in $proteus_id
        do
            xinput set-button-map $i $proteus_profile
        done

    Last="$Class"
  fi

done

Eu tentei mover o conteúdo do meu script tártaroPerfil para o script autoProfileSwitch (o motivo da variável não usada tártaro_id e as linhas comentadas diretamente sob a chamada para tártaroPerfil), mas continuei recebendo um "sed não podia flush stdout: Broken pipe "erro por algum motivo. O código funciona bem quando em seu próprio script

tartarusProfile:

#!/bin/bash

# Set profile variable to argument (or default if none)

PROFILE="default"

while getopts p: option; do
    case "$option" in
        p) PROFILE=$OPTARG;;
    esac
done

# Get xinput device id for Razer Tartarus

tartarus_id=$(
    xinput list |
    sed -n 's/.*Tartarus.*id=\([0-9]*\).*keyboard.*//p'
)
[ "$tartarus_id" ] || exit

# Remap Razer Tartarus to selected profile

setxkbmap -device $tartarus_id -print | 
 sed 's/\(xkb_symbols.*\)"/+tartarus('$PROFILE')"/' | 
 xkbcomp -I$HOME/xbindkeys\ profiles/xkb -i $tartarus_id -synch - $DISPLAY 2>/dev/null

Esses scripts funcionam principalmente como pretendido, mas há algumas coisas estranhas acontecendo que eu não consigo isolar. Para começar: as chamadas xkb só parecem acontecer se uma janela de terminal estiver aberta e não minimizada (ou em mais uma instância que descreverei mais adiante), mesmo quando eu executar o script com "autoProfileSwitch &"; xbindkeys e xinput são chamados independentemente de se uma janela de terminal está aberta no entanto.

Outra questão é que a tecla "Tab" está ocasionalmente ligada ao botão direito do mouse (além da troca pretendida de MMB e RMB) ao mudar para os perfis do Dwarf Fortress, apesar de não haver nada no perfil xbindkeys para causar isso. .

Finalmente: mesmo que os xbindkeys não exijam que a janela do terminal seja aberta, algo estranho está acontecendo quando eu vou da Dwarf Fortress para outra janela quando ela está fechada. No momento, meu padrão é ter o botão G7 do meu mouse ligado à tecla "f" para permitir uma tela cheia em vídeos, mas quando eu alternar do Anão para outra janela, o primeiro clique do G7 dá um "s" (nada em xbindkeys isso deve estar causando isso), e o segundo clique dá o esperado "f". Depois que o G7 se torna minha chave "f", o xkb é chamado e meu tártaro também muda para o padrão. Isso tende a impedir que o xkb retorne ao perfil do Dwarf Fortress quando apropriado, mesmo quando a janela do terminal estiver aberta.

Qualquer ajuda seria muito apreciada e eu posso fornecer mais informações, se necessário.

    
por DreadPirateLynx 12.09.2017 / 19:39

1 resposta

0

Após mais testes, reduzi o problema a esta seção do código:

setxkbmap -device $tartarus_id -print | 
    sed 's/\(xkb_symbols.*\)"/+tartarus('$tartarus_profile')"/' | 
    xkbcomp -I$HOME/xbindkeys\ profiles/xkb -i $tartarus_id -synch - $DISPLAY 2>/dev/null

Meu problema anterior com o erro de cano quebrado foi devido a um erro de digitação em que acidentalmente eu deletei o espaço entre - e $ DISPLAY, então este código agora funciona dentro do script principal. O problema é que esta seção de código está temporariamente estragando minha configuração de xbindkeys, e o novo layout de teclado só entra em vigor depois que o xbindkeys se endireita. O Xbindkeys só consegue endireitar-se depois que eu clico no botão do mouse que ele se recuperou, mas sempre há algo errado com esse primeiro clique. Eu sei que este é o código que está causando os problemas porque eu comentei todo o resto, e o problema ainda persiste mesmo quando o perfil do xkb é a única coisa que está sendo alterada.

UPDATE 1: Embora eu estivesse fazendo tudo que estava ao meu alcance para evitá-lo, acabei cedendo e aproveitei para descobrir como adicionar layouts ao xkb lendo esta resposta de tal forma que agora posso chamar

setxkbmap -device $tartarus_id -layout tartarus -variant $tartarus_profile

Isso resolveu um dos meus dois problemas restantes: Meu Tartarus é revinculado com êxito assim que a janela ativa é alterada. Meu mouse ainda está se comportando de forma bizarra.

É estranho. Todo o problema do "botão G7 enviando 'o primeiro clique" parece ter parado, mas o "Tab" ainda está se mapeando no meu botão RMB e G9 para o primeiro clique no Dwarf Fortress. Eu não notei nenhuma outra irregularidade, mas ainda não adicionei um monte de perfis para ver como isso continua. O que quer que esteja acontecendo, parece estar relacionado a botões do meu mouse que estão sendo configurados para enviar teclas que são as mesmas que foram alteradas no Tártaro.

De qualquer forma, o script está funcionando bem o suficiente para que eu esteja realmente disposto a usá-lo. Se é apenas o primeiro clique de qualquer botão do mouse em particular que está errado (e não todos os botões ), é algo com o qual eu posso viver; se meu problema com o mouse for consertado no futuro, isso é apenas um bônus.

UPDATE 2: Enquanto problematizava o estranho comportamento do meu mouse, reorganizei a ordem dos comandos para que os xbindkeys não estivessem sendo executados enquanto o setxkbmap estivesse fazendo as alterações. Eu também tentei mudar de xte para xdotool quando enviando chaves e removendo "+ Release" dos botões problemáticos em minhas configurações de xbindkeys. O comportamento tornou-se menos frequente, mas ainda acontece ocasionalmente.

Em uma nota não relacionada, usar xprop para obter a classe de janela ativa estava me dando alguns problemas com vídeo em tela cheia, então mudei para usar xdotool para obter o nome da janela ativa, que honestamente é muito mais legível e parece estar funcionando perfeitamente.

UPDATE 3: Movido a verificação de IDs de mouse e teclado no loop principal e usado se foram ou não encontrados para determinar se devem ou não alterar seus perfis. Como resultado, o script agora pode manipular mouses e teclados conectados / desconectados durante a execução.

UPDATE 4: Descobriu-se que o estranho comportamento dos xbindkeys não foi realmente corrigido. Longa história curta: é um problema com xte e xdotool pirando quando vários teclados com diferentes layouts estão conectados. Encontrou uma solução alternativa para qualquer um que tenha esse problema: adicionar chave xdotool Cancelar & & para o início de cada linha de macro que chama xdotool (suponho que isso funcione para xte também, mas eu não tentei). Por exemplo:

"xdotool key Return"
b:10

torna-se

"xdotool key Cancel && xdotool key Return"
b:10

Isso faz com que a primeira chamada de xdotool (que geralmente está errada) envie uma chave "morta", enquanto a segunda chamada fornece o que você realmente queria.

Este é o script autoProfileSwitch atualizado para quem quiser dar uma chance aos seus próprios combos de mouse e teclado. Faça-me um favor e faça uma votação positiva se achar este script útil:

#!/bin/bash

# Set Mouse and Keypad Names
# Edit These To Uniquely Identify Your Mouse and Keyboard in Xinput Output
# Leading and Trailing Wildcard Characters Not Necessary For Partial Names
mouse_name="YourMouseNameHere"
keypad_name="YourKeypadNameHere"

# Location of Xbindkeys Configuration Files
xbindkeys_dir="$HOME/xbindkeys profiles"

# Set Initialization Profiles
# keypad_layout Is an XKB Symbols File
# keypad_profile Is an XKB Symbols Definition within keypad_layout
# mouse_profile Is an xinput set-button-map Button Map String
# macro_profile Is an Xbindkeys Configuration File
keypad_layout="tartarus"
keypad_profile="default"
mouse_profile="1 2 3 4 5 6 7 8 9 10 11 12 13"
macro_profile=".xbindkeysrc"

# Keep Track of Last Time Active Window Changed
Last=""

# Main Loop
while true; do

  # Get Mouse and Keypad Xinput IDs
  mouse_id=$(
    xinput list | 
    sed -n 's/.*'$mouse_name'.*id=\([0-9]*\).*pointer.*//p'
  )

  keypad_id=$(
    xinput list |
    sed -n 's/.*'$keypad_name'.*id=\([0-9]*\).*keyboard.*//p'
  )

  # Get Name of Active Window
  Name="$(xdotool getwindowfocus getwindowname)"

  # Execute if Currently Active Window is Different from the Last Time It Changed
  if [ "$Name" != "$Last" ]
  then

    # Set Profiles Based on Name of Currently Active Window
    case $Name in
        Dwarf\ Fortress)   
            keypad_profile="dwarfFortress"
            mouse_profile="1 3 2 4 5 6 7 8 9 10 11 12 13"
            macro_profile="dwarfFortress";;

        *Firefox)          
            keypad_profile="blankSlate"
            mouse_profile="1 2 3 4 5 6 7 8 9 10 11 12 13"
            macro_profile="firefox";;

        *)                  
            keypad_profile="default"
            mouse_profile="1 2 3 4 5 6 7 8 9 10 11 12 13"
            macro_profile=".xbindkeysrc";;
    esac

    # Kill Xbindkeys
    if pgrep -x "xbindkeys" 1>/dev/null
    then
        killall xbindkeys
    fi  

    # Change Keypad Keymap to Appropriate Profile
    # Layout Is the Name of Your XKB Symbols File
    # Variant Is the Name of an xkb_symbols Section of the Layout File
    # Save Layout to /usr/share/X11/xkb/symbols/ 
    # Modify /usr/share/X11/xkb/rules/evdev .../evdev.xml and .../evdev.lst to Include Your Layout
    # Run "sudo dpkg-reconfigure xkb-data" After Any Changes to xkb/ Directory    
    # See https://askubuntu.com/a/483026 For More Info
    [ ! "$keypad_id" ] || setxkbmap -device $keypad_id -layout $keypad_layout -variant $keypad_profile

    # Restart Xbindkeys Using Appropriate Profile
    xbindkeys -f "$xbindkeys_dir"/$macro_profile

    # Set Mouse Profile
    # For When Your Device Appears More Than Once in Xinput
    [ ! "$mouse_id" ] || for i in $mouse_id; do xinput set-button-map $i $mouse_profile; done

    # This Is the Last Time The Active Window Changed
    Last="$Name"
  fi

# Short Sleep to Minimize CPU Usage
sleep .5

done
    
por 15.11.2017 / 19:13