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

在C语言ncurses库中,如何检测鼠标/触摸板的水平滚动?

解决方案

一、检测鼠标水平滚动

ncurses对水平滚轮的支持依赖终端环境:

  • 部分终端会将水平滚动触发BUTTON6_PRESSED(向左滚)和BUTTON7_PRESSED(向右滚)事件,但这并非标准行为,需要终端的terminfo定义包含对应条目。
  • 使用前需将这两个事件加入mousemask,例如mousemask(ALL_MOUSE_EVENTS | BUTTON6_PRESSED | BUTTON7_PRESSED, NULL);(注:ALL_MOUSE_EVENTS可能已包含相关事件,显式添加是为了兼容性)。

但要注意,多数终端(如默认xterm)不支持原生水平滚轮事件,这种情况下建议用修饰键+垂直滚轮的方案替代,兼容性更强。

二、检测滚动时的修饰键

MEVENT结构体的bstate字段可结合修饰键掩码判断,支持的掩码包括:

  • MOD_SHIFT:Shift键
  • MOD_CTRL:Ctrl键
  • MOD_ALT:Alt键

通过event.bstate & MOD_SHIFT这类判断,就能检测滚动时是否按住了对应修饰键,以此触发水平滚动逻辑。

修改后的示例代码

以下代码整合了两种方案,新增水平偏移变量pad_scroll_x,同时支持原生水平滚轮和Shift+垂直滚轮触发水平滚动:

#include <ncurses.h>
#include <stdio.h>

#define MAX_FILES 100
#define FILE_NAME_SZ 100

int main() {
    initscr();              
    cbreak();               
    noecho();               
    keypad(stdscr, TRUE);   
    // 包含水平滚轮事件和修饰键检测
    mousemask(ALL_MOUSE_EVENTS | BUTTON6_PRESSED | BUTTON7_PRESSED, NULL);  

    int pad_ht, pad_width, pad_start_y, pad_start_x, pad_end_y, pad_end_x;
    int pad_scroll_y = 0;
    int pad_scroll_x = 0; // 新增水平偏移变量
    int matches = MAX_FILES;  
    char filelist[MAX_FILES][FILE_NAME_SZ];
    int ch;
    MEVENT event;
    WINDOW *pad;

    /* 模拟长文件名的文件列表,用于测试水平滚动 */
    for (int i = 0; i < matches; i++) {
        snprintf(filelist[i], FILE_NAME_SZ, "file_%03d_long_demo_name_for_horizontal_scroll_test.txt", i);
    }

    /* 初始化pad参数 */
    pad_ht = matches;
    pad_width = 2 * getmaxx(stdscr); // 加宽pad,让内容超出显示区域
    pad_start_y = 0;
    pad_start_x = getmaxx(stdscr) / 4;
    pad_end_x = getmaxx(stdscr);
    pad_end_y = 3 * (getmaxy(stdscr) / 4);
    int visible_width = pad_end_x - pad_start_x;

    /* 创建pad并启用滚动 */
    pad = newpad(pad_ht, pad_width);
    scrollok(pad, TRUE);

    /* 将文件列表打印到pad */
    wmove(pad, 0, 0);
    for (int i = 0; i < matches; i++) {
        wprintw(pad, "%d: %s\n", i, filelist[i]);
    }

    prefresh(pad, pad_scroll_y, pad_scroll_x, pad_start_y, pad_start_x, pad_end_y - 1, pad_end_x - 1);

    /* 循环处理事件 */
    while (1) {
        ch = getch();   

        if (ch == KEY_MOUSE) {
            if (getmouse(&event) == OK) {
                // 原生水平滚动:向左
                if (event.bstate & BUTTON6_PRESSED) {
                    if (pad_scroll_x > 0)
                        --pad_scroll_x;
                    prefresh(pad, pad_scroll_y, pad_scroll_x, pad_start_y, pad_start_x, pad_end_y - 1, pad_end_x - 1);
                }
                // 原生水平滚动:向右
                else if (event.bstate & BUTTON7_PRESSED) {
                    int max_scroll_x = pad_width - visible_width;
                    if (pad_scroll_x < max_scroll_x)
                        ++pad_scroll_x;
                    prefresh(pad, pad_scroll_y, pad_scroll_x, pad_start_y, pad_start_x, pad_end_y - 1, pad_end_x - 1);
                }
                // Shift+向上滚轮:触发向左水平滚动
                else if ((event.bstate & BUTTON4_PRESSED) && (event.bstate & MOD_SHIFT)) {
                    if (pad_scroll_x > 0)
                        --pad_scroll_x;
                    prefresh(pad, pad_scroll_y, pad_scroll_x, pad_start_y, pad_start_x, pad_end_y - 1, pad_end_x - 1);
                }
                // Shift+向下滚轮:触发向右水平滚动
                else if ((event.bstate & BUTTON5_PRESSED) && (event.bstate & MOD_SHIFT)) {
                    int max_scroll_x = pad_width - visible_width;
                    if (pad_scroll_x < max_scroll_x)
                        ++pad_scroll_x;
                    prefresh(pad, pad_scroll_y, pad_scroll_x, pad_start_y, pad_start_x, pad_end_y - 1, pad_end_x - 1);
                }
                // 普通垂直滚动逻辑
                else if (event.bstate & BUTTON4_PRESSED) {
                    if (pad_scroll_y > 0)
                        --pad_scroll_y;
                    prefresh(pad, pad_scroll_y, pad_scroll_x, pad_start_y, pad_start_x, pad_end_y - 1, pad_end_x - 1);
                } else if (event.bstate & BUTTON5_PRESSED) {
                    int max_scroll_y = matches - (pad_end_y - pad_start_y);
                    if (pad_scroll_y < max_scroll_y)
                        ++pad_scroll_y;
                    prefresh(pad, pad_scroll_y, pad_scroll_x, pad_start_y, pad_start_x, pad_end_y - 1, pad_end_x - 1);
                }
            }
        } else if (ch == 'q') {
            break;  
        }
    }

    delwin(pad);
    endwin();  

    return 0;
}

注意事项

  1. 原生水平滚轮兼容性较差,建议优先使用修饰键+垂直滚轮的方案,保证在大多数终端上可用。
  2. 编译时需链接ncurses库:gcc your_code.c -o your_program -lncurses。
  3. 若终端不支持修饰键与鼠标事件的组合,可能需要调整终端配置(如xterm的资源设置)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 11:34:55