Захват мыши в виртуальном терминале с помощью ANSI Escape

Я начал узнавать об escape-последовательностях ANSI в Интернете благодаря магии Google. Очень удобно иметь возможность позиционировать курсор \e[row;colH на экране и устанавливать цвета выходных данных (т.е.: \e[31m).

Далее я хотел бы попробовать и посмотреть, как мышь может быть захвачена в виртуальном терминале. Я понимаю, что этот код не является переносимым, и я знаю, что могу использовать ncurses или какую-либо другую библиотеку curses, но цель здесь — узнать, как она работает, а не писать с ней производственный код.

Я пробовал \e[?1003h, и он начинает заполнять экран событиями мыши. (Очень круто!) Однако как их зафиксировать в программе на C или C++?

Я видел пример того, что я хотел бы сделать в PHP: https://stackoverflow.com/a/58390575/1770034

Однако, когда я пытаюсь перенести код на что-то на C, он просто блокируется в цикле while. (Протестировано с помощью GDB, чтобы выяснить это.)

#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
}

Я также пробовал scanf, и он просто блокируется, пока я не нажму Enter, и я не уверен, что он видит события мыши.

Изменить

Теперь у меня есть рабочий код, который выдает события мыши.

Вот обновленный код применения принятого ответа на этот вопрос:

#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;
}

person Questionable    schedule 22.01.2020    source источник


Ответы (1)


Две проблемы:

  • printf (используя stdout) буферизуется, поэтому нет гарантии, что escape-последовательности дойдут до терминала до попытки чтения.
  • stdin не обязательно является терминалом (хотя может им быть). Опять же, fread буферизуется (и вы можете не получить результат так быстро, как вы хотите).

Так как stderr не буферизуется, было бы полезно отправить escape-последовательности с этим потоком. Вместо использования fread может помочь использование read, например,

read(fileno(stdin), buffer, 16)
person Thomas Dickey    schedule 22.01.2020