我正在尝试设置一些鼠标按钮和弦,灵感来自 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
触发检测到的按钮组合的鼠标和弦命令,或者在未检测到组合时触发正常的按钮事件。
- 安装
xinput
和evtest
- 运行
evtest
并记下你的 USB 鼠标(类似/dev/input/event3
) - 运行
xinput -list
并记下设备 ID - 运行
xinput set-button-map {id} 0 0 0 0 0 0 0 0 0 0
(10 个零) - 跑步
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! :)