Skip to content

Commit f9db65c

Browse files
authored
Merge pull request #4093 from jerch/sgr_pixels
SGR-pixels mouse report
2 parents f816ed0 + 70d30f5 commit f9db65c

File tree

11 files changed

+380
-341
lines changed

11 files changed

+380
-341
lines changed

bin/test_mousemodes.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*
55
* Script to test different mouse modes in terminal emulators.
66
* Tests for protocols DECSET 9, 1000, 1002, 1003 with different
7-
* report encodings (default, UTF8, SGR, URXVT).
7+
* report encodings (default, UTF8, SGR, URXVT, SGR-pixels).
88
*
99
* VT200 Highlight mode (DECSET 1001) is not implemented.
1010
*
@@ -152,13 +152,27 @@ const ENC = {
152152
],
153153
'URXVT': [
154154
'\x1b[?1015h',
155-
// format: CSI <button + 32> ; Prow ; Pcol M
155+
// format: CSI <button + 32> ; Prow ; Pcol M
156156
report => {
157157
// strip off introducer + M
158158
const sReport = report.toString().slice(2, -1);
159159
const [button, col, row] = sReport.split(';');
160160
return {state: evalButtonCode(button - 32), row, col};
161161
}
162+
],
163+
'SGR_PIXELS' : [
164+
'\x1b[?1016h',
165+
// format: CSI < Pbutton ; Prow ; Pcol M
166+
report => {
167+
// strip off introducer + M
168+
const sReport = report.toString().slice(3, -1);
169+
const [buttonCode, col, row] = sReport.split(';');
170+
const state = evalButtonCode(buttonCode);
171+
if (report[report.length - 1] === 'm'.charCodeAt(0)) {
172+
state.action = 'release';
173+
}
174+
return {state, row, col};
175+
}
162176
]
163177
}
164178

@@ -203,9 +217,12 @@ function activate() {
203217

204218
function applyReportData(data) {
205219
let {state, row, col} = ENC[Object.keys(ENC)[activeEnc]][1](data);
206-
console.log('\x1b[2KButton:', state.button, 'Action:', state.action, 'Modifier:', state.modifier, 'row:', row, 'col:', col);
207-
// apply to cursor position
208-
process.stdout.write(`\x1b[${row};${col}H`);
220+
if (Object.keys(ENC)[activeEnc] !== 'SGR_PIXELS') {
221+
console.log('\x1b[2KButton:', state.button, 'Action:', state.action, 'Modifier:', state.modifier, 'row:', row, 'col:', col);
222+
process.stdout.write(`\x1b[${row};${col}H`);
223+
} else {
224+
console.log('\x1b[2KButton:', state.button, 'Action:', state.action, 'Modifier:', state.modifier, 'x:', row, 'y:', col);
225+
}
209226
}
210227

211228
printMenu();

src/browser/Terminal.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
645645
// send event to CoreMouseService
646646
function sendEvent(ev: MouseEvent | WheelEvent): boolean {
647647
// get mouse coordinates
648-
const pos = self._mouseService!.getRawByteCoords(ev, self.screenElement!, self.cols, self.rows);
648+
const pos = self._mouseService!.getMouseReportCoords(ev, self.screenElement!);
649649
if (!pos) {
650650
return false;
651651
}
@@ -699,8 +699,10 @@ export class Terminal extends CoreTerminal implements ITerminal {
699699
}
700700

701701
return self.coreMouseService.triggerMouseEvent({
702-
col: pos.x - 33, // FIXME: why -33 here?
703-
row: pos.y - 33,
702+
col: pos.col,
703+
row: pos.row,
704+
x: pos.x,
705+
y: pos.y,
704706
button: but,
705707
action,
706708
ctrl: ev.ctrlKey,
@@ -1320,13 +1322,13 @@ export class Terminal extends CoreTerminal implements ITerminal {
13201322

13211323
switch (type) {
13221324
case WindowsOptionsReportType.GET_WIN_SIZE_PIXELS:
1323-
const canvasWidth = this._renderService.dimensions.scaledCanvasWidth.toFixed(0);
1324-
const canvasHeight = this._renderService.dimensions.scaledCanvasHeight.toFixed(0);
1325+
const canvasWidth = this._renderService.dimensions.canvasWidth.toFixed(0);
1326+
const canvasHeight = this._renderService.dimensions.canvasHeight.toFixed(0);
13251327
this.coreService.triggerDataEvent(`${C0.ESC}[4;${canvasHeight};${canvasWidth}t`);
13261328
break;
13271329
case WindowsOptionsReportType.GET_CELL_SIZE_PIXELS:
1328-
const cellWidth = this._renderService.dimensions.scaledCellWidth.toFixed(0);
1329-
const cellHeight = this._renderService.dimensions.scaledCellHeight.toFixed(0);
1330+
const cellWidth = this._renderService.dimensions.actualCellWidth.toFixed(0);
1331+
const cellHeight = this._renderService.dimensions.actualCellHeight.toFixed(0);
13301332
this.coreService.triggerDataEvent(`${C0.ESC}[6;${cellHeight};${cellWidth}t`);
13311333
break;
13321334
}

src/browser/TestUtils.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ export class MockMouseService implements IMouseService {
359359
throw new Error('Not implemented');
360360
}
361361

362-
public getRawByteCoords(event: MouseEvent, element: HTMLElement, colCount: number, rowCount: number): { x: number, y: number } | undefined {
362+
public getMouseReportCoords(event: MouseEvent, element: HTMLElement): { col: number, row: number, x: number, y: number } | undefined {
363363
throw new Error('Not implemented');
364364
}
365365
}

src/browser/input/Mouse.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,3 @@ export function getCoords(window: Pick<Window, 'getComputedStyle'>, event: {clie
4848

4949
return coords;
5050
}
51-
52-
/**
53-
* Gets coordinates within the terminal for a particular mouse event, wrapping
54-
* them to the bounds of the terminal and adding 32 to both the x and y values
55-
* as expected by xterm.
56-
*/
57-
export function getRawByteCoords(coords: [number, number] | undefined): { x: number, y: number } | undefined {
58-
if (!coords) {
59-
return undefined;
60-
}
61-
62-
// xterm sends raw bytes and starts at 32 (SP) for each.
63-
return { x: coords[0] + 32, y: coords[1] + 32 };
64-
}

src/browser/services/MouseService.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { ICharSizeService, IRenderService, IMouseService } from './Services';
7-
import { getCoords, getRawByteCoords } from 'browser/input/Mouse';
7+
import { getCoords, getCoordsRelativeToElement } from 'browser/input/Mouse';
88

99
export class MouseService implements IMouseService {
1010
public serviceBrand: undefined;
@@ -29,8 +29,24 @@ export class MouseService implements IMouseService {
2929
);
3030
}
3131

32-
public getRawByteCoords(event: MouseEvent, element: HTMLElement, colCount: number, rowCount: number): { x: number, y: number } | undefined {
33-
const coords = this.getCoords(event, element, colCount, rowCount);
34-
return getRawByteCoords(coords);
32+
public getMouseReportCoords(event: MouseEvent, element: HTMLElement): { col: number, row: number, x: number, y: number } | undefined {
33+
const coords = getCoordsRelativeToElement(window, event, element);
34+
35+
// due to rounding issues in zoom states pixel values might be negative or overflow actual canvas
36+
// ignore those events effectively narrowing mouse area a tiny bit at the edges
37+
if (!this._charSizeService.hasValidSize
38+
|| coords[0] < 0
39+
|| coords[1] < 0
40+
|| coords[0] >= this._renderService.dimensions.canvasWidth
41+
|| coords[1] >= this._renderService.dimensions.canvasHeight) {
42+
return undefined;
43+
}
44+
45+
return {
46+
col: Math.floor(coords[0] / this._renderService.dimensions.actualCellWidth),
47+
row: Math.floor(coords[1] / this._renderService.dimensions.actualCellHeight),
48+
x: Math.floor(coords[0]),
49+
y: Math.floor(coords[1])
50+
};
3551
}
3652
}

src/browser/services/Services.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export interface IMouseService {
3535
serviceBrand: undefined;
3636

3737
getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined;
38-
getRawByteCoords(event: MouseEvent, element: HTMLElement, colCount: number, rowCount: number): { x: number, y: number } | undefined;
38+
getMouseReportCoords(event: MouseEvent, element: HTMLElement): { col: number, row: number, x: number, y: number } | undefined;
3939
}
4040

4141
export const IRenderService = createDecorator<IRenderService>('RenderService');

src/common/InputHandler.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1908,6 +1908,7 @@ export class InputHandler extends Disposable implements IInputHandler {
19081908
* | 1005 | Enable UTF-8 Mouse Mode. | #N |
19091909
* | 1006 | Enable SGR Mouse Mode. | #Y |
19101910
* | 1015 | Enable urxvt Mouse Mode. | #N |
1911+
* | 1016 | Enable SGR-Pixels Mouse Mode. | #Y |
19111912
* | 1047 | Use Alternate Screen Buffer. | #Y |
19121913
* | 1048 | Save cursor as in DECSC. | #Y |
19131914
* | 1049 | Save cursor and switch to alternate buffer clearing it. | #P[Does not clear the alternate buffer.] |
@@ -1989,6 +1990,9 @@ export class InputHandler extends Disposable implements IInputHandler {
19891990
case 1015: // urxvt ext mode mouse - removed in #2507
19901991
this._logService.debug('DECSET 1015 not supported (see #2507)');
19911992
break;
1993+
case 1016: // sgr pixels mode mouse
1994+
this._coreMouseService.activeEncoding = 'SGR_PIXELS';
1995+
break;
19921996
case 25: // show cursor
19931997
this._coreService.isCursorHidden = false;
19941998
break;
@@ -2149,6 +2153,7 @@ export class InputHandler extends Disposable implements IInputHandler {
21492153
* | 1005 | Disable UTF-8 Mouse Mode. | #N |
21502154
* | 1006 | Disable SGR Mouse Mode. | #Y |
21512155
* | 1015 | Disable urxvt Mouse Mode. | #N |
2156+
* | 1006 | Disable SGR-Pixels Mouse Mode. | #Y |
21522157
* | 1047 | Use Normal Screen Buffer (clearing screen if in alt). | #Y |
21532158
* | 1048 | Restore cursor as in DECRC. | #Y |
21542159
* | 1049 | Use Normal Screen Buffer and restore cursor. | #Y |
@@ -2210,6 +2215,9 @@ export class InputHandler extends Disposable implements IInputHandler {
22102215
case 1015: // urxvt ext mode mouse - removed in #2507
22112216
this._logService.debug('DECRST 1015 not supported (see #2507)');
22122217
break;
2218+
case 1006: // sgr pixels mode mouse
2219+
this._coreMouseService.activeEncoding = 'DEFAULT';
2220+
break;
22132221
case 25: // hide cursor
22142222
this._coreService.isCursorHidden = true;
22152223
break;

src/common/Types.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,9 @@ export interface ICoreMouseEvent {
281281
col: number;
282282
/** row (zero based). */
283283
row: number;
284+
/** xy pixel positions. */
285+
x: number;
286+
y: number;
284287
/**
285288
* Button the action occured. Due to restrictions of the tracking protocols
286289
* it is not possible to report multiple buttons at once.

0 commit comments

Comments
 (0)