使用termios.h在C程序中询问用户输入时,如何使箭头键和退格键正常工作?
作者:互联网
所以我有以下代码,它基本上只读取字符用户输入并打印它们直到输入’q’.
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<termios.h>
int main(void) {
char c;
static struct termios oldtio, newtio;
tcgetattr(0, &oldtio);
newtio = oldtio;
newtio.c_lflag &= ~ICANON;
newtio.c_lflag &= ~ECHO;
tcsetattr(0, TCSANOW, &newtio);
printf("Give text: ");
fflush(stdout);
while (1) {
read(0, &c, 1);
printf("%c", c);
fflush(stdout);
if (c == 'q') { break; }
}
printf("\n");
tcsetattr(0, TCSANOW, &oldtio);
return 0;
}
在主要功能的开头,我关闭了规范模式,让用户在给出时可以看到他的输入.我也关闭回声,所以像“^ [[A”在按下向上箭头键时不会弹出这样的东西.这可行,但我也能够将光标移动到终端窗口的上行,这并不好.有没有办法解决这个问题,以便用户只能在当前行内移动?
另一个问题是退格.当我按下它时,程序打印一个奇怪的符号(我假设是0x7f),而不是删除光标当前位置的字符.我应该以某种方式处理程序中的退格键输出,但我不知道该怎么做,因为它是这个奇怪的十六进制数.有什么提示吗?
我一直在考虑使用的一个选项是使用规范模式,因此箭头键和退格功能会自动使用.但是,规范模式逐行工作,因此在用户点击“Enter”之前不会显示文本.到目前为止,我还没有找到任何方法让用户在打字时看到他的输入.这甚至可能吗?
请,没有ncurses或readline建议.我想用termios.h做到这一点.
解决方法:
你看过手册了吗? (应该是man termios或者看看某个地方online)
在那里我发现ECHOE标志据说有以下效果:
If ICANON is also set, the ERASE character erases the preceding input character, and WERASE erases the preceding word.
这应该解决您的退格问题?
我还建议,您可以查看手册页中的示例.例如.你可以做以下事情:
newtio.c_lflag &= ~(ECHO | ECHOE | ICANON);
…一次只能在一行中设置多个标志.我知道手册对于初学者来说很难阅读,但你会习惯他们,你使用它们越多,他们查找C / POSIX函数等就越有效(以防你不使用它们)无论如何).
箭头键问题:也许你可以试试cfmakeraw() – 函数;它的描述很有希望.我没有时间进一步调查箭头键.但是,也许您在手册页中找到了其他有用的东西.
BTW:termios看起来很有趣,我总是想知道某些命令行程序正在使用哪些函数;通过你的问题学到了什么,谢谢!
编辑
我本周末做了一些研究.按下退格键时打印的“奇怪”符号很容易隐藏.它是ASCII值0x7f.所以添加一个简单的
if (c == 0x7f) { continue; }
…只是忽略退格键.或者以删除最后一个字符的方式处理它(请参阅下面的代码示例).
这个简单的解决方法不适用于箭头键,因为它们不是ASCII字符:/但是,这两个主题帮助我处理了这个问题:topic 1和topic 2.基本上按箭头键会导致几个字符的序列被发送到stdin(有关更多信息,请参阅第二个链接).
这是我的完整代码(我认为)按您希望的方式工作:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
int main(void) {
char c;
static struct termios oldtio, newtio;
tcgetattr(0, &oldtio);
newtio = oldtio;
newtio.c_lflag &= ~ICANON;
newtio.c_lflag &= ~ECHO;
tcsetattr(0, TCSANOW, &newtio);
printf("Give text:\n");
fflush(stdout);
while (1) {
c = getchar();
// is this an escape sequence?
if (c == 27) {
// "throw away" next two characters which specify escape sequence
c = getchar();
c = getchar();
continue;
}
// if backspace
if (c == 0x7f) {
// go one char left
printf("\b");
// overwrite the char with whitespace
printf(" ");
// go back to "now removed char position"
printf("\b");
continue;
}
if (c == 'q') {
break;
}
printf("%c", c);
}
printf("\n");
tcsetattr(0, TCSANOW, &oldtio);
return 0;
}
顺便说一下,你可以通过以下代码获得完整的转义序列:
int main(void) {
char c;
while (1) {
c = getchar();
printf("%d", c);
}
return 0;
}
我想我不必说这个完整的东西是一个非常脏的黑客,很容易忘记处理一些特殊的密钥.例如.在我的代码中,我不处理页面向上/向下或主页键… – >上面的代码远非完整,但为您提供了一个重点.你还应该看看terminfo,它可以为你提供很多必要的信息;它还应该有助于提供更便携的解决方案.如你所见,这个“简单”的东西会变得相当复杂.所以你可能会重新考虑你对ncurses的决定:)
标签:c-3,linux,terminal,termios 来源: https://codeday.me/bug/20190624/1276808.html