所以我有以下代码,基本上只读取字符用户输入并打印它们直到输入'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来做这件事。
答案 0 :(得分:4)
man termios
或查看某处online)
在那里我发现ECHOE
标志据说具有以下效果:
如果还设置了ICANON,则ERASE字符将删除前面的输入字符,并且WERASE将删除前一个字。
这应该可以解决你的退格问题吗?
我也建议,您可以查看手册页中的示例。例如。你可以做到以下几点:
newtio.c_lflag &= ~(ECHO | ECHOE | ICANON);
...一次只能在一行中设置多个标志。我知道手册很难为初学者阅读,但你会习惯他们,你使用它们越多,他们查找C / POSIX功能等就越有效(以防万一,你不要这样做)无论如何使用它们。)
箭头键问题:也许你可以试试cfmakeraw()
- 函数;它的描述很有希望。我还没来得及进一步调查箭头键。但是,也许您在手册页中找到了其他有用的东西。
BTW:termios
看起来很有趣,我总是想知道某些命令行程序正在使用哪些函数;通过你的问题学到了一些东西,谢谢!
本周末我做了更多的研究。 &#34;奇怪&#34;按下退格键时打印的符号很容易隐藏。它是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;
}
我想我不必说完整的东西是一个非常脏的黑客,并且很容易忘记处理一些特殊的密钥。例如。在我的代码中,我无法处理page-up/down
或home
键... - &gt;上面的代码远非完整,但为您提供了一个重点。你还应该看看terminfo
,它可以为你提供很多必要的信息;它还应该有助于提供更便携的解决方案。如你所见,这个&#34;简单&#34;事情可能变得相当复杂。所以你可能会重新考虑你对ncurses的决定:)
答案 1 :(得分:0)
实际上,为了处理箭头键,你必须实现一个大小的ncurses块。有利有弊:在命令行应用程序中使用ncurses的主要缺点可能是它通常会清除屏幕。但是,(n)curses提供了一个函数filter。有一个示例程序&#34; test / filter.c&#34;在ncurses源代码中,通过使用左箭头键作为擦除字符来说明这一点,并将结果行传递给system()以运行简单命令。该样本少于100行代码 - 似乎比上面的例子更简单,更完整。