C 退出终端模式时,“我的内容”保留在屏幕上

C 退出终端模式时,“我的内容”保留在屏幕上,c,terminal,C,Terminal,我使用这个小编辑器作为我正在做的项目的基础: 编辑器在rawmode模式下使用终端,并使用VT100转义序列进行写入,但是,当退出程序时,显示的内容将保持显示状态。 在退出之前。。。 退出后。。。 如您所见,提示再次出现,但编辑器的剩余内容将保留在那里,直到写入 // Low level terminal handling void disable_raw_mode(int fd) { // dont bother checking the return value as

我使用这个小编辑器作为我正在做的项目的基础:

编辑器在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;
}
//低级终端处理
无效禁用原始模式(int fd)
{
//不要费心检查返回值,因为太晚了
if(Editor.rawmode){
tcsetattr(fd、TCSAFLUSH和orig_termios);
Editor.rawmode=0;
}
}
退出时的无效编辑器(无效)
{
禁用原始模式(标准文件号);
}
int enable_raw_模式(int fd)
{
结构termios raw;
if(Editor.rawmode)返回0;//已启用
如果(!isatty(STDIN_FILENO))转到致命;
atexit(编辑在退出时);
如果(tcgetattr(fd,&orig_termios)=-1)转到致命;
raw=orig_termios;//修改原始模式
/*输入模式:无中断、无CR到NL、无奇偶校验、无带字符、,
**无启动/停止输出控制*/
原始c|u iflag&=~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
//输出模式-禁用后处理
拉格的原始c_&=~(OPOST);
//控制模式-设置8位字符
原始c|u cflag |=(CS8);
//本地模式、选择关闭、规范关闭、无扩展函数、无信号字符(,等等)
raw.c|lflag&=~(ECHO | ICANON | IEXTEN | ISIG);
//控制字符-设置返回条件:最小字节数和计时器
raw.c_cc[VMIN]=0;//返回每个字节,或0表示超时
原始.c_cc[VTIME]=1;//100ms超时
//冲洗后,将终端置于原始模式
如果(tcsetattr(fd、TCSAFLUSH和raw)<0)转到致命;
Editor.rawmode=1;
返回0;
致命的:
errno=ENOTTY;
返回-1;
}

据我所知,当程序退出时,调用
atexit(编辑器在退出时)
函数,并在该函数中禁用原始模式。将终端清理回编辑器打开前的状态,我缺少了什么。我不想只清除整个终端。

您要寻找的功能称为“备用屏幕缓冲区”,它起源于
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;
}
int写入项(常量字符*p)
{
常量字符*q=p;
(三);
int retval=0,已保存\u errno;
/*没什么要写的吗?*/
如果(!q | |!*q)
返回0;
保存的\u errno=errno;
/*异步信号安全版本q=p+strlen(p)*/
while(*q)
q++;
而(p0){
p+=n;
}否则
如果(n!=-1){
retval=EIO;
打破
}否则
if(errno!=EINTR&&errno!=EAGAIN&&errno!=ewoldblock){
retval=errno;
打破
}
}
errno=已保存\u errno;
返回返回;
}
将字符串写入终端,因为标准C I/O函数可能无法写入标准输入(毕竟,该函数仅用于读取)。上述函数非常小心,忽略了信号传递(如果标准输入为非阻塞输入,则在必要时甚至忙于循环),甚至保持
errno
的完整性;它也是异步信号安全的,这意味着它可以在信号处理程序中安全地使用(尽管我建议不要更改信号处理程序中的终端缓冲区模式或设置,因为这样做会变得非常复杂)


(OP的代码可能已经实现了一个合适的低级I/O功能,但问题中没有显示。)

您正在寻找的功能称为“备用屏幕缓冲区”,它起源于
xterm
,但现在大多数终端都支持它

备用屏幕缓冲区的设计正是为了提供这一功能