Backspace(\b) 不清除非规范模式 termios 中的文本
Backspace(\b) not clearing text in Non-canonical mode termios
我正在尝试通过按退格键来清除文本,我在非规范模式下使用 termios。我创建了一个条件语句,当用户按下退格键时,它应该通过向后退一个字符来删除前一个字符。
但是当我按下 Backspace 而不是删除字符时,它会在该行打印 ^?
。
我不想使用规范模式。
我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#define MAX_COMMANDS 1000
#define MAX_LENGTH 200
static struct termios initial_settings, new_settings;
static int peek_character = -1;
void init_keyboard();
void close_keyboard();
int kbhit();
int readch();
void run(char inp[]) {
system(inp);
}
int main() {
//65 = UP
//66 = DOWN
//Backspace = 127
int ch;
char str[MAX_COMMANDS][MAX_LENGTH];
init_keyboard();
int i = 0;
int j = 0;
while(ch != 'q') {
if(kbhit()) {
ch = readch();
if (ch == 127) {
const char delbuf[] = "\b \b";
write(STDOUT_FILENO, delbuf, strlen(delbuf));
}
if (ch == 10) {
run(str[i]);
i++;
j = 0;
} else {
str[i][j] = ch;
j++;
}
}
}
close_keyboard();
exit(0);
}
void init_keyboard() {
tcgetattr(0, &initial_settings);
new_settings = initial_settings;
new_settings.c_lflag &= (ECHO | ECHOE | ~ICANON);
new_settings.c_lflag &= ~ISIG;
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VMIN] = 0;
tcsetattr(0, TCSANOW, &new_settings);
}
void close_keyboard() {
tcsetattr(0, TCSANOW, &initial_settings);
}
int kbhit() {
char ch;
int nread;
if (peek_character != -1) {
return 1;
}
new_settings.c_cc[VMIN] = 0;
tcsetattr(0, TCSANOW, &new_settings);
nread = read(0, &ch,1);
new_settings.c_cc[VMIN]=1;
tcsetattr(0, TCSANOW, &new_settings);
if (nread == 1) {
peek_character = ch;
return 1;
}
return 0;
}
int readch() {
char ch;
if (peek_character != -1) {
ch = peek_character;
peek_character = -1;
return ch;
}
read(0, &ch,1);
return ch;
}
i am trying to clear text by pressing the backspace key, i am using termios in non canonical mode.
您的 termios 初始化有一些错误(这只是代码中问题的一部分)。
来自 init_keyboard() 的以下陈述是不合逻辑的:
new_settings.c_lflag &= (ECHO | ECHOE | ~ICANON);
推测您打算禁用 ICANON 属性(即使用非规范输入)。
您打算启用 ECHO 和 ECHOE 属性吗? (根据您代码的其他部分,我会这么认为。)
此语句不启用 ECHO 和 ECHOE 属性,但最终只是保留它们已经处于的任何状态。
这是一个潜在的错误,运气不好,可能永远不会被触发。
由于以下两个语句使用相同的索引 VMIN,其中一个不应该改为索引 VTIME 吗?
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VMIN] = 0;
i have created a conditional statement that when the users press backspace it should remove the previous character by going one character back.
请注意,您的代码仅尝试“删除显示的前一个字符”。
没有尝试从输入缓冲区中“删除前一个字符”。
这可能是另一个错误。
But when i press Backspace instead of removing the character it prints ^? on that line.
这似乎不是对代码功能的准确报告。
当我在 PC 终端 window(LXTerminal 0.2.0)中执行您的代码时,我最终只看到 ^
而不是您为退格键报告的 ^?
关键。
明确地说,Backspace 键是 echoed/displayed 和 ^?
(因为默认情况下启用 ECHOCTL),但是您的程序会尝试执行额外的处理以响应该特定输入,结果只显示 ^
.
您的代码假定每个输入字符都作为单个字符回显(即显示)。
这是一个错误的假设。
生成 ASCII 控制代码而不是可打印字符的密钥将 echoed/displayed 作为两个字符(如您所报告的那样)。
无法轻松映射到 ASCII 代码的 "special" 键(来自 PC 键盘)将被读取为转义序列,即几个(3 到 5 个或更多)字符的序列。
如果您希望 "erase" 或覆盖前一个字符以及回显的退格键,那么您的代码将需要返回(至少)三 (3) 个字符。
换句话说
const char delbuf[] = "\b \b";
需要
const char delbuf[] = "\b\b\b \b\b\b";
请注意,这不是一个完整的解决方案,因为它只能 "remove" 单个前一个字符,前一个控制字符或转义序列将无法正确处理。
这个答案只是为了解释为什么你没有得到你期望的结果。
请注意,本地回声(由 termios)的使用可能被认为是问题的一部分,因为您似乎没有掌握后果。
非规范模式通常会抑制任何自动回显,以便简化控制字符和转义序列的处理。
仅供参考,如果没有自动回显,那么您尝试使用的 backspace, space, backspace
序列可能有效(因为只有前一个字符要覆盖,并且不会有退格键的回显)。
请注意,您的代码使用 busy wait 到 check/retrieve 键盘输入(因为 VTIME 可能设置为 0 且 VMIN 为 0)。
这是浪费 CPU 个周期的 最 低效的输入方法,并且在电池供电的系统上耗尽电池的速度比必要的更快。
即使您添加了延迟,代码仍然会低效地 轮询 数据的系统缓冲区,而不是利用由OS。
我正在尝试通过按退格键来清除文本,我在非规范模式下使用 termios。我创建了一个条件语句,当用户按下退格键时,它应该通过向后退一个字符来删除前一个字符。
但是当我按下 Backspace 而不是删除字符时,它会在该行打印 ^?
。
我不想使用规范模式。
我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#define MAX_COMMANDS 1000
#define MAX_LENGTH 200
static struct termios initial_settings, new_settings;
static int peek_character = -1;
void init_keyboard();
void close_keyboard();
int kbhit();
int readch();
void run(char inp[]) {
system(inp);
}
int main() {
//65 = UP
//66 = DOWN
//Backspace = 127
int ch;
char str[MAX_COMMANDS][MAX_LENGTH];
init_keyboard();
int i = 0;
int j = 0;
while(ch != 'q') {
if(kbhit()) {
ch = readch();
if (ch == 127) {
const char delbuf[] = "\b \b";
write(STDOUT_FILENO, delbuf, strlen(delbuf));
}
if (ch == 10) {
run(str[i]);
i++;
j = 0;
} else {
str[i][j] = ch;
j++;
}
}
}
close_keyboard();
exit(0);
}
void init_keyboard() {
tcgetattr(0, &initial_settings);
new_settings = initial_settings;
new_settings.c_lflag &= (ECHO | ECHOE | ~ICANON);
new_settings.c_lflag &= ~ISIG;
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VMIN] = 0;
tcsetattr(0, TCSANOW, &new_settings);
}
void close_keyboard() {
tcsetattr(0, TCSANOW, &initial_settings);
}
int kbhit() {
char ch;
int nread;
if (peek_character != -1) {
return 1;
}
new_settings.c_cc[VMIN] = 0;
tcsetattr(0, TCSANOW, &new_settings);
nread = read(0, &ch,1);
new_settings.c_cc[VMIN]=1;
tcsetattr(0, TCSANOW, &new_settings);
if (nread == 1) {
peek_character = ch;
return 1;
}
return 0;
}
int readch() {
char ch;
if (peek_character != -1) {
ch = peek_character;
peek_character = -1;
return ch;
}
read(0, &ch,1);
return ch;
}
i am trying to clear text by pressing the backspace key, i am using termios in non canonical mode.
您的 termios 初始化有一些错误(这只是代码中问题的一部分)。
来自 init_keyboard() 的以下陈述是不合逻辑的:
new_settings.c_lflag &= (ECHO | ECHOE | ~ICANON);
推测您打算禁用 ICANON 属性(即使用非规范输入)。
您打算启用 ECHO 和 ECHOE 属性吗? (根据您代码的其他部分,我会这么认为。)
此语句不启用 ECHO 和 ECHOE 属性,但最终只是保留它们已经处于的任何状态。
这是一个潜在的错误,运气不好,可能永远不会被触发。
由于以下两个语句使用相同的索引 VMIN,其中一个不应该改为索引 VTIME 吗?
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VMIN] = 0;
i have created a conditional statement that when the users press backspace it should remove the previous character by going one character back.
请注意,您的代码仅尝试“删除显示的前一个字符”。
没有尝试从输入缓冲区中“删除前一个字符”。
这可能是另一个错误。
But when i press Backspace instead of removing the character it prints ^? on that line.
这似乎不是对代码功能的准确报告。
当我在 PC 终端 window(LXTerminal 0.2.0)中执行您的代码时,我最终只看到 ^
而不是您为退格键报告的 ^?
关键。
明确地说,Backspace 键是 echoed/displayed 和 ^?
(因为默认情况下启用 ECHOCTL),但是您的程序会尝试执行额外的处理以响应该特定输入,结果只显示 ^
.
您的代码假定每个输入字符都作为单个字符回显(即显示)。
这是一个错误的假设。
生成 ASCII 控制代码而不是可打印字符的密钥将 echoed/displayed 作为两个字符(如您所报告的那样)。
无法轻松映射到 ASCII 代码的 "special" 键(来自 PC 键盘)将被读取为转义序列,即几个(3 到 5 个或更多)字符的序列。
如果您希望 "erase" 或覆盖前一个字符以及回显的退格键,那么您的代码将需要返回(至少)三 (3) 个字符。
换句话说
const char delbuf[] = "\b \b";
需要
const char delbuf[] = "\b\b\b \b\b\b";
请注意,这不是一个完整的解决方案,因为它只能 "remove" 单个前一个字符,前一个控制字符或转义序列将无法正确处理。
这个答案只是为了解释为什么你没有得到你期望的结果。
请注意,本地回声(由 termios)的使用可能被认为是问题的一部分,因为您似乎没有掌握后果。
非规范模式通常会抑制任何自动回显,以便简化控制字符和转义序列的处理。
仅供参考,如果没有自动回显,那么您尝试使用的 backspace, space, backspace
序列可能有效(因为只有前一个字符要覆盖,并且不会有退格键的回显)。
请注意,您的代码使用 busy wait 到 check/retrieve 键盘输入(因为 VTIME 可能设置为 0 且 VMIN 为 0)。
这是浪费 CPU 个周期的 最 低效的输入方法,并且在电池供电的系统上耗尽电池的速度比必要的更快。
即使您添加了延迟,代码仍然会低效地 轮询 数据的系统缓冲区,而不是利用由OS。