diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 3f8163ba0..1f28d5893 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -15,13 +15,14 @@ import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { RenderModel, COMBINED_CHAR_BIT_MASK } from './RenderModel'; import { Disposable } from 'common/Lifecycle'; import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX, NULL_CELL_CODE } from 'common/buffer/Constants'; -import { Terminal } from 'xterm'; +import { Terminal, IEvent } from 'xterm'; import { getLuminance } from './ColorUtils'; import { IRenderLayer } from './renderLayer/Types'; -import { IRenderDimensions, IRenderer } from 'browser/renderer/Types'; +import { IRenderDimensions, IRenderer, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; import { IColorSet } from 'browser/Types'; import { FLAGS } from './Constants'; import { getCompatAttr } from './CharDataCompat'; +import { EventEmitter } from 'common/EventEmitter'; export const INDICIES_PER_CELL = 4; @@ -41,6 +42,9 @@ export class WebglRenderer extends Disposable implements IRenderer { private _core: ITerminal; + private _onRequestRefreshRows = new EventEmitter(); + public get onRequestRefreshRows(): IEvent { return this._onRequestRefreshRows.event; } + constructor( private _terminal: Terminal, private _colors: IColorSet, @@ -54,7 +58,7 @@ export class WebglRenderer extends Disposable implements IRenderer { this._renderLayers = [ new LinkRenderLayer(this._core.screenElement, 2, this._colors, this._core), - new CursorRenderLayer(this._core.screenElement, 3, this._colors) + new CursorRenderLayer(this._core.screenElement, 3, this._colors, this._onRequestRefreshRows) ]; this.dimensions = { scaledCharWidth: 0, @@ -186,7 +190,7 @@ export class WebglRenderer extends Disposable implements IRenderer { this._glyphRenderer.updateSelection(this._model, columnSelectMode); // TODO: #2102 Should this move to RenderCoordinator? - this._core.refresh(0, this._terminal.rows - 1); + this._onRequestRefreshRows.fire({ start: 0, end: this._terminal.rows - 1 }); } public onCursorMove(): void { diff --git a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts index 8a67526e7..c7c68ff6f 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts @@ -8,7 +8,8 @@ import { BaseRenderLayer } from './BaseRenderLayer'; import { ICellData } from 'common/Types'; import { CellData } from 'common/buffer/CellData'; import { IColorSet } from 'browser/Types'; -import { IRenderDimensions } from 'browser/renderer/Types'; +import { IRenderDimensions, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; +import { IEventEmitter } from 'common/EventEmitter'; interface ICursorState { x: number; @@ -29,7 +30,12 @@ export class CursorRenderLayer extends BaseRenderLayer { private _cursorBlinkStateManager: CursorBlinkStateManager | undefined; private _cell: ICellData = new CellData(); - constructor(container: HTMLElement, zIndex: number, colors: IColorSet) { + constructor( + container: HTMLElement, + zIndex: number, + colors: IColorSet, + private _onRequestRefreshRowsEvent: IEventEmitter + ) { super(container, 'cursor', zIndex, true, colors); this._state = { x: 0, @@ -70,14 +76,14 @@ export class CursorRenderLayer extends BaseRenderLayer { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.pause(); } - terminal.refresh(terminal.buffer.cursorY, terminal.buffer.cursorY); + this._onRequestRefreshRowsEvent.fire({ start: terminal.buffer.cursorY, end: terminal.buffer.cursorY }); } public onFocus(terminal: Terminal): void { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.resume(terminal); } else { - terminal.refresh(terminal.buffer.cursorY, terminal.buffer.cursorY); + this._onRequestRefreshRowsEvent.fire({ start: terminal.buffer.cursorY, end: terminal.buffer.cursorY }); } } @@ -95,7 +101,7 @@ 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.cursorY, terminal.buffer.cursorY); + this._onRequestRefreshRowsEvent.fire({ start: terminal.buffer.cursorY, end: terminal.buffer.cursorY }); } public onCursorMove(terminal: Terminal): void { diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index a70f751c5..e7c483811 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IRenderer, IRenderDimensions, CharacterJoinerHandler } from 'browser/renderer/Types'; +import { IRenderer, IRenderDimensions, CharacterJoinerHandler, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; import { IInputHandlingTerminal, ICompositionHelper, ITerminal, IBrowser, ITerminalOptions } from './Types'; import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types'; import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, CoreMouseEventType } from 'common/Types'; @@ -362,6 +362,7 @@ export class MockBuffer implements IBuffer { } export class MockRenderer implements IRenderer { + onRequestRefreshRows: IEvent; onCanvasResize: IEvent<{ width: number; height: number; }>; onRender: IEvent<{ start: number; end: number; }>; dispose(): void { diff --git a/src/browser/renderer/Types.d.ts b/src/browser/renderer/Types.d.ts index 7f84dfd49..b4c321a19 100644 --- a/src/browser/renderer/Types.d.ts +++ b/src/browser/renderer/Types.d.ts @@ -5,6 +5,7 @@ import { IDisposable } from 'common/Types'; import { IColorSet } from 'browser/Types'; +import { IEvent } from 'common/EventEmitter'; export type CharacterJoinerHandler = (text: string) => [number, number][]; @@ -23,6 +24,11 @@ export interface IRenderDimensions { actualCellHeight: number; } +export interface IRequestRefreshRowsEvent { + start: number; + end: number; +} + /** * Note that IRenderer implementations should emit the refresh event after * rendering rows to the screen. @@ -30,6 +36,8 @@ export interface IRenderDimensions { export interface IRenderer extends IDisposable { readonly dimensions: IRenderDimensions; + readonly onRequestRefreshRows: IEvent; + dispose(): void; setColors(colors: IColorSet): void; onDevicePixelRatioChange(): void; diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index b512aa70f..3d544a902 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -3,13 +3,14 @@ * @license MIT */ -import { IRenderer, IRenderDimensions, CharacterJoinerHandler } from 'browser/renderer/Types'; +import { IRenderer, IRenderDimensions, CharacterJoinerHandler, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_BLINK_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory } from 'browser/renderer/dom/DomRendererRowFactory'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { Disposable } from 'common/Lifecycle'; import { IColorSet, ILinkifierEvent, ILinkifier } from 'browser/Types'; -import { ICharSizeService, IRenderService } from 'browser/services/Services'; +import { ICharSizeService } from 'browser/services/Services'; import { IOptionsService, IBufferService } from 'common/services/Services'; +import { EventEmitter, IEvent } from 'common/EventEmitter'; const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-'; const ROW_CONTAINER_CLASS = 'xterm-rows'; @@ -37,6 +38,9 @@ export class DomRenderer extends Disposable implements IRenderer { public dimensions: IRenderDimensions; + private _onRequestRefreshRows = new EventEmitter(); + public get onRequestRefreshRows(): IEvent { return this._onRequestRefreshRows.event; } + constructor( private _colors: IColorSet, private readonly _element: HTMLElement, @@ -45,8 +49,7 @@ export class DomRenderer extends Disposable implements IRenderer { private readonly _linkifier: ILinkifier, @ICharSizeService private readonly _charSizeService: ICharSizeService, @IOptionsService private readonly _optionsService: IOptionsService, - @IBufferService private readonly _bufferService: IBufferService, - @IRenderService private readonly _renderService: IRenderService + @IBufferService private readonly _bufferService: IBufferService ) { super(); @@ -337,7 +340,7 @@ export class DomRenderer extends Disposable implements IRenderer { // Force a refresh this._updateDimensions(); this._injectCss(); - this._renderService.refreshRows(0, this._bufferService.rows - 1); + this._onRequestRefreshRows.fire({ start: 0, end: this._bufferService.rows - 1 }); } public clear(): void { diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 63084f545..b28c99713 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -51,6 +51,9 @@ export class RenderService extends Disposable implements IRenderService { this.register(optionsService.onOptionChange(() => this._renderer.onOptionsChanged())); this.register(charSizeService.onCharSizeChange(() => this.onCharSizeChanged())); + // No need to register this as renderer is explicitly disposed in RenderService.dispose + this._renderer.onRequestRefreshRows(e => this.refreshRows(e.start, e.end)); + // dprchange should handle this case, we need this as well for browsers that don't support the // matchMedia query. this.register(addDisposableDomListener(window, 'resize', () => this.onDevicePixelRatioChange())); @@ -105,12 +108,14 @@ export class RenderService extends Disposable implements IRenderService { public dispose(): void { this._renderer.dispose(); + super.dispose(); } public setRenderer(renderer: IRenderer): void { // TODO: RenderService should be the only one to dispose the renderer this._renderer.dispose(); this._renderer = renderer; + this._renderer.onRequestRefreshRows(e => this.refreshRows(e.start, e.end)); this.refreshRows(0, this._rowCount - 1); } diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts index 1f2791f16..d2278983f 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/renderer/CursorRenderLayer.ts @@ -3,13 +3,14 @@ * @license MIT */ -import { IRenderDimensions } from 'browser/renderer/Types'; +import { IRenderDimensions, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; import { BaseRenderLayer } from '../browser/renderer/BaseRenderLayer'; 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'; +import { IEventEmitter } from 'common/EventEmitter'; interface ICursorState { x: number; @@ -36,6 +37,7 @@ export class CursorRenderLayer extends BaseRenderLayer { colors: IColorSet, private _terminal: ITerminal, rendererId: number, + private _onRequestRefreshRowsEvent: IEventEmitter, readonly bufferService: IBufferService, readonly optionsService: IOptionsService ) { @@ -80,14 +82,14 @@ export class CursorRenderLayer extends BaseRenderLayer { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.pause(); } - this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y); + this._onRequestRefreshRowsEvent.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y }); } public onFocus(): void { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.resume(); } else { - this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y); + this._onRequestRefreshRowsEvent.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y }); } } @@ -106,7 +108,7 @@ 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._bufferService.buffer.y, this._bufferService.buffer.y); + this._onRequestRefreshRowsEvent.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y }); } public onCursorMove(): void { diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 5bc9d0048..45abbb828 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -6,7 +6,7 @@ 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 { IRenderLayer, IRenderer, IRenderDimensions, CharacterJoinerHandler, ICharacterJoinerRegistry, IRequestRefreshRowsEvent } from 'browser/renderer/Types'; import { ITerminal } from '../Types'; import { LinkRenderLayer } from '../browser/renderer/LinkRenderLayer'; import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistry'; @@ -15,6 +15,7 @@ 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'; +import { EventEmitter, IEvent } from 'common/EventEmitter'; let nextRendererId = 1; @@ -27,6 +28,9 @@ export class Renderer extends Disposable implements IRenderer { public dimensions: IRenderDimensions; + private _onRequestRefreshRows = new EventEmitter(); + public get onRequestRefreshRows(): IEvent { return this._onRequestRefreshRows.event; } + constructor( private _colors: IColorSet, private readonly _terminal: ITerminal, @@ -42,7 +46,7 @@ export class Renderer extends Disposable implements IRenderer { new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._id, this._bufferService, _optionsService), new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors, this._id, this._bufferService, _optionsService), new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._id, this._terminal.linkifier, this._bufferService, _optionsService), - new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal, this._id, this._bufferService, _optionsService) + new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal, this._id, this._onRequestRefreshRows, this._bufferService, _optionsService) ]; this.dimensions = { scaledCharWidth: null,