32

I'm wondering how to get the MouseClick and MouseMove events in bash scripting for my own simple OS events.

Please tell me how to get that events.

2

7 Answers 7

51

The xterm terminal emulator defines some control sequences to do mouse tracking, you can learn more about them in the section Mouse Tracking in the document ctlseqs for the xterm distribution. If you have xterm installed, you'll probably have a copy at /usr/share/doc/xterm/ctlseqs.txt.gz or a similar path.

Most terminal emulators running on the X Window System (e.g: Konsole, gnome-terminal, eterm, ...) understand at least some of these control sequences. If you want to use them directly on one of Linux's virtual terminals, you'll probably have to run gpm(8).

There are several control sequences for enabling and disabling mouse movement reporting:

  • 9 -> X10 mouse reporting, for compatibility with X10's xterm, reports on button press.
  • 1000 -> X11 mouse reporting, reports on button press and release.
  • 1001 -> highlight reporting, useful for reporting mouse highlights.
  • 1002 -> button movement reporting, reports movement when a button is pressed.
  • 1003 -> all movement reporting, reports all movements.

The control sequence is CSI ? number h for enabling and CSI ? number l for disabling. CSI is either ESC [ or character 0x9b. So, you could use them as follows:

echo -e "\e[?1000h"

Then, you'll get a bunch of characters on button press, see ctlseqs or console_codes(4) for details. Then, you can disable mouse tracking with:

echo -e "\e[?1000l"

Unfortunately, the previous mouse reporting modes can only handle coordinates up to 223 (255 - 32), or in some situations 95 (127 - 32). So there are some new switches to change the format in which mouse coordinates are reported:

  • 1006 -> report back as decimal values (xterm, many other terminal emulators, but not urxvt)
  • 1015 -> report back as decimal values (urxvt, xterm, other terminal emulators, some applications find it complex to parse)
  • 1005 -> report back encoded as utf-8 (xterm, urxvt, broken in several ways)

A good strategy for an application would be to enable mouse reporting, then (optionally request urxvt 1015 mode and then) request SGR 1006 mode. The application should handle both the new and legacy mouse reporting responses, to continue working on terminal emulators without support for the new modes.

More information on the new reporting modes at:

3
  • 2
    Nice answer, and a nice edit years later about the extensions. Truly appreciated! :)
    – egmont
    Commented Dec 6, 2016 at 9:33
  • 3
    echo -e "\e[?1000l" - exactly what I need to stop track my mouse coords in terminal! Thanks Commented Feb 22, 2017 at 7:40
  • 2
    you dont say how to capture and parse the events , that is 50% of the job
    – Jackt
    Commented Aug 5, 2020 at 3:48
21

Based on the precious informations given here, and after a bit of digging around.

We can catch mouse downs and releases, the wheel movement and side, the middle click (wheel click), and positions. No right click.

The following is only an example in php, used as cli. It hide the movements printing on the terminal, and set it back properly when quiting.

It is verbose enough to be adapted in any programming langs able to read the STDIN and print to STDOUT, so surely a big list of them!

#!/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"; 
  }
}

enter image description here

1
  • 3
    Thank you for spelling this out. Wasn't obvious for me reading the documentation what the buttons actually are encoded as. Commented May 23, 2021 at 21:57
19

Short way

  1. Enable xterm mouse tracking reporting
  2. Set readline bindings to consume the escape sequence generated by clicks

Long way

Xterm have a mouse tracking feature

echo -e "\e[?1000;1006;1015h" # Enable tracking
echo -e "\e[?1000;1006;1015l" # Disable tracking
  • Mouse click looks like \e[<0;3;21M and a release \e[<0;3;21m. Where 3 is x and 21 is y, from top-left 1-based. (Note that it is x-y and not row-col).
  • Mouse whell up : \e[<64;3;21M
  • Mouse whell down : \e[<65;3;21M
  • Press Ctrl + v or enter read after enabling the mouse tracking to see that

Readline can trigger a bash callback

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 can call multiple functions

# 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 callback can change cursor (point) position with READLINE_POINT environment variable

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
}

Links

1
  • How do you store the result (\e[<0;3;21m) in a variable to process the event?
    – Tcll
    Commented May 6, 2023 at 11:51
2

the bash doesn't know anything about an mouse or mouse-clicks. By default there is no cursor or something like that.

You could install the "General Purpose Mouse Server". Look at this: http://www.linuxfromscratch.org/blfs/view/6.3/general/gpm.html for example to use copy and paste inside an console. Maybe you can use the tools to work with your bash - script.

...but at all: there is no native mouse support for the bash-shell

4
  • 1
    I have absolutely no clue how it works but when using elinks (text mode browser) in a SSH session (SSH client Ubuntu, server Debian). I can use the scroll wheel to scroll the page and click on links or buttons. Commented May 11, 2011 at 15:56
  • 1
    I think/suspect you are using an terminal/xterm inside an graphical environment / X-Windows (KDE, Genome,...) So this is an feature from the terminal emulator. It's not an bash feature. I thing it would also work with an other shell, like ksh, ash or sh. And I'm sure, it will not work on (for example) runlevel 3 - without an running window system.
    – The Bndr
    Commented May 11, 2011 at 16:05
  • Sounds reasonable. Yes, the described feature works e.g. in KDE. But which way of communication provides the information about mouse events to elinks? I expected that the shall had to pass some data. Can somebody explain this in detail or provide useful links? Commented May 12, 2011 at 10:21
  • Of course there is a cursor! There isn’t a mouse pointer though. That’s not called a cursor.
    – anon
    Commented Apr 15, 2022 at 13:50
1

I hope you understand GoLang :)
Based on this comment. <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

you can use xdotool for mousemove and mouseclick events. xdotool is a tool which fakes keyboard and mouse. install it by typing, sudo apt-get install xdotool. By using xdotool you can automate almost everything you do using keyboard and mouse.

0

You can use the command 'xte' from package 'xautomation'.

apt-get install xautomation

As an example, the following command can be noted:

xte 'mousemove 200 300'

So, the mouse pointer moves to the width 200 and height 300 of the screen. As an other example, we have:

xte 'mouseclick 3'

that click the right button of mouse (1: left click, 2: middle click, 3: right click). Moreover you can press the keys on keyboard via shell:

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

This example send ctrl+c to shell.

Not the answer you're looking for? Browse other questions tagged or ask your own question.