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

鼠标滚动事件转义序列文档查询及TUI编辑器滚动捕获问询

鼠标滚动事件捕获与终端编码解读问题

问题描述

我想实现类似Vim的TUI编辑器鼠标滚动事件捕获功能,不使用libcurses库。目前遇到以下问题:

  • 向下滚动时终端发送编码看似不固定的\x1b[MaXX,向上滚动时发送\x1b[MXX`,这类编码难以解析
  • 查阅xterm相关文档,但内容和实际观测结果不符
  • 另外发现运行script后启动Vim并滚动,不会出现上述鼠标转义序列

测试用示例代码:

#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

#define ENABLE_ALT_SCREEN "\x1b[?1049h"
#define DISABLE_ALT_SCREEN "\x1b[?1049l"
#define ENABLE_ALT_SCREEN_SZ 8
#define DISABLE_ALT_SCREEN_SZ ENABLE_ALT_SCREEN_SZ
#define ENABLE_MOUSE_TRACKING "\x1b[?1000h"
#define DISABLE_MOUSE_TRACKING "\x1b[?1000l"
#define ENABLE_MOUSE_TRACKING_SZ 8
#define DISABLE_MOUSE_TRACKING_SZ ENABLE_MOUSE_TRACKING_SZ

struct termios orig_termios;

void
disable_raw_mode (void)
{
  tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
  write(STDIN_FILENO, DISABLE_ALT_SCREEN,DISABLE_ALT_SCREEN_SZ);
  write(STDIN_FILENO, DISABLE_MOUSE_TRACKING, DISABLE_MOUSE_TRACKING_SZ);  
}

void
enable_raw_mode (void)
{
  write(STDIN_FILENO, ENABLE_ALT_SCREEN,ENABLE_ALT_SCREEN_SZ);
  write(STDIN_FILENO, ENABLE_MOUSE_TRACKING, ENABLE_MOUSE_TRACKING_SZ);  
  tcgetattr(STDIN_FILENO, &orig_termios);
  atexit(disable_raw_mode);

  struct termios raw = orig_termios;
  raw.c_iflag &= ~(BRKINT | INPCK | PARMRK | INLCR | IGNCR | ISTRIP | ICRNL | IXON);
  //  raw.c_oflag &= ~(OPOST);
  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
  raw.c_cflag &= ~(CSIZE | PARENB);
  raw.c_cflag |= (CS8);


  tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}

int
main (void)
{
  enable_raw_mode();
  
  char c;
  while ( read(STDIN_FILENO,  &c, 1) == 1 && c != 'q')
    {
      if (iscntrl(c))
        printf("%d\n", c);
      else
        printf("%d ('%c')\n", c, c);
    }

  return 0;
}

解决方案与编码解读

1. 基础鼠标模式(1000模式)编码解析

你当前启用的是Xterm基础鼠标跟踪模式(1000模式),滚动事件的编码是固定格式,并非“非确定性”:

  • 完整序列为\x1b[M + 3个字节,共5字节(\x1b是1字节,[和M各1字节,加后续3字节)
  • 第一个后续字节:代表按钮状态与滚动方向
    • 向上滚动:字节值为0x60(对应ASCII反引号),所以序列显示为\x1b[MXX
    • 向下滚动:字节值为0x61(对应ASCII字符a),所以序列显示为\x1b[MaXX
  • 第二个后续字节:鼠标列号(实际列数 = 字节值 - 32,从1开始计数)
  • 第三个后续字节:鼠标行号(实际行数 = 字节值 - 32,从1开始计数)

2. 更易用的扩展鼠标模式(1003模式)

如果觉得基础模式解析麻烦,推荐启用Xterm扩展鼠标模式(1003模式),它发送的序列更直观:

  • 启用命令:\x1b[?1003h
  • 关闭命令:\x1b[?1003l
  • 滚动事件序列格式:
    • 向上滚动:\x1b[<64;x;yM,其中x是列号,y是行号(十进制数字,无需偏移计算)
    • 向下滚动:\x1b[<65;x;yM

3. script命令下无滚动序列的原因

script会创建一个简化的终端环境,默认不支持高级鼠标跟踪功能,Vim检测到该环境后会自动禁用鼠标事件捕获,因此不会产生滚动转义序列。

4. 编码解析的关键注意事项

  • 不要逐字节读取输入:终端发送的是多字节转义序列,需要批量读取后匹配起始标识(如\x1b[M或\x1b[<),再解析后续内容
  • 兼容不同终端:部分终端对鼠标模式的实现略有差异,建议同时处理基础模式和扩展模式的序列

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 11:14:52