【问题标题】:C Avoid cursor positioning on key pressC 避免在按键上定位光标
【发布时间】:2016-01-20 07:11:18
【问题描述】:

我正在尝试在 C 中实现一种终端。我正在尝试在上下键压力上构建终端历史记录,但如果我按下这些键,光标将向上(或向下)一行。如何“控制”这个功能?

编辑: 举个例子:

fgets(cmd, sizeof(cmd), stdin);

    if (cmd[0] == '\033')
        if (cmd[2] == 'A')
            printf("up-arrow pressed!\n");

这段代码很糟糕,但它是一个很好的起点。有用。问题是,如果您按下向上箭头键(光标),它会移动到上一行并打印“按下向上箭头!”。在没有外部库的情况下如何避免光标上下移动?

【问题讨论】:

  • 没有代码根本无法回答这个问题……
  • 我强烈建议为此目的使用 libreadline。它提供了许多功能,同时独立于特定的终端仿真
  • 没错,就是上下光标,通常在终端中用于历史记录。我读到了图书馆,它真的很有用,但我试图独自构建一切。检查问题,我编辑了
  • @AndreaMartinelli 通常您处于 linemode,您的程序将逐行接收数据(即按 Enter 后)。但是对于这个任务,您需要处于字符模式,这样您的程序可以在输入字符后立即处理输入(即光标向上)。您可以在 unix 下通过适当调用 ioctl() 来执行此操作
  • @AndreaMartinelli 我在回答中添加了一些内容,因为代码太大,无法评论。

标签: c terminal key


【解决方案1】:

除了^C 之外,真正的应用程序会提供退出程序的功能。这是一个示例,修改@ctx 的示例以执行此操作(并改进转义序列的处理),当连续读取两个转义时退出:

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

int main (void) {

        struct termios t, save;
        int last;
        tcgetattr(0, &t);
        save = t;
        t.c_lflag &= ~(ECHO | ICANON);
        tcsetattr(0, TCSANOW, &t);

        while (1) {
                int ch = getchar();
                if (ch == '\033') {
                    if (last == ch)
                            break;
                        do {
                            last = ch;
                            ch = getchar();
                        } while (ispunct(ch) || isdigit(ch));
                        if (isalpha(ch)) {
                            printf("Got escape sequence: ");
                            switch (ch) {
                                case 'A':
                                        printf("Cursor up\n");
                                        break;
                                case 'B':
                                        printf("Cursor down\n");
                                        break;
                                case 'D':
                                        printf("Cursor left\n");
                                        break;
                                case 'C':
                                        printf("Cursor right\n");
                                        break;
                                default:
                                    printf("%c\n", ch);
                                    break;
                                    break;
                            }
                        }
                } else if (ch > 0) {
                        printf("Got %c\n", ch);
                }
            last = ch;
        }

        tcsetattr(0, TCSANOW, &save);
        return 0;
}

curses 中的等价物(带有不同的消息)更简单:

#include <curses.h>

int main (void) {
    int last = 0;

    filter();
    initscr();
    noecho();
    cbreak();
    keypad(stdscr, TRUE);

    while (1) {
            int ch = getch();
            if (ch >= 0) {
                    if (ch >= KEY_MIN) {
                            printf("Got special key %s\r\n", keyname(ch));
                    } else {
                            if (ch == '\033' && last == ch)
                                    break;
                            printf("Got %s\r\n", unctrl(ch));
                    }
                    last = ch;
                    fflush(stdout);
            }
        }

        endwin();
        return 0;
}

【讨论】:

  • 以前连续读取两个转义符怎么不起作用?我看不出现在哪里有改进。
  • @AndreaMartinelli 如果您使用库,则应该为此目的使用 libreadline,它基于 ncurses,但在更高级别上实现了您想要的。
  • 这一行:save = t; 不会按预期工作,因为 termios 不是 C 固有的大小/类型。建议使用memcpy( &amp;save, &amp;t, sizeof(struct termios) )l
  • struct-copy 在 ANSI C 中工作了一段时间:在编译器不知道类型或结构包含指针的特殊情况下,我会使用 memcpy。 (我对 2 次转义的评论是关于退出原本的无限循环 - 已修复)。
【解决方案2】:

首先,提供一个如何初始化终端的示例和一些基本代码:

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

int main (void) {

        struct termios t;
        tcgetattr(0, &t);
        t.c_lflag &= ~(ECHO | ICANON);
        tcsetattr(0, TCSANOW, &t);

        while (1) {
                char ch = getchar();
                if (ch == '\x1b') {
                        do {
                                ch = getchar();
                        } while (!isalpha(ch));
                        printf("Got escape sequence: ");
                        switch (ch) {
                                case 'A':
                                        printf("Cursor up\n");
                                        break;
                                case 'B':
                                        printf("Cursor down\n");
                                        break;
                                case 'D':
                                        printf("Cursor left\n");
                                        break;
                                case 'C':
                                        printf("Cursor right\n");
                                        break;
                                default:
                                        printf("%c\n", ch);
                        }
                        // handle escape sequence
                } else {
                        printf("Got %c\n", ch);
                }
        }

        return 0;
}

现在您必须输出键入的字符并在收到光标-* 或其他一些控制序列(而不是上面的 debug-printfs)时通过发送适当的转义序列来移动.为此,您必须密切跟踪当前的屏幕布局。

正如我在 cmets 中已经建议的那样,这是一项非常乏味的工作,除了学习(或者可能是嵌入式系统)之外,与使用 libreadline 或类似 API 相比,我没有看到任何好处。

【讨论】:

  • 谢谢,我想我会尝试一个库,也许它可以更强大和简单
猜你喜欢
  • 1970-01-01
  • 2018-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多