我使用这个小编辑器作为我正在做的项目的基础:https://github.com/antirez/kilo
编辑器使用rawmode中的终端并使用VT100转义序列进行写入,但是当退出程序时,显示的内容会保持显示状态。 在退出之前......
正如您所看到的那样,提示再次出现,但编辑器剩下的内容会一直存在,直到写完为止。
// Low level terminal handling
void disable_raw_mode(int fd)
{
// dont bother checking the return value as its too late
if (Editor.rawmode) {
tcsetattr(fd, TCSAFLUSH, &orig_termios);
Editor.rawmode = 0;
}
}
void editor_at_exit(void)
{
disable_raw_mode(STDIN_FILENO);
}
int enable_raw_mode(int fd)
{
struct termios raw;
if(Editor.rawmode) return 0; //already enabled
if(!isatty(STDIN_FILENO)) goto fatal;
atexit(editor_at_exit);
if(tcgetattr(fd, &orig_termios) == -1) goto fatal;
raw = orig_termios; // modify the original mode
/* input modes: no break, no CR to NL, no parity check, no strip char,
* * no start/stop output control. */
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
// output modes - disable post processing
raw.c_oflag &= ~(OPOST);
//control modes - set 8 bit chars
raw.c_cflag |= (CS8);
//local modes, choing off, canonical off, no extended functions, no signal chars (, etc)
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
//control chars - set return condition: min number of bytes and a timer
raw.c_cc[VMIN] = 0; // return each byte, or zero for a timeout
raw.c_cc[VTIME] = 1; //100ms timeout
//put terminal in raw mode after flushing
if(tcsetattr(fd, TCSAFLUSH, &raw) < 0) goto fatal;
Editor.rawmode = 1;
return 0;
fatal:
errno = ENOTTY;
return -1;
}
根据我的理解,当程序退出时,调用atexit(editor_at_exit)
函数并在该函数中禁用原始模式。在编辑器打开之前,我将丢失的终端恢复到原来的样子。我不是只想clear
整个终端。
答案 0 :(得分:5)
您正在寻找的功能称为“备用屏幕缓冲区”,它起源于xterm
,但现在大多数终端都支持。
备用屏幕缓冲区旨在为全屏终端程序提供此功能。在正常操作中,输出被添加到回滚缓冲区(大多数终端允许用户滚动回到前一行)。切换到备用屏幕缓冲区时,回滚缓冲区将保持不变,并且备用屏幕缓冲区输出不会添加到回滚缓冲区。从备用屏幕缓冲区返回时,将恢复原始回滚缓冲区状态。这就是像nano
这样的全屏应用程序。
要切换到备用屏幕缓冲区,我建议写(C字符串)
"\033[?1049h\033[2J\033[H"
(15个字符)
到终点站。如果终端仿真器支持备用屏幕缓冲区,则会更改它,清除它并将光标移动到左上角。如果终端仿真器不支持它,则会清除屏幕并将光标移动到左上角。
要从备用屏幕缓冲区返回,我建议写(C字符串)
"\033[2J\033[H\033[?1049l"
(15个字符)
到终点站。如果终端仿真器支持备用屏幕缓冲区,则首先清除备用屏幕缓冲区,然后返回到原始回滚缓冲区(例如nano
)。如果终端仿真器不支持它,则会清除屏幕并将光标移动到左上角。
我推荐这对("\033[?1049h\033[2J\033[H"
和"\033[2J\033[H\033[?1049l"
),因为无论终端模拟器是否支持备用屏幕缓冲区,它都以合理的方式工作,而不是离开全屏应用程序状态之后可见。
如果标准输入是终端,我还建议使用例如
int write_term(const char *p)
{
const char *q = p;
ssize_t n;
int retval = 0, saved_errno;
/* Nothing to write? */
if (!q || !*q)
return 0;
saved_errno = errno;
/* async-signal safe version of q = p + strlen(p) */
while (*q)
q++;
while (p < q) {
n = write(STDIN_FILENO, p, (size_t)(q - p));
if (n > 0) {
p += n;
} else
if (n != -1) {
retval = EIO;
break;
} else
if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
retval = errno;
break;
}
}
errno = saved_errno;
return retval;
}
将字符串写入终端,因为标准C I / O函数可能无法写入标准输入(毕竟只用于读取)。上述功能非常小心,忽略了信号传递(如果标准输入是非阻塞的话,甚至在必要时进行繁忙循环),甚至保持errno
完整无缺;它也是异步信号安全的,这意味着它可以安全地用在信号处理程序中(尽管我建议不要改变终端缓冲模式或信号处理程序中的设置,因为这样做变得非常复杂)。
(OP的代码可能已经实现了一个合适的低级I / O功能,但问题中没有显示。)