Merge pull request #4733 from Tyriar/mutable

Adopt MutableDisposable for most usages of pattern `<var>?.dispose(); <var> = undefined`
This commit is contained in:
Daniel Imms
2023-08-27 07:29:02 -07:00
committed by GitHub
10 changed files with 114 additions and 144 deletions
@@ -3,24 +3,24 @@
* @license MIT
*/
import { ReadonlyColorSet } from 'browser/Types';
import { CellColorResolver } from 'browser/renderer/shared/CellColorResolver';
import { acquireTextureAtlas } from 'browser/renderer/shared/CharAtlasCache';
import { TEXT_BASELINE } from 'browser/renderer/shared/Constants';
import { tryDrawCustomChar } from 'browser/renderer/shared/CustomGlyphs';
import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
import { IRasterizedGlyph, IRenderDimensions, ISelectionRenderModel, ITextureAtlas } from 'browser/renderer/shared/Types';
import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel';
import { IRasterizedGlyph, IRenderDimensions, ISelectionRenderModel, ITextureAtlas } from 'browser/renderer/shared/Types';
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { ReadonlyColorSet } from 'browser/Types';
import { EventEmitter, forwardEvent } from 'common/EventEmitter';
import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle';
import { isSafari } from 'common/Platform';
import { ICellData } from 'common/Types';
import { CellData } from 'common/buffer/CellData';
import { WHITESPACE_CELL_CODE } from 'common/buffer/Constants';
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
import { ICellData, IDisposable } from 'common/Types';
import { Terminal } from 'xterm';
import { IRenderLayer } from './Types';
import { CellColorResolver } from 'browser/renderer/shared/CellColorResolver';
import { Disposable, toDisposable } from 'common/Lifecycle';
import { isSafari } from 'common/Platform';
import { EventEmitter, forwardEvent } from 'common/EventEmitter';
export abstract class BaseRenderLayer extends Disposable implements IRenderLayer {
private _canvas: HTMLCanvasElement;
@@ -37,7 +37,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
private _bitmapGenerator: (BitmapGenerator | undefined)[] = [];
protected _charAtlas!: ITextureAtlas;
private _charAtlasDisposable?: IDisposable;
protected _charAtlasDisposable = this.register(new MutableDisposable());
public get canvas(): HTMLCanvasElement { return this._canvas; }
public get cacheCanvas(): HTMLCanvasElement { return this._charAtlas?.pages[0].canvas!; }
@@ -74,7 +74,6 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
this.register(toDisposable(() => {
this._canvas.remove();
this._charAtlas?.dispose();
}));
}
@@ -122,9 +121,8 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
if (this._deviceCharWidth <= 0 && this._deviceCharHeight <= 0) {
return;
}
this._charAtlasDisposable?.dispose();
this._charAtlas = acquireTextureAtlas(this._terminal, this._optionsService.rawOptions, colorSet, this._deviceCellWidth, this._deviceCellHeight, this._deviceCharWidth, this._deviceCharHeight, this._coreBrowserService.dpr);
this._charAtlasDisposable = forwardEvent(this._charAtlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas);
this._charAtlasDisposable.value = forwardEvent(this._charAtlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas);
this._charAtlas.warmUp();
for (let i = 0; i < this._charAtlas.pages.length; i++) {
this._bitmapGenerator[i] = new BitmapGenerator(this._charAtlas.pages[i].canvas);
@@ -7,7 +7,7 @@ import { CursorBlinkStateManager } from 'browser/renderer/shared/CursorBlinkStat
import { IRenderDimensions, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { IEventEmitter } from 'common/EventEmitter';
import { toDisposable } from 'common/Lifecycle';
import { MutableDisposable } from 'common/Lifecycle';
import { isFirefox } from 'common/Platform';
import { ICellData } from 'common/Types';
import { CellData } from 'common/buffer/CellData';
@@ -26,7 +26,7 @@ interface ICursorState {
export class CursorRenderLayer extends BaseRenderLayer {
private _state: ICursorState;
private _cursorRenderers: {[key: string]: (x: number, y: number, cell: ICellData) => void};
private _cursorBlinkStateManager: CursorBlinkStateManager | undefined;
private _cursorBlinkStateManager: MutableDisposable<CursorBlinkStateManager> = this.register(new MutableDisposable());
private _cell: ICellData = new CellData();
constructor(
@@ -57,10 +57,6 @@ export class CursorRenderLayer extends BaseRenderLayer {
};
this.register(optionsService.onOptionChange(() => this._handleOptionsChanged()));
this._handleOptionsChanged();
this.register(toDisposable(() => {
this._cursorBlinkStateManager?.dispose();
this._cursorBlinkStateManager = undefined;
}));
}
public resize(dim: IRenderDimensions): void {
@@ -77,28 +73,27 @@ export class CursorRenderLayer extends BaseRenderLayer {
public reset(): void {
this._clearCursor();
this._cursorBlinkStateManager?.restartBlinkAnimation();
this._cursorBlinkStateManager.value?.restartBlinkAnimation();
this._handleOptionsChanged();
}
public handleBlur(): void {
this._cursorBlinkStateManager?.pause();
this._cursorBlinkStateManager.value?.pause();
this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y });
}
public handleFocus(): void {
this._cursorBlinkStateManager?.resume();
this._cursorBlinkStateManager.value?.resume();
this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y });
}
private _handleOptionsChanged(): void {
if (this._optionsService.rawOptions.cursorBlink) {
if (!this._cursorBlinkStateManager) {
this._cursorBlinkStateManager = new CursorBlinkStateManager(() => this._render(true), this._coreBrowserService);
if (!this._cursorBlinkStateManager.value) {
this._cursorBlinkStateManager.value = new CursorBlinkStateManager(() => this._render(true), this._coreBrowserService);
}
} else {
this._cursorBlinkStateManager?.dispose();
this._cursorBlinkStateManager = undefined;
this._cursorBlinkStateManager.clear();
}
// Request a refresh from the terminal as management of rendering is being
// moved back to the terminal
@@ -106,14 +101,14 @@ export class CursorRenderLayer extends BaseRenderLayer {
}
public handleCursorMove(): void {
this._cursorBlinkStateManager?.restartBlinkAnimation();
this._cursorBlinkStateManager.value?.restartBlinkAnimation();
}
public handleGridChanged(startRow: number, endRow: number): void {
if (!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isPaused) {
if (!this._cursorBlinkStateManager.value || this._cursorBlinkStateManager.value.isPaused) {
this._render(false);
} else {
this._cursorBlinkStateManager.restartBlinkAnimation();
this._cursorBlinkStateManager.value.restartBlinkAnimation();
}
}
@@ -159,7 +154,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
}
// Don't draw the cursor if it's blinking
if (this._cursorBlinkStateManager && !this._cursorBlinkStateManager.isCursorVisible) {
if (this._cursorBlinkStateManager.value && !this._cursorBlinkStateManager.value.isCursorVisible) {
this._clearCursor();
return;
}
+22 -23
View File
@@ -6,6 +6,7 @@
import { toRGBA8888 } from 'sixel/lib/Colors';
import { IDisposable } from 'xterm';
import { ICellSize, ITerminalExt, IImageSpec, IRenderDimensions, IRenderService } from './Types';
import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle';
const PLACEHOLDER_LENGTH = 4096;
@@ -17,12 +18,12 @@ const PLACEHOLDER_HEIGHT = 24;
* - add canvas layer to DOM (browser only for now)
* - draw image tiles onRender
*/
export class ImageRenderer implements IDisposable {
export class ImageRenderer extends Disposable implements IDisposable {
public canvas: HTMLCanvasElement | undefined;
private _ctx: CanvasRenderingContext2D | null | undefined;
private _placeholder: HTMLCanvasElement | undefined;
private _placeholderBitmap: ImageBitmap | undefined;
private _optionsRefresh: IDisposable | undefined;
private _optionsRefresh = this.register(new MutableDisposable());
private _oldOpen: ((parent: HTMLElement) => void) | undefined;
private _renderService: IRenderService | undefined;
private _oldSetRenderer: ((renderer: any) => void) | undefined;
@@ -68,6 +69,7 @@ export class ImageRenderer implements IDisposable {
constructor(private _terminal: ITerminalExt) {
super();
this._oldOpen = this._terminal._core.open;
this._terminal._core.open = (parent: HTMLElement): void => {
this._oldOpen?.call(this._terminal._core, parent);
@@ -77,32 +79,29 @@ export class ImageRenderer implements IDisposable {
this._open();
}
// hack to spot fontSize changes
this._optionsRefresh = this._terminal._core.optionsService.onOptionChange(option => {
this._optionsRefresh.value = this._terminal._core.optionsService.onOptionChange(option => {
if (option === 'fontSize') {
this.rescaleCanvas();
this._renderService?.refreshRows(0, this._terminal.rows);
}
});
}
public dispose(): void {
this._optionsRefresh?.dispose();
this.removeLayerFromDom();
if (this._terminal._core && this._oldOpen) {
this._terminal._core.open = this._oldOpen;
this._oldOpen = undefined;
}
if (this._renderService && this._oldSetRenderer) {
this._renderService.setRenderer = this._oldSetRenderer;
this._oldSetRenderer = undefined;
}
this._renderService = undefined;
this.canvas = undefined;
this._ctx = undefined;
this._placeholderBitmap?.close();
this._placeholderBitmap = undefined;
this._placeholder = undefined;
this.register(toDisposable(() => {
this.removeLayerFromDom();
if (this._terminal._core && this._oldOpen) {
this._terminal._core.open = this._oldOpen;
this._oldOpen = undefined;
}
if (this._renderService && this._oldSetRenderer) {
this._renderService.setRenderer = this._oldSetRenderer;
this._oldSetRenderer = undefined;
}
this._renderService = undefined;
this.canvas = undefined;
this._ctx = undefined;
this._placeholderBitmap?.close();
this._placeholderBitmap = undefined;
this._placeholder = undefined;
}));
}
/**
+7 -12
View File
@@ -5,7 +5,7 @@
import { Terminal, IDisposable, ITerminalAddon, IDecoration } from 'xterm';
import { EventEmitter } from 'common/EventEmitter';
import { Disposable, toDisposable, disposeArray } from 'common/Lifecycle';
import { Disposable, toDisposable, disposeArray, MutableDisposable } from 'common/Lifecycle';
export interface ISearchOptions {
regex?: boolean;
@@ -66,7 +66,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
private _cachedSearchTerm: string | undefined;
private _highlightedLines: Set<number> = new Set();
private _highlightDecorations: IHighlight[] = [];
private _selectedDecoration: IHighlight | undefined;
private _selectedDecoration: MutableDisposable<IHighlight> = this.register(new MutableDisposable());
private _highlightLimit: number;
private _lastSearchOptions: ISearchOptions | undefined;
private _highlightTimeout: number | undefined;
@@ -110,7 +110,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
}
public clearDecorations(retainCachedSearchTerm?: boolean): void {
this.clearActiveDecoration();
this._selectedDecoration.clear();
disposeArray(this._highlightDecorations);
this._highlightDecorations = [];
this._highlightedLines.clear();
@@ -119,11 +119,6 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
}
}
public clearActiveDecoration(): void {
this._selectedDecoration?.dispose();
this._selectedDecoration = undefined;
}
/**
* Find the next instance of the term, then scroll to and select it. If it
* doesn't exist, do nothing.
@@ -320,8 +315,8 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
private _fireResults(searchOptions?: ISearchOptions): void {
if (searchOptions?.decorations) {
let resultIndex = -1;
if (this._selectedDecoration) {
const selectedMatch = this._selectedDecoration.match;
if (this._selectedDecoration.value) {
const selectedMatch = this._selectedDecoration.value.match;
for (let i = 0; i < this._highlightDecorations.length; i++) {
const match = this._highlightDecorations[i].match;
if (match.row === selectedMatch.row && match.col === selectedMatch.col && match.size === selectedMatch.size) {
@@ -642,7 +637,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
*/
private _selectResult(result: ISearchResult | undefined, options?: ISearchDecorationOptions, noScroll?: boolean): boolean {
const terminal = this._terminal!;
this.clearActiveDecoration();
this._selectedDecoration.clear();
if (!result) {
terminal.clearSelection();
return false;
@@ -666,7 +661,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
disposables.push(marker);
disposables.push(decoration.onRender((e) => this._applyStyles(e, options.activeMatchBorder, true)));
disposables.push(decoration.onDispose(() => disposeArray(disposables)));
this._selectedDecoration = { decoration, match: result, dispose() { decoration.dispose(); } };
this._selectedDecoration.value = { decoration, match: result, dispose() { decoration.dispose(); } };
}
}
}
+24 -29
View File
@@ -20,7 +20,7 @@ import { CellData } from 'common/buffer/CellData';
import { Attributes, Content, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
import { traceCall } from 'common/services/LogService';
import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
import { IDisposable, Terminal } from 'xterm';
import { Terminal } from 'xterm';
import { GlyphRenderer } from './GlyphRenderer';
import { RectangleRenderer } from './RectangleRenderer';
import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_EXT_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL, RenderModel } from './RenderModel';
@@ -31,7 +31,7 @@ import { IRenderLayer } from './renderLayer/Types';
export class WebglRenderer extends Disposable implements IRenderer {
private _renderLayers: IRenderLayer[];
private _cursorBlinkStateManager: MutableDisposable<CursorBlinkStateManager> = new MutableDisposable();
private _charAtlasDisposable: IDisposable | undefined;
private _charAtlasDisposable = this.register(new MutableDisposable());
private _charAtlas: ITextureAtlas | undefined;
private _devicePixelRatio: number;
@@ -41,8 +41,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
private _canvas: HTMLCanvasElement;
private _gl: IWebGL2RenderingContext;
private _rectangleRenderer?: RectangleRenderer;
private _glyphRenderer?: GlyphRenderer;
private _rectangleRenderer: MutableDisposable<RectangleRenderer> = this.register(new MutableDisposable());
private _glyphRenderer: MutableDisposable<GlyphRenderer> = this.register(new MutableDisposable());
public readonly dimensions: IRenderDimensions;
@@ -128,7 +128,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
this._core.screenElement!.appendChild(this._canvas);
[this._rectangleRenderer, this._glyphRenderer] = this._initializeWebGLState();
[this._rectangleRenderer.value, this._glyphRenderer.value] = this._initializeWebGLState();
this._isAttached = this._coreBrowserService.window.document.body.contains(this._core.screenElement!);
@@ -182,10 +182,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
this._core.screenElement!.style.width = `${this.dimensions.css.canvas.width}px`;
this._core.screenElement!.style.height = `${this.dimensions.css.canvas.height}px`;
this._rectangleRenderer?.setDimensions(this.dimensions);
this._rectangleRenderer?.handleResize();
this._glyphRenderer?.setDimensions(this.dimensions);
this._glyphRenderer?.handleResize();
this._rectangleRenderer.value?.setDimensions(this.dimensions);
this._rectangleRenderer.value?.handleResize();
this._glyphRenderer.value?.setDimensions(this.dimensions);
this._glyphRenderer.value?.handleResize();
this._refreshCharAtlas();
@@ -241,17 +241,13 @@ export class WebglRenderer extends Disposable implements IRenderer {
* Initializes members dependent on WebGL context state.
*/
private _initializeWebGLState(): [RectangleRenderer, GlyphRenderer] {
// Dispose any previous rectangle and glyph renderers before creating new ones.
this._rectangleRenderer?.dispose();
this._glyphRenderer?.dispose();
this._rectangleRenderer = this.register(new RectangleRenderer(this._terminal, this._gl, this.dimensions, this._themeService));
this._glyphRenderer = this.register(new GlyphRenderer(this._terminal, this._gl, this.dimensions));
this._rectangleRenderer.value = new RectangleRenderer(this._terminal, this._gl, this.dimensions, this._themeService);
this._glyphRenderer.value = new GlyphRenderer(this._terminal, this._gl, this.dimensions);
// Update dimensions and acquire char atlas
this.handleCharSizeChanged();
return [this._rectangleRenderer, this._glyphRenderer];
return [this._rectangleRenderer.value, this._glyphRenderer.value];
}
/**
@@ -275,16 +271,15 @@ export class WebglRenderer extends Disposable implements IRenderer {
this._coreBrowserService.dpr
);
if (this._charAtlas !== atlas) {
this._charAtlasDisposable?.dispose();
this._onChangeTextureAtlas.fire(atlas.pages[0].canvas);
this._charAtlasDisposable = getDisposeArrayDisposable([
this._charAtlasDisposable.value = getDisposeArrayDisposable([
forwardEvent(atlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas),
forwardEvent(atlas.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas)
]);
}
this._charAtlas = atlas;
this._charAtlas.warmUp();
this._glyphRenderer?.setAtlas(this._charAtlas);
this._glyphRenderer.value?.setAtlas(this._charAtlas);
}
/**
@@ -295,7 +290,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
private _clearModel(clearGlyphRenderer: boolean): void {
this._model.clear();
if (clearGlyphRenderer) {
this._glyphRenderer?.clear();
this._glyphRenderer.value?.clear();
}
}
@@ -340,14 +335,14 @@ export class WebglRenderer extends Disposable implements IRenderer {
l.handleGridChanged(this._terminal, start, end);
}
if (!this._glyphRenderer || !this._rectangleRenderer) {
if (!this._glyphRenderer.value || !this._rectangleRenderer.value) {
return;
}
// Tell renderer the frame is beginning
// upon a model clear also refresh the full viewport model
// (also triggered by an atlas page merge, part of #4480)
if (this._glyphRenderer.beginFrame()) {
if (this._glyphRenderer.value.beginFrame()) {
this._clearModel(true);
this._updateModel(0, this._terminal.rows - 1);
} else {
@@ -356,10 +351,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
}
// Render
this._rectangleRenderer?.renderBackgrounds();
this._glyphRenderer?.render(this._model);
this._rectangleRenderer.value.renderBackgrounds();
this._glyphRenderer.value.render(this._model);
if (!this._cursorBlinkStateManager.value || this._cursorBlinkStateManager.value.isCursorVisible) {
this._rectangleRenderer?.renderCursor();
this._rectangleRenderer.value.renderCursor();
}
}
@@ -502,7 +497,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
this._model.cells[i + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg;
this._model.cells[i + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext;
this._glyphRenderer!.updateCell(x, y, code, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, chars, lastBg);
this._glyphRenderer.value!.updateCell(x, y, code, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, chars, lastBg);
if (isJoined) {
// Restore work cell
@@ -511,7 +506,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
// Null out non-first cells
for (x++; x < lastCharX; x++) {
j = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
this._glyphRenderer!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0);
this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0);
this._model.cells[j] = NULL_CELL_CODE;
this._model.cells[j + RENDER_MODEL_BG_OFFSET] = this._cellColorResolver.result.bg;
this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg;
@@ -521,9 +516,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
}
}
if (modelUpdated) {
this._rectangleRenderer!.updateBackgrounds(this._model);
this._rectangleRenderer.value!.updateBackgrounds(this._model);
}
this._rectangleRenderer!.updateCursor(this._model);
this._rectangleRenderer.value!.updateCursor(this._model);
}
/**
@@ -49,7 +49,6 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
}));
this.register(toDisposable(() => {
this._canvas.remove();
this._charAtlas?.dispose();
}));
}
@@ -1106,7 +1106,7 @@ describe('WebGL Renderer Integration Tests', async () => {
async function getCellColor(col: number, row: number): Promise<number[]> {
await page.evaluate(`
window.gl = window.term._core._renderService._renderer._gl;
window.gl = window.term._core._renderService._renderer.value._gl;
window.result = new Uint8Array(4);
window.d = window.term._core._renderService.dimensions;
window.gl.readPixels(
@@ -1120,7 +1120,7 @@ async function getCellColor(col: number, row: number): Promise<number[]> {
async function getCellPixels(col: number, row: number): Promise<number[]> {
await page.evaluate(`
window.gl = window.term._core._renderService._renderer._gl;
window.gl = window.term._core._renderService._renderer.value._gl;
window.result = new Uint8Array(window.d.device.cell.width * window.d.device.cell.height * 4);
window.d = window.term._core._renderService.dimensions;
window.gl.readPixels(
+6 -7
View File
@@ -44,7 +44,7 @@ import { ThemeService } from 'browser/services/ThemeService';
import { color, rgba } from 'common/Color';
import { CoreTerminal } from 'common/CoreTerminal';
import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
import { toDisposable } from 'common/Lifecycle';
import { MutableDisposable, toDisposable } from 'common/Lifecycle';
import * as Browser from 'common/Platform';
import { ColorRequestType, CoreMouseAction, CoreMouseButton, CoreMouseEventType, IColorEvent, ITerminalOptions, KeyboardResultType, ScrollSource, SpecialColorIndex } from 'common/Types';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
@@ -118,7 +118,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
public linkifier2: ILinkifier2;
public viewport: IViewport | undefined;
private _compositionHelper: ICompositionHelper | undefined;
private _accessibilityManager: AccessibilityManager | undefined;
private _accessibilityManager: MutableDisposable<AccessibilityManager> = this.register(new MutableDisposable());
private readonly _onCursorMove = this.register(new EventEmitter<void>());
public readonly onCursorMove = this._onCursorMove.event;
@@ -252,12 +252,11 @@ export class Terminal extends CoreTerminal implements ITerminal {
private _handleScreenReaderModeOptionChange(value: boolean): void {
if (value) {
if (!this._accessibilityManager && this._renderService) {
this._accessibilityManager = this._instantiationService.createInstance(AccessibilityManager, this);
if (!this._accessibilityManager.value && this._renderService) {
this._accessibilityManager.value = this._instantiationService.createInstance(AccessibilityManager, this);
}
} else {
this._accessibilityManager?.dispose();
this._accessibilityManager = undefined;
this._accessibilityManager.clear();
}
}
@@ -535,7 +534,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
if (this.options.screenReaderMode) {
// Note that this must be done *after* the renderer is created in order to
// ensure the correct order of the dprchange event
this._accessibilityManager = this._instantiationService.createInstance(AccessibilityManager, this);
this._accessibilityManager.value = this._instantiationService.createInstance(AccessibilityManager, this);
}
this.register(this.optionsService.onSpecificOptionChange('screenReaderMode', e => this._handleScreenReaderModeOptionChange(e)));
+27 -31
View File
@@ -10,7 +10,7 @@ import { IRenderDebouncerWithCallback } from 'browser/Types';
import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types';
import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
import { EventEmitter } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
import { Disposable, MutableDisposable } from 'common/Lifecycle';
import { DebouncedIdleTask } from 'common/TaskQueue';
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
@@ -23,7 +23,7 @@ interface ISelectionState {
export class RenderService extends Disposable implements IRenderService {
public serviceBrand: undefined;
private _renderer: IRenderer | undefined;
private _renderer: MutableDisposable<IRenderer> = this.register(new MutableDisposable());
private _renderDebouncer: IRenderDebouncerWithCallback;
private _screenDprMonitor: ScreenDprMonitor;
private _pausedResizeTask = new DebouncedIdleTask();
@@ -49,7 +49,7 @@ export class RenderService extends Disposable implements IRenderService {
private readonly _onRefreshRequest = this.register(new EventEmitter<{ start: number, end: number }>());
public readonly onRefreshRequest = this._onRefreshRequest.event;
public get dimensions(): IRenderDimensions { return this._renderer!.dimensions; }
public get dimensions(): IRenderDimensions { return this._renderer.value!.dimensions; }
constructor(
private _rowCount: number,
@@ -63,8 +63,6 @@ export class RenderService extends Disposable implements IRenderService {
) {
super();
this.register({ dispose: () => this._renderer?.dispose() });
this._renderDebouncer = new RenderDebouncer(coreBrowserService.window, (start, end) => this._renderRows(start, end));
this.register(this._renderDebouncer);
@@ -73,7 +71,7 @@ export class RenderService extends Disposable implements IRenderService {
this.register(this._screenDprMonitor);
this.register(bufferService.onResize(() => this._fullRefresh()));
this.register(bufferService.buffers.onBufferActivate(() => this._renderer?.clear()));
this.register(bufferService.buffers.onBufferActivate(() => this._renderer.value?.clear()));
this.register(optionsService.onOptionChange(() => this._handleOptionsChanged()));
this.register(this._charSizeService.onCharSizeChange(() => this.handleCharSizeChanged()));
@@ -148,7 +146,7 @@ export class RenderService extends Disposable implements IRenderService {
}
private _renderRows(start: number, end: number): void {
if (!this._renderer) {
if (!this._renderer.value) {
return;
}
@@ -159,11 +157,11 @@ export class RenderService extends Disposable implements IRenderService {
end = Math.min(end, this._rowCount - 1);
// Render
this._renderer.renderRows(start, end);
this._renderer.value.renderRows(start, end);
// Update selection if needed
if (this._needsSelectionRefresh) {
this._renderer.handleSelectionChanged(this._selectionState.start, this._selectionState.end, this._selectionState.columnSelectMode);
this._renderer.value.handleSelectionChanged(this._selectionState.start, this._selectionState.end, this._selectionState.columnSelectMode);
this._needsSelectionRefresh = false;
}
@@ -181,7 +179,7 @@ export class RenderService extends Disposable implements IRenderService {
}
private _handleOptionsChanged(): void {
if (!this._renderer) {
if (!this._renderer.value) {
return;
}
this.refreshRows(0, this._rowCount - 1);
@@ -189,25 +187,23 @@ export class RenderService extends Disposable implements IRenderService {
}
private _fireOnCanvasResize(): void {
if (!this._renderer) {
if (!this._renderer.value) {
return;
}
// Don't fire the event if the dimensions haven't changed
if (this._renderer.dimensions.css.canvas.width === this._canvasWidth && this._renderer.dimensions.css.canvas.height === this._canvasHeight) {
if (this._renderer.value.dimensions.css.canvas.width === this._canvasWidth && this._renderer.value.dimensions.css.canvas.height === this._canvasHeight) {
return;
}
this._onDimensionsChange.fire(this._renderer.dimensions);
this._onDimensionsChange.fire(this._renderer.value.dimensions);
}
public hasRenderer(): boolean {
return !!this._renderer;
return !!this._renderer.value;
}
public setRenderer(renderer: IRenderer): void {
// TODO: RenderService should be the only one to dispose the renderer
this._renderer?.dispose();
this._renderer = renderer;
this._renderer.onRequestRedraw(e => this.refreshRows(e.start, e.end, true));
this._renderer.value = renderer;
this._renderer.value.onRequestRedraw(e => this.refreshRows(e.start, e.end, true));
// Force a refresh
this._needsSelectionRefresh = true;
@@ -227,10 +223,10 @@ export class RenderService extends Disposable implements IRenderService {
}
public clearTextureAtlas(): void {
if (!this._renderer) {
if (!this._renderer.value) {
return;
}
this._renderer.clearTextureAtlas?.();
this._renderer.value.clearTextureAtlas?.();
this._fullRefresh();
}
@@ -239,50 +235,50 @@ export class RenderService extends Disposable implements IRenderService {
// when devicePixelRatio changes
this._charSizeService.measure();
if (!this._renderer) {
if (!this._renderer.value) {
return;
}
this._renderer.handleDevicePixelRatioChange();
this._renderer.value.handleDevicePixelRatioChange();
this.refreshRows(0, this._rowCount - 1);
}
public handleResize(cols: number, rows: number): void {
if (!this._renderer) {
if (!this._renderer.value) {
return;
}
if (this._isPaused) {
this._pausedResizeTask.set(() => this._renderer!.handleResize(cols, rows));
this._pausedResizeTask.set(() => this._renderer.value!.handleResize(cols, rows));
} else {
this._renderer.handleResize(cols, rows);
this._renderer.value.handleResize(cols, rows);
}
this._fullRefresh();
}
// TODO: Is this useful when we have onResize?
public handleCharSizeChanged(): void {
this._renderer?.handleCharSizeChanged();
this._renderer.value?.handleCharSizeChanged();
}
public handleBlur(): void {
this._renderer?.handleBlur();
this._renderer.value?.handleBlur();
}
public handleFocus(): void {
this._renderer?.handleFocus();
this._renderer.value?.handleFocus();
}
public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
this._selectionState.start = start;
this._selectionState.end = end;
this._selectionState.columnSelectMode = columnSelectMode;
this._renderer?.handleSelectionChanged(start, end, columnSelectMode);
this._renderer.value?.handleSelectionChanged(start, end, columnSelectMode);
}
public handleCursorMove(): void {
this._renderer?.handleCursorMove();
this._renderer.value?.handleCursorMove();
}
public clear(): void {
this._renderer?.clear();
this._renderer.value?.clear();
}
}
+5 -11
View File
@@ -21,7 +21,7 @@
* http://linux.die.net/man/7/urxvt
*/
import { Disposable, toDisposable } from 'common/Lifecycle';
import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle';
import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, ICoreMouseService, IUnicodeService, LogLevelEnum, ITerminalOptions, IOscLinkService } from 'common/services/Services';
import { InstantiationService } from 'common/services/InstantiationService';
import { LogService } from 'common/services/LogService';
@@ -57,7 +57,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
protected _inputHandler: InputHandler;
private _writeBuffer: WriteBuffer;
private _windowsWrappingHeuristics: IDisposable | undefined;
private _windowsWrappingHeuristics = this.register(new MutableDisposable());
private readonly _onBinary = this.register(new EventEmitter<string>());
public readonly onBinary = this._onBinary.event;
@@ -144,11 +144,6 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
// Setup WriteBuffer
this._writeBuffer = this.register(new WriteBuffer((data, promiseResult) => this._inputHandler.parse(data, promiseResult)));
this.register(forwardEvent(this._writeBuffer.onWriteParsed, this._onWriteParsed));
this.register(toDisposable(() => {
this._windowsWrappingHeuristics?.dispose();
this._windowsWrappingHeuristics = undefined;
}));
}
public write(data: string | Uint8Array, callback?: () => void): void {
@@ -267,20 +262,19 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
if (value) {
this._enableWindowsWrappingHeuristics();
} else {
this._windowsWrappingHeuristics?.dispose();
this._windowsWrappingHeuristics = undefined;
this._windowsWrappingHeuristics.clear();
}
}
protected _enableWindowsWrappingHeuristics(): void {
if (!this._windowsWrappingHeuristics) {
if (!this._windowsWrappingHeuristics.value) {
const disposables: IDisposable[] = [];
disposables.push(this.onLineFeed(updateWindowsModeWrappedState.bind(null, this._bufferService)));
disposables.push(this.registerCsiHandler({ final: 'H' }, () => {
updateWindowsModeWrappedState(this._bufferService);
return false;
}));
this._windowsWrappingHeuristics = toDisposable(() => {
this._windowsWrappingHeuristics.value = toDisposable(() => {
for (const d of disposables) {
d.dispose();
}