在规范模式下处理转义键的任何方法?

时间:2014-10-13 23:20:18

标签: c unix terminal termios

在unix plain C termios编程中,如果我使用规范模式接收来自用户的输入行,我该如何处理转义键?通常,如果用户正在输入一行文本并按下转义,则不会发生任何事情。如果用户按下escape,我想取消当前输入。我知道我可以处理单个字符,但后来我失去了规范模式(退格等)的所有好处。

3 个答案:

答案 0 :(得分:4)

原始

Jonathan Leffler for his comment that hinted me at the right direction的所有应付信用额,底部是我注释的第一个termios程序演示器(谢谢!)。

关键是在当前终端的文件描述符上使用tcgetattr(ttyfd, &attributes)以在struct termios中检索其当前属性,编辑属性,然后使用tcsetattr(ttyfd, when, &attributes)应用更改。

其中一个属性是“kill”字符 - 导致整个当前缓冲行被丢弃的字符。它是通过索引到c_cc的{​​{1}}成员数组并将struct termios设置为任何想要的值来设置的(这里, Esc ,等于八进制{ {1}})。

kill字符应该在退出时恢复到之前的值。

attr.c_cc[VKILL]

此演示似乎适用于Mac OS X 10.6.8。我也在Linux上测试了这个,显然 Esc 来杀死缓冲区似乎是默认的,好像我打印出033我获得了#include <termios.h> #include <fcntl.h> #include <stdio.h> int main(){ char buf[80]; int numBytes; struct termios original, tattered; int ttyfd; /* Open the controlling terminal. */ ttyfd = open("/dev/tty", O_RDWR); if(ttyfd < 0){ printf("Could not open tty!\n"); return -1; } /** * Get current terminal properties, save them (including current KILL char), * set the new KILL char, and make this the new setting. */ tcgetattr(ttyfd, &original); tattered = original; tattered.c_cc[VKILL] = 033;/* New killchar, 033 == ESC. */ tcsetattr(ttyfd, TCSANOW, &tattered); /** * Simple test to see whether it works. */ write(1, "Please enter a line: ", 21); numBytes = read(0, buf, sizeof buf); write(1, buf, numBytes); /** * Restore original settings. */ tcsetattr(ttyfd, TCSANOW, &original); /* Clean up. */ close(ttyfd); return 0; }

修改

以下尝试尽可能模仿您在评论中描述的行为。它将c_cc[VKILL]设置为 Esc ; EOL2是备用终端。它还删除 Esc 作为kill字符,因为你想要接收该行。

现在发生的是,如果按下正常的 Ret ,一切正常。但是,如果按下 Esc ,缓冲区中的最后一个字符将设置为 Esc ,这是一个可以测试的条件(尽管只有在首先读取并缓冲整行之后)

根据您澄清的规格,以下是演示者。它等待一行输入并用

回复它
  • 27 == 033 == ESC如果该行以 Esc
  • 终止
  • c_cc[VEOL2]如果该行以 Ret 终止。

享受!

<CANCELLED>

答案 1 :(得分:2)

您需要使用EOF功能将ESC字符设置为Enter而不是tcsetattr()。有关详细信息,请访问http://pubs.opengroup.org/onlinepubs/7908799/xbd/termios.html#tag_008_001_009

答案 2 :(得分:2)

这是我的getLine()函数的略微修改版本,用于来自用户的强大输入。您可以查看原始here的详细信息,但已修改此内容以使用termios内容,这样可以对输入进行一定程度的控制。

因为termios的工作级别低于标准C输入,所以它也会影响它。

首先,getLine()函数所需的标头和返回值:

#include <termios.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

#define OK        0
#define NO_INPUT  1
#define TOO_LONG  2
#define TERM_PROB 3

接下来,一个帮助函数,用于将终端恢复到其原始状态,这使您可以轻松地从getLine()返回一个值,知道终端将保持其原始状态。

static int revertTerm (int fd, struct termios *ptio, int old, int rc) {
    // Revert the terminal to its original state then return
    // specified value.

    ptio->c_cc[VKILL] = old;
    tcsetattr (fd, TCSANOW, ptio);
    close (fd);
    return rc;
}

接下来,实际的getLine()函数本身修改终端属性以使 ESC 为kill字符,然后调用fgets()以及所有附加内容以提示,检测缓冲区溢出,将输入刷新到行尾等等。

在用户在fgets()范围内作为此功能的一部分时,修改后的终端行为处于活动状态,您可以使用 ESC 清除该行。

static int getLine (char *prmpt, char *buff, size_t sz) {
    int old, fd, ch, extra;
    struct termios tio;

    // Modify teminal so ESC is KILL character.

    fd = open ("/dev/tty", O_RDWR);
    if (fd < 0)
        return TERM_PROB;

    tcgetattr (fd, &tio);
    old = tio.c_cc[VKILL];
    tio.c_cc[VKILL] = 0x1b;
    tcsetattr (fd, TCSANOW, &tio);

    // Get line with buffer overrun protection.

    if (prmpt != NULL) {
        printf ("%s", prmpt);
        fflush (stdout);
    }
    if (fgets (buff, sz, stdin) == NULL)
        return revertTerm (fd, &tio, old, NO_INPUT);

    // If it was too long, there'll be no newline. In that case, we flush
    // to end of line so that excess doesn't affect the next call.

    if (buff[strlen(buff)-1] != '\n') {
        extra = 0;
        while (((ch = getchar()) != '\n') && (ch != EOF))
            extra = 1;
        return revertTerm (fd, &tio, old, (extra == 1) ? TOO_LONG : OK);
    }

    // Otherwise remove newline and give string back to caller.

    buff[strlen(buff)-1] = '\0';
    return revertTerm (fd, &tio, old, OK);
}

最后,一个测试程序,以便您可以检查其行为。基本上,它允许你输入最多20个字符的行,然后它会打印出状态(太长,没有输入等)。

如果在输入过程中的任何时候按 ESC ,它将终止该行并重新开始。

输入exit将导致程序退出。

// Test program for getLine().

int main (void) {
    int rc, done = 0;
    char buff[21];

    while (!done) {
        rc = getLine ("Enter string (ESC to clear, exit to stop)> ",
            buff, sizeof(buff));
        if (rc == NO_INPUT) {
            // Extra NL since my system doesn't output that on EOF.
            printf ("\nNo input\n");
        } else if (rc == TOO_LONG) {
            printf ("Input too long [%s]\n", buff);
        } else {
            done = (strcmp (buff, "exit") == 0);
            if (!done)
                printf ("OK [%s]\n", buff);
        }
    }

    return 0;
}