Bash скрипт, который автоматически меняет раскладки мыши и клавиатуры в зависимости от активного окна
ОБНОВЛЕНИЕ - Полностью рабочий скрипт, расположенный в нижней части моего ответа для тех, кто не заинтересован в процессе получения там.
Я пытался написать 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/ {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.*\)"/\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, даже когда окно терминала открыто.
Любая помощь будет высоко ценится, и я могу предоставить больше информации, если это необходимо.
1 ответ
После дальнейшего тестирования я сузил проблему до следующего раздела кода:
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