根据活动窗口自动更改鼠标和键盘布局的 Bash 脚本

根据活动窗口自动更改鼠标和键盘布局的 Bash 脚本

更新-对于那些对实现该过程不感兴趣的人,完整工作的脚本位于我的答案的底部。

我一直在尝试编写一个使用 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

相关内容