You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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(&current_cols, &current_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(&current_cols, &current_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(&current_cols, &current_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 00:27:05