使用 ANSI Escape 在虚拟终端中捕获鼠标
Capturing Mouse in Virtual Terminal with ANSI Escape
我已经开始通过 Google 的魔法在线学习 ANSI 转义序列。能够将光标 \e[row;colH
定位在屏幕上并设置输出的颜色(即:\e[31m
)。
接下来我想试试看如何在虚拟终端中捕获鼠标。我意识到这段代码不可移植,我知道我可以使用 ncurses 或其他一些 curses 库,但这里的目标是了解它是如何工作的,而不是用它编写生产代码。
我试过\e[?1003h
,它开始用鼠标事件填满屏幕。 (非常酷!)但是,我如何在 C 或 C++ 程序中捕获这些?
我在 PHP 中看到了我想做的事情的示例:
但是,当我尝试将代码移植到 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;
}
两个问题:
printf
是(使用 stdout)缓冲的,因此不能保证转义序列在尝试读取之前到达终端。
- stdin 不一定是终端(尽管它可能是)。同样,
fread
is buffered(您可能不会像您希望的那样迅速得到结果)。
因为 stderr 没有缓冲,所以用那个流发送转义序列会有所帮助。与其使用 fread
,不如使用 read
,例如
read(fileno(stdin), buffer, 16)
我已经开始通过 Google 的魔法在线学习 ANSI 转义序列。能够将光标 \e[row;colH
定位在屏幕上并设置输出的颜色(即:\e[31m
)。
接下来我想试试看如何在虚拟终端中捕获鼠标。我意识到这段代码不可移植,我知道我可以使用 ncurses 或其他一些 curses 库,但这里的目标是了解它是如何工作的,而不是用它编写生产代码。
我试过\e[?1003h
,它开始用鼠标事件填满屏幕。 (非常酷!)但是,我如何在 C 或 C++ 程序中捕获这些?
我在 PHP 中看到了我想做的事情的示例:
但是,当我尝试将代码移植到 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;
}
两个问题:
printf
是(使用 stdout)缓冲的,因此不能保证转义序列在尝试读取之前到达终端。- stdin 不一定是终端(尽管它可能是)。同样,
fread
is buffered(您可能不会像您希望的那样迅速得到结果)。
因为 stderr 没有缓冲,所以用那个流发送转义序列会有所帮助。与其使用 fread
,不如使用 read
,例如
read(fileno(stdin), buffer, 16)