Bash-скрипт, который автоматически меняет раскладки мыши и клавиатуры на основе активного окна

1336
DreadPirateLynx

ОБНОВЛЕНИЕ - Полностью рабочий скрипт, расположенный в нижней части моего ответа для тех, кто не заинтересован в процессе получения там.

Я пытался написать bash-скрипт, который использует xbindkeys, xkb и xinput set-button-map для автоматического изменения раскладок моей клавиатуры Razer Tartarus и мыши Logitech G502 Proteus на основе текущего активного окна. Я решил сделать это, имея скрипт, постоянно работающий в фоновом режиме в бесконечном цикле, который проверяет, отличается ли активное окно от последней проверки. Я видел, как другие предлагали, чтобы ваш .xbindkeysrc запускал свой скрипт для каждой комбинации клавиш / клавиш, который проверяет активное окно, прежде чем решить, какую команду отправлять, но с помощью 13-кнопочной мыши и 21-кнопочной клавиатуры, число необходимых скриптов быстро выйдет из-под контроля, особенно когда я начну добавлять комбинации.

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 

Я пытался переместить содержимое моего скрипта tartarusProfile в скрипт autoProfileSwitch (причина неиспользуемой переменной tartarus_id и закомментированных строк непосредственно под вызовом tartarusProfile), но продолжал получать ошибку «sed not flush stdout: Broken pipe» по какой-то причине. Код работает нормально, когда в своем собственном скрипте

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 

Эти сценарии в основном работают как задумано, но происходят некоторые странные вещи, которые я не могу изолировать. Для начала: кажется, что вызовы xkb происходят только в том случае, если окно терминала открыто и не свернуто (или в еще одном случае, который я опишу позже), даже когда я запускаю скрипт с «autoProfileSwitch &»; xbindkeys и xinput вызываются независимо от того, открыто ли окно терминала.

Другая проблема заключается в том, что клавиша «Tab» иногда привязывается к моей правой кнопке мыши (в дополнение к предполагаемому обмену MMB и RMB) при переключении на мои профили Dwarf Fortress, несмотря на то, что в профиле xbindkeys нет ничего, что могло бы вызвать это.

И наконец: даже если xbindkeys не требует, чтобы окно терминала было открыто, происходит нечто странное, когда я перехожу из Dwarf Fortress в другое окно, когда оно закрыто. Прямо сейчас я по умолчанию привязываю кнопку G7 моей мыши к клавише «f», чтобы обеспечить быстрый полноэкранный режим на видео, но когда я переключаюсь с Dwarf Fortress на другое окно, первый щелчок G7 дает «s» (ничего в xbindkeys это должно быть причиной этого), и второй щелчок дает ожидаемое «е». После того, как G7 становится моей клавишей "f", вызывается xkb, и мой тартар также переключается на значение по умолчанию. Это, как правило, не позволяет xkb переключаться обратно на профиль Dwarf Fortress, даже когда окно терминала открыто.

Любая помощь будет высоко ценится, и я могу предоставить больше информации, если это необходимо.

0
Отредактируйте ответ и опубликуйте его как ответ, потому что это вопрос, а не ответ random 6 лет назад 0

1 ответ на вопрос

0
DreadPirateLynx

После дальнейшего тестирования я сузил проблему до этого раздела кода:

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 

Моя ранняя проблема с ошибкой сломанного канала была связана с опечаткой, в которой я случайно удалил пробел между - и $ DISPLAY, поэтому этот код теперь работает в основном скрипте. Проблема в том, что этот раздел кода временно испортил мою конфигурацию xbindkeys, и новая раскладка клавиатуры вступает в силу только после того, как xbindkeys выпрямляется. Xbindkeys удается выпрямиться только после того, как я нажму кнопку мыши, что у него отскок, но с первым щелчком всегда что-то не так. Я знаю, что это код, который вызывает проблемы, потому что я закомментировал все остальное, и проблема все еще сохраняется, даже когда профиль xkb - единственное, что изменяется.

ОБНОВЛЕНИЕ 1: Несмотря на то, что я делал все, что в моих силах, чтобы избежать этого, я в конечном итоге уступил и нашел время, чтобы выяснить, как добавить макеты в xkb, прочитав этот ответ, так что теперь я могу вызвать

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

Это решило одну из двух моих оставшихся проблем: Мой Tartarus успешно переподключается, как только меняется активное окно. Моя мышь все еще ведет себя странно, хотя.

Это странно. Кажется, что вся проблема «кнопка G7 отправляет первый клик» прекратилась, но «Tab» все еще привязывается к моим кнопкам RMB и G9 для первого щелчка в Dwarf Fortress. Я не заметил каких-либо других нарушений, но я еще не добавил кучу профилей, чтобы посмотреть, как это будет продолжаться. Что бы ни происходило, похоже, что это связано с тем, что кнопки моей мыши настроены на отправку нажатий клавиш, которые совпадают с клавишами, которые были изменены на Тартаре.

В любом случае, скрипт работает достаточно хорошо, и я действительно хочу начать его использовать. Если это только первый щелчок какой-либо конкретной кнопки мыши, который является неправильным (и не каждая кнопка имеет в виду вас), это то, с чем я могу жить; если моя проблема с мышью будет исправлена ​​в будущем, это просто бонус.

ОБНОВЛЕНИЕ 2: устраняя странное поведение моей мыши, я изменил порядок команд, чтобы xbindkeys не работал, пока setxkbmap вносит изменения. Я также пытался переключиться с xte на xdotool при отправке ключей и удалении «+ Release» из проблемных кнопок в моих конфигах xbindkeys. Поведение стало менее частым, но все же иногда происходит.

На несвязанной ноте, использование xprop для получения класса активного окна вызывало у меня некоторые проблемы с полноэкранным видео, поэтому я переключился на использование xdotool для получения имени активного окна, которое, честно говоря, намного более читаемо в любом случае и, кажется, работает отлично.

ОБНОВЛЕНИЕ 3: перенес проверку идентификаторов мыши и клавиатуры в основной цикл и использовал, были ли они найдены, чтобы определить, стоит ли менять свои профили. В результате, скрипт теперь может обрабатывать мыши и клавиатуры, подключенные / отключенные во время работы.

ОБНОВЛЕНИЕ 4: Оказалось, что странное поведение от xbindkeys фактически не было исправлено вообще. Короче говоря: проблема в том, что xte и xdotool сходят с ума, когда подключено несколько клавиатур с разными раскладками. Найден обходной путь для тех, кто сталкивается с этой проблемой: добавьте ключ xdotool Cancel && в начало каждой строки макроса, вызывающей xdotool (я предполагаю, что это работает и для xte, но я не пробовал). Например:

"xdotool key Return" b:10 

становится

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

Это приводит к тому, что первый вызов xdotool (который обычно неверен) отправляет «мертвый» ключ, а второй вызов дает вам то, что вы на самом деле хотели.

Вот обновленный скрипт autoProfileSwitch для тех, кто хочет сделать это для своих собственных комбинаций мыши и клавиатуры. Сделайте мне одолжение и поддержите этот вопрос, если вы найдете этот скрипт полезным:

#!/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 

Похожие вопросы