@xterm/xterm 6.1.0-beta.2 → 6.1.0-beta.200
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/README.md +60 -38
- package/css/xterm.css +29 -22
- package/lib/xterm.js +1 -1
- package/lib/xterm.js.map +1 -1
- package/lib/xterm.mjs +8 -34
- package/lib/xterm.mjs.map +4 -4
- package/package.json +36 -24
- package/src/browser/AccessibilityManager.ts +6 -3
- package/src/browser/Clipboard.ts +6 -3
- package/src/browser/CoreBrowserTerminal.ts +147 -318
- package/src/browser/Dom.ts +178 -0
- package/src/browser/Linkifier.ts +11 -11
- package/src/browser/OscLinkProvider.ts +4 -2
- package/src/browser/RenderDebouncer.ts +2 -2
- package/src/browser/TimeBasedDebouncer.ts +2 -2
- package/src/browser/Types.ts +12 -11
- package/src/browser/Viewport.ts +55 -20
- package/src/browser/decorations/BufferDecorationRenderer.ts +1 -1
- package/src/browser/decorations/OverviewRulerRenderer.ts +33 -17
- package/src/browser/input/CompositionHelper.ts +44 -8
- package/src/browser/public/Terminal.ts +25 -28
- package/src/browser/renderer/dom/DomRenderer.ts +205 -41
- package/src/browser/renderer/dom/DomRendererRowFactory.ts +19 -13
- package/src/browser/renderer/dom/WidthCache.ts +54 -52
- package/src/browser/renderer/shared/Constants.ts +7 -0
- package/src/browser/renderer/shared/TextBlinkStateManager.ts +97 -0
- package/src/browser/renderer/shared/Types.ts +8 -2
- package/src/browser/scrollable/abstractScrollbar.ts +300 -0
- package/src/browser/scrollable/fastDomNode.ts +126 -0
- package/src/browser/scrollable/globalPointerMoveMonitor.ts +90 -0
- package/src/browser/scrollable/horizontalScrollbar.ts +85 -0
- package/src/browser/scrollable/mouseEvent.ts +292 -0
- package/src/browser/scrollable/scrollable.ts +486 -0
- package/src/browser/scrollable/scrollableElement.ts +579 -0
- package/src/browser/scrollable/scrollableElementOptions.ts +161 -0
- package/src/browser/scrollable/scrollbarArrow.ts +110 -0
- package/src/browser/scrollable/scrollbarState.ts +246 -0
- package/src/browser/scrollable/scrollbarVisibilityController.ts +113 -0
- package/src/browser/scrollable/touch.ts +485 -0
- package/src/browser/scrollable/verticalScrollbar.ts +143 -0
- package/src/browser/scrollable/widget.ts +23 -0
- package/src/browser/services/CharSizeService.ts +2 -2
- package/src/browser/services/CoreBrowserService.ts +7 -5
- package/src/browser/services/KeyboardService.ts +67 -0
- package/src/browser/services/LinkProviderService.ts +1 -1
- package/src/browser/services/MouseCoordsService.ts +47 -0
- package/src/browser/services/MouseService.ts +518 -25
- package/src/browser/services/RenderService.ts +22 -16
- package/src/browser/services/SelectionService.ts +16 -8
- package/src/browser/services/Services.ts +40 -17
- package/src/browser/services/ThemeService.ts +2 -2
- package/src/common/Async.ts +105 -0
- package/src/common/CircularList.ts +2 -2
- package/src/common/Color.ts +8 -0
- package/src/common/CoreTerminal.ts +29 -21
- package/src/common/Event.ts +118 -0
- package/src/common/InputHandler.ts +256 -36
- package/src/common/Lifecycle.ts +113 -0
- package/src/common/Platform.ts +13 -3
- package/src/common/SortedList.ts +7 -3
- package/src/common/TaskQueue.ts +14 -5
- package/src/common/Types.ts +36 -16
- package/src/common/Version.ts +9 -0
- package/src/common/buffer/Buffer.ts +22 -16
- package/src/common/buffer/BufferLine.ts +4 -5
- package/src/common/buffer/BufferSet.ts +7 -6
- package/src/common/buffer/CellData.ts +57 -0
- package/src/common/buffer/Marker.ts +2 -2
- package/src/common/buffer/Types.ts +6 -2
- package/src/common/data/Charsets.ts +1 -1
- package/src/common/data/EscapeSequences.ts +71 -70
- package/src/common/input/Keyboard.ts +14 -7
- package/src/common/input/KittyKeyboard.ts +519 -0
- package/src/common/input/Win32InputMode.ts +297 -0
- package/src/common/input/WriteBuffer.ts +34 -2
- package/src/common/input/XParseColor.ts +2 -2
- package/src/common/parser/ApcParser.ts +245 -0
- package/src/common/parser/Constants.ts +22 -4
- package/src/common/parser/DcsParser.ts +5 -5
- package/src/common/parser/EscapeSequenceParser.ts +155 -43
- package/src/common/parser/OscParser.ts +5 -5
- package/src/common/parser/Types.ts +34 -1
- package/src/common/public/BufferLineApiView.ts +2 -2
- package/src/common/public/BufferNamespaceApi.ts +2 -2
- package/src/common/public/ParserApi.ts +3 -0
- package/src/common/services/BufferService.ts +8 -5
- package/src/common/services/CharsetService.ts +4 -0
- package/src/common/services/CoreService.ts +18 -4
- package/src/common/services/DecorationService.ts +24 -8
- package/src/common/services/LogService.ts +1 -31
- package/src/common/services/{CoreMouseService.ts → MouseStateService.ts} +21 -132
- package/src/common/services/OptionsService.ts +13 -7
- package/src/common/services/Services.ts +47 -44
- package/src/common/services/UnicodeService.ts +1 -1
- package/typings/xterm.d.ts +320 -45
- package/src/common/TypedArrayUtils.ts +0 -17
- package/src/vs/base/browser/browser.ts +0 -141
- package/src/vs/base/browser/canIUse.ts +0 -49
- package/src/vs/base/browser/dom.ts +0 -2369
- package/src/vs/base/browser/fastDomNode.ts +0 -316
- package/src/vs/base/browser/globalPointerMoveMonitor.ts +0 -112
- package/src/vs/base/browser/iframe.ts +0 -135
- package/src/vs/base/browser/keyboardEvent.ts +0 -213
- package/src/vs/base/browser/mouseEvent.ts +0 -229
- package/src/vs/base/browser/touch.ts +0 -372
- package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +0 -303
- package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +0 -114
- package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +0 -720
- package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +0 -165
- package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +0 -114
- package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +0 -243
- package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +0 -118
- package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +0 -116
- package/src/vs/base/browser/ui/widget.ts +0 -57
- package/src/vs/base/browser/window.ts +0 -14
- package/src/vs/base/common/arrays.ts +0 -887
- package/src/vs/base/common/arraysFind.ts +0 -202
- package/src/vs/base/common/assert.ts +0 -71
- package/src/vs/base/common/async.ts +0 -1992
- package/src/vs/base/common/cancellation.ts +0 -148
- package/src/vs/base/common/charCode.ts +0 -450
- package/src/vs/base/common/collections.ts +0 -140
- package/src/vs/base/common/decorators.ts +0 -130
- package/src/vs/base/common/equals.ts +0 -146
- package/src/vs/base/common/errors.ts +0 -303
- package/src/vs/base/common/event.ts +0 -1778
- package/src/vs/base/common/functional.ts +0 -32
- package/src/vs/base/common/hash.ts +0 -316
- package/src/vs/base/common/iterator.ts +0 -159
- package/src/vs/base/common/keyCodes.ts +0 -526
- package/src/vs/base/common/keybindings.ts +0 -284
- package/src/vs/base/common/lazy.ts +0 -47
- package/src/vs/base/common/lifecycle.ts +0 -801
- package/src/vs/base/common/linkedList.ts +0 -142
- package/src/vs/base/common/map.ts +0 -202
- package/src/vs/base/common/numbers.ts +0 -98
- package/src/vs/base/common/observable.ts +0 -76
- package/src/vs/base/common/observableInternal/api.ts +0 -31
- package/src/vs/base/common/observableInternal/autorun.ts +0 -281
- package/src/vs/base/common/observableInternal/base.ts +0 -489
- package/src/vs/base/common/observableInternal/debugName.ts +0 -145
- package/src/vs/base/common/observableInternal/derived.ts +0 -428
- package/src/vs/base/common/observableInternal/lazyObservableValue.ts +0 -146
- package/src/vs/base/common/observableInternal/logging.ts +0 -328
- package/src/vs/base/common/observableInternal/promise.ts +0 -209
- package/src/vs/base/common/observableInternal/utils.ts +0 -610
- package/src/vs/base/common/platform.ts +0 -281
- package/src/vs/base/common/scrollable.ts +0 -522
- package/src/vs/base/common/sequence.ts +0 -34
- package/src/vs/base/common/stopwatch.ts +0 -43
- package/src/vs/base/common/strings.ts +0 -557
- package/src/vs/base/common/symbols.ts +0 -9
- package/src/vs/base/common/uint.ts +0 -59
- package/src/vs/patches/nls.ts +0 -90
- package/src/vs/typings/base-common.d.ts +0 -20
- package/src/vs/typings/require.d.ts +0 -42
- package/src/vs/typings/vscode-globals-nls.d.ts +0 -36
- package/src/vs/typings/vscode-globals-product.d.ts +0 -33
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { IKeyboardService } from 'browser/services/Services';
|
|
7
|
+
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
|
|
8
|
+
import { KittyKeyboard, KittyKeyboardEventType, KittyKeyboardFlags } from 'common/input/KittyKeyboard';
|
|
9
|
+
import { Win32InputMode } from 'common/input/Win32InputMode';
|
|
10
|
+
import { isMac } from 'common/Platform';
|
|
11
|
+
import { ICoreService, IOptionsService } from 'common/services/Services';
|
|
12
|
+
import { IKeyboardResult } from 'common/Types';
|
|
13
|
+
|
|
14
|
+
export class KeyboardService implements IKeyboardService {
|
|
15
|
+
public serviceBrand: undefined;
|
|
16
|
+
|
|
17
|
+
private _win32InputMode: Win32InputMode | undefined;
|
|
18
|
+
private _kittyKeyboard: KittyKeyboard | undefined;
|
|
19
|
+
|
|
20
|
+
constructor(
|
|
21
|
+
@ICoreService private readonly _coreService: ICoreService,
|
|
22
|
+
@IOptionsService private readonly _optionsService: IOptionsService
|
|
23
|
+
) {
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private _getWin32InputMode(): Win32InputMode {
|
|
27
|
+
this._win32InputMode ??= new Win32InputMode();
|
|
28
|
+
return this._win32InputMode;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private _getKittyKeyboard(): KittyKeyboard {
|
|
32
|
+
this._kittyKeyboard ??= new KittyKeyboard();
|
|
33
|
+
return this._kittyKeyboard;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public evaluateKeyDown(event: KeyboardEvent): IKeyboardResult {
|
|
37
|
+
// Win32 input mode takes priority (most raw)
|
|
38
|
+
if (this.useWin32InputMode) {
|
|
39
|
+
return this._getWin32InputMode().evaluateKeyboardEvent(event, true);
|
|
40
|
+
}
|
|
41
|
+
const kittyFlags = this._coreService.kittyKeyboard.flags;
|
|
42
|
+
return this.useKitty
|
|
43
|
+
? this._getKittyKeyboard().evaluate(event, kittyFlags, event.repeat ? KittyKeyboardEventType.REPEAT : KittyKeyboardEventType.PRESS, isMac && this._optionsService.rawOptions.macOptionIsMeta)
|
|
44
|
+
: evaluateKeyboardEvent(event, this._coreService.decPrivateModes.applicationCursorKeys, isMac, this._optionsService.rawOptions.macOptionIsMeta);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public evaluateKeyUp(event: KeyboardEvent): IKeyboardResult | undefined {
|
|
48
|
+
// Win32 input mode sends key up events
|
|
49
|
+
if (this.useWin32InputMode) {
|
|
50
|
+
return this._getWin32InputMode().evaluateKeyboardEvent(event, false);
|
|
51
|
+
}
|
|
52
|
+
const kittyFlags = this._coreService.kittyKeyboard.flags;
|
|
53
|
+
if (this.useKitty && (kittyFlags & KittyKeyboardFlags.REPORT_EVENT_TYPES)) {
|
|
54
|
+
return this._getKittyKeyboard().evaluate(event, kittyFlags, KittyKeyboardEventType.RELEASE, isMac && this._optionsService.rawOptions.macOptionIsMeta);
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public get useKitty(): boolean {
|
|
60
|
+
const kittyFlags = this._coreService.kittyKeyboard.flags;
|
|
61
|
+
return !!(this._optionsService.rawOptions.vtExtensions?.kittyKeyboard && KittyKeyboard.shouldUseProtocol(kittyFlags));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public get useWin32InputMode(): boolean {
|
|
65
|
+
return !!(this._optionsService.rawOptions.vtExtensions?.win32InputMode && this._coreService.decPrivateModes.win32InputMode);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ILinkProvider, ILinkProviderService } from 'browser/services/Services';
|
|
2
|
-
import { Disposable, toDisposable } from '
|
|
2
|
+
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
3
3
|
import { IDisposable } from 'common/Types';
|
|
4
4
|
|
|
5
5
|
export class LinkProviderService extends Disposable implements ILinkProviderService {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getWindow } from 'browser/Dom';
|
|
7
|
+
import { getCoords, getCoordsRelativeToElement } from 'browser/input/Mouse';
|
|
8
|
+
import { ICharSizeService, IMouseCoordsService, IRenderService } from 'browser/services/Services';
|
|
9
|
+
|
|
10
|
+
export class MouseCoordsService implements IMouseCoordsService {
|
|
11
|
+
public serviceBrand: undefined;
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
|
15
|
+
@IRenderService private readonly _renderService: IRenderService
|
|
16
|
+
) {
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined {
|
|
20
|
+
return getCoords(
|
|
21
|
+
window,
|
|
22
|
+
event,
|
|
23
|
+
element,
|
|
24
|
+
colCount,
|
|
25
|
+
rowCount,
|
|
26
|
+
this._charSizeService.hasValidSize,
|
|
27
|
+
this._renderService.dimensions.css.cell.width,
|
|
28
|
+
this._renderService.dimensions.css.cell.height,
|
|
29
|
+
isSelection
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public getMouseReportCoords(event: MouseEvent, element: HTMLElement): { col: number, row: number, x: number, y: number } | undefined {
|
|
34
|
+
const coords = getCoordsRelativeToElement(getWindow(element), event, element);
|
|
35
|
+
if (!this._charSizeService.hasValidSize) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
coords[0] = Math.min(Math.max(coords[0], 0), this._renderService.dimensions.css.canvas.width - 1);
|
|
39
|
+
coords[1] = Math.min(Math.max(coords[1], 0), this._renderService.dimensions.css.canvas.height - 1);
|
|
40
|
+
return {
|
|
41
|
+
col: Math.floor(coords[0] / this._renderService.dimensions.css.cell.width),
|
|
42
|
+
row: Math.floor(coords[1] / this._renderService.dimensions.css.cell.height),
|
|
43
|
+
x: Math.floor(coords[0]),
|
|
44
|
+
y: Math.floor(coords[1])
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -3,44 +3,537 @@
|
|
|
3
3
|
* @license MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { addDisposableListener } from 'browser/Dom';
|
|
7
|
+
import { IBufferService, IMouseStateService, ICoreService, ILogService, IOptionsService } from 'common/services/Services';
|
|
8
|
+
import { CoreMouseAction, CoreMouseButton, CoreMouseEventType, ICoreMouseEvent, IDisposable } from 'common/Types';
|
|
9
|
+
import { C0 } from 'common/data/EscapeSequences';
|
|
10
|
+
import { toDisposable } from 'common/Lifecycle';
|
|
11
|
+
import { ICoreBrowserService, IMouseCoordsService, IMouseService, IMouseServiceTarget, IRenderService, ISelectionService } from './Services';
|
|
12
|
+
import { Gesture, EventType as GestureEventType, IGestureEvent } from 'browser/scrollable/touch';
|
|
13
|
+
|
|
14
|
+
type RequestedMouseEvents = Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener | null>;
|
|
15
|
+
|
|
16
|
+
interface IMouseBindContext {
|
|
17
|
+
readonly target: IMouseServiceTarget;
|
|
18
|
+
readonly focus: () => void;
|
|
19
|
+
readonly requestedEvents: RequestedMouseEvents;
|
|
20
|
+
}
|
|
8
21
|
|
|
9
22
|
export class MouseService implements IMouseService {
|
|
10
23
|
public serviceBrand: undefined;
|
|
11
24
|
|
|
25
|
+
private _lastEvent: ICoreMouseEvent | null = null;
|
|
26
|
+
private _wheelPartialScroll: number = 0;
|
|
27
|
+
private _touchScrollAccumulator: number = 0;
|
|
28
|
+
|
|
12
29
|
constructor(
|
|
13
30
|
@IRenderService private readonly _renderService: IRenderService,
|
|
14
|
-
@
|
|
31
|
+
@IMouseCoordsService private readonly _mouseCoordsService: IMouseCoordsService,
|
|
32
|
+
@IMouseStateService private readonly _mouseStateService: IMouseStateService,
|
|
33
|
+
@ICoreService private readonly _coreService: ICoreService,
|
|
34
|
+
@IBufferService private readonly _bufferService: IBufferService,
|
|
35
|
+
@IOptionsService private readonly _optionsService: IOptionsService,
|
|
36
|
+
@ISelectionService private readonly _selectionService: ISelectionService,
|
|
37
|
+
@ILogService private readonly _logService: ILogService,
|
|
38
|
+
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService
|
|
15
39
|
) {
|
|
16
40
|
}
|
|
17
41
|
|
|
18
|
-
public
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
42
|
+
public bindMouse(target: IMouseServiceTarget, register: (disposable: IDisposable) => void, focus: () => void): void {
|
|
43
|
+
const { element, document } = target;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Event listener state handling.
|
|
47
|
+
* We listen to the onProtocolChange event of MouseStateService and put
|
|
48
|
+
* requested listeners in `requestedEvents`. With this the listeners
|
|
49
|
+
* have all bits to do the event listener juggling.
|
|
50
|
+
* Note: 'mousedown' currently is "always on" and not managed
|
|
51
|
+
* by onProtocolChange.
|
|
52
|
+
*/
|
|
53
|
+
const requestedEvents: RequestedMouseEvents = {
|
|
54
|
+
mouseup: null,
|
|
55
|
+
wheel: null,
|
|
56
|
+
mousedrag: null,
|
|
57
|
+
mousemove: null
|
|
58
|
+
};
|
|
59
|
+
const ctx: IMouseBindContext = { target, focus, requestedEvents };
|
|
60
|
+
const eventListeners: Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener> = {
|
|
61
|
+
mouseup: (ev: Event) => this._handleMouseUp(ctx, ev as MouseEvent),
|
|
62
|
+
wheel: (ev: Event) => this._handleWheel(ctx, ev as WheelEvent),
|
|
63
|
+
mousedrag: (ev: Event) => this._handleMouseDrag(ctx, ev as MouseEvent),
|
|
64
|
+
mousemove: (ev: Event) => this._handleMouseMove(ctx, ev as MouseEvent)
|
|
65
|
+
};
|
|
66
|
+
register(this._mouseStateService.onProtocolChange(events => {
|
|
67
|
+
this._handleProtocolChange(ctx, eventListeners, events);
|
|
68
|
+
}));
|
|
69
|
+
// force initial onProtocolChange so we dont miss early mouse requests
|
|
70
|
+
this._mouseStateService.activeProtocol = this._mouseStateService.activeProtocol;
|
|
71
|
+
|
|
72
|
+
// Ensure document-level listeners are removed on dispose
|
|
73
|
+
register(toDisposable(() => {
|
|
74
|
+
if (requestedEvents.mouseup) {
|
|
75
|
+
document.removeEventListener('mouseup', requestedEvents.mouseup);
|
|
76
|
+
}
|
|
77
|
+
if (requestedEvents.mousedrag) {
|
|
78
|
+
document.removeEventListener('mousemove', requestedEvents.mousedrag);
|
|
79
|
+
}
|
|
80
|
+
}));
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* "Always on" event listeners.
|
|
84
|
+
*/
|
|
85
|
+
register(addDisposableListener(element, 'mousedown', (ev: MouseEvent) => this._handleMouseDown(ctx, ev)));
|
|
86
|
+
register(addDisposableListener(element, 'wheel', (ev: WheelEvent) => this._handlePassiveWheel(ctx, ev), { passive: false }));
|
|
87
|
+
register(Gesture.addTarget(target.screenElement));
|
|
88
|
+
register(addDisposableListener(target.screenElement, GestureEventType.START, () => this._handleTouchStart()));
|
|
89
|
+
register(addDisposableListener(target.screenElement, GestureEventType.CHANGE, (e: IGestureEvent) => this._handleTouchChange(ctx, e)));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private _sendEvent(ctx: IMouseBindContext, ev: MouseEvent | WheelEvent): boolean {
|
|
93
|
+
// Get mouse coordinates
|
|
94
|
+
const pos = this._mouseCoordsService.getMouseReportCoords(ev as MouseEvent, ctx.target.screenElement);
|
|
95
|
+
if (!pos) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let but: CoreMouseButton;
|
|
100
|
+
let action: CoreMouseAction | undefined;
|
|
101
|
+
switch ((ev as MouseEvent & { overrideType?: string }).overrideType || ev.type) {
|
|
102
|
+
case 'mousemove':
|
|
103
|
+
action = CoreMouseAction.MOVE;
|
|
104
|
+
if (ev.buttons === undefined) {
|
|
105
|
+
// buttons is not supported on macOS, try to get a value from button instead
|
|
106
|
+
but = CoreMouseButton.NONE;
|
|
107
|
+
if (ev.button !== undefined) {
|
|
108
|
+
but = ev.button < 3 ? ev.button : CoreMouseButton.NONE;
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
// according to MDN buttons only reports up to button 5 (AUX2)
|
|
112
|
+
but = ev.buttons & 1 ? CoreMouseButton.LEFT :
|
|
113
|
+
ev.buttons & 4 ? CoreMouseButton.MIDDLE :
|
|
114
|
+
ev.buttons & 2 ? CoreMouseButton.RIGHT :
|
|
115
|
+
CoreMouseButton.NONE; // fallback to NONE
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
case 'mouseup':
|
|
119
|
+
action = CoreMouseAction.UP;
|
|
120
|
+
but = ev.button < 3 ? ev.button : CoreMouseButton.NONE;
|
|
121
|
+
break;
|
|
122
|
+
case 'mousedown':
|
|
123
|
+
action = CoreMouseAction.DOWN;
|
|
124
|
+
but = ev.button < 3 ? ev.button : CoreMouseButton.NONE;
|
|
125
|
+
break;
|
|
126
|
+
case 'wheel':
|
|
127
|
+
if (!this._mouseStateService.allowCustomWheelEvent(ev as WheelEvent)) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
const deltaY = (ev as WheelEvent).deltaY;
|
|
131
|
+
if (deltaY === 0) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
const lines = this._consumeWheelEvent(
|
|
135
|
+
ev as WheelEvent,
|
|
136
|
+
this._renderService?.dimensions?.device?.cell?.height,
|
|
137
|
+
this._coreBrowserService?.dpr
|
|
138
|
+
);
|
|
139
|
+
if (lines === 0) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
action = deltaY < 0 ? CoreMouseAction.UP : CoreMouseAction.DOWN;
|
|
143
|
+
but = CoreMouseButton.WHEEL;
|
|
144
|
+
break;
|
|
145
|
+
default:
|
|
146
|
+
// dont handle other event types by accident
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// exit if we cannot determine valid button/action values
|
|
151
|
+
// do nothing for higher buttons than wheel
|
|
152
|
+
if (action === undefined || but === undefined || but > CoreMouseButton.WHEEL) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return this._triggerMouseEvent({
|
|
157
|
+
col: pos.col,
|
|
158
|
+
row: pos.row,
|
|
159
|
+
x: pos.x,
|
|
160
|
+
y: pos.y,
|
|
161
|
+
button: but,
|
|
162
|
+
action,
|
|
163
|
+
ctrl: ev.ctrlKey,
|
|
164
|
+
alt: ev.altKey,
|
|
165
|
+
shift: ev.shiftKey
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private _handleMouseUp(ctx: IMouseBindContext, ev: MouseEvent): void {
|
|
170
|
+
this._sendEvent(ctx, ev);
|
|
171
|
+
if (!ev.buttons) {
|
|
172
|
+
// if no other button is held remove global handlers
|
|
173
|
+
if (ctx.requestedEvents.mouseup) {
|
|
174
|
+
ctx.target.document.removeEventListener('mouseup', ctx.requestedEvents.mouseup);
|
|
175
|
+
}
|
|
176
|
+
if (ctx.requestedEvents.mousedrag) {
|
|
177
|
+
ctx.target.document.removeEventListener('mousemove', ctx.requestedEvents.mousedrag);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private _handleWheel(ctx: IMouseBindContext, ev: WheelEvent): false {
|
|
183
|
+
this._sendEvent(ctx, ev);
|
|
184
|
+
ev.preventDefault();
|
|
185
|
+
ev.stopPropagation();
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private _handleMouseDrag(ctx: IMouseBindContext, ev: MouseEvent): void {
|
|
190
|
+
// deal only with move while a button is held
|
|
191
|
+
if (ev.buttons) {
|
|
192
|
+
this._sendEvent(ctx, ev);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private _handleMouseMove(ctx: IMouseBindContext, ev: MouseEvent): void {
|
|
197
|
+
// deal only with move without any button
|
|
198
|
+
if (!ev.buttons) {
|
|
199
|
+
this._sendEvent(ctx, ev);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private _handleMouseDown(ctx: IMouseBindContext, ev: MouseEvent): void {
|
|
204
|
+
ev.preventDefault();
|
|
205
|
+
ctx.focus();
|
|
206
|
+
|
|
207
|
+
// Don't send the mouse button to the pty if mouse events are disabled or
|
|
208
|
+
// if the selection manager is having selection forced (ie. a modifier is
|
|
209
|
+
// held).
|
|
210
|
+
if (!this._mouseStateService.areMouseEventsActive || this._selectionService.shouldForceSelection(ev)) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
this._sendEvent(ctx, ev);
|
|
215
|
+
|
|
216
|
+
// Register additional global handlers which should keep reporting outside
|
|
217
|
+
// of the terminal element.
|
|
218
|
+
// Note: Other emulators also do this for 'mousedown' while a button
|
|
219
|
+
// is held, we currently limit 'mousedown' to the terminal only.
|
|
220
|
+
if (ctx.requestedEvents.mouseup) {
|
|
221
|
+
ctx.target.document.addEventListener('mouseup', ctx.requestedEvents.mouseup);
|
|
222
|
+
}
|
|
223
|
+
if (ctx.requestedEvents.mousedrag) {
|
|
224
|
+
ctx.target.document.addEventListener('mousemove', ctx.requestedEvents.mousedrag);
|
|
225
|
+
}
|
|
30
226
|
}
|
|
31
227
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (
|
|
35
|
-
return
|
|
228
|
+
private _handlePassiveWheel(ctx: IMouseBindContext, ev: WheelEvent): false | void {
|
|
229
|
+
// do nothing, if app side handles wheel itself
|
|
230
|
+
if (ctx.requestedEvents.wheel) {
|
|
231
|
+
return;
|
|
36
232
|
}
|
|
37
|
-
|
|
38
|
-
|
|
233
|
+
|
|
234
|
+
if (!this._mouseStateService.allowCustomWheelEvent(ev)) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (!this._bufferService.buffer.hasScrollback) {
|
|
239
|
+
// Convert wheel events into up/down events when the buffer does not have scrollback, this
|
|
240
|
+
// enables scrolling in apps hosted in the alt buffer such as vim or tmux even when mouse
|
|
241
|
+
// events are not enabled.
|
|
242
|
+
// This used implementation used get the actual lines/partial lines scrolled from the
|
|
243
|
+
// viewport but since moving to the new viewport implementation has been simplified to
|
|
244
|
+
// simply send a single up or down sequence.
|
|
245
|
+
|
|
246
|
+
// Do nothing if there's no vertical scroll
|
|
247
|
+
const deltaY = ev.deltaY;
|
|
248
|
+
if (deltaY === 0) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const lines = this._consumeWheelEvent(
|
|
253
|
+
ev,
|
|
254
|
+
this._renderService?.dimensions?.device?.cell?.height,
|
|
255
|
+
this._coreBrowserService?.dpr
|
|
256
|
+
);
|
|
257
|
+
if (lines === 0) {
|
|
258
|
+
ev.preventDefault();
|
|
259
|
+
ev.stopPropagation();
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Construct and send sequences
|
|
264
|
+
const sequence = C0.ESC + (this._coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[') + (ev.deltaY < 0 ? 'A' : 'B');
|
|
265
|
+
this._coreService.triggerDataEvent(sequence, true);
|
|
266
|
+
ev.preventDefault();
|
|
267
|
+
ev.stopPropagation();
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private _handleTouchStart(): void {
|
|
273
|
+
this._touchScrollAccumulator = 0;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private _handleTouchChange(ctx: IMouseBindContext, e: IGestureEvent): void {
|
|
277
|
+
e.preventDefault();
|
|
278
|
+
e.stopPropagation();
|
|
279
|
+
|
|
280
|
+
// When mouse protocol has wheel events active, send as mouse wheel events.
|
|
281
|
+
if (ctx.requestedEvents.wheel) {
|
|
282
|
+
this._handleTouchScrollAsWheel(ctx, e);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// When in alt buffer (no scrollback), send up/down key sequences.
|
|
287
|
+
if (!this._bufferService.buffer.hasScrollback) {
|
|
288
|
+
this._handleTouchScrollAsKeys(e);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Normal scrollback: delegate to viewport scrolling when available.
|
|
293
|
+
ctx.target.handleTouchScroll?.(e.translationY);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private _handleTouchScrollAsKeys(e: IGestureEvent): void {
|
|
297
|
+
const cellHeight = this._renderService?.dimensions.css.cell.height;
|
|
298
|
+
if (!cellHeight) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
this._touchScrollAccumulator -= e.translationY;
|
|
303
|
+
const lines = Math.trunc(this._touchScrollAccumulator / cellHeight);
|
|
304
|
+
if (lines === 0) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
this._touchScrollAccumulator -= lines * cellHeight;
|
|
309
|
+
const sequence = C0.ESC
|
|
310
|
+
+ (this._coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[')
|
|
311
|
+
+ (lines < 0 ? 'A' : 'B');
|
|
312
|
+
for (let i = 0; i < Math.abs(lines); i++) {
|
|
313
|
+
this._coreService.triggerDataEvent(sequence, true);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private _handleTouchScrollAsWheel(ctx: IMouseBindContext, e: IGestureEvent): void {
|
|
318
|
+
const cellHeight = this._renderService?.dimensions.css.cell.height;
|
|
319
|
+
if (!cellHeight) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
this._touchScrollAccumulator -= e.translationY;
|
|
324
|
+
const lines = Math.trunc(this._touchScrollAccumulator / cellHeight);
|
|
325
|
+
if (lines === 0) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
this._touchScrollAccumulator -= lines * cellHeight;
|
|
330
|
+
const pos = this._mouseCoordsService.getMouseReportCoords(e, ctx.target.screenElement);
|
|
331
|
+
if (!pos) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
for (let i = 0; i < Math.abs(lines); i++) {
|
|
336
|
+
this._triggerMouseEvent({
|
|
337
|
+
col: pos.col,
|
|
338
|
+
row: pos.row,
|
|
339
|
+
x: pos.x,
|
|
340
|
+
y: pos.y,
|
|
341
|
+
button: CoreMouseButton.WHEEL,
|
|
342
|
+
action: lines < 0 ? CoreMouseAction.UP : CoreMouseAction.DOWN,
|
|
343
|
+
ctrl: false,
|
|
344
|
+
alt: false,
|
|
345
|
+
shift: false
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
public reset(): void {
|
|
351
|
+
this._lastEvent = null;
|
|
352
|
+
this._wheelPartialScroll = 0;
|
|
353
|
+
this._touchScrollAccumulator = 0;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
private _handleProtocolChange(ctx: IMouseBindContext, eventListeners: Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener>, events: CoreMouseEventType): void {
|
|
357
|
+
const { element, document } = ctx.target;
|
|
358
|
+
const { requestedEvents } = ctx;
|
|
359
|
+
// apply global changes on events
|
|
360
|
+
if (events) {
|
|
361
|
+
if (this._optionsService.rawOptions.logLevel === 'debug') {
|
|
362
|
+
this._logService.debug('Binding to mouse events:', this._explainEvents(events));
|
|
363
|
+
}
|
|
364
|
+
element.classList.add('enable-mouse-events');
|
|
365
|
+
this._selectionService.disable();
|
|
366
|
+
} else {
|
|
367
|
+
this._logService.debug('Unbinding from mouse events.');
|
|
368
|
+
element.classList.remove('enable-mouse-events');
|
|
369
|
+
this._selectionService.enable();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// add/remove handlers from requestedEvents
|
|
373
|
+
if (!(events & CoreMouseEventType.MOVE)) {
|
|
374
|
+
if (requestedEvents.mousemove) {
|
|
375
|
+
element.removeEventListener('mousemove', requestedEvents.mousemove);
|
|
376
|
+
}
|
|
377
|
+
requestedEvents.mousemove = null;
|
|
378
|
+
} else if (!requestedEvents.mousemove) {
|
|
379
|
+
element.addEventListener('mousemove', eventListeners.mousemove);
|
|
380
|
+
requestedEvents.mousemove = eventListeners.mousemove;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (!(events & CoreMouseEventType.WHEEL)) {
|
|
384
|
+
if (requestedEvents.wheel) {
|
|
385
|
+
element.removeEventListener('wheel', requestedEvents.wheel);
|
|
386
|
+
}
|
|
387
|
+
requestedEvents.wheel = null;
|
|
388
|
+
} else if (!requestedEvents.wheel) {
|
|
389
|
+
element.addEventListener('wheel', eventListeners.wheel, { passive: false });
|
|
390
|
+
requestedEvents.wheel = eventListeners.wheel;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (!(events & CoreMouseEventType.UP)) {
|
|
394
|
+
if (requestedEvents.mouseup) {
|
|
395
|
+
document.removeEventListener('mouseup', requestedEvents.mouseup);
|
|
396
|
+
}
|
|
397
|
+
requestedEvents.mouseup = null;
|
|
398
|
+
} else {
|
|
399
|
+
requestedEvents.mouseup ??= eventListeners.mouseup;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (!(events & CoreMouseEventType.DRAG)) {
|
|
403
|
+
if (requestedEvents.mousedrag) {
|
|
404
|
+
document.removeEventListener('mousemove', requestedEvents.mousedrag);
|
|
405
|
+
}
|
|
406
|
+
requestedEvents.mousedrag = null;
|
|
407
|
+
} else {
|
|
408
|
+
requestedEvents.mousedrag ??= eventListeners.mousedrag;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
private _applyScrollModifier(amount: number, ev: WheelEvent): number {
|
|
413
|
+
// Multiply the scroll speed when the modifier key is pressed
|
|
414
|
+
if (ev.altKey || ev.ctrlKey || ev.shiftKey) {
|
|
415
|
+
return amount * this._optionsService.rawOptions.fastScrollSensitivity * this._optionsService.rawOptions.scrollSensitivity;
|
|
416
|
+
}
|
|
417
|
+
return amount * this._optionsService.rawOptions.scrollSensitivity;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Processes a wheel event, accounting for partial scrolls for trackpad, mouse scrolls.
|
|
422
|
+
* This prevents hyper-sensitive scrolling in alt buffer.
|
|
423
|
+
*/
|
|
424
|
+
private _consumeWheelEvent(ev: WheelEvent, cellHeight?: number, dpr?: number): number {
|
|
425
|
+
// Do nothing if it's not a vertical scroll event
|
|
426
|
+
if (ev.deltaY === 0 || ev.shiftKey) {
|
|
427
|
+
return 0;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (cellHeight === undefined || dpr === undefined) {
|
|
431
|
+
return 0;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const targetWheelEventPixels = cellHeight / dpr;
|
|
435
|
+
let amount = this._applyScrollModifier(ev.deltaY, ev);
|
|
436
|
+
|
|
437
|
+
if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
|
|
438
|
+
amount /= (targetWheelEventPixels + 0.0); // Prevent integer division
|
|
439
|
+
|
|
440
|
+
const isLikelyTrackpad = Math.abs(ev.deltaY) < 50;
|
|
441
|
+
if (isLikelyTrackpad) {
|
|
442
|
+
amount *= 0.3;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
this._wheelPartialScroll += amount;
|
|
446
|
+
amount = Math.floor(Math.abs(this._wheelPartialScroll)) * (this._wheelPartialScroll > 0 ? 1 : -1);
|
|
447
|
+
this._wheelPartialScroll %= 1;
|
|
448
|
+
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
|
|
449
|
+
amount *= this._bufferService.rows;
|
|
450
|
+
}
|
|
451
|
+
return amount;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Triggers a mouse event to be sent.
|
|
456
|
+
*
|
|
457
|
+
* Returns true if the event passed all protocol restrictions and a report
|
|
458
|
+
* was sent, otherwise false. The return value may be used to decide whether
|
|
459
|
+
* the default event action in the browser component should be omitted.
|
|
460
|
+
*
|
|
461
|
+
* Note: The method will change values of the given event object
|
|
462
|
+
* to fulfill protocol and encoding restrictions.
|
|
463
|
+
*/
|
|
464
|
+
private _triggerMouseEvent(e: ICoreMouseEvent): boolean {
|
|
465
|
+
// range check for col/row
|
|
466
|
+
if (e.col < 0 || e.col >= this._bufferService.cols
|
|
467
|
+
|| e.row < 0 || e.row >= this._bufferService.rows) {
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// filter nonsense combinations of button + action
|
|
472
|
+
if (e.button === CoreMouseButton.WHEEL && e.action === CoreMouseAction.MOVE) {
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
if (e.button === CoreMouseButton.NONE && e.action !== CoreMouseAction.MOVE) {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
if (e.button !== CoreMouseButton.WHEEL && (e.action === CoreMouseAction.LEFT || e.action === CoreMouseAction.RIGHT)) {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// report 1-based coords
|
|
483
|
+
e.col++;
|
|
484
|
+
e.row++;
|
|
485
|
+
|
|
486
|
+
// debounce move events at grid or pixel level
|
|
487
|
+
if (e.action === CoreMouseAction.MOVE
|
|
488
|
+
&& this._lastEvent
|
|
489
|
+
&& this._equalEvents(this._lastEvent, e, this._mouseStateService.isPixelEncoding)
|
|
490
|
+
) {
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// apply protocol restrictions
|
|
495
|
+
if (!this._mouseStateService.restrictMouseEvent(e)) {
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// encode report and send
|
|
500
|
+
const report = this._mouseStateService.encodeMouseEvent(e);
|
|
501
|
+
if (report) {
|
|
502
|
+
if (this._mouseStateService.isDefaultEncoding) {
|
|
503
|
+
this._coreService.triggerBinaryEvent(report);
|
|
504
|
+
} else {
|
|
505
|
+
this._coreService.triggerDataEvent(report, true);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
this._lastEvent = e;
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
private _explainEvents(events: CoreMouseEventType): { [event: string]: boolean } {
|
|
39
514
|
return {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
515
|
+
down: !!(events & CoreMouseEventType.DOWN),
|
|
516
|
+
up: !!(events & CoreMouseEventType.UP),
|
|
517
|
+
drag: !!(events & CoreMouseEventType.DRAG),
|
|
518
|
+
move: !!(events & CoreMouseEventType.MOVE),
|
|
519
|
+
wheel: !!(events & CoreMouseEventType.WHEEL)
|
|
44
520
|
};
|
|
45
521
|
}
|
|
522
|
+
|
|
523
|
+
private _equalEvents(e1: ICoreMouseEvent, e2: ICoreMouseEvent, pixels: boolean): boolean {
|
|
524
|
+
if (pixels) {
|
|
525
|
+
if (e1.x !== e2.x) return false;
|
|
526
|
+
if (e1.y !== e2.y) return false;
|
|
527
|
+
} else {
|
|
528
|
+
if (e1.col !== e2.col) return false;
|
|
529
|
+
if (e1.row !== e2.row) return false;
|
|
530
|
+
}
|
|
531
|
+
if (e1.button !== e2.button) return false;
|
|
532
|
+
if (e1.action !== e2.action) return false;
|
|
533
|
+
if (e1.ctrl !== e2.ctrl) return false;
|
|
534
|
+
if (e1.alt !== e2.alt) return false;
|
|
535
|
+
if (e1.shift !== e2.shift) return false;
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
|
|
46
539
|
}
|