当用于鼠标按钮和弦时,防止 xbindkeys 覆盖鼠标单击

当用于鼠标按钮和弦时,防止 xbindkeys 覆盖鼠标单击

我正在尝试设置一些鼠标按钮和弦,灵感来自 Acme 文本编辑器:http://acme.cat-v.org/mouse

在 Windows 和 Mac 上,我能够分别使用 AutoHotKey 和 Hammerspoon 进行以下设置:

Left+Middle   = Cut   (Ctrl+x)
Left+Right    = Paste (Ctrl+v)
Middle+Left   = Return
Middle+Right  = Space
Right+Left    = Undo  (Ctrl+z)
Right+Middle  = Redo  (Ctrl+Y / Ctrl+Shift+z)

Middle+Scroll = Switch window

我的主计算机从 Windows 迁移到 Linux 后,我尝试在 Linux 上进行相同的设置。我正在使用 X11 和 XFCE 运行 OpenSUSE Tumbleweed。

Left+Middle = Ctrl+x为简单起见,我将在以下代码示例中仅关注 cut ( )。


使用xev我发现当鼠标左键按下时,state变量设置为0x100

所以 cut 的一个简单配置是:

"xdotool key ctrl+x"
    m:0x100 + b:2

这不起作用,我收到了臭名昭著的冲突程序警告:

*** Warning ***
Please verify that there is not another program running
which captures one of the keys captured by xbindkeys.
It seems that there is a conflict, and xbindkeys can't
grab all the keys defined in its configuration file.

m:0x4 + b:2确实有效(0x4 是 Ctrl),所以我不确定问题是什么。

有办法让它发挥作用吗?


四处搜索,我发现了一些鼠标和弦的设置,我能够适应我的需要。不过,这些需要 xbindkeys 中的 guile 支持。

.xbindkeysrc.scm

(define actionperformed 0)

(define (first-binding)
"First binding"
  (xbindkey-function '("b:1") b1-second-binding) ;; Left Mouse Button
)

(define (reset-first-binding)
"Reset first binding"
  (ungrab-all-keys)
  (remove-all-keys)
  (set! actionperformed 0)
  (first-binding)
  (grab-all-keys)
)


(define (b1-second-binding)
"Left Mouse Button"
  (remove-all-keys)
  (ungrab-all-keys)

  (run-command "xdotool mousedown 1 &") ;; <--- THIS DOES NOT WORK! 

  ;; Left+Middle
  (xbindkey-function '("b:2")
    (lambda ()
      ;; Copy
      (run-command "xdotool key ctrl+x&")
      (run-command "xdotool mouseup 1&")
      (set! actionperformed 1)
    )
  )

  ;; Release left
  (xbindkey-function '(release "b:1") 
    (lambda ()
      (unless actionperformed (begin
        (run-command "xdotool mouseup 1&"))) 
      (reset-first-binding)
    )
  )
  (grab-all-keys)
)

(first-binding)

这确实使 Left+Middle 执行剪切操作。

问题是左键单击被覆盖,因此我无法进行简单的左键单击或文本选择。

我添加了xdotool mousedown 1修复这个问题,但它不起作用。

xdotool当同一鼠标事件处于活动状态时,似乎无法触发鼠标事件。

有什么办法可以防止 xbindkeys 覆盖按钮的正常操作吗?


我还有另一个带有按钮 9 的鼠标,使用它我至少可以设置一些东西。

由于所有按钮和弦必须以同一个按钮开始,所以我只能真正获得 3 个和弦(+ 滚动),这只是我想要的一半。

按下按钮也很尴尬,我不能认为这是一个永久的解决方案:

(define (first-binding)
  (xbindkey-function '("b:9") second-binding)
)

(define (second-binding)
  (remove-all-keys)
  (ungrab-all-keys)

  (xbindkey-function '("b:2") (lambda () (run-command "xdotool key ctrl+x")))

  (xbindkey-function '(Release "b:9") reset-first-binding)
)

(define (reset-first-binding)
  (ungrab-all-keys)
  (remove-all-keys)
  (first-binding)
  (grab-all-keys)
)

(first-binding)

关于解决这个问题的方法有什么想法吗?

答案1

我终于解决了它,但解决方法有点复杂(无论如何与Windows和Mac版本相比)。

这个想法是用来xinput重新映射 USB 鼠标的鼠标按钮,导致按钮不起作用,但保留鼠标移动。

然后监听鼠标按钮事件并evtest触发检测到的按钮组合的鼠标和弦命令,或者在未检测到组合时触发正常的按钮事件。

  1. 安装xinputevtest
  2. 运行evtest并记下你的 USB 鼠标(类似/dev/input/event3
  3. 运行xinput -list并记下设备 ID
  4. 运行xinput set-button-map {id} 0 0 0 0 0 0 0 0 0 0(10 个零)
  5. 跑步evtest /dev/input/event3 | ./mouse-chording.pl

内容mouse-chording.pl

#!/usr/bin/perl -wlnF/\s|,/

BEGIN
{
    our %btns         = (272 => 0, 273 => 0, 274 => 0);
    our $active_chord = 0;

    # mouse button codes
    our $left_code   = 272;
    our $right_code  = 273;
    our $middle_code = 274;
    our $scroll_code = 8;
}

# only handle events
next unless  0 == index $_, 'Event';
next unless -1 == index $_, 'SYN';

# extract event values
my ($time, $type, $code, $value) = @F[2, 5, 9, 13];

# skip mouse movements and hi-res scroll
next if $code == 11;
next if $type == 2 && $code != 8;

# keep track of pressed buttons
$btns{$code} = $value;

# emulate button presses
if ( ($code == $left_code   && !($btns{$right_code} || $btns{$middle_code}))
  || ($code == $right_code  && !($btns{$left_code}  || $btns{$middle_code}))
  || ($code == $middle_code && !($btns{$left_code}  || $btns{$right_code})) )
{
    if ($code == $left_code)
    {
        # left button can be held for selecting text
        system $value ? "xdotool mousedown 1&" : "xdotool mouseup 1&";
    }
    elsif($value == 0 && not $active_chord)
    {
        # middle and right will only trigger click - cannot be held
        system "xdotool click " . ($code == $middle_code ? 2 : 3) . "&";
    }

    $active_chord = 0;
    next;
}
# emulate scroll wheel
elsif ($code == 8)
{
    system( sprintf("xdotool click %d&", $value > 0 ? 4 : 5) );
}

# only do chords when button is pressed (not released)
next unless $value;

# Left + Middle = Cut
if ($btns{$left_code} && $code == $middle_code)
{
    system "xdotool mouseup 1&"; # prevent accidental selection after snarf
    system "xdotool key ctrl+c key ctrl+x&";
    $active_chord = 1;
    next;
}

# Left + Right = Paste
if ($btns{$left_code} && $code == $right_code)
{
    system "xdotool key ctrl+v&";
    $active_chord = 1;
    next;
}

END
{
    # release buttons to prevent getting stuck when closing
    system "xdotool mouseup $_&" for (1..3);
}

该解决方案与 github 上的 windows 和 mac 版本一起维护:https://github.com/levigutt/mouse-chording/

欢迎 PR! :)

相关内容