Skip to content

Commit

Permalink
Merge pull request #4185 from Tyriar/4167_register_emitter
Browse files Browse the repository at this point in the history
Fix a bunch of memory retention problems
  • Loading branch information
Tyriar committed Oct 8, 2022
2 parents d948b31 + d8cf3af commit d8c31d7
Show file tree
Hide file tree
Showing 41 changed files with 278 additions and 321 deletions.
12 changes: 7 additions & 5 deletions addons/xterm-addon-canvas/src/BaseRenderLayer.ts
Expand Up @@ -19,8 +19,9 @@ import { ICellData } from 'common/Types';
import { Terminal } from 'xterm';
import { IRenderLayer } from './Types';
import { CellColorResolver } from 'browser/renderer/shared/CellColorResolver';
import { Disposable, toDisposable } from 'common/Lifecycle';

export abstract class BaseRenderLayer implements IRenderLayer {
export abstract class BaseRenderLayer extends Disposable implements IRenderLayer {
private _canvas: HTMLCanvasElement;
protected _ctx!: CanvasRenderingContext2D;
private _scaledCharWidth: number = 0;
Expand Down Expand Up @@ -51,18 +52,19 @@ export abstract class BaseRenderLayer implements IRenderLayer {
protected readonly _decorationService: IDecorationService,
protected readonly _coreBrowserService: ICoreBrowserService
) {
super();
this._cellColorResolver = new CellColorResolver(this._terminal, this._colors, this._selectionModel, this._decorationService, this._coreBrowserService);
this._canvas = document.createElement('canvas');
this._canvas.classList.add(`xterm-${id}-layer`);
this._canvas.style.zIndex = zIndex.toString();
this._initCanvas();
this._container.appendChild(this._canvas);
this._refreshCharAtlas(this._colors);
}

public dispose(): void {
removeElementFromParent(this._canvas);
this._charAtlas?.dispose();
this.register(toDisposable(() => {
removeElementFromParent(this._canvas);
this._charAtlas?.dispose();
}));
}

private _initCanvas(): void {
Expand Down
35 changes: 17 additions & 18 deletions addons/xterm-addon-canvas/src/CanvasAddon.ts
Expand Up @@ -9,20 +9,26 @@ import { CanvasRenderer } from './CanvasRenderer';
import { IBufferService, ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
import { ITerminalAddon, Terminal } from 'xterm';
import { EventEmitter, forwardEvent } from 'common/EventEmitter';
import { Disposable, toDisposable } from 'common/Lifecycle';

export class CanvasAddon implements ITerminalAddon {
export class CanvasAddon extends Disposable implements ITerminalAddon {
private _terminal?: Terminal;
private _renderer?: CanvasRenderer;

private readonly _onChangeTextureAtlas = new EventEmitter<HTMLCanvasElement>();
private readonly _onChangeTextureAtlas = this.register(new EventEmitter<HTMLCanvasElement>());
public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event;

public get textureAtlas(): HTMLCanvasElement | undefined {
return this._renderer?.textureAtlas;
}

public activate(terminal: Terminal): void {
const core = (terminal as any)._core;
if (!terminal.element) {
core.onWillOpen(() => this.activate(terminal));
this.register(core.onWillOpen(() => this.activate(terminal)));
return;
}

this._terminal = terminal;
const bufferService: IBufferService = core._bufferService;
const renderService: IRenderService = core._renderService;
Expand All @@ -35,24 +41,17 @@ export class CanvasAddon implements ITerminalAddon {
const colors: IColorSet = core._colorManager.colors;
const screenElement: HTMLElement = core.screenElement;
const linkifier = core.linkifier2;

this._renderer = new CanvasRenderer(terminal, colors, screenElement, linkifier, bufferService, charSizeService, optionsService, characterJoinerService, coreService, coreBrowserService, decorationService);
forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas);
this.register(forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas));
renderService.setRenderer(this._renderer);
renderService.onResize(bufferService.cols, bufferService.rows);
}

public dispose(): void {
if (!this._terminal) {
throw new Error('Cannot dispose CanvasAddon because it is activated');
}
const renderService: IRenderService = (this._terminal as any)._core._renderService;
renderService.setRenderer((this._terminal as any)._core._createRenderer());
renderService.onResize(this._terminal.cols, this._terminal.rows);
this._renderer?.dispose();
this._renderer = undefined;
}

public get textureAtlas(): HTMLCanvasElement | undefined {
return this._renderer?.textureAtlas;
this.register(toDisposable(() => {
renderService.setRenderer((this._terminal as any)._core._createRenderer());
renderService.onResize(terminal.cols, terminal.rows);
this._renderer?.dispose();
this._renderer = undefined;
}));
}
}
19 changes: 9 additions & 10 deletions addons/xterm-addon-canvas/src/CanvasRenderer.ts
Expand Up @@ -9,7 +9,7 @@ import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/rende
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService } from 'browser/services/Services';
import { IColorSet, ILinkifier2 } from 'browser/Types';
import { EventEmitter } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
import { Disposable, toDisposable } from 'common/Lifecycle';
import { IBufferService, ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
import { Terminal } from 'xterm';
import { CursorRenderLayer } from './CursorRenderLayer';
Expand All @@ -24,9 +24,9 @@ export class CanvasRenderer extends Disposable implements IRenderer {

public dimensions: IRenderDimensions;

private readonly _onRequestRedraw = new EventEmitter<IRequestRedrawEvent>();
private readonly _onRequestRedraw = this.register(new EventEmitter<IRequestRedrawEvent>());
public readonly onRequestRedraw = this._onRequestRedraw.event;
private readonly _onChangeTextureAtlas = new EventEmitter<HTMLCanvasElement>();
private readonly _onChangeTextureAtlas = this.register(new EventEmitter<HTMLCanvasElement>());
public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event;

constructor(
Expand Down Expand Up @@ -70,14 +70,13 @@ export class CanvasRenderer extends Disposable implements IRenderer {
this.register(observeDevicePixelDimensions(this._renderLayers[0].canvas, this._coreBrowserService.window, (w, h) => this._setCanvasDevicePixelDimensions(w, h)));

this.onOptionsChanged();
}

public dispose(): void {
for (const l of this._renderLayers) {
l.dispose();
}
super.dispose();
removeTerminalFromCache(this._terminal);
this.register(toDisposable(() => {
for (const l of this._renderLayers) {
l.dispose();
}
removeTerminalFromCache(this._terminal);
}));
}

public get textureAtlas(): HTMLCanvasElement | undefined {
Expand Down
11 changes: 4 additions & 7 deletions addons/xterm-addon-canvas/src/CursorRenderLayer.ts
Expand Up @@ -12,6 +12,7 @@ import { IBufferService, IOptionsService, ICoreService, IDecorationService } fro
import { IEventEmitter } from 'common/EventEmitter';
import { ICoreBrowserService } from 'browser/services/Services';
import { Terminal } from 'xterm';
import { toDisposable } from 'common/Lifecycle';

interface ICursorState {
x: number;
Expand Down Expand Up @@ -57,14 +58,10 @@ export class CursorRenderLayer extends BaseRenderLayer {
'block': this._renderBlockCursor.bind(this),
'underline': this._renderUnderlineCursor.bind(this)
};
}

public dispose(): void {
if (this._cursorBlinkStateManager) {
this._cursorBlinkStateManager.dispose();
this.register(toDisposable(() => {
this._cursorBlinkStateManager?.dispose();
this._cursorBlinkStateManager = undefined;
}
super.dispose();
}));
}

public resize(dim: IRenderDimensions): void {
Expand Down
4 changes: 2 additions & 2 deletions addons/xterm-addon-canvas/src/LinkRenderLayer.ts
Expand Up @@ -28,8 +28,8 @@ export class LinkRenderLayer extends BaseRenderLayer {
) {
super(terminal, container, 'link', zIndex, true, colors, bufferService, optionsService, decorationService, coreBrowserService);

linkifier2.onShowLinkUnderline(e => this._onShowLinkUnderline(e));
linkifier2.onHideLinkUnderline(e => this._onHideLinkUnderline(e));
this.register(linkifier2.onShowLinkUnderline(e => this._onShowLinkUnderline(e)));
this.register(linkifier2.onHideLinkUnderline(e => this._onHideLinkUnderline(e)));
}

public resize(dim: IRenderDimensions): void {
Expand Down
15 changes: 0 additions & 15 deletions addons/xterm-addon-canvas/src/TextRenderLayer.ts
Expand Up @@ -293,19 +293,4 @@ export class TextRenderLayer extends BaseRenderLayer {
this._characterOverlapCache[chars] = overlaps;
return overlaps;
}

/**
* Clear the charcater at the cell specified.
* @param x The column of the char.
* @param y The row of the char.
*/
// private _clearChar(x: number, y: number): void {
// let colsToClear = 1;
// // Clear the adjacent character if it was wide
// const state = this._state.cache[x][y];
// if (state && state[CHAR_DATA_WIDTH_INDEX] === 2) {
// colsToClear = 2;
// }
// this.clearCells(x, y, colsToClear, 1);
// }
}
1 change: 0 additions & 1 deletion addons/xterm-addon-canvas/src/Types.d.ts
Expand Up @@ -41,7 +41,6 @@ export interface IRenderer extends IDisposable {
*/
readonly onRequestRedraw: IEvent<IRequestRedrawEvent>;

dispose(): void;
setColors(colors: IColorSet): void;
onDevicePixelRatioChange(): void;
onResize(cols: number, rows: number): void;
Expand Down
20 changes: 10 additions & 10 deletions addons/xterm-addon-search/src/SearchAddon.ts
Expand Up @@ -5,6 +5,7 @@

import { Terminal, IDisposable, ITerminalAddon, IBufferRange, IDecoration } from 'xterm';
import { EventEmitter } from 'common/EventEmitter';
import { Disposable, toDisposable } from 'common/Lifecycle';

export interface ISearchOptions {
regex?: boolean;
Expand Down Expand Up @@ -50,7 +51,7 @@ type LineCacheEntry = [
const NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\;:"\',./<>?';
const LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs

export class SearchAddon implements ITerminalAddon {
export class SearchAddon extends Disposable implements ITerminalAddon {
private _terminal: Terminal | undefined;
private _cachedSearchTerm: string | undefined;
private _selectedDecoration: IDecoration | undefined;
Expand All @@ -72,13 +73,18 @@ export class SearchAddon implements ITerminalAddon {

private _resultIndex: number | undefined;

private readonly _onDidChangeResults = new EventEmitter<{ resultIndex: number, resultCount: number } | undefined>();
private readonly _onDidChangeResults = this.register(new EventEmitter<{ resultIndex: number, resultCount: number } | undefined>());
public readonly onDidChangeResults = this._onDidChangeResults.event;

public activate(terminal: Terminal): void {
this._terminal = terminal;
this._onDataDisposable = this._terminal.onWriteParsed(() => this._updateMatches());
this._onResizeDisposable = this._terminal.onResize(() => this._updateMatches());
this._onDataDisposable = this.register(this._terminal.onWriteParsed(() => this._updateMatches()));
this._onResizeDisposable = this.register(this._terminal.onResize(() => this._updateMatches()));
this.register(toDisposable(() => {
this.clearDecorations();
this._onDataDisposable?.dispose();
this._onResizeDisposable?.dispose();
}));
}

private _updateMatches(): void {
Expand All @@ -94,12 +100,6 @@ export class SearchAddon implements ITerminalAddon {
}
}

public dispose(): void {
this.clearDecorations();
this._onDataDisposable?.dispose();
this._onResizeDisposable?.dispose();
}

public clearDecorations(retainCachedSearchTerm?: boolean): void {
this._selectedDecoration?.dispose();
this._searchResults?.clear();
Expand Down
3 changes: 1 addition & 2 deletions addons/xterm-addon-webgl/src/GlyphRenderer.ts
Expand Up @@ -76,7 +76,7 @@ let $glyph: IRasterizedGlyph | undefined = undefined;
let $leftCellPadding = 0;
let $clippedPixels = 0;

export class GlyphRenderer extends Disposable {
export class GlyphRenderer extends Disposable {
private _atlas: ITextureAtlas | undefined;

private _program: WebGLProgram;
Expand All @@ -99,7 +99,6 @@ export class GlyphRenderer extends Disposable {

constructor(
private _terminal: Terminal,
private _colors: IColorSet,
private _gl: IWebGL2RenderingContext,
private _dimensions: IRenderDimensions
) {
Expand Down
40 changes: 19 additions & 21 deletions addons/xterm-addon-webgl/src/WebglAddon.ts
Expand Up @@ -10,51 +10,49 @@ import { IColorSet } from 'browser/Types';
import { EventEmitter, forwardEvent } from 'common/EventEmitter';
import { isSafari } from 'common/Platform';
import { ICoreService, IDecorationService } from 'common/services/Services';
import { Disposable, toDisposable } from 'common/Lifecycle';

export class WebglAddon implements ITerminalAddon {
export class WebglAddon extends Disposable implements ITerminalAddon {
private _terminal?: Terminal;
private _renderer?: WebglRenderer;

private readonly _onChangeTextureAtlas = new EventEmitter<HTMLElement>();
private readonly _onChangeTextureAtlas = this.register(new EventEmitter<HTMLElement>());
public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event;
private readonly _onContextLoss = new EventEmitter<void>();
private readonly _onContextLoss = this.register(new EventEmitter<void>());
public readonly onContextLoss = this._onContextLoss.event;

constructor(
private _preserveDrawingBuffer?: boolean
) {}
) {
super();
}

public activate(terminal: Terminal): void {
if (isSafari) {
throw new Error('Webgl is not currently supported on Safari');
}
const core = (terminal as any)._core;
if (!terminal.element) {
core.onWillOpen(() => this.activate(terminal));
this.register(core.onWillOpen(() => this.activate(terminal)));
return;
}
if (isSafari) {
throw new Error('Webgl is not currently supported on Safari');
}
this._terminal = terminal;
const renderService: IRenderService = core._renderService;
const characterJoinerService: ICharacterJoinerService = core._characterJoinerService;
const coreBrowserService: ICoreBrowserService = core._coreBrowserService;
const coreService: ICoreService = core.coreService;
const decorationService: IDecorationService = core._decorationService;
const colors: IColorSet = core._colorManager.colors;
this._renderer = new WebglRenderer(terminal, colors, characterJoinerService, coreBrowserService, coreService, decorationService, this._preserveDrawingBuffer);
forwardEvent(this._renderer.onContextLoss, this._onContextLoss);
forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas);
this._renderer = this.register(new WebglRenderer(terminal, colors, characterJoinerService, coreBrowserService, coreService, decorationService, this._preserveDrawingBuffer));
this.register(forwardEvent(this._renderer.onContextLoss, this._onContextLoss));
this.register(forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas));
renderService.setRenderer(this._renderer);
}

public dispose(): void {
if (!this._terminal) {
throw new Error('Cannot dispose WebglAddon because it is activated');
}
const renderService: IRenderService = (this._terminal as any)._core._renderService;
renderService.setRenderer((this._terminal as any)._core._createRenderer());
renderService.onResize(this._terminal.cols, this._terminal.rows);
this._renderer?.dispose();
this._renderer = undefined;
this.register(toDisposable(() => {
const renderService: IRenderService = (this._terminal as any)._core._renderService;
renderService.setRenderer((this._terminal as any)._core._createRenderer());
renderService.onResize(terminal.cols, terminal.rows);
}));
}

public get textureAtlas(): HTMLCanvasElement | undefined {
Expand Down

0 comments on commit d8c31d7

Please sign in to comment.