Windows下使用ncurses检测终端窗口大小变化的方法
Windows下ncurses终端窗口大小检测与重绘方案
问题说明
用ncurses开发Windows终端应用时,调整窗口会导致界面错乱,现有方案均失效:
- Windows无
SIGWINCH信号,窗口调整不触发KEY_RESIZE getmaxx/getmaxy始终返回终端初始尺寸,不会随窗口更新- 调用
initscr()后,原本有效的GetConsoleBufferInfo也无法获取正确窗口大小(测试代码如下)
void getConsoleSize(int *w, int *h){ CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); *w = csbi.srWindow.Right - csbi.srWindow.Left + 1; *h = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; }
可行解决办法
1. 轮询检测+强制更新ncurses尺寸
主动在主循环中定期检查终端实际尺寸,发现变化后调用resize_term更新ncurses内部状态,再重绘界面:
#include <ncurses.h> #include <windows.h> BOOL getCurrentConsoleSize(int *cols, int *rows) { HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); if (hOut == INVALID_HANDLE_VALUE) return FALSE; CONSOLE_SCREEN_BUFFER_INFO csbi; if (!GetConsoleScreenBufferInfo(hOut, &csbi)) return FALSE; *cols = csbi.srWindow.Right - csbi.srWindow.Left + 1; *rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; return TRUE; } int main() { initscr(); cbreak(); noecho(); keypad(stdscr, TRUE); int last_cols = getmaxx(stdscr); int last_rows = getmaxy(stdscr); int current_cols, current_rows; while (1) { if (getCurrentConsoleSize(¤t_cols, ¤t_rows)) { if (current_cols != last_cols || current_rows != last_rows) { resize_term(current_rows, current_cols); clear(); // 这里替换为你的界面重绘逻辑 box(stdscr, 0, 0); mvprintw(1, 1, "当前尺寸: %dx%d", current_cols, current_rows); refresh(); last_cols = current_cols; last_rows = current_rows; } } // 处理用户输入,非阻塞模式减少CPU占用 nodelay(stdscr, TRUE); int ch = getch(); if (ch == 'q') break; nodelay(stdscr, FALSE); Sleep(100); } endwin(); return 0; }
2. 监听Windows窗口消息
通过接管控制台的消息循环,监听WM_SIZE事件实现实时响应,无需轮询:
#include <ncurses.h> #include <windows.h> HWND console_hwnd; int current_cols, current_rows; BOOL getCurrentConsoleSize(int *cols, int *rows) { HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); if (hOut == INVALID_HANDLE_VALUE) return FALSE; CONSOLE_SCREEN_BUFFER_INFO csbi; if (!GetConsoleScreenBufferInfo(hOut, &csbi)) return FALSE; *cols = csbi.srWindow.Right - csbi.srWindow.Left + 1; *rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; return TRUE; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_SIZE: getCurrentConsoleSize(¤t_cols, ¤t_rows); resize_term(current_rows, current_cols); clear(); box(stdscr, 0, 0); mvprintw(1, 1, "当前尺寸: %dx%d", current_cols, current_rows); refresh(); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; } int main() { initscr(); cbreak(); noecho(); keypad(stdscr, TRUE); console_hwnd = GetConsoleWindow(); if (!console_hwnd) { endwin(); return 1; } WNDCLASSEX wc = {0}; wc.cbSize = sizeof(WNDCLASSEX); wc.lpfnWndProc = WndProc; wc.hInstance = GetModuleHandle(NULL); wc.lpszClassName = L"NcursesConsoleClass"; RegisterClassEx(&wc); HWND hidden_hwnd = CreateWindowEx(0, L"NcursesConsoleClass", L"", 0, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL), NULL); if (!hidden_hwnd) { endwin(); return 1; } getCurrentConsoleSize(¤t_cols, ¤t_rows); box(stdscr, 0, 0); mvprintw(1, 1, "当前尺寸: %dx%d", current_cols, current_rows); refresh(); MSG msg; while (1) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } nodelay(stdscr, TRUE); int ch = getch(); if (ch == 'q') break; nodelay(stdscr, FALSE); Sleep(50); } DestroyWindow(hidden_hwnd); UnregisterClass(L"NcursesConsoleClass", GetModuleHandle(NULL)); endwin(); return 0; }
3. 更换为PDCurses实现
PDCurses是专门针对Windows优化的ncurses移植版本,原生支持窗口resize事件,无需额外处理:
#include <curses.h> int main() { initscr(); cbreak(); noecho(); keypad(stdscr, TRUE); int cols, rows; getmaxyx(stdscr, rows, cols); while (1) { int ch = getch(); if (ch == KEY_RESIZE) { getmaxyx(stdscr, rows, cols); clear(); box(stdscr, 0, 0); mvprintw(1, 1, "当前尺寸: %dx%d", cols, rows); refresh(); } else if (ch == 'q') { break; } } endwin(); return 0; }
内容的提问来源于stack exchange,提问作者Rhuamer
相关产品推荐
相关产品推荐

