
Spewing data all over the screen might look impressive, as shown in last week’s Lesson, but the point of knowing where the mouse is and what it’s doing is to capture its data and make it available to your program for interpretation.
The goal is to extract relevant values from the mouse input and place it into a useful format. For me, this format is a structure containing the mouse’s location and button status. Something like this:
struct mouse_data {
int button;
int column;
int row;
};
The three members of the mouse_data structure represent the three integer values generated by ANSI mouse input escape sequences: the button press and the row/column values for the mouse’s location. To obtain this data, the ANSI escape sequence string must be captured and interpreted.
In the following code, the read_mouse() function is passed the address of a mouse_data structure and an ANSI escape string. The extract() function pulls the relevant integer data from the string, storing the proper values in the mouse_data structure. Coding this function is the topic for this month’s Exercise.
2026_02_21-Lesson.c
#include <stdio.h>
#include <stdlib.h>
#include <ctype.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")
struct mouse_data {
int button;
int column;
int row;
};
/* read an integer from a string
Exercise 2026_02 */
char *extract(char *s)
{
static char *sp;
/* check for recall */
if( s != NULL )
sp = s;
else
while( isdigit(*sp) )
sp++;
/* find the next digit */
while( *sp != '\0' )
{
if( isdigit(*sp) )
{
return(sp);
}
sp++;
}
return(NULL);
}
/* obtain values from mouse data */
void read_mouse(char *b, struct mouse_data *m)
{
m->button = atoi(extract(b));
m->column = atoi(extract(NULL));
m->row = atoi(extract(NULL));
}
int main()
{
struct termios original,noecho;
struct mouse_data mickey;
char buffer[13];
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);
buffer[12] = '\0'; /* make it a string */
read_mouse(buffer,&mickey);
printf("Button %2d Column %2d Row %2d\n",
mickey.button,
mickey.column,
mickey.row
);
}
/* clean-up */
mouse_disable(); /* disable mouse reporting */
tcsetattr(fileno(stdin), TCSANOW, &original);
return 0;
}
The code starts with the four ANSI mouse macros as described earlier in this series.
Next comes the mouse_data structure definition. This structure must be defined globally as it’s used in several functions in the source code file.
The extract() function is lifted from this month’s Exercise solution.
The read_mouse() function fills a mouse_data structure with the proper values read from standard input. The structure is passed as a pointer (address), so the -> operator is used to fill values in the structure.
Differences between the main() function in this code and what was shown in last week’s Lesson are first found in the buffer definition. It’s one byte larger. The reason is that this buffer must now hold a string for proper examination by the extract() function.
I’ve also modified the while loop: The buffer is capped with a null character to make it a string:
buffer[12] = '\0';
Then the characters captured in buffer are passed to the read_mouse() function, along with the address of a mouse_data structure, mickey. A printf() statement outputs the data.
Here’s a sample run:
Play with the mouse; press Enter to end Button 35 Column 57 Row 16 Button 35 Column 58 Row 16 Button 0 Column 58 Row 16 Button 0 Column 58 Row 16 Button 35 Column 58 Row 16 Button 2 Column 58 Row 16 Button 2 Column 58 Row 16 Button 35 Column 58 Row 16 Button 35 Column 59 Row 16 Button 35 Column 60 Row 16 Button 35 Column 61 Row 17 Button 35 Column 62 Row 17 Button 35 Column 63 Row 17
Button zero is the left mouse button; 2 is the right mouse button. “Button 35” means that the mouse is moving; no button is pressed.
Remember: If mouse input isn’t disabled after the program quits, use the reset command in the terminal window.
For next week’s Lesson, I update the code so that text is output at the location where the mouse button is pressed.