【问题标题】:proper way of catching control+key in ncurses在 ncurses 中捕获 control+key 的正确方法
【发布时间】:2023-07-05 07:46:01
【问题描述】:

在 ncurses 中捕获 control+key 的正确方法是什么? 目前我正在这样做定义这样的控制:

#define ctl(x) ((x) & 0x1f)

它工作正常,但问题是我无法同时捕捉 C-j 和 ENTER,这是因为:

   j = 106 = 1101010
0x1f = 31 = 0011111
1101010 & 0011111 = 0001010 = 10 = ENTER key..

那么..我该如何抓住它? 谢谢!

-- 编辑: 如果我尝试下面的代码, 我无法正确捕捉回车键,即使在数字键盘中也是如此。 Enter 被捕获为 ctrl-j。

#include <stdio.h>
#include <ncurses.h>
#define ctrl(x) ((x) & 0x1f)

int main(void) {
    initscr();
    int c = getch();
    nonl();
    switch (c) {
        case KEY_ENTER:
            printw("key: %c", c);
            break;
        case ctrl('j'):
            printw("key: ctrl j");
            break;
    }
    getch();
    endwin();
    return;
}

新代码:

#include <stdio.h>
#include <ncurses.h>
#define ctrl(x)           ((x) & 0x1f)

int main(void) {
    initscr();
    int l = -1;
    int c = getch();
    cbreak();
    noecho();
    nonl();
    keypad(stdscr, TRUE);
    switch (c) {
        case KEY_ENTER:
            printw("key: %c", c);
            break;
        case ctrl('j'):
            printw("key: ctrl j");
            break;
    }
    printw("\nnow press a key to end");
    getch();
    endwin();
    return;
}

【问题讨论】:

    标签: ncurses


    【解决方案1】:

    试试nonl:

    nlnonl 例程控制底层显示设备是否 将 return 键转换为输入时的换行符,以及是否将换行符转换为 return 并在输出时换行(在任何一种情况下, call addch('\n') 相当于在 虚拟屏幕)。最初,确实会发生这些翻译。如果你使用 nonl 禁用它们,curses 将能够更好地利用 换行能力,导致更快的光标移动。还有诅咒 然后将能够检测到返回键。

    延伸阅读:getch 手册页的Notes section

    通常,KEY_ENTER 表示由 Enter 键发送的字符 数字小键盘:

    • 终端描述列出了最有用的键,
    • 常规键盘上的 Enter 键已由 用于回车和换行的标准 ASCII 字符,
    • 取决于调用的是 nl 还是 nonl,在 常规键盘可以返回回车或换行, 最后

    • “输入或发送”是此键的标准描述。

    这解决了关于换行/回车翻译的问题。后续评论提醒您指出手册页在Initialization 部分提供了基本建议:

    在不回显的情况下一次获取一个字符输入(大多数交互性, 面向屏幕的程序需要这个),下面的顺序应该是 使用:

         initscr(); cbreak(); noecho();
    

    并且该 OP 的示例程序没有使用cbreak(或raw)。 cbreak 的手册页说

    通常,tty 驱动程序会缓冲键入的字符,直到换行符或 输入回车。 cbreak 例程禁用行缓冲 和擦除/终止字符处理(中断和流控制字符不受影响),使用户立即输入字符 可用于程序。 nocbreak 例程将终端返回到 正常(熟)模式。

    最初终端可能处于也可能不处于 cbreak 模式,因为该模式是 遗传;因此,程序应显式调用 cbreaknocbreak大多数使用 curses 的交互式程序设置 cbreak 模式。 请注意,cbreak 会覆盖 raw。 (请参阅curs_getch(3x) 进行讨论 这些例程如何与 echonoecho 交互。)

    另外,curs_getch 你可以阅读

    如果 keypad 为 TRUE,并且按下了功能键,则该标记 返回功能键而不是原始字符:

    • 预定义的功能键在&lt;curses.h&gt; 中作为宏列出 值超出 8 位字符范围。他们的名字以 KEY_ 开头。

    也就是说,如果程序调用keypad,curses 只会返回KEY_ENTER

    keypad(stdscr, TRUE);
    

    为了便于讨论,这里有一个示例解决了截至 5 月 17 日您的示例程序存在的一些问题:

    #include <stdio.h>
    #include <ncurses.h>
    #define ctrl(x)           ((x) & 0x1f)
    
    int
    main(void)
    {
        int c;
        initscr();
        keypad(stdscr, TRUE);
        cbreak();
        noecho();
        nonl();
        c = getch();
        switch (c) {
        case KEY_ENTER:
            printw("\nkey_enter: %d", c);
            break;
        case ctrl('j'):
            printw("\nkey: ctrl j");
            break;
        default:
            printw("\nkeyname: %d = %s\n", c, keyname(c));
            break;
        }
        printw("\nnow press a key to end");
        getch();
        endwin();
        return 0;
    }
    

    也就是说,你必须在getch之前调用keypad,并且KEY_ENTER返回的值不是字符(不能用%c打印)。 p>

    使用通常的终端描述在 Linux 控制台上运行,您只会看到数字键盘 Enter 的回车,因为该描述不使用 应用程序模式。 Linux控制台确实支持应用模式,可以写相应的描述。作为快速检查(存在差异...),您可以设置 TERM=vt100 以查看 KEY_ENTER

    【讨论】:

    • 如果我设置了nonl,那么我就不能再捕捉回车键了。我用 ncurses.h 的 KEY_ENTER 宏来捕捉它。编辑:我定义 ctrl 宏的方式正确吗?
    • 再次编辑,添加了一个使用键盘、cbreak 和 noecho 的新示例程序。结果相同:ENTER 被捕获为 C-j..
    • 我的错。如果我将其更改为 TRUE,我会得到相同的结果。 Enter 被捕获为 C-j。
    • 您没有提到 (a) TERM 或 (b) 实际终端(模拟器)。鉴于此,人们可能会指出问题所在。
    • /sbin/agetty,期限=linux。它也发生在 tmux 中。我更新了第二个示例代码。