使用Termcaps实时获取终端窗口尺寸:窗口Resize时lines与columns值不更新的问题求解
如何通过Termcaps实时获取终端窗口尺寸?
我来帮你解决这个问题——你遇到的核心问题是:tgetnum("li")和tgetnum("co")只会从termcap数据库读取终端的默认初始尺寸,并不会主动追踪窗口大小的实时变化。同样,ncurses的LINES和COLS全局变量也不会自动更新,除非你主动处理窗口变化的信号并触发尺寸刷新。
问题根源
termcap本质上是一个静态的终端特性数据库,tgetnum("li")和"co"对应的是终端的标准默认行数/列数,不是当前运行时终端窗口的实际大小。而ncurses在初始化后,默认不会自动监听窗口调整事件,所以LINES和COLS会一直停留在初始值。
解决方案
要实现实时获取,你需要处理系统发送的SIGWINCH信号(窗口大小改变时系统会触发这个信号),并在信号触发后重新获取当前终端的实际尺寸。这里有两种可行的方案:
方案1:Termcap + ioctl(轻量无ncurses依赖)
ioctl的TIOCGWINSZ命令可以直接从终端设备文件描述符获取当前窗口的实时尺寸,这是最可靠的方式。我们可以结合它和信号处理来实现实时更新:
#include <term.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <signal.h> #include <sys/ioctl.h> // 全局变量存储当前窗口尺寸(信号处理函数中可以访问) int current_lines, current_cols; void handle_window_resize(int sig) { struct winsize win_size; // 从标准输出获取当前窗口尺寸 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win_size) == 0) { current_lines = win_size.ws_row; current_cols = win_size.ws_col; } } int main(void) { char * term_type = getenv("TERM"); int ret; if (!term_type) { write(2, "TERM env must be set\n", 21); return (-1); } if ((ret = tgetent(NULL, term_type)) == -1) { write(2, "Could not access to the termcap database\n", 41); return (-1); } if (!ret) { write(2, "This terminal is not supported by termcaps\n", 43); return (-1); } // 初始化尺寸 handle_window_resize(0); // 注册SIGWINCH信号处理函数,窗口调整时自动触发 signal(SIGWINCH, handle_window_resize); while (1) { sleep(1); printf("%d %d\n", current_lines, current_cols); } return (0); }
方案2:使用ncurses正确更新尺寸
如果你已经依赖ncurses,需要在SIGWINCH信号触发时,让ncurses重新获取窗口尺寸。可以用endwin()+refresh()组合,或者更高效的resizeterm():
#include <ncurses.h> #include <signal.h> #include <unistd.h> void handle_window_resize(int sig) { // 重置ncurses终端状态,重新获取尺寸 endwin(); refresh(); // 也可以用resizeterm,它会直接更新LINES和COLS // resizeterm(LINES, COLS); } int main(void) { // 初始化ncurses initscr(); cbreak(); noecho(); // 注册窗口调整信号处理 signal(SIGWINCH, handle_window_resize); while (1) { sleep(1); // 把尺寸打印到终端左上角 mvprintw(0, 0, "%d %d", LINES, COLS); refresh(); } endwin(); return 0; }
额外注意事项
SIGWINCH信号可能会打断sleep()这类系统调用,如果你需要确保睡眠时长准确,可以在循环中检查sleep()的返回值(返回剩余睡眠秒数),然后补全睡眠。- 如果你不需要termcap的其他终端特性,直接用
ioctl获取尺寸是更轻量的选择,不需要依赖termcap数据库。
内容的提问来源于stack exchange,提问作者Fayeure
相关产品推荐
相关产品推荐

