
Monitoring the mouse in a terminal window happens thanks to various ANSI commands. Last week’s Lesson demonstrated how mouse clicks are detected. By issuing another ANSI command, the mouse’s location data is obtained, but doing so carelessly can create a horrid mess.
The ANSI command to report the mouse’s location is \e[?1003h, which I’ve assigned to the mouse_motion() macro. This macro is demonstrated in my original mouse monitoring post. If you ran the code back then, you saw the program barf data all over the screen as you whip around the mouse.
The following code is an update to the original mouse monitoring example. In this code, I borrow the terminal modification routines presented in last week’s Lesson. The main difference in the output is that all mouse activity is reported and output.
2026_02_14-Lesson.c
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#define mouse_enable() printf("\e[?1000h")
#define mouse_extended() printf("\e[?1006h")
#define mouse_motion() printf("\e[?1003h")
#define mouse_disable() printf("\e[?1000l")
int main()
{
struct termios original,noecho;
char buffer[12];
int key;
/* obtain terminal configuration */
tcgetattr(fileno(stdin),&original);
noecho = original; /* duplicate */
/* enable raw input */
noecho.c_lflag = noecho.c_lflag ^ ICANON;
/* disable full duplex */
noecho.c_lflag = noecho.c_lflag ^ ECHO;
/* update terminal definition */
tcsetattr(fileno(stdin), TCSANOW, &noecho);
/* enable mouse reporting */
mouse_enable();
mouse_motion();
mouse_extended();
/* remove line buffering */
setvbuf(stdin,NULL,_IONBF,0);
/* read stdin */
puts("Play with the mouse; press Enter to end");
while( (key=getchar()) != '\n' )
{
read(fileno(stdin),buffer,12);
write(fileno(stdout),buffer,12);
putchar('\n');
}
/* clean-up */
mouse_disable(); /* disable mouse reporting */
tcsetattr(fileno(stdin), TCSANOW, &original);
return 0;
}
This code adds the mouse_motion() macro to the code presented last week. It’s effect is that all mouse movement appears in the output. And moving the mouse while the program runs generates a lot of output, as you can see in the following sample output:
Play with the mouse; press Enter to end [<35;71;19M [<35;70;19M [<35;70;20M [<35;69;20M [<35;68;20M [<35;67;20M [<35;67;21M [<35;66;21M [<35;65;21M [<35;65;20M ...
The first value, 35, indicates mouse movement. The next two values are column and row values. Each time the mouse changes a character position, new data is output.
Clicking the mouse results in output like this:
[<0;37;14MM [<0;37;14mM
The value zero represents the left mouse button. Big M means that the button is pressed and little M means that the button is released. But you see extra garbage in the buffer, leftover characters from the previous call: The big M at the end of the data. This residue is only part of the problem when it comes to capturing this data.
The program reads terminal input directly. The data recorded is saved in a character buffer; it's not a string.
While outputting the mouse data is interesting, it's only useful when the data can be stored. I provide this update in next week's Lesson.