mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into fixdemo
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables a canvas-based renderer using a 2d context to draw. This addon requires xterm.js v5+.
|
||||
|
||||
The purpose of this addon is to be used as a fallback for the [webgl addon](https://www.npmjs.com/package/xterm-addon-webgl) when better performance is desired over the default DOM renderer, but WebGL2 isn't supported or performant for some reason.
|
||||
|
||||
### Install
|
||||
|
||||
@@ -21,3 +22,7 @@ terminal.loadAddon(new CanvasAddon());
|
||||
```
|
||||
|
||||
See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/xterm-addon-canvas/typings/xterm-addon-canvas.d.ts) for more advanced usage.
|
||||
|
||||
### See also
|
||||
|
||||
- [xterm-addon-webgl](https://www.npmjs.com/package/xterm-addon-webgl) A renderer for xterm.js that uses WebGL
|
||||
|
||||
@@ -36,3 +36,7 @@ terminal.loadAddon(addon);
|
||||
```
|
||||
|
||||
Read more about handling WebGL context losses on the [Khronos wiki](https://www.khronos.org/webgl/wiki/HandlingContextLost).
|
||||
|
||||
### See also
|
||||
|
||||
- [xterm-addon-canvas](https://www.npmjs.com/package/xterm-addon-canvas) A renderer for xterm.js that uses a 2d canvas that can be used as a fallback when WebGL is not available
|
||||
|
||||
@@ -203,10 +203,11 @@ export class GlyphRenderer extends Disposable {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bg !== lastBg && rasterizedGlyph.offset.x > 0) {
|
||||
const clippedPixels = rasterizedGlyph.offset.x;
|
||||
const leftCellPadding = Math.floor((this._dimensions.scaledCellWidth - this._dimensions.scaledCharWidth) / 2);
|
||||
if (bg !== lastBg && rasterizedGlyph.offset.x > leftCellPadding) {
|
||||
const clippedPixels = rasterizedGlyph.offset.x - leftCellPadding;
|
||||
// a_origin
|
||||
array[i ] = this._dimensions.scaledCharLeft;
|
||||
array[i ] = -(rasterizedGlyph.offset.x - clippedPixels) + this._dimensions.scaledCharLeft;
|
||||
array[i + 1] = -rasterizedGlyph.offset.y + this._dimensions.scaledCharTop;
|
||||
// a_size
|
||||
array[i + 2] = (rasterizedGlyph.size.x - clippedPixels) / this._dimensions.scaledCanvasWidth;
|
||||
|
||||
@@ -12,6 +12,7 @@ import { IColorSet } from 'browser/Types';
|
||||
import { IRenderDimensions } from 'browser/renderer/Types';
|
||||
import { RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel';
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
import { DIM_OPACITY } from 'browser/renderer/Constants';
|
||||
|
||||
const enum VertexAttribLocations {
|
||||
POSITION = 0,
|
||||
@@ -208,6 +209,7 @@ export class RectangleRenderer extends Disposable {
|
||||
|
||||
private _updateRectangle(vertices: IVertices, offset: number, fg: number, bg: number, startX: number, endX: number, y: number): void {
|
||||
let rgba: number | undefined;
|
||||
let isDefault = false;
|
||||
if (fg & FgFlags.INVERSE) {
|
||||
switch (fg & Attributes.CM_MASK) {
|
||||
case Attributes.CM_P16:
|
||||
@@ -233,6 +235,7 @@ export class RectangleRenderer extends Disposable {
|
||||
case Attributes.CM_DEFAULT:
|
||||
default:
|
||||
rgba = this._colors.background.rgba;
|
||||
isDefault = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +247,7 @@ export class RectangleRenderer extends Disposable {
|
||||
const r = ((rgba >> 24) & 0xFF) / 255;
|
||||
const g = ((rgba >> 16) & 0xFF) / 255;
|
||||
const b = ((rgba >> 8 ) & 0xFF) / 255;
|
||||
const a = bg & BgFlags.DIM ? 0.5 : 1;
|
||||
const a = (!isDefault && bg & BgFlags.DIM) ? DIM_OPACITY : 1;
|
||||
|
||||
this._addRectangle(vertices.attributes, offset, x1, y1, (endX - startX) * this._dimensions.scaledCellWidth, this._dimensions.scaledCellHeight, r, g, b, a);
|
||||
}
|
||||
|
||||
@@ -7,13 +7,16 @@ import { Terminal, ITerminalAddon, IEvent } from 'xterm';
|
||||
import { WebglRenderer } from './WebglRenderer';
|
||||
import { ICharacterJoinerService, ICoreBrowserService, IRenderService } from 'browser/services/Services';
|
||||
import { IColorSet } from 'browser/Types';
|
||||
import { EventEmitter } from 'common/EventEmitter';
|
||||
import { EventEmitter, forwardEvent } from 'common/EventEmitter';
|
||||
import { isSafari } from 'common/Platform';
|
||||
import { ICoreService, IDecorationService } from 'common/services/Services';
|
||||
|
||||
export class WebglAddon implements ITerminalAddon {
|
||||
private _terminal?: Terminal;
|
||||
private _renderer?: WebglRenderer;
|
||||
|
||||
private _onChangeTextureAtlas = new EventEmitter<HTMLElement>();
|
||||
public get onChangeTextureAtlas(): IEvent<HTMLElement> { return this._onChangeTextureAtlas.event; }
|
||||
private _onContextLoss = new EventEmitter<void>();
|
||||
public get onContextLoss(): IEvent<void> { return this._onContextLoss.event; }
|
||||
|
||||
@@ -36,7 +39,8 @@ export class WebglAddon implements ITerminalAddon {
|
||||
const decorationService: IDecorationService = (terminal as any)._core._decorationService;
|
||||
const colors: IColorSet = (terminal as any)._core._colorManager.colors;
|
||||
this._renderer = new WebglRenderer(terminal, colors, characterJoinerService, coreBrowserService, coreService, decorationService, this._preserveDrawingBuffer);
|
||||
this._renderer.onContextLoss(() => this._onContextLoss.fire());
|
||||
forwardEvent(this._renderer.onContextLoss, this._onContextLoss);
|
||||
forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas);
|
||||
renderService.setRenderer(this._renderer);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import { ICharacterJoinerService, ICoreBrowserService } from 'browser/services/S
|
||||
import { CharData, ICellData } from 'common/Types';
|
||||
import { AttributeData } from 'common/buffer/AttributeData';
|
||||
import { ICoreService, IDecorationService } from 'common/services/Services';
|
||||
import { color, rgba as rgbaNs } from 'common/Color';
|
||||
|
||||
export class WebglRenderer extends Disposable implements IRenderer {
|
||||
private _renderLayers: IRenderLayer[];
|
||||
@@ -46,6 +45,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
private _core: ITerminal;
|
||||
private _isAttached: boolean;
|
||||
|
||||
private _onChangeTextureAtlas = new EventEmitter<HTMLCanvasElement>();
|
||||
public get onChangeTextureAtlas(): IEvent<HTMLCanvasElement> { return this._onChangeTextureAtlas.event; }
|
||||
private _onRequestRedraw = new EventEmitter<IRequestRedrawEvent>();
|
||||
public get onRequestRedraw(): IEvent<IRequestRedrawEvent> { return this._onRequestRedraw.event; }
|
||||
|
||||
@@ -240,7 +241,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
if (!('getRasterizedGlyph' in atlas)) {
|
||||
throw new Error('The webgl renderer only works with the webgl char atlas');
|
||||
}
|
||||
this._charAtlas = atlas as WebglCharAtlas;
|
||||
if (this._charAtlas !== atlas) {
|
||||
this._onChangeTextureAtlas.fire(atlas.cacheCanvas);
|
||||
}
|
||||
this._charAtlas = atlas;
|
||||
this._charAtlas.warmUp();
|
||||
this._glyphRenderer.setAtlas(this._charAtlas);
|
||||
}
|
||||
@@ -402,6 +406,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
// override logic throughout the different sub-renderers
|
||||
let bgOverride: number | undefined;
|
||||
let fgOverride: number | undefined;
|
||||
let isSelected: boolean = false;
|
||||
|
||||
// Apply decorations on the bottom layer
|
||||
for (const d of this._decorationService.getDecorationsAtCell(x, y, 'bottom')) {
|
||||
@@ -414,7 +419,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
// Apply the selection color if needed
|
||||
if (this._isCellSelected(x, y)) {
|
||||
isSelected = this._isCellSelected(x, y);
|
||||
if (isSelected) {
|
||||
bgOverride = (this._coreBrowserService.isFocused ? this._colors.selectionBackgroundOpaque : this._colors.selectionInactiveBackgroundOpaque).rgba >> 8 & 0xFFFFFF;
|
||||
if (this._colors.selectionForeground) {
|
||||
fgOverride = this._colors.selectionForeground.rgba >> 8 & 0xFFFFFF;
|
||||
@@ -434,8 +440,13 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
// Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag
|
||||
// ahead of time in order to use the correct cache key
|
||||
if (bgOverride !== undefined) {
|
||||
// Non-RGB attributes from model + override + force RGB color mode
|
||||
bgOverride = (this._workCell.bg & ~Attributes.RGB_MASK) | bgOverride | Attributes.CM_RGB;
|
||||
if (isSelected) {
|
||||
// Non-RGB attributes from model + force non-dim + override + force RGB color mode
|
||||
bgOverride = (this._workCell.bg & ~Attributes.RGB_MASK & ~BgFlags.DIM) | bgOverride | Attributes.CM_RGB;
|
||||
} else {
|
||||
// Non-RGB attributes from model + override + force RGB color mode
|
||||
bgOverride = (this._workCell.bg & ~Attributes.RGB_MASK) | bgOverride | Attributes.CM_RGB;
|
||||
}
|
||||
}
|
||||
if (fgOverride !== undefined) {
|
||||
// Non-RGB attributes from model + force disable inverse + override + force RGB color mode
|
||||
|
||||
@@ -45,6 +45,12 @@ const NULL_RASTERIZED_GLYPH: IRasterizedGlyph = {
|
||||
|
||||
const TMP_CANVAS_GLYPH_PADDING = 2;
|
||||
|
||||
interface ICharAtlasActiveRow {
|
||||
x: number;
|
||||
y: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export class WebglCharAtlas implements IDisposable {
|
||||
private _didWarmUp: boolean = false;
|
||||
|
||||
@@ -59,13 +65,22 @@ export class WebglCharAtlas implements IDisposable {
|
||||
// A temporary context that glyphs are drawn to before being transfered to the atlas.
|
||||
private _tmpCtx: CanvasRenderingContext2D;
|
||||
|
||||
// Since glyphs are expected to be around the same height, the packing
|
||||
// strategy used it to fill a row with glyphs while keeping track of the
|
||||
// tallest glyph in the row. Once the row is full a new row is started at
|
||||
// (0,lastRow+lastRowTallestGlyph).
|
||||
private _currentRowY: number = 0;
|
||||
private _currentRowX: number = 0;
|
||||
private _currentRowHeight: number = 0;
|
||||
// Texture atlas current positioning data. The texture packing strategy used is to fill from
|
||||
// left-to-right and top-to-bottom. When the glyph being written is less than half of the current
|
||||
// row's height, the following happens:
|
||||
//
|
||||
// - The current row becomes the fixed height row A
|
||||
// - A new fixed height row B the exact size of the glyph is created below the current row
|
||||
// - A new dynamic height current row is created below B
|
||||
//
|
||||
// This strategy does a good job preventing space being wasted for very short glyphs such as
|
||||
// underscores, hyphens etc. or those with underlines rendered.
|
||||
private _currentRow: ICharAtlasActiveRow = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
height: 0
|
||||
};
|
||||
private readonly _fixedRows: ICharAtlasActiveRow[] = [];
|
||||
|
||||
public hasCanvasChanged = false;
|
||||
|
||||
@@ -118,7 +133,7 @@ export class WebglCharAtlas implements IDisposable {
|
||||
}
|
||||
|
||||
public beginFrame(): boolean {
|
||||
if (this._currentRowY > TEXTURE_CAPACITY) {
|
||||
if (this._currentRow.y > TEXTURE_CAPACITY) {
|
||||
this.clearTexture();
|
||||
this.warmUp();
|
||||
return true;
|
||||
@@ -127,15 +142,16 @@ export class WebglCharAtlas implements IDisposable {
|
||||
}
|
||||
|
||||
public clearTexture(): void {
|
||||
if (this._currentRowX === 0 && this._currentRowY === 0) {
|
||||
if (this._currentRow.x === 0 && this._currentRow.y === 0) {
|
||||
return;
|
||||
}
|
||||
this._cacheCtx.clearRect(0, 0, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
this._cacheMap = {};
|
||||
this._cacheMapCombined = {};
|
||||
this._currentRowHeight = 0;
|
||||
this._currentRowX = 0;
|
||||
this._currentRowY = 0;
|
||||
this._currentRow.x = 0;
|
||||
this._currentRow.y = 0;
|
||||
this._currentRow.height = 0;
|
||||
this._fixedRows.length = 0;
|
||||
this._didWarmUp = false;
|
||||
}
|
||||
|
||||
@@ -225,7 +241,7 @@ export class WebglCharAtlas implements IDisposable {
|
||||
if (dim) {
|
||||
// Blend here instead of using opacity because transparent colors mess with clipping the
|
||||
// glyph's bounding box
|
||||
result = color.blend(this._config.colors.background, color.multiplyOpacity(result, 0.5));
|
||||
result = color.blend(this._config.colors.background, color.multiplyOpacity(result, DIM_OPACITY));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -267,7 +283,7 @@ export class WebglCharAtlas implements IDisposable {
|
||||
|
||||
// Apply dim to the color, opacity is fine to use for the foreground color
|
||||
if (dim) {
|
||||
result = color.multiplyOpacity(result, 0.5);
|
||||
result = color.multiplyOpacity(result, DIM_OPACITY);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -401,29 +417,31 @@ export class WebglCharAtlas implements IDisposable {
|
||||
`${fontStyle} ${fontWeight} ${this._config.fontSize * this._config.devicePixelRatio}px ${this._config.fontFamily}`;
|
||||
this._tmpCtx.textBaseline = TEXT_BASELINE;
|
||||
|
||||
const powerLineGlyph = chars.length === 1 && isPowerlineGlyph(chars.charCodeAt(0));
|
||||
const powerlineGlyph = chars.length === 1 && isPowerlineGlyph(chars.charCodeAt(0));
|
||||
const foregroundColor = this._getForegroundColor(bg, bgColorMode, bgColor, fg, fgColorMode, fgColor, inverse, dim, bold, excludeFromContrastRatioDemands(chars.charCodeAt(0)));
|
||||
this._tmpCtx.fillStyle = foregroundColor.css;
|
||||
|
||||
// For powerline glyphs left/top padding is excluded (https://github.com/microsoft/vscode/issues/120129)
|
||||
const padding = powerLineGlyph ? 0 : TMP_CANVAS_GLYPH_PADDING * 2;
|
||||
const padding = powerlineGlyph ? 0 : TMP_CANVAS_GLYPH_PADDING * 2;
|
||||
|
||||
// Draw custom characters if applicable
|
||||
let drawSuccess = false;
|
||||
let customGlyph = false;
|
||||
if (this._config.customGlyphs !== false) {
|
||||
drawSuccess = tryDrawCustomChar(this._tmpCtx, chars, padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight);
|
||||
customGlyph = tryDrawCustomChar(this._tmpCtx, chars, padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight);
|
||||
}
|
||||
|
||||
// Whether to clear pixels based on a threshold difference between the glyph color and the
|
||||
// background color. This should be disabled when the glyph contains multiple colors such as
|
||||
// underline colors to prevent important colors could get cleared.
|
||||
let enableClearThresholdCheck = true;
|
||||
let enableClearThresholdCheck = !powerlineGlyph;
|
||||
|
||||
// Draw underline
|
||||
if (underline) {
|
||||
this._tmpCtx.save();
|
||||
const lineWidth = Math.max(1, Math.floor(this._config.fontSize * window.devicePixelRatio / 10));
|
||||
const yOffset = this._tmpCtx.lineWidth % 2 === 1 ? 0.5 : 0; // When the width is odd, draw at 0.5 position
|
||||
const lineWidth = Math.max(1, Math.floor(this._config.fontSize * window.devicePixelRatio / 15));
|
||||
// When the width is odd, draw at 0.5 position. Offset by an additional 1 dpr to bring the
|
||||
// underline closer to the character
|
||||
const yOffset = (lineWidth % 2 === 1 ? 0.5 : 0) + window.devicePixelRatio;
|
||||
this._tmpCtx.lineWidth = lineWidth;
|
||||
|
||||
// Underline color
|
||||
@@ -492,18 +510,18 @@ export class WebglCharAtlas implements IDisposable {
|
||||
break;
|
||||
case UnderlineStyle.DOTTED:
|
||||
this._tmpCtx.setLineDash([window.devicePixelRatio * 2, window.devicePixelRatio]);
|
||||
this._tmpCtx.moveTo(xLeft, yMid);
|
||||
this._tmpCtx.lineTo(xRight, yMid);
|
||||
this._tmpCtx.moveTo(xLeft, yTop);
|
||||
this._tmpCtx.lineTo(xRight, yTop);
|
||||
break;
|
||||
case UnderlineStyle.DASHED:
|
||||
this._tmpCtx.setLineDash([window.devicePixelRatio * 4, window.devicePixelRatio * 3]);
|
||||
this._tmpCtx.moveTo(xLeft, yMid);
|
||||
this._tmpCtx.lineTo(xRight, yMid);
|
||||
this._tmpCtx.moveTo(xLeft, yTop);
|
||||
this._tmpCtx.lineTo(xRight, yTop);
|
||||
break;
|
||||
case UnderlineStyle.SINGLE:
|
||||
default:
|
||||
this._tmpCtx.moveTo(xLeft, yMid);
|
||||
this._tmpCtx.lineTo(xRight, yMid);
|
||||
this._tmpCtx.moveTo(xLeft, yTop);
|
||||
this._tmpCtx.lineTo(xRight, yTop);
|
||||
break;
|
||||
}
|
||||
this._tmpCtx.stroke();
|
||||
@@ -511,20 +529,28 @@ export class WebglCharAtlas implements IDisposable {
|
||||
|
||||
// Draw stroke in the background color for non custom characters in order to give an outline
|
||||
// between the text and the underline
|
||||
if (!drawSuccess) {
|
||||
if (!customGlyph) {
|
||||
// This only works when transparency is disabled because it's not clear how to clear stroked
|
||||
// text
|
||||
if (!this._config.allowTransparency && chars !== ' ') {
|
||||
// This translates to 1/2 the line width in either direction
|
||||
this._tmpCtx.save();
|
||||
// Clip the region to only draw in valid pixels near the underline to avoid a slight
|
||||
// outline around the whole glyph, as well as additional pixels in the glyph at the top
|
||||
// which would increase GPU memory demands
|
||||
const clipRegion = new Path2D();
|
||||
clipRegion.rect(xLeft, yTop - Math.ceil(lineWidth / 2), this._config.scaledCellWidth, yBot - yTop + Math.ceil(lineWidth / 2));
|
||||
this._tmpCtx.clip(clipRegion);
|
||||
this._tmpCtx.lineWidth = window.devicePixelRatio * 3;
|
||||
this._tmpCtx.strokeStyle = backgroundColor.css;
|
||||
this._tmpCtx.strokeText(chars, padding, padding + this._config.scaledCharHeight);
|
||||
this._tmpCtx.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the character
|
||||
if (!drawSuccess) {
|
||||
if (!customGlyph) {
|
||||
this._tmpCtx.fillText(chars, padding, padding + this._config.scaledCharHeight);
|
||||
}
|
||||
|
||||
@@ -577,25 +603,69 @@ export class WebglCharAtlas implements IDisposable {
|
||||
return NULL_RASTERIZED_GLYPH;
|
||||
}
|
||||
|
||||
const rasterizedGlyph = this._findGlyphBoundingBox(imageData, this._workBoundingBox, allowedWidth, powerLineGlyph, drawSuccess);
|
||||
const rasterizedGlyph = this._findGlyphBoundingBox(imageData, this._workBoundingBox, allowedWidth, powerlineGlyph, customGlyph, padding);
|
||||
const clippedImageData = this._clipImageData(imageData, this._workBoundingBox);
|
||||
|
||||
// Check if there is enough room in the current row and go to next if needed
|
||||
if (this._currentRowX + rasterizedGlyph.size.x > TEXTURE_WIDTH) {
|
||||
this._currentRowX = 0;
|
||||
this._currentRowY += this._currentRowHeight;
|
||||
this._currentRowHeight = 0;
|
||||
// Find the best atlas row to use
|
||||
let activeRow: ICharAtlasActiveRow;
|
||||
while (true) {
|
||||
// Select the ideal existing row, preferring fixed rows over the current row
|
||||
activeRow = this._currentRow;
|
||||
for (const row of this._fixedRows) {
|
||||
if ((activeRow === this._currentRow || row.height < activeRow.height) && rasterizedGlyph.size.y <= row.height) {
|
||||
activeRow = row;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new one if vertical space would be wasted, fixing the previously active row in the
|
||||
// process as it now has a fixed height
|
||||
if (activeRow.height > rasterizedGlyph.size.y * 2) {
|
||||
// Fix the current row as the new row is being added below
|
||||
if (this._currentRow.height > 0) {
|
||||
this._fixedRows.push(this._currentRow);
|
||||
}
|
||||
|
||||
// Create the new fixed height row
|
||||
activeRow = {
|
||||
x: 0,
|
||||
y: this._currentRow.y + this._currentRow.height,
|
||||
height: rasterizedGlyph.size.y
|
||||
};
|
||||
this._fixedRows.push(activeRow);
|
||||
|
||||
// Create the new current row below the new fixed height row
|
||||
this._currentRow = {
|
||||
x: 0,
|
||||
y: activeRow.y + activeRow.height,
|
||||
height: 0
|
||||
};
|
||||
}
|
||||
|
||||
// Exit the loop if there is enough room in the row
|
||||
if (activeRow.x + rasterizedGlyph.size.x <= TEXTURE_WIDTH) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If there is enough room in the current row, finish it and try again
|
||||
if (activeRow === this._currentRow) {
|
||||
activeRow.x = 0;
|
||||
activeRow.y += activeRow.height;
|
||||
activeRow.height = 0;
|
||||
} else {
|
||||
this._fixedRows.splice(this._fixedRows.indexOf(activeRow), 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Record texture position
|
||||
rasterizedGlyph.texturePosition.x = this._currentRowX;
|
||||
rasterizedGlyph.texturePosition.y = this._currentRowY;
|
||||
rasterizedGlyph.texturePositionClipSpace.x = this._currentRowX / TEXTURE_WIDTH;
|
||||
rasterizedGlyph.texturePositionClipSpace.y = this._currentRowY / TEXTURE_HEIGHT;
|
||||
rasterizedGlyph.texturePosition.x = activeRow.x;
|
||||
rasterizedGlyph.texturePosition.y = activeRow.y;
|
||||
rasterizedGlyph.texturePositionClipSpace.x = activeRow.x / TEXTURE_WIDTH;
|
||||
rasterizedGlyph.texturePositionClipSpace.y = activeRow.y / TEXTURE_HEIGHT;
|
||||
|
||||
// Update atlas current row
|
||||
this._currentRowHeight = Math.max(this._currentRowHeight, rasterizedGlyph.size.y);
|
||||
this._currentRowX += rasterizedGlyph.size.x;
|
||||
// Update atlas current row, for fixed rows the glyph height will never be larger than the row
|
||||
// height
|
||||
activeRow.height = Math.max(activeRow.height, rasterizedGlyph.size.y);
|
||||
activeRow.x += rasterizedGlyph.size.x;
|
||||
|
||||
// putImageData doesn't do any blending, so it will overwrite any existing cache entry for us
|
||||
this._cacheCtx.putImageData(clippedImageData, rasterizedGlyph.texturePosition.x, rasterizedGlyph.texturePosition.y);
|
||||
@@ -610,10 +680,10 @@ export class WebglCharAtlas implements IDisposable {
|
||||
* @param imageData The image data to read.
|
||||
* @param boundingBox An IBoundingBox to put the clipped bounding box values.
|
||||
*/
|
||||
private _findGlyphBoundingBox(imageData: ImageData, boundingBox: IBoundingBox, allowedWidth: number, restrictedGlyph: boolean, customGlyph: boolean): IRasterizedGlyph {
|
||||
private _findGlyphBoundingBox(imageData: ImageData, boundingBox: IBoundingBox, allowedWidth: number, restrictedGlyph: boolean, customGlyph: boolean, padding: number): IRasterizedGlyph {
|
||||
boundingBox.top = 0;
|
||||
const height = restrictedGlyph ? this._config.scaledCellHeight : this._tmpCanvas.height;
|
||||
const width = restrictedGlyph ? this._config.scaledCharWidth : allowedWidth;
|
||||
const width = restrictedGlyph ? this._config.scaledCellWidth : allowedWidth;
|
||||
let found = false;
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
@@ -685,8 +755,8 @@ export class WebglCharAtlas implements IDisposable {
|
||||
y: (boundingBox.bottom - boundingBox.top + 1) / TEXTURE_HEIGHT
|
||||
},
|
||||
offset: {
|
||||
x: -boundingBox.left + (restrictedGlyph ? 0 : TMP_CANVAS_GLYPH_PADDING) + (customGlyph ? Math.floor(this._config.letterSpacing / 2) : 0),
|
||||
y: -boundingBox.top + (restrictedGlyph ? 0 : TMP_CANVAS_GLYPH_PADDING) + (customGlyph ? this._config.lineHeight === 1 ? 0 : Math.round((this._config.scaledCellHeight - this._config.scaledCharHeight) / 2) : 0)
|
||||
x: -boundingBox.left + padding + ((restrictedGlyph || customGlyph) ? Math.floor((this._config.scaledCellWidth - this._config.scaledCharWidth) / 2) : 0),
|
||||
y: -boundingBox.top + padding + ((restrictedGlyph || customGlyph) ? this._config.lineHeight === 1 ? 0 : Math.round((this._config.scaledCellHeight - this._config.scaledCharHeight) / 2) : 0)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -763,8 +833,3 @@ function checkCompletelyTransparent(imageData: ImageData): boolean {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function toPaddedHex(c: number): string {
|
||||
const s = c.toString(16);
|
||||
return s.length < 2 ? '0' + s : s;
|
||||
}
|
||||
|
||||
+10
-5
@@ -12,6 +12,16 @@ declare module 'xterm-addon-webgl' {
|
||||
export class WebglAddon implements ITerminalAddon {
|
||||
public textureAtlas?: HTMLCanvasElement;
|
||||
|
||||
/**
|
||||
* An event that is fired when the renderer loses its canvas context.
|
||||
*/
|
||||
public get onContextLoss(): IEvent<void>;
|
||||
|
||||
/**
|
||||
* An event that is fired when the texture atlas of the renderer changes.
|
||||
*/
|
||||
public get onChangeTextureAtlas(): IEvent<HTMLCanvasElement>;
|
||||
|
||||
constructor(preserveDrawingBuffer?: boolean);
|
||||
|
||||
/**
|
||||
@@ -29,10 +39,5 @@ declare module 'xterm-addon-webgl' {
|
||||
* Clears the terminal's texture atlas and triggers a redraw.
|
||||
*/
|
||||
public clearTextureAtlas(): void;
|
||||
|
||||
/**
|
||||
* Fired when the WebglRenderer loses context
|
||||
*/
|
||||
public get onContextLoss(): IEvent<void>;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-11
@@ -240,7 +240,8 @@ function createTerminal(): void {
|
||||
try {
|
||||
typedTerm.loadAddon(addons.webgl.instance);
|
||||
setTimeout(() => {
|
||||
document.body.appendChild(addons.webgl.instance.textureAtlas);
|
||||
addTextureAtlas(addons.webgl.instance.textureAtlas);
|
||||
addons.webgl.instance.onChangeTextureAtlas(e => addTextureAtlas(e));
|
||||
}, 0);
|
||||
}
|
||||
catch {
|
||||
@@ -404,20 +405,18 @@ function initOptions(term: TerminalType): void {
|
||||
const input = <HTMLInputElement>document.getElementById(`opt-${o}`);
|
||||
addDomListener(input, 'change', () => {
|
||||
console.log('change', o, input.value);
|
||||
if (o === 'cols' || o === 'rows') {
|
||||
updateTerminalSize();
|
||||
} else if (o === 'lineHeight') {
|
||||
if (o === 'lineHeight') {
|
||||
term.options.lineHeight = parseFloat(input.value);
|
||||
updateTerminalSize();
|
||||
} else if (o === 'scrollSensitivity') {
|
||||
term.options.scrollSensitivity = parseFloat(input.value);
|
||||
updateTerminalSize();
|
||||
} else if (o === 'scrollback') {
|
||||
term.options.scrollback = parseInt(input.value);
|
||||
setTimeout(() => updateTerminalSize(), 5);
|
||||
} else {
|
||||
term.options[o] = parseInt(input.value);
|
||||
}
|
||||
// Always update terminal size in case the option changes the dimensions
|
||||
updateTerminalSize();
|
||||
});
|
||||
});
|
||||
Object.keys(stringOptions).forEach(o => {
|
||||
@@ -526,7 +525,7 @@ function initAddons(term: TerminalType): void {
|
||||
}
|
||||
} else {
|
||||
if (name === 'webgl') {
|
||||
document.body.removeChild((addon.instance as WebglAddon).textureAtlas);
|
||||
(addon.instance as WebglAddon).textureAtlas.remove();
|
||||
} else if (name === 'unicode11') {
|
||||
term.unicode.activeVersion = '6';
|
||||
}
|
||||
@@ -602,6 +601,9 @@ function htmlSerializeButtonHandler(): void {
|
||||
document.getElementById("htmlserialize-output-result").innerText = "Copied to clipboard";
|
||||
}
|
||||
|
||||
function addTextureAtlas(e: HTMLCanvasElement) {
|
||||
document.querySelector('#texture-atlas').appendChild(e);
|
||||
}
|
||||
|
||||
function writeCustomGlyphHandler() {
|
||||
term.write('\n\r');
|
||||
@@ -700,7 +702,7 @@ function powerlineSymbolTest() {
|
||||
` 3 \ue0b1 \x1b[33;44m\ue0b0\x1b[39m` +
|
||||
` 4 \ue0b1 \x1b[34;45m\ue0b0\x1b[39m` +
|
||||
` 5 \ue0b1 \x1b[35;46m\ue0b0\x1b[39m` +
|
||||
` 6 \ue0b1 \x1b[36;47m\ue0b0\x1b[39m` +
|
||||
` 6 \ue0b1 \x1b[36;47m\ue0b0\x1b[30m` +
|
||||
` 7 \ue0b1 \x1b[37;49m\ue0b0\x1b[0m`
|
||||
);
|
||||
term.writeln('');
|
||||
@@ -713,7 +715,7 @@ function powerlineSymbolTest() {
|
||||
` 3 \ue0b3 \x1b[7;33;44m\ue0b2\x1b[27;39m` +
|
||||
` 4 \ue0b3 \x1b[7;34;45m\ue0b2\x1b[27;39m` +
|
||||
` 5 \ue0b3 \x1b[7;35;46m\ue0b2\x1b[27;39m` +
|
||||
` 6 \ue0b3 \x1b[7;36;47m\ue0b2\x1b[27;39m` +
|
||||
` 6 \ue0b3 \x1b[7;36;47m\ue0b2\x1b[27;30m` +
|
||||
` 7 \ue0b3 \x1b[7;37;49m\ue0b2\x1b[0m`
|
||||
);
|
||||
term.writeln('');
|
||||
@@ -726,7 +728,7 @@ function powerlineSymbolTest() {
|
||||
` 3 \ue0b5 \x1b[33;44m\ue0b4\x1b[39m` +
|
||||
` 4 \ue0b5 \x1b[34;45m\ue0b4\x1b[39m` +
|
||||
` 5 \ue0b5 \x1b[35;46m\ue0b4\x1b[39m` +
|
||||
` 6 \ue0b5 \x1b[36;47m\ue0b4\x1b[39m` +
|
||||
` 6 \ue0b5 \x1b[36;47m\ue0b4\x1b[30m` +
|
||||
` 7 \ue0b5 \x1b[37;49m\ue0b4\x1b[0m`
|
||||
);
|
||||
term.writeln('');
|
||||
@@ -739,7 +741,7 @@ function powerlineSymbolTest() {
|
||||
` 3 \ue0b7 \x1b[7;33;44m\ue0b6\x1b[27;39m` +
|
||||
` 4 \ue0b7 \x1b[7;34;45m\ue0b6\x1b[27;39m` +
|
||||
` 5 \ue0b7 \x1b[7;35;46m\ue0b6\x1b[27;39m` +
|
||||
` 6 \ue0b7 \x1b[7;36;47m\ue0b6\x1b[27;39m` +
|
||||
` 6 \ue0b7 \x1b[7;36;47m\ue0b6\x1b[27;30m` +
|
||||
` 7 \ue0b7 \x1b[7;37;49m\ue0b6\x1b[0m`
|
||||
);
|
||||
term.writeln('');
|
||||
|
||||
@@ -87,6 +87,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="texture-atlas"></div>
|
||||
<script src="dist/client-bundle.js" defer ></script>
|
||||
<script>
|
||||
var tab = localStorage.getItem("tab");
|
||||
|
||||
@@ -89,3 +89,17 @@ pre {
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#texture-atlas {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
overflow: scroll;
|
||||
}
|
||||
#texture-atlas canvas {
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
#texture-atlas canvas:hover {
|
||||
/* zoom to 4x on hover */
|
||||
width: 4096px;
|
||||
height: 4096px;
|
||||
}
|
||||
|
||||
@@ -957,11 +957,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
}
|
||||
|
||||
public addMarker(cursorYOffset: number): IMarker | undefined {
|
||||
// Disallow markers on the alt buffer
|
||||
if (this.buffer !== this.buffers.normal) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.buffer.addMarker(this.buffer.ybase + this.buffer.y + cursorYOffset);
|
||||
}
|
||||
|
||||
|
||||
@@ -334,8 +334,8 @@ export const boxDrawingDefinitions: { [character: string]: { [fontWeight: number
|
||||
interface IVectorShape {
|
||||
d: string;
|
||||
type: VectorType;
|
||||
/** Padding to apply to the vector's x axis in CSS pixels. */
|
||||
horizontalPadding?: number;
|
||||
leftPadding?: number;
|
||||
rightPadding?: number;
|
||||
}
|
||||
|
||||
const enum VectorType {
|
||||
@@ -355,19 +355,19 @@ export const powerlineDefinitions: { [index: string]: IVectorShape } = {
|
||||
// Right triangle solid
|
||||
'\u{E0B0}': { d: 'M0,0 L1,.5 L0,1', type: VectorType.FILL },
|
||||
// Right triangle line
|
||||
'\u{E0B1}': { d: 'M0,0 L1,.5 L0,1', type: VectorType.STROKE, horizontalPadding: 0.5 },
|
||||
'\u{E0B1}': { d: 'M0,0 L1,.5 L0,1', type: VectorType.STROKE, leftPadding: window.devicePixelRatio / 2, rightPadding: window.devicePixelRatio / 2 },
|
||||
// Left triangle solid
|
||||
'\u{E0B2}': { d: 'M1,0 L0,.5 L1,1', type: VectorType.FILL },
|
||||
// Left triangle line
|
||||
'\u{E0B3}': { d: 'M1,0 L0,.5 L1,1', type: VectorType.STROKE, horizontalPadding: 0.5 },
|
||||
'\u{E0B3}': { d: 'M1,0 L0,.5 L1,1', type: VectorType.STROKE, leftPadding: window.devicePixelRatio / 2, rightPadding: window.devicePixelRatio / 2 },
|
||||
// Right semi-circle solid,
|
||||
'\u{E0B4}': { d: 'M0,0 L0,1 C0.552,1,1,0.776,1,.5 C1,0.224,0.552,0,0,0', type: VectorType.FILL },
|
||||
// Right semi-circle line,
|
||||
'\u{E0B5}': { d: 'M0,1 C0.552,1,1,0.776,1,.5 C1,0.224,0.552,0,0,0', type: VectorType.STROKE },
|
||||
'\u{E0B5}': { d: 'M0,1 C0.552,1,1,0.776,1,.5 C1,0.224,0.552,0,0,0', type: VectorType.STROKE, rightPadding: window.devicePixelRatio / 2 },
|
||||
// Left semi-circle solid,
|
||||
'\u{E0B6}': { d: 'M1,0 L1,1 C0.448,1,0,0.776,0,.5 C0,0.224,0.448,0,1,0', type: VectorType.FILL },
|
||||
// Left semi-circle line,
|
||||
'\u{E0B7}': { d: 'M1,1 C0.448,1,0,0.776,0,.5 C0,0.224,0.448,0,1,0', type: VectorType.STROKE }
|
||||
'\u{E0B7}': { d: 'M1,1 C0.448,1,0,0.776,0,.5 C0,0.224,0.448,0,1,0', type: VectorType.STROKE, leftPadding: window.devicePixelRatio / 2 }
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -590,7 +590,7 @@ function drawPowerlineChar(
|
||||
if (!args[0] || !args[1]) {
|
||||
continue;
|
||||
}
|
||||
f(ctx, translateArgs(args, scaledCellWidth, scaledCellHeight, xOffset, yOffset, charDefinition.horizontalPadding));
|
||||
f(ctx, translateArgs(args, scaledCellWidth, scaledCellHeight, xOffset, yOffset, charDefinition.leftPadding, charDefinition.rightPadding));
|
||||
}
|
||||
if (charDefinition.type === VectorType.STROKE) {
|
||||
ctx.strokeStyle = ctx.fillStyle;
|
||||
@@ -611,7 +611,7 @@ const svgToCanvasInstructionMap: { [index: string]: any } = {
|
||||
'M': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.moveTo(args[0], args[1])
|
||||
};
|
||||
|
||||
function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, horizontalPadding: number = 0): number[] {
|
||||
function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, leftPadding: number = 0, rightPadding: number = 0): number[] {
|
||||
const result = args.map(e => parseFloat(e) || parseInt(e));
|
||||
|
||||
if (result.length < 2) {
|
||||
@@ -620,14 +620,14 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO
|
||||
|
||||
for (let x = 0; x < result.length; x += 2) {
|
||||
// Translate from 0-1 to 0-cellWidth
|
||||
result[x] *= cellWidth - (horizontalPadding * 2 * window.devicePixelRatio);
|
||||
result[x] *= cellWidth - (leftPadding * window.devicePixelRatio) - (rightPadding * window.devicePixelRatio);
|
||||
// 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 + (horizontalPadding * window.devicePixelRatio);
|
||||
result[x] += xOffset + (leftPadding * window.devicePixelRatio);
|
||||
}
|
||||
|
||||
for (let y = 1; y < result.length; y += 2) {
|
||||
|
||||
@@ -139,7 +139,7 @@ describe('DomRendererRowFactory', () => {
|
||||
lineData.setCell(0, cell);
|
||||
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||
assert.equal(getFragmentHtml(fragment),
|
||||
'<span class="xterm-underline-1" style="text-decoration-color: rgb(255,255,255);">a</span>'
|
||||
'<span class="xterm-underline-1">a</span>'
|
||||
);
|
||||
});
|
||||
it('should add class for double underline style', () => {
|
||||
@@ -150,7 +150,7 @@ describe('DomRendererRowFactory', () => {
|
||||
lineData.setCell(0, cell);
|
||||
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||
assert.equal(getFragmentHtml(fragment),
|
||||
'<span class="xterm-underline-2" style="text-decoration-color: rgb(255,255,255);">a</span>'
|
||||
'<span class="xterm-underline-2">a</span>'
|
||||
);
|
||||
});
|
||||
it('should add class for curly underline style', () => {
|
||||
@@ -161,7 +161,7 @@ describe('DomRendererRowFactory', () => {
|
||||
lineData.setCell(0, cell);
|
||||
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||
assert.equal(getFragmentHtml(fragment),
|
||||
'<span class="xterm-underline-3" style="text-decoration-color: rgb(255,255,255);">a</span>'
|
||||
'<span class="xterm-underline-3">a</span>'
|
||||
);
|
||||
});
|
||||
it('should add class for double dotted style', () => {
|
||||
@@ -172,7 +172,7 @@ describe('DomRendererRowFactory', () => {
|
||||
lineData.setCell(0, cell);
|
||||
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||
assert.equal(getFragmentHtml(fragment),
|
||||
'<span class="xterm-underline-4" style="text-decoration-color: rgb(255,255,255);">a</span>'
|
||||
'<span class="xterm-underline-4">a</span>'
|
||||
);
|
||||
});
|
||||
it('should add class for dashed underline style', () => {
|
||||
@@ -183,7 +183,7 @@ describe('DomRendererRowFactory', () => {
|
||||
lineData.setCell(0, cell);
|
||||
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||
assert.equal(getFragmentHtml(fragment),
|
||||
'<span class="xterm-underline-5" style="text-decoration-color: rgb(255,255,255);">a</span>'
|
||||
'<span class="xterm-underline-5">a</span>'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -149,7 +149,7 @@ export class ExtendedAttrs implements IExtendedAttrs {
|
||||
|
||||
constructor(
|
||||
underlineStyle: UnderlineStyle = UnderlineStyle.NONE,
|
||||
underlineColor: number = -1
|
||||
underlineColor: number = Attributes.CM_DEFAULT
|
||||
) {
|
||||
this.underlineStyle = underlineStyle;
|
||||
this.underlineColor = underlineColor;
|
||||
|
||||
@@ -71,4 +71,14 @@ describe('BufferSet', () => {
|
||||
assert.equal(bufferSet.active.y, 10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('markers', () => {
|
||||
it('should clear the markers when the buffer is switched', () => {
|
||||
bufferSet.activateAltBuffer();
|
||||
bufferSet.alt.addMarker(1);
|
||||
assert.equal(bufferSet.alt.markers.length, 1);
|
||||
bufferSet.activateNormalBuffer();
|
||||
assert.equal(bufferSet.alt.markers.length, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -83,6 +83,7 @@ export class BufferSet extends Disposable implements IBufferSet {
|
||||
// The alt buffer should always be cleared when we switch to the normal
|
||||
// buffer. This frees up memory since the alt buffer should always be new
|
||||
// when activated.
|
||||
this._alt.clearAllMarkers();
|
||||
this._alt.clear();
|
||||
this._activeBuffer = this._normal;
|
||||
this._onBufferActivate.fire({
|
||||
|
||||
@@ -125,13 +125,10 @@ describe('Headless API Tests', function (): void {
|
||||
});
|
||||
it('get options', () => {
|
||||
const options: ITerminalOptions = term.options;
|
||||
strictEqual(options.cols, 80);
|
||||
strictEqual(options.rows, 24);
|
||||
strictEqual(options.lineHeight, 1);
|
||||
strictEqual(options.cursorWidth, 1);
|
||||
});
|
||||
it('set options', async () => {
|
||||
const options: ITerminalOptions = term.options;
|
||||
throws(() => options.cols = 40);
|
||||
throws(() => options.rows = 20);
|
||||
term.options.scrollback = 1;
|
||||
strictEqual(term.options.scrollback, 1);
|
||||
term.options= {
|
||||
|
||||
@@ -7,11 +7,10 @@ import { IEvent } from 'common/EventEmitter';
|
||||
import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi';
|
||||
import { ParserApi } from 'common/public/ParserApi';
|
||||
import { UnicodeApi } from 'common/public/UnicodeApi';
|
||||
import { IBufferNamespace as IBufferNamespaceApi, IMarker, IModes, IParser, ITerminalAddon, IUnicodeHandling, Terminal as ITerminalApi } from 'xterm-headless';
|
||||
import { IBufferNamespace as IBufferNamespaceApi, IMarker, IModes, IParser, ITerminalAddon, ITerminalInitOnlyOptions, IUnicodeHandling, Terminal as ITerminalApi } from 'xterm-headless';
|
||||
import { Terminal as TerminalCore } from 'headless/Terminal';
|
||||
import { AddonManager } from 'common/public/AddonManager';
|
||||
import { ITerminalOptions } from 'common/Types';
|
||||
|
||||
/**
|
||||
* The set of options that only have an effect when set in the Terminal constructor.
|
||||
*/
|
||||
@@ -24,7 +23,7 @@ export class Terminal implements ITerminalApi {
|
||||
private _buffer: BufferNamespaceApi | undefined;
|
||||
private _publicOptions: ITerminalOptions;
|
||||
|
||||
constructor(options?: ITerminalOptions) {
|
||||
constructor(options?: ITerminalOptions & ITerminalInitOnlyOptions) {
|
||||
this._core = new TerminalCore(options);
|
||||
this._addonManager = new AddonManager();
|
||||
|
||||
|
||||
Vendored
+18
-12
@@ -14,7 +14,7 @@ declare module 'xterm-headless' {
|
||||
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'off';
|
||||
|
||||
/**
|
||||
* An object containing start up options for the terminal.
|
||||
* An object containing options for the terminal.
|
||||
*/
|
||||
export interface ITerminalOptions {
|
||||
/**
|
||||
@@ -48,11 +48,6 @@ declare module 'xterm-headless' {
|
||||
*/
|
||||
convertEol?: boolean;
|
||||
|
||||
/**
|
||||
* The number of columns in the terminal.
|
||||
*/
|
||||
cols?: number;
|
||||
|
||||
/**
|
||||
* Whether the cursor blinks.
|
||||
*/
|
||||
@@ -145,11 +140,6 @@ declare module 'xterm-headless' {
|
||||
*/
|
||||
rightClickSelectsWord?: boolean;
|
||||
|
||||
/**
|
||||
* The number of rows in the terminal.
|
||||
*/
|
||||
rows?: number;
|
||||
|
||||
/**
|
||||
* Whether screen reader support is enabled. When on this will expose
|
||||
* supporting elements in the DOM to support NVDA on Windows and VoiceOver
|
||||
@@ -210,6 +200,22 @@ declare module 'xterm-headless' {
|
||||
windowOptions?: IWindowOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* An object containing additional options for the terminal that can only be
|
||||
* set on start up.
|
||||
*/
|
||||
export interface ITerminalInitOnlyOptions {
|
||||
/**
|
||||
* The number of columns in the terminal.
|
||||
*/
|
||||
cols?: number;
|
||||
|
||||
/**
|
||||
* The number of rows in the terminal.
|
||||
*/
|
||||
rows?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains colors to theme the terminal with.
|
||||
*/
|
||||
@@ -558,7 +564,7 @@ declare module 'xterm-headless' {
|
||||
*
|
||||
* @param options An object containing a set of options.
|
||||
*/
|
||||
constructor(options?: ITerminalOptions);
|
||||
constructor(options?: ITerminalOptions & ITerminalInitOnlyOptions);
|
||||
|
||||
/**
|
||||
* Adds an event listener for when the bell is triggered.
|
||||
|
||||
Reference in New Issue
Block a user