6

I've started to learn about ANSI escape sequences online through the magic of Google. It is neat being able to position the cursor \e[row;colH on the screen and set the colors of the outputs (ie: \e[31m).

Next I would like try and see how the mouse able to be captured in a virtual terminal. I realize this code is not portable, and I know I can use ncurses or some other curses library, but the goal here is to learn how it works, not write production code with it.

I have tried \e[?1003h and it starts to fill the screen with mouse events. (Pretty cool!) However, how do I capture these in a C or C++ program?

I saw an example of what I would like to do in PHP: https://stackoverflow.com/a/58390575/1770034

However, when I try to port the code over to something in C it just locks up in the while loop. (Tested with GDB to find that out.)

#include <stdio.h> //bring in printf and fread

int main()
{
    system("stty -echo"); //make the terminal not output mouse events
    system("stty -icanon"); //put stdin in raw mode
    printf("\e[?1003h\e[?1015h\e[?1006h"); //start getting mouse events
    char* buffer[255];
    while(fread(buffer, 16, 1, stdin)) // <-- suppose to read in the mouse events
    {
        printf("here"); //Did you actually work?!
    }
    printf("\e[?1000l"); //Turn off mouse events
    system("stty echo"); //Turn echoing of the display back on
    return 0; //end the program in a successful state
}

I have also tried scanf and it just locks up until I hit enter, and I'm not convinced it is seeing the mouse events.

Edit

I now have some working code that spits out the mouse events.

Here is the updated code from applying the accepted answer to this question:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main()
{
    system("stty -echo"); //don't show mouse events on screen
    system("stty -icanon");
    fprintf(stderr, "\e[?1003h\e[?1015h\e[?1006h"); //use stderr since not buffered turn on mouse event capture
    char buffer[16] = " ";
    char previousBuffer[16] = " ";

    //Make standard in not be blocking
    int flags = fcntl(stdin->_fileno, F_GETFL, 0);
    fcntl(stdin->_fileno, F_SETFL, flags | O_NONBLOCK);

    for (int hunSeconds = 0; hunSeconds < 500; hunSeconds++) //Run for 50 seconds
    {
        read(fileno(stdin), buffer, 16); //read mouse input
        if (strcmp(buffer, previousBuffer) != 0) //only show event if it is different
        {
            fprintf(stderr, "%s", buffer);
            strncpy(previousBuffer, buffer, 16);
        }
        usleep(100); // sleep for .1 seconds
    }
    printf("\e[?1000l"); //turn off mouse events
    system("stty echo"); //turn on screen echo again
    return 0;
}
Questionable
  • 768
  • 5
  • 26

1 Answers1

2

Two problems:

  • printf is (using stdout) buffered, so there's no guarantee that the escape sequences got to the terminal before attempting to read.
  • stdin isn't necessarily the terminal (though it might be). Again, fread is buffered (and you may not get the result as promptly as you wish).

Since stderr is not buffered, it would help to send the escape sequences with that stream. Rather than using fread, it can help to use read, e.g.,

read(fileno(stdin), buffer, 16)
Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105