diff options
author | Toby Vincent <tobyv13@gmail.com> | 2022-03-23 14:04:55 -0500 |
---|---|---|
committer | Toby Vincent <tobyv13@gmail.com> | 2022-03-23 14:15:18 -0500 |
commit | 8e36499326e969c7c34ecc537e589bd5ef5598a0 (patch) | |
tree | 7943cda837324847875d472d2ad68922c5019a50 /node_modules/xterm/src/browser/renderer | |
parent | 337fa04fe3686e0f0d236eef5df89179ee68f673 (diff) |
chore: removed node_modules from git
Diffstat (limited to 'node_modules/xterm/src/browser/renderer')
19 files changed, 0 insertions, 3813 deletions
diff --git a/node_modules/xterm/src/browser/renderer/BaseRenderLayer.ts b/node_modules/xterm/src/browser/renderer/BaseRenderLayer.ts deleted file mode 100644 index 629e943..0000000 --- a/node_modules/xterm/src/browser/renderer/BaseRenderLayer.ts +++ /dev/null @@ -1,513 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { IRenderDimensions, IRenderLayer } from 'browser/renderer/Types'; -import { ICellData } from 'common/Types'; -import { DEFAULT_COLOR, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE, Attributes } from 'common/buffer/Constants'; -import { IGlyphIdentifier } from 'browser/renderer/atlas/Types'; -import { DIM_OPACITY, INVERTED_DEFAULT_COLOR, TEXT_BASELINE } from 'browser/renderer/atlas/Constants'; -import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas'; -import { acquireCharAtlas } from 'browser/renderer/atlas/CharAtlasCache'; -import { AttributeData } from 'common/buffer/AttributeData'; -import { IColorSet, IColor } from 'browser/Types'; -import { CellData } from 'common/buffer/CellData'; -import { IBufferService, IOptionsService } from 'common/services/Services'; -import { throwIfFalsy } from 'browser/renderer/RendererUtils'; -import { channels, color, rgba } from 'browser/Color'; -import { removeElementFromParent } from 'browser/Dom'; -import { tryDrawCustomChar } from 'browser/renderer/CustomGlyphs'; - -export abstract class BaseRenderLayer implements IRenderLayer { - private _canvas: HTMLCanvasElement; - protected _ctx!: CanvasRenderingContext2D; - private _scaledCharWidth: number = 0; - private _scaledCharHeight: number = 0; - private _scaledCellWidth: number = 0; - private _scaledCellHeight: number = 0; - private _scaledCharLeft: number = 0; - private _scaledCharTop: number = 0; - - protected _charAtlas: BaseCharAtlas | undefined; - - /** - * An object that's reused when drawing glyphs in order to reduce GC. - */ - private _currentGlyphIdentifier: IGlyphIdentifier = { - chars: '', - code: 0, - bg: 0, - fg: 0, - bold: false, - dim: false, - italic: false - }; - - constructor( - private _container: HTMLElement, - id: string, - zIndex: number, - private _alpha: boolean, - protected _colors: IColorSet, - private _rendererId: number, - protected readonly _bufferService: IBufferService, - protected readonly _optionsService: IOptionsService - ) { - 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); - } - - public dispose(): void { - removeElementFromParent(this._canvas); - this._charAtlas?.dispose(); - } - - private _initCanvas(): void { - this._ctx = throwIfFalsy(this._canvas.getContext('2d', { alpha: this._alpha })); - // Draw the background if this is an opaque layer - if (!this._alpha) { - this._clearAll(); - } - } - - public onOptionsChanged(): void {} - public onBlur(): void {} - public onFocus(): void {} - public onCursorMove(): void {} - public onGridChanged(startRow: number, endRow: number): void {} - public onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {} - - public setColors(colorSet: IColorSet): void { - this._refreshCharAtlas(colorSet); - } - - protected _setTransparency(alpha: boolean): void { - // Do nothing when alpha doesn't change - if (alpha === this._alpha) { - return; - } - - // Create new canvas and replace old one - const oldCanvas = this._canvas; - this._alpha = alpha; - // Cloning preserves properties - this._canvas = this._canvas.cloneNode() as HTMLCanvasElement; - this._initCanvas(); - this._container.replaceChild(this._canvas, oldCanvas); - - // Regenerate char atlas and force a full redraw - this._refreshCharAtlas(this._colors); - this.onGridChanged(0, this._bufferService.rows - 1); - } - - /** - * Refreshes the char atlas, aquiring a new one if necessary. - * @param colorSet The color set to use for the char atlas. - */ - private _refreshCharAtlas(colorSet: IColorSet): void { - if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) { - return; - } - this._charAtlas = acquireCharAtlas(this._optionsService.rawOptions, this._rendererId, colorSet, this._scaledCharWidth, this._scaledCharHeight); - this._charAtlas.warmUp(); - } - - public resize(dim: IRenderDimensions): void { - this._scaledCellWidth = dim.scaledCellWidth; - this._scaledCellHeight = dim.scaledCellHeight; - this._scaledCharWidth = dim.scaledCharWidth; - this._scaledCharHeight = dim.scaledCharHeight; - this._scaledCharLeft = dim.scaledCharLeft; - this._scaledCharTop = dim.scaledCharTop; - this._canvas.width = dim.scaledCanvasWidth; - this._canvas.height = dim.scaledCanvasHeight; - this._canvas.style.width = `${dim.canvasWidth}px`; - this._canvas.style.height = `${dim.canvasHeight}px`; - - // Draw the background if this is an opaque layer - if (!this._alpha) { - this._clearAll(); - } - - this._refreshCharAtlas(this._colors); - } - - public abstract reset(): void; - - public clearTextureAtlas(): void { - this._charAtlas?.clear(); - } - - /** - * Fills 1+ cells completely. This uses the existing fillStyle on the context. - * @param x The column to start at. - * @param y The row to start at - * @param width The number of columns to fill. - * @param height The number of rows to fill. - */ - protected _fillCells(x: number, y: number, width: number, height: number): void { - this._ctx.fillRect( - x * this._scaledCellWidth, - y * this._scaledCellHeight, - width * this._scaledCellWidth, - height * this._scaledCellHeight); - } - - /** - * Fills a 1px line (2px on HDPI) at the middle of the cell. This uses the - * existing fillStyle on the context. - * @param x The column to fill. - * @param y The row to fill. - */ - protected _fillMiddleLineAtCells(x: number, y: number, width: number = 1): void { - const cellOffset = Math.ceil(this._scaledCellHeight * 0.5); - this._ctx.fillRect( - x * this._scaledCellWidth, - (y + 1) * this._scaledCellHeight - cellOffset - window.devicePixelRatio, - width * this._scaledCellWidth, - window.devicePixelRatio); - } - - /** - * Fills a 1px line (2px on HDPI) at the bottom of the cell. This uses the - * existing fillStyle on the context. - * @param x The column to fill. - * @param y The row to fill. - */ - protected _fillBottomLineAtCells(x: number, y: number, width: number = 1): void { - this._ctx.fillRect( - x * this._scaledCellWidth, - (y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1 /* Ensure it's drawn within the cell */, - width * this._scaledCellWidth, - window.devicePixelRatio); - } - - /** - * Fills a 1px line (2px on HDPI) at the left of the cell. This uses the - * existing fillStyle on the context. - * @param x The column to fill. - * @param y The row to fill. - */ - protected _fillLeftLineAtCell(x: number, y: number, width: number): void { - this._ctx.fillRect( - x * this._scaledCellWidth, - y * this._scaledCellHeight, - window.devicePixelRatio * width, - this._scaledCellHeight); - } - - /** - * Strokes a 1px rectangle (2px on HDPI) around a cell. This uses the existing - * strokeStyle on the context. - * @param x The column to fill. - * @param y The row to fill. - */ - protected _strokeRectAtCell(x: number, y: number, width: number, height: number): void { - this._ctx.lineWidth = window.devicePixelRatio; - this._ctx.strokeRect( - x * this._scaledCellWidth + window.devicePixelRatio / 2, - y * this._scaledCellHeight + (window.devicePixelRatio / 2), - width * this._scaledCellWidth - window.devicePixelRatio, - (height * this._scaledCellHeight) - window.devicePixelRatio); - } - - /** - * Clears the entire canvas. - */ - protected _clearAll(): void { - if (this._alpha) { - this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); - } else { - this._ctx.fillStyle = this._colors.background.css; - this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height); - } - } - - /** - * Clears 1+ cells completely. - * @param x The column to start at. - * @param y The row to start at. - * @param width The number of columns to clear. - * @param height The number of rows to clear. - */ - protected _clearCells(x: number, y: number, width: number, height: number): void { - if (this._alpha) { - this._ctx.clearRect( - x * this._scaledCellWidth, - y * this._scaledCellHeight, - width * this._scaledCellWidth, - height * this._scaledCellHeight); - } else { - this._ctx.fillStyle = this._colors.background.css; - this._ctx.fillRect( - x * this._scaledCellWidth, - y * this._scaledCellHeight, - width * this._scaledCellWidth, - height * this._scaledCellHeight); - } - } - - /** - * Draws a truecolor character at the cell. The character will be clipped to - * ensure that it fits with the cell, including the cell to the right if it's - * a wide character. This uses the existing fillStyle on the context. - * @param cell The cell data for the character to draw. - * @param x The column to draw at. - * @param y The row to draw at. - * @param color The color of the character. - */ - protected _fillCharTrueColor(cell: CellData, x: number, y: number): void { - this._ctx.font = this._getFont(false, false); - this._ctx.textBaseline = TEXT_BASELINE; - this._clipRow(y); - - // Draw custom characters if applicable - let drawSuccess = false; - if (this._optionsService.rawOptions.customGlyphs !== false) { - drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight); - } - - // Draw the character - if (!drawSuccess) { - this._ctx.fillText( - cell.getChars(), - x * this._scaledCellWidth + this._scaledCharLeft, - y * this._scaledCellHeight + this._scaledCharTop + this._scaledCharHeight); - } - } - - /** - * Draws one or more characters at a cell. If possible this will draw using - * the character atlas to reduce draw time. - * @param chars The character or characters. - * @param code The character code. - * @param width The width of the characters. - * @param x The column to draw at. - * @param y The row to draw at. - * @param fg The foreground color, in the format stored within the attributes. - * @param bg The background color, in the format stored within the attributes. - * This is used to validate whether a cached image can be used. - * @param bold Whether the text is bold. - */ - protected _drawChars(cell: ICellData, x: number, y: number): void { - const contrastColor = this._getContrastColor(cell); - - // skip cache right away if we draw in RGB - // Note: to avoid bad runtime JoinedCellData will be skipped - // in the cache handler itself (atlasDidDraw == false) and - // fall through to uncached later down below - if (contrastColor || cell.isFgRGB() || cell.isBgRGB()) { - this._drawUncachedChars(cell, x, y, contrastColor); - return; - } - - let fg; - let bg; - if (cell.isInverse()) { - fg = (cell.isBgDefault()) ? INVERTED_DEFAULT_COLOR : cell.getBgColor(); - bg = (cell.isFgDefault()) ? INVERTED_DEFAULT_COLOR : cell.getFgColor(); - } else { - bg = (cell.isBgDefault()) ? DEFAULT_COLOR : cell.getBgColor(); - fg = (cell.isFgDefault()) ? DEFAULT_COLOR : cell.getFgColor(); - } - - const drawInBrightColor = this._optionsService.rawOptions.drawBoldTextInBrightColors && cell.isBold() && fg < 8; - - fg += drawInBrightColor ? 8 : 0; - this._currentGlyphIdentifier.chars = cell.getChars() || WHITESPACE_CELL_CHAR; - this._currentGlyphIdentifier.code = cell.getCode() || WHITESPACE_CELL_CODE; - this._currentGlyphIdentifier.bg = bg; - this._currentGlyphIdentifier.fg = fg; - this._currentGlyphIdentifier.bold = !!cell.isBold(); - this._currentGlyphIdentifier.dim = !!cell.isDim(); - this._currentGlyphIdentifier.italic = !!cell.isItalic(); - const atlasDidDraw = this._charAtlas?.draw(this._ctx, this._currentGlyphIdentifier, x * this._scaledCellWidth + this._scaledCharLeft, y * this._scaledCellHeight + this._scaledCharTop); - - if (!atlasDidDraw) { - this._drawUncachedChars(cell, x, y); - } - } - - /** - * Draws one or more characters at one or more cells. The character(s) will be - * clipped to ensure that they fit with the cell(s), including the cell to the - * right if the last character is a wide character. - * @param chars The character. - * @param width The width of the character. - * @param fg The foreground color, in the format stored within the attributes. - * @param x The column to draw at. - * @param y The row to draw at. - */ - private _drawUncachedChars(cell: ICellData, x: number, y: number, fgOverride?: IColor): void { - this._ctx.save(); - this._ctx.font = this._getFont(!!cell.isBold(), !!cell.isItalic()); - this._ctx.textBaseline = TEXT_BASELINE; - - if (cell.isInverse()) { - if (fgOverride) { - this._ctx.fillStyle = fgOverride.css; - } else if (cell.isBgDefault()) { - this._ctx.fillStyle = color.opaque(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 (fgOverride) { - this._ctx.fillStyle = fgOverride.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; - } - } - - this._clipRow(y); - - // Apply alpha to dim the character - if (cell.isDim()) { - this._ctx.globalAlpha = DIM_OPACITY; - } - - // Draw custom characters if applicable - let drawSuccess = false; - if (this._optionsService.rawOptions.customGlyphs !== false) { - drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight); - } - - // Draw the character - if (!drawSuccess) { - this._ctx.fillText( - cell.getChars(), - x * this._scaledCellWidth + this._scaledCharLeft, - y * this._scaledCellHeight + this._scaledCharTop + this._scaledCharHeight); - } - - this._ctx.restore(); - } - - - /** - * Clips a row to ensure no pixels will be drawn outside the cells in the row. - * @param y The row to clip. - */ - private _clipRow(y: number): void { - this._ctx.beginPath(); - this._ctx.rect( - 0, - y * this._scaledCellHeight, - this._bufferService.cols * this._scaledCellWidth, - this._scaledCellHeight); - this._ctx.clip(); - } - - /** - * Gets the current font. - * @param isBold If we should use the bold fontWeight. - */ - protected _getFont(isBold: boolean, isItalic: boolean): string { - const fontWeight = isBold ? this._optionsService.rawOptions.fontWeightBold : this._optionsService.rawOptions.fontWeight; - const fontStyle = isItalic ? 'italic' : ''; - - return `${fontStyle} ${fontWeight} ${this._optionsService.rawOptions.fontSize * window.devicePixelRatio}px ${this._optionsService.rawOptions.fontFamily}`; - } - - private _getContrastColor(cell: CellData): IColor | undefined { - if (this._optionsService.rawOptions.minimumContrastRatio === 1) { - return undefined; - } - - // Try get from cache first - const adjustedColor = this._colors.contrastCache.getColor(cell.bg, cell.fg); - if (adjustedColor !== undefined) { - return adjustedColor || undefined; - } - - let fgColor = cell.getFgColor(); - let fgColorMode = cell.getFgColorMode(); - let bgColor = cell.getBgColor(); - let bgColorMode = cell.getBgColorMode(); - const isInverse = !!cell.isInverse(); - const isBold = !!cell.isInverse(); - if (isInverse) { - const temp = fgColor; - fgColor = bgColor; - bgColor = temp; - const temp2 = fgColorMode; - fgColorMode = bgColorMode; - bgColorMode = temp2; - } - - const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, isInverse); - const fgRgba = this._resolveForegroundRgba(fgColorMode, fgColor, isInverse, isBold); - const result = rgba.ensureContrastRatio(bgRgba, fgRgba, this._optionsService.rawOptions.minimumContrastRatio); - - if (!result) { - this._colors.contrastCache.setColor(cell.bg, cell.fg, null); - return undefined; - } - - const color: IColor = { - css: channels.toCss( - (result >> 24) & 0xFF, - (result >> 16) & 0xFF, - (result >> 8) & 0xFF - ), - rgba: result - }; - this._colors.contrastCache.setColor(cell.bg, cell.fg, color); - - return color; - } - - private _resolveBackgroundRgba(bgColorMode: number, bgColor: number, inverse: boolean): number { - switch (bgColorMode) { - case Attributes.CM_P16: - case Attributes.CM_P256: - return this._colors.ansi[bgColor].rgba; - case Attributes.CM_RGB: - return bgColor << 8; - case Attributes.CM_DEFAULT: - default: - if (inverse) { - return this._colors.foreground.rgba; - } - return this._colors.background.rgba; - } - } - - private _resolveForegroundRgba(fgColorMode: number, fgColor: number, inverse: boolean, bold: boolean): number { - switch (fgColorMode) { - case Attributes.CM_P16: - case Attributes.CM_P256: - if (this._optionsService.rawOptions.drawBoldTextInBrightColors && bold && fgColor < 8) { - fgColor += 8; - } - return this._colors.ansi[fgColor].rgba; - case Attributes.CM_RGB: - return fgColor << 8; - case Attributes.CM_DEFAULT: - default: - if (inverse) { - return this._colors.background.rgba; - } - return this._colors.foreground.rgba; - } - } -} - diff --git a/node_modules/xterm/src/browser/renderer/CursorRenderLayer.ts b/node_modules/xterm/src/browser/renderer/CursorRenderLayer.ts deleted file mode 100644 index ea419cb..0000000 --- a/node_modules/xterm/src/browser/renderer/CursorRenderLayer.ts +++ /dev/null @@ -1,377 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { IRenderDimensions, IRequestRedrawEvent } from 'browser/renderer/Types'; -import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer'; -import { ICellData } from 'common/Types'; -import { CellData } from 'common/buffer/CellData'; -import { IColorSet } from 'browser/Types'; -import { IBufferService, IOptionsService, ICoreService } from 'common/services/Services'; -import { IEventEmitter } from 'common/EventEmitter'; -import { ICoreBrowserService } from 'browser/services/Services'; - -interface ICursorState { - x: number; - y: number; - isFocused: boolean; - style: string; - width: number; -} - -/** - * The time between cursor blinks. - */ -const BLINK_INTERVAL = 600; - -export class CursorRenderLayer extends BaseRenderLayer { - private _state: ICursorState; - private _cursorRenderers: {[key: string]: (x: number, y: number, cell: ICellData) => void}; - private _cursorBlinkStateManager: CursorBlinkStateManager | undefined; - private _cell: ICellData = new CellData(); - - constructor( - container: HTMLElement, - zIndex: number, - colors: IColorSet, - rendererId: number, - private _onRequestRedraw: IEventEmitter<IRequestRedrawEvent>, - @IBufferService bufferService: IBufferService, - @IOptionsService optionsService: IOptionsService, - @ICoreService private readonly _coreService: ICoreService, - @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService - ) { - super(container, 'cursor', zIndex, true, colors, rendererId, bufferService, optionsService); - this._state = { - x: 0, - y: 0, - isFocused: false, - style: '', - width: 0 - }; - this._cursorRenderers = { - 'bar': this._renderBarCursor.bind(this), - 'block': this._renderBlockCursor.bind(this), - 'underline': this._renderUnderlineCursor.bind(this) - }; - } - - public dispose(): void { - if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.dispose(); - this._cursorBlinkStateManager = undefined; - } - super.dispose(); - } - - public resize(dim: IRenderDimensions): void { - super.resize(dim); - // Resizing the canvas discards the contents of the canvas so clear state - this._state = { - x: 0, - y: 0, - isFocused: false, - style: '', - width: 0 - }; - } - - public reset(): void { - this._clearCursor(); - this._cursorBlinkStateManager?.restartBlinkAnimation(); - this.onOptionsChanged(); - } - - public onBlur(): void { - this._cursorBlinkStateManager?.pause(); - this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y }); - } - - public onFocus(): void { - this._cursorBlinkStateManager?.resume(); - this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y }); - } - - public onOptionsChanged(): void { - if (this._optionsService.rawOptions.cursorBlink) { - if (!this._cursorBlinkStateManager) { - this._cursorBlinkStateManager = new CursorBlinkStateManager(this._coreBrowserService.isFocused, () => { - this._render(true); - }); - } - } else { - this._cursorBlinkStateManager?.dispose(); - this._cursorBlinkStateManager = undefined; - } - // Request a refresh from the terminal as management of rendering is being - // moved back to the terminal - this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y }); - } - - public onCursorMove(): void { - this._cursorBlinkStateManager?.restartBlinkAnimation(); - } - - public onGridChanged(startRow: number, endRow: number): void { - if (!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isPaused) { - this._render(false); - } else { - this._cursorBlinkStateManager.restartBlinkAnimation(); - } - } - - private _render(triggeredByAnimationFrame: boolean): void { - // Don't draw the cursor if it's hidden - if (!this._coreService.isCursorInitialized || this._coreService.isCursorHidden) { - this._clearCursor(); - return; - } - - const cursorY = this._bufferService.buffer.ybase + this._bufferService.buffer.y; - const viewportRelativeCursorY = cursorY - this._bufferService.buffer.ydisp; - - // Don't draw the cursor if it's off-screen - if (viewportRelativeCursorY < 0 || viewportRelativeCursorY >= this._bufferService.rows) { - this._clearCursor(); - return; - } - - // in case cursor.x == cols adjust visual cursor to cols - 1 - const cursorX = Math.min(this._bufferService.buffer.x, this._bufferService.cols - 1); - this._bufferService.buffer.lines.get(cursorY)!.loadCell(cursorX, this._cell); - if (this._cell.content === undefined) { - return; - } - - if (!this._coreBrowserService.isFocused) { - this._clearCursor(); - this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor.css; - const cursorStyle = this._optionsService.rawOptions.cursorStyle; - if (cursorStyle && cursorStyle !== 'block') { - this._cursorRenderers[cursorStyle](cursorX, viewportRelativeCursorY, this._cell); - } else { - this._renderBlurCursor(cursorX, viewportRelativeCursorY, this._cell); - } - this._ctx.restore(); - this._state.x = cursorX; - this._state.y = viewportRelativeCursorY; - this._state.isFocused = false; - this._state.style = cursorStyle; - this._state.width = this._cell.getWidth(); - return; - } - - // Don't draw the cursor if it's blinking - if (this._cursorBlinkStateManager && !this._cursorBlinkStateManager.isCursorVisible) { - this._clearCursor(); - return; - } - - if (this._state) { - // The cursor is already in the correct spot, don't redraw - if (this._state.x === cursorX && - this._state.y === viewportRelativeCursorY && - this._state.isFocused === this._coreBrowserService.isFocused && - this._state.style === this._optionsService.rawOptions.cursorStyle && - this._state.width === this._cell.getWidth()) { - return; - } - this._clearCursor(); - } - - this._ctx.save(); - this._cursorRenderers[this._optionsService.rawOptions.cursorStyle || 'block'](cursorX, viewportRelativeCursorY, this._cell); - this._ctx.restore(); - - this._state.x = cursorX; - this._state.y = viewportRelativeCursorY; - this._state.isFocused = false; - this._state.style = this._optionsService.rawOptions.cursorStyle; - this._state.width = this._cell.getWidth(); - } - - private _clearCursor(): void { - if (this._state) { - // Avoid potential rounding errors when device pixel ratio is less than 1 - if (window.devicePixelRatio < 1) { - this._clearAll(); - } else { - this._clearCells(this._state.x, this._state.y, this._state.width, 1); - } - this._state = { - x: 0, - y: 0, - isFocused: false, - style: '', - width: 0 - }; - } - } - - private _renderBarCursor(x: number, y: number, cell: ICellData): void { - this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor.css; - this._fillLeftLineAtCell(x, y, this._optionsService.rawOptions.cursorWidth); - this._ctx.restore(); - } - - private _renderBlockCursor(x: number, y: number, cell: ICellData): void { - this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor.css; - this._fillCells(x, y, cell.getWidth(), 1); - this._ctx.fillStyle = this._colors.cursorAccent.css; - this._fillCharTrueColor(cell, x, y); - this._ctx.restore(); - } - - private _renderUnderlineCursor(x: number, y: number, cell: ICellData): void { - this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor.css; - this._fillBottomLineAtCells(x, y); - this._ctx.restore(); - } - - private _renderBlurCursor(x: number, y: number, cell: ICellData): void { - this._ctx.save(); - this._ctx.strokeStyle = this._colors.cursor.css; - this._strokeRectAtCell(x, y, cell.getWidth(), 1); - this._ctx.restore(); - } -} - -class CursorBlinkStateManager { - public isCursorVisible: boolean; - - private _animationFrame: number | undefined; - private _blinkStartTimeout: number | undefined; - private _blinkInterval: number | undefined; - - /** - * The time at which the animation frame was restarted, this is used on the - * next render to restart the timers so they don't need to restart the timers - * multiple times over a short period. - */ - private _animationTimeRestarted: number | undefined; - - constructor( - isFocused: boolean, - private _renderCallback: () => void - ) { - this.isCursorVisible = true; - if (isFocused) { - this._restartInterval(); - } - } - - public get isPaused(): boolean { return !(this._blinkStartTimeout || this._blinkInterval); } - - public dispose(): void { - if (this._blinkInterval) { - window.clearInterval(this._blinkInterval); - this._blinkInterval = undefined; - } - if (this._blinkStartTimeout) { - window.clearTimeout(this._blinkStartTimeout); - this._blinkStartTimeout = undefined; - } - if (this._animationFrame) { - window.cancelAnimationFrame(this._animationFrame); - this._animationFrame = undefined; - } - } - - public restartBlinkAnimation(): void { - if (this.isPaused) { - return; - } - // Save a timestamp so that the restart can be done on the next interval - this._animationTimeRestarted = Date.now(); - // Force a cursor render to ensure it's visible and in the correct position - this.isCursorVisible = true; - if (!this._animationFrame) { - this._animationFrame = window.requestAnimationFrame(() => { - this._renderCallback(); - this._animationFrame = undefined; - }); - } - } - - private _restartInterval(timeToStart: number = BLINK_INTERVAL): void { - // Clear any existing interval - if (this._blinkInterval) { - window.clearInterval(this._blinkInterval); - this._blinkInterval = undefined; - } - - // Setup the initial timeout which will hide the cursor, this is done before - // the regular interval is setup in order to support restarting the blink - // animation in a lightweight way (without thrashing clearInterval and - // setInterval). - this._blinkStartTimeout = window.setTimeout(() => { - // Check if another animation restart was requested while this was being - // started - if (this._animationTimeRestarted) { - const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); - this._animationTimeRestarted = undefined; - if (time > 0) { - this._restartInterval(time); - return; - } - } - - // Hide the cursor - this.isCursorVisible = false; - this._animationFrame = window.requestAnimationFrame(() => { - this._renderCallback(); - this._animationFrame = undefined; - }); - - // Setup the blink interval - this._blinkInterval = window.setInterval(() => { - // Adjust the animation time if it was restarted - if (this._animationTimeRestarted) { - // calc time diff - // Make restart interval do a setTimeout initially? - const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); - this._animationTimeRestarted = undefined; - this._restartInterval(time); - return; - } - - // Invert visibility and render - this.isCursorVisible = !this.isCursorVisible; - this._animationFrame = window.requestAnimationFrame(() => { - this._renderCallback(); - this._animationFrame = undefined; - }); - }, BLINK_INTERVAL); - }, timeToStart); - } - - public pause(): void { - this.isCursorVisible = true; - if (this._blinkInterval) { - window.clearInterval(this._blinkInterval); - this._blinkInterval = undefined; - } - if (this._blinkStartTimeout) { - window.clearTimeout(this._blinkStartTimeout); - this._blinkStartTimeout = undefined; - } - if (this._animationFrame) { - window.cancelAnimationFrame(this._animationFrame); - this._animationFrame = undefined; - } - } - - public resume(): void { - // Clear out any existing timers just in case - this.pause(); - - this._animationTimeRestarted = undefined; - this._restartInterval(); - this.restartBlinkAnimation(); - } -} diff --git a/node_modules/xterm/src/browser/renderer/CustomGlyphs.ts b/node_modules/xterm/src/browser/renderer/CustomGlyphs.ts deleted file mode 100644 index 7756279..0000000 --- a/node_modules/xterm/src/browser/renderer/CustomGlyphs.ts +++ /dev/null @@ -1,563 +0,0 @@ -/** - * Copyright (c) 2021 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { throwIfFalsy } from 'browser/renderer/RendererUtils'; - -interface IBlockVector { - x: number; - y: number; - w: number; - h: number; -} - -export const blockElementDefinitions: { [index: string]: IBlockVector[] | undefined } = { - // Block elements (0x2580-0x2590) - '▀': [{ x: 0, y: 0, w: 8, h: 4 }], // UPPER HALF BLOCK - '▁': [{ x: 0, y: 7, w: 8, h: 1 }], // LOWER ONE EIGHTH BLOCK - '▂': [{ x: 0, y: 6, w: 8, h: 2 }], // LOWER ONE QUARTER BLOCK - '▃': [{ x: 0, y: 5, w: 8, h: 3 }], // LOWER THREE EIGHTHS BLOCK - '▄': [{ x: 0, y: 4, w: 8, h: 4 }], // LOWER HALF BLOCK - '▅': [{ x: 0, y: 3, w: 8, h: 5 }], // LOWER FIVE EIGHTHS BLOCK - '▆': [{ x: 0, y: 2, w: 8, h: 6 }], // LOWER THREE QUARTERS BLOCK - '▇': [{ x: 0, y: 1, w: 8, h: 7 }], // LOWER SEVEN EIGHTHS BLOCK - '█': [{ x: 0, y: 0, w: 8, h: 8 }], // FULL BLOCK - '▉': [{ x: 0, y: 0, w: 7, h: 8 }], // LEFT SEVEN EIGHTHS BLOCK - '▊': [{ x: 0, y: 0, w: 6, h: 8 }], // LEFT THREE QUARTERS BLOCK - '▋': [{ x: 0, y: 0, w: 5, h: 8 }], // LEFT FIVE EIGHTHS BLOCK - '▌': [{ x: 0, y: 0, w: 4, h: 8 }], // LEFT HALF BLOCK - '▍': [{ x: 0, y: 0, w: 3, h: 8 }], // LEFT THREE EIGHTHS BLOCK - '▎': [{ x: 0, y: 0, w: 2, h: 8 }], // LEFT ONE QUARTER BLOCK - '▏': [{ x: 0, y: 0, w: 1, h: 8 }], // LEFT ONE EIGHTH BLOCK - '▐': [{ x: 4, y: 0, w: 4, h: 8 }], // RIGHT HALF BLOCK - - // Block elements (0x2594-0x2595) - '▔': [{ x: 0, y: 0, w: 9, h: 1 }], // UPPER ONE EIGHTH BLOCK - '▕': [{ x: 7, y: 0, w: 1, h: 8 }], // RIGHT ONE EIGHTH BLOCK - - // Terminal graphic characters (0x2596-0x259F) - '▖': [{ x: 0, y: 4, w: 4, h: 4 }], // QUADRANT LOWER LEFT - '▗': [{ x: 4, y: 4, w: 4, h: 4 }], // QUADRANT LOWER RIGHT - '▘': [{ x: 0, y: 0, w: 4, h: 4 }], // QUADRANT UPPER LEFT - '▙': [{ x: 0, y: 0, w: 4, h: 8 }, { x: 0, y: 4, w: 8, h: 4 }], // QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT - '▚': [{ x: 0, y: 0, w: 4, h: 4 }, { x: 4, y: 4, w: 4, h: 4 }], // QUADRANT UPPER LEFT AND LOWER RIGHT - '▛': [{ x: 0, y: 0, w: 4, h: 8 }, { x: 0, y: 0, w: 4, h: 8 }], // QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT - '▜': [{ x: 0, y: 0, w: 8, h: 4 }, { x: 4, y: 0, w: 4, h: 8 }], // QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT - '▝': [{ x: 4, y: 0, w: 4, h: 4 }], // QUADRANT UPPER RIGHT - '▞': [{ x: 4, y: 0, w: 4, h: 4 }, { x: 0, y: 4, w: 4, h: 4 }], // QUADRANT UPPER RIGHT AND LOWER LEFT - '▟': [{ x: 4, y: 0, w: 4, h: 8 }, { x: 0, y: 4, w: 8, h: 4 }], // QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT - - // VERTICAL ONE EIGHTH BLOCK-2 through VERTICAL ONE EIGHTH BLOCK-7 - '\u{1FB70}': [{ x: 1, y: 0, w: 1, h: 8 }], - '\u{1FB71}': [{ x: 2, y: 0, w: 1, h: 8 }], - '\u{1FB72}': [{ x: 3, y: 0, w: 1, h: 8 }], - '\u{1FB73}': [{ x: 4, y: 0, w: 1, h: 8 }], - '\u{1FB74}': [{ x: 5, y: 0, w: 1, h: 8 }], - '\u{1FB75}': [{ x: 6, y: 0, w: 1, h: 8 }], - - // HORIZONTAL ONE EIGHTH BLOCK-2 through HORIZONTAL ONE EIGHTH BLOCK-7 - '\u{1FB76}': [{ x: 0, y: 1, w: 8, h: 1 }], - '\u{1FB77}': [{ x: 0, y: 2, w: 8, h: 1 }], - '\u{1FB78}': [{ x: 0, y: 3, w: 8, h: 1 }], - '\u{1FB79}': [{ x: 0, y: 4, w: 8, h: 1 }], - '\u{1FB7A}': [{ x: 0, y: 5, w: 8, h: 1 }], - '\u{1FB7B}': [{ x: 0, y: 6, w: 8, h: 1 }], - - // LEFT AND LOWER ONE EIGHTH BLOCK - '\u{1FB7C}': [{ x: 0, y: 0, w: 1, h: 8 }, { x: 0, y: 7, w: 8, h: 1 }], - // LEFT AND UPPER ONE EIGHTH BLOCK - '\u{1FB7D}': [{ x: 0, y: 0, w: 1, h: 8 }, { x: 0, y: 0, w: 8, h: 1 }], - // RIGHT AND UPPER ONE EIGHTH BLOCK - '\u{1FB7E}': [{ x: 7, y: 0, w: 1, h: 8 }, { x: 0, y: 0, w: 8, h: 1 }], - // RIGHT AND LOWER ONE EIGHTH BLOCK - '\u{1FB7F}': [{ x: 7, y: 0, w: 1, h: 8 }, { x: 0, y: 7, w: 8, h: 1 }], - // UPPER AND LOWER ONE EIGHTH BLOCK - '\u{1FB80}': [{ x: 0, y: 0, w: 8, h: 1 }, { x: 0, y: 7, w: 8, h: 1 }], - // HORIZONTAL ONE EIGHTH BLOCK-1358 - '\u{1FB81}': [{ x: 0, y: 0, w: 8, h: 1 }, { x: 0, y: 2, w: 8, h: 1 }, { x: 0, y: 4, w: 8, h: 1 }, { x: 0, y: 7, w: 8, h: 1 }], - - // UPPER ONE QUARTER BLOCK - '\u{1FB82}': [{ x: 0, y: 0, w: 8, h: 2 }], - // UPPER THREE EIGHTHS BLOCK - '\u{1FB83}': [{ x: 0, y: 0, w: 8, h: 3 }], - // UPPER FIVE EIGHTHS BLOCK - '\u{1FB84}': [{ x: 0, y: 0, w: 8, h: 5 }], - // UPPER THREE QUARTERS BLOCK - '\u{1FB85}': [{ x: 0, y: 0, w: 8, h: 6 }], - // UPPER SEVEN EIGHTHS BLOCK - '\u{1FB86}': [{ x: 0, y: 0, w: 8, h: 7 }], - - // RIGHT ONE QUARTER BLOCK - '\u{1FB87}': [{ x: 6, y: 0, w: 2, h: 8 }], - // RIGHT THREE EIGHTHS B0OCK - '\u{1FB88}': [{ x: 5, y: 0, w: 3, h: 8 }], - // RIGHT FIVE EIGHTHS BL0CK - '\u{1FB89}': [{ x: 3, y: 0, w: 5, h: 8 }], - // RIGHT THREE QUARTERS 0LOCK - '\u{1FB8A}': [{ x: 2, y: 0, w: 6, h: 8 }], - // RIGHT SEVEN EIGHTHS B0OCK - '\u{1FB8B}': [{ x: 1, y: 0, w: 7, h: 8 }], - - // CHECKER BOARD FILL - '\u{1FB95}': [ - { x: 0, y: 0, w: 2, h: 2 }, { x: 4, y: 0, w: 2, h: 2 }, - { x: 2, y: 2, w: 2, h: 2 }, { x: 6, y: 2, w: 2, h: 2 }, - { x: 0, y: 4, w: 2, h: 2 }, { x: 4, y: 4, w: 2, h: 2 }, - { x: 2, y: 6, w: 2, h: 2 }, { x: 6, y: 6, w: 2, h: 2 } - ], - // INVERSE CHECKER BOARD FILL - '\u{1FB96}': [ - { x: 2, y: 0, w: 2, h: 2 }, { x: 6, y: 0, w: 2, h: 2 }, - { x: 0, y: 2, w: 2, h: 2 }, { x: 4, y: 2, w: 2, h: 2 }, - { x: 2, y: 4, w: 2, h: 2 }, { x: 6, y: 4, w: 2, h: 2 }, - { x: 0, y: 6, w: 2, h: 2 }, { x: 4, y: 6, w: 2, h: 2 } - ], - // HEAVY HORIZONTAL FILL (upper middle and lower one quarter block) - '\u{1FB97}': [{ x: 0, y: 2, w: 8, h: 2 }, { x: 0, y: 6, w: 8, h: 2 }] -}; - -type PatternDefinition = number[][]; - -/** - * Defines the repeating pattern used by special characters, the pattern is made up of a 2d array of - * pixel values to be filled (1) or not filled (0). - */ -const patternCharacterDefinitions: { [key: string]: PatternDefinition | undefined } = { - // Shade characters (0x2591-0x2593) - '░': [ // LIGHT SHADE (25%) - [1, 0, 0, 0], - [0, 0, 0, 0], - [0, 0, 1, 0], - [0, 0, 0, 0] - ], - '▒': [ // MEDIUM SHADE (50%) - [1, 0], - [0, 0], - [0, 1], - [0, 0] - ], - '▓': [ // DARK SHADE (75%) - [0, 1], - [1, 1], - [1, 0], - [1, 1] - ] -}; - -const enum Shapes { - /** │ */ TOP_TO_BOTTOM = 'M.5,0 L.5,1', - /** ─ */ LEFT_TO_RIGHT = 'M0,.5 L1,.5', - - /** └ */ TOP_TO_RIGHT = 'M.5,0 L.5,.5 L1,.5', - /** ┘ */ TOP_TO_LEFT = 'M.5,0 L.5,.5 L0,.5', - /** ┐ */ LEFT_TO_BOTTOM = 'M0,.5 L.5,.5 L.5,1', - /** ┌ */ RIGHT_TO_BOTTOM = 'M0.5,1 L.5,.5 L1,.5', - - /** ╵ */ MIDDLE_TO_TOP = 'M.5,.5 L.5,0', - /** ╴ */ MIDDLE_TO_LEFT = 'M.5,.5 L0,.5', - /** ╶ */ MIDDLE_TO_RIGHT = 'M.5,.5 L1,.5', - /** ╷ */ MIDDLE_TO_BOTTOM = 'M.5,.5 L.5,1', - - /** ┴ */ T_TOP = 'M0,.5 L1,.5 M.5,.5 L.5,0', - /** ┤ */ T_LEFT = 'M.5,0 L.5,1 M.5,.5 L0,.5', - /** ├ */ T_RIGHT = 'M.5,0 L.5,1 M.5,.5 L1,.5', - /** ┬ */ T_BOTTOM = 'M0,.5 L1,.5 M.5,.5 L.5,1', - - /** ┼ */ CROSS = 'M0,.5 L1,.5 M.5,0 L.5,1', - - /** ╌ */ TWO_DASHES_HORIZONTAL = 'M.1,.5 L.4,.5 M.6,.5 L.9,.5', // .2 empty, .3 filled - /** ┄ */ THREE_DASHES_HORIZONTAL = 'M.0667,.5 L.2667,.5 M.4,.5 L.6,.5 M.7333,.5 L.9333,.5', // .1333 empty, .2 filled - /** ┉ */ FOUR_DASHES_HORIZONTAL = 'M.05,.5 L.2,.5 M.3,.5 L.45,.5 M.55,.5 L.7,.5 M.8,.5 L.95,.5', // .1 empty, .15 filled - /** ╎ */ TWO_DASHES_VERTICAL = 'M.5,.1 L.5,.4 M.5,.6 L.5,.9', - /** ┆ */ THREE_DASHES_VERTICAL = 'M.5,.0667 L.5,.2667 M.5,.4 L.5,.6 M.5,.7333 L.5,.9333', - /** ┊ */ FOUR_DASHES_VERTICAL = 'M.5,.05 L.5,.2 M.5,.3 L.5,.45 L.5,.55 M.5,.7 L.5,.95', -} - -const enum Style { - NORMAL = 1, - BOLD = 3 -} - -/** - * This contains the definitions of all box drawing characters in the format of SVG paths (ie. the - * svg d attribute). - */ -export const boxDrawingDefinitions: { [character: string]: { [fontWeight: number]: string | ((xp: number, yp: number) => string) } | undefined } = { - // Uniform normal and bold - '─': { [Style.NORMAL]: Shapes.LEFT_TO_RIGHT }, - '━': { [Style.BOLD]: Shapes.LEFT_TO_RIGHT }, - '│': { [Style.NORMAL]: Shapes.TOP_TO_BOTTOM }, - '┃': { [Style.BOLD]: Shapes.TOP_TO_BOTTOM }, - '┌': { [Style.NORMAL]: Shapes.RIGHT_TO_BOTTOM }, - '┏': { [Style.BOLD]: Shapes.RIGHT_TO_BOTTOM }, - '┐': { [Style.NORMAL]: Shapes.LEFT_TO_BOTTOM }, - '┓': { [Style.BOLD]: Shapes.LEFT_TO_BOTTOM }, - '└': { [Style.NORMAL]: Shapes.TOP_TO_RIGHT }, - '┗': { [Style.BOLD]: Shapes.TOP_TO_RIGHT }, - '┘': { [Style.NORMAL]: Shapes.TOP_TO_LEFT }, - '┛': { [Style.BOLD]: Shapes.TOP_TO_LEFT }, - '├': { [Style.NORMAL]: Shapes.T_RIGHT }, - '┣': { [Style.BOLD]: Shapes.T_RIGHT }, - '┤': { [Style.NORMAL]: Shapes.T_LEFT }, - '┫': { [Style.BOLD]: Shapes.T_LEFT }, - '┬': { [Style.NORMAL]: Shapes.T_BOTTOM }, - '┳': { [Style.BOLD]: Shapes.T_BOTTOM }, - '┴': { [Style.NORMAL]: Shapes.T_TOP }, - '┻': { [Style.BOLD]: Shapes.T_TOP }, - '┼': { [Style.NORMAL]: Shapes.CROSS }, - '╋': { [Style.BOLD]: Shapes.CROSS }, - '╴': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT }, - '╸': { [Style.BOLD]: Shapes.MIDDLE_TO_LEFT }, - '╵': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP }, - '╹': { [Style.BOLD]: Shapes.MIDDLE_TO_TOP }, - '╶': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT }, - '╺': { [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT }, - '╷': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM }, - '╻': { [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM }, - - // Double border - '═': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp}` }, - '║': { [Style.NORMAL]: (xp, yp) => `M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1` }, - '╒': { [Style.NORMAL]: (xp, yp) => `M.5,1 L.5,${.5 - yp} L1,${.5 - yp} M.5,${.5 + yp} L1,${.5 + yp}` }, - '╓': { [Style.NORMAL]: (xp, yp) => `M${.5 - xp},1 L${.5 - xp},.5 L1,.5 M${.5 + xp},.5 L${.5 + xp},1` }, - '╔': { [Style.NORMAL]: (xp, yp) => `M1,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1` }, - '╕': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L.5,${.5 - yp} L.5,1 M0,${.5 + yp} L.5,${.5 + yp}` }, - '╖': { [Style.NORMAL]: (xp, yp) => `M${.5 + xp},1 L${.5 + xp},.5 L0,.5 M${.5 - xp},.5 L${.5 - xp},1` }, - '╗': { [Style.NORMAL]: (xp, yp) => `M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M0,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},1` }, - '╘': { [Style.NORMAL]: (xp, yp) => `M.5,0 L.5,${.5 + yp} L1,${.5 + yp} M.5,${.5 - yp} L1,${.5 - yp}` }, - '╙': { [Style.NORMAL]: (xp, yp) => `M1,.5 L${.5 - xp},.5 L${.5 - xp},0 M${.5 + xp},.5 L${.5 + xp},0` }, - '╚': { [Style.NORMAL]: (xp, yp) => `M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0 M1,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},0` }, - '╛': { [Style.NORMAL]: (xp, yp) => `M0,${.5 + yp} L.5,${.5 + yp} L.5,0 M0,${.5 - yp} L.5,${.5 - yp}` }, - '╜': { [Style.NORMAL]: (xp, yp) => `M0,.5 L${.5 + xp},.5 L${.5 + xp},0 M${.5 - xp},.5 L${.5 - xp},0` }, - '╝': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0 M0,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},0` }, - '╞': { [Style.NORMAL]: (xp, yp) => `${Shapes.TOP_TO_BOTTOM} M.5,${.5 - yp} L1,${.5 - yp} M.5,${.5 + yp} L1,${.5 + yp}` }, - '╟': { [Style.NORMAL]: (xp, yp) => `M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1 M${.5 + xp},.5 L1,.5` }, - '╠': { [Style.NORMAL]: (xp, yp) => `M${.5 - xp},0 L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1 M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0` }, - '╡': { [Style.NORMAL]: (xp, yp) => `${Shapes.TOP_TO_BOTTOM} M0,${.5 - yp} L.5,${.5 - yp} M0,${.5 + yp} L.5,${.5 + yp}` }, - '╢': { [Style.NORMAL]: (xp, yp) => `M0,.5 L${.5 - xp},.5 M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1` }, - '╣': { [Style.NORMAL]: (xp, yp) => `M${.5 + xp},0 L${.5 + xp},1 M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0` }, - '╤': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp} M.5,${.5 + yp} L.5,1` }, - '╥': { [Style.NORMAL]: (xp, yp) => `${Shapes.LEFT_TO_RIGHT} M${.5 - xp},.5 L${.5 - xp},1 M${.5 + xp},.5 L${.5 + xp},1` }, - '╦': { [Style.NORMAL]: (xp, yp) => `M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1` }, - '╧': { [Style.NORMAL]: (xp, yp) => `M.5,0 L.5,${.5 - yp} M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp}` }, - '╨': { [Style.NORMAL]: (xp, yp) => `${Shapes.LEFT_TO_RIGHT} M${.5 - xp},.5 L${.5 - xp},0 M${.5 + xp},.5 L${.5 + xp},0` }, - '╩': { [Style.NORMAL]: (xp, yp) => `M0,${.5 + yp} L1,${.5 + yp} M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0 M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0` }, - '╪': { [Style.NORMAL]: (xp, yp) => `${Shapes.TOP_TO_BOTTOM} M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp}` }, - '╫': { [Style.NORMAL]: (xp, yp) => `${Shapes.LEFT_TO_RIGHT} M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1` }, - '╬': { [Style.NORMAL]: (xp, yp) => `M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1 M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0 M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0` }, - - // Diagonal - '╱': { [Style.NORMAL]: 'M1,0 L0,1' }, - '╲': { [Style.NORMAL]: 'M0,0 L1,1' }, - '╳': { [Style.NORMAL]: 'M1,0 L0,1 M0,0 L1,1' }, - - // Mixed weight - '╼': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT }, - '╽': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM }, - '╾': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT }, - '╿': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_TOP }, - '┍': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT }, - '┎': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM }, - '┑': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT }, - '┒': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM }, - '┕': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT }, - '┖': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_TOP }, - '┙': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT }, - '┚': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_TOP }, - '┝': { [Style.NORMAL]: Shapes.TOP_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT }, - '┞': { [Style.NORMAL]: Shapes.RIGHT_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_TOP }, - '┟': { [Style.NORMAL]: Shapes.TOP_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM }, - '┠': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.TOP_TO_BOTTOM }, - '┡': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.TOP_TO_RIGHT }, - '┢': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.RIGHT_TO_BOTTOM }, - '┥': { [Style.NORMAL]: Shapes.TOP_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT }, - '┦': { [Style.NORMAL]: Shapes.LEFT_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_TOP }, - '┧': { [Style.NORMAL]: Shapes.TOP_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM }, - '┨': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.TOP_TO_BOTTOM }, - '┩': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.TOP_TO_LEFT }, - '┪': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.LEFT_TO_BOTTOM }, - '┭': { [Style.NORMAL]: Shapes.RIGHT_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT }, - '┮': { [Style.NORMAL]: Shapes.LEFT_TO_BOTTOM, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT }, - '┯': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: Shapes.LEFT_TO_RIGHT }, - '┰': { [Style.NORMAL]: Shapes.LEFT_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM }, - '┱': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.LEFT_TO_BOTTOM }, - '┲': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.RIGHT_TO_BOTTOM }, - '┵': { [Style.NORMAL]: Shapes.TOP_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT }, - '┶': { [Style.NORMAL]: Shapes.TOP_TO_LEFT, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT }, - '┷': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: Shapes.LEFT_TO_RIGHT }, - '┸': { [Style.NORMAL]: Shapes.LEFT_TO_RIGHT, [Style.BOLD]: Shapes.MIDDLE_TO_TOP }, - '┹': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: Shapes.TOP_TO_LEFT }, - '┺': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: Shapes.TOP_TO_RIGHT }, - '┽': { [Style.NORMAL]: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_RIGHT}`, [Style.BOLD]: Shapes.MIDDLE_TO_LEFT }, - '┾': { [Style.NORMAL]: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_LEFT}`, [Style.BOLD]: Shapes.MIDDLE_TO_RIGHT }, - '┿': { [Style.NORMAL]: Shapes.TOP_TO_BOTTOM, [Style.BOLD]: Shapes.LEFT_TO_RIGHT }, - '╀': { [Style.NORMAL]: `${Shapes.LEFT_TO_RIGHT} ${Shapes.MIDDLE_TO_BOTTOM}`, [Style.BOLD]: Shapes.MIDDLE_TO_TOP }, - '╁': { [Style.NORMAL]: `${Shapes.MIDDLE_TO_TOP} ${Shapes.LEFT_TO_RIGHT}`, [Style.BOLD]: Shapes.MIDDLE_TO_BOTTOM }, - '╂': { [Style.NORMAL]: Shapes.LEFT_TO_RIGHT, [Style.BOLD]: Shapes.TOP_TO_BOTTOM }, - '╃': { [Style.NORMAL]: Shapes.RIGHT_TO_BOTTOM, [Style.BOLD]: Shapes.TOP_TO_LEFT }, - '╄': { [Style.NORMAL]: Shapes.LEFT_TO_BOTTOM, [Style.BOLD]: Shapes.TOP_TO_RIGHT }, - '╅': { [Style.NORMAL]: Shapes.TOP_TO_RIGHT, [Style.BOLD]: Shapes.LEFT_TO_BOTTOM }, - '╆': { [Style.NORMAL]: Shapes.TOP_TO_LEFT, [Style.BOLD]: Shapes.RIGHT_TO_BOTTOM }, - '╇': { [Style.NORMAL]: Shapes.MIDDLE_TO_BOTTOM, [Style.BOLD]: `${Shapes.MIDDLE_TO_TOP} ${Shapes.LEFT_TO_RIGHT}` }, - '╈': { [Style.NORMAL]: Shapes.MIDDLE_TO_TOP, [Style.BOLD]: `${Shapes.LEFT_TO_RIGHT} ${Shapes.MIDDLE_TO_BOTTOM}` }, - '╉': { [Style.NORMAL]: Shapes.MIDDLE_TO_RIGHT, [Style.BOLD]: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_LEFT}` }, - '╊': { [Style.NORMAL]: Shapes.MIDDLE_TO_LEFT, [Style.BOLD]: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_RIGHT}` }, - - // Dashed - '╌': { [Style.NORMAL]: Shapes.TWO_DASHES_HORIZONTAL }, - '╍': { [Style.BOLD]: Shapes.TWO_DASHES_HORIZONTAL }, - '┄': { [Style.NORMAL]: Shapes.THREE_DASHES_HORIZONTAL }, - '┅': { [Style.BOLD]: Shapes.THREE_DASHES_HORIZONTAL }, - '┈': { [Style.NORMAL]: Shapes.FOUR_DASHES_HORIZONTAL }, - '┉': { [Style.BOLD]: Shapes.FOUR_DASHES_HORIZONTAL }, - '╎': { [Style.NORMAL]: Shapes.TWO_DASHES_VERTICAL }, - '╏': { [Style.BOLD]: Shapes.TWO_DASHES_VERTICAL }, - '┆': { [Style.NORMAL]: Shapes.THREE_DASHES_VERTICAL }, - '┇': { [Style.BOLD]: Shapes.THREE_DASHES_VERTICAL }, - '┊': { [Style.NORMAL]: Shapes.FOUR_DASHES_VERTICAL }, - '┋': { [Style.BOLD]: Shapes.FOUR_DASHES_VERTICAL }, - - // Curved - '╭': { [Style.NORMAL]: 'C.5,1,.5,.5,1,.5' }, - '╮': { [Style.NORMAL]: 'C.5,1,.5,.5,0,.5' }, - '╯': { [Style.NORMAL]: 'C.5,0,.5,.5,0,.5' }, - '╰': { [Style.NORMAL]: 'C.5,0,.5,.5,1,.5' } -}; - -/** - * Try drawing a custom block element or box drawing character, returning whether it was - * successfully drawn. - */ -export function tryDrawCustomChar( - ctx: CanvasRenderingContext2D, - c: string, - xOffset: number, - yOffset: number, - scaledCellWidth: number, - scaledCellHeight: number -): boolean { - const blockElementDefinition = blockElementDefinitions[c]; - if (blockElementDefinition) { - drawBlockElementChar(ctx, blockElementDefinition, xOffset, yOffset, scaledCellWidth, scaledCellHeight); - return true; - } - - const patternDefinition = patternCharacterDefinitions[c]; - if (patternDefinition) { - drawPatternChar(ctx, patternDefinition, xOffset, yOffset, scaledCellWidth, scaledCellHeight); - return true; - } - - const boxDrawingDefinition = boxDrawingDefinitions[c]; - if (boxDrawingDefinition) { - drawBoxDrawingChar(ctx, boxDrawingDefinition, xOffset, yOffset, scaledCellWidth, scaledCellHeight); - return true; - } - - return false; -} - -function drawBlockElementChar( - ctx: CanvasRenderingContext2D, - charDefinition: IBlockVector[], - xOffset: number, - yOffset: number, - scaledCellWidth: number, - scaledCellHeight: number -): void { - for (let i = 0; i < charDefinition.length; i++) { - const box = charDefinition[i]; - const xEighth = scaledCellWidth / 8; - const yEighth = scaledCellHeight / 8; - ctx.fillRect( - xOffset + box.x * xEighth, - yOffset + box.y * yEighth, - box.w * xEighth, - box.h * yEighth - ); - } -} - -const cachedPatterns: Map<PatternDefinition, Map</* fillStyle */string, CanvasPattern>> = new Map(); - -function drawPatternChar( - ctx: CanvasRenderingContext2D, - charDefinition: number[][], - xOffset: number, - yOffset: number, - scaledCellWidth: number, - scaledCellHeight: number -): void { - let patternSet = cachedPatterns.get(charDefinition); - if (!patternSet) { - patternSet = new Map(); - cachedPatterns.set(charDefinition, patternSet); - } - const fillStyle = ctx.fillStyle; - if (typeof fillStyle !== 'string') { - throw new Error(`Unexpected fillStyle type "${fillStyle}"`); - } - let pattern = patternSet.get(fillStyle); - if (!pattern) { - const width = charDefinition[0].length; - const height = charDefinition.length; - const tmpCanvas = document.createElement('canvas'); - tmpCanvas.width = width; - tmpCanvas.height = height; - const tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d')); - const imageData = new ImageData(width, height); - - // Extract rgba from fillStyle - let r: number; - let g: number; - let b: number; - let a: number; - if (fillStyle.startsWith('#')) { - r = parseInt(fillStyle.substr(1, 2), 16); - g = parseInt(fillStyle.substr(3, 2), 16); - b = parseInt(fillStyle.substr(5, 2), 16); - a = fillStyle.length > 7 && parseInt(fillStyle.substr(7, 2), 16) || 1; - } else if (fillStyle.startsWith('rgba')) { - ([r, g, b, a] = fillStyle.substring(5, fillStyle.length - 1).split(',').map(e => parseFloat(e))); - } else { - throw new Error(`Unexpected fillStyle color format "${fillStyle}" when drawing pattern glyph`); - } - - for (let y = 0; y < height; y++) { - for (let x = 0; x < width; x++) { - imageData.data[(y * width + x) * 4 ] = r; - imageData.data[(y * width + x) * 4 + 1] = g; - imageData.data[(y * width + x) * 4 + 2] = b; - imageData.data[(y * width + x) * 4 + 3] = charDefinition[y][x] * (a * 255); - } - } - tmpCtx.putImageData(imageData, 0, 0); - pattern = throwIfFalsy(ctx.createPattern(tmpCanvas, null)); - patternSet.set(fillStyle, pattern); - } - ctx.fillStyle = pattern; - ctx.fillRect(xOffset, yOffset, scaledCellWidth, scaledCellHeight); -} - -/** - * Draws the following box drawing characters by mapping a subset of SVG d attribute instructions to - * canvas draw calls. - * - * Box styles: ┎┰┒┍┯┑╓╥╖╒╤╕ ┏┳┓┌┲┓┌┬┐┏┱┐ - * ┌─┬─┐ ┏━┳━┓ ╔═╦═╗ ┠╂┨┝┿┥╟╫╢╞╪╡ ┡╇┩├╊┫┢╈┪┣╉┤ - * │ │ │ ┃ ┃ ┃ ║ ║ ║ ┖┸┚┕┷┙╙╨╜╘╧╛ └┴┘└┺┛┗┻┛┗┹┘ - * ├─┼─┤ ┣━╋━┫ ╠═╬═╣ ┏┱┐┌┲┓┌┬┐┌┬┐ ┏┳┓┌┮┓┌┬┐┏┭┐ - * │ │ │ ┃ ┃ ┃ ║ ║ ║ ┡╃┤├╄┩├╆┪┢╅┤ ┞╀┦├┾┫┟╁┧┣┽┤ - * └─┴─┘ ┗━┻━┛ ╚═╩═╝ └┴┘└┴┘└┺┛┗┹┘ └┴┘└┶┛┗┻┛┗┵┘ - * - * Other: - * ╭─╮ ╲ ╱ ╷╻╎╏┆┇┊┋ ╺╾╴ ╌╌╌ ┄┄┄ ┈┈┈ - * │ │ ╳ ╽╿╎╏┆┇┊┋ ╶╼╸ ╍╍╍ ┅┅┅ ┉┉┉ - * ╰─╯ ╱ ╲ ╹╵╎╏┆┇┊┋ - * - * All box drawing characters: - * ─ ━ │ ┃ ┄ ┅ ┆ ┇ ┈ ┉ ┊ ┋ ┌ ┍ ┎ ┏ - * ┐ ┑ ┒ ┓ └ ┕ ┖ ┗ ┘ ┙ ┚ ┛ ├ ┝ ┞ ┟ - * ┠ ┡ ┢ ┣ ┤ ┥ ┦ ┧ ┨ ┩ ┪ ┫ ┬ ┭ ┮ ┯ - * ┰ ┱ ┲ ┳ ┴ ┵ ┶ ┷ ┸ ┹ ┺ ┻ ┼ ┽ ┾ ┿ - * ╀ ╁ ╂ ╃ ╄ ╅ ╆ ╇ ╈ ╉ ╊ ╋ ╌ ╍ ╎ ╏ - * ═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟ - * ╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ ╭ ╮ ╯ - * ╰ ╱ ╲ ╳ ╴ ╵ ╶ ╷ ╸ ╹ ╺ ╻ ╼ ╽ ╾ ╿ - * - * --- - * - * Box drawing alignment tests: █ - * ▉ - * ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳ - * ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳ - * ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳ - * ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳ - * ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎ - * ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏ - * ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█ - * - * Source: https://www.w3.org/2001/06/utf-8-test/UTF-8-demo.html - */ -function drawBoxDrawingChar( - ctx: CanvasRenderingContext2D, - charDefinition: { [fontWeight: number]: string | ((xp: number, yp: number) => string) }, - xOffset: number, - yOffset: number, - scaledCellWidth: number, - scaledCellHeight: number -): void { - ctx.strokeStyle = ctx.fillStyle; - for (const [fontWeight, instructions] of Object.entries(charDefinition)) { - ctx.beginPath(); - ctx.lineWidth = window.devicePixelRatio * Number.parseInt(fontWeight); - let actualInstructions: string; - if (typeof instructions === 'function') { - const xp = .15; - const yp = .15 / scaledCellHeight * scaledCellWidth; - actualInstructions = instructions(xp, yp); - } else { - actualInstructions = instructions; - } - for (const instruction of actualInstructions.split(' ')) { - const type = instruction[0]; - const f = svgToCanvasInstructionMap[type]; - if (!f) { - console.error(`Could not find drawing instructions for "${type}"`); - continue; - } - const args: string[] = instruction.substring(1).split(','); - if (!args[0] || !args[1]) { - continue; - } - f(ctx, translateArgs(args, scaledCellWidth, scaledCellHeight, xOffset, yOffset)); - } - ctx.stroke(); - ctx.closePath(); - } -} - -function clamp(value: number, max: number, min: number = 0): number { - return Math.max(Math.min(value, max), min); -} - -const svgToCanvasInstructionMap: { [index: string]: any } = { - 'C': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.bezierCurveTo(args[0], args[1], args[2], args[3], args[4], args[5]), - 'L': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.lineTo(args[0], args[1]), - 'M': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.moveTo(args[0], args[1]) -}; - -function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number): number[] { - const result = args.map(e => parseFloat(e) || parseInt(e)); - - if (result.length < 2) { - throw new Error('Too few arguments for instruction'); - } - - for (let x = 0; x < result.length; x += 2) { - // Translate from 0-1 to 0-cellWidth - result[x] *= cellWidth; - // Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp - // line at 100% devicePixelRatio - if (result[x] !== 0) { - result[x] = clamp(Math.round(result[x] + 0.5) - 0.5, cellWidth, 0); - } - // Apply the cell's offset (ie. x*cellWidth) - result[x] += xOffset; - } - - for (let y = 1; y < result.length; y += 2) { - // Translate from 0-1 to 0-cellHeight - result[y] *= cellHeight; - // Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp - // line at 100% devicePixelRatio - if (result[y] !== 0) { - result[y] = clamp(Math.round(result[y] + 0.5) - 0.5, cellHeight, 0); - } - // Apply the cell's offset (ie. x*cellHeight) - result[y] += yOffset; - } - - return result; -} diff --git a/node_modules/xterm/src/browser/renderer/GridCache.ts b/node_modules/xterm/src/browser/renderer/GridCache.ts deleted file mode 100644 index b48798d..0000000 --- a/node_modules/xterm/src/browser/renderer/GridCache.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -export class GridCache<T> { - public cache: (T | undefined)[][]; - - public constructor() { - this.cache = []; - } - - public resize(width: number, height: number): void { - for (let x = 0; x < width; x++) { - if (this.cache.length <= x) { - this.cache.push([]); - } - for (let y = this.cache[x].length; y < height; y++) { - this.cache[x].push(undefined); - } - this.cache[x].length = height; - } - this.cache.length = width; - } - - public clear(): void { - for (let x = 0; x < this.cache.length; x++) { - for (let y = 0; y < this.cache[x].length; y++) { - this.cache[x][y] = undefined; - } - } - } -} diff --git a/node_modules/xterm/src/browser/renderer/LinkRenderLayer.ts b/node_modules/xterm/src/browser/renderer/LinkRenderLayer.ts deleted file mode 100644 index 2492f92..0000000 --- a/node_modules/xterm/src/browser/renderer/LinkRenderLayer.ts +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { IRenderDimensions } from 'browser/renderer/Types'; -import { BaseRenderLayer } from './BaseRenderLayer'; -import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; -import { is256Color } from 'browser/renderer/atlas/CharAtlasUtils'; -import { IColorSet, ILinkifierEvent, ILinkifier, ILinkifier2 } from 'browser/Types'; -import { IBufferService, IOptionsService } from 'common/services/Services'; - -export class LinkRenderLayer extends BaseRenderLayer { - private _state: ILinkifierEvent | undefined; - - constructor( - container: HTMLElement, - zIndex: number, - colors: IColorSet, - rendererId: number, - linkifier: ILinkifier, - linkifier2: ILinkifier2, - @IBufferService bufferService: IBufferService, - @IOptionsService optionsService: IOptionsService - ) { - super(container, 'link', zIndex, true, colors, rendererId, bufferService, optionsService); - linkifier.onShowLinkUnderline(e => this._onShowLinkUnderline(e)); - linkifier.onHideLinkUnderline(e => this._onHideLinkUnderline(e)); - - linkifier2.onShowLinkUnderline(e => this._onShowLinkUnderline(e)); - linkifier2.onHideLinkUnderline(e => this._onHideLinkUnderline(e)); - } - - public resize(dim: IRenderDimensions): void { - super.resize(dim); - // Resizing the canvas discards the contents of the canvas so clear state - this._state = undefined; - } - - public reset(): void { - this._clearCurrentLink(); - } - - private _clearCurrentLink(): void { - if (this._state) { - this._clearCells(this._state.x1, this._state.y1, this._state.cols - this._state.x1, 1); - const middleRowCount = this._state.y2 - this._state.y1 - 1; - if (middleRowCount > 0) { - this._clearCells(0, this._state.y1 + 1, this._state.cols, middleRowCount); - } - this._clearCells(0, this._state.y2, this._state.x2, 1); - this._state = undefined; - } - } - - private _onShowLinkUnderline(e: ILinkifierEvent): void { - if (e.fg === INVERTED_DEFAULT_COLOR) { - this._ctx.fillStyle = this._colors.background.css; - } else if (e.fg && is256Color(e.fg)) { - // 256 color support - this._ctx.fillStyle = this._colors.ansi[e.fg].css; - } else { - this._ctx.fillStyle = this._colors.foreground.css; - } - - if (e.y1 === e.y2) { - // Single line link - this._fillBottomLineAtCells(e.x1, e.y1, e.x2 - e.x1); - } else { - // Multi-line link - this._fillBottomLineAtCells(e.x1, e.y1, e.cols - e.x1); - for (let y = e.y1 + 1; y < e.y2; y++) { - this._fillBottomLineAtCells(0, y, e.cols); - } - this._fillBottomLineAtCells(0, e.y2, e.x2); - } - this._state = e; - } - - private _onHideLinkUnderline(e: ILinkifierEvent): void { - this._clearCurrentLink(); - } -} diff --git a/node_modules/xterm/src/browser/renderer/Renderer.ts b/node_modules/xterm/src/browser/renderer/Renderer.ts deleted file mode 100644 index a58893b..0000000 --- a/node_modules/xterm/src/browser/renderer/Renderer.ts +++ /dev/null @@ -1,216 +0,0 @@ -/** - * 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 'browser/renderer/CursorRenderLayer'; -import { IRenderLayer, IRenderer, IRenderDimensions, IRequestRedrawEvent } from 'browser/renderer/Types'; -import { LinkRenderLayer } from 'browser/renderer/LinkRenderLayer'; -import { Disposable } from 'common/Lifecycle'; -import { IColorSet, ILinkifier, ILinkifier2 } from 'browser/Types'; -import { ICharSizeService } from 'browser/services/Services'; -import { IBufferService, IOptionsService, IInstantiationService } from 'common/services/Services'; -import { removeTerminalFromCache } from 'browser/renderer/atlas/CharAtlasCache'; -import { EventEmitter, IEvent } from 'common/EventEmitter'; -import { IDecorationOptions, IDecoration } from 'xterm'; - -let nextRendererId = 1; - -export class Renderer extends Disposable implements IRenderer { - private _id = nextRendererId++; - - private _renderLayers: IRenderLayer[]; - private _devicePixelRatio: number; - - public dimensions: IRenderDimensions; - - private _onRequestRedraw = new EventEmitter<IRequestRedrawEvent>(); - public get onRequestRedraw(): IEvent<IRequestRedrawEvent> { return this._onRequestRedraw.event; } - - constructor( - private _colors: IColorSet, - private readonly _screenElement: HTMLElement, - linkifier: ILinkifier, - linkifier2: ILinkifier2, - @IInstantiationService instantiationService: IInstantiationService, - @IBufferService private readonly _bufferService: IBufferService, - @ICharSizeService private readonly _charSizeService: ICharSizeService, - @IOptionsService private readonly _optionsService: IOptionsService - ) { - super(); - const allowTransparency = this._optionsService.rawOptions.allowTransparency; - this._renderLayers = [ - instantiationService.createInstance(TextRenderLayer, this._screenElement, 0, this._colors, allowTransparency, this._id), - instantiationService.createInstance(SelectionRenderLayer, this._screenElement, 1, this._colors, this._id), - instantiationService.createInstance(LinkRenderLayer, this._screenElement, 2, this._colors, this._id, linkifier, linkifier2), - instantiationService.createInstance(CursorRenderLayer, this._screenElement, 3, this._colors, this._id, this._onRequestRedraw) - ]; - this.dimensions = { - scaledCharWidth: 0, - scaledCharHeight: 0, - scaledCellWidth: 0, - scaledCellHeight: 0, - scaledCharLeft: 0, - scaledCharTop: 0, - scaledCanvasWidth: 0, - scaledCanvasHeight: 0, - canvasWidth: 0, - canvasHeight: 0, - actualCellWidth: 0, - actualCellHeight: 0 - }; - this._devicePixelRatio = window.devicePixelRatio; - this._updateDimensions(); - this.onOptionsChanged(); - } - - public dispose(): void { - for (const l of this._renderLayers) { - l.dispose(); - } - super.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._bufferService.cols, this._bufferService.rows); - } - } - - public setColors(colors: IColorSet): void { - this._colors = colors; - // Clear layers and force a full render - for (const l of this._renderLayers) { - l.setColors(this._colors); - l.reset(); - } - } - - public onResize(cols: number, rows: number): void { - // Update character and canvas dimensions - this._updateDimensions(); - - // Resize all render layers - for (const l of this._renderLayers) { - l.resize(this.dimensions); - } - - // Resize the screen - this._screenElement.style.width = `${this.dimensions.canvasWidth}px`; - this._screenElement.style.height = `${this.dimensions.canvasHeight}px`; - } - - public onCharSizeChanged(): void { - this.onResize(this._bufferService.cols, this._bufferService.rows); - } - - public onBlur(): void { - this._runOperation(l => l.onBlur()); - } - - public onFocus(): void { - this._runOperation(l => l.onFocus()); - } - - public onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, 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 { - for (const l of this._renderLayers) { - 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 { - for (const l of this._renderLayers) { - l.onGridChanged(start, end); - } - } - - public clearTextureAtlas(): void { - for (const layer of this._renderLayers) { - layer.clearTextureAtlas(); - } - } - - /** - * 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._optionsService.rawOptions.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._optionsService.rawOptions.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._optionsService.rawOptions.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._optionsService.rawOptions.letterSpacing / 2); - - // Recalculate the canvas dimensions; scaled* define the actual number of - // pixel in the canvas - this.dimensions.scaledCanvasHeight = this._bufferService.rows * this.dimensions.scaledCellHeight; - this.dimensions.scaledCanvasWidth = this._bufferService.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._bufferService.rows; - this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._bufferService.cols; - } -} diff --git a/node_modules/xterm/src/browser/renderer/RendererUtils.ts b/node_modules/xterm/src/browser/renderer/RendererUtils.ts deleted file mode 100644 index 48fd26a..0000000 --- a/node_modules/xterm/src/browser/renderer/RendererUtils.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) 2019 The xterm.js authors. All rights reserved. - * @license MIT - */ - -export function throwIfFalsy<T>(value: T | undefined | null): T { - if (!value) { - throw new Error('value must not be falsy'); - } - return value; -} diff --git a/node_modules/xterm/src/browser/renderer/SelectionRenderLayer.ts b/node_modules/xterm/src/browser/renderer/SelectionRenderLayer.ts deleted file mode 100644 index 9054e3c..0000000 --- a/node_modules/xterm/src/browser/renderer/SelectionRenderLayer.ts +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { IRenderDimensions } from 'browser/renderer/Types'; -import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer'; -import { IColorSet } from 'browser/Types'; -import { IBufferService, IOptionsService } from 'common/services/Services'; - -interface ISelectionState { - start?: [number, number]; - end?: [number, number]; - columnSelectMode?: boolean; - ydisp?: number; -} - -export class SelectionRenderLayer extends BaseRenderLayer { - private _state!: ISelectionState; - - constructor( - container: HTMLElement, - zIndex: number, - colors: IColorSet, - rendererId: number, - @IBufferService bufferService: IBufferService, - @IOptionsService optionsService: IOptionsService - ) { - super(container, 'selection', zIndex, true, colors, rendererId, bufferService, optionsService); - this._clearState(); - } - - private _clearState(): void { - this._state = { - start: undefined, - end: undefined, - columnSelectMode: undefined, - ydisp: undefined - }; - } - - public resize(dim: IRenderDimensions): void { - super.resize(dim); - // Resizing the canvas discards the contents of the canvas so clear state - this._clearState(); - } - - public reset(): void { - if (this._state.start && this._state.end) { - this._clearState(); - this._clearAll(); - } - } - - public onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void { - // Selection has not changed - if (!this._didStateChange(start, end, columnSelectMode, this._bufferService.buffer.ydisp)) { - return; - } - - // Remove all selections - this._clearAll(); - - // Selection does not exist - if (!start || !end) { - this._clearState(); - return; - } - - // Translate from buffer position to viewport position - const viewportStartRow = start[1] - this._bufferService.buffer.ydisp; - const viewportEndRow = end[1] - this._bufferService.buffer.ydisp; - const viewportCappedStartRow = Math.max(viewportStartRow, 0); - const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1); - - // No need to draw the selection - if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) { - this._state.ydisp = this._bufferService.buffer.ydisp; - return; - } - - this._ctx.fillStyle = this._colors.selectionTransparent.css; - - if (columnSelectMode) { - const startCol = start[0]; - const width = end[0] - startCol; - const height = viewportCappedEndRow - viewportCappedStartRow + 1; - this._fillCells(startCol, viewportCappedStartRow, width, height); - } else { - // Draw first row - const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0; - const startRowEndCol = viewportCappedStartRow === viewportEndRow ? end[0] : this._bufferService.cols; - this._fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1); - - // Draw middle rows - const middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0); - this._fillCells(0, viewportCappedStartRow + 1, this._bufferService.cols, middleRowsCount); - - // Draw final row - if (viewportCappedStartRow !== viewportCappedEndRow) { - // Only draw viewportEndRow if it's not the same as viewportStartRow - const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._bufferService.cols; - this._fillCells(0, viewportCappedEndRow, endCol, 1); - } - } - - // Save state for next render - this._state.start = [start[0], start[1]]; - this._state.end = [end[0], end[1]]; - this._state.columnSelectMode = columnSelectMode; - this._state.ydisp = this._bufferService.buffer.ydisp; - } - - private _didStateChange(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean, ydisp: number): boolean { - return !this._areCoordinatesEqual(start, this._state.start) || - !this._areCoordinatesEqual(end, this._state.end) || - columnSelectMode !== this._state.columnSelectMode || - ydisp !== this._state.ydisp; - } - - private _areCoordinatesEqual(coord1: [number, number] | undefined, coord2: [number, number] | undefined): boolean { - if (!coord1 || !coord2) { - return false; - } - - return coord1[0] === coord2[0] && coord1[1] === coord2[1]; - } -} diff --git a/node_modules/xterm/src/browser/renderer/TextRenderLayer.ts b/node_modules/xterm/src/browser/renderer/TextRenderLayer.ts deleted file mode 100644 index 33d942f..0000000 --- a/node_modules/xterm/src/browser/renderer/TextRenderLayer.ts +++ /dev/null @@ -1,330 +0,0 @@ -/** - * 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); - // } -} diff --git a/node_modules/xterm/src/browser/renderer/Types.d.ts b/node_modules/xterm/src/browser/renderer/Types.d.ts deleted file mode 100644 index 6818a92..0000000 --- a/node_modules/xterm/src/browser/renderer/Types.d.ts +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright (c) 2019 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { IDisposable } from 'common/Types'; -import { IColorSet } from 'browser/Types'; -import { IEvent } from 'common/EventEmitter'; - -export interface IRenderDimensions { - scaledCharWidth: number; - scaledCharHeight: number; - scaledCellWidth: number; - scaledCellHeight: number; - scaledCharLeft: number; - scaledCharTop: number; - scaledCanvasWidth: number; - scaledCanvasHeight: number; - canvasWidth: number; - canvasHeight: number; - actualCellWidth: number; - actualCellHeight: number; -} - -export interface IRequestRedrawEvent { - start: number; - end: number; -} - -/** - * Note that IRenderer implementations should emit the refresh event after - * rendering rows to the screen. - */ -export interface IRenderer extends IDisposable { - readonly dimensions: IRenderDimensions; - - /** - * Fires when the renderer is requesting to be redrawn on the next animation - * frame but is _not_ a result of content changing (eg. selection changes). - */ - readonly onRequestRedraw: IEvent<IRequestRedrawEvent>; - - dispose(): void; - setColors(colors: IColorSet): void; - onDevicePixelRatioChange(): void; - onResize(cols: number, rows: number): void; - onCharSizeChanged(): void; - onBlur(): void; - onFocus(): void; - onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void; - onCursorMove(): void; - onOptionsChanged(): void; - clear(): void; - renderRows(start: number, end: number): void; - clearTextureAtlas?(): void; -} - -export interface IRenderLayer extends IDisposable { - /** - * Called when the terminal loses focus. - */ - onBlur(): void; - - /** - * * Called when the terminal gets focus. - */ - onFocus(): void; - - /** - * Called when the cursor is moved. - */ - onCursorMove(): void; - - /** - * Called when options change. - */ - onOptionsChanged(): void; - - /** - * Called when the theme changes. - */ - setColors(colorSet: IColorSet): void; - - /** - * Called when the data in the grid has changed (or needs to be rendered - * again). - */ - onGridChanged(startRow: number, endRow: number): void; - - /** - * Calls when the selection changes. - */ - onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void; - - /** - * Resize the render layer. - */ - resize(dim: IRenderDimensions): void; - - /** - * Clear the state of the render layer. - */ - reset(): void; - - /** - * Clears the texture atlas. - */ - clearTextureAtlas(): void; -} diff --git a/node_modules/xterm/src/browser/renderer/atlas/BaseCharAtlas.ts b/node_modules/xterm/src/browser/renderer/atlas/BaseCharAtlas.ts deleted file mode 100644 index 83c30d2..0000000 --- a/node_modules/xterm/src/browser/renderer/atlas/BaseCharAtlas.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { IGlyphIdentifier } from 'browser/renderer/atlas/Types'; -import { IDisposable } from 'common/Types'; - -export abstract class BaseCharAtlas implements IDisposable { - private _didWarmUp: boolean = false; - - public dispose(): void { } - - /** - * Perform any work needed to warm the cache before it can be used. May be called multiple times. - * Implement _doWarmUp instead if you only want to get called once. - */ - public warmUp(): void { - if (!this._didWarmUp) { - this._doWarmUp(); - this._didWarmUp = true; - } - } - - /** - * Perform any work needed to warm the cache before it can be used. Used by the default - * implementation of warmUp(), and will only be called once. - */ - private _doWarmUp(): void { } - - public clear(): void { } - - /** - * Called when we start drawing a new frame. - * - * TODO: We rely on this getting called by TextRenderLayer. This should really be called by - * Renderer instead, but we need to make Renderer the source-of-truth for the char atlas, instead - * of BaseRenderLayer. - */ - public beginFrame(): void { } - - /** - * May be called before warmUp finishes, however it is okay for the implementation to - * do nothing and return false in that case. - * - * @param ctx Where to draw the character onto. - * @param glyph Information about what to draw - * @param x The position on the context to start drawing at - * @param y The position on the context to start drawing at - * @returns The success state. True if we drew the character. - */ - public abstract draw( - ctx: CanvasRenderingContext2D, - glyph: IGlyphIdentifier, - x: number, - y: number - ): boolean; -} diff --git a/node_modules/xterm/src/browser/renderer/atlas/CharAtlasCache.ts b/node_modules/xterm/src/browser/renderer/atlas/CharAtlasCache.ts deleted file mode 100644 index 257835b..0000000 --- a/node_modules/xterm/src/browser/renderer/atlas/CharAtlasCache.ts +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { generateConfig, configEquals } from 'browser/renderer/atlas/CharAtlasUtils'; -import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas'; -import { DynamicCharAtlas } from 'browser/renderer/atlas/DynamicCharAtlas'; -import { ICharAtlasConfig } from 'browser/renderer/atlas/Types'; -import { IColorSet } from 'browser/Types'; -import { ITerminalOptions } from 'common/services/Services'; - -interface ICharAtlasCacheEntry { - atlas: BaseCharAtlas; - config: ICharAtlasConfig; - // N.B. This implementation potentially holds onto copies of the terminal forever, so - // this may cause memory leaks. - ownedBy: number[]; -} - -const charAtlasCache: ICharAtlasCacheEntry[] = []; - -/** - * Acquires a char atlas, either generating a new one or returning an existing - * one that is in use by another terminal. - */ -export function acquireCharAtlas( - options: ITerminalOptions, - rendererId: number, - colors: IColorSet, - scaledCharWidth: number, - scaledCharHeight: number -): BaseCharAtlas { - const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, options, colors); - - // Check to see if the renderer already owns this config - for (let i = 0; i < charAtlasCache.length; i++) { - const entry = charAtlasCache[i]; - const ownedByIndex = entry.ownedBy.indexOf(rendererId); - if (ownedByIndex >= 0) { - if (configEquals(entry.config, newConfig)) { - return entry.atlas; - } - // The configs differ, release the renderer from the entry - if (entry.ownedBy.length === 1) { - entry.atlas.dispose(); - charAtlasCache.splice(i, 1); - } else { - entry.ownedBy.splice(ownedByIndex, 1); - } - break; - } - } - - // Try match a char atlas from the cache - for (let i = 0; i < charAtlasCache.length; i++) { - const entry = charAtlasCache[i]; - if (configEquals(entry.config, newConfig)) { - // Add the renderer to the cache entry and return - entry.ownedBy.push(rendererId); - return entry.atlas; - } - } - - const newEntry: ICharAtlasCacheEntry = { - atlas: new DynamicCharAtlas( - document, - newConfig - ), - config: newConfig, - ownedBy: [rendererId] - }; - charAtlasCache.push(newEntry); - return newEntry.atlas; -} - -/** - * Removes a terminal reference from the cache, allowing its memory to be freed. - */ -export function removeTerminalFromCache(rendererId: number): void { - for (let i = 0; i < charAtlasCache.length; i++) { - const index = charAtlasCache[i].ownedBy.indexOf(rendererId); - if (index !== -1) { - if (charAtlasCache[i].ownedBy.length === 1) { - // Remove the cache entry if it's the only renderer - charAtlasCache[i].atlas.dispose(); - charAtlasCache.splice(i, 1); - } else { - // Remove the reference from the cache entry - charAtlasCache[i].ownedBy.splice(index, 1); - } - break; - } - } -} diff --git a/node_modules/xterm/src/browser/renderer/atlas/CharAtlasUtils.ts b/node_modules/xterm/src/browser/renderer/atlas/CharAtlasUtils.ts deleted file mode 100644 index be92727..0000000 --- a/node_modules/xterm/src/browser/renderer/atlas/CharAtlasUtils.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { ICharAtlasConfig } from 'browser/renderer/atlas/Types'; -import { DEFAULT_COLOR } from 'common/buffer/Constants'; -import { IColorSet, IPartialColorSet } from 'browser/Types'; -import { ITerminalOptions } from 'common/services/Services'; - -export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, options: ITerminalOptions, colors: IColorSet): ICharAtlasConfig { - // null out some fields that don't matter - const clonedColors: IPartialColorSet = { - foreground: colors.foreground, - background: colors.background, - cursor: undefined, - cursorAccent: undefined, - selection: undefined, - ansi: [...colors.ansi] - }; - return { - devicePixelRatio: window.devicePixelRatio, - scaledCharWidth, - scaledCharHeight, - fontFamily: options.fontFamily, - fontSize: options.fontSize, - fontWeight: options.fontWeight, - fontWeightBold: options.fontWeightBold, - allowTransparency: options.allowTransparency, - colors: clonedColors - }; -} - -export function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean { - for (let i = 0; i < a.colors.ansi.length; i++) { - if (a.colors.ansi[i].rgba !== b.colors.ansi[i].rgba) { - return false; - } - } - return a.devicePixelRatio === b.devicePixelRatio && - a.fontFamily === b.fontFamily && - a.fontSize === b.fontSize && - a.fontWeight === b.fontWeight && - a.fontWeightBold === b.fontWeightBold && - a.allowTransparency === b.allowTransparency && - a.scaledCharWidth === b.scaledCharWidth && - a.scaledCharHeight === b.scaledCharHeight && - a.colors.foreground === b.colors.foreground && - a.colors.background === b.colors.background; -} - -export function is256Color(colorCode: number): boolean { - return colorCode < DEFAULT_COLOR; -} diff --git a/node_modules/xterm/src/browser/renderer/atlas/Constants.ts b/node_modules/xterm/src/browser/renderer/atlas/Constants.ts deleted file mode 100644 index c1701e9..0000000 --- a/node_modules/xterm/src/browser/renderer/atlas/Constants.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { isFirefox, isLegacyEdge } from 'common/Platform'; - -export const INVERTED_DEFAULT_COLOR = 257; -export const DIM_OPACITY = 0.5; -// The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge would -// result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly -// unaligned Powerline fonts (PR 3356#issuecomment-850928179). -export const TEXT_BASELINE: CanvasTextBaseline = isFirefox || isLegacyEdge ? 'bottom' : 'ideographic'; - -export const CHAR_ATLAS_CELL_SPACING = 1; diff --git a/node_modules/xterm/src/browser/renderer/atlas/DynamicCharAtlas.ts b/node_modules/xterm/src/browser/renderer/atlas/DynamicCharAtlas.ts deleted file mode 100644 index 118dbcd..0000000 --- a/node_modules/xterm/src/browser/renderer/atlas/DynamicCharAtlas.ts +++ /dev/null @@ -1,404 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { DIM_OPACITY, INVERTED_DEFAULT_COLOR, TEXT_BASELINE } from 'browser/renderer/atlas/Constants'; -import { IGlyphIdentifier, ICharAtlasConfig } from 'browser/renderer/atlas/Types'; -import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas'; -import { DEFAULT_ANSI_COLORS } from 'browser/ColorManager'; -import { LRUMap } from 'browser/renderer/atlas/LRUMap'; -import { isFirefox, isSafari } from 'common/Platform'; -import { IColor } from 'browser/Types'; -import { throwIfFalsy } from 'browser/renderer/RendererUtils'; -import { color } from 'browser/Color'; - -// In practice we're probably never going to exhaust a texture this large. For debugging purposes, -// however, it can be useful to set this to a really tiny value, to verify that LRU eviction works. -const TEXTURE_WIDTH = 1024; -const TEXTURE_HEIGHT = 1024; - -const TRANSPARENT_COLOR = { - css: 'rgba(0, 0, 0, 0)', - rgba: 0 -}; - -// Drawing to the cache is expensive: If we have to draw more than this number of glyphs to the -// cache in a single frame, give up on trying to cache anything else, and try to finish the current -// frame ASAP. -// -// This helps to limit the amount of damage a program can do when it would otherwise thrash the -// cache. -const FRAME_CACHE_DRAW_LIMIT = 100; - -/** - * The number of milliseconds to wait before generating the ImageBitmap, this is to debounce/batch - * the operation as window.createImageBitmap is asynchronous. - */ -const GLYPH_BITMAP_COMMIT_DELAY = 100; - -interface IGlyphCacheValue { - index: number; - isEmpty: boolean; - inBitmap: boolean; -} - -export function getGlyphCacheKey(glyph: IGlyphIdentifier): number { - // Note that this only returns a valid key when code < 256 - // Layout: - // 0b00000000000000000000000000000001: italic (1) - // 0b00000000000000000000000000000010: dim (1) - // 0b00000000000000000000000000000100: bold (1) - // 0b00000000000000000000111111111000: fg (9) - // 0b00000000000111111111000000000000: bg (9) - // 0b00011111111000000000000000000000: code (8) - // 0b11100000000000000000000000000000: unused (3) - return glyph.code << 21 | glyph.bg << 12 | glyph.fg << 3 | (glyph.bold ? 0 : 4) + (glyph.dim ? 0 : 2) + (glyph.italic ? 0 : 1); -} - -export class DynamicCharAtlas extends BaseCharAtlas { - // An ordered map that we're using to keep track of where each glyph is in the atlas texture. - // It's ordered so that we can determine when to remove the old entries. - private _cacheMap: LRUMap<IGlyphCacheValue>; - - // The texture that the atlas is drawn to - private _cacheCanvas: HTMLCanvasElement; - private _cacheCtx: CanvasRenderingContext2D; - - // A temporary context that glyphs are drawn to before being transfered to the atlas. - private _tmpCtx: CanvasRenderingContext2D; - - // The number of characters stored in the atlas by width/height - private _width: number; - private _height: number; - - private _drawToCacheCount: number = 0; - - // An array of glyph keys that are waiting on the bitmap to be generated. - private _glyphsWaitingOnBitmap: IGlyphCacheValue[] = []; - - // The timeout that is used to batch bitmap generation so it's not requested for every new glyph. - private _bitmapCommitTimeout: number | null = null; - - // The bitmap to draw from, this is much faster on other browsers than others. - private _bitmap: ImageBitmap | null = null; - - constructor(document: Document, private _config: ICharAtlasConfig) { - super(); - this._cacheCanvas = document.createElement('canvas'); - this._cacheCanvas.width = TEXTURE_WIDTH; - this._cacheCanvas.height = TEXTURE_HEIGHT; - // The canvas needs alpha because we use clearColor to convert the background color to alpha. - // It might also contain some characters with transparent backgrounds if allowTransparency is - // set. - this._cacheCtx = throwIfFalsy(this._cacheCanvas.getContext('2d', { alpha: true })); - - const tmpCanvas = document.createElement('canvas'); - tmpCanvas.width = this._config.scaledCharWidth; - tmpCanvas.height = this._config.scaledCharHeight; - this._tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d', { alpha: this._config.allowTransparency })); - - this._width = Math.floor(TEXTURE_WIDTH / this._config.scaledCharWidth); - this._height = Math.floor(TEXTURE_HEIGHT / this._config.scaledCharHeight); - const capacity = this._width * this._height; - this._cacheMap = new LRUMap(capacity); - this._cacheMap.prealloc(capacity); - - // This is useful for debugging - // document.body.appendChild(this._cacheCanvas); - } - - public dispose(): void { - if (this._bitmapCommitTimeout !== null) { - window.clearTimeout(this._bitmapCommitTimeout); - this._bitmapCommitTimeout = null; - } - } - - public beginFrame(): void { - this._drawToCacheCount = 0; - } - - public clear(): void { - if (this._cacheMap.size > 0) { - const capacity = this._width * this._height; - this._cacheMap = new LRUMap(capacity); - this._cacheMap.prealloc(capacity); - } - this._cacheCtx.clearRect(0, 0, TEXTURE_WIDTH, TEXTURE_HEIGHT); - this._tmpCtx.clearRect(0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight); - } - - public draw( - ctx: CanvasRenderingContext2D, - glyph: IGlyphIdentifier, - x: number, - y: number - ): boolean { - // Space is always an empty cell, special case this as it's so common - if (glyph.code === 32) { - return true; - } - - // Exit early for uncachable glyphs - if (!this._canCache(glyph)) { - return false; - } - - const glyphKey = getGlyphCacheKey(glyph); - const cacheValue = this._cacheMap.get(glyphKey); - if (cacheValue !== null && cacheValue !== undefined) { - this._drawFromCache(ctx, cacheValue, x, y); - return true; - } - if (this._drawToCacheCount < FRAME_CACHE_DRAW_LIMIT) { - let index; - if (this._cacheMap.size < this._cacheMap.capacity) { - index = this._cacheMap.size; - } else { - // we're out of space, so our call to set will delete this item - index = this._cacheMap.peek()!.index; - } - const cacheValue = this._drawToCache(glyph, index); - this._cacheMap.set(glyphKey, cacheValue); - this._drawFromCache(ctx, cacheValue, x, y); - return true; - } - return false; - } - - private _canCache(glyph: IGlyphIdentifier): boolean { - // Only cache ascii and extended characters for now, to be safe. In the future, we could do - // something more complicated to determine the expected width of a character. - // - // If we switch the renderer over to webgl at some point, we may be able to use blending modes - // to draw overlapping glyphs from the atlas: - // https://github.com/servo/webrender/issues/464#issuecomment-255632875 - // https://webglfundamentals.org/webgl/lessons/webgl-text-texture.html - return glyph.code < 256; - } - - private _toCoordinateX(index: number): number { - return (index % this._width) * this._config.scaledCharWidth; - } - - private _toCoordinateY(index: number): number { - return Math.floor(index / this._width) * this._config.scaledCharHeight; - } - - private _drawFromCache( - ctx: CanvasRenderingContext2D, - cacheValue: IGlyphCacheValue, - x: number, - y: number - ): void { - // We don't actually need to do anything if this is whitespace. - if (cacheValue.isEmpty) { - return; - } - const cacheX = this._toCoordinateX(cacheValue.index); - const cacheY = this._toCoordinateY(cacheValue.index); - ctx.drawImage( - cacheValue.inBitmap ? this._bitmap! : this._cacheCanvas, - cacheX, - cacheY, - this._config.scaledCharWidth, - this._config.scaledCharHeight, - x, - y, - this._config.scaledCharWidth, - this._config.scaledCharHeight - ); - } - - private _getColorFromAnsiIndex(idx: number): IColor { - if (idx < this._config.colors.ansi.length) { - return this._config.colors.ansi[idx]; - } - return DEFAULT_ANSI_COLORS[idx]; - } - - private _getBackgroundColor(glyph: IGlyphIdentifier): IColor { - if (this._config.allowTransparency) { - // The background color might have some transparency, so we need to render it as fully - // transparent in the atlas. Otherwise we'd end up drawing the transparent background twice - // around the anti-aliased edges of the glyph, and it would look too dark. - return TRANSPARENT_COLOR; - } - if (glyph.bg === INVERTED_DEFAULT_COLOR) { - return this._config.colors.foreground; - } - if (glyph.bg < 256) { - return this._getColorFromAnsiIndex(glyph.bg); - } - return this._config.colors.background; - } - - private _getForegroundColor(glyph: IGlyphIdentifier): IColor { - if (glyph.fg === INVERTED_DEFAULT_COLOR) { - return color.opaque(this._config.colors.background); - } - if (glyph.fg < 256) { - // 256 color support - return this._getColorFromAnsiIndex(glyph.fg); - } - return this._config.colors.foreground; - } - - // TODO: We do this (or something similar) in multiple places. We should split this off - // into a shared function. - private _drawToCache(glyph: IGlyphIdentifier, index: number): IGlyphCacheValue { - this._drawToCacheCount++; - - this._tmpCtx.save(); - - // draw the background - const backgroundColor = this._getBackgroundColor(glyph); - // Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha, regardless of - // transparency in backgroundColor - this._tmpCtx.globalCompositeOperation = 'copy'; - this._tmpCtx.fillStyle = backgroundColor.css; - this._tmpCtx.fillRect(0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight); - this._tmpCtx.globalCompositeOperation = 'source-over'; - - // draw the foreground/glyph - const fontWeight = glyph.bold ? this._config.fontWeightBold : this._config.fontWeight; - const fontStyle = glyph.italic ? 'italic' : ''; - this._tmpCtx.font = - `${fontStyle} ${fontWeight} ${this._config.fontSize * this._config.devicePixelRatio}px ${this._config.fontFamily}`; - this._tmpCtx.textBaseline = TEXT_BASELINE; - - this._tmpCtx.fillStyle = this._getForegroundColor(glyph).css; - - // Apply alpha to dim the character - if (glyph.dim) { - this._tmpCtx.globalAlpha = DIM_OPACITY; - } - // Draw the character - this._tmpCtx.fillText(glyph.chars, 0, this._config.scaledCharHeight); - - // clear the background from the character to avoid issues with drawing over the previous - // character if it extends past it's bounds - let imageData = this._tmpCtx.getImageData( - 0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight - ); - let isEmpty = false; - if (!this._config.allowTransparency) { - isEmpty = clearColor(imageData, backgroundColor); - } - - // If this charcater is underscore and empty, shift it up until it is visible, try for a maximum - // of 5 pixels. - if (isEmpty && glyph.chars === '_' && !this._config.allowTransparency) { - for (let offset = 1; offset <= 5; offset++) { - // Draw the character - this._tmpCtx.fillText(glyph.chars, 0, this._config.scaledCharHeight - offset); - - // clear the background from the character to avoid issues with drawing over the previous - // character if it extends past it's bounds - imageData = this._tmpCtx.getImageData( - 0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight - ); - isEmpty = clearColor(imageData, backgroundColor); - if (!isEmpty) { - break; - } - } - } - - this._tmpCtx.restore(); - - // copy the data from imageData to _cacheCanvas - const x = this._toCoordinateX(index); - const y = this._toCoordinateY(index); - // putImageData doesn't do any blending, so it will overwrite any existing cache entry for us - this._cacheCtx.putImageData(imageData, x, y); - - // Add the glyph and queue it to the bitmap (if the browser supports it) - const cacheValue = { - index, - isEmpty, - inBitmap: false - }; - this._addGlyphToBitmap(cacheValue); - - return cacheValue; - } - - private _addGlyphToBitmap(cacheValue: IGlyphCacheValue): void { - // Support is patchy for createImageBitmap at the moment, pass a canvas back - // if support is lacking as drawImage works there too. Firefox is also - // included here as ImageBitmap appears both buggy and has horrible - // performance (tested on v55). - if (!('createImageBitmap' in window) || isFirefox || isSafari) { - return; - } - - // Add the glyph to the queue - this._glyphsWaitingOnBitmap.push(cacheValue); - - // Check if bitmap generation timeout already exists - if (this._bitmapCommitTimeout !== null) { - return; - } - - this._bitmapCommitTimeout = window.setTimeout(() => this._generateBitmap(), GLYPH_BITMAP_COMMIT_DELAY); - } - - private _generateBitmap(): void { - const glyphsMovingToBitmap = this._glyphsWaitingOnBitmap; - this._glyphsWaitingOnBitmap = []; - window.createImageBitmap(this._cacheCanvas).then(bitmap => { - // Set bitmap - this._bitmap = bitmap; - - // Mark all new glyphs as in bitmap, excluding glyphs that came in after - // the bitmap was requested - for (let i = 0; i < glyphsMovingToBitmap.length; i++) { - const value = glyphsMovingToBitmap[i]; - // It doesn't matter if the value was already evicted, it will be - // released from memory after this block if so. - value.inBitmap = true; - } - }); - this._bitmapCommitTimeout = null; - } -} - -// This is used for debugging the renderer, just swap out `new DynamicCharAtlas` with -// `new NoneCharAtlas`. -export class NoneCharAtlas extends BaseCharAtlas { - constructor(document: Document, config: ICharAtlasConfig) { - super(); - } - - public draw( - ctx: CanvasRenderingContext2D, - glyph: IGlyphIdentifier, - x: number, - y: number - ): boolean { - return false; - } -} - -/** - * Makes a partiicular rgb color in an ImageData completely transparent. - * @returns True if the result is "empty", meaning all pixels are fully transparent. - */ -function clearColor(imageData: ImageData, color: IColor): boolean { - let isEmpty = true; - const r = color.rgba >>> 24; - const g = color.rgba >>> 16 & 0xFF; - const b = color.rgba >>> 8 & 0xFF; - for (let offset = 0; offset < imageData.data.length; offset += 4) { - if (imageData.data[offset] === r && - imageData.data[offset + 1] === g && - imageData.data[offset + 2] === b) { - imageData.data[offset + 3] = 0; - } else { - isEmpty = false; - } - } - return isEmpty; -} diff --git a/node_modules/xterm/src/browser/renderer/atlas/LRUMap.ts b/node_modules/xterm/src/browser/renderer/atlas/LRUMap.ts deleted file mode 100644 index f70962f..0000000 --- a/node_modules/xterm/src/browser/renderer/atlas/LRUMap.ts +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -interface ILinkedListNode<T> { - prev: ILinkedListNode<T> | null; - next: ILinkedListNode<T> | null; - key: number | null; - value: T | null; -} - -export class LRUMap<T> { - private _map: { [key: number]: ILinkedListNode<T> } = {}; - private _head: ILinkedListNode<T> | null = null; - private _tail: ILinkedListNode<T> | null = null; - private _nodePool: ILinkedListNode<T>[] = []; - public size: number = 0; - - constructor(public capacity: number) { } - - private _unlinkNode(node: ILinkedListNode<T>): void { - const prev = node.prev; - const next = node.next; - if (node === this._head) { - this._head = next; - } - if (node === this._tail) { - this._tail = prev; - } - if (prev !== null) { - prev.next = next; - } - if (next !== null) { - next.prev = prev; - } - } - - private _appendNode(node: ILinkedListNode<T>): void { - const tail = this._tail; - if (tail !== null) { - tail.next = node; - } - node.prev = tail; - node.next = null; - this._tail = node; - if (this._head === null) { - this._head = node; - } - } - - /** - * Preallocate a bunch of linked-list nodes. Allocating these nodes ahead of time means that - * they're more likely to live next to each other in memory, which seems to improve performance. - * - * Each empty object only consumes about 60 bytes of memory, so this is pretty cheap, even for - * large maps. - */ - public prealloc(count: number): void { - const nodePool = this._nodePool; - for (let i = 0; i < count; i++) { - nodePool.push({ - prev: null, - next: null, - key: null, - value: null - }); - } - } - - public get(key: number): T | null { - // This is unsafe: We're assuming our keyspace doesn't overlap with Object.prototype. However, - // it's faster than calling hasOwnProperty, and in our case, it would never overlap. - const node = this._map[key]; - if (node !== undefined) { - this._unlinkNode(node); - this._appendNode(node); - return node.value; - } - return null; - } - - /** - * Gets a value from a key without marking it as the most recently used item. - */ - public peekValue(key: number): T | null { - const node = this._map[key]; - if (node !== undefined) { - return node.value; - } - return null; - } - - public peek(): T | null { - const head = this._head; - return head === null ? null : head.value; - } - - public set(key: number, value: T): void { - // This is unsafe: See note above. - let node = this._map[key]; - if (node !== undefined) { - // already exists, we just need to mutate it and move it to the end of the list - node = this._map[key]; - this._unlinkNode(node); - node.value = value; - } else if (this.size >= this.capacity) { - // we're out of space: recycle the head node, move it to the tail - node = this._head!; - this._unlinkNode(node); - delete this._map[node.key!]; - node.key = key; - node.value = value; - this._map[key] = node; - } else { - // make a new element - const nodePool = this._nodePool; - if (nodePool.length > 0) { - // use a preallocated node if we can - node = nodePool.pop()!; - node.key = key; - node.value = value; - } else { - node = { - prev: null, - next: null, - key, - value - }; - } - this._map[key] = node; - this.size++; - } - this._appendNode(node); - } -} diff --git a/node_modules/xterm/src/browser/renderer/atlas/Types.d.ts b/node_modules/xterm/src/browser/renderer/atlas/Types.d.ts deleted file mode 100644 index d8bc54c..0000000 --- a/node_modules/xterm/src/browser/renderer/atlas/Types.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { FontWeight } from 'common/services/Services'; -import { IPartialColorSet } from 'browser/Types'; - -export interface IGlyphIdentifier { - chars: string; - code: number; - bg: number; - fg: number; - bold: boolean; - dim: boolean; - italic: boolean; -} - -export interface ICharAtlasConfig { - devicePixelRatio: number; - fontSize: number; - fontFamily: string; - fontWeight: FontWeight; - fontWeightBold: FontWeight; - scaledCharWidth: number; - scaledCharHeight: number; - allowTransparency: boolean; - colors: IPartialColorSet; -} diff --git a/node_modules/xterm/src/browser/renderer/dom/DomRenderer.ts b/node_modules/xterm/src/browser/renderer/dom/DomRenderer.ts deleted file mode 100644 index ee28339..0000000 --- a/node_modules/xterm/src/browser/renderer/dom/DomRenderer.ts +++ /dev/null @@ -1,400 +0,0 @@ -/** - * Copyright (c) 2018 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { IRenderer, IRenderDimensions, IRequestRedrawEvent } 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, ILinkifier2 } from 'browser/Types'; -import { ICharSizeService } from 'browser/services/Services'; -import { IOptionsService, IBufferService, IInstantiationService } from 'common/services/Services'; -import { EventEmitter, IEvent } from 'common/EventEmitter'; -import { color } from 'browser/Color'; -import { removeElementFromParent } from 'browser/Dom'; - -const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-'; -const ROW_CONTAINER_CLASS = 'xterm-rows'; -const FG_CLASS_PREFIX = 'xterm-fg-'; -const BG_CLASS_PREFIX = 'xterm-bg-'; -const FOCUS_CLASS = 'xterm-focus'; -const SELECTION_CLASS = 'xterm-selection'; - -let nextTerminalId = 1; - -/** - * A fallback renderer for when canvas is slow. This is not meant to be - * particularly fast or feature complete, more just stable and usable for when - * canvas is not an option. - */ -export class DomRenderer extends Disposable implements IRenderer { - private _rowFactory: DomRendererRowFactory; - private _terminalClass: number = nextTerminalId++; - - private _themeStyleElement!: HTMLStyleElement; - private _dimensionsStyleElement!: HTMLStyleElement; - private _rowContainer: HTMLElement; - private _rowElements: HTMLElement[] = []; - private _selectionContainer: HTMLElement; - - public dimensions: IRenderDimensions; - - public get onRequestRedraw(): IEvent<IRequestRedrawEvent> { return new EventEmitter<IRequestRedrawEvent>().event; } - - constructor( - private _colors: IColorSet, - private readonly _element: HTMLElement, - private readonly _screenElement: HTMLElement, - private readonly _viewportElement: HTMLElement, - private readonly _linkifier: ILinkifier, - private readonly _linkifier2: ILinkifier2, - @IInstantiationService instantiationService: IInstantiationService, - @ICharSizeService private readonly _charSizeService: ICharSizeService, - @IOptionsService private readonly _optionsService: IOptionsService, - @IBufferService private readonly _bufferService: IBufferService - ) { - super(); - this._rowContainer = document.createElement('div'); - this._rowContainer.classList.add(ROW_CONTAINER_CLASS); - this._rowContainer.style.lineHeight = 'normal'; - this._rowContainer.setAttribute('aria-hidden', 'true'); - this._refreshRowElements(this._bufferService.cols, this._bufferService.rows); - this._selectionContainer = document.createElement('div'); - this._selectionContainer.classList.add(SELECTION_CLASS); - this._selectionContainer.setAttribute('aria-hidden', 'true'); - - this.dimensions = { - scaledCharWidth: 0, - scaledCharHeight: 0, - scaledCellWidth: 0, - scaledCellHeight: 0, - scaledCharLeft: 0, - scaledCharTop: 0, - scaledCanvasWidth: 0, - scaledCanvasHeight: 0, - canvasWidth: 0, - canvasHeight: 0, - actualCellWidth: 0, - actualCellHeight: 0 - }; - this._updateDimensions(); - this._injectCss(); - - this._rowFactory = instantiationService.createInstance(DomRendererRowFactory, document, this._colors); - - this._element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass); - this._screenElement.appendChild(this._rowContainer); - this._screenElement.appendChild(this._selectionContainer); - - this._linkifier.onShowLinkUnderline(e => this._onLinkHover(e)); - this._linkifier.onHideLinkUnderline(e => this._onLinkLeave(e)); - - this._linkifier2.onShowLinkUnderline(e => this._onLinkHover(e)); - this._linkifier2.onHideLinkUnderline(e => this._onLinkLeave(e)); - } - - public dispose(): void { - this._element.classList.remove(TERMINAL_CLASS_PREFIX + this._terminalClass); - - // Outside influences such as React unmounts may manipulate the DOM before our disposal. - // https://github.com/xtermjs/xterm.js/issues/2960 - removeElementFromParent(this._rowContainer, this._selectionContainer, this._themeStyleElement, this._dimensionsStyleElement); - - super.dispose(); - } - - private _updateDimensions(): void { - this.dimensions.scaledCharWidth = this._charSizeService.width * window.devicePixelRatio; - this.dimensions.scaledCharHeight = Math.ceil(this._charSizeService.height * window.devicePixelRatio); - this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._optionsService.rawOptions.letterSpacing); - this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._optionsService.rawOptions.lineHeight); - this.dimensions.scaledCharLeft = 0; - this.dimensions.scaledCharTop = 0; - this.dimensions.scaledCanvasWidth = this.dimensions.scaledCellWidth * this._bufferService.cols; - this.dimensions.scaledCanvasHeight = this.dimensions.scaledCellHeight * this._bufferService.rows; - this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / window.devicePixelRatio); - this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / window.devicePixelRatio); - this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._bufferService.cols; - this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._bufferService.rows; - - for (const element of this._rowElements) { - element.style.width = `${this.dimensions.canvasWidth}px`; - element.style.height = `${this.dimensions.actualCellHeight}px`; - element.style.lineHeight = `${this.dimensions.actualCellHeight}px`; - // Make sure rows don't overflow onto following row - element.style.overflow = 'hidden'; - } - - if (!this._dimensionsStyleElement) { - this._dimensionsStyleElement = document.createElement('style'); - this._screenElement.appendChild(this._dimensionsStyleElement); - } - - const styles = - `${this._terminalSelector} .${ROW_CONTAINER_CLASS} span {` + - ` display: inline-block;` + - ` height: 100%;` + - ` vertical-align: top;` + - ` width: ${this.dimensions.actualCellWidth}px` + - `}`; - - this._dimensionsStyleElement.textContent = styles; - - this._selectionContainer.style.height = this._viewportElement.style.height; - this._screenElement.style.width = `${this.dimensions.canvasWidth}px`; - this._screenElement.style.height = `${this.dimensions.canvasHeight}px`; - } - - public setColors(colors: IColorSet): void { - this._colors = colors; - this._injectCss(); - } - - private _injectCss(): void { - if (!this._themeStyleElement) { - this._themeStyleElement = document.createElement('style'); - this._screenElement.appendChild(this._themeStyleElement); - } - - // Base CSS - let styles = - `${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` + - ` color: ${this._colors.foreground.css};` + - ` font-family: ${this._optionsService.rawOptions.fontFamily};` + - ` font-size: ${this._optionsService.rawOptions.fontSize}px;` + - `}`; - // Text styles - styles += - `${this._terminalSelector} span:not(.${BOLD_CLASS}) {` + - ` font-weight: ${this._optionsService.rawOptions.fontWeight};` + - `}` + - `${this._terminalSelector} span.${BOLD_CLASS} {` + - ` font-weight: ${this._optionsService.rawOptions.fontWeightBold};` + - `}` + - `${this._terminalSelector} span.${ITALIC_CLASS} {` + - ` font-style: italic;` + - `}`; - // Blink animation - styles += - `@keyframes blink_box_shadow` + `_` + this._terminalClass + ` {` + - ` 50% {` + - ` box-shadow: none;` + - ` }` + - `}`; - styles += - `@keyframes blink_block` + `_` + this._terminalClass + ` {` + - ` 0% {` + - ` background-color: ${this._colors.cursor.css};` + - ` color: ${this._colors.cursorAccent.css};` + - ` }` + - ` 50% {` + - ` background-color: ${this._colors.cursorAccent.css};` + - ` color: ${this._colors.cursor.css};` + - ` }` + - `}`; - // Cursor - styles += - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` + - ` outline: 1px solid ${this._colors.cursor.css};` + - ` outline-offset: -1px;` + - `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}:not(.${CURSOR_STYLE_BLOCK_CLASS}) {` + - ` animation: blink_box_shadow` + `_` + this._terminalClass + ` 1s step-end infinite;` + - `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` + - ` animation: blink_block` + `_` + this._terminalClass + ` 1s step-end infinite;` + - `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` + - ` background-color: ${this._colors.cursor.css};` + - ` color: ${this._colors.cursorAccent.css};` + - `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` + - ` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${this._colors.cursor.css} inset;` + - `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` + - ` box-shadow: 0 -1px 0 ${this._colors.cursor.css} inset;` + - `}`; - // Selection - styles += - `${this._terminalSelector} .${SELECTION_CLASS} {` + - ` position: absolute;` + - ` top: 0;` + - ` left: 0;` + - ` z-index: 1;` + - ` pointer-events: none;` + - `}` + - `${this._terminalSelector} .${SELECTION_CLASS} div {` + - ` position: absolute;` + - ` background-color: ${this._colors.selectionTransparent.css};` + - `}`; - // Colors - this._colors.ansi.forEach((c, i) => { - styles += - `${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` + - `${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`; - }); - styles += - `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(this._colors.background).css}; }` + - `${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${this._colors.foreground.css}; }`; - - this._themeStyleElement.textContent = styles; - } - - public onDevicePixelRatioChange(): void { - this._updateDimensions(); - } - - private _refreshRowElements(cols: number, rows: number): void { - // Add missing elements - for (let i = this._rowElements.length; i <= rows; i++) { - const row = document.createElement('div'); - this._rowContainer.appendChild(row); - this._rowElements.push(row); - } - // Remove excess elements - while (this._rowElements.length > rows) { - this._rowContainer.removeChild(this._rowElements.pop()!); - } - } - - public onResize(cols: number, rows: number): void { - this._refreshRowElements(cols, rows); - this._updateDimensions(); - } - - public onCharSizeChanged(): void { - this._updateDimensions(); - } - - public onBlur(): void { - this._rowContainer.classList.remove(FOCUS_CLASS); - } - - public onFocus(): void { - this._rowContainer.classList.add(FOCUS_CLASS); - } - - public onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void { - // Remove all selections - while (this._selectionContainer.children.length) { - this._selectionContainer.removeChild(this._selectionContainer.children[0]); - } - - // Selection does not exist - if (!start || !end) { - return; - } - - // Translate from buffer position to viewport position - const viewportStartRow = start[1] - this._bufferService.buffer.ydisp; - const viewportEndRow = end[1] - this._bufferService.buffer.ydisp; - const viewportCappedStartRow = Math.max(viewportStartRow, 0); - const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1); - - // No need to draw the selection - if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) { - return; - } - - // Create the selections - const documentFragment = document.createDocumentFragment(); - - if (columnSelectMode) { - documentFragment.appendChild( - this._createSelectionElement(viewportCappedStartRow, start[0], end[0], viewportCappedEndRow - viewportCappedStartRow + 1) - ); - } else { - // Draw first row - const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0; - const endCol = viewportCappedStartRow === viewportEndRow ? end[0] : this._bufferService.cols; - documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow, startCol, endCol)); - // Draw middle rows - const middleRowsCount = viewportCappedEndRow - viewportCappedStartRow - 1; - documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow + 1, 0, this._bufferService.cols, middleRowsCount)); - // Draw final row - if (viewportCappedStartRow !== viewportCappedEndRow) { - // Only draw viewportEndRow if it's not the same as viewporttartRow - const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._bufferService.cols; - documentFragment.appendChild(this._createSelectionElement(viewportCappedEndRow, 0, endCol)); - } - } - this._selectionContainer.appendChild(documentFragment); - } - - /** - * Creates a selection element at the specified position. - * @param row The row of the selection. - * @param colStart The start column. - * @param colEnd The end columns. - */ - private _createSelectionElement(row: number, colStart: number, colEnd: number, rowCount: number = 1): HTMLElement { - const element = document.createElement('div'); - element.style.height = `${rowCount * this.dimensions.actualCellHeight}px`; - element.style.top = `${row * this.dimensions.actualCellHeight}px`; - element.style.left = `${colStart * this.dimensions.actualCellWidth}px`; - element.style.width = `${this.dimensions.actualCellWidth * (colEnd - colStart)}px`; - return element; - } - - public onCursorMove(): void { - // No-op, the cursor is drawn when rows are drawn - } - - public onOptionsChanged(): void { - // Force a refresh - this._updateDimensions(); - this._injectCss(); - } - - public clear(): void { - for (const e of this._rowElements) { - e.innerText = ''; - } - } - - public renderRows(start: number, end: number): void { - const cursorAbsoluteY = this._bufferService.buffer.ybase + this._bufferService.buffer.y; - const cursorX = Math.min(this._bufferService.buffer.x, this._bufferService.cols - 1); - const cursorBlink = this._optionsService.rawOptions.cursorBlink; - - for (let y = start; y <= end; y++) { - const rowElement = this._rowElements[y]; - rowElement.innerText = ''; - - const row = y + this._bufferService.buffer.ydisp; - const lineData = this._bufferService.buffer.lines.get(row); - const cursorStyle = this._optionsService.rawOptions.cursorStyle; - rowElement.appendChild(this._rowFactory.createRow(lineData!, row, row === cursorAbsoluteY, cursorStyle, cursorX, cursorBlink, this.dimensions.actualCellWidth, this._bufferService.cols)); - } - } - - private get _terminalSelector(): string { - return `.${TERMINAL_CLASS_PREFIX}${this._terminalClass}`; - } - - private _onLinkHover(e: ILinkifierEvent): void { - this._setCellUnderline(e.x1, e.x2, e.y1, e.y2, e.cols, true); - } - - private _onLinkLeave(e: ILinkifierEvent): void { - this._setCellUnderline(e.x1, e.x2, e.y1, e.y2, e.cols, false); - } - - private _setCellUnderline(x: number, x2: number, y: number, y2: number, cols: number, enabled: boolean): void { - while (x !== x2 || y !== y2) { - const row = this._rowElements[y]; - if (!row) { - return; - } - const span = row.children[x] as HTMLElement; - if (span) { - span.style.textDecoration = enabled ? 'underline' : 'none'; - } - if (++x >= cols) { - x = 0; - y++; - } - } - } -} diff --git a/node_modules/xterm/src/browser/renderer/dom/DomRendererRowFactory.ts b/node_modules/xterm/src/browser/renderer/dom/DomRendererRowFactory.ts deleted file mode 100644 index fda800a..0000000 --- a/node_modules/xterm/src/browser/renderer/dom/DomRendererRowFactory.ts +++ /dev/null @@ -1,259 +0,0 @@ -/** - * Copyright (c) 2018 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { IBufferLine } from 'common/Types'; -import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; -import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Attributes } from 'common/buffer/Constants'; -import { CellData } from 'common/buffer/CellData'; -import { ICoreService, IOptionsService } from 'common/services/Services'; -import { color, rgba } from 'browser/Color'; -import { IColorSet, IColor } from 'browser/Types'; -import { ICharacterJoinerService } from 'browser/services/Services'; -import { JoinedCellData } from 'browser/services/CharacterJoinerService'; - -export const BOLD_CLASS = 'xterm-bold'; -export const DIM_CLASS = 'xterm-dim'; -export const ITALIC_CLASS = 'xterm-italic'; -export const UNDERLINE_CLASS = 'xterm-underline'; -export const STRIKETHROUGH_CLASS = 'xterm-strikethrough'; -export const CURSOR_CLASS = 'xterm-cursor'; -export const CURSOR_BLINK_CLASS = 'xterm-cursor-blink'; -export const CURSOR_STYLE_BLOCK_CLASS = 'xterm-cursor-block'; -export const CURSOR_STYLE_BAR_CLASS = 'xterm-cursor-bar'; -export const CURSOR_STYLE_UNDERLINE_CLASS = 'xterm-cursor-underline'; - -export class DomRendererRowFactory { - private _workCell: CellData = new CellData(); - - constructor( - private readonly _document: Document, - private _colors: IColorSet, - @ICharacterJoinerService private readonly _characterJoinerService: ICharacterJoinerService, - @IOptionsService private readonly _optionsService: IOptionsService, - @ICoreService private readonly _coreService: ICoreService - ) { - } - - public setColors(colors: IColorSet): void { - this._colors = colors; - } - - public createRow(lineData: IBufferLine, row: number, isCursorRow: boolean, cursorStyle: string | undefined, cursorX: number, cursorBlink: boolean, cellWidth: number, cols: number): DocumentFragment { - const fragment = this._document.createDocumentFragment(); - - const joinedRanges = this._characterJoinerService.getJoinedCharacters(row); - // Find the line length first, this prevents the need to output a bunch of - // empty cells at the end. This cannot easily be integrated into the main - // loop below because of the colCount feature (which can be removed after we - // properly support reflow and disallow data to go beyond the right-side of - // the viewport). - let lineLength = 0; - for (let x = Math.min(lineData.length, cols) - 1; x >= 0; x--) { - if (lineData.loadCell(x, this._workCell).getCode() !== NULL_CELL_CODE || (isCursorRow && x === cursorX)) { - lineLength = x + 1; - break; - } - } - - for (let x = 0; x < lineLength; x++) { - lineData.loadCell(x, this._workCell); - let width = this._workCell.getWidth(); - - // The character to the left is a wide character, drawing is owned by the char at x-1 - if (width === 0) { - continue; - } - - // If true, indicates that the current character(s) to draw were joined. - let isJoined = false; - let lastCharX = x; - - // 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. - let cell = this._workCell; - 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, - lineData.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; - - // Recalculate width - width = cell.getWidth(); - } - - const charElement = this._document.createElement('span'); - if (width > 1) { - charElement.style.width = `${cellWidth * width}px`; - } - - if (isJoined) { - // Ligatures in the DOM renderer must use display inline, as they may not show with - // inline-block if they are outside the bounds of the element - charElement.style.display = 'inline'; - - // The DOM renderer colors the background of the cursor but for ligatures all cells are - // joined. The workaround here is to show a cursor around the whole ligature so it shows up, - // the cursor looks the same when on any character of the ligature though - if (cursorX >= x && cursorX <= lastCharX) { - cursorX = x; - } - } - - if (!this._coreService.isCursorHidden && isCursorRow && x === cursorX) { - charElement.classList.add(CURSOR_CLASS); - - if (cursorBlink) { - charElement.classList.add(CURSOR_BLINK_CLASS); - } - - switch (cursorStyle) { - case 'bar': - charElement.classList.add(CURSOR_STYLE_BAR_CLASS); - break; - case 'underline': - charElement.classList.add(CURSOR_STYLE_UNDERLINE_CLASS); - break; - default: - charElement.classList.add(CURSOR_STYLE_BLOCK_CLASS); - break; - } - } - - if (cell.isBold()) { - charElement.classList.add(BOLD_CLASS); - } - - if (cell.isItalic()) { - charElement.classList.add(ITALIC_CLASS); - } - - if (cell.isDim()) { - charElement.classList.add(DIM_CLASS); - } - - if (cell.isUnderline()) { - charElement.classList.add(UNDERLINE_CLASS); - } - - if (cell.isInvisible()) { - charElement.textContent = WHITESPACE_CELL_CHAR; - } else { - charElement.textContent = cell.getChars() || WHITESPACE_CELL_CHAR; - } - - if (cell.isStrikethrough()) { - charElement.classList.add(STRIKETHROUGH_CLASS); - } - - let fg = cell.getFgColor(); - let fgColorMode = cell.getFgColorMode(); - let bg = cell.getBgColor(); - let bgColorMode = cell.getBgColorMode(); - const isInverse = !!cell.isInverse(); - if (isInverse) { - const temp = fg; - fg = bg; - bg = temp; - const temp2 = fgColorMode; - fgColorMode = bgColorMode; - bgColorMode = temp2; - } - - // Foreground - switch (fgColorMode) { - case Attributes.CM_P16: - case Attributes.CM_P256: - if (cell.isBold() && fg < 8 && this._optionsService.rawOptions.drawBoldTextInBrightColors) { - fg += 8; - } - if (!this._applyMinimumContrast(charElement, this._colors.background, this._colors.ansi[fg])) { - charElement.classList.add(`xterm-fg-${fg}`); - } - break; - case Attributes.CM_RGB: - const color = rgba.toColor( - (fg >> 16) & 0xFF, - (fg >> 8) & 0xFF, - (fg ) & 0xFF - ); - if (!this._applyMinimumContrast(charElement, this._colors.background, color)) { - this._addStyle(charElement, `color:#${padStart(fg.toString(16), '0', 6)}`); - } - break; - case Attributes.CM_DEFAULT: - default: - if (!this._applyMinimumContrast(charElement, this._colors.background, this._colors.foreground)) { - if (isInverse) { - charElement.classList.add(`xterm-fg-${INVERTED_DEFAULT_COLOR}`); - } - } - } - - // Background - switch (bgColorMode) { - case Attributes.CM_P16: - case Attributes.CM_P256: - charElement.classList.add(`xterm-bg-${bg}`); - break; - case Attributes.CM_RGB: - this._addStyle(charElement, `background-color:#${padStart(bg.toString(16), '0', 6)}`); - break; - case Attributes.CM_DEFAULT: - default: - if (isInverse) { - charElement.classList.add(`xterm-bg-${INVERTED_DEFAULT_COLOR}`); - } - } - - fragment.appendChild(charElement); - - x = lastCharX; - } - return fragment; - } - - private _applyMinimumContrast(element: HTMLElement, bg: IColor, fg: IColor): boolean { - if (this._optionsService.rawOptions.minimumContrastRatio === 1) { - return false; - } - - // Try get from cache first - let adjustedColor = this._colors.contrastCache.getColor(this._workCell.bg, this._workCell.fg); - - // Calculate and store in cache - if (adjustedColor === undefined) { - adjustedColor = color.ensureContrastRatio(bg, fg, this._optionsService.rawOptions.minimumContrastRatio); - this._colors.contrastCache.setColor(this._workCell.bg, this._workCell.fg, adjustedColor ?? null); - } - - if (adjustedColor) { - this._addStyle(element, `color:${adjustedColor.css}`); - return true; - } - - return false; - } - - private _addStyle(element: HTMLElement, style: string): void { - element.setAttribute('style', `${element.getAttribute('style') || ''}${style};`); - } -} - -function padStart(text: string, padChar: string, length: number): string { - while (text.length < length) { - text = padChar + text; - } - return text; -} |