如何在按键后立即读取终端的输入缓冲区
How to read terminal's input buffer immediately after keypress
我想读取我的 c 程序中的箭头按键并用其他字符串替换它们(立即在终端本身中)。我正在尝试像在 unix 终端中那样实现 bash 历史记录功能。我写了这段代码。
int
main(int argc, char *argv[])
{
char c;
char str[1024];
int i = 0;
while((c = fgetc(stdin)) != '\n'){
if(((int)c) == 27){
c=fgetc(stdin);
c=fgetc(stdin);
if (c == 'A')
{
printf("%c[A%c[2K",27, 27);
printf("UP");
}
}
str[i++] = c;
}
printf("\n");
return 0;
}
但是,这不起作用,因为终端等待换行符或 EOF 将输入缓冲区发送到标准输入。所以,我必须按 enter/return 键来分析用户输入。
在此 answer 中,用户提到使用 system ("/bin/stty raw");
但这会替换所有默认的终端行为(例如退格、删除等)。
那么,有什么方法可以 read/manipulate 终端直接输入缓冲区并在检测到箭头按键时调整缓冲区本身?
环境 - Ubuntu (Linux)
更新 1: 有没有一种方法可以更改 signal/interrupt(默认是按回车键)使终端将存储的输入发送到缓冲区?这也可以帮助我实现相同的行为。
最终代码:
我通过检查 strace bash
的输出找出了特定按键的 ASCII 字符
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#define ESCAPE ''
#define BACKSPACE '7'
#define DELETE '~'
int main(){
struct termios termios_p;
struct termios termios_save;
tcgetattr(0, &termios_p);
termios_save = termios_p;
termios_p.c_lflag &= ~(ICANON|ECHO);
tcsetattr(0,TCSANOW, &termios_p);
char buff;
while(read(0, &buff, 1) >= 0){
if(buff > 0){
if(buff == ESCAPE){
read(0, &buff, 1);
read(0, &buff, 1);
if(buff == 'A'){
write(2, "up", 2);
}else if(buff == 'B'){
write(2, "down", 4);
}else if(buff == 'C'){
write(2, "[C", 3);
}else if(buff == 'D'){
write(2, "", 2);
}
}else if(buff == BACKSPACE){
write(2, "[1P", 5);
}else if(buff == DELETE){
write(2, "[1P", 4);
}else{
write(2,&buff,1);
}
// write(2,&buff,1);
if(buff == 'q'){
break;
}
}
}
tcsetattr(0,TCSANOW, &termios_save);
return 0;
}
在我的 Linux 机器上我使用 ncurses:
#include<stdio.h>
#include<ncurses.h>
void checkArrow(){
int ch;
initscr();
raw();
keypad(stdscr, TRUE);
noecho();
printw("Press q to Exit\t");
do{
switch(ch){
case KEY_UP: {
printw("\n\nUp Arrow\n");
printw("Press q to Exit\t");
break;
}
case KEY_DOWN:{
printw("\n\nDown Arrow\n");
printw("Press q to Exit\t");
break;
}
case KEY_LEFT:{
printw("\n\nLeft Arrow\n");
printw("Press q to Exit\t");
break;
}
case KEY_RIGHT:{
printw("\n\nRight Arrow\n");
printw("Press q to Exit\t");
break;
}
}
}while((ch = getch()) != 'q');
printw("\n\n\t\t\t-=GoodBye=-!\n");
refresh();
endwin();
}
int main(){
checkArrow();
return 0;
}
Up Arrow
Press q to Exit
Down Arrow
Press q to Exit
Left Arrow
Press q to Exit
Right Arrow
Press q to Exit
看来您正在寻找这样的东西。
程序本质上是在等待用户输入。如果按下向上箭头键,程序打印 "Arrow key pressed" 然后退出。如果按下其他任何东西,它会等待用户完成他正在输入的内容并打印出来,然后退出。
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main()
{
struct termios oldt, newt;
char ch, command[20];
int oldf;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
while(1)
{
ch = getchar();
if (ch == '3')
{ printf("Arrow key\n"); ch=-1; break;}
else if(ch == -1) // by default the function returns -1, as it is non blocking
{
continue;
}
else
{
break;
}
}
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if(ch != EOF)
{
ungetc(ch,stdin);ith
putchar(ch);
scanf("%s",command);
printf("\n%s\n",command);
return 1;
}
return 0;
}
我想读取我的 c 程序中的箭头按键并用其他字符串替换它们(立即在终端本身中)。我正在尝试像在 unix 终端中那样实现 bash 历史记录功能。我写了这段代码。
int
main(int argc, char *argv[])
{
char c;
char str[1024];
int i = 0;
while((c = fgetc(stdin)) != '\n'){
if(((int)c) == 27){
c=fgetc(stdin);
c=fgetc(stdin);
if (c == 'A')
{
printf("%c[A%c[2K",27, 27);
printf("UP");
}
}
str[i++] = c;
}
printf("\n");
return 0;
}
但是,这不起作用,因为终端等待换行符或 EOF 将输入缓冲区发送到标准输入。所以,我必须按 enter/return 键来分析用户输入。
在此 answer 中,用户提到使用 system ("/bin/stty raw");
但这会替换所有默认的终端行为(例如退格、删除等)。
那么,有什么方法可以 read/manipulate 终端直接输入缓冲区并在检测到箭头按键时调整缓冲区本身?
环境 - Ubuntu (Linux)
更新 1: 有没有一种方法可以更改 signal/interrupt(默认是按回车键)使终端将存储的输入发送到缓冲区?这也可以帮助我实现相同的行为。
最终代码:
我通过检查 strace bash
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#define ESCAPE ''
#define BACKSPACE '7'
#define DELETE '~'
int main(){
struct termios termios_p;
struct termios termios_save;
tcgetattr(0, &termios_p);
termios_save = termios_p;
termios_p.c_lflag &= ~(ICANON|ECHO);
tcsetattr(0,TCSANOW, &termios_p);
char buff;
while(read(0, &buff, 1) >= 0){
if(buff > 0){
if(buff == ESCAPE){
read(0, &buff, 1);
read(0, &buff, 1);
if(buff == 'A'){
write(2, "up", 2);
}else if(buff == 'B'){
write(2, "down", 4);
}else if(buff == 'C'){
write(2, "[C", 3);
}else if(buff == 'D'){
write(2, "", 2);
}
}else if(buff == BACKSPACE){
write(2, "[1P", 5);
}else if(buff == DELETE){
write(2, "[1P", 4);
}else{
write(2,&buff,1);
}
// write(2,&buff,1);
if(buff == 'q'){
break;
}
}
}
tcsetattr(0,TCSANOW, &termios_save);
return 0;
}
在我的 Linux 机器上我使用 ncurses:
#include<stdio.h>
#include<ncurses.h>
void checkArrow(){
int ch;
initscr();
raw();
keypad(stdscr, TRUE);
noecho();
printw("Press q to Exit\t");
do{
switch(ch){
case KEY_UP: {
printw("\n\nUp Arrow\n");
printw("Press q to Exit\t");
break;
}
case KEY_DOWN:{
printw("\n\nDown Arrow\n");
printw("Press q to Exit\t");
break;
}
case KEY_LEFT:{
printw("\n\nLeft Arrow\n");
printw("Press q to Exit\t");
break;
}
case KEY_RIGHT:{
printw("\n\nRight Arrow\n");
printw("Press q to Exit\t");
break;
}
}
}while((ch = getch()) != 'q');
printw("\n\n\t\t\t-=GoodBye=-!\n");
refresh();
endwin();
}
int main(){
checkArrow();
return 0;
}
Up Arrow Press q to Exit
Down Arrow
Press q to Exit
Left Arrow
Press q to Exit
Right Arrow
Press q to Exit
看来您正在寻找这样的东西。
程序本质上是在等待用户输入。如果按下向上箭头键,程序打印 "Arrow key pressed" 然后退出。如果按下其他任何东西,它会等待用户完成他正在输入的内容并打印出来,然后退出。
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main()
{
struct termios oldt, newt;
char ch, command[20];
int oldf;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
while(1)
{
ch = getchar();
if (ch == '3')
{ printf("Arrow key\n"); ch=-1; break;}
else if(ch == -1) // by default the function returns -1, as it is non blocking
{
continue;
}
else
{
break;
}
}
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if(ch != EOF)
{
ungetc(ch,stdin);ith
putchar(ch);
scanf("%s",command);
printf("\n%s\n",command);
return 1;
}
return 0;
}