xterm
[VSoRC/.git] / node_modules / xterm / src / renderer / Renderer.ts
diff --git a/node_modules/xterm/src/renderer/Renderer.ts b/node_modules/xterm/src/renderer/Renderer.ts
new file mode 100644 (file)
index 0000000..d3977af
--- /dev/null
@@ -0,0 +1,208 @@
+/**
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
+ * @license MIT
+ */
+
+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 '../browser/renderer/LinkRenderLayer';
+import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistry';
+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;
+
+  public dimensions: IRenderDimensions;
+
+  constructor(
+    private _colors: IColorSet,
+    private readonly _terminal: ITerminal,
+    readonly bufferService: IBufferService,
+    private readonly _charSizeService: ICharSizeService,
+    readonly optionsService: IOptionsService
+  ) {
+    super();
+    const allowTransparency = this._terminal.options.allowTransparency;
+    this._characterJoinerRegistry = new CharacterJoinerRegistry(bufferService);
+
+    this._renderLayers = [
+      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,
+      scaledCharHeight: null,
+      scaledCellWidth: null,
+      scaledCellHeight: null,
+      scaledCharLeft: null,
+      scaledCharTop: null,
+      scaledCanvasWidth: null,
+      scaledCanvasHeight: null,
+      canvasWidth: null,
+      canvasHeight: null,
+      actualCellWidth: null,
+      actualCellHeight: null
+    };
+    this._devicePixelRatio = window.devicePixelRatio;
+    this._updateDimensions();
+    this.onOptionsChanged();
+  }
+
+  public dispose(): void {
+    super.dispose();
+    this._renderLayers.forEach(l => l.dispose());
+    removeTerminalFromCache(this._id);
+  }
+
+  public onDevicePixelRatioChange(): void {
+    // If the device pixel ratio changed, the char atlas needs to be regenerated
+    // and the terminal needs to refreshed
+    if (this._devicePixelRatio !== window.devicePixelRatio) {
+      this._devicePixelRatio = window.devicePixelRatio;
+      this.onResize(this._terminal.cols, this._terminal.rows);
+    }
+  }
+
+  public setColors(colors: IColorSet): void {
+    this._colors = colors;
+
+    // Clear layers and force a full render
+    this._renderLayers.forEach(l => {
+      l.setColors(this._colors);
+      l.reset();
+    });
+  }
+
+  public onResize(cols: number, rows: number): void {
+    // Update character and canvas dimensions
+    this._updateDimensions();
+
+    // Resize all render layers
+    this._renderLayers.forEach(l => l.resize(this.dimensions));
+
+    // Resize the screen
+    this._terminal.screenElement.style.width = `${this.dimensions.canvasWidth}px`;
+    this._terminal.screenElement.style.height = `${this.dimensions.canvasHeight}px`;
+  }
+
+  public onCharSizeChanged(): void {
+    this.onResize(this._terminal.cols, this._terminal.rows);
+  }
+
+  public onBlur(): void {
+    this._runOperation(l => l.onBlur());
+  }
+
+  public onFocus(): void {
+    this._runOperation(l => l.onFocus());
+  }
+
+  public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean = false): void {
+    this._runOperation(l => l.onSelectionChanged(start, end, columnSelectMode));
+  }
+
+  public onCursorMove(): void {
+    this._runOperation(l => l.onCursorMove());
+  }
+
+  public onOptionsChanged(): void {
+    this._runOperation(l => l.onOptionsChanged());
+  }
+
+  public clear(): void {
+    this._runOperation(l => l.reset());
+  }
+
+  private _runOperation(operation: (layer: IRenderLayer) => void): void {
+    this._renderLayers.forEach(l => operation(l));
+  }
+
+  /**
+   * Performs the refresh loop callback, calling refresh only if a refresh is
+   * necessary before queueing up the next one.
+   */
+  public renderRows(start: number, end: number): void {
+    this._renderLayers.forEach(l => l.onGridChanged(start, end));
+  }
+
+  /**
+   * Recalculates the character and canvas dimensions.
+   */
+  private _updateDimensions(): void {
+    if (!this._charSizeService.hasValidSize) {
+      return;
+    }
+
+    // Calculate the scaled character width. Width is floored as it must be
+    // drawn to an integer grid in order for the CharAtlas "stamps" to not be
+    // blurry. When text is drawn to the grid not using the CharAtlas, it is
+    // clipped to ensure there is no overlap with the next cell.
+    this.dimensions.scaledCharWidth = Math.floor(this._charSizeService.width * window.devicePixelRatio);
+
+    // Calculate the scaled character height. Height is ceiled in case
+    // devicePixelRatio is a floating point number in order to ensure there is
+    // enough space to draw the character to the cell.
+    this.dimensions.scaledCharHeight = Math.ceil(this._charSizeService.height * window.devicePixelRatio);
+
+    // Calculate the scaled cell height, if lineHeight is not 1 then the value
+    // will be floored because since lineHeight can never be lower then 1, there
+    // is a guarentee that the scaled line height will always be larger than
+    // scaled char height.
+    this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._terminal.options.lineHeight);
+
+    // Calculate the y coordinate within a cell that text should draw from in
+    // order to draw in the center of a cell.
+    this.dimensions.scaledCharTop = this._terminal.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2);
+
+    // Calculate the scaled cell width, taking the letterSpacing into account.
+    this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._terminal.options.letterSpacing);
+
+    // Calculate the x coordinate with a cell that text should draw from in
+    // order to draw in the center of a cell.
+    this.dimensions.scaledCharLeft = Math.floor(this._terminal.options.letterSpacing / 2);
+
+    // Recalculate the canvas dimensions; scaled* define the actual number of
+    // pixel in the canvas
+    this.dimensions.scaledCanvasHeight = this._terminal.rows * this.dimensions.scaledCellHeight;
+    this.dimensions.scaledCanvasWidth = this._terminal.cols * this.dimensions.scaledCellWidth;
+
+    // The the size of the canvas on the page. It's very important that this
+    // rounds to nearest integer and not ceils as browsers often set
+    // window.devicePixelRatio as something like 1.100000023841858, when it's
+    // actually 1.1. Ceiling causes blurriness as the backing canvas image is 1
+    // pixel too large for the canvas element size.
+    this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / window.devicePixelRatio);
+    this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / window.devicePixelRatio);
+
+    // Get the _actual_ dimensions of an individual cell. This needs to be
+    // derived from the canvasWidth/Height calculated above which takes into
+    // account window.devicePixelRatio. ICharSizeService.width/height by itself
+    // is insufficient when the page is not at 100% zoom level as it's measured
+    // in CSS pixels, but the actual char size on the canvas can differ.
+    this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._terminal.rows;
+    this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._terminal.cols;
+  }
+
+  public registerCharacterJoiner(handler: CharacterJoinerHandler): number {
+    return this._characterJoinerRegistry.registerCharacterJoiner(handler);
+  }
+
+  public deregisterCharacterJoiner(joinerId: number): boolean {
+    return this._characterJoinerRegistry.deregisterCharacterJoiner(joinerId);
+  }
+}