You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Windows 10控制台TUI程序无法捕获鼠标滚轮事件的问题求助

解决Windows 10控制台TUI程序无法捕获鼠标滚轮事件的问题

我之前开发控制台TUI程序时也碰到过一模一样的状况——鼠标点击、移动都能正常捕获,唯独滚轮事件没反应,哪怕系统本身能滚动控制台内容。其实问题出在控制台输入模式的配置上,你漏加了一个关键的标志位!

问题原因

默认情况下,Windows控制台会把鼠标滚轮事件用来滚动窗口的缓冲区内容,不会把它传递给你的程序。要让程序能接收滚轮事件,必须在调用SetConsoleMode时加上ENABLE_EXTENDED_FLAGS这个标志,它会改变控制台的输入处理逻辑,将滚轮事件转发给你的应用。

修改后的关键代码

只需要修改main函数里设置输入模式的那一行,添加ENABLE_EXTENDED_FLAGS

// 启用窗口、鼠标输入事件,以及扩展标志以接收滚轮事件
fdwMode = ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | ENABLE_EXTENDED_FLAGS;

另外,你还可以在MouseEventProc里添加滚动方向的判断,利用dwButtonState的高字来获取滚轮的增量(标准滚动增量是WHEEL_DELTA = 120,正数表示向上滚动,负数表示向下滚动):

case MOUSE_WHEELED:
{
    int wheelDelta = HIWORD(mer.dwButtonState);
    if (wheelDelta > 0)
        printf("vertical mouse wheel: up\n");
    else
        printf("vertical mouse wheel: down\n");
    break;
}

完整验证后的代码

这里是修改后的完整代码,测试后滚轮事件可以正常触发:

#include <windows.h>
#include <stdio.h>

HANDLE hStdin;
DWORD fdwSaveOldMode;

VOID ErrorExit(LPCSTR);
VOID KeyEventProc(KEY_EVENT_RECORD);
VOID MouseEventProc(MOUSE_EVENT_RECORD);
VOID ResizeEventProc(WINDOW_BUFFER_SIZE_RECORD);

int main(VOID)
{
    DWORD cNumRead, fdwMode, i;
    INPUT_RECORD irInBuf[128];
    int counter=0;

    // 获取标准输入句柄
    hStdin = GetStdHandle(STD_INPUT_HANDLE);
    if (hStdin == INVALID_HANDLE_VALUE)
        ErrorExit("GetStdHandle");

    // 保存当前输入模式,以便退出时恢复
    if (! GetConsoleMode(hStdin, &fdwSaveOldMode) )
        ErrorExit("GetConsoleMode");

    // 启用窗口、鼠标输入事件,以及扩展标志以接收滚轮事件
    fdwMode = ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | ENABLE_EXTENDED_FLAGS;
    if (! SetConsoleMode(hStdin, fdwMode) )
        ErrorExit("SetConsoleMode");

    // 循环读取并处理接下来的100个输入事件
    while (counter++ <= 100)
    {
        // 等待事件
        if (! ReadConsoleInput(
                hStdin,      // 输入缓冲区句柄
                irInBuf,     // 读取目标缓冲区
                128,         // 读取缓冲区大小
                &cNumRead) ) // 已读取的记录数
            ErrorExit("ReadConsoleInput");

        // 将事件分发至对应的处理函数
        for (i = 0; i < cNumRead; i++)
        {
            switch(irInBuf[i].EventType)
            {
                case KEY_EVENT: // 键盘输入
                    KeyEventProc(irInBuf[i].Event.KeyEvent);
                    break;

                case MOUSE_EVENT: // 鼠标输入
                    MouseEventProc(irInBuf[i].Event.MouseEvent);
                    break;

                case WINDOW_BUFFER_SIZE_EVENT: // 屏幕缓冲区调整
                    ResizeEventProc( irInBuf[i].Event.WindowBufferSizeEvent );
                    break;

                case FOCUS_EVENT: // 忽略焦点事件
                case MENU_EVENT:  // 忽略菜单事件
                    break;

                default:
                    ErrorExit("Unknown event type");
                    break;
            }
        }
    }

    // 退出时恢复输入模式
    SetConsoleMode(hStdin, fdwSaveOldMode);
    return 0;
}

VOID ErrorExit (LPCSTR lpszMessage)
{
    fprintf(stderr, "%s\n", lpszMessage);

    // 退出时恢复输入模式
    SetConsoleMode(hStdin, fdwSaveOldMode);

    ExitProcess(0);
}

VOID KeyEventProc(KEY_EVENT_RECORD ker)
{
    printf("Key event: ");
    if(ker.bKeyDown)
        printf("key pressed\n");
    else
        printf("key released\n");
}

VOID MouseEventProc(MOUSE_EVENT_RECORD mer)
{
#ifndef MOUSE_HWHEELED
#define MOUSE_HWHEELED 0x0008
#endif
    printf("Mouse event: ");

    switch(mer.dwEventFlags)
    {
        case 0:
            if(mer.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED)
            {
                printf("left button press \n");
            }
            else if(mer.dwButtonState == RIGHTMOST_BUTTON_PRESSED)
            {
                printf("right button press \n");
            }
            else
            {
                printf("button press\n");
            }
            break;

        case DOUBLE_CLICK:
            printf("double click\n");
            break;

        case MOUSE_HWHEELED:
            printf("horizontal mouse wheel\n");
            break;

        case MOUSE_MOVED:
            printf("mouse moved\n");
            break;

        case MOUSE_WHEELED:
        {
            int wheelDelta = HIWORD(mer.dwButtonState);
            if (wheelDelta > 0)
                printf("vertical mouse wheel: up\n");
            else
                printf("vertical mouse wheel: down\n");
            break;
        }

        default:
            printf("unknown\n");
            break;
    }
}

VOID ResizeEventProc(WINDOW_BUFFER_SIZE_RECORD wbsr)
{
    printf("Resize event\n");
    printf("Console screen buffer is %d columns by %d rows.\n", wbsr.dwSize.X, wbsr.dwSize.Y);
}

注意事项

  • 加上ENABLE_EXTENDED_FLAGS后,控制台原本的滚轮滚动窗口功能会被禁用,滚轮事件完全由你的程序处理。
  • 如果需要同时保留系统的滚动功能和程序捕获事件,这是做不到的——只能二选一,因为系统会优先处理滚轮事件,除非你通过这个标志把它接管过来。

内容的提问来源于stack exchange,提问作者Itsbananas

火山引擎 最新活动