@xterm/xterm 6.1.0-beta.183 → 6.1.0-beta.185
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 +4 -4
- package/package.json +2 -2
- package/src/browser/CoreBrowserTerminal.ts +16 -280
- package/src/browser/Linkifier.ts +8 -8
- package/src/browser/Viewport.ts +3 -3
- package/src/browser/public/Terminal.ts +1 -1
- package/src/browser/services/MouseCoordsService.ts +47 -0
- package/src/browser/services/MouseService.ts +434 -26
- package/src/browser/services/SelectionService.ts +4 -4
- package/src/browser/services/Services.ts +15 -2
- package/src/common/CoreTerminal.ts +7 -7
- package/src/common/InputHandler.ts +12 -12
- package/src/common/Types.ts +6 -6
- package/src/common/Version.ts +1 -1
- package/src/common/services/{CoreMouseService.ts → MouseStateService.ts} +19 -130
- package/src/common/services/Services.ts +8 -24
|
@@ -3,45 +3,453 @@
|
|
|
3
3
|
* @license MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
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
|
+
|
|
13
|
+
type RequestedMouseEvents = Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener | null>;
|
|
14
|
+
|
|
15
|
+
interface IMouseBindContext {
|
|
16
|
+
readonly target: IMouseServiceTarget;
|
|
17
|
+
readonly focus: () => void;
|
|
18
|
+
readonly requestedEvents: RequestedMouseEvents;
|
|
19
|
+
}
|
|
9
20
|
|
|
10
21
|
export class MouseService implements IMouseService {
|
|
11
22
|
public serviceBrand: undefined;
|
|
12
23
|
|
|
24
|
+
private _lastEvent: ICoreMouseEvent | null = null;
|
|
25
|
+
private _wheelPartialScroll: number = 0;
|
|
26
|
+
|
|
13
27
|
constructor(
|
|
14
28
|
@IRenderService private readonly _renderService: IRenderService,
|
|
15
|
-
@
|
|
29
|
+
@IMouseCoordsService private readonly _mouseCoordsService: IMouseCoordsService,
|
|
30
|
+
@IMouseStateService private readonly _mouseStateService: IMouseStateService,
|
|
31
|
+
@ICoreService private readonly _coreService: ICoreService,
|
|
32
|
+
@IBufferService private readonly _bufferService: IBufferService,
|
|
33
|
+
@IOptionsService private readonly _optionsService: IOptionsService,
|
|
34
|
+
@ISelectionService private readonly _selectionService: ISelectionService,
|
|
35
|
+
@ILogService private readonly _logService: ILogService,
|
|
36
|
+
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService
|
|
16
37
|
) {
|
|
17
38
|
}
|
|
18
39
|
|
|
19
|
-
public
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
40
|
+
public bindMouse(target: IMouseServiceTarget, register: (disposable: IDisposable) => void, focus: () => void): void {
|
|
41
|
+
const { element, document } = target;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Event listener state handling.
|
|
45
|
+
* We listen to the onProtocolChange event of MouseStateService and put
|
|
46
|
+
* requested listeners in `requestedEvents`. With this the listeners
|
|
47
|
+
* have all bits to do the event listener juggling.
|
|
48
|
+
* Note: 'mousedown' currently is "always on" and not managed
|
|
49
|
+
* by onProtocolChange.
|
|
50
|
+
*/
|
|
51
|
+
const requestedEvents: RequestedMouseEvents = {
|
|
52
|
+
mouseup: null,
|
|
53
|
+
wheel: null,
|
|
54
|
+
mousedrag: null,
|
|
55
|
+
mousemove: null
|
|
56
|
+
};
|
|
57
|
+
const ctx: IMouseBindContext = { target, focus, requestedEvents };
|
|
58
|
+
const eventListeners: Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener> = {
|
|
59
|
+
mouseup: (ev: Event) => this._handleMouseUp(ctx, ev as MouseEvent),
|
|
60
|
+
wheel: (ev: Event) => this._handleWheel(ctx, ev as WheelEvent),
|
|
61
|
+
mousedrag: (ev: Event) => this._handleMouseDrag(ctx, ev as MouseEvent),
|
|
62
|
+
mousemove: (ev: Event) => this._handleMouseMove(ctx, ev as MouseEvent)
|
|
63
|
+
};
|
|
64
|
+
register(this._mouseStateService.onProtocolChange(events => {
|
|
65
|
+
this._handleProtocolChange(ctx, eventListeners, events);
|
|
66
|
+
}));
|
|
67
|
+
// force initial onProtocolChange so we dont miss early mouse requests
|
|
68
|
+
this._mouseStateService.activeProtocol = this._mouseStateService.activeProtocol;
|
|
69
|
+
|
|
70
|
+
// Ensure document-level listeners are removed on dispose
|
|
71
|
+
register(toDisposable(() => {
|
|
72
|
+
if (requestedEvents.mouseup) {
|
|
73
|
+
document.removeEventListener('mouseup', requestedEvents.mouseup);
|
|
74
|
+
}
|
|
75
|
+
if (requestedEvents.mousedrag) {
|
|
76
|
+
document.removeEventListener('mousemove', requestedEvents.mousedrag);
|
|
77
|
+
}
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* "Always on" event listeners.
|
|
82
|
+
*/
|
|
83
|
+
register(addDisposableListener(element, 'mousedown', (ev: MouseEvent) => this._handleMouseDown(ctx, ev)));
|
|
84
|
+
register(addDisposableListener(element, 'wheel', (ev: WheelEvent) => this._handlePassiveWheel(ctx, ev), { passive: false }));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private _sendEvent(ctx: IMouseBindContext, ev: MouseEvent | WheelEvent): boolean {
|
|
88
|
+
// Get mouse coordinates
|
|
89
|
+
const pos = this._mouseCoordsService.getMouseReportCoords(ev as MouseEvent, ctx.target.screenElement);
|
|
90
|
+
if (!pos) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let but: CoreMouseButton;
|
|
95
|
+
let action: CoreMouseAction | undefined;
|
|
96
|
+
switch ((ev as MouseEvent & { overrideType?: string }).overrideType || ev.type) {
|
|
97
|
+
case 'mousemove':
|
|
98
|
+
action = CoreMouseAction.MOVE;
|
|
99
|
+
if (ev.buttons === undefined) {
|
|
100
|
+
// buttons is not supported on macOS, try to get a value from button instead
|
|
101
|
+
but = CoreMouseButton.NONE;
|
|
102
|
+
if (ev.button !== undefined) {
|
|
103
|
+
but = ev.button < 3 ? ev.button : CoreMouseButton.NONE;
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
// according to MDN buttons only reports up to button 5 (AUX2)
|
|
107
|
+
but = ev.buttons & 1 ? CoreMouseButton.LEFT :
|
|
108
|
+
ev.buttons & 4 ? CoreMouseButton.MIDDLE :
|
|
109
|
+
ev.buttons & 2 ? CoreMouseButton.RIGHT :
|
|
110
|
+
CoreMouseButton.NONE; // fallback to NONE
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
case 'mouseup':
|
|
114
|
+
action = CoreMouseAction.UP;
|
|
115
|
+
but = ev.button < 3 ? ev.button : CoreMouseButton.NONE;
|
|
116
|
+
break;
|
|
117
|
+
case 'mousedown':
|
|
118
|
+
action = CoreMouseAction.DOWN;
|
|
119
|
+
but = ev.button < 3 ? ev.button : CoreMouseButton.NONE;
|
|
120
|
+
break;
|
|
121
|
+
case 'wheel':
|
|
122
|
+
if (!this._mouseStateService.allowCustomWheelEvent(ev as WheelEvent)) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const deltaY = (ev as WheelEvent).deltaY;
|
|
126
|
+
if (deltaY === 0) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
const lines = this._consumeWheelEvent(
|
|
130
|
+
ev as WheelEvent,
|
|
131
|
+
this._renderService?.dimensions?.device?.cell?.height,
|
|
132
|
+
this._coreBrowserService?.dpr
|
|
133
|
+
);
|
|
134
|
+
if (lines === 0) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
action = deltaY < 0 ? CoreMouseAction.UP : CoreMouseAction.DOWN;
|
|
138
|
+
but = CoreMouseButton.WHEEL;
|
|
139
|
+
break;
|
|
140
|
+
default:
|
|
141
|
+
// dont handle other event types by accident
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// exit if we cannot determine valid button/action values
|
|
146
|
+
// do nothing for higher buttons than wheel
|
|
147
|
+
if (action === undefined || but === undefined || but > CoreMouseButton.WHEEL) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return this._triggerMouseEvent({
|
|
152
|
+
col: pos.col,
|
|
153
|
+
row: pos.row,
|
|
154
|
+
x: pos.x,
|
|
155
|
+
y: pos.y,
|
|
156
|
+
button: but,
|
|
157
|
+
action,
|
|
158
|
+
ctrl: ev.ctrlKey,
|
|
159
|
+
alt: ev.altKey,
|
|
160
|
+
shift: ev.shiftKey
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private _handleMouseUp(ctx: IMouseBindContext, ev: MouseEvent): void {
|
|
165
|
+
this._sendEvent(ctx, ev);
|
|
166
|
+
if (!ev.buttons) {
|
|
167
|
+
// if no other button is held remove global handlers
|
|
168
|
+
if (ctx.requestedEvents.mouseup) {
|
|
169
|
+
ctx.target.document.removeEventListener('mouseup', ctx.requestedEvents.mouseup);
|
|
170
|
+
}
|
|
171
|
+
if (ctx.requestedEvents.mousedrag) {
|
|
172
|
+
ctx.target.document.removeEventListener('mousemove', ctx.requestedEvents.mousedrag);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private _handleWheel(ctx: IMouseBindContext, ev: WheelEvent): false {
|
|
178
|
+
this._sendEvent(ctx, ev);
|
|
179
|
+
ev.preventDefault();
|
|
180
|
+
ev.stopPropagation();
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private _handleMouseDrag(ctx: IMouseBindContext, ev: MouseEvent): void {
|
|
185
|
+
// deal only with move while a button is held
|
|
186
|
+
if (ev.buttons) {
|
|
187
|
+
this._sendEvent(ctx, ev);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private _handleMouseMove(ctx: IMouseBindContext, ev: MouseEvent): void {
|
|
192
|
+
// deal only with move without any button
|
|
193
|
+
if (!ev.buttons) {
|
|
194
|
+
this._sendEvent(ctx, ev);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private _handleMouseDown(ctx: IMouseBindContext, ev: MouseEvent): void {
|
|
199
|
+
ev.preventDefault();
|
|
200
|
+
ctx.focus();
|
|
201
|
+
|
|
202
|
+
// Don't send the mouse button to the pty if mouse events are disabled or
|
|
203
|
+
// if the selection manager is having selection forced (ie. a modifier is
|
|
204
|
+
// held).
|
|
205
|
+
if (!this._mouseStateService.areMouseEventsActive || this._selectionService.shouldForceSelection(ev)) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
this._sendEvent(ctx, ev);
|
|
210
|
+
|
|
211
|
+
// Register additional global handlers which should keep reporting outside
|
|
212
|
+
// of the terminal element.
|
|
213
|
+
// Note: Other emulators also do this for 'mousedown' while a button
|
|
214
|
+
// is held, we currently limit 'mousedown' to the terminal only.
|
|
215
|
+
if (ctx.requestedEvents.mouseup) {
|
|
216
|
+
ctx.target.document.addEventListener('mouseup', ctx.requestedEvents.mouseup);
|
|
217
|
+
}
|
|
218
|
+
if (ctx.requestedEvents.mousedrag) {
|
|
219
|
+
ctx.target.document.addEventListener('mousemove', ctx.requestedEvents.mousedrag);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private _handlePassiveWheel(ctx: IMouseBindContext, ev: WheelEvent): false | void {
|
|
224
|
+
// do nothing, if app side handles wheel itself
|
|
225
|
+
if (ctx.requestedEvents.wheel) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!this._mouseStateService.allowCustomWheelEvent(ev)) {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (!this._bufferService.buffer.hasScrollback) {
|
|
234
|
+
// Convert wheel events into up/down events when the buffer does not have scrollback, this
|
|
235
|
+
// enables scrolling in apps hosted in the alt buffer such as vim or tmux even when mouse
|
|
236
|
+
// events are not enabled.
|
|
237
|
+
// This used implementation used get the actual lines/partial lines scrolled from the
|
|
238
|
+
// viewport but since moving to the new viewport implementation has been simplified to
|
|
239
|
+
// simply send a single up or down sequence.
|
|
240
|
+
|
|
241
|
+
// Do nothing if there's no vertical scroll
|
|
242
|
+
const deltaY = ev.deltaY;
|
|
243
|
+
if (deltaY === 0) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const lines = this._consumeWheelEvent(
|
|
248
|
+
ev,
|
|
249
|
+
this._renderService?.dimensions?.device?.cell?.height,
|
|
250
|
+
this._coreBrowserService?.dpr
|
|
251
|
+
);
|
|
252
|
+
if (lines === 0) {
|
|
253
|
+
ev.preventDefault();
|
|
254
|
+
ev.stopPropagation();
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Construct and send sequences
|
|
259
|
+
const sequence = C0.ESC + (this._coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[') + (ev.deltaY < 0 ? 'A' : 'B');
|
|
260
|
+
this._coreService.triggerDataEvent(sequence, true);
|
|
261
|
+
ev.preventDefault();
|
|
262
|
+
ev.stopPropagation();
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
public reset(): void {
|
|
268
|
+
this._lastEvent = null;
|
|
269
|
+
this._wheelPartialScroll = 0;
|
|
31
270
|
}
|
|
32
271
|
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
272
|
+
private _handleProtocolChange(ctx: IMouseBindContext, eventListeners: Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener>, events: CoreMouseEventType): void {
|
|
273
|
+
const { element, document } = ctx.target;
|
|
274
|
+
const { requestedEvents } = ctx;
|
|
275
|
+
// apply global changes on events
|
|
276
|
+
if (events) {
|
|
277
|
+
if (this._optionsService.rawOptions.logLevel === 'debug') {
|
|
278
|
+
this._logService.debug('Binding to mouse events:', this._explainEvents(events));
|
|
279
|
+
}
|
|
280
|
+
element.classList.add('enable-mouse-events');
|
|
281
|
+
this._selectionService.disable();
|
|
282
|
+
} else {
|
|
283
|
+
this._logService.debug('Unbinding from mouse events.');
|
|
284
|
+
element.classList.remove('enable-mouse-events');
|
|
285
|
+
this._selectionService.enable();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// add/remove handlers from requestedEvents
|
|
289
|
+
if (!(events & CoreMouseEventType.MOVE)) {
|
|
290
|
+
if (requestedEvents.mousemove) {
|
|
291
|
+
element.removeEventListener('mousemove', requestedEvents.mousemove);
|
|
292
|
+
}
|
|
293
|
+
requestedEvents.mousemove = null;
|
|
294
|
+
} else if (!requestedEvents.mousemove) {
|
|
295
|
+
element.addEventListener('mousemove', eventListeners.mousemove);
|
|
296
|
+
requestedEvents.mousemove = eventListeners.mousemove;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (!(events & CoreMouseEventType.WHEEL)) {
|
|
300
|
+
if (requestedEvents.wheel) {
|
|
301
|
+
element.removeEventListener('wheel', requestedEvents.wheel);
|
|
302
|
+
}
|
|
303
|
+
requestedEvents.wheel = null;
|
|
304
|
+
} else if (!requestedEvents.wheel) {
|
|
305
|
+
element.addEventListener('wheel', eventListeners.wheel, { passive: false });
|
|
306
|
+
requestedEvents.wheel = eventListeners.wheel;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (!(events & CoreMouseEventType.UP)) {
|
|
310
|
+
if (requestedEvents.mouseup) {
|
|
311
|
+
document.removeEventListener('mouseup', requestedEvents.mouseup);
|
|
312
|
+
}
|
|
313
|
+
requestedEvents.mouseup = null;
|
|
314
|
+
} else {
|
|
315
|
+
requestedEvents.mouseup ??= eventListeners.mouseup;
|
|
37
316
|
}
|
|
38
|
-
|
|
39
|
-
|
|
317
|
+
|
|
318
|
+
if (!(events & CoreMouseEventType.DRAG)) {
|
|
319
|
+
if (requestedEvents.mousedrag) {
|
|
320
|
+
document.removeEventListener('mousemove', requestedEvents.mousedrag);
|
|
321
|
+
}
|
|
322
|
+
requestedEvents.mousedrag = null;
|
|
323
|
+
} else {
|
|
324
|
+
requestedEvents.mousedrag ??= eventListeners.mousedrag;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private _applyScrollModifier(amount: number, ev: WheelEvent): number {
|
|
329
|
+
// Multiply the scroll speed when the modifier key is pressed
|
|
330
|
+
if (ev.altKey || ev.ctrlKey || ev.shiftKey) {
|
|
331
|
+
return amount * this._optionsService.rawOptions.fastScrollSensitivity * this._optionsService.rawOptions.scrollSensitivity;
|
|
332
|
+
}
|
|
333
|
+
return amount * this._optionsService.rawOptions.scrollSensitivity;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Processes a wheel event, accounting for partial scrolls for trackpad, mouse scrolls.
|
|
338
|
+
* This prevents hyper-sensitive scrolling in alt buffer.
|
|
339
|
+
*/
|
|
340
|
+
private _consumeWheelEvent(ev: WheelEvent, cellHeight?: number, dpr?: number): number {
|
|
341
|
+
// Do nothing if it's not a vertical scroll event
|
|
342
|
+
if (ev.deltaY === 0 || ev.shiftKey) {
|
|
343
|
+
return 0;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (cellHeight === undefined || dpr === undefined) {
|
|
347
|
+
return 0;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const targetWheelEventPixels = cellHeight / dpr;
|
|
351
|
+
let amount = this._applyScrollModifier(ev.deltaY, ev);
|
|
352
|
+
|
|
353
|
+
if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
|
|
354
|
+
amount /= (targetWheelEventPixels + 0.0); // Prevent integer division
|
|
355
|
+
|
|
356
|
+
const isLikelyTrackpad = Math.abs(ev.deltaY) < 50;
|
|
357
|
+
if (isLikelyTrackpad) {
|
|
358
|
+
amount *= 0.3;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
this._wheelPartialScroll += amount;
|
|
362
|
+
amount = Math.floor(Math.abs(this._wheelPartialScroll)) * (this._wheelPartialScroll > 0 ? 1 : -1);
|
|
363
|
+
this._wheelPartialScroll %= 1;
|
|
364
|
+
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
|
|
365
|
+
amount *= this._bufferService.rows;
|
|
366
|
+
}
|
|
367
|
+
return amount;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Triggers a mouse event to be sent.
|
|
372
|
+
*
|
|
373
|
+
* Returns true if the event passed all protocol restrictions and a report
|
|
374
|
+
* was sent, otherwise false. The return value may be used to decide whether
|
|
375
|
+
* the default event action in the browser component should be omitted.
|
|
376
|
+
*
|
|
377
|
+
* Note: The method will change values of the given event object
|
|
378
|
+
* to fulfill protocol and encoding restrictions.
|
|
379
|
+
*/
|
|
380
|
+
private _triggerMouseEvent(e: ICoreMouseEvent): boolean {
|
|
381
|
+
// range check for col/row
|
|
382
|
+
if (e.col < 0 || e.col >= this._bufferService.cols
|
|
383
|
+
|| e.row < 0 || e.row >= this._bufferService.rows) {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// filter nonsense combinations of button + action
|
|
388
|
+
if (e.button === CoreMouseButton.WHEEL && e.action === CoreMouseAction.MOVE) {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
if (e.button === CoreMouseButton.NONE && e.action !== CoreMouseAction.MOVE) {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
if (e.button !== CoreMouseButton.WHEEL && (e.action === CoreMouseAction.LEFT || e.action === CoreMouseAction.RIGHT)) {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// report 1-based coords
|
|
399
|
+
e.col++;
|
|
400
|
+
e.row++;
|
|
401
|
+
|
|
402
|
+
// debounce move events at grid or pixel level
|
|
403
|
+
if (e.action === CoreMouseAction.MOVE
|
|
404
|
+
&& this._lastEvent
|
|
405
|
+
&& this._equalEvents(this._lastEvent, e, this._mouseStateService.isPixelEncoding)
|
|
406
|
+
) {
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// apply protocol restrictions
|
|
411
|
+
if (!this._mouseStateService.restrictMouseEvent(e)) {
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// encode report and send
|
|
416
|
+
const report = this._mouseStateService.encodeMouseEvent(e);
|
|
417
|
+
if (report) {
|
|
418
|
+
if (this._mouseStateService.isDefaultEncoding) {
|
|
419
|
+
this._coreService.triggerBinaryEvent(report);
|
|
420
|
+
} else {
|
|
421
|
+
this._coreService.triggerDataEvent(report, true);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
this._lastEvent = e;
|
|
426
|
+
return true;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
private _explainEvents(events: CoreMouseEventType): { [event: string]: boolean } {
|
|
40
430
|
return {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
431
|
+
down: !!(events & CoreMouseEventType.DOWN),
|
|
432
|
+
up: !!(events & CoreMouseEventType.UP),
|
|
433
|
+
drag: !!(events & CoreMouseEventType.DRAG),
|
|
434
|
+
move: !!(events & CoreMouseEventType.MOVE),
|
|
435
|
+
wheel: !!(events & CoreMouseEventType.WHEEL)
|
|
45
436
|
};
|
|
46
437
|
}
|
|
438
|
+
|
|
439
|
+
private _equalEvents(e1: ICoreMouseEvent, e2: ICoreMouseEvent, pixels: boolean): boolean {
|
|
440
|
+
if (pixels) {
|
|
441
|
+
if (e1.x !== e2.x) return false;
|
|
442
|
+
if (e1.y !== e2.y) return false;
|
|
443
|
+
} else {
|
|
444
|
+
if (e1.col !== e2.col) return false;
|
|
445
|
+
if (e1.row !== e2.row) return false;
|
|
446
|
+
}
|
|
447
|
+
if (e1.button !== e2.button) return false;
|
|
448
|
+
if (e1.action !== e2.action) return false;
|
|
449
|
+
if (e1.ctrl !== e2.ctrl) return false;
|
|
450
|
+
if (e1.alt !== e2.alt) return false;
|
|
451
|
+
if (e1.shift !== e2.shift) return false;
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
|
|
47
455
|
}
|
|
@@ -8,7 +8,7 @@ import { getCoordsRelativeToElement } from 'browser/input/Mouse';
|
|
|
8
8
|
import { moveToCellSequence } from 'browser/input/MoveToCell';
|
|
9
9
|
import { SelectionModel } from 'browser/selection/SelectionModel';
|
|
10
10
|
import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types';
|
|
11
|
-
import { ICoreBrowserService,
|
|
11
|
+
import { ICoreBrowserService, IMouseCoordsService, IRenderService, ISelectionService } from 'browser/services/Services';
|
|
12
12
|
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
13
13
|
import * as Browser from 'common/Platform';
|
|
14
14
|
import { IBufferLine, ICellData, IDisposable } from 'common/Types';
|
|
@@ -126,7 +126,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
|
|
126
126
|
private readonly _linkifier: ILinkifier2,
|
|
127
127
|
@IBufferService private readonly _bufferService: IBufferService,
|
|
128
128
|
@ICoreService private readonly _coreService: ICoreService,
|
|
129
|
-
@
|
|
129
|
+
@IMouseCoordsService private readonly _mouseCoordsService: IMouseCoordsService,
|
|
130
130
|
@IOptionsService private readonly _optionsService: IOptionsService,
|
|
131
131
|
@IRenderService private readonly _renderService: IRenderService,
|
|
132
132
|
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService
|
|
@@ -395,7 +395,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
|
|
395
395
|
* @param event The mouse event.
|
|
396
396
|
*/
|
|
397
397
|
private _getMouseBufferCoords(event: MouseEvent): [number, number] | undefined {
|
|
398
|
-
const coords = this.
|
|
398
|
+
const coords = this._mouseCoordsService.getCoords(event, this._screenElement, this._bufferService.cols, this._bufferService.rows, true);
|
|
399
399
|
if (!coords) {
|
|
400
400
|
return undefined;
|
|
401
401
|
}
|
|
@@ -715,7 +715,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
|
|
715
715
|
|
|
716
716
|
if (this.selectionText.length <= 1 && timeElapsed < ALT_CLICK_MOVE_CURSOR_TIME && event.altKey && this._optionsService.rawOptions.altClickMovesCursor) {
|
|
717
717
|
if (this._bufferService.buffer.ybase === this._bufferService.buffer.ydisp) {
|
|
718
|
-
const coordinates = this.
|
|
718
|
+
const coordinates = this._mouseCoordsService.getCoords(
|
|
719
719
|
event,
|
|
720
720
|
this._element,
|
|
721
721
|
this._bufferService.cols,
|
|
@@ -49,14 +49,27 @@ export interface ICoreBrowserService {
|
|
|
49
49
|
readonly dpr: number;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
export const
|
|
53
|
-
export interface
|
|
52
|
+
export const IMouseCoordsService = createDecorator<IMouseCoordsService>('MouseCoordsService');
|
|
53
|
+
export interface IMouseCoordsService {
|
|
54
54
|
serviceBrand: undefined;
|
|
55
55
|
|
|
56
56
|
getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined;
|
|
57
57
|
getMouseReportCoords(event: MouseEvent, element: HTMLElement): { col: number, row: number, x: number, y: number } | undefined;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
export const IMouseService = createDecorator<IMouseService>('MouseService');
|
|
61
|
+
export interface IMouseService {
|
|
62
|
+
serviceBrand: undefined;
|
|
63
|
+
|
|
64
|
+
bindMouse(target: IMouseServiceTarget, register: (disposable: IDisposable) => void, focus: () => void): void;
|
|
65
|
+
reset(): void;
|
|
66
|
+
}
|
|
67
|
+
export interface IMouseServiceTarget {
|
|
68
|
+
element: HTMLElement;
|
|
69
|
+
screenElement: HTMLElement;
|
|
70
|
+
document: Document;
|
|
71
|
+
}
|
|
72
|
+
|
|
60
73
|
export const IRenderService = createDecorator<IRenderService>('RenderService');
|
|
61
74
|
export interface IRenderService extends IDisposable {
|
|
62
75
|
serviceBrand: undefined;
|
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
* http://linux.die.net/man/7/urxvt
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService,
|
|
24
|
+
import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, IMouseStateService, IUnicodeService, LogLevelEnum, ITerminalOptions, IOscLinkService } from 'common/services/Services';
|
|
25
25
|
import { InstantiationService } from 'common/services/InstantiationService';
|
|
26
26
|
import { LogService } from 'common/services/LogService';
|
|
27
27
|
import { BufferService, MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService';
|
|
28
28
|
import { OptionsService } from 'common/services/OptionsService';
|
|
29
29
|
import { IDisposable, IAttributeData, ICoreTerminal, IScrollEvent } from 'common/Types';
|
|
30
30
|
import { CoreService } from 'common/services/CoreService';
|
|
31
|
-
import {
|
|
31
|
+
import { MouseStateService } from 'common/services/MouseStateService';
|
|
32
32
|
import { UnicodeService } from 'common/services/UnicodeService';
|
|
33
33
|
import { CharsetService } from 'common/services/CharsetService';
|
|
34
34
|
import { updateWindowsModeWrappedState } from 'common/WindowsMode';
|
|
@@ -50,7 +50,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
|
|
|
50
50
|
protected readonly _charsetService: ICharsetService;
|
|
51
51
|
protected readonly _oscLinkService: IOscLinkService;
|
|
52
52
|
|
|
53
|
-
public readonly
|
|
53
|
+
public readonly mouseStateService: IMouseStateService;
|
|
54
54
|
public readonly coreService: ICoreService;
|
|
55
55
|
public readonly unicodeService: IUnicodeService;
|
|
56
56
|
public readonly optionsService: IOptionsService;
|
|
@@ -113,8 +113,8 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
|
|
|
113
113
|
this._instantiationService.setService(IBufferService, this._bufferService);
|
|
114
114
|
this.coreService = this._register(this._instantiationService.createInstance(CoreService));
|
|
115
115
|
this._instantiationService.setService(ICoreService, this.coreService);
|
|
116
|
-
this.
|
|
117
|
-
this._instantiationService.setService(
|
|
116
|
+
this.mouseStateService = this._register(this._instantiationService.createInstance(MouseStateService));
|
|
117
|
+
this._instantiationService.setService(IMouseStateService, this.mouseStateService);
|
|
118
118
|
this.unicodeService = this._register(this._instantiationService.createInstance(UnicodeService));
|
|
119
119
|
this._instantiationService.setService(IUnicodeService, this.unicodeService);
|
|
120
120
|
this._charsetService = this._instantiationService.createInstance(CharsetService);
|
|
@@ -124,7 +124,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
|
|
|
124
124
|
|
|
125
125
|
|
|
126
126
|
// Register input handler and handle/forward events
|
|
127
|
-
this._inputHandler = this._register(new InputHandler(this._bufferService, this._charsetService, this.coreService, this._logService, this.optionsService, this._oscLinkService, this.
|
|
127
|
+
this._inputHandler = this._register(new InputHandler(this._bufferService, this._charsetService, this.coreService, this._logService, this.optionsService, this._oscLinkService, this.mouseStateService, this.unicodeService));
|
|
128
128
|
this._register(EventUtils.forward(this._inputHandler.onLineFeed, this._onLineFeed));
|
|
129
129
|
|
|
130
130
|
// Setup listeners
|
|
@@ -256,7 +256,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
|
|
|
256
256
|
this._bufferService.reset();
|
|
257
257
|
this._charsetService.reset();
|
|
258
258
|
this.coreService.reset();
|
|
259
|
-
this.
|
|
259
|
+
this.mouseStateService.reset();
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
|