更新-对于那些对实现该过程不感兴趣的人,完整工作的脚本位于我的答案的底部。
我一直在尝试编写一个使用 xbindkeys、xkb 和 xinput set-button-map 的 bash 脚本,以便根据当前活动窗口自动更改我的 Razer Tartarus 键盘和 Logitech G502 Proteus 鼠标的布局。我选择通过让脚本在后台无限循环中不断运行来实现这一点,该脚本会检查活动窗口是否与上次检查不同。我见过其他人建议让您的 .xbindkeysrc 为每个键/键组合运行不同的脚本,以检查活动窗口,然后再决定发送哪个命令,但是对于 13 键鼠标和 21 键键盘,所需的脚本数量很快就会失控,尤其是当我开始添加组合时。
自动配置文件切换:
#!/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 无法刷新 stdout:管道损坏”错误。代码在自己的脚本中运行良好
tartarus个人资料:
#!/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 都会被调用。
另一个问题是,当切换到我的 Dwarf Fortress 配置文件时,“Tab”键偶尔会绑定到我的鼠标右键(除了预期的 MMB 和 RMB 交换),尽管 xbindkeys 配置文件中没有任何内容导致这种情况。
最后:尽管 xbindkeys 不需要打开终端窗口,但当我从 Dwarf Fortress 转到另一个关闭的窗口时,会发生一些奇怪的事情。现在我的默认设置是将鼠标的 G7 按钮绑定到“f”键,以便快速全屏显示视频,但当我从 Dwarf Fortress 切换到另一个窗口时,第一次单击 G7 会出现“s”(xbindkeys 中没有任何东西应该导致这种情况),第二次单击会出现预期的“f”。在 G7 成为我的“f”键后,xkb 被调用,然后我的 tartarus 也切换到其默认值。这往往会阻止 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 按钮发送‘s’第一次点击”问题似乎已经停止,但“Tab”仍然将自身映射到我的 RMB 和 G9 按钮上,用于 Dwarf Fortress 中的第一次点击。我没有注意到任何其他异常,但我还没有添加一堆配置文件来查看这种情况如何继续发生。无论发生了什么,它似乎与我的鼠标上的按钮配置为发送与 Tartarus 上已更改的键相同的击键有关。
无论如何,这个脚本运行良好,我愿意开始使用它。如果只是第一次点击某个鼠标按钮是错误的(而不是每一个按钮(请注意),这是我可以忍受的;如果我的鼠标问题在将来得到解决,那就只是一个额外的奖励。
更新2:在排除鼠标异常行为时,我重新安排了命令顺序,这样当 setxkbmap 进行更改时 xbindkeys 就不会运行。我还尝试在发送按键时从 xte 切换到 xdotool,并从 xbindkeys 配置中的问题按钮中删除“+ Release”。这种行为已经不那么频繁了,但偶尔还是会发生。
另外,使用 xprop 来获取活动窗口类给我的全屏视频带来了一些问题,所以我改用 xdotool 来获取活动窗口的名称,老实说,这无论如何都更具可读性并且似乎运行良好。
更新 3:将对鼠标和键盘 ID 的检查移至主循环,并根据是否找到它们来确定是否更改其配置文件。因此,脚本现在可以处理运行时连接/断开的鼠标和键盘。
更新 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