Remove the canvas renderer

Fixes #4779
This commit is contained in:
Daniel Imms
2024-07-14 10:48:39 -07:00
parent 9d8bba7a18
commit 9712057b91
34 changed files with 31 additions and 2149 deletions
-2
View File
@@ -16,8 +16,6 @@
"test/playwright/tsconfig.json",
"addons/addon-attach/src/tsconfig.json",
"addons/addon-attach/test/tsconfig.json",
"addons/addon-canvas/src/tsconfig.json",
"addons/addon-canvas/test/tsconfig.json",
"addons/addon-clipboard/src/tsconfig.json",
"addons/addon-clipboard/test/tsconfig.json",
"addons/addon-fit/src/tsconfig.json",
-5
View File
@@ -32,9 +32,6 @@ jobs:
./addons/addon-attach/lib/* \
./addons/addon-attach/out/* \
./addons/addon-attach/out-*/* \
./addons/addon-canvas/lib/* \
./addons/addon-canvas/out/* \
./addons/addon-canvas/out-*/* \
./addons/addon-clipboard/lib/* \
./addons/addon-clipboard/out/* \
./addons/addon-clipboard/out-*/* \
@@ -209,8 +206,6 @@ jobs:
run: yarn test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=core
- name: Integration tests (addon-attach)
run: yarn test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-attach
- name: Integration tests (addon-canvas)
run: yarn test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-canvas
- name: Integration tests (addon-clipboard)
run: yarn test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-clipboard
- name: Integration tests (addon-fit)
-1
View File
@@ -77,7 +77,6 @@ terminal.loadAddon(new WebLinksAddon());
The xterm.js team maintains the following addons, but anyone can build them:
- [`@xterm/addon-attach`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-attach): Attaches to a server running a process via a websocket
- [`@xterm/addon-canvas`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-canvas): Renders xterm.js using a `canvas` element's 2d context
- [`@xterm/addon-clipboard`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-clipboard): Access the browser's clipboard
- [`@xterm/addon-fit`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-fit): Fits the terminal to the containing element
- [`@xterm/addon-image`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-image): Adds image support
-2
View File
@@ -1,2 +0,0 @@
lib
node_modules
-32
View File
@@ -1,32 +0,0 @@
# Blacklist - exclude everything except npm defaults such as LICENSE, etc
*
!*/
# Whitelist - lib/
!lib/**/*.d.ts
!lib/**/*.js
!lib/**/*.js.map
!lib/**/*.mjs
!lib/**/*.mjs.map
!lib/**/*.css
# Whitelist - src/
!src/**/*.ts
!src/**/*.d.ts
!src/**/*.js
!src/**/*.js.map
!src/**/*.css
# Blacklist - src/ test files
src/**/*.test.ts
src/**/*.test.d.ts
src/**/*.test.js
src/**/*.test.js.map
# Whitelist - typings/
!typings/*.d.ts
-19
View File
@@ -1,19 +0,0 @@
Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-28
View File
@@ -1,28 +0,0 @@
## @xterm/addon-canvas
An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables a canvas-based renderer using a 2d context to draw. This addon requires xterm.js v5+.
The purpose of this addon is to be used as a fallback for the [webgl addon](https://www.npmjs.com/package/@xterm/addon-webgl) when better performance is desired over the default DOM renderer, but WebGL2 isn't supported or performant for some reason.
### Install
```bash
npm install --save @xterm/addon-canvas
```
### Usage
```ts
import { Terminal } from '@xterm/xterm';
import { CanvasAddon } from '@xterm/addon-canvas';
const terminal = new Terminal();
terminal.open(element);
terminal.loadAddon(new CanvasAddon());
```
See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-canvas/typings/addon-canvas.d.ts) for more advanced usage.
### See also
- [@xterm/addon-webgl](https://www.npmjs.com/package/@xterm/addon-webgl) A renderer for xterm.js that uses WebGL
-29
View File
@@ -1,29 +0,0 @@
{
"name": "@xterm/addon-canvas",
"version": "0.7.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
},
"main": "lib/addon-canvas.js",
"module": "lib/addon-canvas.mjs",
"types": "typings/addon-canvas.d.ts",
"repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-canvas",
"license": "MIT",
"keywords": [
"terminal",
"canvas",
"xterm",
"xterm.js"
],
"scripts": {
"build": "../../node_modules/.bin/tsc -p .",
"prepackage": "npm run build",
"package": "../../node_modules/.bin/webpack",
"prepublishOnly": "npm run package",
"start": "node ../../demo/start"
},
"peerDependencies": {
"@xterm/xterm": "^5.0.0"
}
}
File diff suppressed because it is too large Load Diff
-73
View File
@@ -1,73 +0,0 @@
/**
* Copyright (c) 2022 The xterm.js authors. All rights reserved.
* @license MIT
*/
import type { ITerminalAddon, Terminal } from '@xterm/xterm';
import type { CanvasAddon as ICanvasApi } from '@xterm/addon-canvas';
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
import { ITerminal } from 'browser/Types';
import { Disposable, toDisposable } from 'common/Lifecycle';
import { setTraceLogger } from 'common/services/LogService';
import { IBufferService, IDecorationService, ILogService } from 'common/services/Services';
import { CanvasRenderer } from './CanvasRenderer';
import { Emitter, Event } from 'vs/base/common/event';
export class CanvasAddon extends Disposable implements ITerminalAddon , ICanvasApi {
private _terminal?: Terminal;
private _renderer?: CanvasRenderer;
private readonly _onChangeTextureAtlas = this.register(new Emitter<HTMLCanvasElement>());
public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event;
private readonly _onAddTextureAtlasCanvas = this.register(new Emitter<HTMLCanvasElement>());
public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event;
public get textureAtlas(): HTMLCanvasElement | undefined {
return this._renderer?.textureAtlas;
}
public activate(terminal: Terminal): void {
const core = (terminal as any)._core as ITerminal;
if (!terminal.element) {
this.register(core.onWillOpen(() => this.activate(terminal)));
return;
}
this._terminal = terminal;
const coreService = core.coreService;
const optionsService = core.optionsService;
const screenElement = core.screenElement!;
const linkifier = core.linkifier!;
const unsafeCore = core as any;
const bufferService: IBufferService = unsafeCore._bufferService;
const renderService: IRenderService = unsafeCore._renderService;
const characterJoinerService: ICharacterJoinerService = unsafeCore._characterJoinerService;
const charSizeService: ICharSizeService = unsafeCore._charSizeService;
const coreBrowserService: ICoreBrowserService = unsafeCore._coreBrowserService;
const decorationService: IDecorationService = unsafeCore._decorationService;
const logService: ILogService = unsafeCore._logService;
const themeService: IThemeService = unsafeCore._themeService;
// Set trace logger just in case it hasn't been yet which could happen when the addon is
// bundled separately to the core module
setTraceLogger(logService);
this._renderer = new CanvasRenderer(terminal, screenElement, linkifier, bufferService, charSizeService, optionsService, characterJoinerService, coreService, coreBrowserService, decorationService, themeService);
this.register(Event.forward(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas));
this.register(Event.forward(this._renderer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas));
renderService.setRenderer(this._renderer);
renderService.handleResize(bufferService.cols, bufferService.rows);
this.register(toDisposable(() => {
renderService.setRenderer((this._terminal as any)._core._createRenderer());
renderService.handleResize(terminal.cols, terminal.rows);
this._renderer?.dispose();
this._renderer = undefined;
}));
}
public clearTextureAtlas(): void {
this._renderer?.clearTextureAtlas();
}
}
-191
View File
@@ -1,191 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ILinkifier2 } from 'browser/Types';
import { removeTerminalFromCache } from 'browser/renderer/shared/CharAtlasCache';
import { observeDevicePixelDimensions } from 'browser/renderer/shared/DevicePixelObserver';
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle';
import { IBufferService, ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
import { Terminal } from '@xterm/xterm';
import { CursorRenderLayer } from './CursorRenderLayer';
import { LinkRenderLayer } from './LinkRenderLayer';
import { SelectionRenderLayer } from './SelectionRenderLayer';
import { TextRenderLayer } from './TextRenderLayer';
import { IRenderLayer } from './Types';
import { Emitter, Event } from 'vs/base/common/event';
export class CanvasRenderer extends Disposable implements IRenderer {
private _renderLayers: IRenderLayer[];
private _devicePixelRatio: number;
private _observerDisposable = this.register(new MutableDisposable());
public dimensions: IRenderDimensions;
private readonly _onRequestRedraw = this.register(new Emitter<IRequestRedrawEvent>());
public readonly onRequestRedraw = this._onRequestRedraw.event;
private readonly _onChangeTextureAtlas = this.register(new Emitter<HTMLCanvasElement>());
public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event;
private readonly _onAddTextureAtlasCanvas = this.register(new Emitter<HTMLCanvasElement>());
public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event;
constructor(
private readonly _terminal: Terminal,
private readonly _screenElement: HTMLElement,
linkifier2: ILinkifier2,
private readonly _bufferService: IBufferService,
private readonly _charSizeService: ICharSizeService,
private readonly _optionsService: IOptionsService,
characterJoinerService: ICharacterJoinerService,
coreService: ICoreService,
private readonly _coreBrowserService: ICoreBrowserService,
decorationService: IDecorationService,
private readonly _themeService: IThemeService
) {
super();
const allowTransparency = this._optionsService.rawOptions.allowTransparency;
this._renderLayers = [
new TextRenderLayer(this._terminal, this._screenElement, 0, allowTransparency, this._bufferService, this._optionsService, characterJoinerService, decorationService, this._coreBrowserService, _themeService),
new SelectionRenderLayer(this._terminal, this._screenElement, 1, this._bufferService, this._coreBrowserService, decorationService, this._optionsService, _themeService),
new LinkRenderLayer(this._terminal, this._screenElement, 2, linkifier2, this._bufferService, this._optionsService, decorationService, this._coreBrowserService, _themeService),
new CursorRenderLayer(this._terminal, this._screenElement, 3, this._onRequestRedraw, this._bufferService, this._optionsService, coreService, this._coreBrowserService, decorationService, _themeService)
];
for (const layer of this._renderLayers) {
Event.forward(layer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas);
}
this.dimensions = createRenderDimensions();
this._devicePixelRatio = this._coreBrowserService.dpr;
this._updateDimensions();
this._observerDisposable.value = observeDevicePixelDimensions(this._renderLayers[0].canvas, this._coreBrowserService.window, (w, h) => this._setCanvasDevicePixelDimensions(w, h));
this.register(this._coreBrowserService.onWindowChange(w => {
this._observerDisposable.value = observeDevicePixelDimensions(this._renderLayers[0].canvas, w, (w, h) => this._setCanvasDevicePixelDimensions(w, h));
}));
this.register(toDisposable(() => {
for (const l of this._renderLayers) {
l.dispose();
}
removeTerminalFromCache(this._terminal);
}));
}
public get textureAtlas(): HTMLCanvasElement | undefined {
return this._renderLayers[0].cacheCanvas;
}
public handleDevicePixelRatioChange(): void {
// If the device pixel ratio changed, the char atlas needs to be regenerated
// and the terminal needs to refreshed
if (this._devicePixelRatio !== this._coreBrowserService.dpr) {
this._devicePixelRatio = this._coreBrowserService.dpr;
this.handleResize(this._bufferService.cols, this._bufferService.rows);
}
}
public handleResize(cols: number, rows: number): void {
// Update character and canvas dimensions
this._updateDimensions();
// Resize all render layers
for (const l of this._renderLayers) {
l.resize(this.dimensions);
}
// Resize the screen
this._screenElement.style.width = `${this.dimensions.css.canvas.width}px`;
this._screenElement.style.height = `${this.dimensions.css.canvas.height}px`;
}
public handleCharSizeChanged(): void {
this.handleResize(this._bufferService.cols, this._bufferService.rows);
}
public handleBlur(): void {
this._runOperation(l => l.handleBlur());
}
public handleFocus(): void {
this._runOperation(l => l.handleFocus());
}
public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {
this._runOperation(l => l.handleSelectionChanged(start, end, columnSelectMode));
// Selection foreground requires a full re-render
if (this._themeService.colors.selectionForeground) {
this._onRequestRedraw.fire({ start: 0, end: this._bufferService.rows - 1 });
}
}
public handleCursorMove(): void {
this._runOperation(l => l.handleCursorMove());
}
public clear(): void {
this._runOperation(l => l.reset());
}
private _runOperation(operation: (layer: IRenderLayer) => void): void {
for (const l of this._renderLayers) {
operation(l);
}
}
/**
* Performs the refresh loop callback, calling refresh only if a refresh is
* necessary before queueing up the next one.
*/
public renderRows(start: number, end: number): void {
for (const l of this._renderLayers) {
l.handleGridChanged(start, end);
}
}
public clearTextureAtlas(): void {
for (const layer of this._renderLayers) {
layer.clearTextureAtlas();
}
}
/**
* Recalculates the character and canvas dimensions.
*/
private _updateDimensions(): void {
if (!this._charSizeService.hasValidSize) {
return;
}
// See the WebGL renderer for an explanation of this section.
const dpr = this._coreBrowserService.dpr;
this.dimensions.device.char.width = Math.floor(this._charSizeService.width * dpr);
this.dimensions.device.char.height = Math.ceil(this._charSizeService.height * dpr);
this.dimensions.device.cell.height = Math.floor(this.dimensions.device.char.height * this._optionsService.rawOptions.lineHeight);
this.dimensions.device.char.top = this._optionsService.rawOptions.lineHeight === 1 ? 0 : Math.round((this.dimensions.device.cell.height - this.dimensions.device.char.height) / 2);
this.dimensions.device.cell.width = this.dimensions.device.char.width + Math.round(this._optionsService.rawOptions.letterSpacing);
this.dimensions.device.char.left = Math.floor(this._optionsService.rawOptions.letterSpacing / 2);
this.dimensions.device.canvas.height = this._bufferService.rows * this.dimensions.device.cell.height;
this.dimensions.device.canvas.width = this._bufferService.cols * this.dimensions.device.cell.width;
this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / dpr);
this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / dpr);
this.dimensions.css.cell.height = this.dimensions.css.canvas.height / this._bufferService.rows;
this.dimensions.css.cell.width = this.dimensions.css.canvas.width / this._bufferService.cols;
}
private _setCanvasDevicePixelDimensions(width: number, height: number): void {
this.dimensions.device.canvas.height = height;
this.dimensions.device.canvas.width = width;
// Resize all render layers
for (const l of this._renderLayers) {
l.resize(this.dimensions);
}
this._requestRedrawViewport();
}
private _requestRedrawViewport(): void {
this._onRequestRedraw.fire({ start: 0, end: this._bufferService.rows - 1 });
}
}
@@ -1,233 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { CursorBlinkStateManager } from 'browser/renderer/shared/CursorBlinkStateManager';
import { IRenderDimensions, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { MutableDisposable } from 'common/Lifecycle';
import { isFirefox } from 'common/Platform';
import { ICellData } from 'common/Types';
import { CellData } from 'common/buffer/CellData';
import { IBufferService, ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
import { Terminal } from '@xterm/xterm';
import { BaseRenderLayer } from './BaseRenderLayer';
import type { Emitter } from 'vs/base/common/event';
interface ICursorState {
x: number;
y: number;
isFocused: boolean;
style: string;
width: number;
}
export class CursorRenderLayer extends BaseRenderLayer {
private _state: ICursorState;
private _cursorRenderers: {[key: string]: (x: number, y: number, cell: ICellData) => void};
private _cursorBlinkStateManager: MutableDisposable<CursorBlinkStateManager> = this.register(new MutableDisposable());
private _cell: ICellData = new CellData();
constructor(
terminal: Terminal,
container: HTMLElement,
zIndex: number,
private readonly _onRequestRedraw: Emitter<IRequestRedrawEvent>,
bufferService: IBufferService,
optionsService: IOptionsService,
private readonly _coreService: ICoreService,
coreBrowserService: ICoreBrowserService,
decorationService: IDecorationService,
themeService: IThemeService
) {
super(terminal, container, 'cursor', zIndex, true, themeService, bufferService, optionsService, decorationService, coreBrowserService);
this._state = {
x: 0,
y: 0,
isFocused: false,
style: '',
width: 0
};
this._cursorRenderers = {
'bar': this._renderBarCursor.bind(this),
'block': this._renderBlockCursor.bind(this),
'underline': this._renderUnderlineCursor.bind(this),
'outline': this._renderOutlineCursor.bind(this)
};
this.register(optionsService.onOptionChange(() => this._handleOptionsChanged()));
this._handleOptionsChanged();
}
public resize(dim: IRenderDimensions): void {
super.resize(dim);
// Resizing the canvas discards the contents of the canvas so clear state
this._state = {
x: 0,
y: 0,
isFocused: false,
style: '',
width: 0
};
}
public reset(): void {
this._clearCursor();
this._cursorBlinkStateManager.value?.restartBlinkAnimation();
this._handleOptionsChanged();
}
public handleBlur(): void {
this._cursorBlinkStateManager.value?.pause();
this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y });
}
public handleFocus(): void {
this._cursorBlinkStateManager.value?.resume();
this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y });
}
private _handleOptionsChanged(): void {
if (this._optionsService.rawOptions.cursorBlink) {
if (!this._cursorBlinkStateManager.value) {
this._cursorBlinkStateManager.value = new CursorBlinkStateManager(() => this._render(true), this._coreBrowserService);
}
} else {
this._cursorBlinkStateManager.clear();
}
// Request a refresh from the terminal as management of rendering is being
// moved back to the terminal
this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y });
}
public handleCursorMove(): void {
this._cursorBlinkStateManager.value?.restartBlinkAnimation();
}
public handleGridChanged(startRow: number, endRow: number): void {
if (!this._cursorBlinkStateManager.value || this._cursorBlinkStateManager.value.isPaused) {
this._render(false);
} else {
this._cursorBlinkStateManager.value.restartBlinkAnimation();
}
}
private _render(triggeredByAnimationFrame: boolean): void {
// Don't draw the cursor if it's hidden
if (!this._coreService.isCursorInitialized || this._coreService.isCursorHidden) {
this._clearCursor();
return;
}
const cursorY = this._bufferService.buffer.ybase + this._bufferService.buffer.y;
const viewportRelativeCursorY = cursorY - this._bufferService.buffer.ydisp;
// Don't draw the cursor if it's off-screen
if (viewportRelativeCursorY < 0 || viewportRelativeCursorY >= this._bufferService.rows) {
this._clearCursor();
return;
}
// in case cursor.x == cols adjust visual cursor to cols - 1
const cursorX = Math.min(this._bufferService.buffer.x, this._bufferService.cols - 1);
this._bufferService.buffer.lines.get(cursorY)!.loadCell(cursorX, this._cell);
if (this._cell.content === undefined) {
return;
}
if (!this._coreBrowserService.isFocused) {
this._clearCursor();
this._ctx.save();
this._ctx.fillStyle = this._themeService.colors.cursor.css;
const cursorStyle = this._optionsService.rawOptions.cursorStyle;
const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
if (cursorInactiveStyle && cursorInactiveStyle !== 'none') {
this._cursorRenderers[cursorInactiveStyle](cursorX, viewportRelativeCursorY, this._cell);
}
this._ctx.restore();
this._state.x = cursorX;
this._state.y = viewportRelativeCursorY;
this._state.isFocused = false;
this._state.style = cursorStyle;
this._state.width = this._cell.getWidth();
return;
}
// Don't draw the cursor if it's blinking
if (this._cursorBlinkStateManager.value && !this._cursorBlinkStateManager.value.isCursorVisible) {
this._clearCursor();
return;
}
if (this._state) {
// The cursor is already in the correct spot, don't redraw
if (this._state.x === cursorX &&
this._state.y === viewportRelativeCursorY &&
this._state.isFocused === this._coreBrowserService.isFocused &&
this._state.style === this._optionsService.rawOptions.cursorStyle &&
this._state.width === this._cell.getWidth()) {
return;
}
this._clearCursor();
}
this._ctx.save();
this._cursorRenderers[this._optionsService.rawOptions.cursorStyle || 'block'](cursorX, viewportRelativeCursorY, this._cell);
this._ctx.restore();
this._state.x = cursorX;
this._state.y = viewportRelativeCursorY;
this._state.isFocused = false;
this._state.style = this._optionsService.rawOptions.cursorStyle;
this._state.width = this._cell.getWidth();
}
private _clearCursor(): void {
if (this._state) {
// Avoid potential rounding errors when browser is Firefox (#4487) or device pixel ratio is
// less than 1
if (isFirefox || this._coreBrowserService.dpr < 1) {
this._clearAll();
} else {
this._clearCells(this._state.x, this._state.y, this._state.width, 1);
}
this._state = {
x: 0,
y: 0,
isFocused: false,
style: '',
width: 0
};
}
}
private _renderBarCursor(x: number, y: number, cell: ICellData): void {
this._ctx.save();
this._ctx.fillStyle = this._themeService.colors.cursor.css;
this._fillLeftLineAtCell(x, y, this._optionsService.rawOptions.cursorWidth);
this._ctx.restore();
}
private _renderBlockCursor(x: number, y: number, cell: ICellData): void {
this._ctx.save();
this._ctx.fillStyle = this._themeService.colors.cursor.css;
this._fillCells(x, y, cell.getWidth(), 1);
this._ctx.fillStyle = this._themeService.colors.cursorAccent.css;
this._fillCharTrueColor(cell, x, y);
this._ctx.restore();
}
private _renderUnderlineCursor(x: number, y: number, cell: ICellData): void {
this._ctx.save();
this._ctx.fillStyle = this._themeService.colors.cursor.css;
this._fillBottomLineAtCells(x, y);
this._ctx.restore();
}
private _renderOutlineCursor(x: number, y: number, cell: ICellData): void {
this._ctx.save();
this._ctx.strokeStyle = this._themeService.colors.cursor.css;
this._strokeRectAtCell(x, y, cell.getWidth(), 1);
this._ctx.restore();
}
}
-62
View File
@@ -1,62 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { GridCache } from './GridCache';
describe('GridCache', () => {
let grid: GridCache<number>;
beforeEach(() => {
grid = new GridCache<number>();
});
describe('constructor', () => {
it('should create an empty cache', () => {
assert.equal(grid.cache.length, 0);
});
});
describe('resize', () => {
it('should fill all new elements with null', () => {
grid.resize(2, 2);
assert.equal(grid.cache.length, 2);
assert.equal(grid.cache[0].length, 2);
assert.equal(grid.cache[0][0], null);
assert.equal(grid.cache[0][1], null);
assert.equal(grid.cache[1].length, 2);
assert.equal(grid.cache[1][0], null);
assert.equal(grid.cache[1][1], null);
grid.resize(3, 2);
assert.equal(grid.cache.length, 3);
assert.equal(grid.cache[2][0], null);
assert.equal(grid.cache[2][1], null);
});
it('should remove rows/cols from the cache when reduced', () => {
grid.resize(2, 2);
grid.resize(1, 1);
assert.equal(grid.cache.length, 1);
assert.equal(grid.cache[0].length, 1);
});
it('should not touch existing cache entries if they fit in the new cache', () => {
grid.resize(1, 1);
assert.equal(grid.cache[0][0], null);
grid.cache[0][0] = 1;
grid.resize(2, 1);
assert.equal(grid.cache[0][0], 1);
});
});
describe('clear', () => {
it('should make all entries null', () => {
grid.resize(1, 1);
grid.cache[0][0] = 1;
grid.clear();
assert.equal(grid.cache[0][0], null);
});
});
});
-33
View File
@@ -1,33 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
export class GridCache<T> {
public cache: (T | undefined)[][];
public constructor() {
this.cache = [];
}
public resize(width: number, height: number): void {
for (let x = 0; x < width; x++) {
if (this.cache.length <= x) {
this.cache.push([]);
}
for (let y = this.cache[x].length; y < height; y++) {
this.cache[x].push(undefined);
}
this.cache[x].length = height;
}
this.cache.length = width;
}
public clear(): void {
for (let x = 0; x < this.cache.length; x++) {
for (let y = 0; y < this.cache[x].length; y++) {
this.cache[x][y] = undefined;
}
}
}
}
@@ -1,84 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ILinkifier2, ILinkifierEvent } from 'browser/Types';
import { is256Color } from 'browser/renderer/shared/CharAtlasUtils';
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants';
import { IRenderDimensions } from 'browser/renderer/shared/Types';
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
import { Terminal } from '@xterm/xterm';
import { BaseRenderLayer } from './BaseRenderLayer';
export class LinkRenderLayer extends BaseRenderLayer {
private _state: ILinkifierEvent | undefined;
constructor(
terminal: Terminal,
container: HTMLElement,
zIndex: number,
linkifier2: ILinkifier2,
bufferService: IBufferService,
optionsService: IOptionsService,
decorationService: IDecorationService,
coreBrowserService: ICoreBrowserService,
themeService: IThemeService
) {
super(terminal, container, 'link', zIndex, true, themeService, bufferService, optionsService, decorationService, coreBrowserService);
this.register(linkifier2.onShowLinkUnderline(e => this._handleShowLinkUnderline(e)));
this.register(linkifier2.onHideLinkUnderline(e => this._handleHideLinkUnderline(e)));
}
public resize(dim: IRenderDimensions): void {
super.resize(dim);
// Resizing the canvas discards the contents of the canvas so clear state
this._state = undefined;
}
public reset(): void {
this._clearCurrentLink();
}
private _clearCurrentLink(): void {
if (this._state) {
this._clearCells(this._state.x1, this._state.y1, this._state.cols - this._state.x1, 1);
const middleRowCount = this._state.y2 - this._state.y1 - 1;
if (middleRowCount > 0) {
this._clearCells(0, this._state.y1 + 1, this._state.cols, middleRowCount);
}
this._clearCells(0, this._state.y2, this._state.x2, 1);
this._state = undefined;
}
}
private _handleShowLinkUnderline(e: ILinkifierEvent): void {
if (e.fg === INVERTED_DEFAULT_COLOR) {
this._ctx.fillStyle = this._themeService.colors.background.css;
} else if (e.fg && is256Color(e.fg)) {
// 256 color support
this._ctx.fillStyle = this._themeService.colors.ansi[e.fg].css;
} else {
this._ctx.fillStyle = this._themeService.colors.foreground.css;
}
if (e.y1 === e.y2) {
// Single line link
this._fillBottomLineAtCells(e.x1, e.y1, e.x2 - e.x1);
} else {
// Multi-line link
this._fillBottomLineAtCells(e.x1, e.y1, e.cols - e.x1);
for (let y = e.y1 + 1; y < e.y2; y++) {
this._fillBottomLineAtCells(0, y, e.cols);
}
this._fillBottomLineAtCells(0, e.y2, e.x2);
}
this._state = e;
}
private _handleHideLinkUnderline(e: ILinkifierEvent): void {
this._clearCurrentLink();
}
}
@@ -1,152 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IRenderDimensions } from 'browser/renderer/shared/Types';
import { BaseRenderLayer } from './BaseRenderLayer';
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { Terminal } from '@xterm/xterm';
interface ISelectionState {
start?: [number, number];
end?: [number, number];
columnSelectMode?: boolean;
ydisp?: number;
}
export class SelectionRenderLayer extends BaseRenderLayer {
private _state!: ISelectionState;
constructor(
terminal: Terminal,
container: HTMLElement,
zIndex: number,
bufferService: IBufferService,
coreBrowserService: ICoreBrowserService,
decorationService: IDecorationService,
optionsService: IOptionsService,
themeService: IThemeService
) {
super(terminal, container, 'selection', zIndex, true, themeService, bufferService, optionsService, decorationService, coreBrowserService);
this._clearState();
}
private _clearState(): void {
this._state = {
start: undefined,
end: undefined,
columnSelectMode: undefined,
ydisp: undefined
};
}
public resize(dim: IRenderDimensions): void {
super.resize(dim);
// On resize use the base render layer's cached selection values since resize clears _state
// inside reset.
if (this._selectionModel.selectionStart && this._selectionModel.selectionEnd) {
this._clearState();
this._redrawSelection(this._selectionModel.selectionStart, this._selectionModel.selectionEnd, this._selectionModel.columnSelectMode);
}
}
public reset(): void {
if (this._state.start && this._state.end) {
this._clearState();
this._clearAll();
}
}
public handleBlur(): void {
this.reset();
this._redrawSelection(this._selectionModel.selectionStart, this._selectionModel.selectionEnd, this._selectionModel.columnSelectMode);
}
public handleFocus(): void {
this.reset();
this._redrawSelection(this._selectionModel.selectionStart, this._selectionModel.selectionEnd, this._selectionModel.columnSelectMode);
}
public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
super.handleSelectionChanged(start, end, columnSelectMode);
this._redrawSelection(start, end, columnSelectMode);
}
private _redrawSelection(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
// Selection has not changed
if (!this._didStateChange(start, end, columnSelectMode, this._bufferService.buffer.ydisp)) {
return;
}
// Remove all selections
this._clearAll();
// Selection does not exist
if (!start || !end) {
this._clearState();
return;
}
// Translate from buffer position to viewport position
const viewportStartRow = start[1] - this._bufferService.buffer.ydisp;
const viewportEndRow = end[1] - this._bufferService.buffer.ydisp;
const viewportCappedStartRow = Math.max(viewportStartRow, 0);
const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1);
// No need to draw the selection
if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) {
this._state.ydisp = this._bufferService.buffer.ydisp;
return;
}
this._ctx.fillStyle = (this._coreBrowserService.isFocused
? this._themeService.colors.selectionBackgroundTransparent
: this._themeService.colors.selectionInactiveBackgroundTransparent).css;
if (columnSelectMode) {
const startCol = start[0];
const width = end[0] - startCol;
const height = viewportCappedEndRow - viewportCappedStartRow + 1;
this._fillCells(startCol, viewportCappedStartRow, width, height);
} else {
// Draw first row
const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
const startRowEndCol = viewportCappedStartRow === viewportEndRow ? end[0] : this._bufferService.cols;
this._fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1);
// Draw middle rows
const middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0);
this._fillCells(0, viewportCappedStartRow + 1, this._bufferService.cols, middleRowsCount);
// Draw final row
if (viewportCappedStartRow !== viewportCappedEndRow) {
// Only draw viewportEndRow if it's not the same as viewportStartRow
const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._bufferService.cols;
this._fillCells(0, viewportCappedEndRow, endCol, 1);
}
}
// Save state for next render
this._state.start = [start[0], start[1]];
this._state.end = [end[0], end[1]];
this._state.columnSelectMode = columnSelectMode;
this._state.ydisp = this._bufferService.buffer.ydisp;
}
private _didStateChange(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean, ydisp: number): boolean {
return !this._areCoordinatesEqual(start, this._state.start) ||
!this._areCoordinatesEqual(end, this._state.end) ||
columnSelectMode !== this._state.columnSelectMode ||
ydisp !== this._state.ydisp;
}
private _areCoordinatesEqual(coord1: [number, number] | undefined, coord2: [number, number] | undefined): boolean {
if (!coord1 || !coord2) {
return false;
}
return coord1[0] === coord2[0] && coord1[1] === coord2[1];
}
}
-292
View File
@@ -1,292 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IRenderDimensions } from 'browser/renderer/shared/Types';
import { JoinedCellData } from 'browser/services/CharacterJoinerService';
import { ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { CharData, ICellData } from 'common/Types';
import { AttributeData } from 'common/buffer/AttributeData';
import { CellData } from 'common/buffer/CellData';
import { Content, NULL_CELL_CODE } from 'common/buffer/Constants';
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
import { Terminal } from '@xterm/xterm';
import { BaseRenderLayer } from './BaseRenderLayer';
import { GridCache } from './GridCache';
/**
* This CharData looks like a null character, which will forc a clear and render
* when the character changes (a regular space ' ' character may not as it's
* drawn state is a cleared cell).
*/
// const OVERLAP_OWNED_CHAR_DATA: CharData = [null, '', 0, -1];
export class TextRenderLayer extends BaseRenderLayer {
private _state: GridCache<CharData>;
private _characterWidth: number = 0;
private _characterFont: string = '';
private _characterOverlapCache: { [key: string]: boolean } = {};
private _workCell = new CellData();
constructor(
terminal: Terminal,
container: HTMLElement,
zIndex: number,
alpha: boolean,
bufferService: IBufferService,
optionsService: IOptionsService,
private readonly _characterJoinerService: ICharacterJoinerService,
decorationService: IDecorationService,
coreBrowserService: ICoreBrowserService,
themeService: IThemeService
) {
super(terminal, container, 'text', zIndex, alpha, themeService, bufferService, optionsService, decorationService, coreBrowserService);
this._state = new GridCache<CharData>();
this.register(optionsService.onSpecificOptionChange('allowTransparency', value => this._setTransparency(value)));
}
public resize(dim: IRenderDimensions): void {
super.resize(dim);
// Clear the character width cache if the font or width has changed
const terminalFont = this._getFont(false, false);
if (this._characterWidth !== dim.device.char.width || this._characterFont !== terminalFont) {
this._characterWidth = dim.device.char.width;
this._characterFont = terminalFont;
this._characterOverlapCache = {};
}
// Resizing the canvas discards the contents of the canvas so clear state
this._state.clear();
this._state.resize(this._bufferService.cols, this._bufferService.rows);
}
public reset(): void {
this._state.clear();
this._clearAll();
}
private _forEachCell(
firstRow: number,
lastRow: number,
callback: (
cell: ICellData,
x: number,
y: number
) => void
): void {
for (let y = firstRow; y <= lastRow; y++) {
const row = y + this._bufferService.buffer.ydisp;
const line = this._bufferService.buffer.lines.get(row);
const joinedRanges = this._characterJoinerService.getJoinedCharacters(row);
for (let x = 0; x < this._bufferService.cols; x++) {
line!.loadCell(x, this._workCell);
let cell = this._workCell;
// If true, indicates that the current character(s) to draw were joined.
let isJoined = false;
let lastCharX = x;
// The character to the left is a wide character, drawing is owned by
// the char at x-1
if (cell.getWidth() === 0) {
continue;
}
// exit early for NULL and SP
// NOTE: commented out due to #4120 (needs a more clever patch to keep things performant)
// const code = cell.getCode();
// if (code === 0 || code === 32) {
// continue;
// }
// Process any joined character ranges as needed. Because of how the
// ranges are produced, we know that they are valid for the characters
// and attributes of our input.
if (joinedRanges.length > 0 && x === joinedRanges[0][0]) {
isJoined = true;
const range = joinedRanges.shift()!;
// We already know the exact start and end column of the joined range,
// so we get the string and width representing it directly
cell = new JoinedCellData(
this._workCell,
line!.translateToString(true, range[0], range[1]),
range[1] - range[0]
);
// Skip over the cells occupied by this range in the loop
lastCharX = range[1] - 1;
}
// If the character is an overlapping char and the character to the
// right is a space, take ownership of the cell to the right. We skip
// this check for joined characters because their rendering likely won't
// yield the same result as rendering the last character individually.
if (!isJoined && this._isOverlapping(cell)) {
// If the character is overlapping, we want to force a re-render on every
// frame. This is specifically to work around the case where two
// overlaping chars `a` and `b` are adjacent, the cursor is moved to b and a
// space is added. Without this, the first half of `b` would never
// get removed, and `a` would not re-render because it thinks it's
// already in the correct state.
// this._state.cache[x][y] = OVERLAP_OWNED_CHAR_DATA;
if (lastCharX < line!.length - 1 && line!.getCodePoint(lastCharX + 1) === NULL_CELL_CODE) {
// patch width to 2
cell.content &= ~Content.WIDTH_MASK;
cell.content |= 2 << Content.WIDTH_SHIFT;
// this._clearChar(x + 1, y);
// The overlapping char's char data will force a clear and render when the
// overlapping char is no longer to the left of the character and also when
// the space changes to another character.
// this._state.cache[x + 1][y] = OVERLAP_OWNED_CHAR_DATA;
}
}
callback(
cell,
x,
y
);
x = lastCharX;
}
}
}
/**
* Draws the background for a specified range of columns. Tries to batch adjacent cells of the
* same color together to reduce draw calls.
*/
private _drawBackground(firstRow: number, lastRow: number): void {
const ctx = this._ctx;
const cols = this._bufferService.cols;
let startX: number = 0;
let startY: number = 0;
let prevFillStyle: string | null = null;
ctx.save();
this._forEachCell(firstRow, lastRow, (cell, x, y) => {
// libvte and xterm both draw the background (but not foreground) of invisible characters,
// so we should too.
let nextFillStyle = null; // null represents default background color
if (cell.isInverse()) {
if (cell.isFgDefault()) {
nextFillStyle = this._themeService.colors.foreground.css;
} else if (cell.isFgRGB()) {
nextFillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;
} else {
nextFillStyle = this._themeService.colors.ansi[cell.getFgColor()].css;
}
} else if (cell.isBgRGB()) {
nextFillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`;
} else if (cell.isBgPalette()) {
nextFillStyle = this._themeService.colors.ansi[cell.getBgColor()].css;
}
// Get any decoration foreground/background overrides, this must be fetched before the early
// exist but applied after inverse
let isTop = false;
this._decorationService.forEachDecorationAtCell(x, this._bufferService.buffer.ydisp + y, undefined, d => {
if (d.options.layer !== 'top' && isTop) {
return;
}
if (d.backgroundColorRGB) {
nextFillStyle = d.backgroundColorRGB.css;
}
isTop = d.options.layer === 'top';
});
if (prevFillStyle === null) {
// This is either the first iteration, or the default background was set. Either way, we
// don't need to draw anything.
startX = x;
startY = y;
}
if (y !== startY) {
// our row changed, draw the previous row
ctx.fillStyle = prevFillStyle || '';
this._fillCells(startX, startY, cols - startX, 1);
startX = x;
startY = y;
} else if (prevFillStyle !== nextFillStyle) {
// our color changed, draw the previous characters in this row
ctx.fillStyle = prevFillStyle || '';
this._fillCells(startX, startY, x - startX, 1);
startX = x;
startY = y;
}
prevFillStyle = nextFillStyle;
});
// flush the last color we encountered
if (prevFillStyle !== null) {
ctx.fillStyle = prevFillStyle;
this._fillCells(startX, startY, cols - startX, 1);
}
ctx.restore();
}
private _drawForeground(firstRow: number, lastRow: number): void {
this._forEachCell(firstRow, lastRow, (cell, x, y) => this._drawChars(cell, x, y));
}
public handleGridChanged(firstRow: number, lastRow: number): void {
// Resize has not been called yet
if (this._state.cache.length === 0) {
return;
}
if (this._charAtlas) {
this._charAtlas.beginFrame();
}
this._clearCells(0, firstRow, this._bufferService.cols, lastRow - firstRow + 1);
this._drawBackground(firstRow, lastRow);
this._drawForeground(firstRow, lastRow);
}
/**
* Whether a character is overlapping to the next cell.
*/
private _isOverlapping(cell: ICellData): boolean {
// Only single cell characters can be overlapping, rendering issues can
// occur without this check
if (cell.getWidth() !== 1) {
return false;
}
// We assume that any ascii character will not overlap
if (cell.getCode() < 256) {
return false;
}
const chars = cell.getChars();
// Deliver from cache if available
if (this._characterOverlapCache.hasOwnProperty(chars)) {
return this._characterOverlapCache[chars];
}
// Setup the font
this._ctx.save();
this._ctx.font = this._characterFont;
// Measure the width of the character, but Math.floor it
// because that is what the renderer does when it calculates
// the character dimensions we are comparing against
const overlaps = Math.floor(this._ctx.measureText(chars).width) > this._characterWidth;
// Restore the original context
this._ctx.restore();
// Cache and return
this._characterOverlapCache[chars] = overlaps;
return overlaps;
}
}
-86
View File
@@ -1,86 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IDisposable } from 'common/Types';
import { IRenderDimensions } from 'browser/renderer/shared/Types';
import type { Event } from 'vs/base/common/event';
export interface IRequestRedrawEvent {
start: number;
end: number;
}
/**
* Note that IRenderer implementations should emit the refresh event after
* rendering rows to the screen.
*/
export interface IRenderer extends IDisposable {
readonly dimensions: IRenderDimensions;
/**
* Fires when the renderer is requesting to be redrawn on the next animation
* frame but is _not_ a result of content changing (eg. selection changes).
*/
readonly onRequestRedraw: Event<IRequestRedrawEvent>;
handleDevicePixelRatioChange(): void;
handleResize(cols: number, rows: number): void;
handleCharSizeChanged(): void;
handleBlur(): void;
handleFocus(): void;
handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void;
handleCursorMove(): void;
handleOptionsChanged(): void;
clear(): void;
renderRows(start: number, end: number): void;
clearTextureAtlas?(): void;
}
export interface IRenderLayer extends IDisposable {
readonly canvas: HTMLCanvasElement;
readonly cacheCanvas: HTMLCanvasElement;
readonly onAddTextureAtlasCanvas: Event<HTMLCanvasElement>;
/**
* Called when the terminal loses focus.
*/
handleBlur(): void;
/**
* Called when the terminal gets focus.
*/
handleFocus(): void;
/**
* Called when the cursor is moved.
*/
handleCursorMove(): void;
/**
* Called when the data in the grid has changed (or needs to be rendered
* again).
*/
handleGridChanged(startRow: number, endRow: number): void;
/**
* Calls when the selection changes.
*/
handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void;
/**
* Resize the render layer.
*/
resize(dim: IRenderDimensions): void;
/**
* Clear the state of the render layer.
*/
reset(): void;
/**
* Clears the texture atlas.
*/
clearTextureAtlas(): void;
}
-49
View File
@@ -1,49 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2021",
"lib": [
"dom",
"es2021"
],
"rootDir": ".",
"outDir": "../out",
"sourceMap": true,
"removeComments": true,
"baseUrl": ".",
"paths": {
"common/*": [
"../../../src/common/*"
],
"browser/*": [
"../../../src/browser/*"
],
"vs/*": [
"../../../src/vs/*"
],
"@xterm/addon-canvas": [
"../typings/addon-canvas.d.ts"
]
},
"strict": true,
"downlevelIteration": true,
"types": [
"../../../node_modules/@types/mocha"
]
},
"include": [
"./**/*",
"../../../typings/xterm.d.ts"
],
"references": [
{
"path": "../../../src/common"
},
{
"path": "../../../src/browser"
},
{
"path": "../../../src/vs"
}
]
}
@@ -1,37 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import test from '@playwright/test';
import { ISharedRendererTestContext, injectSharedRendererTests, injectSharedRendererTestsStandalone } from '../../../test/playwright/SharedRendererTests';
import { ITestContext, createTestContext, openTerminal } from '../../../test/playwright/TestUtils';
let ctx: ITestContext;
const ctxWrapper: ISharedRendererTestContext = {
value: undefined,
skipCanvasExceptions: true
} as any;
test.beforeAll(async ({ browser }) => {
ctx = await createTestContext(browser);
await openTerminal(ctx);
ctxWrapper.value = ctx;
await ctx.page.evaluate(`
window.addon = new window.CanvasAddon(true);
window.term.loadAddon(window.addon);
`);
});
test.afterAll(async () => await ctx.page.close());
test.describe('Canvas Renderer Integration Tests', () => {
// HACK: The tests fail for an unknown reason
test.skip(({ browserName }) => browserName === 'webkit');
injectSharedRendererTests(ctxWrapper);
injectSharedRendererTestsStandalone(ctxWrapper, async () => {
await ctx.page.evaluate(`
window.addon = new window.CanvasAddon(true);
window.term.loadAddon(window.addon);
`);
});
});

Some files were not shown because too many files have changed in this diff Show More