我想在OS X上的命令行应用程序中执行以下操作:
while (true) {
// do some task
if (user_has_pressed('x')) {
// break out of the loop and do something different
}
}为了清楚起见,我不希望程序阻塞等待用户输入。我正在运行一个需要运行几个小时的数值模拟,我想按一个键来打印其进度的详细统计数据,或者中断它并更改参数等。
有一些现有的similar questions,但答案要么建议使用仅支持windows的getch功能,要么将终端切换到不同的输入模式。我不想这样做,因为我需要保留使用ctrl-c中断的能力,而不会弄乱终端。
我不想构建Cocoa应用程序,也不关心跨平台。我只是在寻找在命令行应用程序中做这件事的最简单、最快、最脏的方法,这个应用程序只能在我自己的机器上运行。
我想一种选择是使用ncurses。从简单的阅读来看,这似乎是一个比我想要的更重的选择--但如果有人能发布一个简单的最小示例来完成上面的任务,那将是非常有帮助的。
发布于 2016-12-08 18:36:31
基本思想是使用轮询,但终端通常会在将按键发送到stdin之前等待enter键。因此,我们需要首先禁用规范模式。
#include <poll.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <iostream>
void disable_canonical(){
struct termios old = {0};
if (tcgetattr(0, &old) < 0)
perror("tcsetattr()");
old.c_lflag &= ~ICANON;
old.c_lflag &= ~ECHO;
old.c_cc[VMIN] = 1;
old.c_cc[VTIME] = 0;
if (tcsetattr(0, TCSANOW, &old) < 0)
perror("tcsetattr ICANON");
}
void enable_canonical(){
struct termios old = {0};
if (tcgetattr(0, &old) < 0)
perror("tcsetattr()");
old.c_lflag |= ICANON;
old.c_lflag |= ECHO;
old.c_cc[VMIN] = 1;
old.c_cc[VTIME] = 0;
if (tcsetattr(0, TCSANOW, &old) < 0)
perror("tcsetattr ICANON");
}
bool key_pressed(char c){
struct pollfd fds[1];
fds[0].fd = 0;
fds[0].events = POLLIN;
fds[0].revents = 0;
int r = poll(fds, 1, 1);
if(r > 0){
if(fds[0].revents & POLLIN || fds[0].revents & POLLRDBAND || fds[0].revents & POLLRDNORM){
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
int n = read(0, buffer, sizeof(buffer) -1);
for(int i = 0; i < n; ++i){
if(buffer[i] == c){
return true;
}
}
}
}
return false;
}
int main(){
disable_canonical();
while(true){
if(key_pressed('x')){
break;
}
usleep(500);
//std::cout << "looping...\n";
}
enable_canonical();
}附注: tcsetattr代码是从https://stackoverflow.com/a/912796/573789借用的
https://stackoverflow.com/questions/40903215
复制相似问题