在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; }
注意事项
- 原生水平滚轮兼容性较差,建议优先使用修饰键+垂直滚轮的方案,保证在大多数终端上可用。
- 编译时需链接ncurses库:
gcc your_code.c -o your_program -lncurses。 - 若终端不支持修饰键与鼠标事件的组合,可能需要调整终端配置(如xterm的资源设置)。
内容的提问来源于stack exchange,提问作者Shreyas Jenner
相关产品推荐
相关产品推荐

