@xterm/xterm 6.1.0-beta.253 → 6.1.0-beta.255
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/xterm.js +1 -1
- package/lib/xterm.js.map +1 -1
- package/lib/xterm.mjs +8 -8
- package/lib/xterm.mjs.map +3 -3
- package/package.json +2 -2
- package/src/browser/CoreBrowserTerminal.ts +4 -3
- package/src/browser/services/MouseService.ts +109 -6
- package/src/browser/services/SelectionService.ts +13 -1
- package/src/common/Version.ts +1 -1
- package/src/common/services/OptionsService.ts +1 -0
- package/src/common/services/Services.ts +1 -0
- package/typings/xterm.d.ts +11 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xterm/xterm",
|
|
3
3
|
"description": "Full xterm terminal, in your browser",
|
|
4
|
-
"version": "6.1.0-beta.
|
|
4
|
+
"version": "6.1.0-beta.255",
|
|
5
5
|
"main": "lib/xterm.js",
|
|
6
6
|
"module": "lib/xterm.mjs",
|
|
7
7
|
"style": "css/xterm.css",
|
|
@@ -112,5 +112,5 @@
|
|
|
112
112
|
"ws": "^8.20.0",
|
|
113
113
|
"xterm-benchmark": "^0.3.2"
|
|
114
114
|
},
|
|
115
|
-
"commit": "
|
|
115
|
+
"commit": "4bb9c06b5fe7ed1eb6e4512a81637c82b2d176f6"
|
|
116
116
|
}
|
|
@@ -37,7 +37,7 @@ import { CharacterJoinerService } from 'browser/services/CharacterJoinerService'
|
|
|
37
37
|
import { CoreBrowserService } from 'browser/services/CoreBrowserService';
|
|
38
38
|
import { LinkProviderService } from 'browser/services/LinkProviderService';
|
|
39
39
|
import { MouseCoordsService } from 'browser/services/MouseCoordsService';
|
|
40
|
-
import { MouseService } from 'browser/services/MouseService';
|
|
40
|
+
import { MouseEventCssClasses, MouseService } from 'browser/services/MouseService';
|
|
41
41
|
import { RenderService } from 'browser/services/RenderService';
|
|
42
42
|
import { SelectionService } from 'browser/services/SelectionService';
|
|
43
43
|
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IKeyboardService, ILinkProviderService, IMouseCoordsService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
|
|
@@ -618,11 +618,12 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
618
618
|
this._register(addDisposableListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.handleMouseDown(e)));
|
|
619
619
|
|
|
620
620
|
// apply mouse event classes set by escape codes before terminal was attached
|
|
621
|
-
if (this.mouseStateService.areMouseEventsActive) {
|
|
621
|
+
if (this.mouseStateService.areMouseEventsActive && !this.options.mouseEventsRequireAlt) {
|
|
622
622
|
this._selectionService.disable();
|
|
623
|
-
this.element.classList.add(
|
|
623
|
+
this.element.classList.add(MouseEventCssClasses.ENABLE_MOUSE_EVENTS);
|
|
624
624
|
} else {
|
|
625
625
|
this._selectionService.enable();
|
|
626
|
+
this.element.classList.remove(MouseEventCssClasses.ENABLE_MOUSE_EVENTS);
|
|
626
627
|
}
|
|
627
628
|
|
|
628
629
|
if (this.options.screenReaderMode) {
|
|
@@ -7,12 +7,16 @@ import { addDisposableListener } from 'browser/Dom';
|
|
|
7
7
|
import { IBufferService, IMouseStateService, ICoreService, ILogService, IOptionsService } from 'common/services/Services';
|
|
8
8
|
import { CoreMouseAction, CoreMouseButton, CoreMouseEventType, ICoreMouseEvent, IDisposable } from 'common/Types';
|
|
9
9
|
import { C0 } from 'common/data/EscapeSequences';
|
|
10
|
-
import { toDisposable } from 'common/Lifecycle';
|
|
10
|
+
import { DisposableStore, MutableDisposable, toDisposable } from 'common/Lifecycle';
|
|
11
11
|
import { ICoreBrowserService, IMouseCoordsService, IMouseService, IMouseServiceTarget, IRenderService, ISelectionService } from './Services';
|
|
12
12
|
import { Gesture, EventType as GestureEventType, IGestureEvent } from 'browser/scrollable/touch';
|
|
13
13
|
|
|
14
14
|
type RequestedMouseEvents = Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener | null>;
|
|
15
15
|
|
|
16
|
+
export const enum MouseEventCssClasses {
|
|
17
|
+
ENABLE_MOUSE_EVENTS = 'enable-mouse-events'
|
|
18
|
+
}
|
|
19
|
+
|
|
16
20
|
interface IMouseBindContext {
|
|
17
21
|
readonly target: IMouseServiceTarget;
|
|
18
22
|
readonly focus: () => void;
|
|
@@ -25,6 +29,7 @@ export class MouseService implements IMouseService {
|
|
|
25
29
|
private _lastEvent: ICoreMouseEvent | null = null;
|
|
26
30
|
private _wheelPartialScroll: number = 0;
|
|
27
31
|
private _touchScrollAccumulator: number = 0;
|
|
32
|
+
private _altMouseCursor: AltMouseCursorController | undefined;
|
|
28
33
|
|
|
29
34
|
constructor(
|
|
30
35
|
@IRenderService private readonly _renderService: IRenderService,
|
|
@@ -63,9 +68,20 @@ export class MouseService implements IMouseService {
|
|
|
63
68
|
mousedrag: (ev: Event) => this._handleMouseDrag(ctx, ev as MouseEvent),
|
|
64
69
|
mousemove: (ev: Event) => this._handleMouseMove(ctx, ev as MouseEvent)
|
|
65
70
|
};
|
|
71
|
+
this._altMouseCursor = new AltMouseCursorController(
|
|
72
|
+
element,
|
|
73
|
+
document,
|
|
74
|
+
() => this._mouseStateService.areMouseEventsActive
|
|
75
|
+
&& !!this._optionsService.rawOptions.mouseEventsRequireAlt
|
|
76
|
+
);
|
|
77
|
+
register(this._altMouseCursor);
|
|
66
78
|
register(this._mouseStateService.onProtocolChange(events => {
|
|
67
79
|
this._handleProtocolChange(ctx, eventListeners, events);
|
|
68
80
|
}));
|
|
81
|
+
register(this._optionsService.onSpecificOptionChange('mouseEventsRequireAlt', () => {
|
|
82
|
+
this._syncMouseModeState(element);
|
|
83
|
+
this._altMouseCursor?.sync();
|
|
84
|
+
}));
|
|
69
85
|
// force initial onProtocolChange so we dont miss early mouse requests
|
|
70
86
|
this._mouseStateService.activeProtocol = this._mouseStateService.activeProtocol;
|
|
71
87
|
|
|
@@ -153,6 +169,19 @@ export class MouseService implements IMouseService {
|
|
|
153
169
|
return false;
|
|
154
170
|
}
|
|
155
171
|
|
|
172
|
+
if (but !== CoreMouseButton.WHEEL
|
|
173
|
+
&& this._optionsService.rawOptions.mouseEventsRequireAlt
|
|
174
|
+
&& this._mouseStateService.areMouseEventsActive
|
|
175
|
+
&& !ev.altKey) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Alt is only used locally to gate mouse passthrough; do not forward it to the
|
|
180
|
+
// application (e.g. tmux ignores alt-modified mouse reports).
|
|
181
|
+
const stripAltFromReport = but !== CoreMouseButton.WHEEL
|
|
182
|
+
&& this._optionsService.rawOptions.mouseEventsRequireAlt
|
|
183
|
+
&& this._mouseStateService.areMouseEventsActive;
|
|
184
|
+
|
|
156
185
|
return this._triggerMouseEvent({
|
|
157
186
|
col: pos.col,
|
|
158
187
|
row: pos.row,
|
|
@@ -161,7 +190,7 @@ export class MouseService implements IMouseService {
|
|
|
161
190
|
button: but,
|
|
162
191
|
action,
|
|
163
192
|
ctrl: ev.ctrlKey,
|
|
164
|
-
alt: ev.altKey,
|
|
193
|
+
alt: stripAltFromReport ? false : ev.altKey,
|
|
165
194
|
shift: ev.shiftKey
|
|
166
195
|
});
|
|
167
196
|
}
|
|
@@ -353,6 +382,21 @@ export class MouseService implements IMouseService {
|
|
|
353
382
|
this._touchScrollAccumulator = 0;
|
|
354
383
|
}
|
|
355
384
|
|
|
385
|
+
private _syncMouseModeState(element: HTMLElement): void {
|
|
386
|
+
if (this._mouseStateService.areMouseEventsActive) {
|
|
387
|
+
if (this._optionsService.rawOptions.mouseEventsRequireAlt) {
|
|
388
|
+
this._altMouseCursor?.resetClass();
|
|
389
|
+
this._selectionService.enable();
|
|
390
|
+
} else {
|
|
391
|
+
element.classList.add(MouseEventCssClasses.ENABLE_MOUSE_EVENTS);
|
|
392
|
+
this._selectionService.disable();
|
|
393
|
+
}
|
|
394
|
+
} else {
|
|
395
|
+
element.classList.remove(MouseEventCssClasses.ENABLE_MOUSE_EVENTS);
|
|
396
|
+
this._selectionService.enable();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
356
400
|
private _handleProtocolChange(ctx: IMouseBindContext, eventListeners: Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener>, events: CoreMouseEventType): void {
|
|
357
401
|
const { element, document } = ctx.target;
|
|
358
402
|
const { requestedEvents } = ctx;
|
|
@@ -361,13 +405,11 @@ export class MouseService implements IMouseService {
|
|
|
361
405
|
if (this._optionsService.rawOptions.logLevel === 'debug') {
|
|
362
406
|
this._logService.debug('Binding to mouse events:', this._explainEvents(events));
|
|
363
407
|
}
|
|
364
|
-
element.classList.add('enable-mouse-events');
|
|
365
|
-
this._selectionService.disable();
|
|
366
408
|
} else {
|
|
367
409
|
this._logService.debug('Unbinding from mouse events.');
|
|
368
|
-
element.classList.remove('enable-mouse-events');
|
|
369
|
-
this._selectionService.enable();
|
|
370
410
|
}
|
|
411
|
+
this._syncMouseModeState(element);
|
|
412
|
+
this._altMouseCursor?.sync();
|
|
371
413
|
|
|
372
414
|
// add/remove handlers from requestedEvents
|
|
373
415
|
if (!(events & CoreMouseEventType.MOVE)) {
|
|
@@ -537,3 +579,64 @@ export class MouseService implements IMouseService {
|
|
|
537
579
|
}
|
|
538
580
|
|
|
539
581
|
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Toggles MouseEventCssClasses.ENABLE_MOUSE_EVENTS on the terminal element while alt is held when
|
|
585
|
+
* `mouseEventsRequireAlt` is active. DOM listeners are only registered while active.
|
|
586
|
+
*/
|
|
587
|
+
export class AltMouseCursorController implements IDisposable {
|
|
588
|
+
private readonly _listeners = new MutableDisposable<IDisposable>();
|
|
589
|
+
|
|
590
|
+
constructor(
|
|
591
|
+
private readonly _element: HTMLElement,
|
|
592
|
+
private readonly _document: Document,
|
|
593
|
+
private readonly _isActive: () => boolean
|
|
594
|
+
) {
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
public dispose(): void {
|
|
598
|
+
this._listeners.dispose();
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
public sync(): void {
|
|
602
|
+
this._listeners.clear();
|
|
603
|
+
|
|
604
|
+
if (!this._isActive()) {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const store = new DisposableStore();
|
|
609
|
+
const syncFromModifier = (ev: KeyboardEvent | MouseEvent): void => this.syncFromModifier(ev);
|
|
610
|
+
store.add(addDisposableListener(this._document, 'keydown', syncFromModifier));
|
|
611
|
+
store.add(addDisposableListener(this._document, 'keyup', syncFromModifier));
|
|
612
|
+
store.add(addDisposableListener(this._element, 'mousemove', syncFromModifier));
|
|
613
|
+
const targetWindow = this._element.ownerDocument?.defaultView;
|
|
614
|
+
if (targetWindow) {
|
|
615
|
+
store.add(addDisposableListener(targetWindow, 'blur', () => {
|
|
616
|
+
if (this._isActive()) {
|
|
617
|
+
this.resetClass();
|
|
618
|
+
}
|
|
619
|
+
}));
|
|
620
|
+
}
|
|
621
|
+
this._listeners.value = store;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
public resetClass(): void {
|
|
625
|
+
this._updateClass(false);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
public syncFromModifier(ev: KeyboardEvent | MouseEvent): void {
|
|
629
|
+
if (!this._isActive()) {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
this._updateClass(ev.getModifierState('Alt'));
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
private _updateClass(altHeld: boolean): void {
|
|
636
|
+
if (altHeld) {
|
|
637
|
+
this._element.classList.add(MouseEventCssClasses.ENABLE_MOUSE_EVENTS);
|
|
638
|
+
} else {
|
|
639
|
+
this._element.classList.remove(MouseEventCssClasses.ENABLE_MOUSE_EVENTS);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
@@ -15,7 +15,7 @@ import { IBufferLine, ICellData, IDisposable } from 'common/Types';
|
|
|
15
15
|
import { getRangeLength } from 'common/buffer/BufferRange';
|
|
16
16
|
import { CellData } from 'common/buffer/CellData';
|
|
17
17
|
import { IBuffer } from 'common/buffer/Types';
|
|
18
|
-
import { IBufferService, ICoreService, IOptionsService } from 'common/services/Services';
|
|
18
|
+
import { IBufferService, ICoreService, IMouseStateService, IOptionsService } from 'common/services/Services';
|
|
19
19
|
import { Emitter } from 'common/Event';
|
|
20
20
|
|
|
21
21
|
const enum Constants {
|
|
@@ -127,6 +127,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
|
|
127
127
|
@ICoreService private readonly _coreService: ICoreService,
|
|
128
128
|
@IMouseCoordsService private readonly _mouseCoordsService: IMouseCoordsService,
|
|
129
129
|
@IOptionsService private readonly _optionsService: IOptionsService,
|
|
130
|
+
@IMouseStateService private readonly _mouseStateService: IMouseStateService,
|
|
130
131
|
@IRenderService private readonly _renderService: IRenderService,
|
|
131
132
|
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService
|
|
132
133
|
) {
|
|
@@ -434,6 +435,10 @@ export class SelectionService extends Disposable implements ISelectionService {
|
|
|
434
435
|
* @param event The mouse event.
|
|
435
436
|
*/
|
|
436
437
|
public shouldForceSelection(event: MouseEvent): boolean {
|
|
438
|
+
if (this._optionsService.rawOptions.mouseEventsRequireAlt && this._mouseStateService.areMouseEventsActive) {
|
|
439
|
+
return !event.altKey;
|
|
440
|
+
}
|
|
441
|
+
|
|
437
442
|
if (Browser.isMac) {
|
|
438
443
|
return event.altKey && this._optionsService.rawOptions.macOptionClickForcesSelection;
|
|
439
444
|
}
|
|
@@ -458,6 +463,10 @@ export class SelectionService extends Disposable implements ISelectionService {
|
|
|
458
463
|
return;
|
|
459
464
|
}
|
|
460
465
|
|
|
466
|
+
if (this._optionsService.rawOptions.mouseEventsRequireAlt && this._mouseStateService.areMouseEventsActive && event.altKey) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
|
|
461
470
|
// Allow selection when using a specific modifier key, even when disabled
|
|
462
471
|
if (!this._enabled) {
|
|
463
472
|
if (!this.shouldForceSelection(event)) {
|
|
@@ -596,6 +605,9 @@ export class SelectionService extends Disposable implements ISelectionService {
|
|
|
596
605
|
* @param event the mouse or keyboard event
|
|
597
606
|
*/
|
|
598
607
|
public shouldColumnSelect(event: KeyboardEvent | MouseEvent): boolean {
|
|
608
|
+
if (this._optionsService.rawOptions.mouseEventsRequireAlt && this._mouseStateService.areMouseEventsActive) {
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
599
611
|
return event.altKey && !(Browser.isMac && this._optionsService.rawOptions.macOptionClickForcesSelection);
|
|
600
612
|
}
|
|
601
613
|
|
package/src/common/Version.ts
CHANGED
|
@@ -41,6 +41,7 @@ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
|
|
|
41
41
|
macOptionIsMeta: false,
|
|
42
42
|
macOptionClickForcesSelection: false,
|
|
43
43
|
minimumContrastRatio: 1,
|
|
44
|
+
mouseEventsRequireAlt: false,
|
|
44
45
|
disableStdin: false,
|
|
45
46
|
allowProposedApi: false,
|
|
46
47
|
allowTransparency: false,
|
|
@@ -234,6 +234,7 @@ export interface ITerminalOptions {
|
|
|
234
234
|
macOptionIsMeta?: boolean;
|
|
235
235
|
macOptionClickForcesSelection?: boolean;
|
|
236
236
|
minimumContrastRatio?: number;
|
|
237
|
+
mouseEventsRequireAlt?: boolean;
|
|
237
238
|
reflowCursorLine?: boolean;
|
|
238
239
|
rescaleOverlappingGlyphs?: boolean;
|
|
239
240
|
rightClickSelectsWord?: boolean;
|
package/typings/xterm.d.ts
CHANGED
|
@@ -206,6 +206,17 @@ declare module '@xterm/xterm' {
|
|
|
206
206
|
*/
|
|
207
207
|
minimumContrastRatio?: number;
|
|
208
208
|
|
|
209
|
+
/**
|
|
210
|
+
* When enabled and the terminal is in mouse events mode, mouse click, drag,
|
|
211
|
+
* and move events are only sent to the underlying application when the alt
|
|
212
|
+
* key is held. The alt key is not included in the mouse reports sent to the
|
|
213
|
+
* application. Wheel events are not affected. This allows normal text
|
|
214
|
+
* selection by default while still supporting application mouse interaction
|
|
215
|
+
* and scrolling when holding alt. When enabled, this takes precedence over
|
|
216
|
+
* `macOptionClickForcesSelection`.
|
|
217
|
+
*/
|
|
218
|
+
mouseEventsRequireAlt?: boolean;
|
|
219
|
+
|
|
209
220
|
/**
|
|
210
221
|
* Control various quirks features that are either non-standard or standard
|
|
211
222
|
* in but generally rejected in modern terminals.
|