如何在无缓冲/非规范模式下允许退格?

如何在无缓冲/非规范模式下允许退格?

我正在开发多个 C 程序,例如 shell 和文本编辑器,这些程序需要在没有 ECHO 和 ICANON 标志的情况下运行。我使用 termios.h 禁用了这些功能,并设法编写了自己的 gets 函数,该函数可以将返回的字符串中继到我的程序,并对转义字符执行特殊操作。我唯一不能做的就是打印退格键。例如,如果我使用以下代码:

void mgets(char *str)
{
    int c, i = 0;

    while((c = getchar()) != '\n')
        if(c == 27)
            // the user hit ESC, ignore it for now
        else if(c == '\b')
            puts("\b \b") // this is where it SHOULD backspace
        // else if it's a regular character:
        else {
            str+i = c; i++; // write that to the string...
            putchar(c); // ...and echo it to the screen
        }
}

一切都很好,但当我退格时程序没有响应。如果我稍微改变一下 if 语句...

if(c == '\b')
    printf("You hit a backspace!");

但它仍然没有反应。我知道 put("\b \b") 有效,所以唯一的结论是我的退格键没有被检测为 '\b'。我能做些什么?请帮忙?提前致谢!

答案1

您更改 termios 设置,节省他们。该信息与您所看到的“相同”stty -a

例如

$ stty -a
speed 38400 baud; rows 40; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^H; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke

之后显示的值erase是您所说的“退格键”字符。根据系统约定,可能是^H(8 或 ASCII 退格键)或^?(127 或 ASCII DEL)。

在 termios 设置中,例如,

#include <termios.h>
...
struct termios data;
tcgetattr(0, &data);

然后

data.c_cc[VERASE]

是一样的擦除字符(当数据从未设置时有一些警告:stty将显示“undef”)。

与其他一些设置不同,擦除字符在 raw/cbreak 模式中仍然相关,因为它是一个告诉终端驱动程序您的终端预计发送什么内容的设置。如果您的stty设置不正确,测试这两种可能性并没有什么坏处......

进一步阅读:

答案2

我的退格键没有被检测为“\b”。我能做些什么?

(退格)和(删除)键生成的控制或转义序列各不相同。

在尝试类似于 DEC VT 的终端上:

  • 按键发出 DECFNK 控制序列。
  • 的行为可在 ASCII BS 和 DEL 字符之间切换,并向终端发送名为 DECBKM 的控制序列。

但并不是每个终端(模拟器)都尝试像 DEC VT 一样,更不用说实现 DECBKM 机制了。您不能只是硬连线键盘输入序列并期望它们在所有情况下都能工作。

你的程序需要工作的方式是这样的:它需要从环境变量中获取终端类型,从(或)数据库TERM中获取相应的记录,并在那里查找两个功能:terminfotermcap

  • kbs( ) —密钥kb发送的序列
  • kdch1( ) —密钥kD发送的序列

它必须将这些字符串与其读取的输入相匹配。为了区分实际的控制序列和简单的按下,Esc您的程序需要比fgetc()循环更复杂。您必须设置非规范模式并设置(短)读取超时。仅当read()在超时期限内返回整个字符串时,才应将其视为输入控制序列。

请注意,这里设置的特殊字符stty几乎是转移注意力。它们不适用于非规范模式,并且仅影响线路纪律反正。这终端的行为完全取决于它如何将硬件按键事件映射到控制序列,然后将其“沿着线路”发送到线路规程。如前所述,终端可以通过多种方式自由地执行此操作,从使用键码和控制序列(FreeBSD 内置内核终端仿真器)之间的可加载映射到 X 资源。

进一步阅读

相关内容