使用termios.h在C程序中要求用户输入时,如何使箭头键和退格键正常工作? [英] How to make arrow keys and backspace work correctly when asking input from user in C program using termios.h?

查看:72
本文介绍了使用termios.h在C程序中要求用户输入时,如何使箭头键和退格键正常工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我有以下代码,该代码基本上只读取用户输入的字符并打印它们,直到输入"q"为止.

So I have the following code which basically just reads characters user inputs and prints them until 'q' is entered.

#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""之类的东西.这可行,但是我也可以将光标移到终端窗口的上排,这不好.有没有办法解决此问题,使用户只能在当前行内移动?

In the beginning of the main function I turn off the canonical mode to make user able to see his input when he's giving it. I also turn off the echo so stuff like "^[[A" doesn't pop up when pressing the up arrow key for example. This works, but I'm also able to move the cursor to upper rows on a terminal window and that's not good. Is there a way fix this so that user can only move within the current row?

另一个问题是退格键.当我按下它时,程序将打印一个奇怪的符号(我假设它是0x7f),而不是擦除光标当前位置的剩余字符.我应该以某种方式适当地处理程序中的退格键输出,但是我不知道该怎么办,因为它是这个奇怪的十六进制数.有什么提示吗?

Another problem is the backspace. When I press it the program prints a weird symbol (which I assume is 0x7f) instead of erasing the character left to the cursor's current location. I should propably handle the backspace key output in the program somehow but I don't know how to do it since it's this weird hexadecimal number. Any tips for this?

我也一直在考虑使这项工作可行的一个选项是使用规范模式,以便自动使用箭头键和退格功能.但是,规范模式逐行工作,因此直到用户单击"Enter"后,文本才会出现.到目前为止,我还没有找到让用户在键入时看到其输入的任何方法.这有可能吗?

One option I've also been thinking about to make this work is to use canonical mode so the arrow keys and backspace functionalities are automatically in use. However, canonical mode works line by line and so the text doesn't appear until "Enter" is hit by the user. So far, I haven't figured out any way to make user see his input while typing. Is this even possible?

而且,请不要提出任何建议或阅读专栏文章.我想使用termios.h来做到这一点.

And please, no ncurses or readline suggestions. I want to do this using termios.h.

推荐答案

您是否看过手册页? (应该为man termios或在在线处找到的地方)

have you looked into the man pages? (should be man termios or look somewhere online)

我在那里发现了ECHOE标志,据说具有以下作用:

There I found the ECHOE flag which is said to have the following effect:

如果还设置了ICANON,则ERASE字符将擦除前面的输入字符,而WERASE将擦除前面的单词.

If ICANON is also set, the ERASE character erases the preceding input character, and WERASE erases the preceding word.

这应该解决您的退格问题吗?

This should fix your backspace problem?

我还建议您看一下手册页中的示例.例如.您可以执行以下操作:

I also suggest, you have a look into the examples in the man page. E.g. you could do the following:

newtio.c_lflag &= ~(ECHO | ECHOE | ICANON);

...仅在一行中一次设置多个标志.我知道手册页对于初学者来说很难阅读,但是您会习惯它们,并且使用它们的次数越多,查找C/POSIX函数等的效率就越高(以防万一,您不使用它们无论如何).

...to set more than one flag at a time in only one line. I know the man pages are hard to read for beginners but you will get used to them and the more you use them, the more efficient they become for looking up C/POSIX-functions etc (just in case, you don't use them anyway).

箭头键问题:也许您可以尝试使用cfmakeraw()功能;它的描述听起来很有希望.我还没有时间进一步研究箭头键.但是,也许您会在手册页中找到其他有用的东西.

The arrow-key-problem: Maybe you can try the cfmakeraw()-function; its description sounds promising. I haven't had time to investigate any further about the arrow keys. However, maybe you find something else useful in the man page.

BTW:termios看起来很有趣,我一直想知道某些命令行程序正在使用哪些功能;通过您的问题学到了一些东西,谢谢!

BTW: termios looks interesting, I always wondered which functions certain command line programmes are using; learned something by your question, thanks!

这个周末我做了更多的研究.按下退格键时打印的奇怪"符号非常容易隐藏.它是ASCII值0x7f.因此,添加一个简单的

I've done some more research this weekend. The "strange" symbol printed when pressing the backspace key is quite easy to hide. It is the ASCII-value 0x7f. So add a simple

if (c == 0x7f) { continue; }

...只是忽略退格键.或以删除最后一个字符的方式处理它(请参见下面的代码示例).

...to just ignore the backspace key. Or handle it in a way to remove the last character (see code example below).

尽管箭头键不是ASCII字符,但这种简单的解决方法不适用于箭头键:/但是,这两个主题也帮助我处理了此问题:主题2 .基本上,按箭头键会导致将几个字符序列发送到stdin(有关更多信息,请参见第二个链接).

This simple workaround doesn't work for the arrow keys though as they are no ASCII characters :/ However, these two topics helped me handling also this problem: topic 1 and topic 2. Basically pressing the arrow keys results in a sequence of a couple of chars being sent to stdin (see the second link for more information).

这是我完整的代码,(我认为)可以按照您希望的方式工作:

Here is my complete code which (I think) works the way you wish:

#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;
}

顺便说一句,您可以通过以下代码获取完整的转义序列:

BTW you can get the complete escape sequences by the following code:

int main(void) {
    char c;
    while (1) {
        c = getchar();
        printf("%d", c);
    }
    return 0;
}

我想我不必说这个完整的东西是很脏的hack,很容易忘记处理一些特殊的密钥.例如.在我的代码中,我不处理page-up/downhome键...->上面的代码远未完成,但为您提供了一个起点.您还应该查看terminfo,它可以为您提供许多必要的信息.它也应该为便携式解决方案提供帮助.如您所见,这种简单"的事情可能变得非常复杂.因此,您可能会重新考虑针对ncurses的决定:)

I think I don't have to say that this complete thing is quite a dirty hack and it's easy to forget handling some special keys. E.g. in my code I don't handle the page-up/down or home keys... --> the code above is far from complete but gives you a point to start. You should also have a look at terminfo which can provide you a lot of the necessary information; it should also help with a more portable solution. As you see, this "simple" thing can become quite complex.So you might rethink your decision against ncurses :)

这篇关于使用termios.h在C程序中要求用户输入时,如何使箭头键和退格键正常工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆