diff --git a/src/Buffer.ts b/src/Buffer.ts index 7b5ee99a..0f75bdad 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -39,6 +39,7 @@ export class Buffer implements IBuffer { public tabs: any; public savedY: number; public savedX: number; + public savedCurAttr: number; public markers: Marker[] = []; private _bufferLineConstructor: IBufferLineConstructor; @@ -114,11 +115,14 @@ export class Buffer implements IBuffer { /** * Fills the buffer's viewport with blank lines. */ - public fillViewportRows(): void { + public fillViewportRows(fillAttr?: number): void { if (this.lines.length === 0) { + if (fillAttr === undefined) { + fillAttr = DEFAULT_ATTR; + } let i = this._terminal.rows; while (i--) { - this.lines.push(this.getBlankLine(DEFAULT_ATTR)); + this.lines.push(this.getBlankLine(fillAttr)); } } } diff --git a/src/BufferSet.test.ts b/src/BufferSet.test.ts index 009ebf2e..38f2ddab 100644 --- a/src/BufferSet.test.ts +++ b/src/BufferSet.test.ts @@ -48,4 +48,30 @@ describe('BufferSet', () => { assert.equal(bufferSet.active, bufferSet.alt); }); }); + + describe('cursor handling when swapping buffers', () => { + beforeEach(() => { + bufferSet.normal.x = 0; + bufferSet.normal.y = 0; + bufferSet.alt.x = 0; + bufferSet.alt.y = 0; + }); + + it('should keep the cursor stationary when activating alt buffer', () => { + bufferSet.activateNormalBuffer(); + bufferSet.active.x = 30; + bufferSet.active.y = 10; + bufferSet.activateAltBuffer(); + assert.equal(bufferSet.active.x, 30); + assert.equal(bufferSet.active.y, 10); + }); + it('should keep the cursor stationary when activating normal buffer', () => { + bufferSet.activateAltBuffer(); + bufferSet.active.x = 30; + bufferSet.active.y = 10; + bufferSet.activateNormalBuffer(); + assert.equal(bufferSet.active.x, 30); + assert.equal(bufferSet.active.y, 10); + }); + }); }); diff --git a/src/BufferSet.ts b/src/BufferSet.ts index c91ab751..f84757d1 100644 --- a/src/BufferSet.ts +++ b/src/BufferSet.ts @@ -61,6 +61,8 @@ export class BufferSet extends EventEmitter implements IBufferSet { if (this._activeBuffer === this._normal) { return; } + this._normal.x = this._alt.x; + this._normal.y = this._alt.y; // The alt buffer should always be cleared when we switch to the normal // buffer. This frees up memory since the alt buffer should always be new // when activated. @@ -75,13 +77,15 @@ export class BufferSet extends EventEmitter implements IBufferSet { /** * Sets the alt Buffer of the BufferSet as its currently active Buffer */ - public activateAltBuffer(): void { + public activateAltBuffer(fillAttr?: number): void { if (this._activeBuffer === this._alt) { return; } // Since the alt buffer is always cleared when the normal buffer is // activated, we want to fill it when switching to it. - this._alt.fillViewportRows(); + this._alt.fillViewportRows(fillAttr); + this._alt.x = this._normal.x; + this._alt.y = this._normal.y; this._activeBuffer = this._alt; this.emit('activate', { activeBuffer: this._alt, diff --git a/src/CharWidth.ts b/src/CharWidth.ts index fd6ac55f..43cd948e 100644 --- a/src/CharWidth.ts +++ b/src/CharWidth.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { fill } from './core/TypedArrayUtils'; +import { fill } from './common/TypedArrayUtils'; export const wcwidth = (function(opts: {nul: number, control: number}): (ucs: number) => number { // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index aaaf57c3..b2fea06a 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -6,7 +6,7 @@ import { assert, expect } from 'chai'; import { InputHandler } from './InputHandler'; import { MockInputHandlingTerminal } from './utils/TestUtils.test'; -import { NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, CHAR_DATA_CHAR_INDEX } from './Buffer'; +import { NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, CHAR_DATA_CHAR_INDEX, CHAR_DATA_ATTR_INDEX, DEFAULT_ATTR } from './Buffer'; import { Terminal } from './Terminal'; import { IBufferLine } from './Types'; @@ -506,4 +506,65 @@ describe('InputHandler', () => { inputHandler.print(String.fromCharCode(0x200B), 0, 1); }); }); + + describe('alt screen', () => { + let term: Terminal; + let handler: InputHandler; + + function lineContent(line: IBufferLine): string { + let content = ''; + for (let i = 0; i < line.length; ++i) content += line.get(i)[CHAR_DATA_CHAR_INDEX]; + return content; + } + + beforeEach(() => { + term = new Terminal(); + handler = new InputHandler(term); + }); + it('should handle DECSET/DECRST 47 (alt screen buffer)', () => { + handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST'); + expect(lineContent(term.buffer.lines.get(0))).to.equal(Array(term.cols + 1).join(' ')); + expect(lineContent(term.buffer.lines.get(1))).to.equal(' TEST' + Array(term.cols - 7).join(' ')); + // Text color of 'TEST' should be red + expect((term.buffer.lines.get(1).get(4)[CHAR_DATA_ATTR_INDEX] >> 9) & 0x1ff).to.equal(1); + }); + it('should handle DECSET/DECRST 1047 (alt screen buffer)', () => { + handler.parse('\x1b[?1047h\r\n\x1b[31mJUNK\x1b[?1047lTEST'); + expect(lineContent(term.buffer.lines.get(0))).to.equal(Array(term.cols + 1).join(' ')); + expect(lineContent(term.buffer.lines.get(1))).to.equal(' TEST' + Array(term.cols - 7).join(' ')); + // Text color of 'TEST' should be red + expect((term.buffer.lines.get(1).get(4)[CHAR_DATA_ATTR_INDEX] >> 9) & 0x1ff).to.equal(1); + }); + it('should handle DECSET/DECRST 1048 (alt screen cursor)', () => { + handler.parse('\x1b[?1048h\r\n\x1b[31mJUNK\x1b[?1048lTEST'); + expect(lineContent(term.buffer.lines.get(0))).to.equal('TEST' + Array(term.cols - 3).join(' ')); + expect(lineContent(term.buffer.lines.get(1))).to.equal('JUNK' + Array(term.cols - 3).join(' ')); + // Text color of 'TEST' should be default + expect(term.buffer.lines.get(0).get(0)[CHAR_DATA_ATTR_INDEX]).to.equal(DEFAULT_ATTR); + // Text color of 'JUNK' should be red + expect((term.buffer.lines.get(1).get(0)[CHAR_DATA_ATTR_INDEX] >> 9) & 0x1ff).to.equal(1); + }); + it('should handle DECSET/DECRST 1049 (alt screen buffer+cursor)', () => { + handler.parse('\x1b[?1049h\r\n\x1b[31mJUNK\x1b[?1049lTEST'); + expect(lineContent(term.buffer.lines.get(0))).to.equal('TEST' + Array(term.cols - 3).join(' ')); + expect(lineContent(term.buffer.lines.get(1))).to.equal(Array(term.cols + 1).join(' ')); + // Text color of 'TEST' should be default + expect(term.buffer.lines.get(0).get(0)[CHAR_DATA_ATTR_INDEX]).to.equal(DEFAULT_ATTR); + }); + it('should handle DECSET/DECRST 1049 - maintains saved cursor for alt buffer', () => { + handler.parse('\x1b[?1049h\r\n\x1b[31m\x1b[s\x1b[?1049lTEST'); + expect(lineContent(term.buffer.lines.get(0))).to.equal('TEST' + Array(term.cols - 3).join(' ')); + // Text color of 'TEST' should be default + expect(term.buffer.lines.get(0).get(0)[CHAR_DATA_ATTR_INDEX]).to.equal(DEFAULT_ATTR); + handler.parse('\x1b[?1049h\x1b[uTEST'); + expect(lineContent(term.buffer.lines.get(1))).to.equal('TEST' + Array(term.cols - 3).join(' ')); + // Text color of 'TEST' should be red + expect((term.buffer.lines.get(1).get(0)[CHAR_DATA_ATTR_INDEX] >> 9) & 0x1ff).to.equal(1); + }); + it('should handle DECSET/DECRST 1049 - clears alt buffer with erase attributes', () => { + handler.parse('\x1b[42m\x1b[?1049h'); + // Buffer should be filled with green background + expect(term.buffer.lines.get(20).get(10)[CHAR_DATA_ATTR_INDEX] & 0x1ff).to.equal(2); + }); + }); }); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index b4ce054f..7604b01f 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -1284,7 +1284,9 @@ export class InputHandler extends Disposable implements IInputHandler { case 66: this._terminal.log('Serial port requested application keypad.'); this._terminal.applicationKeypad = true; - this._terminal.viewport.syncScrollArea(); + if (this._terminal.viewport) { + this._terminal.viewport.syncScrollArea(); + } break; case 9: // X10 Mouse // no release, no motion, no wheel, no modifiers. @@ -1333,14 +1335,19 @@ export class InputHandler extends Disposable implements IInputHandler { case 25: // show cursor this._terminal.cursorHidden = false; break; + case 1048: // alt screen cursor + this.saveCursor(params); + break; case 1049: // alt screen buffer cursor - // TODO: Not sure if we need to save/restore after switching the buffer - // this.saveCursor(params); + this.saveCursor(params); // FALL-THROUGH case 47: // alt screen buffer case 1047: // alt screen buffer - this._terminal.buffers.activateAltBuffer(); - this._terminal.viewport.syncScrollArea(); + this._terminal.buffers.activateAltBuffer(this._terminal.eraseAttr()); + this._terminal.refresh(0, this._terminal.rows - 1); + if (this._terminal.viewport) { + this._terminal.viewport.syncScrollArea(); + } this._terminal.showCursor(); break; case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste) @@ -1473,7 +1480,9 @@ export class InputHandler extends Disposable implements IInputHandler { case 66: this._terminal.log('Switching back to normal keypad.'); this._terminal.applicationKeypad = false; - this._terminal.viewport.syncScrollArea(); + if (this._terminal.viewport) { + this._terminal.viewport.syncScrollArea(); + } break; case 9: // X10 Mouse case 1000: // vt200 mouse @@ -1501,18 +1510,22 @@ export class InputHandler extends Disposable implements IInputHandler { case 25: // hide cursor this._terminal.cursorHidden = true; break; + case 1048: // alt screen cursor + this.restoreCursor(params); + break; case 1049: // alt screen buffer cursor // FALL-THROUGH case 47: // normal screen buffer case 1047: // normal screen buffer - clearing it first // Ensure the selection manager has the correct buffer this._terminal.buffers.activateNormalBuffer(); - // TODO: Not sure if we need to save/restore after switching the buffer - // if (params[0] === 1049) { - // this.restoreCursor(params); - // } + if (params[0] === 1049) { + this.restoreCursor(params); + } this._terminal.refresh(0, this._terminal.rows - 1); - this._terminal.viewport.syncScrollArea(); + if (this._terminal.viewport) { + this._terminal.viewport.syncScrollArea(); + } this._terminal.showCursor(); break; case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste) @@ -1791,7 +1804,9 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.originMode = false; this._terminal.wraparoundMode = true; // defaults: xterm - true, vt100 - false this._terminal.applicationKeypad = false; // ? - this._terminal.viewport.syncScrollArea(); + if (this._terminal.viewport) { + this._terminal.viewport.syncScrollArea(); + } this._terminal.applicationCursor = false; this._terminal.buffer.scrollTop = 0; this._terminal.buffer.scrollBottom = this._terminal.rows - 1; @@ -1858,7 +1873,7 @@ export class InputHandler extends Disposable implements IInputHandler { public saveCursor(params: number[]): void { this._terminal.buffer.savedX = this._terminal.buffer.x; this._terminal.buffer.savedY = this._terminal.buffer.y; - this._terminal.savedCurAttr = this._terminal.curAttr; + this._terminal.buffer.savedCurAttr = this._terminal.curAttr; } @@ -1870,7 +1885,7 @@ export class InputHandler extends Disposable implements IInputHandler { public restoreCursor(params: number[]): void { this._terminal.buffer.x = this._terminal.buffer.savedX || 0; this._terminal.buffer.y = this._terminal.buffer.savedY || 0; - this._terminal.curAttr = this._terminal.savedCurAttr || DEFAULT_ATTR; + this._terminal.curAttr = this._terminal.buffer.savedCurAttr || DEFAULT_ATTR; } diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 86be0c48..49e21e2c 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -347,7 +347,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager * @param event The mouse event. */ private _getMouseBufferCoords(event: MouseEvent): [number, number] { - const coords = this._terminal.mouseHelper.getCoords(event, this._terminal.screenElement, this._charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows, true); + const coords = this._terminal.mouseHelper.getCoords(event, this._terminal.screenElement, this._charMeasure, this._terminal.cols, this._terminal.rows, true); if (!coords) { return null; } diff --git a/src/Terminal.ts b/src/Terminal.ts index 38eb3d5b..8e89c9ca 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -170,7 +170,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II public savedCols: number; public curAttr: number; - public savedCurAttr: number; public params: (string | number)[]; public currentParam: string | number; @@ -808,7 +807,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II button = getButton(ev); // get mouse coordinates - pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.charMeasure, self.options.lineHeight, self.cols, self.rows); + pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.charMeasure, self.cols, self.rows); if (!pos) return; sendEvent(button, pos); @@ -834,7 +833,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II // ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7< function sendMove(ev: MouseEvent): void { let button = pressed; - const pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.charMeasure, self.options.lineHeight, self.cols, self.rows); + const pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.charMeasure, self.cols, self.rows); if (!pos) return; // buttons marked as motions diff --git a/src/Types.ts b/src/Types.ts index e8578426..430c6575 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -49,7 +49,6 @@ export interface IInputHandlingTerminal extends IEventEmitter { wraparoundMode: boolean; bracketedPasteMode: boolean; curAttr: number; - savedCurAttr: number; savedCols: number; x10Mouse: boolean; vt200Mouse: boolean; @@ -246,8 +245,8 @@ export interface ILinkifierAccessor { } export interface IMouseHelper { - getCoords(event: { pageX: number, pageY: number }, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number, isSelection?: boolean): [number, number]; - getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number): { x: number, y: number }; + getCoords(event: { pageX: number, pageY: number }, element: HTMLElement, charMeasure: ICharMeasure, colCount: number, rowCount: number, isSelection?: boolean): [number, number]; + getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, colCount: number, rowCount: number): { x: number, y: number }; } export interface ICharMeasure { @@ -290,6 +289,7 @@ export interface IBuffer { hasScrollback: boolean; savedY: number; savedX: number; + savedCurAttr: number; isCursorInViewport: boolean; translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string; getWrappedRangeForLine(y: number): { first: number, last: number }; @@ -306,7 +306,7 @@ export interface IBufferSet extends IEventEmitter { active: IBuffer; activateNormalBuffer(): void; - activateAltBuffer(): void; + activateAltBuffer(fillAttr?: number): void; } export interface ISelectionManager { diff --git a/src/common/TypedArrayUtils.test.ts b/src/common/TypedArrayUtils.test.ts new file mode 100644 index 00000000..69a62abc --- /dev/null +++ b/src/common/TypedArrayUtils.test.ts @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ +import { assert } from 'chai'; +import { fillFallback } from './TypedArrayUtils'; + +type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray + | Int8Array | Int16Array | Int32Array + | Float32Array | Float64Array; + +describe('polyfill conformance tests', function(): void { + + function deepEquals(a: TypedArray, b: TypedArray): void { + assert.equal(a.length, b.length); + for (let i = 0; i < a.length; ++i) { + assert.equal(a[i], b[i]); + } + } + + describe('TypedArray.fill', function(): void { + it('should work with all typed array types', function(): void { + const u81 = new Uint8Array(5); + const u82 = new Uint8Array(5); + deepEquals(fillFallback(u81, 2), u82.fill(2)); + deepEquals(fillFallback(u81, -1), u82.fill(-1)); + const u161 = new Uint16Array(5); + const u162 = new Uint16Array(5); + deepEquals(fillFallback(u161, 2), u162.fill(2)); + deepEquals(fillFallback(u161, 65535), u162.fill(65535)); + deepEquals(fillFallback(u161, -1), u162.fill(-1)); + const u321 = new Uint32Array(5); + const u322 = new Uint32Array(5); + deepEquals(fillFallback(u321, 2), u322.fill(2)); + deepEquals(fillFallback(u321, 65537), u322.fill(65537)); + deepEquals(fillFallback(u321, -1), u322.fill(-1)); + const i81 = new Int8Array(5); + const i82 = new Int8Array(5); + deepEquals(fillFallback(i81, 2), i82.fill(2)); + deepEquals(fillFallback(i81, -1), i82.fill(-1)); + const i161 = new Int16Array(5); + const i162 = new Int16Array(5); + deepEquals(fillFallback(i161, 2), i162.fill(2)); + deepEquals(fillFallback(i161, 65535), i162.fill(65535)); + deepEquals(fillFallback(i161, -1), i162.fill(-1)); + const i321 = new Int32Array(5); + const i322 = new Int32Array(5); + deepEquals(fillFallback(i321, 2), i322.fill(2)); + deepEquals(fillFallback(i321, 65537), i322.fill(65537)); + deepEquals(fillFallback(i321, -1), i322.fill(-1)); + const f321 = new Float32Array(5); + const f322 = new Float32Array(5); + deepEquals(fillFallback(f321, 1.2345), f322.fill(1.2345)); + const f641 = new Float64Array(5); + const f642 = new Float64Array(5); + deepEquals(fillFallback(f641, 1.2345), f642.fill(1.2345)); + const u8Clamped1 = new Uint8ClampedArray(5); + const u8Clamped2 = new Uint8ClampedArray(5); + deepEquals(fillFallback(u8Clamped1, 2), u8Clamped2.fill(2)); + deepEquals(fillFallback(u8Clamped1, 257), u8Clamped2.fill(257)); + }); + it('start offset', function(): void { + for (let i = -2; i < 10; ++i) { + const u81 = new Uint8Array(5); + const u83 = new Uint8Array(5); + deepEquals(fillFallback(u81, 2, i), u83.fill(2, i)); + deepEquals(fillFallback(u81, -1, i), u83.fill(-1, i)); + } + }); + it('end offset', function(): void { + for (let i = -2; i < 10; ++i) { + const u81 = new Uint8Array(5); + const u83 = new Uint8Array(5); + deepEquals(fillFallback(u81, 2, 0, i), u83.fill(2, 0, i)); + deepEquals(fillFallback(u81, -1, 0, i), u83.fill(-1, 0, i)); + } + }); + it('start/end offset', function(): void { + for (let i = -2; i < 10; ++i) { + for (let j = -2; j < 10; ++j) { + const u81 = new Uint8Array(5); + const u83 = new Uint8Array(5); + deepEquals(fillFallback(u81, 2, i, j), u83.fill(2, i, j)); + deepEquals(fillFallback(u81, -1, i, j), u83.fill(-1, i, j)); + } + } + }); + }); +}); diff --git a/src/core/TypedArrayUtils.ts b/src/common/TypedArrayUtils.ts similarity index 73% rename from src/core/TypedArrayUtils.ts rename to src/common/TypedArrayUtils.ts index 56e9d7b0..6e1a3630 100644 --- a/src/core/TypedArrayUtils.ts +++ b/src/common/TypedArrayUtils.ts @@ -12,11 +12,15 @@ type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | Int8Array | Int16Array | Int32Array | Float32Array | Float64Array; -export function fill(array: TypedArray, value: number, start: number = 0, end?: number | undefined): TypedArray { +export function fill(array: T, value: number, start?: number, end?: number): T { // all modern engines that support .fill if (array.fill) { - return array.fill(value, start, end); + return array.fill(value, start, end) as T; } + return fillFallback(array, value, start, end); +} + +export function fillFallback(array: T, value: number, start: number = 0, end: number = array.length): T { // safari and IE 11 // since IE 11 does not support Array.prototype.fill either // we cannot use the suggested polyfill from MDN @@ -25,9 +29,6 @@ export function fill(array: TypedArray, value: number, start: number = 0, end?: return array; } start = (array.length + start) % array.length; - if (end === undefined) { - end = array.length; - } if (end >= array.length) { end = array.length; } else { diff --git a/src/core/TypedArrayUtils.test.ts b/src/core/TypedArrayUtils.test.ts deleted file mode 100644 index ef86d314..00000000 --- a/src/core/TypedArrayUtils.test.ts +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Copyright (c) 2018 The xterm.js authors. All rights reserved. - * @license MIT - */ -import { assert } from 'chai'; -import { fill } from './TypedArrayUtils'; - -type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray - | Int8Array | Int16Array | Int32Array - | Float32Array | Float64Array; - -// we explicitly test against the looping version in the test cases -function loopFill(array: TypedArray, value: number, start: number = 0, end?: number | undefined): TypedArray { - if (start >= array.length) { - return array; - } - start = (array.length + start) % array.length; - if (end === undefined) { - end = array.length; - } - if (end >= array.length) { - end = array.length; - } else { - end = (array.length + end) % array.length; - } - for (let i = start; i < end; ++i) { - array[i] = value; - } - return array; -} - -describe('polyfill conformance tests', function(): void { - - function deepEquals(a: TypedArray, b: TypedArray): void { - assert.equal(a.length, b.length); - for (let i = 0; i < a.length; ++i) { - assert.equal(a[i], b[i]); - } - } - - describe('TypedArray.fill', function(): void { - it('should work with all typed array types', function(): void { - const u81 = new Uint8Array(5); - const u82 = new Uint8Array(5); - deepEquals(fill(u81, 2), u82.fill(2)); - deepEquals(fill(u81, -1), u82.fill(-1)); - const u161 = new Uint16Array(5); - const u162 = new Uint16Array(5); - deepEquals(fill(u161, 2), u162.fill(2)); - deepEquals(fill(u161, 65535), u162.fill(65535)); - deepEquals(fill(u161, -1), u162.fill(-1)); - const u321 = new Uint32Array(5); - const u322 = new Uint32Array(5); - deepEquals(fill(u321, 2), u322.fill(2)); - deepEquals(fill(u321, 65537), u322.fill(65537)); - deepEquals(fill(u321, -1), u322.fill(-1)); - const i81 = new Int8Array(5); - const i82 = new Int8Array(5); - deepEquals(fill(i81, 2), i82.fill(2)); - deepEquals(fill(i81, -1), i82.fill(-1)); - const i161 = new Int16Array(5); - const i162 = new Int16Array(5); - deepEquals(fill(i161, 2), i162.fill(2)); - deepEquals(fill(i161, 65535), i162.fill(65535)); - deepEquals(fill(i161, -1), i162.fill(-1)); - const i321 = new Int32Array(5); - const i322 = new Int32Array(5); - deepEquals(fill(i321, 2), i322.fill(2)); - deepEquals(fill(i321, 65537), i322.fill(65537)); - deepEquals(fill(i321, -1), i322.fill(-1)); - const f321 = new Float32Array(5); - const f322 = new Float32Array(5); - deepEquals(fill(f321, 1.2345), f322.fill(1.2345)); - const f641 = new Float64Array(5); - const f642 = new Float64Array(5); - deepEquals(fill(f641, 1.2345), f642.fill(1.2345)); - const u8Clamped1 = new Uint8ClampedArray(5); - const u8Clamped2 = new Uint8ClampedArray(5); - deepEquals(fill(u8Clamped1, 2), u8Clamped2.fill(2)); - deepEquals(fill(u8Clamped1, 257), u8Clamped2.fill(257)); - }); - it('should work with all typed array types - explicit looping', function(): void { - const u81 = new Uint8Array(5); - const u82 = new Uint8Array(5); - deepEquals(loopFill(u81, 2), u82.fill(2)); - deepEquals(loopFill(u81, -1), u82.fill(-1)); - const u161 = new Uint16Array(5); - const u162 = new Uint16Array(5); - deepEquals(loopFill(u161, 2), u162.fill(2)); - deepEquals(loopFill(u161, 65535), u162.fill(65535)); - deepEquals(loopFill(u161, -1), u162.fill(-1)); - const u321 = new Uint32Array(5); - const u322 = new Uint32Array(5); - deepEquals(loopFill(u321, 2), u322.fill(2)); - deepEquals(loopFill(u321, 65537), u322.fill(65537)); - deepEquals(loopFill(u321, -1), u322.fill(-1)); - const i81 = new Int8Array(5); - const i82 = new Int8Array(5); - deepEquals(loopFill(i81, 2), i82.fill(2)); - deepEquals(loopFill(i81, -1), i82.fill(-1)); - const i161 = new Int16Array(5); - const i162 = new Int16Array(5); - deepEquals(loopFill(i161, 2), i162.fill(2)); - deepEquals(loopFill(i161, 65535), i162.fill(65535)); - deepEquals(loopFill(i161, -1), i162.fill(-1)); - const i321 = new Int32Array(5); - const i322 = new Int32Array(5); - deepEquals(loopFill(i321, 2), i322.fill(2)); - deepEquals(loopFill(i321, 65537), i322.fill(65537)); - deepEquals(loopFill(i321, -1), i322.fill(-1)); - const f321 = new Float32Array(5); - const f322 = new Float32Array(5); - deepEquals(loopFill(f321, 1.2345), f322.fill(1.2345)); - const f641 = new Float64Array(5); - const f642 = new Float64Array(5); - deepEquals(loopFill(f641, 1.2345), f642.fill(1.2345)); - const u8Clamped1 = new Uint8ClampedArray(5); - const u8Clamped2 = new Uint8ClampedArray(5); - deepEquals(loopFill(u8Clamped1, 2), u8Clamped2.fill(2)); - deepEquals(loopFill(u8Clamped1, 257), u8Clamped2.fill(257)); - }); - it('start offset', function(): void { - for (let i = -2; i < 10; ++i) { - const u81 = new Uint8Array(5); - const u82 = new Uint8Array(5); - const u83 = new Uint8Array(5); - deepEquals(fill(u81, 2, i), u83.fill(2, i)); - deepEquals(fill(u81, -1, i), u83.fill(-1, i)); - deepEquals(loopFill(u82, 2, i), u83.fill(2, i)); - deepEquals(loopFill(u82, -1, i), u83.fill(-1, i)); - } - }); - it('end offset', function(): void { - for (let i = -2; i < 10; ++i) { - const u81 = new Uint8Array(5); - const u82 = new Uint8Array(5); - const u83 = new Uint8Array(5); - deepEquals(fill(u81, 2, 0, i), u83.fill(2, 0, i)); - deepEquals(fill(u81, -1, 0, i), u83.fill(-1, 0, i)); - deepEquals(loopFill(u82, 2, 0, i), u83.fill(2, 0, i)); - deepEquals(loopFill(u82, -1, 0, i), u83.fill(-1, 0, i)); - } - }); - it('start/end offset', function(): void { - for (let i = -2; i < 10; ++i) { - for (let j = -2; j < 10; ++j) { - const u81 = new Uint8Array(5); - const u82 = new Uint8Array(5); - const u83 = new Uint8Array(5); - deepEquals(fill(u81, 2, i, j), u83.fill(2, i, j)); - deepEquals(fill(u81, -1, i, j), u83.fill(-1, i, j)); - deepEquals(loopFill(u82, 2, i, j), u83.fill(2, i, j)); - deepEquals(loopFill(u82, -1, i, j), u83.fill(-1, i, j)); - } - } - }); - }); -}); diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index 8226fd96..9286421e 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -33,7 +33,6 @@ export class AltClickHandler { this._mouseEvent, this._terminal.element, this._terminal.charMeasure, - this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows, false diff --git a/src/ui/MouseZoneManager.ts b/src/ui/MouseZoneManager.ts index 491e2a05..a232f5b9 100644 --- a/src/ui/MouseZoneManager.ts +++ b/src/ui/MouseZoneManager.ts @@ -180,7 +180,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { } private _findZoneEventAt(e: MouseEvent): IMouseZone { - const coords = this._terminal.mouseHelper.getCoords(e, this._terminal.screenElement, this._terminal.charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows); + const coords = this._terminal.mouseHelper.getCoords(e, this._terminal.screenElement, this._terminal.charMeasure, this._terminal.cols, this._terminal.rows); if (!coords) { return null; } diff --git a/src/utils/MouseHelper.test.ts b/src/utils/MouseHelper.test.ts index ac3137f4..94d63b2b 100644 --- a/src/utils/MouseHelper.test.ts +++ b/src/utils/MouseHelper.test.ts @@ -37,34 +37,34 @@ describe('MouseHelper.getCoords', () => { describe('when charMeasure is not initialized', () => { it('should return null', () => { charMeasure = new MockCharMeasure(); - assert.equal(mouseHelper.getCoords({ pageX: 0, pageY: 0 }, document.createElement('div'), charMeasure, 1, 10, 10), null); + assert.equal(mouseHelper.getCoords({ pageX: 0, pageY: 0 }, document.createElement('div'), charMeasure, 10, 10), null); }); }); describe('when pageX/pageY are not supported', () => { it('should return null', () => { - assert.equal(mouseHelper.getCoords({ pageX: undefined, pageY: undefined }, document.createElement('div'), charMeasure, 1, 10, 10), null); + assert.equal(mouseHelper.getCoords({ pageX: undefined, pageY: undefined }, document.createElement('div'), charMeasure, 10, 10), null); }); }); it('should return the cell that was clicked', () => { let coords: [number, number]; - coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH / 2, pageY: CHAR_HEIGHT / 2 }, document.createElement('div'), charMeasure, 1, 10, 10); + coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH / 2, pageY: CHAR_HEIGHT / 2 }, document.createElement('div'), charMeasure, 10, 10); assert.deepEqual(coords, [1, 1]); - coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH, pageY: CHAR_HEIGHT }, document.createElement('div'), charMeasure, 1, 10, 10); + coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH, pageY: CHAR_HEIGHT }, document.createElement('div'), charMeasure, 10, 10); assert.deepEqual(coords, [1, 1]); - coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH, pageY: CHAR_HEIGHT + 1 }, document.createElement('div'), charMeasure, 1, 10, 10); + coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH, pageY: CHAR_HEIGHT + 1 }, document.createElement('div'), charMeasure, 10, 10); assert.deepEqual(coords, [1, 2]); - coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH + 1, pageY: CHAR_HEIGHT }, document.createElement('div'), charMeasure, 1, 10, 10); + coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH + 1, pageY: CHAR_HEIGHT }, document.createElement('div'), charMeasure, 10, 10); assert.deepEqual(coords, [2, 1]); }); it('should ensure the coordinates are returned within the terminal bounds', () => { let coords: [number, number]; - coords = mouseHelper.getCoords({ pageX: -1, pageY: -1 }, document.createElement('div'), charMeasure, 1, 10, 10); + coords = mouseHelper.getCoords({ pageX: -1, pageY: -1 }, document.createElement('div'), charMeasure, 10, 10); assert.deepEqual(coords, [1, 1]); // Event are double the cols/rows - coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH * 20, pageY: CHAR_HEIGHT * 20 }, document.createElement('div'), charMeasure, 1, 10, 10); + coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH * 20, pageY: CHAR_HEIGHT * 20 }, document.createElement('div'), charMeasure, 10, 10); assert.deepEqual(coords, [10, 10], 'coordinates should never come back as larger than the terminal'); }); }); diff --git a/src/utils/MouseHelper.ts b/src/utils/MouseHelper.ts index ca1bb27e..e4b3f211 100644 --- a/src/utils/MouseHelper.ts +++ b/src/utils/MouseHelper.ts @@ -52,7 +52,7 @@ export class MouseHelper { * apply an offset to the x value such that the left half of the cell will * select that cell and the right half will select the next cell. */ - public getCoords(event: {pageX: number, pageY: number}, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number, isSelection?: boolean): [number, number] { + public getCoords(event: {pageX: number, pageY: number}, element: HTMLElement, charMeasure: ICharMeasure, colCount: number, rowCount: number, isSelection?: boolean): [number, number] { // Coordinates cannot be measured if charMeasure has not been initialized if (!charMeasure.width || !charMeasure.height) { return null; @@ -85,8 +85,8 @@ export class MouseHelper { * @param colCount The number of columns in the terminal. * @param rowCount The number of rows in the terminal. */ - public getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number): { x: number, y: number } { - const coords = this.getCoords(event, element, charMeasure, lineHeight, colCount, rowCount); + public getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, colCount: number, rowCount: number): { x: number, y: number } { + const coords = this.getCoords(event, element, charMeasure, colCount, rowCount); let x = coords[0]; let y = coords[1]; diff --git a/src/utils/TestUtils.test.ts b/src/utils/TestUtils.test.ts index 353e615f..a5ef4b9f 100644 --- a/src/utils/TestUtils.test.ts +++ b/src/utils/TestUtils.test.ts @@ -182,7 +182,6 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal { wraparoundMode: boolean; bracketedPasteMode: boolean; curAttr: number; - savedCurAttr: number; savedCols: number; x10Mouse: boolean; vt200Mouse: boolean; @@ -304,6 +303,7 @@ export class MockBuffer implements IBuffer { scrollTop: number; savedY: number; savedX: number; + savedCurAttr: number; translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string { return Buffer.prototype.translateBufferLineToString.apply(this, arguments); }