aboutsummaryrefslogtreecommitdiffstats
path: root/node_modules/xterm/src/browser/renderer/TextRenderLayer.ts
diff options
context:
space:
mode:
authorAnthony Schneider <tonyschneider3@gmail.com>2022-02-11 19:40:35 -0600
committerAnthony Schneider <tonyschneider3@gmail.com>2022-02-11 19:40:35 -0600
commitb52feccdcc58c1f4583c8542632d6c026335dea7 (patch)
tree5e242dd13ed4bbfff85a07109ef826f80874e2a6 /node_modules/xterm/src/browser/renderer/TextRenderLayer.ts
parent94862321e2e4a58e3209c037e8061f0435b3aa82 (diff)
Changed javascript to be in its own file. Began (messy) setup for terminal.
Diffstat (limited to 'node_modules/xterm/src/browser/renderer/TextRenderLayer.ts')
-rw-r--r--node_modules/xterm/src/browser/renderer/TextRenderLayer.ts330
1 files changed, 330 insertions, 0 deletions
diff --git a/node_modules/xterm/src/browser/renderer/TextRenderLayer.ts b/node_modules/xterm/src/browser/renderer/TextRenderLayer.ts
new file mode 100644
index 0000000..33d942f
--- /dev/null
+++ b/node_modules/xterm/src/browser/renderer/TextRenderLayer.ts
@@ -0,0 +1,330 @@
+/**
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
+ * @license MIT
+ */
+
+import { IRenderDimensions } from 'browser/renderer/Types';
+import { CharData, ICellData } from 'common/Types';
+import { GridCache } from 'browser/renderer/GridCache';
+import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer';
+import { AttributeData } from 'common/buffer/AttributeData';
+import { NULL_CELL_CODE, Content } from 'common/buffer/Constants';
+import { IColorSet } from 'browser/Types';
+import { CellData } from 'common/buffer/CellData';
+import { IOptionsService, IBufferService } from 'common/services/Services';
+import { ICharacterJoinerService } from 'browser/services/Services';
+import { JoinedCellData } from 'browser/services/CharacterJoinerService';
+
+/**
+ * This CharData looks like a null character, which will forc a clear and render
+ * when the character changes (a regular space ' ' character may not as it's
+ * drawn state is a cleared cell).
+ */
+// const OVERLAP_OWNED_CHAR_DATA: CharData = [null, '', 0, -1];
+
+export class TextRenderLayer extends BaseRenderLayer {
+ private _state: GridCache<CharData>;
+ private _characterWidth: number = 0;
+ private _characterFont: string = '';
+ private _characterOverlapCache: { [key: string]: boolean } = {};
+ private _workCell = new CellData();
+
+ constructor(
+ container: HTMLElement,
+ zIndex: number,
+ colors: IColorSet,
+ alpha: boolean,
+ rendererId: number,
+ @IBufferService bufferService: IBufferService,
+ @IOptionsService optionsService: IOptionsService,
+ @ICharacterJoinerService private readonly _characterJoinerService: ICharacterJoinerService
+ ) {
+ super(container, 'text', zIndex, alpha, colors, rendererId, bufferService, optionsService);
+ this._state = new GridCache<CharData>();
+ }
+
+ public resize(dim: IRenderDimensions): void {
+ super.resize(dim);
+
+ // Clear the character width cache if the font or width has changed
+ const terminalFont = this._getFont(false, false);
+ if (this._characterWidth !== dim.scaledCharWidth || this._characterFont !== terminalFont) {
+ this._characterWidth = dim.scaledCharWidth;
+ this._characterFont = terminalFont;
+ this._characterOverlapCache = {};
+ }
+ // Resizing the canvas discards the contents of the canvas so clear state
+ this._state.clear();
+ this._state.resize(this._bufferService.cols, this._bufferService.rows);
+ }
+
+ public reset(): void {
+ this._state.clear();
+ this._clearAll();
+ }
+
+ private _forEachCell(
+ firstRow: number,
+ lastRow: number,
+ callback: (
+ cell: ICellData,
+ x: number,
+ y: number
+ ) => void
+ ): void {
+ for (let y = firstRow; y <= lastRow; y++) {
+ const row = y + this._bufferService.buffer.ydisp;
+ const line = this._bufferService.buffer.lines.get(row);
+ const joinedRanges = this._characterJoinerService.getJoinedCharacters(row);
+ for (let x = 0; x < this._bufferService.cols; x++) {
+ line!.loadCell(x, this._workCell);
+ let cell = this._workCell;
+
+ // If true, indicates that the current character(s) to draw were joined.
+ let isJoined = false;
+ let lastCharX = x;
+
+ // The character to the left is a wide character, drawing is owned by
+ // the char at x-1
+ if (cell.getWidth() === 0) {
+ continue;
+ }
+
+ // Process any joined character ranges as needed. Because of how the
+ // ranges are produced, we know that they are valid for the characters
+ // and attributes of our input.
+ if (joinedRanges.length > 0 && x === joinedRanges[0][0]) {
+ isJoined = true;
+ 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]),
+ range[1] - range[0]
+ );
+
+ // Skip over the cells occupied by this range in the loop
+ lastCharX = range[1] - 1;
+ }
+
+ // If the character is an overlapping char and the character to the
+ // right is a space, take ownership of the cell to the right. We skip
+ // this check for joined characters because their rendering likely won't
+ // yield the same result as rendering the last character individually.
+ if (!isJoined && this._isOverlapping(cell)) {
+ // If the character is overlapping, we want to force a re-render on every
+ // frame. This is specifically to work around the case where two
+ // overlaping chars `a` and `b` are adjacent, the cursor is moved to b and a
+ // space is added. Without this, the first half of `b` would never
+ // 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) {
+ // patch width to 2
+ cell.content &= ~Content.WIDTH_MASK;
+ cell.content |= 2 << Content.WIDTH_SHIFT;
+ // this._clearChar(x + 1, y);
+ // The overlapping char's char data will force a clear and render when the
+ // overlapping char is no longer to the left of the character and also when
+ // the space changes to another character.
+ // this._state.cache[x + 1][y] = OVERLAP_OWNED_CHAR_DATA;
+ }
+ }
+
+ callback(
+ cell,
+ x,
+ y
+ );
+
+ x = lastCharX;
+ }
+ }
+ }
+
+ /**
+ * 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(firstRow: number, lastRow: number): void {
+ const ctx = this._ctx;
+ const cols = this._bufferService.cols;
+ let startX: number = 0;
+ let startY: number = 0;
+ let prevFillStyle: string | null = null;
+
+ ctx.save();
+
+ this._forEachCell(firstRow, lastRow, (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
+
+ if (cell.isInverse()) {
+ if (cell.isFgDefault()) {
+ nextFillStyle = this._colors.foreground.css;
+ } else if (cell.isFgRGB()) {
+ nextFillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;
+ } else {
+ nextFillStyle = this._colors.ansi[cell.getFgColor()].css;
+ }
+ } else if (cell.isBgRGB()) {
+ nextFillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`;
+ } else if (cell.isBgPalette()) {
+ nextFillStyle = this._colors.ansi[cell.getBgColor()].css;
+ }
+
+ if (prevFillStyle === null) {
+ // This is either the first iteration, or the default background was set. Either way, we
+ // don't need to draw anything.
+ startX = x;
+ startY = y;
+ }
+
+ if (y !== startY) {
+ // our row changed, draw the previous row
+ ctx.fillStyle = 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 || '';
+ this._fillCells(startX, startY, x - startX, 1);
+ startX = x;
+ startY = y;
+ }
+
+ prevFillStyle = nextFillStyle;
+ });
+
+ // flush the last color we encountered
+ if (prevFillStyle !== null) {
+ ctx.fillStyle = prevFillStyle;
+ this._fillCells(startX, startY, cols - startX, 1);
+ }
+
+ ctx.restore();
+ }
+
+ private _drawForeground(firstRow: number, lastRow: number): void {
+ this._forEachCell(firstRow, lastRow, (cell, x, y) => {
+ if (cell.isInvisible()) {
+ return;
+ }
+ this._drawChars(cell, x, y);
+ if (cell.isUnderline() || cell.isStrikethrough()) {
+ this._ctx.save();
+
+ if (cell.isInverse()) {
+ if (cell.isBgDefault()) {
+ this._ctx.fillStyle = this._colors.background.css;
+ } else if (cell.isBgRGB()) {
+ this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`;
+ } else {
+ let bg = cell.getBgColor();
+ if (this._optionsService.rawOptions.drawBoldTextInBrightColors && cell.isBold() && bg < 8) {
+ bg += 8;
+ }
+ this._ctx.fillStyle = this._colors.ansi[bg].css;
+ }
+ } else {
+ if (cell.isFgDefault()) {
+ this._ctx.fillStyle = this._colors.foreground.css;
+ } else if (cell.isFgRGB()) {
+ this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;
+ } else {
+ let fg = cell.getFgColor();
+ if (this._optionsService.rawOptions.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {
+ fg += 8;
+ }
+ this._ctx.fillStyle = this._colors.ansi[fg].css;
+ }
+ }
+
+ if (cell.isStrikethrough()) {
+ this._fillMiddleLineAtCells(x, y, cell.getWidth());
+ }
+ if (cell.isUnderline()) {
+ this._fillBottomLineAtCells(x, y, cell.getWidth());
+ }
+ this._ctx.restore();
+ }
+ });
+ }
+
+ public onGridChanged(firstRow: number, lastRow: number): void {
+ // Resize has not been called yet
+ if (this._state.cache.length === 0) {
+ return;
+ }
+
+ if (this._charAtlas) {
+ this._charAtlas.beginFrame();
+ }
+
+ this._clearCells(0, firstRow, this._bufferService.cols, lastRow - firstRow + 1);
+ this._drawBackground(firstRow, lastRow);
+ this._drawForeground(firstRow, lastRow);
+ }
+
+ public onOptionsChanged(): void {
+ this._setTransparency(this._optionsService.rawOptions.allowTransparency);
+ }
+
+ /**
+ * Whether a character is overlapping to the next cell.
+ */
+ private _isOverlapping(cell: ICellData): boolean {
+ // Only single cell characters can be overlapping, rendering issues can
+ // occur without this check
+ if (cell.getWidth() !== 1) {
+ return false;
+ }
+
+ // We assume that any ascii character will not overlap
+ if (cell.getCode() < 256) {
+ return false;
+ }
+
+ const chars = cell.getChars();
+
+ // Deliver from cache if available
+ if (this._characterOverlapCache.hasOwnProperty(chars)) {
+ return this._characterOverlapCache[chars];
+ }
+
+ // Setup the font
+ this._ctx.save();
+ this._ctx.font = this._characterFont;
+
+ // Measure the width of the character, but Math.floor it
+ // because that is what the renderer does when it calculates
+ // the character dimensions we are comparing against
+ const overlaps = Math.floor(this._ctx.measureText(chars).width) > this._characterWidth;
+
+ // Restore the original context
+ this._ctx.restore();
+
+ // Cache and return
+ 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);
+ // }
+}