Merge branch 'master' into mouse_services
This commit is contained in:
@@ -4,16 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { IRenderLayer } from './Types';
|
import { IRenderLayer } from './Types';
|
||||||
import { ICellData } from 'common/Types';
|
|
||||||
import { DEFAULT_COLOR, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE } from 'common/buffer/Constants';
|
|
||||||
import { IGlyphIdentifier } from '../atlas/Types';
|
import { IGlyphIdentifier } from '../atlas/Types';
|
||||||
import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
|
||||||
import { acquireCharAtlas } from '../atlas/CharAtlasCache';
|
import { acquireCharAtlas } from '../atlas/CharAtlasCache';
|
||||||
import { Terminal } from 'xterm';
|
import { Terminal } from 'xterm';
|
||||||
import { IColorSet } from 'browser/Types';
|
import { IColorSet } from 'browser/Types';
|
||||||
import { IRenderDimensions } from 'browser/renderer/Types';
|
import { IRenderDimensions } from 'browser/renderer/Types';
|
||||||
import { CellData } from 'common/buffer/CellData';
|
import { CellData } from 'common/buffer/CellData';
|
||||||
import { AttributeData } from 'common/buffer/AttributeData';
|
|
||||||
import { WebglCharAtlas } from 'atlas/WebglCharAtlas';
|
import { WebglCharAtlas } from 'atlas/WebglCharAtlas';
|
||||||
import { throwIfFalsy } from '../WebglUtils';
|
import { throwIfFalsy } from '../WebglUtils';
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -78,7 +78,7 @@ class DECRQSS implements IDcsHandler {
|
|||||||
return this._coreService.triggerDataEvent(`${C0.ESC}P1$r${style} q${C0.ESC}\\`);
|
return this._coreService.triggerDataEvent(`${C0.ESC}P1$r${style} q${C0.ESC}\\`);
|
||||||
default:
|
default:
|
||||||
// invalid: DCS 0 $ r Pt ST (xterm)
|
// invalid: DCS 0 $ r Pt ST (xterm)
|
||||||
this._logService.error('Unknown DCS $q %s', data);
|
this._logService.debug('Unknown DCS $q %s', data);
|
||||||
this._coreService.triggerDataEvent(`${C0.ESC}P0$r${C0.ESC}\\`);
|
this._coreService.triggerDataEvent(`${C0.ESC}P0$r${C0.ESC}\\`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,16 +142,16 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
* custom fallback handlers
|
* custom fallback handlers
|
||||||
*/
|
*/
|
||||||
this._parser.setCsiHandlerFallback((collect: string, params: IParams, flag: number) => {
|
this._parser.setCsiHandlerFallback((collect: string, params: IParams, flag: number) => {
|
||||||
this._logService.error('Unknown CSI code: ', { collect, params: params.toArray(), flag: String.fromCharCode(flag) });
|
this._logService.debug('Unknown CSI code: ', { collect, params: params.toArray(), flag: String.fromCharCode(flag) });
|
||||||
});
|
});
|
||||||
this._parser.setEscHandlerFallback((collect: string, flag: number) => {
|
this._parser.setEscHandlerFallback((collect: string, flag: number) => {
|
||||||
this._logService.error('Unknown ESC code: ', { collect, flag: String.fromCharCode(flag) });
|
this._logService.debug('Unknown ESC code: ', { collect, flag: String.fromCharCode(flag) });
|
||||||
});
|
});
|
||||||
this._parser.setExecuteHandlerFallback((code: number) => {
|
this._parser.setExecuteHandlerFallback((code: number) => {
|
||||||
this._logService.error('Unknown EXECUTE code: ', { code });
|
this._logService.debug('Unknown EXECUTE code: ', { code });
|
||||||
});
|
});
|
||||||
this._parser.setOscHandlerFallback((identifier: number, data: string) => {
|
this._parser.setOscHandlerFallback((identifier: number, data: string) => {
|
||||||
this._logService.error('Unknown OSC code: ', { identifier, data });
|
this._logService.debug('Unknown OSC code: ', { identifier, data });
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1261,7 +1261,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
// this.cursorBlink = true;
|
// this.cursorBlink = true;
|
||||||
break;
|
break;
|
||||||
case 66:
|
case 66:
|
||||||
this._logService.info('Serial port requested application keypad.');
|
this._logService.debug('Serial port requested application keypad.');
|
||||||
this._terminal.applicationKeypad = true;
|
this._terminal.applicationKeypad = true;
|
||||||
if (this._terminal.viewport) {
|
if (this._terminal.viewport) {
|
||||||
this._terminal.viewport.syncScrollArea();
|
this._terminal.viewport.syncScrollArea();
|
||||||
@@ -1457,7 +1457,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
// this.cursorBlink = false;
|
// this.cursorBlink = false;
|
||||||
break;
|
break;
|
||||||
case 66:
|
case 66:
|
||||||
this._logService.info('Switching back to normal keypad.');
|
this._logService.debug('Switching back to normal keypad.');
|
||||||
this._terminal.applicationKeypad = false;
|
this._terminal.applicationKeypad = false;
|
||||||
if (this._terminal.viewport) {
|
if (this._terminal.viewport) {
|
||||||
this._terminal.viewport.syncScrollArea();
|
this._terminal.viewport.syncScrollArea();
|
||||||
@@ -1739,7 +1739,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
attr.bg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
|
attr.bg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
|
||||||
attr.bg |= DEFAULT_ATTR_DATA.bg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
|
attr.bg |= DEFAULT_ATTR_DATA.bg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
|
||||||
} else {
|
} else {
|
||||||
this._logService.error('Unknown SGR attribute: %d.', p);
|
this._logService.debug('Unknown SGR attribute: %d.', p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1953,7 +1953,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
* Enables the numeric keypad to send application sequences to the host.
|
* Enables the numeric keypad to send application sequences to the host.
|
||||||
*/
|
*/
|
||||||
public keypadApplicationMode(): void {
|
public keypadApplicationMode(): void {
|
||||||
this._logService.info('Serial port requested application keypad.');
|
this._logService.debug('Serial port requested application keypad.');
|
||||||
this._terminal.applicationKeypad = true;
|
this._terminal.applicationKeypad = true;
|
||||||
if (this._terminal.viewport) {
|
if (this._terminal.viewport) {
|
||||||
this._terminal.viewport.syncScrollArea();
|
this._terminal.viewport.syncScrollArea();
|
||||||
@@ -1966,7 +1966,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
* Enables the keypad to send numeric characters to the host.
|
* Enables the keypad to send numeric characters to the host.
|
||||||
*/
|
*/
|
||||||
public keypadNumericMode(): void {
|
public keypadNumericMode(): void {
|
||||||
this._logService.info('Switching back to normal keypad.');
|
this._logService.debug('Switching back to normal keypad.');
|
||||||
this._terminal.applicationKeypad = false;
|
this._terminal.applicationKeypad = false;
|
||||||
if (this._terminal.viewport) {
|
if (this._terminal.viewport) {
|
||||||
this._terminal.viewport.syncScrollArea();
|
this._terminal.viewport.syncScrollArea();
|
||||||
|
|||||||
+2
-3
@@ -38,7 +38,6 @@ import { SoundService } from 'browser/services/SoundService';
|
|||||||
import { MouseZoneManager } from 'browser/MouseZoneManager';
|
import { MouseZoneManager } from 'browser/MouseZoneManager';
|
||||||
import { AccessibilityManager } from './AccessibilityManager';
|
import { AccessibilityManager } from './AccessibilityManager';
|
||||||
import { ITheme, IMarker, IDisposable, ISelectionPosition } from 'xterm';
|
import { ITheme, IMarker, IDisposable, ISelectionPosition } from 'xterm';
|
||||||
import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache';
|
|
||||||
import { DomRenderer } from './renderer/dom/DomRenderer';
|
import { DomRenderer } from './renderer/dom/DomRenderer';
|
||||||
import { IKeyboardEvent, KeyboardResultType, ICharset, IBufferLine, IAttributeData, CoreMouseEventType, CoreMouseButton, CoreMouseAction } from 'common/Types';
|
import { IKeyboardEvent, KeyboardResultType, ICharset, IBufferLine, IAttributeData, CoreMouseEventType, CoreMouseButton, CoreMouseAction } from 'common/Types';
|
||||||
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
|
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
|
||||||
@@ -267,8 +266,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
this._windowsMode.dispose();
|
this._windowsMode.dispose();
|
||||||
this._windowsMode = undefined;
|
this._windowsMode = undefined;
|
||||||
}
|
}
|
||||||
|
this._renderService.dispose();
|
||||||
this._customKeyEventHandler = null;
|
this._customKeyEventHandler = null;
|
||||||
removeTerminalFromCache(this);
|
|
||||||
this.write = () => {};
|
this.write = () => {};
|
||||||
if (this.element && this.element.parentNode) {
|
if (this.element && this.element.parentNode) {
|
||||||
this.element.parentNode.removeChild(this.element);
|
this.element.parentNode.removeChild(this.element);
|
||||||
@@ -687,7 +686,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
|
|
||||||
private _createRenderer(): IRenderer {
|
private _createRenderer(): IRenderer {
|
||||||
switch (this.options.rendererType) {
|
switch (this.options.rendererType) {
|
||||||
case 'canvas': return new Renderer(this._colorManager.colors, this, this._bufferService, this._charSizeService);
|
case 'canvas': return new Renderer(this._colorManager.colors, this, this._bufferService, this._charSizeService, this.optionsService);
|
||||||
case 'dom': return new DomRenderer(this, this._colorManager.colors, this._charSizeService, this.optionsService);
|
case 'dom': return new DomRenderer(this, this._colorManager.colors, this._charSizeService, this.optionsService);
|
||||||
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
|
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+9
@@ -24,6 +24,15 @@ export interface IColorSet {
|
|||||||
ansi: IColor[];
|
ansi: IColor[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IPartialColorSet {
|
||||||
|
foreground: IColor;
|
||||||
|
background: IColor;
|
||||||
|
cursor?: IColor;
|
||||||
|
cursorAccent?: IColor;
|
||||||
|
selection?: IColor;
|
||||||
|
ansi: IColor[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface IViewport extends IDisposable {
|
export interface IViewport extends IDisposable {
|
||||||
scrollBarWidth: number;
|
scrollBarWidth: number;
|
||||||
syncScrollArea(): void;
|
syncScrollArea(): void;
|
||||||
|
|||||||
@@ -3,22 +3,22 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IRenderLayer } from './Types';
|
import { IRenderDimensions, IRenderLayer } from 'browser/renderer/Types';
|
||||||
import { IRenderDimensions } from 'browser/renderer/Types';
|
|
||||||
import { ITerminal } from '../Types';
|
|
||||||
import { ICellData } from 'common/Types';
|
import { ICellData } from 'common/Types';
|
||||||
import { DEFAULT_COLOR, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE } from 'common/buffer/Constants';
|
import { DEFAULT_COLOR, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE } from 'common/buffer/Constants';
|
||||||
import { IGlyphIdentifier } from './atlas/Types';
|
import { IGlyphIdentifier } from 'browser/renderer/atlas/Types';
|
||||||
import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
||||||
import { BaseCharAtlas } from './atlas/BaseCharAtlas';
|
import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas';
|
||||||
import { acquireCharAtlas } from './atlas/CharAtlasCache';
|
import { acquireCharAtlas } from 'browser/renderer/atlas/CharAtlasCache';
|
||||||
import { AttributeData } from 'common/buffer/AttributeData';
|
import { AttributeData } from 'common/buffer/AttributeData';
|
||||||
import { IColorSet } from 'browser/Types';
|
import { IColorSet } from 'browser/Types';
|
||||||
import { CellData } from 'common/buffer/CellData';
|
import { CellData } from 'common/buffer/CellData';
|
||||||
|
import { IBufferService, IOptionsService } from 'common/services/Services';
|
||||||
|
import { throwIfFalsy } from 'browser/renderer/RendererUtils';
|
||||||
|
|
||||||
export abstract class BaseRenderLayer implements IRenderLayer {
|
export abstract class BaseRenderLayer implements IRenderLayer {
|
||||||
private _canvas: HTMLCanvasElement;
|
private _canvas: HTMLCanvasElement;
|
||||||
protected _ctx: CanvasRenderingContext2D;
|
protected _ctx!: CanvasRenderingContext2D;
|
||||||
private _scaledCharWidth: number = 0;
|
private _scaledCharWidth: number = 0;
|
||||||
private _scaledCharHeight: number = 0;
|
private _scaledCharHeight: number = 0;
|
||||||
private _scaledCellWidth: number = 0;
|
private _scaledCellWidth: number = 0;
|
||||||
@@ -26,7 +26,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
private _scaledCharLeft: number = 0;
|
private _scaledCharLeft: number = 0;
|
||||||
private _scaledCharTop: number = 0;
|
private _scaledCharTop: number = 0;
|
||||||
|
|
||||||
protected _charAtlas: BaseCharAtlas;
|
protected _charAtlas: BaseCharAtlas | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object that's reused when drawing glyphs in order to reduce GC.
|
* An object that's reused when drawing glyphs in order to reduce GC.
|
||||||
@@ -46,7 +46,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
id: string,
|
id: string,
|
||||||
zIndex: number,
|
zIndex: number,
|
||||||
private _alpha: boolean,
|
private _alpha: boolean,
|
||||||
protected _colors: IColorSet
|
protected _colors: IColorSet,
|
||||||
|
private _rendererId: number,
|
||||||
|
protected readonly _bufferService: IBufferService,
|
||||||
|
protected readonly _optionsService: IOptionsService
|
||||||
) {
|
) {
|
||||||
this._canvas = document.createElement('canvas');
|
this._canvas = document.createElement('canvas');
|
||||||
this._canvas.classList.add(`xterm-${id}-layer`);
|
this._canvas.classList.add(`xterm-${id}-layer`);
|
||||||
@@ -63,25 +66,25 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _initCanvas(): void {
|
private _initCanvas(): void {
|
||||||
this._ctx = this._canvas.getContext('2d', {alpha: this._alpha});
|
this._ctx = throwIfFalsy(this._canvas.getContext('2d', {alpha: this._alpha}));
|
||||||
// Draw the background if this is an opaque layer
|
// Draw the background if this is an opaque layer
|
||||||
if (!this._alpha) {
|
if (!this._alpha) {
|
||||||
this._clearAll();
|
this._clearAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onOptionsChanged(terminal: ITerminal): void {}
|
public onOptionsChanged(): void {}
|
||||||
public onBlur(terminal: ITerminal): void {}
|
public onBlur(): void {}
|
||||||
public onFocus(terminal: ITerminal): void {}
|
public onFocus(): void {}
|
||||||
public onCursorMove(terminal: ITerminal): void {}
|
public onCursorMove(): void {}
|
||||||
public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {}
|
public onGridChanged(startRow: number, endRow: number): void {}
|
||||||
public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number], columnSelectMode: boolean = false): void {}
|
public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean = false): void {}
|
||||||
|
|
||||||
public setColors(terminal: ITerminal, colorSet: IColorSet): void {
|
public setColors(colorSet: IColorSet): void {
|
||||||
this._refreshCharAtlas(terminal, colorSet);
|
this._refreshCharAtlas(colorSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _setTransparency(terminal: ITerminal, alpha: boolean): void {
|
protected _setTransparency(alpha: boolean): void {
|
||||||
// Do nothing when alpha doesn't change
|
// Do nothing when alpha doesn't change
|
||||||
if (alpha === this._alpha) {
|
if (alpha === this._alpha) {
|
||||||
return;
|
return;
|
||||||
@@ -96,24 +99,23 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
this._container.replaceChild(this._canvas, oldCanvas);
|
this._container.replaceChild(this._canvas, oldCanvas);
|
||||||
|
|
||||||
// Regenerate char atlas and force a full redraw
|
// Regenerate char atlas and force a full redraw
|
||||||
this._refreshCharAtlas(terminal, this._colors);
|
this._refreshCharAtlas(this._colors);
|
||||||
this.onGridChanged(terminal, 0, terminal.rows - 1);
|
this.onGridChanged(0, this._bufferService.rows - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes the char atlas, aquiring a new one if necessary.
|
* Refreshes the char atlas, aquiring a new one if necessary.
|
||||||
* @param terminal The terminal.
|
|
||||||
* @param colorSet The color set to use for the char atlas.
|
* @param colorSet The color set to use for the char atlas.
|
||||||
*/
|
*/
|
||||||
private _refreshCharAtlas(terminal: ITerminal, colorSet: IColorSet): void {
|
private _refreshCharAtlas(colorSet: IColorSet): void {
|
||||||
if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) {
|
if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._charAtlas = acquireCharAtlas(terminal, colorSet, this._scaledCharWidth, this._scaledCharHeight);
|
this._charAtlas = acquireCharAtlas(this._optionsService.options, this._rendererId, colorSet, this._scaledCharWidth, this._scaledCharHeight);
|
||||||
this._charAtlas.warmUp();
|
this._charAtlas.warmUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize(terminal: ITerminal, dim: IRenderDimensions): void {
|
public resize(dim: IRenderDimensions): void {
|
||||||
this._scaledCellWidth = dim.scaledCellWidth;
|
this._scaledCellWidth = dim.scaledCellWidth;
|
||||||
this._scaledCellHeight = dim.scaledCellHeight;
|
this._scaledCellHeight = dim.scaledCellHeight;
|
||||||
this._scaledCharWidth = dim.scaledCharWidth;
|
this._scaledCharWidth = dim.scaledCharWidth;
|
||||||
@@ -130,10 +132,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
this._clearAll();
|
this._clearAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._refreshCharAtlas(terminal, this._colors);
|
this._refreshCharAtlas(this._colors);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract reset(terminal: ITerminal): void;
|
public abstract reset(): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fills 1+ cells completely. This uses the existing fillStyle on the context.
|
* Fills 1+ cells completely. This uses the existing fillStyle on the context.
|
||||||
@@ -233,16 +235,15 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
* Draws a truecolor character at the cell. The character will be clipped to
|
* 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
|
* 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.
|
* a wide character. This uses the existing fillStyle on the context.
|
||||||
* @param terminal The terminal.
|
|
||||||
* @param cell The cell data for the character to draw.
|
* @param cell The cell data for the character to draw.
|
||||||
* @param x The column to draw at.
|
* @param x The column to draw at.
|
||||||
* @param y The row to draw at.
|
* @param y The row to draw at.
|
||||||
* @param color The color of the character.
|
* @param color The color of the character.
|
||||||
*/
|
*/
|
||||||
protected _fillCharTrueColor(terminal: ITerminal, cell: CellData, x: number, y: number): void {
|
protected _fillCharTrueColor(cell: CellData, x: number, y: number): void {
|
||||||
this._ctx.font = this._getFont(terminal, false, false);
|
this._ctx.font = this._getFont(false, false);
|
||||||
this._ctx.textBaseline = 'middle';
|
this._ctx.textBaseline = 'middle';
|
||||||
this._clipRow(terminal, y);
|
this._clipRow(y);
|
||||||
this._ctx.fillText(
|
this._ctx.fillText(
|
||||||
cell.getChars(),
|
cell.getChars(),
|
||||||
x * this._scaledCellWidth + this._scaledCharLeft,
|
x * this._scaledCellWidth + this._scaledCharLeft,
|
||||||
@@ -252,7 +253,6 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
/**
|
/**
|
||||||
* Draws one or more characters at a cell. If possible this will draw using
|
* Draws one or more characters at a cell. If possible this will draw using
|
||||||
* the character atlas to reduce draw time.
|
* the character atlas to reduce draw time.
|
||||||
* @param terminal The terminal.
|
|
||||||
* @param chars The character or characters.
|
* @param chars The character or characters.
|
||||||
* @param code The character code.
|
* @param code The character code.
|
||||||
* @param width The width of the characters.
|
* @param width The width of the characters.
|
||||||
@@ -263,14 +263,14 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
* This is used to validate whether a cached image can be used.
|
* This is used to validate whether a cached image can be used.
|
||||||
* @param bold Whether the text is bold.
|
* @param bold Whether the text is bold.
|
||||||
*/
|
*/
|
||||||
protected _drawChars(terminal: ITerminal, cell: ICellData, x: number, y: number): void {
|
protected _drawChars(cell: ICellData, x: number, y: number): void {
|
||||||
|
|
||||||
// skip cache right away if we draw in RGB
|
// skip cache right away if we draw in RGB
|
||||||
// Note: to avoid bad runtime JoinedCellData will be skipped
|
// Note: to avoid bad runtime JoinedCellData will be skipped
|
||||||
// in the cache handler itself (atlasDidDraw == false) and
|
// in the cache handler itself (atlasDidDraw == false) and
|
||||||
// fall through to uncached later down below
|
// fall through to uncached later down below
|
||||||
if (cell.isFgRGB() || cell.isBgRGB()) {
|
if (cell.isFgRGB() || cell.isBgRGB()) {
|
||||||
this._drawUncachedChars(terminal, cell, x, y);
|
this._drawUncachedChars(cell, x, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +284,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
fg = (cell.isFgDefault()) ? DEFAULT_COLOR : cell.getFgColor();
|
fg = (cell.isFgDefault()) ? DEFAULT_COLOR : cell.getFgColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
const drawInBrightColor = terminal.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8 && fg !== INVERTED_DEFAULT_COLOR;
|
const drawInBrightColor = this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8 && fg !== INVERTED_DEFAULT_COLOR;
|
||||||
|
|
||||||
fg += drawInBrightColor ? 8 : 0;
|
fg += drawInBrightColor ? 8 : 0;
|
||||||
this._currentGlyphIdentifier.chars = cell.getChars() || WHITESPACE_CELL_CHAR;
|
this._currentGlyphIdentifier.chars = cell.getChars() || WHITESPACE_CELL_CHAR;
|
||||||
@@ -302,7 +302,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!atlasDidDraw) {
|
if (!atlasDidDraw) {
|
||||||
this._drawUncachedChars(terminal, cell, x, y);
|
this._drawUncachedChars(cell, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,16 +310,15 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
* Draws one or more characters at one or more cells. The character(s) will be
|
* 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
|
* clipped to ensure that they fit with the cell(s), including the cell to the
|
||||||
* right if the last character is a wide character.
|
* right if the last character is a wide character.
|
||||||
* @param terminal The terminal.
|
|
||||||
* @param chars The character.
|
* @param chars The character.
|
||||||
* @param width The width of the character.
|
* @param width The width of the character.
|
||||||
* @param fg The foreground color, in the format stored within the attributes.
|
* @param fg The foreground color, in the format stored within the attributes.
|
||||||
* @param x The column to draw at.
|
* @param x The column to draw at.
|
||||||
* @param y The row to draw at.
|
* @param y The row to draw at.
|
||||||
*/
|
*/
|
||||||
private _drawUncachedChars(terminal: ITerminal, cell: ICellData, x: number, y: number): void {
|
private _drawUncachedChars(cell: ICellData, x: number, y: number): void {
|
||||||
this._ctx.save();
|
this._ctx.save();
|
||||||
this._ctx.font = this._getFont(terminal, !!cell.isBold(), !!cell.isItalic());
|
this._ctx.font = this._getFont(!!cell.isBold(), !!cell.isItalic());
|
||||||
this._ctx.textBaseline = 'middle';
|
this._ctx.textBaseline = 'middle';
|
||||||
|
|
||||||
if (cell.isInverse()) {
|
if (cell.isInverse()) {
|
||||||
@@ -337,14 +336,14 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;
|
this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;
|
||||||
} else {
|
} else {
|
||||||
let fg = cell.getFgColor();
|
let fg = cell.getFgColor();
|
||||||
if (terminal.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {
|
if (this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {
|
||||||
fg += 8;
|
fg += 8;
|
||||||
}
|
}
|
||||||
this._ctx.fillStyle = this._colors.ansi[fg].css;
|
this._ctx.fillStyle = this._colors.ansi[fg].css;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._clipRow(terminal, y);
|
this._clipRow(y);
|
||||||
|
|
||||||
// Apply alpha to dim the character
|
// Apply alpha to dim the character
|
||||||
if (cell.isDim()) {
|
if (cell.isDim()) {
|
||||||
@@ -360,29 +359,27 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Clips a row to ensure no pixels will be drawn outside the cells in the row.
|
* Clips a row to ensure no pixels will be drawn outside the cells in the row.
|
||||||
* @param terminal The terminal.
|
|
||||||
* @param y The row to clip.
|
* @param y The row to clip.
|
||||||
*/
|
*/
|
||||||
private _clipRow(terminal: ITerminal, y: number): void {
|
private _clipRow(y: number): void {
|
||||||
this._ctx.beginPath();
|
this._ctx.beginPath();
|
||||||
this._ctx.rect(
|
this._ctx.rect(
|
||||||
0,
|
0,
|
||||||
y * this._scaledCellHeight,
|
y * this._scaledCellHeight,
|
||||||
terminal.cols * this._scaledCellWidth,
|
this._bufferService.cols * this._scaledCellWidth,
|
||||||
this._scaledCellHeight);
|
this._scaledCellHeight);
|
||||||
this._ctx.clip();
|
this._ctx.clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current font.
|
* Gets the current font.
|
||||||
* @param terminal The terminal.
|
|
||||||
* @param isBold If we should use the bold fontWeight.
|
* @param isBold If we should use the bold fontWeight.
|
||||||
*/
|
*/
|
||||||
protected _getFont(terminal: ITerminal, isBold: boolean, isItalic: boolean): string {
|
protected _getFont(isBold: boolean, isItalic: boolean): string {
|
||||||
const fontWeight = isBold ? terminal.options.fontWeightBold : terminal.options.fontWeight;
|
const fontWeight = isBold ? this._optionsService.options.fontWeightBold : this._optionsService.options.fontWeight;
|
||||||
const fontStyle = isItalic ? 'italic' : '';
|
const fontStyle = isItalic ? 'italic' : '';
|
||||||
|
|
||||||
return `${fontStyle} ${fontWeight} ${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`;
|
return `${fontStyle} ${fontWeight} ${this._optionsService.options.fontSize * window.devicePixelRatio}px ${this._optionsService.options.fontFamily}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3,29 +3,37 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ITerminal, ILinkifierAccessor } from '../Types';
|
|
||||||
import { IRenderDimensions } from 'browser/renderer/Types';
|
import { IRenderDimensions } from 'browser/renderer/Types';
|
||||||
import { BaseRenderLayer } from './BaseRenderLayer';
|
import { BaseRenderLayer } from './BaseRenderLayer';
|
||||||
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
||||||
import { is256Color } from './atlas/CharAtlasUtils';
|
import { is256Color } from 'browser/renderer/atlas/CharAtlasUtils';
|
||||||
import { IColorSet, ILinkifierEvent } from 'browser/Types';
|
import { IColorSet, ILinkifierEvent, ILinkifier } from 'browser/Types';
|
||||||
|
import { IBufferService, IOptionsService } from 'common/services/Services';
|
||||||
|
|
||||||
export class LinkRenderLayer extends BaseRenderLayer {
|
export class LinkRenderLayer extends BaseRenderLayer {
|
||||||
private _state: ILinkifierEvent = null;
|
private _state: ILinkifierEvent | undefined;
|
||||||
|
|
||||||
constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ILinkifierAccessor) {
|
constructor(
|
||||||
super(container, 'link', zIndex, true, colors);
|
container: HTMLElement,
|
||||||
terminal.linkifier.onLinkHover(e => this._onLinkHover(e));
|
zIndex: number,
|
||||||
terminal.linkifier.onLinkLeave(e => this._onLinkLeave(e));
|
colors: IColorSet,
|
||||||
|
rendererId: number,
|
||||||
|
linkifier: ILinkifier,
|
||||||
|
readonly bufferService: IBufferService,
|
||||||
|
readonly optionsService: IOptionsService
|
||||||
|
) {
|
||||||
|
super(container, 'link', zIndex, true, colors, rendererId, bufferService, optionsService);
|
||||||
|
linkifier.onLinkHover(e => this._onLinkHover(e));
|
||||||
|
linkifier.onLinkLeave(e => this._onLinkLeave(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize(terminal: ITerminal, dim: IRenderDimensions): void {
|
public resize(dim: IRenderDimensions): void {
|
||||||
super.resize(terminal, dim);
|
super.resize(dim);
|
||||||
// Resizing the canvas discards the contents of the canvas so clear state
|
// Resizing the canvas discards the contents of the canvas so clear state
|
||||||
this._state = null;
|
this._state = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public reset(terminal: ITerminal): void {
|
public reset(): void {
|
||||||
this._clearCurrentLink();
|
this._clearCurrentLink();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,14 +45,14 @@ export class LinkRenderLayer extends BaseRenderLayer {
|
|||||||
this._clearCells(0, this._state.y1 + 1, this._state.cols, middleRowCount);
|
this._clearCells(0, this._state.y1 + 1, this._state.cols, middleRowCount);
|
||||||
}
|
}
|
||||||
this._clearCells(0, this._state.y2, this._state.x2, 1);
|
this._clearCells(0, this._state.y2, this._state.x2, 1);
|
||||||
this._state = null;
|
this._state = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onLinkHover(e: ILinkifierEvent): void {
|
private _onLinkHover(e: ILinkifierEvent): void {
|
||||||
if (e.fg === INVERTED_DEFAULT_COLOR) {
|
if (e.fg === INVERTED_DEFAULT_COLOR) {
|
||||||
this._ctx.fillStyle = this._colors.background.css;
|
this._ctx.fillStyle = this._colors.background.css;
|
||||||
} else if (is256Color(e.fg)) {
|
} else if (e.fg && is256Color(e.fg)) {
|
||||||
// 256 color support
|
// 256 color support
|
||||||
this._ctx.fillStyle = this._colors.ansi[e.fg].css;
|
this._ctx.fillStyle = this._colors.ansi[e.fg].css;
|
||||||
} else {
|
} else {
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
+34
-27
@@ -3,51 +3,58 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ITerminal } from '../Types';
|
|
||||||
import { IRenderDimensions } from 'browser/renderer/Types';
|
import { IRenderDimensions } from 'browser/renderer/Types';
|
||||||
import { BaseRenderLayer } from './BaseRenderLayer';
|
import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer';
|
||||||
import { IColorSet } from 'browser/Types';
|
import { IColorSet } from 'browser/Types';
|
||||||
|
import { IBufferService, IOptionsService } from 'common/services/Services';
|
||||||
|
|
||||||
interface ISelectionState {
|
interface ISelectionState {
|
||||||
start: [number, number];
|
start?: [number, number];
|
||||||
end: [number, number];
|
end?: [number, number];
|
||||||
columnSelectMode: boolean;
|
columnSelectMode?: boolean;
|
||||||
ydisp: number;
|
ydisp?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SelectionRenderLayer extends BaseRenderLayer {
|
export class SelectionRenderLayer extends BaseRenderLayer {
|
||||||
private _state: ISelectionState;
|
private _state!: ISelectionState;
|
||||||
|
|
||||||
constructor(container: HTMLElement, zIndex: number, colors: IColorSet) {
|
constructor(
|
||||||
super(container, 'selection', zIndex, true, colors);
|
container: HTMLElement,
|
||||||
|
zIndex: number,
|
||||||
|
colors: IColorSet,
|
||||||
|
rendererId: number,
|
||||||
|
readonly bufferService: IBufferService,
|
||||||
|
readonly optionsService: IOptionsService
|
||||||
|
) {
|
||||||
|
super(container, 'selection', zIndex, true, colors, rendererId, bufferService, optionsService);
|
||||||
this._clearState();
|
this._clearState();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _clearState(): void {
|
private _clearState(): void {
|
||||||
this._state = {
|
this._state = {
|
||||||
start: null,
|
start: undefined,
|
||||||
end: null,
|
end: undefined,
|
||||||
columnSelectMode: null,
|
columnSelectMode: undefined,
|
||||||
ydisp: null
|
ydisp: undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize(terminal: ITerminal, dim: IRenderDimensions): void {
|
public resize(dim: IRenderDimensions): void {
|
||||||
super.resize(terminal, dim);
|
super.resize(dim);
|
||||||
// Resizing the canvas discards the contents of the canvas so clear state
|
// Resizing the canvas discards the contents of the canvas so clear state
|
||||||
this._clearState();
|
this._clearState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public reset(terminal: ITerminal): void {
|
public reset(): void {
|
||||||
if (this._state.start && this._state.end) {
|
if (this._state.start && this._state.end) {
|
||||||
this._clearState();
|
this._clearState();
|
||||||
this._clearAll();
|
this._clearAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number], columnSelectMode: boolean): void {
|
public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean): void {
|
||||||
// Selection has not changed
|
// Selection has not changed
|
||||||
if (!this._didStateChange(start, end, columnSelectMode, terminal.buffer.ydisp)) {
|
if (!this._didStateChange(start, end, columnSelectMode, this._bufferService.buffer.ydisp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,13 +68,13 @@ export class SelectionRenderLayer extends BaseRenderLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Translate from buffer position to viewport position
|
// Translate from buffer position to viewport position
|
||||||
const viewportStartRow = start[1] - terminal.buffer.ydisp;
|
const viewportStartRow = start[1] - this._bufferService.buffer.ydisp;
|
||||||
const viewportEndRow = end[1] - terminal.buffer.ydisp;
|
const viewportEndRow = end[1] - this._bufferService.buffer.ydisp;
|
||||||
const viewportCappedStartRow = Math.max(viewportStartRow, 0);
|
const viewportCappedStartRow = Math.max(viewportStartRow, 0);
|
||||||
const viewportCappedEndRow = Math.min(viewportEndRow, terminal.rows - 1);
|
const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1);
|
||||||
|
|
||||||
// No need to draw the selection
|
// No need to draw the selection
|
||||||
if (viewportCappedStartRow >= terminal.rows || viewportCappedEndRow < 0) {
|
if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,17 +88,17 @@ export class SelectionRenderLayer extends BaseRenderLayer {
|
|||||||
} else {
|
} else {
|
||||||
// Draw first row
|
// Draw first row
|
||||||
const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
|
const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
|
||||||
const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : terminal.cols;
|
const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : this._bufferService.cols;
|
||||||
this._fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1);
|
this._fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1);
|
||||||
|
|
||||||
// Draw middle rows
|
// Draw middle rows
|
||||||
const middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0);
|
const middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0);
|
||||||
this._fillCells(0, viewportCappedStartRow + 1, terminal.cols, middleRowsCount);
|
this._fillCells(0, viewportCappedStartRow + 1, this._bufferService.cols, middleRowsCount);
|
||||||
|
|
||||||
// Draw final row
|
// Draw final row
|
||||||
if (viewportCappedStartRow !== viewportCappedEndRow) {
|
if (viewportCappedStartRow !== viewportCappedEndRow) {
|
||||||
// Only draw viewportEndRow if it's not the same as viewportStartRow
|
// Only draw viewportEndRow if it's not the same as viewportStartRow
|
||||||
const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : terminal.cols;
|
const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._bufferService.cols;
|
||||||
this._fillCells(0, viewportCappedEndRow, endCol, 1);
|
this._fillCells(0, viewportCappedEndRow, endCol, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,7 +107,7 @@ export class SelectionRenderLayer extends BaseRenderLayer {
|
|||||||
this._state.start = [start[0], start[1]];
|
this._state.start = [start[0], start[1]];
|
||||||
this._state.end = [end[0], end[1]];
|
this._state.end = [end[0], end[1]];
|
||||||
this._state.columnSelectMode = columnSelectMode;
|
this._state.columnSelectMode = columnSelectMode;
|
||||||
this._state.ydisp = terminal.buffer.ydisp;
|
this._state.ydisp = this._bufferService.buffer.ydisp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _didStateChange(start: [number, number], end: [number, number], columnSelectMode: boolean, ydisp: number): boolean {
|
private _didStateChange(start: [number, number], end: [number, number], columnSelectMode: boolean, ydisp: number): boolean {
|
||||||
@@ -110,7 +117,7 @@ export class SelectionRenderLayer extends BaseRenderLayer {
|
|||||||
ydisp !== this._state.ydisp;
|
ydisp !== this._state.ydisp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _areCoordinatesEqual(coord1: [number, number], coord2: [number, number]): boolean {
|
private _areCoordinatesEqual(coord1: [number, number] | undefined, coord2: [number, number] | undefined): boolean {
|
||||||
if (!coord1 || !coord2) {
|
if (!coord1 || !coord2) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -4,15 +4,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ICharacterJoinerRegistry, IRenderDimensions } from 'browser/renderer/Types';
|
import { ICharacterJoinerRegistry, IRenderDimensions } from 'browser/renderer/Types';
|
||||||
import { ITerminal } from '../Types';
|
|
||||||
import { CharData, ICellData } from 'common/Types';
|
import { CharData, ICellData } from 'common/Types';
|
||||||
import { GridCache } from 'browser/renderer/GridCache';
|
import { GridCache } from 'browser/renderer/GridCache';
|
||||||
import { BaseRenderLayer } from './BaseRenderLayer';
|
import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer';
|
||||||
import { AttributeData } from 'common/buffer/AttributeData';
|
import { AttributeData } from 'common/buffer/AttributeData';
|
||||||
import { NULL_CELL_CODE, Content } from 'common/buffer/Constants';
|
import { NULL_CELL_CODE, Content } from 'common/buffer/Constants';
|
||||||
import { JoinedCellData } from 'browser/renderer/CharacterJoinerRegistry';
|
import { JoinedCellData } from 'browser/renderer/CharacterJoinerRegistry';
|
||||||
import { IColorSet } from 'browser/Types';
|
import { IColorSet } from 'browser/Types';
|
||||||
import { CellData } from 'common/buffer/CellData';
|
import { CellData } from 'common/buffer/CellData';
|
||||||
|
import { IOptionsService, IBufferService } from 'common/services/Services';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This CharData looks like a null character, which will forc a clear and render
|
* This CharData looks like a null character, which will forc a clear and render
|
||||||
@@ -23,23 +23,32 @@ import { CellData } from 'common/buffer/CellData';
|
|||||||
|
|
||||||
export class TextRenderLayer extends BaseRenderLayer {
|
export class TextRenderLayer extends BaseRenderLayer {
|
||||||
private _state: GridCache<CharData>;
|
private _state: GridCache<CharData>;
|
||||||
private _characterWidth: number;
|
private _characterWidth: number = 0;
|
||||||
private _characterFont: string;
|
private _characterFont: string = '';
|
||||||
private _characterOverlapCache: { [key: string]: boolean } = {};
|
private _characterOverlapCache: { [key: string]: boolean } = {};
|
||||||
private _characterJoinerRegistry: ICharacterJoinerRegistry;
|
private _characterJoinerRegistry: ICharacterJoinerRegistry;
|
||||||
private _workCell = new CellData();
|
private _workCell = new CellData();
|
||||||
|
|
||||||
constructor(container: HTMLElement, zIndex: number, colors: IColorSet, characterJoinerRegistry: ICharacterJoinerRegistry, alpha: boolean) {
|
constructor(
|
||||||
super(container, 'text', zIndex, alpha, colors);
|
container: HTMLElement,
|
||||||
|
zIndex: number,
|
||||||
|
colors: IColorSet,
|
||||||
|
characterJoinerRegistry: ICharacterJoinerRegistry,
|
||||||
|
alpha: boolean,
|
||||||
|
rendererId: number,
|
||||||
|
readonly bufferService: IBufferService,
|
||||||
|
readonly optionsService: IOptionsService
|
||||||
|
) {
|
||||||
|
super(container, 'text', zIndex, alpha, colors, rendererId, bufferService, optionsService);
|
||||||
this._state = new GridCache<CharData>();
|
this._state = new GridCache<CharData>();
|
||||||
this._characterJoinerRegistry = characterJoinerRegistry;
|
this._characterJoinerRegistry = characterJoinerRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize(terminal: ITerminal, dim: IRenderDimensions): void {
|
public resize(dim: IRenderDimensions): void {
|
||||||
super.resize(terminal, dim);
|
super.resize(dim);
|
||||||
|
|
||||||
// Clear the character width cache if the font or width has changed
|
// Clear the character width cache if the font or width has changed
|
||||||
const terminalFont = this._getFont(terminal, false, false);
|
const terminalFont = this._getFont(false, false);
|
||||||
if (this._characterWidth !== dim.scaledCharWidth || this._characterFont !== terminalFont) {
|
if (this._characterWidth !== dim.scaledCharWidth || this._characterFont !== terminalFont) {
|
||||||
this._characterWidth = dim.scaledCharWidth;
|
this._characterWidth = dim.scaledCharWidth;
|
||||||
this._characterFont = terminalFont;
|
this._characterFont = terminalFont;
|
||||||
@@ -47,16 +56,15 @@ export class TextRenderLayer extends BaseRenderLayer {
|
|||||||
}
|
}
|
||||||
// Resizing the canvas discards the contents of the canvas so clear state
|
// Resizing the canvas discards the contents of the canvas so clear state
|
||||||
this._state.clear();
|
this._state.clear();
|
||||||
this._state.resize(terminal.cols, terminal.rows);
|
this._state.resize(this._bufferService.cols, this._bufferService.rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
public reset(terminal: ITerminal): void {
|
public reset(): void {
|
||||||
this._state.clear();
|
this._state.clear();
|
||||||
this._clearAll();
|
this._clearAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _forEachCell(
|
private _forEachCell(
|
||||||
terminal: ITerminal,
|
|
||||||
firstRow: number,
|
firstRow: number,
|
||||||
lastRow: number,
|
lastRow: number,
|
||||||
joinerRegistry: ICharacterJoinerRegistry | null,
|
joinerRegistry: ICharacterJoinerRegistry | null,
|
||||||
@@ -67,11 +75,11 @@ export class TextRenderLayer extends BaseRenderLayer {
|
|||||||
) => void
|
) => void
|
||||||
): void {
|
): void {
|
||||||
for (let y = firstRow; y <= lastRow; y++) {
|
for (let y = firstRow; y <= lastRow; y++) {
|
||||||
const row = y + terminal.buffer.ydisp;
|
const row = y + this._bufferService.buffer.ydisp;
|
||||||
const line = terminal.buffer.lines.get(row);
|
const line = this._bufferService.buffer.lines.get(row);
|
||||||
const joinedRanges = joinerRegistry ? joinerRegistry.getJoinedCharacters(row) : [];
|
const joinedRanges = joinerRegistry ? joinerRegistry.getJoinedCharacters(row) : [];
|
||||||
for (let x = 0; x < terminal.cols; x++) {
|
for (let x = 0; x < this._bufferService.cols; x++) {
|
||||||
line.loadCell(x, this._workCell);
|
line!.loadCell(x, this._workCell);
|
||||||
let cell = this._workCell;
|
let cell = this._workCell;
|
||||||
|
|
||||||
// If true, indicates that the current character(s) to draw were joined.
|
// If true, indicates that the current character(s) to draw were joined.
|
||||||
@@ -89,14 +97,14 @@ export class TextRenderLayer extends BaseRenderLayer {
|
|||||||
// and attributes of our input.
|
// and attributes of our input.
|
||||||
if (joinedRanges.length > 0 && x === joinedRanges[0][0]) {
|
if (joinedRanges.length > 0 && x === joinedRanges[0][0]) {
|
||||||
isJoined = true;
|
isJoined = true;
|
||||||
const range = joinedRanges.shift();
|
const range = joinedRanges.shift()!;
|
||||||
|
|
||||||
// We already know the exact start and end column of the joined range,
|
// We already know the exact start and end column of the joined range,
|
||||||
// so we get the string and width representing it directly
|
// so we get the string and width representing it directly
|
||||||
|
|
||||||
cell = new JoinedCellData(
|
cell = new JoinedCellData(
|
||||||
this._workCell,
|
this._workCell,
|
||||||
line.translateToString(true, range[0], range[1]),
|
line!.translateToString(true, range[0], range[1]),
|
||||||
range[1] - range[0]
|
range[1] - range[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -116,7 +124,7 @@ export class TextRenderLayer extends BaseRenderLayer {
|
|||||||
// get removed, and `a` would not re-render because it thinks it's
|
// get removed, and `a` would not re-render because it thinks it's
|
||||||
// already in the correct state.
|
// already in the correct state.
|
||||||
// this._state.cache[x][y] = OVERLAP_OWNED_CHAR_DATA;
|
// this._state.cache[x][y] = OVERLAP_OWNED_CHAR_DATA;
|
||||||
if (lastCharX < line.length - 1 && line.getCodePoint(lastCharX + 1) === NULL_CELL_CODE) {
|
if (lastCharX < line!.length - 1 && line!.getCodePoint(lastCharX + 1) === NULL_CELL_CODE) {
|
||||||
// patch width to 2
|
// patch width to 2
|
||||||
cell.content &= ~Content.WIDTH_MASK;
|
cell.content &= ~Content.WIDTH_MASK;
|
||||||
cell.content |= 2 << Content.WIDTH_SHIFT;
|
cell.content |= 2 << Content.WIDTH_SHIFT;
|
||||||
@@ -143,16 +151,16 @@ export class TextRenderLayer extends BaseRenderLayer {
|
|||||||
* Draws the background for a specified range of columns. Tries to batch adjacent cells of the
|
* Draws the background for a specified range of columns. Tries to batch adjacent cells of the
|
||||||
* same color together to reduce draw calls.
|
* same color together to reduce draw calls.
|
||||||
*/
|
*/
|
||||||
private _drawBackground(terminal: ITerminal, firstRow: number, lastRow: number): void {
|
private _drawBackground(firstRow: number, lastRow: number): void {
|
||||||
const ctx = this._ctx;
|
const ctx = this._ctx;
|
||||||
const cols = terminal.cols;
|
const cols = this._bufferService.cols;
|
||||||
let startX: number = 0;
|
let startX: number = 0;
|
||||||
let startY: number = 0;
|
let startY: number = 0;
|
||||||
let prevFillStyle: string | null = null;
|
let prevFillStyle: string | null = null;
|
||||||
|
|
||||||
ctx.save();
|
ctx.save();
|
||||||
|
|
||||||
this._forEachCell(terminal, firstRow, lastRow, null, (cell, x, y) => {
|
this._forEachCell(firstRow, lastRow, null, (cell, x, y) => {
|
||||||
// libvte and xterm both draw the background (but not foreground) of invisible characters,
|
// libvte and xterm both draw the background (but not foreground) of invisible characters,
|
||||||
// so we should too.
|
// so we should too.
|
||||||
let nextFillStyle = null; // null represents default background color
|
let nextFillStyle = null; // null represents default background color
|
||||||
@@ -176,15 +184,17 @@ export class TextRenderLayer extends BaseRenderLayer {
|
|||||||
// don't need to draw anything.
|
// don't need to draw anything.
|
||||||
startX = x;
|
startX = x;
|
||||||
startY = y;
|
startY = y;
|
||||||
} if (y !== startY) {
|
}
|
||||||
|
|
||||||
|
if (y !== startY) {
|
||||||
// our row changed, draw the previous row
|
// our row changed, draw the previous row
|
||||||
ctx.fillStyle = prevFillStyle;
|
ctx.fillStyle = prevFillStyle ? prevFillStyle : '';
|
||||||
this._fillCells(startX, startY, cols - startX, 1);
|
this._fillCells(startX, startY, cols - startX, 1);
|
||||||
startX = x;
|
startX = x;
|
||||||
startY = y;
|
startY = y;
|
||||||
} else if (prevFillStyle !== nextFillStyle) {
|
} else if (prevFillStyle !== nextFillStyle) {
|
||||||
// our color changed, draw the previous characters in this row
|
// our color changed, draw the previous characters in this row
|
||||||
ctx.fillStyle = prevFillStyle;
|
ctx.fillStyle = prevFillStyle ? prevFillStyle : '';
|
||||||
this._fillCells(startX, startY, x - startX, 1);
|
this._fillCells(startX, startY, x - startX, 1);
|
||||||
startX = x;
|
startX = x;
|
||||||
startY = y;
|
startY = y;
|
||||||
@@ -202,12 +212,12 @@ export class TextRenderLayer extends BaseRenderLayer {
|
|||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _drawForeground(terminal: ITerminal, firstRow: number, lastRow: number): void {
|
private _drawForeground(firstRow: number, lastRow: number): void {
|
||||||
this._forEachCell(terminal, firstRow, lastRow, this._characterJoinerRegistry, (cell, x, y) => {
|
this._forEachCell(firstRow, lastRow, this._characterJoinerRegistry, (cell, x, y) => {
|
||||||
if (cell.isInvisible()) {
|
if (cell.isInvisible()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._drawChars(terminal, cell, x, y);
|
this._drawChars(cell, x, y);
|
||||||
if (cell.isUnderline()) {
|
if (cell.isUnderline()) {
|
||||||
this._ctx.save();
|
this._ctx.save();
|
||||||
|
|
||||||
@@ -226,7 +236,7 @@ export class TextRenderLayer extends BaseRenderLayer {
|
|||||||
this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;
|
this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;
|
||||||
} else {
|
} else {
|
||||||
let fg = cell.getFgColor();
|
let fg = cell.getFgColor();
|
||||||
if (terminal.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {
|
if (this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {
|
||||||
fg += 8;
|
fg += 8;
|
||||||
}
|
}
|
||||||
this._ctx.fillStyle = this._colors.ansi[fg].css;
|
this._ctx.fillStyle = this._colors.ansi[fg].css;
|
||||||
@@ -239,7 +249,7 @@ export class TextRenderLayer extends BaseRenderLayer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public onGridChanged(terminal: ITerminal, firstRow: number, lastRow: number): void {
|
public onGridChanged(firstRow: number, lastRow: number): void {
|
||||||
// Resize has not been called yet
|
// Resize has not been called yet
|
||||||
if (this._state.cache.length === 0) {
|
if (this._state.cache.length === 0) {
|
||||||
return;
|
return;
|
||||||
@@ -249,13 +259,13 @@ export class TextRenderLayer extends BaseRenderLayer {
|
|||||||
this._charAtlas.beginFrame();
|
this._charAtlas.beginFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._clearCells(0, firstRow, terminal.cols, lastRow - firstRow + 1);
|
this._clearCells(0, firstRow, this._bufferService.cols, lastRow - firstRow + 1);
|
||||||
this._drawBackground(terminal, firstRow, lastRow);
|
this._drawBackground(firstRow, lastRow);
|
||||||
this._drawForeground(terminal, firstRow, lastRow);
|
this._drawForeground(firstRow, lastRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onOptionsChanged(terminal: ITerminal): void {
|
public onOptionsChanged(): void {
|
||||||
this._setTransparency(terminal, terminal.options.allowTransparency);
|
this._setTransparency(this._optionsService.options.allowTransparency);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Vendored
+58
@@ -56,3 +56,61 @@ export interface ICharacterJoinerRegistry {
|
|||||||
deregisterCharacterJoiner(joinerId: number): boolean;
|
deregisterCharacterJoiner(joinerId: number): boolean;
|
||||||
getJoinedCharacters(row: number): [number, number][];
|
getJoinedCharacters(row: number): [number, number][];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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], end: [number, number], columnSelectMode: boolean): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a handler to join characters to render as a group
|
||||||
|
*/
|
||||||
|
registerCharacterJoiner?(joiner: ICharacterJoiner): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deregisters the specified character joiner handler
|
||||||
|
*/
|
||||||
|
deregisterCharacterJoiner?(joinerId: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize the render layer.
|
||||||
|
*/
|
||||||
|
resize(dim: IRenderDimensions): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the state of the render layer.
|
||||||
|
*/
|
||||||
|
reset(): void;
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IGlyphIdentifier } from './Types';
|
import { IGlyphIdentifier } from 'browser/renderer/atlas/Types';
|
||||||
import { IDisposable } from 'xterm';
|
import { IDisposable } from 'common/Types';
|
||||||
|
|
||||||
export abstract class BaseCharAtlas implements IDisposable {
|
export abstract class BaseCharAtlas implements IDisposable {
|
||||||
private _didWarmUp: boolean = false;
|
private _didWarmUp: boolean = false;
|
||||||
+18
-20
@@ -3,19 +3,19 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ITerminal } from '../../Types';
|
import { generateConfig, configEquals } from 'browser/renderer/atlas/CharAtlasUtils';
|
||||||
import { generateConfig, configEquals } from './CharAtlasUtils';
|
import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas';
|
||||||
import { BaseCharAtlas } from './BaseCharAtlas';
|
import { DynamicCharAtlas } from 'browser/renderer/atlas/DynamicCharAtlas';
|
||||||
import { DynamicCharAtlas } from './DynamicCharAtlas';
|
import { ICharAtlasConfig } from 'browser/renderer/atlas/Types';
|
||||||
import { ICharAtlasConfig } from './Types';
|
|
||||||
import { IColorSet } from 'browser/Types';
|
import { IColorSet } from 'browser/Types';
|
||||||
|
import { ITerminalOptions } from 'common/services/Services';
|
||||||
|
|
||||||
interface ICharAtlasCacheEntry {
|
interface ICharAtlasCacheEntry {
|
||||||
atlas: BaseCharAtlas;
|
atlas: BaseCharAtlas;
|
||||||
config: ICharAtlasConfig;
|
config: ICharAtlasConfig;
|
||||||
// N.B. This implementation potentially holds onto copies of the terminal forever, so
|
// N.B. This implementation potentially holds onto copies of the terminal forever, so
|
||||||
// this may cause memory leaks.
|
// this may cause memory leaks.
|
||||||
ownedBy: ITerminal[];
|
ownedBy: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const charAtlasCache: ICharAtlasCacheEntry[] = [];
|
const charAtlasCache: ICharAtlasCacheEntry[] = [];
|
||||||
@@ -23,26 +23,25 @@ const charAtlasCache: ICharAtlasCacheEntry[] = [];
|
|||||||
/**
|
/**
|
||||||
* Acquires a char atlas, either generating a new one or returning an existing
|
* Acquires a char atlas, either generating a new one or returning an existing
|
||||||
* one that is in use by another terminal.
|
* one that is in use by another terminal.
|
||||||
* @param terminal The terminal.
|
|
||||||
* @param colors The colors to use.
|
|
||||||
*/
|
*/
|
||||||
export function acquireCharAtlas(
|
export function acquireCharAtlas(
|
||||||
terminal: ITerminal,
|
options: ITerminalOptions,
|
||||||
|
rendererId: number,
|
||||||
colors: IColorSet,
|
colors: IColorSet,
|
||||||
scaledCharWidth: number,
|
scaledCharWidth: number,
|
||||||
scaledCharHeight: number
|
scaledCharHeight: number
|
||||||
): BaseCharAtlas {
|
): BaseCharAtlas {
|
||||||
const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, terminal, colors);
|
const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, options, colors);
|
||||||
|
|
||||||
// Check to see if the terminal already owns this config
|
// Check to see if the renderer already owns this config
|
||||||
for (let i = 0; i < charAtlasCache.length; i++) {
|
for (let i = 0; i < charAtlasCache.length; i++) {
|
||||||
const entry = charAtlasCache[i];
|
const entry = charAtlasCache[i];
|
||||||
const ownedByIndex = entry.ownedBy.indexOf(terminal);
|
const ownedByIndex = entry.ownedBy.indexOf(rendererId);
|
||||||
if (ownedByIndex >= 0) {
|
if (ownedByIndex >= 0) {
|
||||||
if (configEquals(entry.config, newConfig)) {
|
if (configEquals(entry.config, newConfig)) {
|
||||||
return entry.atlas;
|
return entry.atlas;
|
||||||
}
|
}
|
||||||
// The configs differ, release the terminal from the entry
|
// The configs differ, release the renderer from the entry
|
||||||
if (entry.ownedBy.length === 1) {
|
if (entry.ownedBy.length === 1) {
|
||||||
entry.atlas.dispose();
|
entry.atlas.dispose();
|
||||||
charAtlasCache.splice(i, 1);
|
charAtlasCache.splice(i, 1);
|
||||||
@@ -57,8 +56,8 @@ export function acquireCharAtlas(
|
|||||||
for (let i = 0; i < charAtlasCache.length; i++) {
|
for (let i = 0; i < charAtlasCache.length; i++) {
|
||||||
const entry = charAtlasCache[i];
|
const entry = charAtlasCache[i];
|
||||||
if (configEquals(entry.config, newConfig)) {
|
if (configEquals(entry.config, newConfig)) {
|
||||||
// Add the terminal to the cache entry and return
|
// Add the renderer to the cache entry and return
|
||||||
entry.ownedBy.push(terminal);
|
entry.ownedBy.push(rendererId);
|
||||||
return entry.atlas;
|
return entry.atlas;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,7 +68,7 @@ export function acquireCharAtlas(
|
|||||||
newConfig
|
newConfig
|
||||||
),
|
),
|
||||||
config: newConfig,
|
config: newConfig,
|
||||||
ownedBy: [terminal]
|
ownedBy: [rendererId]
|
||||||
};
|
};
|
||||||
charAtlasCache.push(newEntry);
|
charAtlasCache.push(newEntry);
|
||||||
return newEntry.atlas;
|
return newEntry.atlas;
|
||||||
@@ -77,14 +76,13 @@ export function acquireCharAtlas(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a terminal reference from the cache, allowing its memory to be freed.
|
* Removes a terminal reference from the cache, allowing its memory to be freed.
|
||||||
* @param terminal The terminal to remove.
|
|
||||||
*/
|
*/
|
||||||
export function removeTerminalFromCache(terminal: ITerminal): void {
|
export function removeTerminalFromCache(rendererId: number): void {
|
||||||
for (let i = 0; i < charAtlasCache.length; i++) {
|
for (let i = 0; i < charAtlasCache.length; i++) {
|
||||||
const index = charAtlasCache[i].ownedBy.indexOf(terminal);
|
const index = charAtlasCache[i].ownedBy.indexOf(rendererId);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
if (charAtlasCache[i].ownedBy.length === 1) {
|
if (charAtlasCache[i].ownedBy.length === 1) {
|
||||||
// Remove the cache entry if it's the only terminal
|
// Remove the cache entry if it's the only renderer
|
||||||
charAtlasCache[i].atlas.dispose();
|
charAtlasCache[i].atlas.dispose();
|
||||||
charAtlasCache.splice(i, 1);
|
charAtlasCache.splice(i, 1);
|
||||||
} else {
|
} else {
|
||||||
+13
-13
@@ -3,19 +3,19 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ITerminal } from '../../Types';
|
import { ICharAtlasConfig } from 'browser/renderer/atlas/Types';
|
||||||
import { ICharAtlasConfig } from './Types';
|
|
||||||
import { DEFAULT_COLOR } from 'common/buffer/Constants';
|
import { DEFAULT_COLOR } from 'common/buffer/Constants';
|
||||||
import { IColorSet } from 'browser/Types';
|
import { IColorSet, IPartialColorSet } from 'browser/Types';
|
||||||
|
import { ITerminalOptions } from 'common/services/Services';
|
||||||
|
|
||||||
export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, terminal: ITerminal, colors: IColorSet): ICharAtlasConfig {
|
export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, options: ITerminalOptions, colors: IColorSet): ICharAtlasConfig {
|
||||||
// null out some fields that don't matter
|
// null out some fields that don't matter
|
||||||
const clonedColors = <IColorSet>{
|
const clonedColors = <IPartialColorSet>{
|
||||||
foreground: colors.foreground,
|
foreground: colors.foreground,
|
||||||
background: colors.background,
|
background: colors.background,
|
||||||
cursor: null,
|
cursor: undefined,
|
||||||
cursorAccent: null,
|
cursorAccent: undefined,
|
||||||
selection: null,
|
selection: undefined,
|
||||||
// For the static char atlas, we only use the first 16 colors, but we need all 256 for the
|
// For the static char atlas, we only use the first 16 colors, but we need all 256 for the
|
||||||
// dynamic character atlas.
|
// dynamic character atlas.
|
||||||
ansi: colors.ansi.slice(0, 16)
|
ansi: colors.ansi.slice(0, 16)
|
||||||
@@ -24,11 +24,11 @@ export function generateConfig(scaledCharWidth: number, scaledCharHeight: number
|
|||||||
devicePixelRatio: window.devicePixelRatio,
|
devicePixelRatio: window.devicePixelRatio,
|
||||||
scaledCharWidth,
|
scaledCharWidth,
|
||||||
scaledCharHeight,
|
scaledCharHeight,
|
||||||
fontFamily: terminal.options.fontFamily,
|
fontFamily: options.fontFamily,
|
||||||
fontSize: terminal.options.fontSize,
|
fontSize: options.fontSize,
|
||||||
fontWeight: terminal.options.fontWeight,
|
fontWeight: options.fontWeight,
|
||||||
fontWeightBold: terminal.options.fontWeightBold,
|
fontWeightBold: options.fontWeightBold,
|
||||||
allowTransparency: terminal.options.allowTransparency,
|
allowTransparency: options.allowTransparency,
|
||||||
colors: clonedColors
|
colors: clonedColors
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
+7
-6
@@ -3,13 +3,14 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IGlyphIdentifier, ICharAtlasConfig } from './Types';
|
|
||||||
import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
||||||
import { BaseCharAtlas } from './BaseCharAtlas';
|
import { IGlyphIdentifier, ICharAtlasConfig } from 'browser/renderer/atlas/Types';
|
||||||
|
import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas';
|
||||||
import { DEFAULT_ANSI_COLORS } from 'browser/ColorManager';
|
import { DEFAULT_ANSI_COLORS } from 'browser/ColorManager';
|
||||||
import { LRUMap } from 'browser/renderer/atlas/LRUMap';
|
import { LRUMap } from 'browser/renderer/atlas/LRUMap';
|
||||||
import { isFirefox, isSafari } from 'common/Platform';
|
import { isFirefox, isSafari } from 'common/Platform';
|
||||||
import { IColor } from 'browser/Types';
|
import { IColor } from 'browser/Types';
|
||||||
|
import { throwIfFalsy } from 'browser/renderer/RendererUtils';
|
||||||
|
|
||||||
// In practice we're probably never going to exhaust a texture this large. For debugging purposes,
|
// 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.
|
// however, it can be useful to set this to a really tiny value, to verify that LRU eviction works.
|
||||||
@@ -89,12 +90,12 @@ export class DynamicCharAtlas extends BaseCharAtlas {
|
|||||||
// The canvas needs alpha because we use clearColor to convert the background color to alpha.
|
// 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
|
// It might also contain some characters with transparent backgrounds if allowTransparency is
|
||||||
// set.
|
// set.
|
||||||
this._cacheCtx = this._cacheCanvas.getContext('2d', {alpha: true});
|
this._cacheCtx = throwIfFalsy(this._cacheCanvas.getContext('2d', {alpha: true}));
|
||||||
|
|
||||||
const tmpCanvas = document.createElement('canvas');
|
const tmpCanvas = document.createElement('canvas');
|
||||||
tmpCanvas.width = this._config.scaledCharWidth;
|
tmpCanvas.width = this._config.scaledCharWidth;
|
||||||
tmpCanvas.height = this._config.scaledCharHeight;
|
tmpCanvas.height = this._config.scaledCharHeight;
|
||||||
this._tmpCtx = tmpCanvas.getContext('2d', {alpha: this._config.allowTransparency});
|
this._tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d', {alpha: this._config.allowTransparency}));
|
||||||
|
|
||||||
this._width = Math.floor(TEXTURE_WIDTH / this._config.scaledCharWidth);
|
this._width = Math.floor(TEXTURE_WIDTH / this._config.scaledCharWidth);
|
||||||
this._height = Math.floor(TEXTURE_HEIGHT / this._config.scaledCharHeight);
|
this._height = Math.floor(TEXTURE_HEIGHT / this._config.scaledCharHeight);
|
||||||
@@ -144,7 +145,7 @@ export class DynamicCharAtlas extends BaseCharAtlas {
|
|||||||
index = this._cacheMap.size;
|
index = this._cacheMap.size;
|
||||||
} else {
|
} else {
|
||||||
// we're out of space, so our call to set will delete this item
|
// we're out of space, so our call to set will delete this item
|
||||||
index = this._cacheMap.peek().index;
|
index = this._cacheMap.peek()!.index;
|
||||||
}
|
}
|
||||||
const cacheValue = this._drawToCache(glyph, index);
|
const cacheValue = this._drawToCache(glyph, index);
|
||||||
this._cacheMap.set(glyphKey, cacheValue);
|
this._cacheMap.set(glyphKey, cacheValue);
|
||||||
@@ -186,7 +187,7 @@ export class DynamicCharAtlas extends BaseCharAtlas {
|
|||||||
const cacheX = this._toCoordinateX(cacheValue.index);
|
const cacheX = this._toCoordinateX(cacheValue.index);
|
||||||
const cacheY = this._toCoordinateY(cacheValue.index);
|
const cacheY = this._toCoordinateY(cacheValue.index);
|
||||||
ctx.drawImage(
|
ctx.drawImage(
|
||||||
cacheValue.inBitmap ? this._bitmap : this._cacheCanvas,
|
cacheValue.inBitmap ? this._bitmap! : this._cacheCanvas,
|
||||||
cacheX,
|
cacheX,
|
||||||
cacheY,
|
cacheY,
|
||||||
this._config.scaledCharWidth,
|
this._config.scaledCharWidth,
|
||||||
@@ -3,8 +3,8 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FontWeight } from 'xterm';
|
import { FontWeight } from 'common/services/Services';
|
||||||
import { IColorSet } from 'browser/Types';
|
import { IPartialColorSet } from 'browser/Types';
|
||||||
|
|
||||||
export interface IGlyphIdentifier {
|
export interface IGlyphIdentifier {
|
||||||
chars: string;
|
chars: string;
|
||||||
@@ -25,5 +25,5 @@ export interface ICharAtlasConfig {
|
|||||||
scaledCharWidth: number;
|
scaledCharWidth: number;
|
||||||
scaledCharHeight: number;
|
scaledCharHeight: number;
|
||||||
allowTransparency: boolean;
|
allowTransparency: boolean;
|
||||||
colors: IColorSet;
|
colors: IPartialColorSet;
|
||||||
}
|
}
|
||||||
@@ -103,6 +103,10 @@ export class RenderService extends Disposable implements IRenderService {
|
|||||||
this._onDimensionsChange.fire(this._renderer.dimensions);
|
this._onDimensionsChange.fire(this._renderer.dimensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public dispose(): void {
|
||||||
|
this._renderer.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
public setRenderer(renderer: IRenderer): void {
|
public setRenderer(renderer: IRenderer): void {
|
||||||
// TODO: RenderCoordinator should be the only one to dispose the renderer
|
// TODO: RenderCoordinator should be the only one to dispose the renderer
|
||||||
this._renderer.dispose();
|
this._renderer.dispose();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { IRenderDimensions, IRenderer, CharacterJoinerHandler } from 'browser/re
|
|||||||
import { IColorSet } from 'browser/Types';
|
import { IColorSet } from 'browser/Types';
|
||||||
import { ISelectionRedrawRequestEvent } from 'browser/selection/Types';
|
import { ISelectionRedrawRequestEvent } from 'browser/selection/Types';
|
||||||
import { createDecorator } from 'common/services/ServiceRegistry';
|
import { createDecorator } from 'common/services/ServiceRegistry';
|
||||||
|
import { IDisposable } from 'common/Types';
|
||||||
|
|
||||||
export const ICharSizeService = createDecorator<ICharSizeService>('CharSizeService');
|
export const ICharSizeService = createDecorator<ICharSizeService>('CharSizeService');
|
||||||
export interface ICharSizeService {
|
export interface ICharSizeService {
|
||||||
@@ -31,7 +32,7 @@ export interface IMouseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const IRenderService = createDecorator<IRenderService>('RenderService');
|
export const IRenderService = createDecorator<IRenderService>('RenderService');
|
||||||
export interface IRenderService {
|
export interface IRenderService extends IDisposable {
|
||||||
serviceBrand: any;
|
serviceBrand: any;
|
||||||
|
|
||||||
onDimensionsChange: IEvent<IRenderDimensions>;
|
onDimensionsChange: IEvent<IRenderDimensions>;
|
||||||
|
|||||||
@@ -4,11 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { IRenderDimensions } from 'browser/renderer/Types';
|
import { IRenderDimensions } from 'browser/renderer/Types';
|
||||||
import { BaseRenderLayer } from './BaseRenderLayer';
|
import { BaseRenderLayer } from '../browser/renderer/BaseRenderLayer';
|
||||||
import { ITerminal } from '../Types';
|
import { ITerminal } from '../Types';
|
||||||
import { ICellData } from 'common/Types';
|
import { ICellData } from 'common/Types';
|
||||||
import { CellData } from 'common/buffer/CellData';
|
import { CellData } from 'common/buffer/CellData';
|
||||||
import { IColorSet } from 'browser/Types';
|
import { IColorSet } from 'browser/Types';
|
||||||
|
import { IBufferService, IOptionsService } from 'common/services/Services';
|
||||||
|
|
||||||
interface ICursorState {
|
interface ICursorState {
|
||||||
x: number;
|
x: number;
|
||||||
@@ -25,12 +26,20 @@ const BLINK_INTERVAL = 600;
|
|||||||
|
|
||||||
export class CursorRenderLayer extends BaseRenderLayer {
|
export class CursorRenderLayer extends BaseRenderLayer {
|
||||||
private _state: ICursorState;
|
private _state: ICursorState;
|
||||||
private _cursorRenderers: {[key: string]: (terminal: ITerminal, x: number, y: number, cell: ICellData) => void};
|
private _cursorRenderers: {[key: string]: (x: number, y: number, cell: ICellData) => void};
|
||||||
private _cursorBlinkStateManager: CursorBlinkStateManager;
|
private _cursorBlinkStateManager: CursorBlinkStateManager;
|
||||||
private _cell: ICellData = new CellData();
|
private _cell: ICellData = new CellData();
|
||||||
|
|
||||||
constructor(container: HTMLElement, zIndex: number, colors: IColorSet) {
|
constructor(
|
||||||
super(container, 'cursor', zIndex, true, colors);
|
container: HTMLElement,
|
||||||
|
zIndex: number,
|
||||||
|
colors: IColorSet,
|
||||||
|
private _terminal: ITerminal,
|
||||||
|
rendererId: number,
|
||||||
|
readonly bufferService: IBufferService,
|
||||||
|
readonly optionsService: IOptionsService
|
||||||
|
) {
|
||||||
|
super(container, 'cursor', zIndex, true, colors, rendererId, bufferService, optionsService);
|
||||||
this._state = {
|
this._state = {
|
||||||
x: null,
|
x: null,
|
||||||
y: null,
|
y: null,
|
||||||
@@ -46,8 +55,8 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
|||||||
// TODO: Consider initial options? Maybe onOptionsChanged should be called at the end of open?
|
// TODO: Consider initial options? Maybe onOptionsChanged should be called at the end of open?
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize(terminal: ITerminal, dim: IRenderDimensions): void {
|
public resize(dim: IRenderDimensions): void {
|
||||||
super.resize(terminal, dim);
|
super.resize(dim);
|
||||||
// Resizing the canvas discards the contents of the canvas so clear state
|
// Resizing the canvas discards the contents of the canvas so clear state
|
||||||
this._state = {
|
this._state = {
|
||||||
x: null,
|
x: null,
|
||||||
@@ -58,35 +67,35 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public reset(terminal: ITerminal): void {
|
public reset(): void {
|
||||||
this._clearCursor();
|
this._clearCursor();
|
||||||
if (this._cursorBlinkStateManager) {
|
if (this._cursorBlinkStateManager) {
|
||||||
this._cursorBlinkStateManager.dispose();
|
this._cursorBlinkStateManager.dispose();
|
||||||
this._cursorBlinkStateManager = null;
|
this._cursorBlinkStateManager = null;
|
||||||
this.onOptionsChanged(terminal);
|
this.onOptionsChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onBlur(terminal: ITerminal): void {
|
public onBlur(): void {
|
||||||
if (this._cursorBlinkStateManager) {
|
if (this._cursorBlinkStateManager) {
|
||||||
this._cursorBlinkStateManager.pause();
|
this._cursorBlinkStateManager.pause();
|
||||||
}
|
}
|
||||||
terminal.refresh(terminal.buffer.y, terminal.buffer.y);
|
this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onFocus(terminal: ITerminal): void {
|
public onFocus(): void {
|
||||||
if (this._cursorBlinkStateManager) {
|
if (this._cursorBlinkStateManager) {
|
||||||
this._cursorBlinkStateManager.resume(terminal);
|
this._cursorBlinkStateManager.resume();
|
||||||
} else {
|
} else {
|
||||||
terminal.refresh(terminal.buffer.y, terminal.buffer.y);
|
this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onOptionsChanged(terminal: ITerminal): void {
|
public onOptionsChanged(): void {
|
||||||
if (terminal.options.cursorBlink) {
|
if (this._optionsService.options.cursorBlink) {
|
||||||
if (!this._cursorBlinkStateManager) {
|
if (!this._cursorBlinkStateManager) {
|
||||||
this._cursorBlinkStateManager = new CursorBlinkStateManager(terminal, () => {
|
this._cursorBlinkStateManager = new CursorBlinkStateManager(this._terminal.isFocused, () => {
|
||||||
this._render(terminal, true);
|
this._render(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -96,55 +105,55 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
|||||||
}
|
}
|
||||||
// Request a refresh from the terminal as management of rendering is being
|
// Request a refresh from the terminal as management of rendering is being
|
||||||
// moved back to the terminal
|
// moved back to the terminal
|
||||||
terminal.refresh(terminal.buffer.y, terminal.buffer.y);
|
this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onCursorMove(terminal: ITerminal): void {
|
public onCursorMove(): void {
|
||||||
if (this._cursorBlinkStateManager) {
|
if (this._cursorBlinkStateManager) {
|
||||||
this._cursorBlinkStateManager.restartBlinkAnimation(terminal);
|
this._cursorBlinkStateManager.restartBlinkAnimation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {
|
public onGridChanged(startRow: number, endRow: number): void {
|
||||||
if (!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isPaused) {
|
if (!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isPaused) {
|
||||||
this._render(terminal, false);
|
this._render(false);
|
||||||
} else {
|
} else {
|
||||||
this._cursorBlinkStateManager.restartBlinkAnimation(terminal);
|
this._cursorBlinkStateManager.restartBlinkAnimation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _render(terminal: ITerminal, triggeredByAnimationFrame: boolean): void {
|
private _render(triggeredByAnimationFrame: boolean): void {
|
||||||
// Don't draw the cursor if it's hidden
|
// Don't draw the cursor if it's hidden
|
||||||
if (!terminal.cursorState || terminal.cursorHidden) {
|
if (!this._terminal.cursorState || this._terminal.cursorHidden) {
|
||||||
this._clearCursor();
|
this._clearCursor();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cursorY = terminal.buffer.ybase + terminal.buffer.y;
|
const cursorY = this._bufferService.buffer.ybase + this._bufferService.buffer.y;
|
||||||
const viewportRelativeCursorY = cursorY - terminal.buffer.ydisp;
|
const viewportRelativeCursorY = cursorY - this._bufferService.buffer.ydisp;
|
||||||
|
|
||||||
// Don't draw the cursor if it's off-screen
|
// Don't draw the cursor if it's off-screen
|
||||||
if (viewportRelativeCursorY < 0 || viewportRelativeCursorY >= terminal.rows) {
|
if (viewportRelativeCursorY < 0 || viewportRelativeCursorY >= this._bufferService.rows) {
|
||||||
this._clearCursor();
|
this._clearCursor();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
terminal.buffer.lines.get(cursorY).loadCell(terminal.buffer.x, this._cell);
|
this._bufferService.buffer.lines.get(cursorY).loadCell(this._bufferService.buffer.x, this._cell);
|
||||||
if (this._cell.content === undefined) {
|
if (this._cell.content === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!terminal.isFocused) {
|
if (!this._terminal.isFocused) {
|
||||||
this._clearCursor();
|
this._clearCursor();
|
||||||
this._ctx.save();
|
this._ctx.save();
|
||||||
this._ctx.fillStyle = this._colors.cursor.css;
|
this._ctx.fillStyle = this._colors.cursor.css;
|
||||||
this._renderBlurCursor(terminal, terminal.buffer.x, viewportRelativeCursorY, this._cell);
|
this._renderBlurCursor(this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
|
||||||
this._ctx.restore();
|
this._ctx.restore();
|
||||||
this._state.x = terminal.buffer.x;
|
this._state.x = this._bufferService.buffer.x;
|
||||||
this._state.y = viewportRelativeCursorY;
|
this._state.y = viewportRelativeCursorY;
|
||||||
this._state.isFocused = false;
|
this._state.isFocused = false;
|
||||||
this._state.style = terminal.options.cursorStyle;
|
this._state.style = this._optionsService.options.cursorStyle;
|
||||||
this._state.width = this._cell.getWidth();
|
this._state.width = this._cell.getWidth();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -157,10 +166,10 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
|||||||
|
|
||||||
if (this._state) {
|
if (this._state) {
|
||||||
// The cursor is already in the correct spot, don't redraw
|
// The cursor is already in the correct spot, don't redraw
|
||||||
if (this._state.x === terminal.buffer.x &&
|
if (this._state.x === this._bufferService.buffer.x &&
|
||||||
this._state.y === viewportRelativeCursorY &&
|
this._state.y === viewportRelativeCursorY &&
|
||||||
this._state.isFocused === terminal.isFocused &&
|
this._state.isFocused === this._terminal.isFocused &&
|
||||||
this._state.style === terminal.options.cursorStyle &&
|
this._state.style === this._optionsService.options.cursorStyle &&
|
||||||
this._state.width === this._cell.getWidth()) {
|
this._state.width === this._cell.getWidth()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -168,13 +177,13 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._ctx.save();
|
this._ctx.save();
|
||||||
this._cursorRenderers[terminal.options.cursorStyle || 'block'](terminal, terminal.buffer.x, viewportRelativeCursorY, this._cell);
|
this._cursorRenderers[this._optionsService.options.cursorStyle || 'block'](this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
|
||||||
this._ctx.restore();
|
this._ctx.restore();
|
||||||
|
|
||||||
this._state.x = terminal.buffer.x;
|
this._state.x = this._bufferService.buffer.x;
|
||||||
this._state.y = viewportRelativeCursorY;
|
this._state.y = viewportRelativeCursorY;
|
||||||
this._state.isFocused = false;
|
this._state.isFocused = false;
|
||||||
this._state.style = terminal.options.cursorStyle;
|
this._state.style = this._optionsService.options.cursorStyle;
|
||||||
this._state.width = this._cell.getWidth();
|
this._state.width = this._cell.getWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,30 +200,30 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderBarCursor(terminal: ITerminal, x: number, y: number, cell: ICellData): void {
|
private _renderBarCursor(x: number, y: number, cell: ICellData): void {
|
||||||
this._ctx.save();
|
this._ctx.save();
|
||||||
this._ctx.fillStyle = this._colors.cursor.css;
|
this._ctx.fillStyle = this._colors.cursor.css;
|
||||||
this._fillLeftLineAtCell(x, y);
|
this._fillLeftLineAtCell(x, y);
|
||||||
this._ctx.restore();
|
this._ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderBlockCursor(terminal: ITerminal, x: number, y: number, cell: ICellData): void {
|
private _renderBlockCursor(x: number, y: number, cell: ICellData): void {
|
||||||
this._ctx.save();
|
this._ctx.save();
|
||||||
this._ctx.fillStyle = this._colors.cursor.css;
|
this._ctx.fillStyle = this._colors.cursor.css;
|
||||||
this._fillCells(x, y, cell.getWidth(), 1);
|
this._fillCells(x, y, cell.getWidth(), 1);
|
||||||
this._ctx.fillStyle = this._colors.cursorAccent.css;
|
this._ctx.fillStyle = this._colors.cursorAccent.css;
|
||||||
this._fillCharTrueColor(terminal, cell, x, y);
|
this._fillCharTrueColor(cell, x, y);
|
||||||
this._ctx.restore();
|
this._ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderUnderlineCursor(terminal: ITerminal, x: number, y: number, cell: ICellData): void {
|
private _renderUnderlineCursor(x: number, y: number, cell: ICellData): void {
|
||||||
this._ctx.save();
|
this._ctx.save();
|
||||||
this._ctx.fillStyle = this._colors.cursor.css;
|
this._ctx.fillStyle = this._colors.cursor.css;
|
||||||
this._fillBottomLineAtCells(x, y);
|
this._fillBottomLineAtCells(x, y);
|
||||||
this._ctx.restore();
|
this._ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderBlurCursor(terminal: ITerminal, x: number, y: number, cell: ICellData): void {
|
private _renderBlurCursor(x: number, y: number, cell: ICellData): void {
|
||||||
this._ctx.save();
|
this._ctx.save();
|
||||||
this._ctx.strokeStyle = this._colors.cursor.css;
|
this._ctx.strokeStyle = this._colors.cursor.css;
|
||||||
this._strokeRectAtCell(x, y, cell.getWidth(), 1);
|
this._strokeRectAtCell(x, y, cell.getWidth(), 1);
|
||||||
@@ -237,11 +246,11 @@ class CursorBlinkStateManager {
|
|||||||
private _animationTimeRestarted: number;
|
private _animationTimeRestarted: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
terminal: ITerminal,
|
isFocused: boolean,
|
||||||
private _renderCallback: () => void
|
private _renderCallback: () => void
|
||||||
) {
|
) {
|
||||||
this.isCursorVisible = true;
|
this.isCursorVisible = true;
|
||||||
if (terminal.isFocused) {
|
if (isFocused) {
|
||||||
this._restartInterval();
|
this._restartInterval();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,7 +272,7 @@ class CursorBlinkStateManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public restartBlinkAnimation(terminal: ITerminal): void {
|
public restartBlinkAnimation(): void {
|
||||||
if (this.isPaused) {
|
if (this.isPaused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -346,9 +355,9 @@ class CursorBlinkStateManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public resume(terminal: ITerminal): void {
|
public resume(): void {
|
||||||
this._animationTimeRestarted = null;
|
this._animationTimeRestarted = null;
|
||||||
this._restartInterval();
|
this._restartInterval();
|
||||||
this.restartBlinkAnimation(terminal);
|
this.restartBlinkAnimation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-21
@@ -3,20 +3,24 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { TextRenderLayer } from './TextRenderLayer';
|
import { TextRenderLayer } from '../browser/renderer/TextRenderLayer';
|
||||||
import { SelectionRenderLayer } from './SelectionRenderLayer';
|
import { SelectionRenderLayer } from '../browser/renderer/SelectionRenderLayer';
|
||||||
import { CursorRenderLayer } from './CursorRenderLayer';
|
import { CursorRenderLayer } from './CursorRenderLayer';
|
||||||
import { IRenderLayer } from './Types';
|
import { IRenderLayer, IRenderer, IRenderDimensions, CharacterJoinerHandler, ICharacterJoinerRegistry } from 'browser/renderer/Types';
|
||||||
import { IRenderer, IRenderDimensions, CharacterJoinerHandler, ICharacterJoinerRegistry } from 'browser/renderer/Types';
|
|
||||||
import { ITerminal } from '../Types';
|
import { ITerminal } from '../Types';
|
||||||
import { LinkRenderLayer } from './LinkRenderLayer';
|
import { LinkRenderLayer } from '../browser/renderer/LinkRenderLayer';
|
||||||
import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistry';
|
import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistry';
|
||||||
import { Disposable } from 'common/Lifecycle';
|
import { Disposable } from 'common/Lifecycle';
|
||||||
import { IColorSet } from 'browser/Types';
|
import { IColorSet } from 'browser/Types';
|
||||||
import { ICharSizeService } from 'browser/services/Services';
|
import { ICharSizeService } from 'browser/services/Services';
|
||||||
import { IBufferService } from 'common/services/Services';
|
import { IBufferService, IOptionsService } from 'common/services/Services';
|
||||||
|
import { removeTerminalFromCache } from 'browser/renderer/atlas/CharAtlasCache';
|
||||||
|
|
||||||
|
let nextRendererId = 1;
|
||||||
|
|
||||||
export class Renderer extends Disposable implements IRenderer {
|
export class Renderer extends Disposable implements IRenderer {
|
||||||
|
private _id = nextRendererId++;
|
||||||
|
|
||||||
private _renderLayers: IRenderLayer[];
|
private _renderLayers: IRenderLayer[];
|
||||||
private _devicePixelRatio: number;
|
private _devicePixelRatio: number;
|
||||||
private _characterJoinerRegistry: ICharacterJoinerRegistry;
|
private _characterJoinerRegistry: ICharacterJoinerRegistry;
|
||||||
@@ -27,17 +31,18 @@ export class Renderer extends Disposable implements IRenderer {
|
|||||||
private _colors: IColorSet,
|
private _colors: IColorSet,
|
||||||
private readonly _terminal: ITerminal,
|
private readonly _terminal: ITerminal,
|
||||||
readonly bufferService: IBufferService,
|
readonly bufferService: IBufferService,
|
||||||
private readonly _charSizeService: ICharSizeService
|
private readonly _charSizeService: ICharSizeService,
|
||||||
|
readonly optionsService: IOptionsService
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
const allowTransparency = this._terminal.options.allowTransparency;
|
const allowTransparency = this._terminal.options.allowTransparency;
|
||||||
this._characterJoinerRegistry = new CharacterJoinerRegistry(bufferService);
|
this._characterJoinerRegistry = new CharacterJoinerRegistry(bufferService);
|
||||||
|
|
||||||
this._renderLayers = [
|
this._renderLayers = [
|
||||||
new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency),
|
new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._id, bufferService, optionsService),
|
||||||
new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors),
|
new SelectionRenderLayer(this._terminal.screenElement, 1, this._colors, this._id, bufferService, optionsService),
|
||||||
new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._terminal),
|
new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._id, this._terminal.linkifier, bufferService, optionsService),
|
||||||
new CursorRenderLayer(this._terminal.screenElement, 3, this._colors)
|
new CursorRenderLayer(this._terminal.screenElement, 3, this._colors, this._terminal, this._id, bufferService, optionsService)
|
||||||
];
|
];
|
||||||
this.dimensions = {
|
this.dimensions = {
|
||||||
scaledCharWidth: null,
|
scaledCharWidth: null,
|
||||||
@@ -61,6 +66,7 @@ export class Renderer extends Disposable implements IRenderer {
|
|||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
this._renderLayers.forEach(l => l.dispose());
|
this._renderLayers.forEach(l => l.dispose());
|
||||||
|
removeTerminalFromCache(this._id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onDevicePixelRatioChange(): void {
|
public onDevicePixelRatioChange(): void {
|
||||||
@@ -77,8 +83,8 @@ export class Renderer extends Disposable implements IRenderer {
|
|||||||
|
|
||||||
// Clear layers and force a full render
|
// Clear layers and force a full render
|
||||||
this._renderLayers.forEach(l => {
|
this._renderLayers.forEach(l => {
|
||||||
l.setColors(this._terminal, this._colors);
|
l.setColors(this._colors);
|
||||||
l.reset(this._terminal);
|
l.reset();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +93,7 @@ export class Renderer extends Disposable implements IRenderer {
|
|||||||
this._updateDimensions();
|
this._updateDimensions();
|
||||||
|
|
||||||
// Resize all render layers
|
// Resize all render layers
|
||||||
this._renderLayers.forEach(l => l.resize(this._terminal, this.dimensions));
|
this._renderLayers.forEach(l => l.resize(this.dimensions));
|
||||||
|
|
||||||
// Resize the screen
|
// Resize the screen
|
||||||
this._terminal.screenElement.style.width = `${this.dimensions.canvasWidth}px`;
|
this._terminal.screenElement.style.width = `${this.dimensions.canvasWidth}px`;
|
||||||
@@ -99,27 +105,27 @@ export class Renderer extends Disposable implements IRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onBlur(): void {
|
public onBlur(): void {
|
||||||
this._runOperation(l => l.onBlur(this._terminal));
|
this._runOperation(l => l.onBlur());
|
||||||
}
|
}
|
||||||
|
|
||||||
public onFocus(): void {
|
public onFocus(): void {
|
||||||
this._runOperation(l => l.onFocus(this._terminal));
|
this._runOperation(l => l.onFocus());
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean = false): void {
|
public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean = false): void {
|
||||||
this._runOperation(l => l.onSelectionChanged(this._terminal, start, end, columnSelectMode));
|
this._runOperation(l => l.onSelectionChanged(start, end, columnSelectMode));
|
||||||
}
|
}
|
||||||
|
|
||||||
public onCursorMove(): void {
|
public onCursorMove(): void {
|
||||||
this._runOperation(l => l.onCursorMove(this._terminal));
|
this._runOperation(l => l.onCursorMove());
|
||||||
}
|
}
|
||||||
|
|
||||||
public onOptionsChanged(): void {
|
public onOptionsChanged(): void {
|
||||||
this._runOperation(l => l.onOptionsChanged(this._terminal));
|
this._runOperation(l => l.onOptionsChanged());
|
||||||
}
|
}
|
||||||
|
|
||||||
public clear(): void {
|
public clear(): void {
|
||||||
this._runOperation(l => l.reset(this._terminal));
|
this._runOperation(l => l.reset());
|
||||||
}
|
}
|
||||||
|
|
||||||
private _runOperation(operation: (layer: IRenderLayer) => void): void {
|
private _runOperation(operation: (layer: IRenderLayer) => void): void {
|
||||||
@@ -131,7 +137,7 @@ export class Renderer extends Disposable implements IRenderer {
|
|||||||
* necessary before queueing up the next one.
|
* necessary before queueing up the next one.
|
||||||
*/
|
*/
|
||||||
public renderRows(start: number, end: number): void {
|
public renderRows(start: number, end: number): void {
|
||||||
this._renderLayers.forEach(l => l.onGridChanged(this._terminal, start, end));
|
this._renderLayers.forEach(l => l.onGridChanged(start, end));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Vendored
-67
@@ -1,67 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ITerminal } from '../Types';
|
|
||||||
import { IDisposable } from 'xterm';
|
|
||||||
import { IColorSet } from 'browser/Types';
|
|
||||||
import { IRenderDimensions, CharacterJoinerHandler, ICharacterJoiner } from 'browser/renderer/Types';
|
|
||||||
|
|
||||||
export interface IRenderLayer extends IDisposable {
|
|
||||||
/**
|
|
||||||
* Called when the terminal loses focus.
|
|
||||||
*/
|
|
||||||
onBlur(terminal: ITerminal): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* * Called when the terminal gets focus.
|
|
||||||
*/
|
|
||||||
onFocus(terminal: ITerminal): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the cursor is moved.
|
|
||||||
*/
|
|
||||||
onCursorMove(terminal: ITerminal): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when options change.
|
|
||||||
*/
|
|
||||||
onOptionsChanged(terminal: ITerminal): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the theme changes.
|
|
||||||
*/
|
|
||||||
setColors(terminal: ITerminal, colorSet: IColorSet): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the data in the grid has changed (or needs to be rendered
|
|
||||||
* again).
|
|
||||||
*/
|
|
||||||
onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls when the selection changes.
|
|
||||||
*/
|
|
||||||
onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number], columnSelectMode: boolean): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a handler to join characters to render as a group
|
|
||||||
*/
|
|
||||||
registerCharacterJoiner?(joiner: ICharacterJoiner): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deregisters the specified character joiner handler
|
|
||||||
*/
|
|
||||||
deregisterCharacterJoiner?(joinerId: number): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resize the render layer.
|
|
||||||
*/
|
|
||||||
resize(terminal: ITerminal, dim: IRenderDimensions): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the state of the render layer.
|
|
||||||
*/
|
|
||||||
reset(terminal: ITerminal): void;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user