命令行和转义序列的C++输入

zujrkrfu  于 2023-05-02  发布在  其他
关注(0)|答案(1)|浏览(169)

我有一个C++程序(在Mac和Linux上运行),可以使用ANSI代码在终端上显示图像。它提示输入命令行,然后输入gets()。我输入一行,后面跟着RETURN,它将解析字符串并相应地刷新显示。有单字符命令和带参数的命令。一个提示符可以有多个参数。
如果我按下箭头键,我会在提示符中看到转义序列。例如,UP_ARROW显示为^[[A^[ESC字符)。因此,我添加了对转义序列的支持:^[[A将图像向上滚动窗口高度的三分之一。直到我按下RETURN才发生滚动,当它发生时,滚动发生在相对于命令行中任何其他指令的正确位置。
我现在已经发现我可以在X终端上从我的鼠标中获得转义序列。我可以点击我的图像,并得到鼠标的位置,鼠标按下和鼠标上升,或移动的车轮。这给了我大量的转义码,快速填充了一行提示符。
我想像以前一样提示输入一行数据,但是如果第一个字符是ESC,我想立即执行命令而不等待返回。这可以让所有的箭头键和鼠标命令都像它们出现时一样快。
我花了一个上午的时间,从get()ungetc()开始,然后是termiossetvbuf等等。似乎我可以有行缓冲,在那里我看到我键入的内容,删除键起作用,我可以剪切和粘贴;或者我在人物进来的时候读他们,在这种情况下,我必须手工做所有其他的事情。我使用这个工具进行调试,所以我的目标是使用我可以信任的标准例程来实现一个简单、笨拙的解决方案;而不是看起来很优雅但可能有bug的东西。这就是为什么我一直忍受命令行中的转义代码。
我是不是错过了什么把戏?或者有人可以明确地告诉我,没有办法用标准库偷看行缓冲区的第一个字符。

33qvvth1

33qvvth11#

下面是我用来开发可能的解决方案的测试程序。..

#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>

int getLine(char *buf, int size, const char *prompt)
{
    int length = 0;
    unsigned char c;
    buf[0] = '\0';

    for (;;) {
    // Refresh line display...
    fprintf(stdout, " \r%s%s", prompt, buf);
    fflush(stdout);

    // Handle new key...
    if (read(0, &c, 1)) {
        switch(c) {
        case '\n': // RETURN
        case 13:   // RETURN
        printf("\e[2K\r");  
        return length;
        break;
        case 127:  // DEL
        printf("\e[2K\r");  
        if (length) buf[--length] = '\0';
        break;
        default:
        buf[length++] = c;
        if (length == size) --length;
        line[length] = '\0';
        break;
        }
    } else {
        if (buf[0] == '\e') {
        printf("\e[2K\r");  
        return length;
        }
    }
    }

    // Should not get here...
    return 0;
}

int main(int argc, char* argv[])
{
  char buf[128];

  struct termios tio, tio_init;
  tcgetattr(0, &tio_init);    
  tio = tio_init;
  tio.c_lflag &= ~(ECHO | ICANON); 
  tio.c_cc[VMIN] = 0; 
  tio.c_cc[VTIME] = 1; 
  tcsetattr(0, TCSANOW, &tio);

  int length = getLine(buf, 128, "-prompt->");
  printf("string=<%s> length %d\n", buf, length);

  tcsetattr(0, TCSANOW, &tio_init);
}

我能想到的最好的方法是一次读入一个字符,如果第一个字符是ESC,则允许字符串超时完成。在我的实际程序中,如果最后一个字符串以ESC开头,我使用这个,否则使用fgets()。我已经把最低限度的编辑(DEL),因为大多数时候,当我打字,我会有fgets()。在我的程序中,我把tcsetattr()调用放在了getLine()附近,这样就不会有丢失输入字符的风险。
不是很好有足够的空间来做更好的事情。

相关问题