From f8e9a81d0e79d95d52dc7d6722e5292c47223ccd Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 27 May 2026 16:11:35 -0700 Subject: [PATCH] Fix mouseEventsRequireAlt by stripping alt from PTY mouse reports. Alt is only used locally to gate passthrough; applications like tmux ignore alt-modified SGR sequences. Co-authored-by: Cursor --- src/browser/services/MouseService.test.ts | 42 +++++++++++++++++++++++ src/browser/services/MouseService.ts | 8 ++++- typings/xterm-headless.d.ts | 3 +- typings/xterm.d.ts | 3 +- 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/browser/services/MouseService.test.ts b/src/browser/services/MouseService.test.ts index e9be7477c..4afa0336f 100644 --- a/src/browser/services/MouseService.test.ts +++ b/src/browser/services/MouseService.test.ts @@ -290,4 +290,46 @@ describe('MouseService mouseEventsRequireAlt', () => { assert.isTrue(element.classList.contains('enable-mouse-events')); assert.equal(selectionService.disableCount, 2); }); + + it('should strip alt modifier from forwarded mouse reports', () => { + const mouseStateService = new MouseStateService(); + mouseStateService.activeProtocol = 'ANY'; + mouseStateService.activeEncoding = 'SGR'; + const optionsService = new OptionsService({ mouseEventsRequireAlt: true }); + const reports: string[] = []; + const mouseService = new MouseService( + new MockRenderService(), + { + getMouseReportCoords: () => ({ col: 0, row: 0, x: 0, y: 0 }) + } as any, + mouseStateService, + { + triggerDataEvent: (data: string) => reports.push(data), + triggerBinaryEvent: () => {}, + decPrivateModes: { applicationCursorKeys: false } + } as any, + bufferService, + optionsService, + new TestSelectionService(), + logService, + new MockCoreBrowserService() + ); + const ctx = { + target: { + screenElement: createTestMouseTargetElement() + }, + requestedEvents: {} + } as any; + + const sent = (mouseService as any)._sendEvent(ctx, { + type: 'mousedown', + button: 0, + altKey: true, + ctrlKey: false, + shiftKey: false + } as MouseEvent); + + assert.isTrue(sent); + assert.deepEqual(reports, ['\x1b[<0;1;1M']); + }); }); diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index 3611890d3..b90015139 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -163,6 +163,12 @@ export class MouseService implements IMouseService { return false; } + // Alt is only used locally to gate mouse passthrough; do not forward it to the + // application (e.g. tmux ignores alt-modified mouse reports). + const stripAltFromReport = but !== CoreMouseButton.WHEEL + && this._optionsService.rawOptions.mouseEventsRequireAlt + && this._mouseStateService.areMouseEventsActive; + return this._triggerMouseEvent({ col: pos.col, row: pos.row, @@ -171,7 +177,7 @@ export class MouseService implements IMouseService { button: but, action, ctrl: ev.ctrlKey, - alt: ev.altKey, + alt: stripAltFromReport ? false : ev.altKey, shift: ev.shiftKey }); } diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index 830087ef6..c977eb31d 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -139,7 +139,8 @@ declare module '@xterm/headless' { /** * When enabled and the terminal is in mouse events mode, mouse click, drag, * and move events are only sent to the underlying application when the alt - * key is held. Wheel events are not affected. This allows normal text + * key is held. The alt key is not included in the mouse reports sent to the + * application. Wheel events are not affected. This allows normal text * selection by default while still supporting application mouse interaction * and scrolling when holding alt. When enabled, this takes precedence over * `macOptionClickForcesSelection`. diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index f96c316ad..c66a52e85 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -209,7 +209,8 @@ declare module '@xterm/xterm' { /** * When enabled and the terminal is in mouse events mode, mouse click, drag, * and move events are only sent to the underlying application when the alt - * key is held. Wheel events are not affected. This allows normal text + * key is held. The alt key is not included in the mouse reports sent to the + * application. Wheel events are not affected. This allows normal text * selection by default while still supporting application mouse interaction * and scrolling when holding alt. When enabled, this takes precedence over * `macOptionClickForcesSelection`.