网上找到的getch()函数源码如下:
#include <termios.h> #include <unistd.h> #include <stdio.h> int getch(void) { struct termios tm, tm_old; int fd = STDIN_FILENO, c; if(tcgetattr(fd, &tm) < 0) return -1; tm_old = tm; cfmakeraw(&tm); if(tcsetattr(fd, TCSANOW, &tm) < 0) return -1; c = fgetc(stdin); if(tcsetattr(fd, TCSANOW, &tm_old) < 0) return -1; return c; }
经测试,在Linux下无法正常使用方向键,其余键能正常识别,为了解决这问题,我利用了模拟实现的kbhit函数检测是否有多个键值,有的话,累加键值,具体如下:
int set_raw(int t) { static struct termio tty ; struct termio tmp ; if (t) { ioctl(0,TCGETA,&tty) ; tmp = tty ; tmp.c_lflag &= ~(ICANON|ECHOPRT); tmp.c_cc[VMIN] = 1 ; tmp.c_cc[VTIME] = 0 ; ioctl(0,TCSETA,&tmp) ; } else { tty.c_lflag &= ~(ICANON|ECHO); ioctl(0,TCSETA,&tty); } return 0; } int kbhit(void) { struct termios oldt, newt; int ch; int oldf; set_raw(3); tcgetattr(STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); oldf = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); ch = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &oldt); fcntl(STDIN_FILENO, F_SETFL, oldf); if(ch != EOF) { ungetc(ch, stdin); return 1; } set_raw(0); return 0; } int getch(void) { struct termios tm, tm_old; int fd = STDIN_FILENO,c; input = 0; if(tcgetattr(fd, &tm) < 0) return -1; tm_old = tm; tm.c_lflag &= ~(ICANON|ECHO|ISIG); if(tcsetattr(fd, TCSANOW, &tm) < 0) return -1; c = fgetc(stdin); if(kbhit()){ /* 如果还有下一个键值 */ c += getch(); } if(tcsetattr(fd,TCSANOW,&tm_old)<0) return -1; if(c == 3) exit(1); /*如果是Ctrl+C组合键,就强制终止程序*/ return c; }
LCUI 项目中,使用的代码是这样的:
static struct termios tm, tm_old; static int fd = STDIN_FILENO; int Set_Raw(int t) { if (t > 0) { if(tcgetattr(fd, &tm) < 0) return -1; tm_old = tm; tm.c_lflag &= ~(ICANON|ECHO); tm.c_cc[VMIN] = 1; tm.c_cc[VTIME] = 0; if(tcsetattr(fd, TCSANOW, &tm) < 0) return -1; printf("\033[?25l");/* 隐藏光标 */ } else { printf("\e[?25h"); /* 显示光标 */ if(tcsetattr(fd,TCSANOW,&tm_old)<0) return -1; } return 0; } int Check_Key(void) { struct termios oldt, newt; int ch; int oldf; tcgetattr(STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); oldf = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); ch = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &oldt); fcntl(STDIN_FILENO, F_SETFL, oldf); if(ch != EOF) { ungetc(ch, stdin); return 1; } return 0; } static int input,count = 0; int Get_Key(void) { ++count; int k,c; k = fgetc(stdin); input += k; if(Check_Key()){ Get_Key(); } c = input; --count; if(count == 0) input = 0; if(c == 3) exit(1); return c; }
Set_Raw()函数用于初始化和恢复终端属性,参数t大于0时,初始化,否则,恢复终端属性。
Check_Key()函数和kbhit()函数一样,检测是否有按键输入。
Get_Key()函数用于获取按键输入的字符。