既保留 tmux 滚轮,又拿回拖选复制
你打开了 tmux 的鼠标模式,因为从现在开始滚轮就是你读历史的方式。set -g mouse on,窗格第一次用起来如此顺手。然后你想从回滚区里复制一条命令:鼠标划过去,高亮出现了大约零点一秒,松手的瞬间就没了。⌘C 复制到的是上一次留在剪贴板里的东西——也就是什么都没有。把鼠标模式关掉,选中恢复正常,可滚轮又退回到一屏一屏地翻。
这个取舍没有好答案,因为两种行为想要的是同一个手势。electerm 现在有一个设置把它们拆开:鼠标事件需要按下 Alt 键。普通拖拽重新变成本地选中并且松手后保留,滚轮依然能翻 tmux,按住 Option 点击才把点击交还给远端程序。
选中为什么会消失
这不是 bug,tmux 那边也没有错。它就是鼠标模式的约定。
当应用程序向终端申请鼠标报告时,xterm.js 就不再把这个指针当成文本指针——而且它不是把本地选中"降级",而是直接关掉。于是拖拽根本不会创建选区,本该开始选中的那次 mousedown 被编码成一条鼠标报告推给了 pty。松手时没有任何东西需要保留——这就是你看到的高亮一闪而过的原因。
less、开了鼠标模式的 vim、htop,凡是抓走指针的 TUI 都是同样的情况。tmux 只是那个"我既要滚轮,又想复制,而且想永远这样"的场景。
这个设置到底改了什么
一个选项,鼠标事件需要按下 Alt 键,在 设置 → 终端 里。它默认关闭(自 v5.5.66 起),所以你不主动要求,行为一点都不变。
打开之后,选中保持可用,转发则以 Alt 键为条件:
| 手势 | 设置关闭(默认) | 设置打开 |
|---|---|---|
| 普通拖拽 | 发给远端程序,没有本地选中 | 本地选中,松手保留,⌘C 可复制 |
| 滚轮 | 发给远端程序 | 依然发给远端程序,不需要修饰键 |
| 普通右键点击 | 右键报告发给远端程序(右键菜单照常弹出) | 本地处理——右键报告不再发给远端 |
| Option+点击 | 发给远端程序 | 发给远端程序 |
| Option+拖拽 | 发给远端程序 | 发给远端程序,无本地选中 |
| 没有程序申请鼠标事件 | 本地选中,不发送任何东西 | 完全相同——这个选项是惰性的 |
这张表里有两行值得读两遍,因为它们就是整个功能:
滚轮不受影响。 Alt 只对点击、拖拽和移动生效。滚轮报告无论如何都会转发,所以 tmux 还是用你原本那两根手指翻历史。不需要为了滚动去按住任何修饰键。
没有程序想要鼠标事件时,这个选项是惰性的。 electerm 只在某个程序真的开着鼠标模式时才去看它。在普通 shell 里它什么都不做,所以你不会陷入"我明明开了 alt-click 却选不了字"的状态。
按住 Option,把指针交还给远端
设置打开、且远端程序开着鼠标模式时,终端光标会跟着 Alt 键变:按住 Option,光标变成箭头——指针现在归远端程序,点击会被转发;松开,你又回到选文字的状态。
窗口失焦时这个状态会复位,所以按住 Alt 切走窗口也不会把箭头卡在那里。
Alt 键本身不会出现在发给远端程序的点击和拖拽报告里。tmux 看到的就是一次普通的左键点击,和你没按修饰键时完全一样——这正是重点:你不是在重新映射任何东西,你只是在决定指针这一刻归谁。
和其他终端相比
多数终端解决同一个冲突时用的是相反的极性:程序拥有鼠标,修饰键把指针抢回给终端做选中。
| 终端 | 默认归谁 | 修饰键手势 |
|---|---|---|
| iTerm2 | 远端程序 | Option 临时关闭鼠标报告 |
| kitty | 远端程序 | Shift 在鼠标被抢时也能选中 |
| WezTerm | 远端程序 | Shift 绕过鼠标报告(bypass_mouse_reporting_modifiers) |
| xterm / GNOME Terminal / Konsole / Windows Terminal | 远端程序 | Shift+拖拽选中 |
| electerm 打开本选项 | 你 | Alt 把指针交出去 |
也就是说,Option/Alt 在别处本来就是"把选区还给我"那个成熟按键;这个设置刻意用它的反方向。它之所以是可选的,正因为如此——对多数人来说业界既有的行为是更好的取舍,不主动开就什么都不变。
你会失去什么
两件事,而且都只在程序真的抓着鼠标的时候:
矩形(块)选区。 在 xterm.js 里 Alt+拖拽通常是块选;鼠标模式开着时,列选本来就没了,本选项打开后 Alt 还被额外保留给转发。这个状态下可以用双击/三击加 Shift+方向键,或者用程序自带的复制模式。
Alt+点击当"把 shell 光标移到点击处"。 xterm 的 altClickMovesCursor 要求那次 mousedown 真的走到了选中处理里,而且只在"什么都没选中"的那一次点击上触发;程序抓着鼠标时 mousedown 根本走不到那里,所以这个手势本来就用不上。本选项打开后,Alt+点击改为交给远端程序。
其余的都不受影响——双击选词、三击选行、⌘C,包括在 vim 和 less 里面都一样。
拿回选中的其他办法
打开本选项之前,先试试这些,都是零成本的:
- 在程序里关掉鼠标报告:vim 里
:set mouse=,tmux 里set -g mouse off,less 用-m。 - 按住 Shift 拖拽。设置关闭且鼠标模式开启时,Linux/Windows 上 Shift+拖拽会强制本地选中。macOS 上 xterm 的
macOptionClickForcesSelection默认关闭,所以没有可用的手势——这正是本选项要补的空缺。 - 用程序自带的复制机制:tmux copy mode(
prefix + [),或者 less 的-m。
tmux 到底申请了什么
tmux 申请的不是最简鼠标模式。set -g mouse on 会在按下/松开报告之外,额外开启按住按钮时的移动上报(也就是拖拽),并要求使用不会被第 223 列截断的扩展坐标格式。
有了按钮事件跟踪,一次拖拽到达 tmux 时就是"按下 → 移动 → 松开"的一串事件,而不是一次按下一次松开;这也正是 tmux 能让你用光标拖动窗格边框的原因。它默认不上报不按按钮的移动,所以不按按钮光标乱动是不耗流量的。
本选项打开后,普通拖拽一条报告都不会发出——xterm.js 在编码事件之前就返回了,一个字节都不会到 pty。选区完全在本地。
一个不对称的地方值得知道:滚轮会保留真实的 Alt 位。 点击和拖拽的报告里 alt 被强制写成 false,因为修饰键只是本端门禁;滚轮完全不被门禁,所以它的 Alt 位会原样传过去。把 Alt+滚轮当成独立手势的程序会看出这个差别。
不用重连就生效
鼠标事件需要按下 Alt 键 是少数几个被直接写进运行中终端实例的选项之一,而不是在启动时定死的。在一个跑着 tmux 的 SSH 会话里直接切换,立刻生效——不用重连、不用开新标签、丢不了回滚区。它同时也在数据同步的键列表里,所以这个选择会跟着你的其他设置走到别的机器上。
值得信任的数字
- 默认关闭。 这是一个可选的行为变更,绝不会悄悄发生。自 v5.5.66 起提供。
- 不需要重连。 终端每次重新渲染时会重新读这个选项,然后写进运行中的 xterm options。
- alt 位在点击和拖拽里会被剥掉,但滚轮不会。 修饰键只在本端起门禁作用,不是协议变化。
- 滚轮永远不被门禁。 它不属于 Alt 所能解锁的范围。
- 选中的启用/禁用跟的是鼠标模式,而不是单看这个设置。 设置关闭且鼠标模式开启时,选中是真的被关掉了——指针归远端程序。
- 设置优先级。 鼠标事件需要按下 Alt 键 生效时,优先级高于 xterm 自带的"Option 点击强制选中"行为,两者不会为同一个手势打架。
值得知道的事
- 在 macOS 上,Alt 就是 Option 键。这个设置在所有平台都存成同一个名字,物理按键在 Mac 上是 Option,其他平台是 Alt。
- 你改的是指针归谁,不是远端收到什么。 tmux 分不出 Option 点击和普通点击。如果你有依赖鼠标修饰键位的绑定,它看到的位会被剥掉——这是 xterm 的文档化行为,不是 electerm 的选择。
- 如果你一天多半是在全屏 TUI 里点按钮,就别开这个。 那些操作都得按住 Alt,比其他终端用的 Shift/Option 抢回手势更慢。这个选项是为"从抓走指针的程序里读东西、复制东西"准备的,不是为"驱动"程序准备的。
- 它按终端会话生效,本地 shell 也一样——但只有真的申请了鼠标事件的地方,你才会注意到它。
两分钟试一下
开一个会话,进 tmux,把鼠标打开:
$ tmux new -s work
$ tmux set -g mouse on
然后 设置 → 终端 → 鼠标事件需要按下 Alt 键。在输出上拖一段然后松手:选区保留,⌘C 能复制。滚滚轮:历史翻页,不需要按修饰键。按住 Option 点击:tmux 收到点击,同时光标变成箭头告诉你它归谁了。
整个功能就这么点,默认关闭——而它回答的是每一个认真对待鼠标模式的终端都被问过的那句话:怎么两个都要。
更细的内容——完整的行为表,以及它存成哪个配置键——见 wiki:Mouse Event Requires Alt Key。
