如何在没有回车的情况下执行按键对应的选项? (C 编程)
How to execute the key-corresponding option without an Enter? (C programming)
我正在尝试用 C 编写一个“更多”(就像 Linux 中的“更多”指令)程序。
按“q”时退出,按“”时再显示一页。
但每次我必须在命令后按 Enter 键。
我尝试使用 setbuf() 和 setvbuf() 但它没有 work.Why?我该怎么办?
FILE *fp_tty; //read from keyboard
fp_tty = fopen("/dev/tty", "r");
setbuf(fp_tty, NULL);
see_more(fp_tty);
......
int see_more(FILE *cmd) {
int c;
printf("3[7m more? 3[m");
while ((c = getc(cmd)) != EOF) {
if (c == 'q') {
return 0; //quit
}
if (c == ' ') {
return PAGELEN; //next page
}
if (c == '\n') {
return 1; //1 line
}
}
return 0;
}
正如@rici 在评论中指出的那样,termios
可以帮助将终端切换到非规范模式。
在您的情况下,您可以使用以下方式处理这些键:
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#define KEY_ESCAPE 0x001b
#define KEY_ENTER 0x000a
static struct termios term, oterm;
static int getch(void);
static int kbhit(void);
static int kbesc(void);
static int kbget(void);
static int getch(void)
{
int c = 0;
tcgetattr(0, &oterm);
memcpy(&term, &oterm, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &term);
c = getchar();
tcsetattr(0, TCSANOW, &oterm);
return c;
}
static int kbhit(void)
{
int c = 0;
tcgetattr(0, &oterm);
memcpy(&term, &oterm, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 0;
term.c_cc[VTIME] = 1;
tcsetattr(0, TCSANOW, &term);
c = getchar();
tcsetattr(0, TCSANOW, &oterm);
if (c != -1) ungetc(c, stdin);
return ((c != -1) ? 1 : 0);
}
static int kbesc(void)
{
int c;
if (!kbhit()) return KEY_ESCAPE;
c = getch();
while (kbhit()) getch();
return c;
}
static int kbget(void)
{
int c;
c = getch();
return (c == KEY_ESCAPE) ? kbesc() : c;
}
int main(void)
{
int c;
while (1) {
c = kbget();
if (c == 'q')
{
return 0; //quit
}
if (c == ' ') {
return PAGELEN; //next page
}
if (c == '\n') {
return 1; //1 line
}
}
return 0;
}
Linux 共享大多数 Unix 系统的 tty
规则。当设备被视为 终端输入 时,这里意味着接收来自人类的按键的东西,默认情况下输入是面向行的。基本原理是允许用户按错键,取消它,最后按正确的键。在这种情况下,tty 驱动程序会处理修复并仅将最终字符串呈现给应用程序。
这对于面向行的应用程序来说工作得很好,但对于像 emacs 或 vi 这样的纯屏幕应用程序来说是愚蠢的(是的,它们也是 70 年代的......),所以 tty 驱动程序可以在原始模式下设置为立即将任何字符传递给应用程序(存在其他选项......)。 curses
库在幕后使用的是能够立即对任何按键做出反应。
TL/DR:您必须将 tty 驱动程序置于原始模式才能立即处理字符。详细信息在 tcflush 手册页上。
我正在尝试用 C 编写一个“更多”(就像 Linux 中的“更多”指令)程序。 按“q”时退出,按“”时再显示一页。 但每次我必须在命令后按 Enter 键。 我尝试使用 setbuf() 和 setvbuf() 但它没有 work.Why?我该怎么办?
FILE *fp_tty; //read from keyboard
fp_tty = fopen("/dev/tty", "r");
setbuf(fp_tty, NULL);
see_more(fp_tty);
......
int see_more(FILE *cmd) {
int c;
printf("3[7m more? 3[m");
while ((c = getc(cmd)) != EOF) {
if (c == 'q') {
return 0; //quit
}
if (c == ' ') {
return PAGELEN; //next page
}
if (c == '\n') {
return 1; //1 line
}
}
return 0;
}
正如@rici 在评论中指出的那样,termios
可以帮助将终端切换到非规范模式。
在您的情况下,您可以使用以下方式处理这些键:
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#define KEY_ESCAPE 0x001b
#define KEY_ENTER 0x000a
static struct termios term, oterm;
static int getch(void);
static int kbhit(void);
static int kbesc(void);
static int kbget(void);
static int getch(void)
{
int c = 0;
tcgetattr(0, &oterm);
memcpy(&term, &oterm, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &term);
c = getchar();
tcsetattr(0, TCSANOW, &oterm);
return c;
}
static int kbhit(void)
{
int c = 0;
tcgetattr(0, &oterm);
memcpy(&term, &oterm, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 0;
term.c_cc[VTIME] = 1;
tcsetattr(0, TCSANOW, &term);
c = getchar();
tcsetattr(0, TCSANOW, &oterm);
if (c != -1) ungetc(c, stdin);
return ((c != -1) ? 1 : 0);
}
static int kbesc(void)
{
int c;
if (!kbhit()) return KEY_ESCAPE;
c = getch();
while (kbhit()) getch();
return c;
}
static int kbget(void)
{
int c;
c = getch();
return (c == KEY_ESCAPE) ? kbesc() : c;
}
int main(void)
{
int c;
while (1) {
c = kbget();
if (c == 'q')
{
return 0; //quit
}
if (c == ' ') {
return PAGELEN; //next page
}
if (c == '\n') {
return 1; //1 line
}
}
return 0;
}
Linux 共享大多数 Unix 系统的 tty
规则。当设备被视为 终端输入 时,这里意味着接收来自人类的按键的东西,默认情况下输入是面向行的。基本原理是允许用户按错键,取消它,最后按正确的键。在这种情况下,tty 驱动程序会处理修复并仅将最终字符串呈现给应用程序。
这对于面向行的应用程序来说工作得很好,但对于像 emacs 或 vi 这样的纯屏幕应用程序来说是愚蠢的(是的,它们也是 70 年代的......),所以 tty 驱动程序可以在原始模式下设置为立即将任何字符传递给应用程序(存在其他选项......)。 curses
库在幕后使用的是能够立即对任何按键做出反应。
TL/DR:您必须将 tty 驱动程序置于原始模式才能立即处理字符。详细信息在 tcflush 手册页上。