使用ioctl不会更新终端窗口大小

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用ioctl不会更新终端窗口大小相关的知识,希望对你有一定的参考价值。

我在VM中使用vanilla Ubuntu。有问题的代码:

#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>

int main(void){
   struct winsize w;

   ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
   printf("lines %d
", w.ws_row);
   printf("columns %d
", w.ws_col);

   printf("33[8;40;100t");

   ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
   printf("lines %d
", w.ws_row);
   printf("columns %d
", w.ws_col);

   return 0;
}

当我编译并运行它时,打印出原始终端窗口大小,然后将窗口大小调整为40x100,但最后的printf行不反映新的终端窗口大小。

这里发生了什么,如何获得更新的终端窗口大小信息?

答案

你的代码中的一个问题是显而易见的,在printf("33[8;40;100t");之后你没有刷新stdout。因此,当你调用第二个ioctl()时,那些转义序列仍然在stdout的缓冲区中。

但是,即使你最多添加一个fflush(stdout);,结果也没有变化。因为存在一个竞争条件,即窗口是先调整大小还是首先调用下一个ioctl()。即使你添加一个tcdrain()仍然如此。

如果您为SIGWINCH安装信号处理程序,则可以验证这一点。

#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <termios.h>
#include <string.h>

void winsz_handler(int sig) {
    const char *s = "winsize changed!
";
    write(STDOUT_FILENO, s, strlen(s));
}

int main(void){
   signal(SIGWINCH, winsz_handler);

   struct winsize w;

   ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
   printf("lines %d
", w.ws_row);
   printf("columns %d
", w.ws_col);

   printf("33[8;40;100t");
   fflush(stdout);
   tcdrain(STDOUT_FILENO);

   // remove this comment, then it works on my machine
   // However, it's ad hoc because there is no garantee
   //usleep(100000);

   ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
   printf("lines %d
", w.ws_row);
   printf("columns %d
", w.ws_col);
   sleep(1);

   return 0;
}

“winsize changed”输出是最后一个。如果注释中的usleep()被删除,那么它可以在我的机器上运行。当然,对于竞争条件来说,这不是一个好的解决方案。

以上是关于使用ioctl不会更新终端窗口大小的主要内容,如果未能解决你的问题,请参考以下文章

在 c 中为 Windows 获取终端大小?

GMSMarker 信息窗口内容(片段)未更新

c_cpp 通过IOCTL获取终端大小

除非我调整窗口大小,否则 QML 文本不会更新

X11 Window 不会自动更新/绘图

出现键盘后调整活动大小