在虚拟终端中使用ANSI Escape捕获鼠标

8

我通过谷歌神奇地学习了ANSI转义序列。使用\e[row;colH可以在屏幕上定位光标并设置输出的颜色(例如: \e[31m)。

接下来,我想尝试一下如何在虚拟终端中捕获鼠标。我意识到这段代码不具备可移植性,并且我可以使用ncurses或其他curses库,但这里的目标是学习它的工作原理,而不是用它编写生产代码。

我尝试了\e[?1003h,它开始填充屏幕的鼠标事件。(非常酷炫!) 但是,我该如何在C或C++程序中捕获这些事件呢?

我在PHP中看到了一个示例:https://dev59.com/fm025IYBdhLWcg3wf2OE#58390575

但是,当我尝试将代码移植到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,但它只在我按下回车键之前锁定,并且我并不确定它是否能够检测到鼠标事件。

编辑

我现在有一些可以输出鼠标事件的工作代码。

以下是应用了此问题的已接受答案后的更新代码:

#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;
}
1个回答

2

两个问题:

  • printf(使用stdout)是有缓冲的,所以不能保证转义序列在尝试读取之前到达终端。
  • stdin不一定是终端(虽然可能是)。同样,fread是有缓冲的(您可能无法及时获得结果)。

由于stderr未缓冲,因此将转义序列与该流一起发送会有所帮助。与其使用fread,不如使用read,例如:

read(fileno(stdin), buffer, 16)

或者使用 fsetvbuf(file, NULL, _IONBF, 0) - Fox
s/fsetvbuf/setvbuf/ - Thomas Dickey
谢谢提醒,哈哈。我总是在使用FILE*的东西前加上f - Fox

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接