From 58dbc983615f358d69ae44ae0f031de2129c262c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 09:26:09 -0700 Subject: [PATCH 01/11] Move BaseCharAtlas to browser --- src/{ => browser}/renderer/atlas/BaseCharAtlas.ts | 4 ++-- src/browser/renderer/atlas/Types.d.ts | 14 ++++++++++++++ src/renderer/BaseRenderLayer.ts | 4 ++-- src/renderer/atlas/CharAtlasCache.ts | 2 +- src/renderer/atlas/DynamicCharAtlas.ts | 5 +++-- src/renderer/atlas/Types.d.ts | 10 ---------- 6 files changed, 22 insertions(+), 17 deletions(-) rename src/{ => browser}/renderer/atlas/BaseCharAtlas.ts (93%) create mode 100644 src/browser/renderer/atlas/Types.d.ts diff --git a/src/renderer/atlas/BaseCharAtlas.ts b/src/browser/renderer/atlas/BaseCharAtlas.ts similarity index 93% rename from src/renderer/atlas/BaseCharAtlas.ts rename to src/browser/renderer/atlas/BaseCharAtlas.ts index 470736f1..dc114601 100644 --- a/src/renderer/atlas/BaseCharAtlas.ts +++ b/src/browser/renderer/atlas/BaseCharAtlas.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { IGlyphIdentifier } from './Types'; -import { IDisposable } from 'xterm'; +import { IGlyphIdentifier } from 'browser/renderer/atlas/Types'; +import { IDisposable } from 'common/Types'; export abstract class BaseCharAtlas implements IDisposable { private _didWarmUp: boolean = false; diff --git a/src/browser/renderer/atlas/Types.d.ts b/src/browser/renderer/atlas/Types.d.ts new file mode 100644 index 00000000..fd5b596c --- /dev/null +++ b/src/browser/renderer/atlas/Types.d.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +export interface IGlyphIdentifier { + chars: string; + code: number; + bg: number; + fg: number; + bold: boolean; + dim: boolean; + italic: boolean; +} diff --git a/src/renderer/BaseRenderLayer.ts b/src/renderer/BaseRenderLayer.ts index 767ef08c..e704375c 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/renderer/BaseRenderLayer.ts @@ -8,9 +8,9 @@ import { IRenderDimensions } from 'browser/renderer/Types'; import { ITerminal } from '../Types'; import { ICellData } from 'common/Types'; import { DEFAULT_COLOR, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE } from 'common/buffer/Constants'; -import { IGlyphIdentifier } from './atlas/Types'; +import { IGlyphIdentifier } from 'browser/renderer/atlas/Types'; import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; -import { BaseCharAtlas } from './atlas/BaseCharAtlas'; +import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas'; import { acquireCharAtlas } from './atlas/CharAtlasCache'; import { AttributeData } from 'common/buffer/AttributeData'; import { IColorSet } from 'browser/Types'; diff --git a/src/renderer/atlas/CharAtlasCache.ts b/src/renderer/atlas/CharAtlasCache.ts index 43b5eb83..f9bd68a1 100644 --- a/src/renderer/atlas/CharAtlasCache.ts +++ b/src/renderer/atlas/CharAtlasCache.ts @@ -5,7 +5,7 @@ import { ITerminal } from '../../Types'; import { generateConfig, configEquals } from './CharAtlasUtils'; -import { BaseCharAtlas } from './BaseCharAtlas'; +import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas'; import { DynamicCharAtlas } from './DynamicCharAtlas'; import { ICharAtlasConfig } from './Types'; import { IColorSet } from 'browser/Types'; diff --git a/src/renderer/atlas/DynamicCharAtlas.ts b/src/renderer/atlas/DynamicCharAtlas.ts index 6e669d91..9af79e01 100644 --- a/src/renderer/atlas/DynamicCharAtlas.ts +++ b/src/renderer/atlas/DynamicCharAtlas.ts @@ -3,9 +3,10 @@ * @license MIT */ -import { IGlyphIdentifier, ICharAtlasConfig } from './Types'; +import { ICharAtlasConfig } from './Types'; import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; -import { BaseCharAtlas } from './BaseCharAtlas'; +import { IGlyphIdentifier } from 'browser/renderer/atlas/Types'; +import { BaseCharAtlas } from '../../browser/renderer/atlas/BaseCharAtlas'; import { DEFAULT_ANSI_COLORS } from 'browser/ColorManager'; import { LRUMap } from 'browser/renderer/atlas/LRUMap'; import { isFirefox, isSafari } from 'common/Platform'; diff --git a/src/renderer/atlas/Types.d.ts b/src/renderer/atlas/Types.d.ts index 1de843e0..f717381c 100644 --- a/src/renderer/atlas/Types.d.ts +++ b/src/renderer/atlas/Types.d.ts @@ -6,16 +6,6 @@ import { FontWeight } from 'xterm'; import { IColorSet } from 'browser/Types'; -export interface IGlyphIdentifier { - chars: string; - code: number; - bg: number; - fg: number; - bold: boolean; - dim: boolean; - italic: boolean; -} - export interface ICharAtlasConfig { devicePixelRatio: number; fontSize: number; From c0253c10935cd0329f3f02cda07fa55fd0b004c2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 09:31:04 -0700 Subject: [PATCH 02/11] Move DynamicCharAtlas to browser --- .../renderer/atlas/DynamicCharAtlas.ts | 20 ++++++++++++------- src/browser/renderer/atlas/Types.d.ts | 15 ++++++++++++++ src/renderer/atlas/CharAtlasCache.ts | 4 ++-- src/renderer/atlas/CharAtlasUtils.ts | 2 +- src/renderer/atlas/Types.d.ts | 19 ------------------ 5 files changed, 31 insertions(+), 29 deletions(-) rename src/{ => browser}/renderer/atlas/DynamicCharAtlas.ts (95%) delete mode 100644 src/renderer/atlas/Types.d.ts diff --git a/src/renderer/atlas/DynamicCharAtlas.ts b/src/browser/renderer/atlas/DynamicCharAtlas.ts similarity index 95% rename from src/renderer/atlas/DynamicCharAtlas.ts rename to src/browser/renderer/atlas/DynamicCharAtlas.ts index 9af79e01..d591c173 100644 --- a/src/renderer/atlas/DynamicCharAtlas.ts +++ b/src/browser/renderer/atlas/DynamicCharAtlas.ts @@ -3,10 +3,9 @@ * @license MIT */ -import { ICharAtlasConfig } from './Types'; import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; -import { IGlyphIdentifier } from 'browser/renderer/atlas/Types'; -import { BaseCharAtlas } from '../../browser/renderer/atlas/BaseCharAtlas'; +import { IGlyphIdentifier, ICharAtlasConfig } from 'browser/renderer/atlas/Types'; +import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas'; import { DEFAULT_ANSI_COLORS } from 'browser/ColorManager'; import { LRUMap } from 'browser/renderer/atlas/LRUMap'; import { isFirefox, isSafari } from 'common/Platform'; @@ -90,12 +89,12 @@ export class DynamicCharAtlas extends BaseCharAtlas { // The canvas needs alpha because we use clearColor to convert the background color to alpha. // It might also contain some characters with transparent backgrounds if allowTransparency is // set. - this._cacheCtx = this._cacheCanvas.getContext('2d', {alpha: true}); + this._cacheCtx = throwIfFalsy(this._cacheCanvas.getContext('2d', {alpha: true})); const tmpCanvas = document.createElement('canvas'); tmpCanvas.width = this._config.scaledCharWidth; tmpCanvas.height = this._config.scaledCharHeight; - this._tmpCtx = tmpCanvas.getContext('2d', {alpha: this._config.allowTransparency}); + this._tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d', {alpha: this._config.allowTransparency})); this._width = Math.floor(TEXTURE_WIDTH / this._config.scaledCharWidth); this._height = Math.floor(TEXTURE_HEIGHT / this._config.scaledCharHeight); @@ -145,7 +144,7 @@ export class DynamicCharAtlas extends BaseCharAtlas { index = this._cacheMap.size; } else { // we're out of space, so our call to set will delete this item - index = this._cacheMap.peek().index; + index = this._cacheMap.peek()!.index; } const cacheValue = this._drawToCache(glyph, index); this._cacheMap.set(glyphKey, cacheValue); @@ -187,7 +186,7 @@ export class DynamicCharAtlas extends BaseCharAtlas { const cacheX = this._toCoordinateX(cacheValue.index); const cacheY = this._toCoordinateY(cacheValue.index); ctx.drawImage( - cacheValue.inBitmap ? this._bitmap : this._cacheCanvas, + cacheValue.inBitmap ? this._bitmap! : this._cacheCanvas, cacheX, cacheY, this._config.scaledCharWidth, @@ -367,3 +366,10 @@ function clearColor(imageData: ImageData, color: IColor): boolean { } return isEmpty; } + +export function throwIfFalsy(value: T | undefined | null): T { + if (!value) { + throw new Error('value must not be falsy'); + } + return value; +} diff --git a/src/browser/renderer/atlas/Types.d.ts b/src/browser/renderer/atlas/Types.d.ts index fd5b596c..0724b163 100644 --- a/src/browser/renderer/atlas/Types.d.ts +++ b/src/browser/renderer/atlas/Types.d.ts @@ -3,6 +3,9 @@ * @license MIT */ +import { FontWeight } from 'common/services/Services'; +import { IColorSet } from 'browser/Types'; + export interface IGlyphIdentifier { chars: string; code: number; @@ -12,3 +15,15 @@ export interface IGlyphIdentifier { dim: boolean; italic: boolean; } + +export interface ICharAtlasConfig { + devicePixelRatio: number; + fontSize: number; + fontFamily: string; + fontWeight: FontWeight; + fontWeightBold: FontWeight; + scaledCharWidth: number; + scaledCharHeight: number; + allowTransparency: boolean; + colors: IColorSet; +} diff --git a/src/renderer/atlas/CharAtlasCache.ts b/src/renderer/atlas/CharAtlasCache.ts index f9bd68a1..ec66b67a 100644 --- a/src/renderer/atlas/CharAtlasCache.ts +++ b/src/renderer/atlas/CharAtlasCache.ts @@ -6,8 +6,8 @@ import { ITerminal } from '../../Types'; import { generateConfig, configEquals } from './CharAtlasUtils'; import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas'; -import { DynamicCharAtlas } from './DynamicCharAtlas'; -import { ICharAtlasConfig } from './Types'; +import { DynamicCharAtlas } from '../../browser/renderer/atlas/DynamicCharAtlas'; +import { ICharAtlasConfig } from 'browser/renderer/atlas/Types'; import { IColorSet } from 'browser/Types'; interface ICharAtlasCacheEntry { diff --git a/src/renderer/atlas/CharAtlasUtils.ts b/src/renderer/atlas/CharAtlasUtils.ts index ee787a7b..da067f52 100644 --- a/src/renderer/atlas/CharAtlasUtils.ts +++ b/src/renderer/atlas/CharAtlasUtils.ts @@ -4,7 +4,7 @@ */ import { ITerminal } from '../../Types'; -import { ICharAtlasConfig } from './Types'; +import { ICharAtlasConfig } from 'browser/renderer/atlas/Types'; import { DEFAULT_COLOR } from 'common/buffer/Constants'; import { IColorSet } from 'browser/Types'; diff --git a/src/renderer/atlas/Types.d.ts b/src/renderer/atlas/Types.d.ts deleted file mode 100644 index f717381c..00000000 --- a/src/renderer/atlas/Types.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { FontWeight } from 'xterm'; -import { IColorSet } from 'browser/Types'; - -export interface ICharAtlasConfig { - devicePixelRatio: number; - fontSize: number; - fontFamily: string; - fontWeight: FontWeight; - fontWeightBold: FontWeight; - scaledCharWidth: number; - scaledCharHeight: number; - allowTransparency: boolean; - colors: IColorSet; -} From 64062d1a25d7c0c4b252a928370b3230ed74f6d4 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 09:38:49 -0700 Subject: [PATCH 03/11] Move CharAtlasUtils into browser --- src/browser/Types.d.ts | 9 +++++++ .../renderer/atlas/CharAtlasUtils.ts | 24 +++++++++---------- src/browser/renderer/atlas/Types.d.ts | 4 ++-- src/renderer/LinkRenderLayer.ts | 2 +- src/renderer/atlas/CharAtlasCache.ts | 6 ++--- 5 files changed, 27 insertions(+), 18 deletions(-) rename src/{ => browser}/renderer/atlas/CharAtlasUtils.ts (73%) diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts index c9167316..3f3a7637 100644 --- a/src/browser/Types.d.ts +++ b/src/browser/Types.d.ts @@ -24,6 +24,15 @@ export interface IColorSet { ansi: IColor[]; } +export interface IPartialColorSet { + foreground: IColor; + background: IColor; + cursor?: IColor; + cursorAccent?: IColor; + selection?: IColor; + ansi: IColor[]; +} + export interface IViewport extends IDisposable { scrollBarWidth: number; syncScrollArea(): void; diff --git a/src/renderer/atlas/CharAtlasUtils.ts b/src/browser/renderer/atlas/CharAtlasUtils.ts similarity index 73% rename from src/renderer/atlas/CharAtlasUtils.ts rename to src/browser/renderer/atlas/CharAtlasUtils.ts index da067f52..346b35f8 100644 --- a/src/renderer/atlas/CharAtlasUtils.ts +++ b/src/browser/renderer/atlas/CharAtlasUtils.ts @@ -3,19 +3,19 @@ * @license MIT */ -import { ITerminal } from '../../Types'; import { ICharAtlasConfig } from 'browser/renderer/atlas/Types'; import { DEFAULT_COLOR } from 'common/buffer/Constants'; -import { IColorSet } from 'browser/Types'; +import { IColorSet, IPartialColorSet } from 'browser/Types'; +import { ITerminalOptions } from 'common/services/Services'; -export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, terminal: ITerminal, colors: IColorSet): ICharAtlasConfig { +export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, options: ITerminalOptions, colors: IColorSet): ICharAtlasConfig { // null out some fields that don't matter - const clonedColors = { + const clonedColors = { foreground: colors.foreground, background: colors.background, - cursor: null, - cursorAccent: null, - selection: null, + cursor: undefined, + cursorAccent: undefined, + selection: undefined, // For the static char atlas, we only use the first 16 colors, but we need all 256 for the // dynamic character atlas. ansi: colors.ansi.slice(0, 16) @@ -24,11 +24,11 @@ export function generateConfig(scaledCharWidth: number, scaledCharHeight: number devicePixelRatio: window.devicePixelRatio, scaledCharWidth, scaledCharHeight, - fontFamily: terminal.options.fontFamily, - fontSize: terminal.options.fontSize, - fontWeight: terminal.options.fontWeight, - fontWeightBold: terminal.options.fontWeightBold, - allowTransparency: terminal.options.allowTransparency, + fontFamily: options.fontFamily, + fontSize: options.fontSize, + fontWeight: options.fontWeight, + fontWeightBold: options.fontWeightBold, + allowTransparency: options.allowTransparency, colors: clonedColors }; } diff --git a/src/browser/renderer/atlas/Types.d.ts b/src/browser/renderer/atlas/Types.d.ts index 0724b163..d8bc54c1 100644 --- a/src/browser/renderer/atlas/Types.d.ts +++ b/src/browser/renderer/atlas/Types.d.ts @@ -4,7 +4,7 @@ */ import { FontWeight } from 'common/services/Services'; -import { IColorSet } from 'browser/Types'; +import { IPartialColorSet } from 'browser/Types'; export interface IGlyphIdentifier { chars: string; @@ -25,5 +25,5 @@ export interface ICharAtlasConfig { scaledCharWidth: number; scaledCharHeight: number; allowTransparency: boolean; - colors: IColorSet; + colors: IPartialColorSet; } diff --git a/src/renderer/LinkRenderLayer.ts b/src/renderer/LinkRenderLayer.ts index 6e6ba2e4..f4fb966a 100644 --- a/src/renderer/LinkRenderLayer.ts +++ b/src/renderer/LinkRenderLayer.ts @@ -7,7 +7,7 @@ import { ITerminal, ILinkifierAccessor } from '../Types'; import { IRenderDimensions } from 'browser/renderer/Types'; import { BaseRenderLayer } from './BaseRenderLayer'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; -import { is256Color } from './atlas/CharAtlasUtils'; +import { is256Color } from 'browser/renderer/atlas/CharAtlasUtils'; import { IColorSet, ILinkifierEvent } from 'browser/Types'; export class LinkRenderLayer extends BaseRenderLayer { diff --git a/src/renderer/atlas/CharAtlasCache.ts b/src/renderer/atlas/CharAtlasCache.ts index ec66b67a..ccc4437c 100644 --- a/src/renderer/atlas/CharAtlasCache.ts +++ b/src/renderer/atlas/CharAtlasCache.ts @@ -4,9 +4,9 @@ */ import { ITerminal } from '../../Types'; -import { generateConfig, configEquals } from './CharAtlasUtils'; +import { generateConfig, configEquals } from 'browser/renderer/atlas/CharAtlasUtils'; import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas'; -import { DynamicCharAtlas } from '../../browser/renderer/atlas/DynamicCharAtlas'; +import { DynamicCharAtlas } from 'browser/renderer/atlas/DynamicCharAtlas'; import { ICharAtlasConfig } from 'browser/renderer/atlas/Types'; import { IColorSet } from 'browser/Types'; @@ -32,7 +32,7 @@ export function acquireCharAtlas( scaledCharWidth: number, scaledCharHeight: number ): BaseCharAtlas { - const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, terminal, colors); + const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, terminal.optionsService.options, colors); // Check to see if the terminal already owns this config for (let i = 0; i < charAtlasCache.length; i++) { From 60194179f800569c6b1231a87023e0663ba98eb1 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 09:43:54 -0700 Subject: [PATCH 04/11] Move CharAtlasCache to browser --- addons/xterm-addon-webgl/src/WebglRenderer.ts | 2 +- .../xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts | 4 ---- src/Terminal.ts | 2 +- src/{ => browser}/renderer/atlas/CharAtlasCache.ts | 7 +++---- src/renderer/BaseRenderLayer.ts | 2 +- 5 files changed, 6 insertions(+), 11 deletions(-) rename src/{ => browser}/renderer/atlas/CharAtlasCache.ts (94%) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 3d41e1b5..41973138 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -7,7 +7,7 @@ import { ITerminal } from '../../../src/Types'; import { GlyphRenderer } from './GlyphRenderer'; import { LinkRenderLayer } from './renderLayer/LinkRenderLayer'; import { CursorRenderLayer } from './renderLayer/CursorRenderLayer'; -import { acquireCharAtlas } from './atlas/CharAtlasCache'; +import { acquireCharAtlas } from 'browser/renderer/atlas/CharAtlasCache'; import { WebglCharAtlas } from './atlas/WebglCharAtlas'; import { RectangleRenderer } from './RectangleRenderer'; import { IWebGL2RenderingContext } from './Types'; diff --git a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts index df0cdbab..0e2d645e 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts @@ -4,16 +4,12 @@ */ import { IRenderLayer } from './Types'; -import { ICellData } from 'common/Types'; -import { DEFAULT_COLOR, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE } from 'common/buffer/Constants'; import { IGlyphIdentifier } from '../atlas/Types'; -import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { acquireCharAtlas } from '../atlas/CharAtlasCache'; import { Terminal } from 'xterm'; import { IColorSet } from 'browser/Types'; import { IRenderDimensions } from 'browser/renderer/Types'; import { CellData } from 'common/buffer/CellData'; -import { AttributeData } from 'common/buffer/AttributeData'; import { WebglCharAtlas } from 'atlas/WebglCharAtlas'; import { throwIfFalsy } from '../WebglUtils'; diff --git a/src/Terminal.ts b/src/Terminal.ts index 7a068c84..0d8bf834 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -38,7 +38,7 @@ import { SoundService } from 'browser/services/SoundService'; import { MouseZoneManager } from 'browser/MouseZoneManager'; import { AccessibilityManager } from './AccessibilityManager'; import { ITheme, IMarker, IDisposable, ISelectionPosition } from 'xterm'; -import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache'; +import { removeTerminalFromCache } from 'browser/renderer/atlas/CharAtlasCache'; import { DomRenderer } from './renderer/dom/DomRenderer'; import { IKeyboardEvent, KeyboardResultType, ICharset, IBufferLine, IAttributeData } from 'common/Types'; import { evaluateKeyboardEvent } from 'common/input/Keyboard'; diff --git a/src/renderer/atlas/CharAtlasCache.ts b/src/browser/renderer/atlas/CharAtlasCache.ts similarity index 94% rename from src/renderer/atlas/CharAtlasCache.ts rename to src/browser/renderer/atlas/CharAtlasCache.ts index ccc4437c..92c970f2 100644 --- a/src/renderer/atlas/CharAtlasCache.ts +++ b/src/browser/renderer/atlas/CharAtlasCache.ts @@ -3,7 +3,6 @@ * @license MIT */ -import { ITerminal } from '../../Types'; import { generateConfig, configEquals } from 'browser/renderer/atlas/CharAtlasUtils'; import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas'; import { DynamicCharAtlas } from 'browser/renderer/atlas/DynamicCharAtlas'; @@ -15,7 +14,7 @@ interface ICharAtlasCacheEntry { config: ICharAtlasConfig; // N.B. This implementation potentially holds onto copies of the terminal forever, so // this may cause memory leaks. - ownedBy: ITerminal[]; + ownedBy: any[]; } const charAtlasCache: ICharAtlasCacheEntry[] = []; @@ -27,7 +26,7 @@ const charAtlasCache: ICharAtlasCacheEntry[] = []; * @param colors The colors to use. */ export function acquireCharAtlas( - terminal: ITerminal, + terminal: any, colors: IColorSet, scaledCharWidth: number, scaledCharHeight: number @@ -79,7 +78,7 @@ export function acquireCharAtlas( * Removes a terminal reference from the cache, allowing its memory to be freed. * @param terminal The terminal to remove. */ -export function removeTerminalFromCache(terminal: ITerminal): void { +export function removeTerminalFromCache(terminal: any): void { for (let i = 0; i < charAtlasCache.length; i++) { const index = charAtlasCache[i].ownedBy.indexOf(terminal); if (index !== -1) { diff --git a/src/renderer/BaseRenderLayer.ts b/src/renderer/BaseRenderLayer.ts index e704375c..f2eae23d 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/renderer/BaseRenderLayer.ts @@ -11,7 +11,7 @@ import { DEFAULT_COLOR, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE } from 'commo import { IGlyphIdentifier } from 'browser/renderer/atlas/Types'; import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas'; -import { acquireCharAtlas } from './atlas/CharAtlasCache'; +import { acquireCharAtlas } from 'browser/renderer/atlas/CharAtlasCache'; import { AttributeData } from 'common/buffer/AttributeData'; import { IColorSet } from 'browser/Types'; import { CellData } from 'common/buffer/CellData'; From 868125f2585b86e78761253013b28ac0b2487108 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 10:04:51 -0700 Subject: [PATCH 05/11] Don't pass terminal around within render layers Will ease migration to services --- src/renderer/BaseRenderLayer.ts | 73 +++++++++++------------ src/renderer/CursorRenderLayer.ts | 86 ++++++++++++++-------------- src/renderer/LinkRenderLayer.ts | 12 ++-- src/renderer/Renderer.ts | 26 ++++----- src/renderer/SelectionRenderLayer.ts | 30 +++++----- src/renderer/TextRenderLayer.ts | 47 ++++++++------- src/renderer/Types.d.ts | 18 +++--- 7 files changed, 143 insertions(+), 149 deletions(-) diff --git a/src/renderer/BaseRenderLayer.ts b/src/renderer/BaseRenderLayer.ts index f2eae23d..c0df47dc 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/renderer/BaseRenderLayer.ts @@ -46,7 +46,8 @@ export abstract class BaseRenderLayer implements IRenderLayer { id: string, zIndex: number, private _alpha: boolean, - protected _colors: IColorSet + protected _colors: IColorSet, + protected _terminal: ITerminal ) { this._canvas = document.createElement('canvas'); this._canvas.classList.add(`xterm-${id}-layer`); @@ -70,18 +71,18 @@ export abstract class BaseRenderLayer implements IRenderLayer { } } - public onOptionsChanged(terminal: ITerminal): void {} - public onBlur(terminal: ITerminal): void {} - public onFocus(terminal: ITerminal): void {} - public onCursorMove(terminal: ITerminal): void {} - public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {} - public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number], columnSelectMode: boolean = false): void {} + public onOptionsChanged(): void {} + public onBlur(): void {} + public onFocus(): void {} + public onCursorMove(): void {} + public onGridChanged(startRow: number, endRow: number): void {} + public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean = false): void {} - public setColors(terminal: ITerminal, colorSet: IColorSet): void { - this._refreshCharAtlas(terminal, colorSet); + public setColors(colorSet: IColorSet): void { + this._refreshCharAtlas(colorSet); } - protected _setTransparency(terminal: ITerminal, alpha: boolean): void { + protected _setTransparency(alpha: boolean): void { // Do nothing when alpha doesn't change if (alpha === this._alpha) { return; @@ -96,24 +97,23 @@ export abstract class BaseRenderLayer implements IRenderLayer { this._container.replaceChild(this._canvas, oldCanvas); // Regenerate char atlas and force a full redraw - this._refreshCharAtlas(terminal, this._colors); - this.onGridChanged(terminal, 0, terminal.rows - 1); + this._refreshCharAtlas(this._colors); + this.onGridChanged(0, this._terminal.rows - 1); } /** * Refreshes the char atlas, aquiring a new one if necessary. - * @param terminal The terminal. * @param colorSet The color set to use for the char atlas. */ - private _refreshCharAtlas(terminal: ITerminal, colorSet: IColorSet): void { + private _refreshCharAtlas(colorSet: IColorSet): void { if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) { return; } - this._charAtlas = acquireCharAtlas(terminal, colorSet, this._scaledCharWidth, this._scaledCharHeight); + this._charAtlas = acquireCharAtlas(this._terminal, colorSet, this._scaledCharWidth, this._scaledCharHeight); this._charAtlas.warmUp(); } - public resize(terminal: ITerminal, dim: IRenderDimensions): void { + public resize(dim: IRenderDimensions): void { this._scaledCellWidth = dim.scaledCellWidth; this._scaledCellHeight = dim.scaledCellHeight; this._scaledCharWidth = dim.scaledCharWidth; @@ -130,10 +130,10 @@ export abstract class BaseRenderLayer implements IRenderLayer { this._clearAll(); } - this._refreshCharAtlas(terminal, this._colors); + this._refreshCharAtlas(this._colors); } - public abstract reset(terminal: ITerminal): void; + public abstract reset(): void; /** * Fills 1+ cells completely. This uses the existing fillStyle on the context. @@ -233,16 +233,15 @@ export abstract class BaseRenderLayer implements IRenderLayer { * Draws a truecolor character at the cell. The character will be clipped to * ensure that it fits with the cell, including the cell to the right if it's * a wide character. This uses the existing fillStyle on the context. - * @param terminal The terminal. * @param cell The cell data for the character to draw. * @param x The column to draw at. * @param y The row to draw at. * @param color The color of the character. */ - protected _fillCharTrueColor(terminal: ITerminal, cell: CellData, x: number, y: number): void { - this._ctx.font = this._getFont(terminal, false, false); + protected _fillCharTrueColor(cell: CellData, x: number, y: number): void { + this._ctx.font = this._getFont(false, false); this._ctx.textBaseline = 'middle'; - this._clipRow(terminal, y); + this._clipRow(y); this._ctx.fillText( cell.getChars(), x * this._scaledCellWidth + this._scaledCharLeft, @@ -252,7 +251,6 @@ export abstract class BaseRenderLayer implements IRenderLayer { /** * Draws one or more characters at a cell. If possible this will draw using * the character atlas to reduce draw time. - * @param terminal The terminal. * @param chars The character or characters. * @param code The character code. * @param width The width of the characters. @@ -263,14 +261,14 @@ export abstract class BaseRenderLayer implements IRenderLayer { * This is used to validate whether a cached image can be used. * @param bold Whether the text is bold. */ - protected _drawChars(terminal: ITerminal, cell: ICellData, x: number, y: number): void { + protected _drawChars(cell: ICellData, x: number, y: number): void { // skip cache right away if we draw in RGB // Note: to avoid bad runtime JoinedCellData will be skipped // in the cache handler itself (atlasDidDraw == false) and // fall through to uncached later down below if (cell.isFgRGB() || cell.isBgRGB()) { - this._drawUncachedChars(terminal, cell, x, y); + this._drawUncachedChars(cell, x, y); return; } @@ -284,7 +282,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { fg = (cell.isFgDefault()) ? DEFAULT_COLOR : cell.getFgColor(); } - const drawInBrightColor = terminal.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8 && fg !== INVERTED_DEFAULT_COLOR; + const drawInBrightColor = this._terminal.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8 && fg !== INVERTED_DEFAULT_COLOR; fg += drawInBrightColor ? 8 : 0; this._currentGlyphIdentifier.chars = cell.getChars() || WHITESPACE_CELL_CHAR; @@ -302,7 +300,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { ); if (!atlasDidDraw) { - this._drawUncachedChars(terminal, cell, x, y); + this._drawUncachedChars(cell, x, y); } } @@ -310,16 +308,15 @@ export abstract class BaseRenderLayer implements IRenderLayer { * Draws one or more characters at one or more cells. The character(s) will be * clipped to ensure that they fit with the cell(s), including the cell to the * right if the last character is a wide character. - * @param terminal The terminal. * @param chars The character. * @param width The width of the character. * @param fg The foreground color, in the format stored within the attributes. * @param x The column to draw at. * @param y The row to draw at. */ - private _drawUncachedChars(terminal: ITerminal, cell: ICellData, x: number, y: number): void { + private _drawUncachedChars(cell: ICellData, x: number, y: number): void { this._ctx.save(); - this._ctx.font = this._getFont(terminal, !!cell.isBold(), !!cell.isItalic()); + this._ctx.font = this._getFont(!!cell.isBold(), !!cell.isItalic()); this._ctx.textBaseline = 'middle'; if (cell.isInverse()) { @@ -337,14 +334,14 @@ export abstract class BaseRenderLayer implements IRenderLayer { this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`; } else { let fg = cell.getFgColor(); - if (terminal.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) { + if (this._terminal.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) { fg += 8; } this._ctx.fillStyle = this._colors.ansi[fg].css; } } - this._clipRow(terminal, y); + this._clipRow(y); // Apply alpha to dim the character if (cell.isDim()) { @@ -360,29 +357,27 @@ export abstract class BaseRenderLayer implements IRenderLayer { /** * Clips a row to ensure no pixels will be drawn outside the cells in the row. - * @param terminal The terminal. * @param y The row to clip. */ - private _clipRow(terminal: ITerminal, y: number): void { + private _clipRow(y: number): void { this._ctx.beginPath(); this._ctx.rect( 0, y * this._scaledCellHeight, - terminal.cols * this._scaledCellWidth, + this._terminal.cols * this._scaledCellWidth, this._scaledCellHeight); this._ctx.clip(); } /** * Gets the current font. - * @param terminal The terminal. * @param isBold If we should use the bold fontWeight. */ - protected _getFont(terminal: ITerminal, isBold: boolean, isItalic: boolean): string { - const fontWeight = isBold ? terminal.options.fontWeightBold : terminal.options.fontWeight; + protected _getFont(isBold: boolean, isItalic: boolean): string { + const fontWeight = isBold ? this._terminal.options.fontWeightBold : this._terminal.options.fontWeight; const fontStyle = isItalic ? 'italic' : ''; - return `${fontStyle} ${fontWeight} ${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`; + return `${fontStyle} ${fontWeight} ${this._terminal.options.fontSize * window.devicePixelRatio}px ${this._terminal.options.fontFamily}`; } } diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts index b0e43fdc..33674bb7 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/renderer/CursorRenderLayer.ts @@ -25,12 +25,12 @@ const BLINK_INTERVAL = 600; export class CursorRenderLayer extends BaseRenderLayer { private _state: ICursorState; - private _cursorRenderers: {[key: string]: (terminal: ITerminal, x: number, y: number, cell: ICellData) => void}; + private _cursorRenderers: {[key: string]: (x: number, y: number, cell: ICellData) => void}; private _cursorBlinkStateManager: CursorBlinkStateManager; private _cell: ICellData = new CellData(); - constructor(container: HTMLElement, zIndex: number, colors: IColorSet) { - super(container, 'cursor', zIndex, true, colors); + constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ITerminal) { + super(container, 'cursor', zIndex, true, colors, terminal); this._state = { x: null, y: null, @@ -46,8 +46,8 @@ export class CursorRenderLayer extends BaseRenderLayer { // TODO: Consider initial options? Maybe onOptionsChanged should be called at the end of open? } - public resize(terminal: ITerminal, dim: IRenderDimensions): void { - super.resize(terminal, dim); + public resize(dim: IRenderDimensions): void { + super.resize(dim); // Resizing the canvas discards the contents of the canvas so clear state this._state = { x: null, @@ -58,35 +58,35 @@ export class CursorRenderLayer extends BaseRenderLayer { }; } - public reset(terminal: ITerminal): void { + public reset(): void { this._clearCursor(); if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.dispose(); this._cursorBlinkStateManager = null; - this.onOptionsChanged(terminal); + this.onOptionsChanged(); } } - public onBlur(terminal: ITerminal): void { + public onBlur(): void { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.pause(); } - terminal.refresh(terminal.buffer.y, terminal.buffer.y); + this._terminal.refresh(this._terminal.buffer.y, this._terminal.buffer.y); } - public onFocus(terminal: ITerminal): void { + public onFocus(): void { if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.resume(terminal); + this._cursorBlinkStateManager.resume(this._terminal); } else { - terminal.refresh(terminal.buffer.y, terminal.buffer.y); + this._terminal.refresh(this._terminal.buffer.y, this._terminal.buffer.y); } } - public onOptionsChanged(terminal: ITerminal): void { - if (terminal.options.cursorBlink) { + public onOptionsChanged(): void { + if (this._terminal.options.cursorBlink) { if (!this._cursorBlinkStateManager) { - this._cursorBlinkStateManager = new CursorBlinkStateManager(terminal, () => { - this._render(terminal, true); + this._cursorBlinkStateManager = new CursorBlinkStateManager(this._terminal, () => { + this._render(true); }); } } else { @@ -96,55 +96,55 @@ export class CursorRenderLayer extends BaseRenderLayer { } // Request a refresh from the terminal as management of rendering is being // moved back to the terminal - terminal.refresh(terminal.buffer.y, terminal.buffer.y); + this._terminal.refresh(this._terminal.buffer.y, this._terminal.buffer.y); } } - public onCursorMove(terminal: ITerminal): void { + public onCursorMove(): void { if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.restartBlinkAnimation(terminal); + this._cursorBlinkStateManager.restartBlinkAnimation(this._terminal); } } - public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void { + public onGridChanged(startRow: number, endRow: number): void { if (!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isPaused) { - this._render(terminal, false); + this._render(false); } else { - this._cursorBlinkStateManager.restartBlinkAnimation(terminal); + this._cursorBlinkStateManager.restartBlinkAnimation(this._terminal); } } - private _render(terminal: ITerminal, triggeredByAnimationFrame: boolean): void { + private _render(triggeredByAnimationFrame: boolean): void { // Don't draw the cursor if it's hidden - if (!terminal.cursorState || terminal.cursorHidden) { + if (!this._terminal.cursorState || this._terminal.cursorHidden) { this._clearCursor(); return; } - const cursorY = terminal.buffer.ybase + terminal.buffer.y; - const viewportRelativeCursorY = cursorY - terminal.buffer.ydisp; + const cursorY = this._terminal.buffer.ybase + this._terminal.buffer.y; + const viewportRelativeCursorY = cursorY - this._terminal.buffer.ydisp; // Don't draw the cursor if it's off-screen - if (viewportRelativeCursorY < 0 || viewportRelativeCursorY >= terminal.rows) { + if (viewportRelativeCursorY < 0 || viewportRelativeCursorY >= this._terminal.rows) { this._clearCursor(); return; } - terminal.buffer.lines.get(cursorY).loadCell(terminal.buffer.x, this._cell); + this._terminal.buffer.lines.get(cursorY).loadCell(this._terminal.buffer.x, this._cell); if (this._cell.content === undefined) { return; } - if (!terminal.isFocused) { + if (!this._terminal.isFocused) { this._clearCursor(); this._ctx.save(); this._ctx.fillStyle = this._colors.cursor.css; - this._renderBlurCursor(terminal, terminal.buffer.x, viewportRelativeCursorY, this._cell); + this._renderBlurCursor(this._terminal.buffer.x, viewportRelativeCursorY, this._cell); this._ctx.restore(); - this._state.x = terminal.buffer.x; + this._state.x = this._terminal.buffer.x; this._state.y = viewportRelativeCursorY; this._state.isFocused = false; - this._state.style = terminal.options.cursorStyle; + this._state.style = this._terminal.options.cursorStyle; this._state.width = this._cell.getWidth(); return; } @@ -157,10 +157,10 @@ export class CursorRenderLayer extends BaseRenderLayer { if (this._state) { // The cursor is already in the correct spot, don't redraw - if (this._state.x === terminal.buffer.x && + if (this._state.x === this._terminal.buffer.x && this._state.y === viewportRelativeCursorY && - this._state.isFocused === terminal.isFocused && - this._state.style === terminal.options.cursorStyle && + this._state.isFocused === this._terminal.isFocused && + this._state.style === this._terminal.options.cursorStyle && this._state.width === this._cell.getWidth()) { return; } @@ -168,13 +168,13 @@ export class CursorRenderLayer extends BaseRenderLayer { } this._ctx.save(); - this._cursorRenderers[terminal.options.cursorStyle || 'block'](terminal, terminal.buffer.x, viewportRelativeCursorY, this._cell); + this._cursorRenderers[this._terminal.options.cursorStyle || 'block'](this._terminal.buffer.x, viewportRelativeCursorY, this._cell); this._ctx.restore(); - this._state.x = terminal.buffer.x; + this._state.x = this._terminal.buffer.x; this._state.y = viewportRelativeCursorY; this._state.isFocused = false; - this._state.style = terminal.options.cursorStyle; + this._state.style = this._terminal.options.cursorStyle; this._state.width = this._cell.getWidth(); } @@ -191,30 +191,30 @@ export class CursorRenderLayer extends BaseRenderLayer { } } - private _renderBarCursor(terminal: ITerminal, x: number, y: number, cell: ICellData): void { + private _renderBarCursor(x: number, y: number, cell: ICellData): void { this._ctx.save(); this._ctx.fillStyle = this._colors.cursor.css; this._fillLeftLineAtCell(x, y); this._ctx.restore(); } - private _renderBlockCursor(terminal: ITerminal, x: number, y: number, cell: ICellData): void { + private _renderBlockCursor(x: number, y: number, cell: ICellData): void { this._ctx.save(); this._ctx.fillStyle = this._colors.cursor.css; this._fillCells(x, y, cell.getWidth(), 1); this._ctx.fillStyle = this._colors.cursorAccent.css; - this._fillCharTrueColor(terminal, cell, x, y); + this._fillCharTrueColor(cell, x, y); this._ctx.restore(); } - private _renderUnderlineCursor(terminal: ITerminal, x: number, y: number, cell: ICellData): void { + private _renderUnderlineCursor(x: number, y: number, cell: ICellData): void { this._ctx.save(); this._ctx.fillStyle = this._colors.cursor.css; this._fillBottomLineAtCells(x, y); this._ctx.restore(); } - private _renderBlurCursor(terminal: ITerminal, x: number, y: number, cell: ICellData): void { + private _renderBlurCursor(x: number, y: number, cell: ICellData): void { this._ctx.save(); this._ctx.strokeStyle = this._colors.cursor.css; this._strokeRectAtCell(x, y, cell.getWidth(), 1); diff --git a/src/renderer/LinkRenderLayer.ts b/src/renderer/LinkRenderLayer.ts index f4fb966a..52625439 100644 --- a/src/renderer/LinkRenderLayer.ts +++ b/src/renderer/LinkRenderLayer.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { ITerminal, ILinkifierAccessor } from '../Types'; +import { ITerminal } from '../Types'; import { IRenderDimensions } from 'browser/renderer/Types'; import { BaseRenderLayer } from './BaseRenderLayer'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; @@ -13,19 +13,19 @@ import { IColorSet, ILinkifierEvent } from 'browser/Types'; export class LinkRenderLayer extends BaseRenderLayer { private _state: ILinkifierEvent = null; - constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ILinkifierAccessor) { - super(container, 'link', zIndex, true, colors); + constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ITerminal) { + super(container, 'link', zIndex, true, colors, terminal); terminal.linkifier.onLinkHover(e => this._onLinkHover(e)); terminal.linkifier.onLinkLeave(e => this._onLinkLeave(e)); } - public resize(terminal: ITerminal, dim: IRenderDimensions): void { - super.resize(terminal, dim); + public resize(dim: IRenderDimensions): void { + super.resize(dim); // Resizing the canvas discards the contents of the canvas so clear state this._state = null; } - public reset(terminal: ITerminal): void { + public reset(): void { this._clearCurrentLink(); } diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 2ed61b76..04ad6989 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -34,10 +34,10 @@ export class Renderer extends Disposable implements IRenderer { this._characterJoinerRegistry = new CharacterJoinerRegistry(bufferService); this._renderLayers = [ - new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency), - new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors), + new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._terminal), + new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors, this._terminal), new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._terminal), - new CursorRenderLayer(this._terminal.screenElement, 3, this._colors) + new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal) ]; this.dimensions = { scaledCharWidth: null, @@ -77,8 +77,8 @@ export class Renderer extends Disposable implements IRenderer { // Clear layers and force a full render this._renderLayers.forEach(l => { - l.setColors(this._terminal, this._colors); - l.reset(this._terminal); + l.setColors(this._colors); + l.reset(); }); } @@ -87,7 +87,7 @@ export class Renderer extends Disposable implements IRenderer { this._updateDimensions(); // Resize all render layers - this._renderLayers.forEach(l => l.resize(this._terminal, this.dimensions)); + this._renderLayers.forEach(l => l.resize(this.dimensions)); // Resize the screen this._terminal.screenElement.style.width = `${this.dimensions.canvasWidth}px`; @@ -99,27 +99,27 @@ export class Renderer extends Disposable implements IRenderer { } public onBlur(): void { - this._runOperation(l => l.onBlur(this._terminal)); + this._runOperation(l => l.onBlur()); } public onFocus(): void { - this._runOperation(l => l.onFocus(this._terminal)); + this._runOperation(l => l.onFocus()); } public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean = false): void { - this._runOperation(l => l.onSelectionChanged(this._terminal, start, end, columnSelectMode)); + this._runOperation(l => l.onSelectionChanged(start, end, columnSelectMode)); } public onCursorMove(): void { - this._runOperation(l => l.onCursorMove(this._terminal)); + this._runOperation(l => l.onCursorMove()); } public onOptionsChanged(): void { - this._runOperation(l => l.onOptionsChanged(this._terminal)); + this._runOperation(l => l.onOptionsChanged()); } public clear(): void { - this._runOperation(l => l.reset(this._terminal)); + this._runOperation(l => l.reset()); } private _runOperation(operation: (layer: IRenderLayer) => void): void { @@ -131,7 +131,7 @@ export class Renderer extends Disposable implements IRenderer { * necessary before queueing up the next one. */ public renderRows(start: number, end: number): void { - this._renderLayers.forEach(l => l.onGridChanged(this._terminal, start, end)); + this._renderLayers.forEach(l => l.onGridChanged(start, end)); } /** diff --git a/src/renderer/SelectionRenderLayer.ts b/src/renderer/SelectionRenderLayer.ts index 20b86a3f..1735f786 100644 --- a/src/renderer/SelectionRenderLayer.ts +++ b/src/renderer/SelectionRenderLayer.ts @@ -18,8 +18,8 @@ interface ISelectionState { export class SelectionRenderLayer extends BaseRenderLayer { private _state: ISelectionState; - constructor(container: HTMLElement, zIndex: number, colors: IColorSet) { - super(container, 'selection', zIndex, true, colors); + constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ITerminal) { + super(container, 'selection', zIndex, true, colors, terminal); this._clearState(); } @@ -32,22 +32,22 @@ export class SelectionRenderLayer extends BaseRenderLayer { }; } - public resize(terminal: ITerminal, dim: IRenderDimensions): void { - super.resize(terminal, dim); + public resize(dim: IRenderDimensions): void { + super.resize(dim); // Resizing the canvas discards the contents of the canvas so clear state this._clearState(); } - public reset(terminal: ITerminal): void { + public reset(): void { if (this._state.start && this._state.end) { this._clearState(); this._clearAll(); } } - public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number], columnSelectMode: boolean): void { + public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean): void { // Selection has not changed - if (!this._didStateChange(start, end, columnSelectMode, terminal.buffer.ydisp)) { + if (!this._didStateChange(start, end, columnSelectMode, this._terminal.buffer.ydisp)) { return; } @@ -61,13 +61,13 @@ export class SelectionRenderLayer extends BaseRenderLayer { } // Translate from buffer position to viewport position - const viewportStartRow = start[1] - terminal.buffer.ydisp; - const viewportEndRow = end[1] - terminal.buffer.ydisp; + const viewportStartRow = start[1] - this._terminal.buffer.ydisp; + const viewportEndRow = end[1] - this._terminal.buffer.ydisp; const viewportCappedStartRow = Math.max(viewportStartRow, 0); - const viewportCappedEndRow = Math.min(viewportEndRow, terminal.rows - 1); + const viewportCappedEndRow = Math.min(viewportEndRow, this._terminal.rows - 1); // No need to draw the selection - if (viewportCappedStartRow >= terminal.rows || viewportCappedEndRow < 0) { + if (viewportCappedStartRow >= this._terminal.rows || viewportCappedEndRow < 0) { return; } @@ -81,17 +81,17 @@ export class SelectionRenderLayer extends BaseRenderLayer { } else { // Draw first row const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0; - const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : terminal.cols; + const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : this._terminal.cols; this._fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1); // Draw middle rows const middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0); - this._fillCells(0, viewportCappedStartRow + 1, terminal.cols, middleRowsCount); + this._fillCells(0, viewportCappedStartRow + 1, this._terminal.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] : terminal.cols; + const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._terminal.cols; this._fillCells(0, viewportCappedEndRow, endCol, 1); } } @@ -100,7 +100,7 @@ export class SelectionRenderLayer extends BaseRenderLayer { this._state.start = [start[0], start[1]]; this._state.end = [end[0], end[1]]; this._state.columnSelectMode = columnSelectMode; - this._state.ydisp = terminal.buffer.ydisp; + this._state.ydisp = this._terminal.buffer.ydisp; } private _didStateChange(start: [number, number], end: [number, number], columnSelectMode: boolean, ydisp: number): boolean { diff --git a/src/renderer/TextRenderLayer.ts b/src/renderer/TextRenderLayer.ts index 03e5c112..b1e59451 100644 --- a/src/renderer/TextRenderLayer.ts +++ b/src/renderer/TextRenderLayer.ts @@ -29,17 +29,17 @@ export class TextRenderLayer extends BaseRenderLayer { private _characterJoinerRegistry: ICharacterJoinerRegistry; private _workCell = new CellData(); - constructor(container: HTMLElement, zIndex: number, colors: IColorSet, characterJoinerRegistry: ICharacterJoinerRegistry, alpha: boolean) { - super(container, 'text', zIndex, alpha, colors); + constructor(container: HTMLElement, zIndex: number, colors: IColorSet, characterJoinerRegistry: ICharacterJoinerRegistry, alpha: boolean, terminal: ITerminal) { + super(container, 'text', zIndex, alpha, colors, terminal); this._state = new GridCache(); this._characterJoinerRegistry = characterJoinerRegistry; } - public resize(terminal: ITerminal, dim: IRenderDimensions): void { - super.resize(terminal, dim); + public resize(dim: IRenderDimensions): void { + super.resize(dim); // Clear the character width cache if the font or width has changed - const terminalFont = this._getFont(terminal, false, false); + const terminalFont = this._getFont(false, false); if (this._characterWidth !== dim.scaledCharWidth || this._characterFont !== terminalFont) { this._characterWidth = dim.scaledCharWidth; this._characterFont = terminalFont; @@ -47,16 +47,15 @@ export class TextRenderLayer extends BaseRenderLayer { } // Resizing the canvas discards the contents of the canvas so clear state this._state.clear(); - this._state.resize(terminal.cols, terminal.rows); + this._state.resize(this._terminal.cols, this._terminal.rows); } - public reset(terminal: ITerminal): void { + public reset(): void { this._state.clear(); this._clearAll(); } private _forEachCell( - terminal: ITerminal, firstRow: number, lastRow: number, joinerRegistry: ICharacterJoinerRegistry | null, @@ -67,10 +66,10 @@ export class TextRenderLayer extends BaseRenderLayer { ) => void ): void { for (let y = firstRow; y <= lastRow; y++) { - const row = y + terminal.buffer.ydisp; - const line = terminal.buffer.lines.get(row); + const row = y + this._terminal.buffer.ydisp; + const line = this._terminal.buffer.lines.get(row); const joinedRanges = joinerRegistry ? joinerRegistry.getJoinedCharacters(row) : []; - for (let x = 0; x < terminal.cols; x++) { + for (let x = 0; x < this._terminal.cols; x++) { line.loadCell(x, this._workCell); let cell = this._workCell; @@ -143,16 +142,16 @@ export class TextRenderLayer extends BaseRenderLayer { * 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(terminal: ITerminal, firstRow: number, lastRow: number): void { + private _drawBackground(firstRow: number, lastRow: number): void { const ctx = this._ctx; - const cols = terminal.cols; + const cols = this._terminal.cols; let startX: number = 0; let startY: number = 0; let prevFillStyle: string | null = null; ctx.save(); - this._forEachCell(terminal, firstRow, lastRow, null, (cell, x, y) => { + this._forEachCell(firstRow, lastRow, null, (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 @@ -202,12 +201,12 @@ export class TextRenderLayer extends BaseRenderLayer { ctx.restore(); } - private _drawForeground(terminal: ITerminal, firstRow: number, lastRow: number): void { - this._forEachCell(terminal, firstRow, lastRow, this._characterJoinerRegistry, (cell, x, y) => { + private _drawForeground(firstRow: number, lastRow: number): void { + this._forEachCell(firstRow, lastRow, this._characterJoinerRegistry, (cell, x, y) => { if (cell.isInvisible()) { return; } - this._drawChars(terminal, cell, x, y); + this._drawChars(cell, x, y); if (cell.isUnderline()) { this._ctx.save(); @@ -226,7 +225,7 @@ export class TextRenderLayer extends BaseRenderLayer { this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`; } else { let fg = cell.getFgColor(); - if (terminal.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) { + if (this._terminal.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) { fg += 8; } this._ctx.fillStyle = this._colors.ansi[fg].css; @@ -239,7 +238,7 @@ export class TextRenderLayer extends BaseRenderLayer { }); } - public onGridChanged(terminal: ITerminal, firstRow: number, lastRow: number): void { + public onGridChanged(firstRow: number, lastRow: number): void { // Resize has not been called yet if (this._state.cache.length === 0) { return; @@ -249,13 +248,13 @@ export class TextRenderLayer extends BaseRenderLayer { this._charAtlas.beginFrame(); } - this._clearCells(0, firstRow, terminal.cols, lastRow - firstRow + 1); - this._drawBackground(terminal, firstRow, lastRow); - this._drawForeground(terminal, firstRow, lastRow); + this._clearCells(0, firstRow, this._terminal.cols, lastRow - firstRow + 1); + this._drawBackground(firstRow, lastRow); + this._drawForeground(firstRow, lastRow); } - public onOptionsChanged(terminal: ITerminal): void { - this._setTransparency(terminal, terminal.options.allowTransparency); + public onOptionsChanged(): void { + this._setTransparency(this._terminal.options.allowTransparency); } /** diff --git a/src/renderer/Types.d.ts b/src/renderer/Types.d.ts index 1546248a..422a01c6 100644 --- a/src/renderer/Types.d.ts +++ b/src/renderer/Types.d.ts @@ -12,38 +12,38 @@ export interface IRenderLayer extends IDisposable { /** * Called when the terminal loses focus. */ - onBlur(terminal: ITerminal): void; + onBlur(): void; /** * * Called when the terminal gets focus. */ - onFocus(terminal: ITerminal): void; + onFocus(): void; /** * Called when the cursor is moved. */ - onCursorMove(terminal: ITerminal): void; + onCursorMove(): void; /** * Called when options change. */ - onOptionsChanged(terminal: ITerminal): void; + onOptionsChanged(): void; /** * Called when the theme changes. */ - setColors(terminal: ITerminal, colorSet: IColorSet): void; + setColors(colorSet: IColorSet): void; /** * Called when the data in the grid has changed (or needs to be rendered * again). */ - onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void; + onGridChanged(startRow: number, endRow: number): void; /** * Calls when the selection changes. */ - onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number], columnSelectMode: boolean): void; + onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean): void; /** * Registers a handler to join characters to render as a group @@ -58,10 +58,10 @@ export interface IRenderLayer extends IDisposable { /** * Resize the render layer. */ - resize(terminal: ITerminal, dim: IRenderDimensions): void; + resize(dim: IRenderDimensions): void; /** * Clear the state of the render layer. */ - reset(terminal: ITerminal): void; + reset(): void; } From bd95c83ca2e59bd41a92252ebca6735322740e5a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 10:15:58 -0700 Subject: [PATCH 06/11] Start using services in most render layers --- src/Terminal.ts | 2 +- src/renderer/CursorRenderLayer.ts | 60 ++++++++++++++++------------ src/renderer/LinkRenderLayer.ts | 8 ++-- src/renderer/Renderer.ts | 11 ++--- src/renderer/SelectionRenderLayer.ts | 17 +++++--- 5 files changed, 57 insertions(+), 41 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 0d8bf834..48792ac5 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -685,7 +685,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _createRenderer(): IRenderer { switch (this.options.rendererType) { - case 'canvas': return new Renderer(this._colorManager.colors, this, this._bufferService, this._charSizeService); + case 'canvas': return new Renderer(this._colorManager.colors, this, this._bufferService, this._charSizeService, this.optionsService); case 'dom': return new DomRenderer(this, this._colorManager.colors, this._charSizeService, this.optionsService); default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`); } diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts index 33674bb7..26e5e4ac 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/renderer/CursorRenderLayer.ts @@ -9,6 +9,7 @@ import { ITerminal } from '../Types'; import { ICellData } from 'common/Types'; import { CellData } from 'common/buffer/CellData'; import { IColorSet } from 'browser/Types'; +import { IBufferService, IOptionsService } from 'common/services/Services'; interface ICursorState { x: number; @@ -29,7 +30,14 @@ export class CursorRenderLayer extends BaseRenderLayer { private _cursorBlinkStateManager: CursorBlinkStateManager; private _cell: ICellData = new CellData(); - constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ITerminal) { + constructor( + container: HTMLElement, + zIndex: number, + colors: IColorSet, + terminal: ITerminal, + private readonly _bufferService: IBufferService, + private readonly _optionsService: IOptionsService + ) { super(container, 'cursor', zIndex, true, colors, terminal); this._state = { x: null, @@ -71,21 +79,21 @@ export class CursorRenderLayer extends BaseRenderLayer { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.pause(); } - this._terminal.refresh(this._terminal.buffer.y, this._terminal.buffer.y); + this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y); } public onFocus(): void { if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.resume(this._terminal); + this._cursorBlinkStateManager.resume(); } else { - this._terminal.refresh(this._terminal.buffer.y, this._terminal.buffer.y); + this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y); } } public onOptionsChanged(): void { - if (this._terminal.options.cursorBlink) { + if (this._optionsService.options.cursorBlink) { if (!this._cursorBlinkStateManager) { - this._cursorBlinkStateManager = new CursorBlinkStateManager(this._terminal, () => { + this._cursorBlinkStateManager = new CursorBlinkStateManager(this._terminal.isFocused, () => { this._render(true); }); } @@ -96,13 +104,13 @@ export class CursorRenderLayer extends BaseRenderLayer { } // Request a refresh from the terminal as management of rendering is being // moved back to the terminal - this._terminal.refresh(this._terminal.buffer.y, this._terminal.buffer.y); + this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y); } } public onCursorMove(): void { if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.restartBlinkAnimation(this._terminal); + this._cursorBlinkStateManager.restartBlinkAnimation(); } } @@ -110,7 +118,7 @@ export class CursorRenderLayer extends BaseRenderLayer { if (!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isPaused) { this._render(false); } else { - this._cursorBlinkStateManager.restartBlinkAnimation(this._terminal); + this._cursorBlinkStateManager.restartBlinkAnimation(); } } @@ -121,16 +129,16 @@ export class CursorRenderLayer extends BaseRenderLayer { return; } - const cursorY = this._terminal.buffer.ybase + this._terminal.buffer.y; - const viewportRelativeCursorY = cursorY - this._terminal.buffer.ydisp; + 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._terminal.rows) { + if (viewportRelativeCursorY < 0 || viewportRelativeCursorY >= this._bufferService.rows) { this._clearCursor(); return; } - this._terminal.buffer.lines.get(cursorY).loadCell(this._terminal.buffer.x, this._cell); + this._bufferService.buffer.lines.get(cursorY).loadCell(this._bufferService.buffer.x, this._cell); if (this._cell.content === undefined) { return; } @@ -139,12 +147,12 @@ export class CursorRenderLayer extends BaseRenderLayer { this._clearCursor(); this._ctx.save(); this._ctx.fillStyle = this._colors.cursor.css; - this._renderBlurCursor(this._terminal.buffer.x, viewportRelativeCursorY, this._cell); + this._renderBlurCursor(this._bufferService.buffer.x, viewportRelativeCursorY, this._cell); this._ctx.restore(); - this._state.x = this._terminal.buffer.x; + this._state.x = this._bufferService.buffer.x; this._state.y = viewportRelativeCursorY; this._state.isFocused = false; - this._state.style = this._terminal.options.cursorStyle; + this._state.style = this._optionsService.options.cursorStyle; this._state.width = this._cell.getWidth(); return; } @@ -157,10 +165,10 @@ export class CursorRenderLayer extends BaseRenderLayer { if (this._state) { // The cursor is already in the correct spot, don't redraw - if (this._state.x === this._terminal.buffer.x && + if (this._state.x === this._bufferService.buffer.x && this._state.y === viewportRelativeCursorY && this._state.isFocused === this._terminal.isFocused && - this._state.style === this._terminal.options.cursorStyle && + this._state.style === this._optionsService.options.cursorStyle && this._state.width === this._cell.getWidth()) { return; } @@ -168,13 +176,13 @@ export class CursorRenderLayer extends BaseRenderLayer { } this._ctx.save(); - this._cursorRenderers[this._terminal.options.cursorStyle || 'block'](this._terminal.buffer.x, viewportRelativeCursorY, this._cell); + this._cursorRenderers[this._optionsService.options.cursorStyle || 'block'](this._bufferService.buffer.x, viewportRelativeCursorY, this._cell); this._ctx.restore(); - this._state.x = this._terminal.buffer.x; + this._state.x = this._bufferService.buffer.x; this._state.y = viewportRelativeCursorY; this._state.isFocused = false; - this._state.style = this._terminal.options.cursorStyle; + this._state.style = this._optionsService.options.cursorStyle; this._state.width = this._cell.getWidth(); } @@ -237,11 +245,11 @@ class CursorBlinkStateManager { private _animationTimeRestarted: number; constructor( - terminal: ITerminal, + isFocused: boolean, private _renderCallback: () => void ) { this.isCursorVisible = true; - if (terminal.isFocused) { + if (isFocused) { this._restartInterval(); } } @@ -263,7 +271,7 @@ class CursorBlinkStateManager { } } - public restartBlinkAnimation(terminal: ITerminal): void { + public restartBlinkAnimation(): void { if (this.isPaused) { return; } @@ -346,9 +354,9 @@ class CursorBlinkStateManager { } } - public resume(terminal: ITerminal): void { + public resume(): void { this._animationTimeRestarted = null; this._restartInterval(); - this.restartBlinkAnimation(terminal); + this.restartBlinkAnimation(); } } diff --git a/src/renderer/LinkRenderLayer.ts b/src/renderer/LinkRenderLayer.ts index 52625439..b57abaab 100644 --- a/src/renderer/LinkRenderLayer.ts +++ b/src/renderer/LinkRenderLayer.ts @@ -8,15 +8,15 @@ import { IRenderDimensions } from 'browser/renderer/Types'; import { BaseRenderLayer } from './BaseRenderLayer'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { is256Color } from 'browser/renderer/atlas/CharAtlasUtils'; -import { IColorSet, ILinkifierEvent } from 'browser/Types'; +import { IColorSet, ILinkifierEvent, ILinkifier } from 'browser/Types'; export class LinkRenderLayer extends BaseRenderLayer { private _state: ILinkifierEvent = null; - constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ITerminal) { + constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ITerminal, linkifier: ILinkifier) { super(container, 'link', zIndex, true, colors, terminal); - terminal.linkifier.onLinkHover(e => this._onLinkHover(e)); - terminal.linkifier.onLinkLeave(e => this._onLinkLeave(e)); + linkifier.onLinkHover(e => this._onLinkHover(e)); + linkifier.onLinkLeave(e => this._onLinkLeave(e)); } public resize(dim: IRenderDimensions): void { diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 04ad6989..090cc8e8 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -14,7 +14,7 @@ import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistr import { Disposable } from 'common/Lifecycle'; import { IColorSet } from 'browser/Types'; import { ICharSizeService } from 'browser/services/Services'; -import { IBufferService } from 'common/services/Services'; +import { IBufferService, IOptionsService } from 'common/services/Services'; export class Renderer extends Disposable implements IRenderer { private _renderLayers: IRenderLayer[]; @@ -27,7 +27,8 @@ export class Renderer extends Disposable implements IRenderer { private _colors: IColorSet, private readonly _terminal: ITerminal, readonly bufferService: IBufferService, - private readonly _charSizeService: ICharSizeService + private readonly _charSizeService: ICharSizeService, + readonly optionsService: IOptionsService ) { super(); const allowTransparency = this._terminal.options.allowTransparency; @@ -35,9 +36,9 @@ export class Renderer extends Disposable implements IRenderer { this._renderLayers = [ new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._terminal), - new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors, this._terminal), - new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._terminal), - new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal) + new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors, this._terminal, bufferService), + new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._terminal, this._terminal.linkifier), + new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal, bufferService, optionsService) ]; this.dimensions = { scaledCharWidth: null, diff --git a/src/renderer/SelectionRenderLayer.ts b/src/renderer/SelectionRenderLayer.ts index 1735f786..233c4020 100644 --- a/src/renderer/SelectionRenderLayer.ts +++ b/src/renderer/SelectionRenderLayer.ts @@ -7,6 +7,7 @@ import { ITerminal } from '../Types'; import { IRenderDimensions } from 'browser/renderer/Types'; import { BaseRenderLayer } from './BaseRenderLayer'; import { IColorSet } from 'browser/Types'; +import { IBufferService } from 'common/services/Services'; interface ISelectionState { start: [number, number]; @@ -18,7 +19,13 @@ interface ISelectionState { export class SelectionRenderLayer extends BaseRenderLayer { private _state: ISelectionState; - constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ITerminal) { + constructor( + container: HTMLElement, + zIndex: number, + colors: IColorSet, + terminal: ITerminal, + private readonly _bufferService: IBufferService + ) { super(container, 'selection', zIndex, true, colors, terminal); this._clearState(); } @@ -47,7 +54,7 @@ export class SelectionRenderLayer extends BaseRenderLayer { public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean): void { // Selection has not changed - if (!this._didStateChange(start, end, columnSelectMode, this._terminal.buffer.ydisp)) { + if (!this._didStateChange(start, end, columnSelectMode, this._bufferService.buffer.ydisp)) { return; } @@ -61,8 +68,8 @@ export class SelectionRenderLayer extends BaseRenderLayer { } // Translate from buffer position to viewport position - const viewportStartRow = start[1] - this._terminal.buffer.ydisp; - const viewportEndRow = end[1] - this._terminal.buffer.ydisp; + 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._terminal.rows - 1); @@ -100,7 +107,7 @@ export class SelectionRenderLayer extends BaseRenderLayer { this._state.start = [start[0], start[1]]; this._state.end = [end[0], end[1]]; this._state.columnSelectMode = columnSelectMode; - this._state.ydisp = this._terminal.buffer.ydisp; + this._state.ydisp = this._bufferService.buffer.ydisp; } private _didStateChange(start: [number, number], end: [number, number], columnSelectMode: boolean, ydisp: number): boolean { From a48260279cd8d0f27b49be1dd95a1b76b499ffb8 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 11:20:25 -0700 Subject: [PATCH 07/11] Fix import in webgl addon --- addons/xterm-addon-webgl/src/WebglRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 41973138..3d41e1b5 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -7,7 +7,7 @@ import { ITerminal } from '../../../src/Types'; import { GlyphRenderer } from './GlyphRenderer'; import { LinkRenderLayer } from './renderLayer/LinkRenderLayer'; import { CursorRenderLayer } from './renderLayer/CursorRenderLayer'; -import { acquireCharAtlas } from 'browser/renderer/atlas/CharAtlasCache'; +import { acquireCharAtlas } from './atlas/CharAtlasCache'; import { WebglCharAtlas } from './atlas/WebglCharAtlas'; import { RectangleRenderer } from './RectangleRenderer'; import { IWebGL2RenderingContext } from './Types'; From 60ae9add3f214fee997d230409bdaa8e5a0c213a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 11:48:44 -0700 Subject: [PATCH 08/11] Remove most terminal refs from BaseRenderLayer --- src/renderer/BaseRenderLayer.ts | 17 ++++++++++------- src/renderer/CursorRenderLayer.ts | 6 +++--- src/renderer/LinkRenderLayer.ts | 13 +++++++++++-- src/renderer/Renderer.ts | 6 +++--- src/renderer/SelectionRenderLayer.ts | 7 ++++--- src/renderer/TextRenderLayer.ts | 14 ++++++++++++-- 6 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/renderer/BaseRenderLayer.ts b/src/renderer/BaseRenderLayer.ts index c0df47dc..929651fa 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/renderer/BaseRenderLayer.ts @@ -15,6 +15,7 @@ import { acquireCharAtlas } from 'browser/renderer/atlas/CharAtlasCache'; import { AttributeData } from 'common/buffer/AttributeData'; import { IColorSet } from 'browser/Types'; import { CellData } from 'common/buffer/CellData'; +import { IBufferService, IOptionsService } from 'common/services/Services'; export abstract class BaseRenderLayer implements IRenderLayer { private _canvas: HTMLCanvasElement; @@ -47,7 +48,9 @@ export abstract class BaseRenderLayer implements IRenderLayer { zIndex: number, private _alpha: boolean, protected _colors: IColorSet, - protected _terminal: ITerminal + protected _terminal: ITerminal, + protected readonly _bufferService: IBufferService, + protected readonly _optionsService: IOptionsService ) { this._canvas = document.createElement('canvas'); this._canvas.classList.add(`xterm-${id}-layer`); @@ -98,7 +101,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { // Regenerate char atlas and force a full redraw this._refreshCharAtlas(this._colors); - this.onGridChanged(0, this._terminal.rows - 1); + this.onGridChanged(0, this._bufferService.rows - 1); } /** @@ -282,7 +285,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { fg = (cell.isFgDefault()) ? DEFAULT_COLOR : cell.getFgColor(); } - const drawInBrightColor = this._terminal.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8 && fg !== INVERTED_DEFAULT_COLOR; + const drawInBrightColor = this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8 && fg !== INVERTED_DEFAULT_COLOR; fg += drawInBrightColor ? 8 : 0; this._currentGlyphIdentifier.chars = cell.getChars() || WHITESPACE_CELL_CHAR; @@ -334,7 +337,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`; } else { let fg = cell.getFgColor(); - if (this._terminal.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) { + if (this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) { fg += 8; } this._ctx.fillStyle = this._colors.ansi[fg].css; @@ -364,7 +367,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { this._ctx.rect( 0, y * this._scaledCellHeight, - this._terminal.cols * this._scaledCellWidth, + this._bufferService.cols * this._scaledCellWidth, this._scaledCellHeight); this._ctx.clip(); } @@ -374,10 +377,10 @@ export abstract class BaseRenderLayer implements IRenderLayer { * @param isBold If we should use the bold fontWeight. */ protected _getFont(isBold: boolean, isItalic: boolean): string { - const fontWeight = isBold ? this._terminal.options.fontWeightBold : this._terminal.options.fontWeight; + const fontWeight = isBold ? this._optionsService.options.fontWeightBold : this._optionsService.options.fontWeight; const fontStyle = isItalic ? 'italic' : ''; - return `${fontStyle} ${fontWeight} ${this._terminal.options.fontSize * window.devicePixelRatio}px ${this._terminal.options.fontFamily}`; + return `${fontStyle} ${fontWeight} ${this._optionsService.options.fontSize * window.devicePixelRatio}px ${this._optionsService.options.fontFamily}`; } } diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts index 26e5e4ac..57f7bcd5 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/renderer/CursorRenderLayer.ts @@ -35,10 +35,10 @@ export class CursorRenderLayer extends BaseRenderLayer { zIndex: number, colors: IColorSet, terminal: ITerminal, - private readonly _bufferService: IBufferService, - private readonly _optionsService: IOptionsService + readonly bufferService: IBufferService, + readonly optionsService: IOptionsService ) { - super(container, 'cursor', zIndex, true, colors, terminal); + super(container, 'cursor', zIndex, true, colors, terminal, bufferService, optionsService); this._state = { x: null, y: null, diff --git a/src/renderer/LinkRenderLayer.ts b/src/renderer/LinkRenderLayer.ts index b57abaab..f225f745 100644 --- a/src/renderer/LinkRenderLayer.ts +++ b/src/renderer/LinkRenderLayer.ts @@ -9,12 +9,21 @@ import { BaseRenderLayer } from './BaseRenderLayer'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { is256Color } from 'browser/renderer/atlas/CharAtlasUtils'; import { IColorSet, ILinkifierEvent, ILinkifier } from 'browser/Types'; +import { IBufferService, IOptionsService } from 'common/services/Services'; export class LinkRenderLayer extends BaseRenderLayer { private _state: ILinkifierEvent = null; - constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ITerminal, linkifier: ILinkifier) { - super(container, 'link', zIndex, true, colors, terminal); + constructor( + container: HTMLElement, + zIndex: number, + colors: IColorSet, + terminal: ITerminal, + linkifier: ILinkifier, + readonly bufferService: IBufferService, + readonly optionsService: IOptionsService + ) { + super(container, 'link', zIndex, true, colors, terminal, bufferService, optionsService); linkifier.onLinkHover(e => this._onLinkHover(e)); linkifier.onLinkLeave(e => this._onLinkLeave(e)); } diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 090cc8e8..03c44817 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -35,9 +35,9 @@ export class Renderer extends Disposable implements IRenderer { this._characterJoinerRegistry = new CharacterJoinerRegistry(bufferService); this._renderLayers = [ - new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._terminal), - new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors, this._terminal, bufferService), - new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._terminal, this._terminal.linkifier), + new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._terminal, bufferService, optionsService), + new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors, this._terminal, bufferService, optionsService), + new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._terminal, this._terminal.linkifier, bufferService, optionsService), new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal, bufferService, optionsService) ]; this.dimensions = { diff --git a/src/renderer/SelectionRenderLayer.ts b/src/renderer/SelectionRenderLayer.ts index 233c4020..97a38645 100644 --- a/src/renderer/SelectionRenderLayer.ts +++ b/src/renderer/SelectionRenderLayer.ts @@ -7,7 +7,7 @@ import { ITerminal } from '../Types'; import { IRenderDimensions } from 'browser/renderer/Types'; import { BaseRenderLayer } from './BaseRenderLayer'; import { IColorSet } from 'browser/Types'; -import { IBufferService } from 'common/services/Services'; +import { IBufferService, IOptionsService } from 'common/services/Services'; interface ISelectionState { start: [number, number]; @@ -24,9 +24,10 @@ export class SelectionRenderLayer extends BaseRenderLayer { zIndex: number, colors: IColorSet, terminal: ITerminal, - private readonly _bufferService: IBufferService + readonly bufferService: IBufferService, + readonly optionsService: IOptionsService ) { - super(container, 'selection', zIndex, true, colors, terminal); + super(container, 'selection', zIndex, true, colors, terminal, bufferService, optionsService); this._clearState(); } diff --git a/src/renderer/TextRenderLayer.ts b/src/renderer/TextRenderLayer.ts index b1e59451..e3035e97 100644 --- a/src/renderer/TextRenderLayer.ts +++ b/src/renderer/TextRenderLayer.ts @@ -13,6 +13,7 @@ import { NULL_CELL_CODE, Content } from 'common/buffer/Constants'; import { JoinedCellData } from 'browser/renderer/CharacterJoinerRegistry'; import { IColorSet } from 'browser/Types'; import { CellData } from 'common/buffer/CellData'; +import { IOptionsService, IBufferService } from 'common/services/Services'; /** * This CharData looks like a null character, which will forc a clear and render @@ -29,8 +30,17 @@ export class TextRenderLayer extends BaseRenderLayer { private _characterJoinerRegistry: ICharacterJoinerRegistry; private _workCell = new CellData(); - constructor(container: HTMLElement, zIndex: number, colors: IColorSet, characterJoinerRegistry: ICharacterJoinerRegistry, alpha: boolean, terminal: ITerminal) { - super(container, 'text', zIndex, alpha, colors, terminal); + constructor( + container: HTMLElement, + zIndex: number, + colors: IColorSet, + characterJoinerRegistry: ICharacterJoinerRegistry, + alpha: boolean, + terminal: ITerminal, + readonly bufferService: IBufferService, + readonly optionsService: IOptionsService + ) { + super(container, 'text', zIndex, alpha, colors, terminal, bufferService, optionsService); this._state = new GridCache(); this._characterJoinerRegistry = characterJoinerRegistry; } From cdefb3e7f2b4cfb43a69b12ab33dc500987f942a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 12:07:43 -0700 Subject: [PATCH 09/11] Remove more terminal from render layers and cache --- src/Terminal.ts | 3 +- src/browser/renderer/atlas/CharAtlasCache.ts | 29 ++++++++++---------- src/browser/services/RenderService.ts | 4 +++ src/browser/services/Services.ts | 3 +- src/renderer/BaseRenderLayer.ts | 5 ++-- src/renderer/CursorRenderLayer.ts | 5 ++-- src/renderer/LinkRenderLayer.ts | 5 ++-- src/renderer/Renderer.ts | 14 +++++++--- src/renderer/SelectionRenderLayer.ts | 15 +++++----- src/renderer/TextRenderLayer.ts | 21 +++++++------- 10 files changed, 55 insertions(+), 49 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 48792ac5..ddb5893c 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -38,7 +38,6 @@ import { SoundService } from 'browser/services/SoundService'; import { MouseZoneManager } from 'browser/MouseZoneManager'; import { AccessibilityManager } from './AccessibilityManager'; import { ITheme, IMarker, IDisposable, ISelectionPosition } from 'xterm'; -import { removeTerminalFromCache } from 'browser/renderer/atlas/CharAtlasCache'; import { DomRenderer } from './renderer/dom/DomRenderer'; import { IKeyboardEvent, KeyboardResultType, ICharset, IBufferLine, IAttributeData } from 'common/Types'; import { evaluateKeyboardEvent } from 'common/input/Keyboard'; @@ -264,8 +263,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._windowsMode.dispose(); this._windowsMode = undefined; } + this._renderService.dispose(); this._customKeyEventHandler = null; - removeTerminalFromCache(this); this.write = () => {}; if (this.element && this.element.parentNode) { this.element.parentNode.removeChild(this.element); diff --git a/src/browser/renderer/atlas/CharAtlasCache.ts b/src/browser/renderer/atlas/CharAtlasCache.ts index 92c970f2..257835ba 100644 --- a/src/browser/renderer/atlas/CharAtlasCache.ts +++ b/src/browser/renderer/atlas/CharAtlasCache.ts @@ -8,13 +8,14 @@ import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas'; import { DynamicCharAtlas } from 'browser/renderer/atlas/DynamicCharAtlas'; import { ICharAtlasConfig } from 'browser/renderer/atlas/Types'; import { IColorSet } from 'browser/Types'; +import { ITerminalOptions } from 'common/services/Services'; interface ICharAtlasCacheEntry { atlas: BaseCharAtlas; config: ICharAtlasConfig; // N.B. This implementation potentially holds onto copies of the terminal forever, so // this may cause memory leaks. - ownedBy: any[]; + ownedBy: number[]; } const charAtlasCache: ICharAtlasCacheEntry[] = []; @@ -22,26 +23,25 @@ const charAtlasCache: ICharAtlasCacheEntry[] = []; /** * Acquires a char atlas, either generating a new one or returning an existing * one that is in use by another terminal. - * @param terminal The terminal. - * @param colors The colors to use. */ export function acquireCharAtlas( - terminal: any, + options: ITerminalOptions, + rendererId: number, colors: IColorSet, scaledCharWidth: number, scaledCharHeight: number ): BaseCharAtlas { - const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, terminal.optionsService.options, colors); + const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, options, colors); - // Check to see if the terminal already owns this config + // Check to see if the renderer already owns this config for (let i = 0; i < charAtlasCache.length; i++) { const entry = charAtlasCache[i]; - const ownedByIndex = entry.ownedBy.indexOf(terminal); + const ownedByIndex = entry.ownedBy.indexOf(rendererId); if (ownedByIndex >= 0) { if (configEquals(entry.config, newConfig)) { return entry.atlas; } - // The configs differ, release the terminal from the entry + // The configs differ, release the renderer from the entry if (entry.ownedBy.length === 1) { entry.atlas.dispose(); charAtlasCache.splice(i, 1); @@ -56,8 +56,8 @@ export function acquireCharAtlas( for (let i = 0; i < charAtlasCache.length; i++) { const entry = charAtlasCache[i]; if (configEquals(entry.config, newConfig)) { - // Add the terminal to the cache entry and return - entry.ownedBy.push(terminal); + // Add the renderer to the cache entry and return + entry.ownedBy.push(rendererId); return entry.atlas; } } @@ -68,7 +68,7 @@ export function acquireCharAtlas( newConfig ), config: newConfig, - ownedBy: [terminal] + ownedBy: [rendererId] }; charAtlasCache.push(newEntry); return newEntry.atlas; @@ -76,14 +76,13 @@ export function acquireCharAtlas( /** * Removes a terminal reference from the cache, allowing its memory to be freed. - * @param terminal The terminal to remove. */ -export function removeTerminalFromCache(terminal: any): void { +export function removeTerminalFromCache(rendererId: number): void { for (let i = 0; i < charAtlasCache.length; i++) { - const index = charAtlasCache[i].ownedBy.indexOf(terminal); + const index = charAtlasCache[i].ownedBy.indexOf(rendererId); if (index !== -1) { if (charAtlasCache[i].ownedBy.length === 1) { - // Remove the cache entry if it's the only terminal + // Remove the cache entry if it's the only renderer charAtlasCache[i].atlas.dispose(); charAtlasCache.splice(i, 1); } else { diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index ecf20e28..c0b3c0e5 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -103,6 +103,10 @@ export class RenderService extends Disposable implements IRenderService { this._onDimensionsChange.fire(this._renderer.dimensions); } + public dispose(): void { + this._renderer.dispose(); + } + public setRenderer(renderer: IRenderer): void { // TODO: RenderCoordinator should be the only one to dispose the renderer this._renderer.dispose(); diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts index 863f3a9e..2170a04e 100644 --- a/src/browser/services/Services.ts +++ b/src/browser/services/Services.ts @@ -8,6 +8,7 @@ import { IRenderDimensions, IRenderer, CharacterJoinerHandler } from 'browser/re import { IColorSet } from 'browser/Types'; import { ISelectionRedrawRequestEvent } from 'browser/selection/Types'; import { createDecorator } from 'common/services/ServiceRegistry'; +import { IDisposable } from 'common/Types'; export const ICharSizeService = createDecorator('CharSizeService'); export interface ICharSizeService { @@ -31,7 +32,7 @@ export interface IMouseService { } export const IRenderService = createDecorator('RenderService'); -export interface IRenderService { +export interface IRenderService extends IDisposable { serviceBrand: any; onDimensionsChange: IEvent; diff --git a/src/renderer/BaseRenderLayer.ts b/src/renderer/BaseRenderLayer.ts index 929651fa..4fd45e30 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/renderer/BaseRenderLayer.ts @@ -5,7 +5,6 @@ import { IRenderLayer } from './Types'; import { IRenderDimensions } from 'browser/renderer/Types'; -import { ITerminal } from '../Types'; import { ICellData } from 'common/Types'; import { DEFAULT_COLOR, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE } from 'common/buffer/Constants'; import { IGlyphIdentifier } from 'browser/renderer/atlas/Types'; @@ -48,7 +47,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { zIndex: number, private _alpha: boolean, protected _colors: IColorSet, - protected _terminal: ITerminal, + private _rendererId: number, protected readonly _bufferService: IBufferService, protected readonly _optionsService: IOptionsService ) { @@ -112,7 +111,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) { return; } - this._charAtlas = acquireCharAtlas(this._terminal, colorSet, this._scaledCharWidth, this._scaledCharHeight); + this._charAtlas = acquireCharAtlas(this._optionsService.options, this._rendererId, colorSet, this._scaledCharWidth, this._scaledCharHeight); this._charAtlas.warmUp(); } diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts index 57f7bcd5..9d00864a 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/renderer/CursorRenderLayer.ts @@ -34,11 +34,12 @@ export class CursorRenderLayer extends BaseRenderLayer { container: HTMLElement, zIndex: number, colors: IColorSet, - terminal: ITerminal, + private _terminal: ITerminal, + rendererId: number, readonly bufferService: IBufferService, readonly optionsService: IOptionsService ) { - super(container, 'cursor', zIndex, true, colors, terminal, bufferService, optionsService); + super(container, 'cursor', zIndex, true, colors, rendererId, bufferService, optionsService); this._state = { x: null, y: null, diff --git a/src/renderer/LinkRenderLayer.ts b/src/renderer/LinkRenderLayer.ts index f225f745..51b77396 100644 --- a/src/renderer/LinkRenderLayer.ts +++ b/src/renderer/LinkRenderLayer.ts @@ -3,7 +3,6 @@ * @license MIT */ -import { ITerminal } from '../Types'; import { IRenderDimensions } from 'browser/renderer/Types'; import { BaseRenderLayer } from './BaseRenderLayer'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; @@ -18,12 +17,12 @@ export class LinkRenderLayer extends BaseRenderLayer { container: HTMLElement, zIndex: number, colors: IColorSet, - terminal: ITerminal, + rendererId: number, linkifier: ILinkifier, readonly bufferService: IBufferService, readonly optionsService: IOptionsService ) { - super(container, 'link', zIndex, true, colors, terminal, bufferService, optionsService); + super(container, 'link', zIndex, true, colors, rendererId, bufferService, optionsService); linkifier.onLinkHover(e => this._onLinkHover(e)); linkifier.onLinkLeave(e => this._onLinkLeave(e)); } diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 03c44817..c7154c02 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -15,8 +15,13 @@ import { Disposable } from 'common/Lifecycle'; import { IColorSet } from 'browser/Types'; import { ICharSizeService } from 'browser/services/Services'; import { IBufferService, IOptionsService } from 'common/services/Services'; +import { removeTerminalFromCache } from 'browser/renderer/atlas/CharAtlasCache'; + +let nextRendererId = 1; export class Renderer extends Disposable implements IRenderer { + private _id = nextRendererId++; + private _renderLayers: IRenderLayer[]; private _devicePixelRatio: number; private _characterJoinerRegistry: ICharacterJoinerRegistry; @@ -35,10 +40,10 @@ export class Renderer extends Disposable implements IRenderer { this._characterJoinerRegistry = new CharacterJoinerRegistry(bufferService); this._renderLayers = [ - new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._terminal, bufferService, optionsService), - new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors, this._terminal, bufferService, optionsService), - new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._terminal, this._terminal.linkifier, bufferService, optionsService), - new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal, bufferService, optionsService) + new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._id, bufferService, optionsService), + new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors, this._id, bufferService, optionsService), + new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._id, this._terminal.linkifier, bufferService, optionsService), + new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal, this._id, bufferService, optionsService) ]; this.dimensions = { scaledCharWidth: null, @@ -62,6 +67,7 @@ export class Renderer extends Disposable implements IRenderer { public dispose(): void { super.dispose(); this._renderLayers.forEach(l => l.dispose()); + removeTerminalFromCache(this._id); } public onDevicePixelRatioChange(): void { diff --git a/src/renderer/SelectionRenderLayer.ts b/src/renderer/SelectionRenderLayer.ts index 97a38645..4c59594e 100644 --- a/src/renderer/SelectionRenderLayer.ts +++ b/src/renderer/SelectionRenderLayer.ts @@ -3,7 +3,6 @@ * @license MIT */ -import { ITerminal } from '../Types'; import { IRenderDimensions } from 'browser/renderer/Types'; import { BaseRenderLayer } from './BaseRenderLayer'; import { IColorSet } from 'browser/Types'; @@ -23,11 +22,11 @@ export class SelectionRenderLayer extends BaseRenderLayer { container: HTMLElement, zIndex: number, colors: IColorSet, - terminal: ITerminal, + rendererId: number, readonly bufferService: IBufferService, readonly optionsService: IOptionsService ) { - super(container, 'selection', zIndex, true, colors, terminal, bufferService, optionsService); + super(container, 'selection', zIndex, true, colors, rendererId, bufferService, optionsService); this._clearState(); } @@ -72,10 +71,10 @@ export class SelectionRenderLayer extends BaseRenderLayer { 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._terminal.rows - 1); + const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1); // No need to draw the selection - if (viewportCappedStartRow >= this._terminal.rows || viewportCappedEndRow < 0) { + if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) { return; } @@ -89,17 +88,17 @@ export class SelectionRenderLayer extends BaseRenderLayer { } else { // Draw first row const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0; - const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : this._terminal.cols; + const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? 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._terminal.cols, middleRowsCount); + 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._terminal.cols; + const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._bufferService.cols; this._fillCells(0, viewportCappedEndRow, endCol, 1); } } diff --git a/src/renderer/TextRenderLayer.ts b/src/renderer/TextRenderLayer.ts index e3035e97..06e334ef 100644 --- a/src/renderer/TextRenderLayer.ts +++ b/src/renderer/TextRenderLayer.ts @@ -4,7 +4,6 @@ */ import { ICharacterJoinerRegistry, IRenderDimensions } from 'browser/renderer/Types'; -import { ITerminal } from '../Types'; import { CharData, ICellData } from 'common/Types'; import { GridCache } from 'browser/renderer/GridCache'; import { BaseRenderLayer } from './BaseRenderLayer'; @@ -36,11 +35,11 @@ export class TextRenderLayer extends BaseRenderLayer { colors: IColorSet, characterJoinerRegistry: ICharacterJoinerRegistry, alpha: boolean, - terminal: ITerminal, + rendererId: number, readonly bufferService: IBufferService, readonly optionsService: IOptionsService ) { - super(container, 'text', zIndex, alpha, colors, terminal, bufferService, optionsService); + super(container, 'text', zIndex, alpha, colors, rendererId, bufferService, optionsService); this._state = new GridCache(); this._characterJoinerRegistry = characterJoinerRegistry; } @@ -57,7 +56,7 @@ export class TextRenderLayer extends BaseRenderLayer { } // Resizing the canvas discards the contents of the canvas so clear state this._state.clear(); - this._state.resize(this._terminal.cols, this._terminal.rows); + this._state.resize(this._bufferService.cols, this._bufferService.rows); } public reset(): void { @@ -76,10 +75,10 @@ export class TextRenderLayer extends BaseRenderLayer { ) => void ): void { for (let y = firstRow; y <= lastRow; y++) { - const row = y + this._terminal.buffer.ydisp; - const line = this._terminal.buffer.lines.get(row); + const row = y + this._bufferService.buffer.ydisp; + const line = this._bufferService.buffer.lines.get(row); const joinedRanges = joinerRegistry ? joinerRegistry.getJoinedCharacters(row) : []; - for (let x = 0; x < this._terminal.cols; x++) { + for (let x = 0; x < this._bufferService.cols; x++) { line.loadCell(x, this._workCell); let cell = this._workCell; @@ -154,7 +153,7 @@ export class TextRenderLayer extends BaseRenderLayer { */ private _drawBackground(firstRow: number, lastRow: number): void { const ctx = this._ctx; - const cols = this._terminal.cols; + const cols = this._bufferService.cols; let startX: number = 0; let startY: number = 0; let prevFillStyle: string | null = null; @@ -235,7 +234,7 @@ export class TextRenderLayer extends BaseRenderLayer { this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`; } else { let fg = cell.getFgColor(); - if (this._terminal.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) { + if (this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) { fg += 8; } this._ctx.fillStyle = this._colors.ansi[fg].css; @@ -258,13 +257,13 @@ export class TextRenderLayer extends BaseRenderLayer { this._charAtlas.beginFrame(); } - this._clearCells(0, firstRow, this._terminal.cols, lastRow - firstRow + 1); + this._clearCells(0, firstRow, this._bufferService.cols, lastRow - firstRow + 1); this._drawBackground(firstRow, lastRow); this._drawForeground(firstRow, lastRow); } public onOptionsChanged(): void { - this._setTransparency(this._terminal.options.allowTransparency); + this._setTransparency(this._optionsService.options.allowTransparency); } /** From 9830a077c1b275740678a388dbb48ba234485b58 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 12:22:11 -0700 Subject: [PATCH 10/11] Move BaseRenderLayer into browser --- src/{ => browser}/renderer/BaseRenderLayer.ts | 10 +-- src/browser/renderer/RendererUtils.ts | 11 +++ src/browser/renderer/Types.d.ts | 58 ++++++++++++++++ .../renderer/atlas/DynamicCharAtlas.ts | 8 +-- src/renderer/CursorRenderLayer.ts | 2 +- src/renderer/LinkRenderLayer.ts | 2 +- src/renderer/Renderer.ts | 3 +- src/renderer/SelectionRenderLayer.ts | 2 +- src/renderer/TextRenderLayer.ts | 2 +- src/renderer/Types.d.ts | 67 ------------------- 10 files changed, 80 insertions(+), 85 deletions(-) rename src/{ => browser}/renderer/BaseRenderLayer.ts (97%) create mode 100644 src/browser/renderer/RendererUtils.ts delete mode 100644 src/renderer/Types.d.ts diff --git a/src/renderer/BaseRenderLayer.ts b/src/browser/renderer/BaseRenderLayer.ts similarity index 97% rename from src/renderer/BaseRenderLayer.ts rename to src/browser/renderer/BaseRenderLayer.ts index 4fd45e30..e9ad5b3a 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/browser/renderer/BaseRenderLayer.ts @@ -3,8 +3,7 @@ * @license MIT */ -import { IRenderLayer } from './Types'; -import { IRenderDimensions } from 'browser/renderer/Types'; +import { IRenderDimensions, IRenderLayer } from 'browser/renderer/Types'; import { ICellData } from 'common/Types'; import { DEFAULT_COLOR, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE } from 'common/buffer/Constants'; import { IGlyphIdentifier } from 'browser/renderer/atlas/Types'; @@ -15,10 +14,11 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { IColorSet } from 'browser/Types'; import { CellData } from 'common/buffer/CellData'; import { IBufferService, IOptionsService } from 'common/services/Services'; +import { throwIfFalsy } from 'browser/renderer/RendererUtils'; export abstract class BaseRenderLayer implements IRenderLayer { private _canvas: HTMLCanvasElement; - protected _ctx: CanvasRenderingContext2D; + protected _ctx!: CanvasRenderingContext2D; private _scaledCharWidth: number = 0; private _scaledCharHeight: number = 0; private _scaledCellWidth: number = 0; @@ -26,7 +26,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { private _scaledCharLeft: number = 0; private _scaledCharTop: number = 0; - protected _charAtlas: BaseCharAtlas; + protected _charAtlas: BaseCharAtlas | undefined; /** * An object that's reused when drawing glyphs in order to reduce GC. @@ -66,7 +66,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { } private _initCanvas(): void { - this._ctx = this._canvas.getContext('2d', {alpha: this._alpha}); + this._ctx = throwIfFalsy(this._canvas.getContext('2d', {alpha: this._alpha})); // Draw the background if this is an opaque layer if (!this._alpha) { this._clearAll(); diff --git a/src/browser/renderer/RendererUtils.ts b/src/browser/renderer/RendererUtils.ts new file mode 100644 index 00000000..48fd26a4 --- /dev/null +++ b/src/browser/renderer/RendererUtils.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +export function throwIfFalsy(value: T | undefined | null): T { + if (!value) { + throw new Error('value must not be falsy'); + } + return value; +} diff --git a/src/browser/renderer/Types.d.ts b/src/browser/renderer/Types.d.ts index df8f0031..7f84dfd4 100644 --- a/src/browser/renderer/Types.d.ts +++ b/src/browser/renderer/Types.d.ts @@ -56,3 +56,61 @@ export interface ICharacterJoinerRegistry { deregisterCharacterJoiner(joinerId: number): boolean; getJoinedCharacters(row: number): [number, number][]; } + +export interface IRenderLayer extends IDisposable { + /** + * Called when the terminal loses focus. + */ + onBlur(): void; + + /** + * * Called when the terminal gets focus. + */ + onFocus(): void; + + /** + * Called when the cursor is moved. + */ + onCursorMove(): void; + + /** + * Called when options change. + */ + onOptionsChanged(): void; + + /** + * Called when the theme changes. + */ + setColors(colorSet: IColorSet): void; + + /** + * Called when the data in the grid has changed (or needs to be rendered + * again). + */ + onGridChanged(startRow: number, endRow: number): void; + + /** + * Calls when the selection changes. + */ + onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean): void; + + /** + * Registers a handler to join characters to render as a group + */ + registerCharacterJoiner?(joiner: ICharacterJoiner): void; + + /** + * Deregisters the specified character joiner handler + */ + deregisterCharacterJoiner?(joinerId: number): void; + + /** + * Resize the render layer. + */ + resize(dim: IRenderDimensions): void; + + /** + * Clear the state of the render layer. + */ + reset(): void; +} diff --git a/src/browser/renderer/atlas/DynamicCharAtlas.ts b/src/browser/renderer/atlas/DynamicCharAtlas.ts index d591c173..40103bc7 100644 --- a/src/browser/renderer/atlas/DynamicCharAtlas.ts +++ b/src/browser/renderer/atlas/DynamicCharAtlas.ts @@ -10,6 +10,7 @@ import { DEFAULT_ANSI_COLORS } from 'browser/ColorManager'; import { LRUMap } from 'browser/renderer/atlas/LRUMap'; import { isFirefox, isSafari } from 'common/Platform'; import { IColor } from 'browser/Types'; +import { throwIfFalsy } from 'browser/renderer/RendererUtils'; // In practice we're probably never going to exhaust a texture this large. For debugging purposes, // however, it can be useful to set this to a really tiny value, to verify that LRU eviction works. @@ -366,10 +367,3 @@ function clearColor(imageData: ImageData, color: IColor): boolean { } return isEmpty; } - -export function throwIfFalsy(value: T | undefined | null): T { - if (!value) { - throw new Error('value must not be falsy'); - } - return value; -} diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts index 9d00864a..f847c5f6 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/renderer/CursorRenderLayer.ts @@ -4,7 +4,7 @@ */ import { IRenderDimensions } from 'browser/renderer/Types'; -import { BaseRenderLayer } from './BaseRenderLayer'; +import { BaseRenderLayer } from '../browser/renderer/BaseRenderLayer'; import { ITerminal } from '../Types'; import { ICellData } from 'common/Types'; import { CellData } from 'common/buffer/CellData'; diff --git a/src/renderer/LinkRenderLayer.ts b/src/renderer/LinkRenderLayer.ts index 51b77396..d53db9b5 100644 --- a/src/renderer/LinkRenderLayer.ts +++ b/src/renderer/LinkRenderLayer.ts @@ -4,7 +4,7 @@ */ import { IRenderDimensions } from 'browser/renderer/Types'; -import { BaseRenderLayer } from './BaseRenderLayer'; +import { BaseRenderLayer } from '../browser/renderer/BaseRenderLayer'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { is256Color } from 'browser/renderer/atlas/CharAtlasUtils'; import { IColorSet, ILinkifierEvent, ILinkifier } from 'browser/Types'; diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index c7154c02..239a2ca0 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -6,8 +6,7 @@ import { TextRenderLayer } from './TextRenderLayer'; import { SelectionRenderLayer } from './SelectionRenderLayer'; import { CursorRenderLayer } from './CursorRenderLayer'; -import { IRenderLayer } from './Types'; -import { IRenderer, IRenderDimensions, CharacterJoinerHandler, ICharacterJoinerRegistry } from 'browser/renderer/Types'; +import { IRenderLayer, IRenderer, IRenderDimensions, CharacterJoinerHandler, ICharacterJoinerRegistry } from 'browser/renderer/Types'; import { ITerminal } from '../Types'; import { LinkRenderLayer } from './LinkRenderLayer'; import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistry'; diff --git a/src/renderer/SelectionRenderLayer.ts b/src/renderer/SelectionRenderLayer.ts index 4c59594e..4673d841 100644 --- a/src/renderer/SelectionRenderLayer.ts +++ b/src/renderer/SelectionRenderLayer.ts @@ -4,7 +4,7 @@ */ import { IRenderDimensions } from 'browser/renderer/Types'; -import { BaseRenderLayer } from './BaseRenderLayer'; +import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer'; import { IColorSet } from 'browser/Types'; import { IBufferService, IOptionsService } from 'common/services/Services'; diff --git a/src/renderer/TextRenderLayer.ts b/src/renderer/TextRenderLayer.ts index 06e334ef..8a86b57e 100644 --- a/src/renderer/TextRenderLayer.ts +++ b/src/renderer/TextRenderLayer.ts @@ -6,7 +6,7 @@ import { ICharacterJoinerRegistry, IRenderDimensions } from 'browser/renderer/Types'; import { CharData, ICellData } from 'common/Types'; import { GridCache } from 'browser/renderer/GridCache'; -import { BaseRenderLayer } from './BaseRenderLayer'; +import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer'; import { AttributeData } from 'common/buffer/AttributeData'; import { NULL_CELL_CODE, Content } from 'common/buffer/Constants'; import { JoinedCellData } from 'browser/renderer/CharacterJoinerRegistry'; diff --git a/src/renderer/Types.d.ts b/src/renderer/Types.d.ts deleted file mode 100644 index 422a01c6..00000000 --- a/src/renderer/Types.d.ts +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { ITerminal } from '../Types'; -import { IDisposable } from 'xterm'; -import { IColorSet } from 'browser/Types'; -import { IRenderDimensions, CharacterJoinerHandler, ICharacterJoiner } from 'browser/renderer/Types'; - -export interface IRenderLayer extends IDisposable { - /** - * Called when the terminal loses focus. - */ - onBlur(): void; - - /** - * * Called when the terminal gets focus. - */ - onFocus(): void; - - /** - * Called when the cursor is moved. - */ - onCursorMove(): void; - - /** - * Called when options change. - */ - onOptionsChanged(): void; - - /** - * Called when the theme changes. - */ - setColors(colorSet: IColorSet): void; - - /** - * Called when the data in the grid has changed (or needs to be rendered - * again). - */ - onGridChanged(startRow: number, endRow: number): void; - - /** - * Calls when the selection changes. - */ - onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean): void; - - /** - * Registers a handler to join characters to render as a group - */ - registerCharacterJoiner?(joiner: ICharacterJoiner): void; - - /** - * Deregisters the specified character joiner handler - */ - deregisterCharacterJoiner?(joinerId: number): void; - - /** - * Resize the render layer. - */ - resize(dim: IRenderDimensions): void; - - /** - * Clear the state of the render layer. - */ - reset(): void; -} From 2b7be05da355aa7c51119a4ed7146a286e6bc456 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 12:36:37 -0700 Subject: [PATCH 11/11] Move 3 render layers to browser --- src/{ => browser}/renderer/LinkRenderLayer.ts | 10 +++++----- .../renderer/SelectionRenderLayer.ts | 20 +++++++++---------- src/{ => browser}/renderer/TextRenderLayer.ts | 20 ++++++++++--------- src/renderer/Renderer.ts | 6 +++--- 4 files changed, 29 insertions(+), 27 deletions(-) rename src/{ => browser}/renderer/LinkRenderLayer.ts (92%) rename src/{ => browser}/renderer/SelectionRenderLayer.ts (91%) rename src/{ => browser}/renderer/TextRenderLayer.ts (95%) diff --git a/src/renderer/LinkRenderLayer.ts b/src/browser/renderer/LinkRenderLayer.ts similarity index 92% rename from src/renderer/LinkRenderLayer.ts rename to src/browser/renderer/LinkRenderLayer.ts index d53db9b5..a7be54ee 100644 --- a/src/renderer/LinkRenderLayer.ts +++ b/src/browser/renderer/LinkRenderLayer.ts @@ -4,14 +4,14 @@ */ import { IRenderDimensions } from 'browser/renderer/Types'; -import { BaseRenderLayer } from '../browser/renderer/BaseRenderLayer'; +import { BaseRenderLayer } from './BaseRenderLayer'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { is256Color } from 'browser/renderer/atlas/CharAtlasUtils'; import { IColorSet, ILinkifierEvent, ILinkifier } from 'browser/Types'; import { IBufferService, IOptionsService } from 'common/services/Services'; export class LinkRenderLayer extends BaseRenderLayer { - private _state: ILinkifierEvent = null; + private _state: ILinkifierEvent | undefined; constructor( container: HTMLElement, @@ -30,7 +30,7 @@ export class LinkRenderLayer extends BaseRenderLayer { public resize(dim: IRenderDimensions): void { super.resize(dim); // Resizing the canvas discards the contents of the canvas so clear state - this._state = null; + this._state = undefined; } public reset(): void { @@ -45,14 +45,14 @@ export class LinkRenderLayer extends BaseRenderLayer { this._clearCells(0, this._state.y1 + 1, this._state.cols, middleRowCount); } this._clearCells(0, this._state.y2, this._state.x2, 1); - this._state = null; + this._state = undefined; } } private _onLinkHover(e: ILinkifierEvent): void { if (e.fg === INVERTED_DEFAULT_COLOR) { this._ctx.fillStyle = this._colors.background.css; - } else if (is256Color(e.fg)) { + } else if (e.fg && is256Color(e.fg)) { // 256 color support this._ctx.fillStyle = this._colors.ansi[e.fg].css; } else { diff --git a/src/renderer/SelectionRenderLayer.ts b/src/browser/renderer/SelectionRenderLayer.ts similarity index 91% rename from src/renderer/SelectionRenderLayer.ts rename to src/browser/renderer/SelectionRenderLayer.ts index 4673d841..5cd2fd91 100644 --- a/src/renderer/SelectionRenderLayer.ts +++ b/src/browser/renderer/SelectionRenderLayer.ts @@ -9,14 +9,14 @@ import { IColorSet } from 'browser/Types'; import { IBufferService, IOptionsService } from 'common/services/Services'; interface ISelectionState { - start: [number, number]; - end: [number, number]; - columnSelectMode: boolean; - ydisp: number; + start?: [number, number]; + end?: [number, number]; + columnSelectMode?: boolean; + ydisp?: number; } export class SelectionRenderLayer extends BaseRenderLayer { - private _state: ISelectionState; + private _state!: ISelectionState; constructor( container: HTMLElement, @@ -32,10 +32,10 @@ export class SelectionRenderLayer extends BaseRenderLayer { private _clearState(): void { this._state = { - start: null, - end: null, - columnSelectMode: null, - ydisp: null + start: undefined, + end: undefined, + columnSelectMode: undefined, + ydisp: undefined }; } @@ -117,7 +117,7 @@ export class SelectionRenderLayer extends BaseRenderLayer { ydisp !== this._state.ydisp; } - private _areCoordinatesEqual(coord1: [number, number], coord2: [number, number]): boolean { + private _areCoordinatesEqual(coord1: [number, number] | undefined, coord2: [number, number] | undefined): boolean { if (!coord1 || !coord2) { return false; } diff --git a/src/renderer/TextRenderLayer.ts b/src/browser/renderer/TextRenderLayer.ts similarity index 95% rename from src/renderer/TextRenderLayer.ts rename to src/browser/renderer/TextRenderLayer.ts index 8a86b57e..330400ca 100644 --- a/src/renderer/TextRenderLayer.ts +++ b/src/browser/renderer/TextRenderLayer.ts @@ -23,8 +23,8 @@ import { IOptionsService, IBufferService } from 'common/services/Services'; export class TextRenderLayer extends BaseRenderLayer { private _state: GridCache; - private _characterWidth: number; - private _characterFont: string; + private _characterWidth: number = 0; + private _characterFont: string = ''; private _characterOverlapCache: { [key: string]: boolean } = {}; private _characterJoinerRegistry: ICharacterJoinerRegistry; private _workCell = new CellData(); @@ -79,7 +79,7 @@ export class TextRenderLayer extends BaseRenderLayer { const line = this._bufferService.buffer.lines.get(row); const joinedRanges = joinerRegistry ? joinerRegistry.getJoinedCharacters(row) : []; for (let x = 0; x < this._bufferService.cols; x++) { - line.loadCell(x, this._workCell); + line!.loadCell(x, this._workCell); let cell = this._workCell; // If true, indicates that the current character(s) to draw were joined. @@ -97,14 +97,14 @@ export class TextRenderLayer extends BaseRenderLayer { // and attributes of our input. if (joinedRanges.length > 0 && x === joinedRanges[0][0]) { isJoined = true; - const range = joinedRanges.shift(); + 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]), + line!.translateToString(true, range[0], range[1]), range[1] - range[0] ); @@ -124,7 +124,7 @@ export class TextRenderLayer extends BaseRenderLayer { // 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) { + 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; @@ -184,15 +184,17 @@ export class TextRenderLayer extends BaseRenderLayer { // don't need to draw anything. startX = x; startY = y; - } if (y !== startY) { + } + + if (y !== startY) { // our row changed, draw the previous row - ctx.fillStyle = prevFillStyle; + ctx.fillStyle = prevFillStyle ? 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; + ctx.fillStyle = prevFillStyle ? prevFillStyle : ''; this._fillCells(startX, startY, x - startX, 1); startX = x; startY = y; diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 239a2ca0..d3977af3 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -3,12 +3,12 @@ * @license MIT */ -import { TextRenderLayer } from './TextRenderLayer'; -import { SelectionRenderLayer } from './SelectionRenderLayer'; +import { TextRenderLayer } from '../browser/renderer/TextRenderLayer'; +import { SelectionRenderLayer } from '../browser/renderer/SelectionRenderLayer'; import { CursorRenderLayer } from './CursorRenderLayer'; import { IRenderLayer, IRenderer, IRenderDimensions, CharacterJoinerHandler, ICharacterJoinerRegistry } from 'browser/renderer/Types'; import { ITerminal } from '../Types'; -import { LinkRenderLayer } from './LinkRenderLayer'; +import { LinkRenderLayer } from '../browser/renderer/LinkRenderLayer'; import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistry'; import { Disposable } from 'common/Lifecycle'; import { IColorSet } from 'browser/Types';