如何在bash中获取MouseMove和MouseClick?

32

我想知道如何在bash脚本中获取鼠标点击和鼠标移动事件,以实现自己的简单操作系统事件。

请告诉我如何获取这些事件。


还需要停止在终端中跟踪鼠标点击... - mmike
1
@mmike echo -e "\e[?1000l" - NVRM
7个回答

51

xterm终端模拟器定义了一些控制序列来进行鼠标跟踪,您可以在xterm发行版的ctlseqs文档的鼠标跟踪部分中了解更多信息。如果您已经安装了xterm,您可能会在/usr/share/doc/xterm/ctlseqs.txt.gz或类似路径上找到副本。

大多数在X Window系统上运行的终端模拟器(例如:Konsole、gnome-terminal、eterm等)至少理解其中一些控制序列。如果您想在Linux的虚拟终端之一上直接使用它们,您可能需要运行gpm(8)

有几个控制序列可用于启用和禁用鼠标移动报告:

  • 9 -> X10鼠标报告,与X10的xterm兼容,报告按下按钮。
  • 1000 -> X11鼠标报告,报告按下和释放按钮。
  • 1001 -> 高亮报告,用于报告鼠标高亮。
  • 1002 -> 按钮移动报告,在按下按钮时报告移动。
  • 1003 -> 所有移动报告,报告所有移动。

控制序列为CSI ? number h用于启用,CSI ? number l用于禁用。CSI可以是ESC [或字符0x9b。因此,您可以按以下方式使用它们:

echo -e "\e[?1000h"

然后,按下按钮会得到一堆字符,请参阅ctlseqsconsole_codes(4)了解详情。然后,您可以使用以下命令禁用鼠标跟踪:

echo -e "\e[?1000l"

很遗憾,之前的鼠标报告模式只能处理最多223个坐标(255-32),或在某些情况下为95个坐标(127-32)。因此,有一些新的开关可以改变报告鼠标坐标的格式:

  • 1006 -> 以十进制值报告回来(xterm、许多其他终端仿真器,但不包括urxvt)
  • 1015 -> 以十进制值报告回来(urxvt、xterm、其他终端仿真器,一些应用程序发现它难以解析)
  • 1005 -> 编码为utf-8报告回来(xterm、urxvt、存在多种问题)

一种好的策略是启用鼠标报告,然后(可选地请求urxvt 1015模式,然后)请求SGR 1006模式。应用程序应该处理新的和旧的鼠标报告响应,以继续在不支持新模式的终端仿真器上工作。

有关新报告模式的更多信息,请参见:


2
很好的回答,多年后关于扩展的编辑也很棒。非常感谢! :) - egmont
3
“echo -e "\e[?1000l"” 直接达到我停止在终端跟踪鼠标坐标所需的效果!谢谢。 - mmike
2
你没有说明如何捕获和解析事件,这是工作的50%。 - Jackt

21

根据这里提供的宝贵信息,再进行一些挖掘后,我们可以捕获鼠标按下和释放、滚轮移动和侧面滚动、中键单击(滚轮单击)以及位置。没有右键。

以下只是一个用于 cli 的 php 示例。它在终端上隐藏了鼠标移动并在退出时正确地设置回来。

它足够冗长,可以适应任何能够阅读 STDIN 并打印到 STDOUT 的编程语言,肯定有很多!

#!/usr/bin/php
<?php
system("stty -icanon");                                  // Enable shell input
system("stty -echo");                                    // Disable characters printing
echo "\e[?1003h\e[?1015h\e[?1006h";                      // Mouse trap all, urxvt, SGR1006  

function shutdown(){                                     // Cleaning before quiting
    echo "\e[?1000l";                                    // Disable mouse trap
    system("stty echo");                                 // Enable back characters printing
    exit;                                                // Cleaned, quit
}
register_shutdown_function("shutdown");                  // Handle regular END of script

declare(ticks = 1);                                      // Allow posix signal handling
pcntl_signal(SIGINT,"shutdown");                         // Catch SIGINT (CTRL+C)   

$KEY = "";
while ($KEY = fread(STDIN,16)) {
  $e = explode(";",explode("<",$KEY)[1]);
  if ($e[0] === "0" && substr($e[2],-1) === "M"){
     echo "BUTTON DOWN, LINE ".substr($e[2],0,-1)." COLUMN ".$e[1]."\n"; 
  }
  if ($e[0] === "0" && substr($e[2],-1) === "m"){
     echo "BUTTON UP, LINE ".substr($e[2],0,-1)." COLUMN ".$e[1]."\n"; 
  }
  if ($e[0] === "64"){
     echo "WHEEL SCROLL UP, LINE ".substr($e[2],0,-1)." COLUMN ".$e[1]."\n"; 
  }
  if ($e[0] === "65"){
     echo "WHEEL SCROLL DOWN, LINE ".substr($e[2],0,-1)." COLUMN ".$e[1]."\n"; 
  }
  if ($e[0] === "1" && substr($e[2],-1) === "M"){
     echo "WHEEL BUTTON DOWN, LINE ".substr($e[2],0,-1)." COLUMN ".$e[1]."\n"; 
  }
  if ($e[0] === "1" && substr($e[2],-1) === "m"){
     echo "WHEEL BUTTON UP, LINE ".substr($e[2],0,-1)." COLUMN ".$e[1]."\n"; 
  }
  if ($e[0] === "35"){
     echo "MOUSE MOVE, LINE ".substr($e[2],0,-1)." COLUMN ".$e[1]."\n"; 
  }
}

在此输入图片描述


3
感谢您详细说明。在阅读文档时,我并不清楚按钮实际上是如何编码的。 - Christopher Oezbek

17

简单方法

  1. 启用xterm鼠标跟踪报告
  2. 设置readline绑定以消耗点击生成的转义序列

详细方法

Xterm有一个鼠标跟踪功能

echo -e "\e[?1000;1006;1015h" # Enable tracking
echo -e "\e[?1000;1006;1015l" # Disable tracking
  • 鼠标单击看起来像\e[<0;3;21M,释放则为\e[<0;3;21m。其中3是x坐标,21是y坐标,从左上角计数1开始。(请注意,这是x-y而不是行列)。
  • 向上滚动鼠标滚轮:\e[<64;3;21M
  • 向下滚动鼠标滚轮:\e[<65;3;21M
  • 启用鼠标跟踪后,按Ctrl + v或输入read以查看。

Readline可以触发bash回调。

bind -x '"\e[<64;": mouse_void_cb' # Cannot be put in .inputrc
bind    '"\C-h"   : "$(date) \e\C-e\ef\ef\ef\ef\ef"' #Can be put in .inputrc

Readline可以调用多个函数。

# Mouse cursor to begining-of-line before calling click callback
bind    '"\C-98" : beginning-of-line'
bind -x '"\C-99" : mouse_0_cb'
bind    '"\e[<0;": "\C-98\C-99"'

使用READLINE_POINT环境变量,Readline回调函数可以更改光标(插入点)位置。

bind -x '"\C-h"  : xterm_test'
function xterm_test {
    echo "line is $READLINE_LINE and point $READLINE_POINT"
    READLINE_POINT=24    # The cursor position (0 for begining of command)
    READLINE_LINE='coco' # The command line current content
}

链接


你如何将结果(\e[<0;3;21m)存储到变量中以处理事件? - Tcll

2

默认情况下,Bash 不支持鼠标或鼠标点击事件。也没有光标等类似的东西。

您可以安装“通用鼠标服务器”来解决这个问题。例如,可以查看 http://www.linuxfromscratch.org/blfs/view/6.3/general/gpm.html 来在控制台中使用复制和粘贴功能。也许您可以使用这些工具来处理 Bash 脚本。

总之: Bash shell 没有本地的鼠标支持。


1
我完全不知道它是如何工作的,但当在SSH会话(SSH客户端Ubuntu,服务器Debian)中使用elinks(文本模式浏览器)时,我可以使用滚轮滚动页面并单击链接或按钮。 - Daniel Böhmer
1
我认为/怀疑你正在图形环境/X窗口(KDE,Genome等)中使用终端/xterm。因此,这是终端仿真器的一个特性,而不是bash的特性。我认为它也可以与其他shell一起使用,比如ksh、ash或sh。而且我确定,在没有运行窗口系统的情况下,例如在运行级别3上,它将无法工作。 - The Bndr
听起来很合理。是的,所描述的功能在KDE中可以工作。但是哪种通信方式向elinks提供有关鼠标事件的信息?我预计应该传递一些数据。有人能详细解释这个问题或提供有用的链接吗? - Daniel Böhmer
当然有游标!但是没有鼠标指针。那不叫做游标。 - anon

1

我希望你能理解 GoLang :)
基于这条评论。 <3

package main

import (
  "fmt"
  "strings"
  "strconv"

  "github.com/pandasoli/goterm"
)

func main() {
  termios, _ := goterm.SetRawMode()
  defer goterm.RestoreMode(termios)

  fmt.Print("\033[?1003h\033[?1015h\033[?1006h") // Mouse trap all, urxvt, SGR1006
  fmt.Print("\033[?1002h") // Enable mouse motion reporting

  defer fmt.Print("\033[?1002l") // Disable mouse trap
  defer fmt.Print("\033[?1003l\033[?1015l\033[?1006l") // Restore mouse mode and SGR1006

  for {
    key, _ := goterm.Getch()

    if key == "q" { break }

    if strings.HasPrefix(key, "\033[<") {
      list := strings.Split(key[3:], ";")

      ev := list[0]
      x, _ := strconv.Atoi(list[1])
      y, _ := strconv.Atoi(list[2][:len(list[2]) - 1])
      kind := list[2][len(list[2]) - 1]

      switch ev {
      case "0":
        if kind == 'M' {
          fmt.Printf("(%d, %d) Left mouse down\n", x, y)
        } else if kind == 'm' {
          fmt.Printf("(%d, %d) Left mouse up\n", x, y)
        }
      case "1":
        if kind == 'M' {
          fmt.Printf("(%d, %d) Wheel button down\n", x, y)
        } else if kind == 'm' {
          fmt.Printf("(%d, %d) Wheel button up\n", x, y)
        }
      case "2":
        if kind == 'M' {
          fmt.Printf("(%d, %d) Right mouse down\n", x, y)
        } else if kind == 'm' {
          fmt.Printf("(%d, %d) Right mouse up\n", x, y)
        }
      case "32":
        if kind == 'M' {
          fmt.Printf("(%d, %d) Left mouse pressed move\n", x, y)
        }
      case "34":
        if kind == 'M' {
          fmt.Printf("(%d, %d) Right mouse pressed move\n", x, y)
        }
      case "35":
        fmt.Printf("(%d, %d) Mouse move\n", x, y)
      case "64":
        fmt.Printf("(%d, %d) Wheel scroll up\n", x, y)
      case "65":
        fmt.Printf("(%d, %d) Wheel scroll down\n", x, y)
      default:
        code := strings.ReplaceAll(key, "\033", "\\033")
        panic(fmt.Errorf("Could not understand mouse input: \"%s\".", code))
      }
    } else {
      result := key
      codes := [][]string {
        { "\033", "\\033" },
        { "\b", "\\b" },
        { "\n", "\\n" },
        { "\x7f", "\\x7f" },
      }

      for _, code := range codes {
        result = strings.ReplaceAll(result, code[0], code[1])
      }

      fmt.Printf("Pressed key \"%s\" %v\n", result, []byte(key))
    }
  }
}

0

你可以使用xdotool来模拟鼠标移动和点击事件。 xdotool是一个可以模拟键盘和鼠标的工具。 通过输入以下命令进行安装: sudo apt-get install xdotool。 使用xdotool,你可以自动化几乎所有需要使用键盘和鼠标完成的任务。


0
您可以使用“xautomation”软件包中的“xte”命令。
apt-get install xautomation

作为示例,可以记录以下命令:
xte 'mousemove 200 300'

因此,鼠标指针移动到屏幕宽度200和高度300。另一个例子是:

xte 'mouseclick 3'

点击鼠标的右键(1:左键,2:中键,3:右键)。此外,您可以通过shell按键盘上的键:

xte 'keydown Control_L' 'key c' 'keyup Control_L'

这个例子向shell发送ctrl+c。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接