feat(terminal): mouse reporting (P2)
Implement xterm-compatible mouse reporting so vim, htop, btm, tmux, etc. can receive click / drag / wheel events. Core: - Expose `mouse_protocol_mode()` and `mouse_protocol_encoding()` on `TerminalCore`, re-exporting the vt100 enums. Component: - Add `MouseAction`, `mouse_button_base`, `with_mouse_modifiers`, `encode_mouse_report`, and `pixel_to_grid` pure helpers. - Support SGR (1006), Default, and UTF-8 encodings. SGR is fully correct and ASCII-only; Default/UTF-8 are correct for the common ≤94 col/row range. - Wire `on:mousedown` / `on:mouseup` / `on:mousemove` to report mouse events when the app enabled mouse reporting; otherwise fall back to local text selection. - Shift+click bypasses mouse reporting and forces local selection (xterm behaviour). - Throttle motion reports to one per grid cell. - Report vertical/horizontal wheel ticks as buttons 64/65/66/67 when mouse reporting is active, otherwise keep the existing alt-screen arrow-key paging. Tests: 23/23 pass, clippy clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -31,8 +31,9 @@
|
||||
| 基础按键:Ctrl-C/D/L、方向键、Enter/Tab/Esc/Backspace | `component.rs::map_key_to_terminal_input` |
|
||||
| 完整键表(F1–F12/Home/End/PageUp/Down/Ctrl A–Z/Alt-key) | `component.rs::key_to_bytes` |
|
||||
| application-cursor 模式(DECCKM,方向键切 SS3) | `component.rs::cursor_seq` + `TerminalCore::application_cursor` |
|
||||
| 粘贴(Ctrl-V / 浏览器菜单,含 bracketed paste) | `component.rs::handle_paste` / `prepare_paste` |
|
||||
| 文本选区 + 复制(自建模型,Ctrl-Shift-C,跨滚动) | `component.rs` 鼠标处理 / `core.rs::selection_text` |
|
||||
| 粘贴(Ctrl-V / Ctrl-Shift-V / 右键菜单,含 bracketed paste) | `component.rs::handle_paste` / `prepare_paste` |
|
||||
| 文本选区 + 复制(自建模型,Ctrl-Shift-C / 选区存在时 Ctrl+C,跨滚动) | `component.rs` 鼠标处理 / `core.rs::selection_text` |
|
||||
| 鼠标上报(vim/htop/tmux 鼠标,SGR/Default/UTF-8 编码,滚轮) | `component.rs` 鼠标处理 / `encode_mouse_report` |
|
||||
|
||||
---
|
||||
|
||||
@@ -83,21 +84,28 @@
|
||||
|
||||
---
|
||||
|
||||
### P2 — 鼠标上报(mouse reporting)
|
||||
### P2 — 鼠标上报(mouse reporting) ✅ 已完成
|
||||
|
||||
**现状**:没有。点击 / 拖拽位置不会发给应用,vim 鼠标、tmux 选区、btm 点击都用不了。
|
||||
**现状**:已实现并验证。`vim`/`htop`/`btm`/`tmux` 的鼠标交互、tmux 选区、vim 滚轮均可以工作。
|
||||
|
||||
**计划实现**:
|
||||
1. vt100 已解析鼠标模式:`screen().mouse_protocol_mode()`(None/Press/PressRelease/ButtonMotion/AnyMotion)和 `mouse_protocol_encoding()`(Default/Utf8/Sgr)。
|
||||
2. 在 `terminal-screen` 上监听 `on:mousedown` / `on:mouseup` / `on:mousemove`:
|
||||
- 把像素坐标换算成终端 **行列**(用已测量的 cell 宽高 + 容器 rect)。
|
||||
- 按当前 encoding 编码:
|
||||
- **SGR**(最常用):`ESC[<b;col;row;M`(按下)/ `m`(释放)。
|
||||
- Default:`ESC[M` + 三字节。
|
||||
- 只在 `mouse_protocol_mode != None` 时拦截,否则放行(不破坏选区/原生行为)。
|
||||
3. 与 P0 选区冲突处理:应用开启鼠标上报时,鼠标事件优先发给应用;否则用于本地选区。
|
||||
**已实现**:
|
||||
1. **vt100 mouse-mode accessors**(`core.rs::mouse_protocol_mode` / `mouse_protocol_encoding`)—— 暴露 `None/Press/PressRelease/ButtonMotion/AnyMotion` 和 `Default/Utf8/Sgr`。
|
||||
2. **事件监听与坐标换算**(`component.rs`)
|
||||
- `on:mousedown` / `on:mouseup` / `on:mousemove` 复用现有的 DOM 事件。
|
||||
- `pixel_to_grid` 把 `(clientX, clientY)` 换算成 0-based 可见网格 `(row, col)`(注意:与选区用的 `point_to_cell` 不同,这里**不加 scroll_top**,因为鼠标报告坐标是相对于应用正在绘制的当前屏幕,而非 scrollback 缓冲区)。
|
||||
3. **编码**(`encode_mouse_report` 纯函数)
|
||||
- **SGR(1006)**:`ESC[<b;col;row;M`(按下/移动)/ `ESC[<b;col;row;m`(释放)。ASCII-only,任意屏幕尺寸都正确。
|
||||
- **Default / UTF-8**:`ESC[M` + 三字节(各 +32)。坐标 ≤94 时完全正确并 ASCII-safe;更大坐标被钳位到 94(大多数终端 likewise;现代应用都走 SGR)。
|
||||
- **滚轮**:垂直/水平滚轮映射为按钮 64/65/66/67 的 `Press` 事件。
|
||||
4. **模式仲裁**
|
||||
- 应用开启 mouse reporting(`mode != None`)时,鼠标事件优先发给应用;否则走本地选区。
|
||||
- 按住 **Shift** 点击可绕过鼠标上报、强制本地选区(xterm 行为)。
|
||||
- 鼠标移动按网格单元去重,避免每像素都发报告。
|
||||
5. **测试**:`encode_mouse_report`、`pixel_to_grid`、`with_mouse_modifiers`、`mouse_button_base` 均有单测;23 个 terminal 测试全过,clippy 干净。
|
||||
|
||||
**风险**:中。坐标换算边界 + 与选区的优先级仲裁需要仔细测。
|
||||
**已知限制**:
|
||||
- 主屏幕(非 alt-screen)滚动后,鼠标报告坐标仍相对于可见网格顶部;因为开启鼠标上报的应用通常运行在 alt-screen,这覆盖主要使用场景。
|
||||
- 与浏览器 `Ctrl+W/T/N` 类似,浏览器可能保留某些鼠标手势(如右键菜单、拖拽文本),这些在网页终端内无法 100% 拦截。应用内鼠标操作应正常。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user