Bash-Skript, das Maus- und Tastaturlayouts basierend auf dem aktiven Fenster automatisch ändert

796
DreadPirateLynx

UPDATE - Voll funktionsfähiges Skript, das sich am Ende meiner Antwort befindet, für alle, die sich nicht für den Prozess des Hinwegs interessieren.

Ich habe versucht, ein Bash-Skript zu schreiben, das xbindkeys, xkb und xinput set-button-map verwendet, um die Layouts meiner Razer Tartarus-Tastatur und der Logitech G502 Proteus-Maus basierend auf dem derzeit aktiven Fenster automatisch zu ändern. Ich habe mich dazu entschieden, dies zu erreichen, indem das Skript ständig im Hintergrund in einer Endlosschleife ausgeführt wird, die prüft, ob sich das aktive Fenster von der letzten Prüfung unterscheidet. Ich habe gesehen, wie andere vorgeschlagen haben, dass .xbindkeysrc für jede Tastenkombination ein anderes Skript ausführt, das das aktive Fenster prüft, bevor er entscheidet, welcher Befehl gesendet werden soll, aber mit einer 13-Tasten-Maus und einer 21-Tasten-Tastatur die Nummer Die notwendigen Skripte würden schnell außer Kontrolle geraten, besonders wenn ich anfange, Kombinationen hinzuzufügen.

autoProfileSwitch:

#!/bin/bash  Last=""  proteus_id=$( xinput list |  sed -n 's/.*G502.*id=\([0-9]*\).*pointer.*/\1/p' ) [ "$proteus_id" ] || exit  tartarus_id=$( xinput list | sed -n 's/.*Tartarus.*id=\([0-9]*\).*keyboard.*/\1/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/ '\` |nawk -F = '/WM_CLASS/ '`  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.*\)"/\1+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 

Ich habe versucht, den Inhalt meines Skripts "tartarusProfile" in das Skript "autoProfileSwitch" zu verschieben (der Grund für die nicht verwendete Variable "tartarus_id" und die auskommentierten Zeilen direkt unter dem Aufruf von "tartarusProfile"), erhalte jedoch weiterhin den Fehler "sed konnte stdout: Broken pipe" nicht löschen aus irgendeinem Grund. Der Code funktioniert gut, wenn er sich in einem eigenen Skript befindet

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.*/\1/p' ) [ "$tartarus_id" ] || exit  # Remap Razer Tartarus to selected profile  setxkbmap -device $tartarus_id -print |  sed 's/\(xkb_symbols.*\)"/\1+tartarus('$PROFILE')"/' |  xkbcomp -I$HOME/xbindkeys\ profiles/xkb -i $tartarus_id -synch - $DISPLAY 2>/dev/null 

Diese Skripts funktionieren meistens wie beabsichtigt, aber es gibt einige seltsame Dinge, die ich nicht isolieren kann. Für den Anfang: Die xkb-Aufrufe scheinen nur dann zu passieren, wenn ein Terminalfenster geöffnet und nicht minimiert ist (oder in einer weiteren Instanz, die ich später noch beschreiben werde), selbst wenn ich das Skript mit "autoProfileSwitch &" ausführte. xbindkeys und xinput werden beide aufgerufen, unabhängig davon, ob ein Terminalfenster geöffnet ist.

Ein weiteres Problem ist, dass die "Tab" -Taste gelegentlich an meine rechte Maustaste gebunden ist (zusätzlich zum beabsichtigten Tauschen von MMB und RMB), wenn zu meinen Dwarf Fortress-Profilen gewechselt wird, obwohl es im xbindkeys-Profil nichts zu suchen gibt.

Schließlich: Auch wenn xbindkeys nicht erfordert, dass das Terminalfenster geöffnet ist, passiert etwas Seltsames, wenn ich von der Zwergenfestung zu einem anderen Fenster gehe, wenn es geschlossen wird. Im Moment ist meine Standardeinstellung, dass die G7-Taste meiner Maus an die "f" -Taste gebunden ist, um schnell Vollbild auf Videos zu ermöglichen. Wenn ich jedoch von Dwarf Fortress zu einem anderen Fenster wechsle, gibt der erste Klick von G7 ein "s" (nichts in xbindkeys) das sollte dies verursachen), und der zweite Klick gibt das erwartete "f" aus. Nachdem G7 meine "f" -Taste geworden ist, wird xkb aufgerufen, und mein Tartarus wechselt dann ebenfalls zu seiner Standardeinstellung. Dadurch wird verhindert, dass xkb, selbst wenn das Terminalfenster geöffnet ist, ggf. wieder zum Dwarf Fortress-Profil wechselt.

Jede Hilfe wäre sehr dankbar, und ich kann bei Bedarf weitere Informationen bereitstellen.

0
Bearbeiten Sie die Antwort und veröffentlichen Sie sie als Antwort, da dies eine Frage und keine Antwort ist random vor 7 Jahren 0

1 Antwort auf die Frage

0
DreadPirateLynx

Nach weiteren Tests habe ich das Problem auf diesen Codeabschnitt eingegrenzt:

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

Mein früheres Problem mit dem gebrochenen Pipe-Fehler war auf einen Tippfehler zurückzuführen, bei dem ich das Leerzeichen zwischen - und $ DISPLAY versehentlich gelöscht hatte. Daher funktioniert dieser Code jetzt im Hauptskript. Das Problem ist, dass dieser Codeabschnitt meine xbindkeys-Konfiguration vorübergehend durcheinander bringt und das neue Tastaturlayout erst wirksam wird, wenn sich xbindkeys wieder ausrichtet. Xbindkeys schafft es erst, sich selbst zu glätten, nachdem ich mit der Maustaste auf einen Rebound geklickt habe, aber mit dem ersten Klick ist immer etwas nicht in Ordnung. Ich weiß, dass dies der Code ist, der die Probleme verursacht, weil ich alles andere auskommentiert habe. Das Problem bleibt auch dann bestehen, wenn nur das xkb-Profil geändert wird.

UPDATE 1: Obwohl ich alles in meiner Macht Stehende unternommen hatte, um dies zu vermeiden, gab ich schließlich nach und nahm mir die Zeit, um herauszufinden, wie man Layouts zu xkb hinzufügt, indem ich diese Antwort so lese , dass ich sie jetzt anrufen kann

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

Dies hat eines meiner zwei verbleibenden Probleme gelöst: Mein Tartarus wird erfolgreich neu gebunden, sobald sich das aktive Fenster ändert. Meine Maus verhält sich immer noch bizarr.

Es ist seltsam. Das gesamte Problem mit dem "G7-Button, der den ersten Klick sendet" scheint gestoppt zu sein, aber "Tab" bildet sich beim ersten Klick in der Zwergenfestung immer noch auf meinen RMB- und G9-Button. Ich habe keine anderen Unregelmäßigkeiten bemerkt, aber ich habe noch nicht wirklich eine Reihe von Profilen hinzugefügt, um zu sehen, wie sich das weiter entwickelt. Was auch immer passiert, es scheint zu tun zu haben, dass die Tasten meiner Maus so konfiguriert sind, dass sie Tastenanschläge senden, die den Tasten entsprechen, die sich auf dem Tartarus geändert haben.

In jedem Fall funktioniert das Skript so gut, dass ich bereit bin, es tatsächlich zu verwenden. Wenn es nur der erste Klick einer bestimmten falschen Maustaste ist (und nicht jede Taste, die Sie mögen), kann ich damit leben; Wenn mein Mausproblem in der Zukunft behoben wird, ist das nur ein Bonus.

UPDATE 2: Während ich das seltsame Verhalten meiner Maus ausfindig machte, habe ich die Reihenfolge der Befehle geändert, so dass xbindkeys nicht ausgeführt wird, während setxkbmap Änderungen vornimmt. Ich habe auch versucht, beim Senden von Schlüsseln von xte zu xdotool zu wechseln und "+ Release" von den Problemschaltflächen in meinen xbindkeys-Konfigurationen zu entfernen. Das Verhalten ist seltener geworden, tritt jedoch gelegentlich auf.

Die Verwendung von xprop zum Abrufen der aktiven Fensterklasse brachte mir einige Probleme mit dem Vollbildvideo. Daher wechselte ich zu Xdotool, um den Namen des aktiven Fensters zu erhalten, der ehrlich viel besser lesbar ist und perfekt zu funktionieren scheint.

UPDATE 3: Die Prüfung auf Maus- und Tastenfeld-IDs wurde in die Hauptschleife verschoben und anhand der Frage, ob sie gefunden wurden, festgestellt, ob ihre Profile geändert werden sollen oder nicht. Als Ergebnis kann das Skript nun Mäuse und Tastaturen handhaben, die während des Laufens verbunden / getrennt werden.

UPDATE 4: Es stellte sich heraus, dass das merkwürdige Verhalten von xbindkeys überhaupt nicht behoben wurde. Um es kurz zu machen: Es ist ein Problem, da sowohl xte als auch xdotool ausgeflippt sind, wenn mehrere Tastaturen mit unterschiedlichen Layouts angeschlossen sind. Es wurde eine Problemumgehung für alle gefunden, die dieses Problem haben: Fügen Sie am Anfang jeder Makrozeile, die xdotool aufruft, den xdotool-Schlüssel Cancel && hinzu (ich gehe davon aus, dass dies auch für xte funktioniert, aber ich habe es nicht probiert). Zum Beispiel:

"xdotool key Return" b:10 

wird

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

Dies bewirkt, dass der erste Aufruf von xdotool (der normalerweise falsch ist) eine "tote" Taste sendet, während der zweite Aufruf Ihnen das gibt, was Sie eigentlich wollten.

Hier ist das aktuelle autoProfileSwitch-Skript für alle, die dies für ihre eigenen Maus- und Tastaturkombinationen versuchen möchten. Tun Sie mir einen Gefallen und befürworten Sie diese Frage, wenn Sie dieses Skript für nützlich halten:

#!/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.*/\1/p' )  keypad_id=$( xinput list | sed -n 's/.*'$keypad_name'.*id=\([0-9]*\).*keyboard.*/\1/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