Merge remote-tracking branch 'xterm/master' into search-tests

This commit is contained in:
Logan Ramos
2019-07-24 14:05:00 -07:00
23 changed files with 432 additions and 347 deletions
@@ -4,16 +4,12 @@
*/
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 { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
import { acquireCharAtlas } from '../atlas/CharAtlasCache';
import { Terminal } from 'xterm';
import { IColorSet } from 'browser/Types';
import { IRenderDimensions } from 'browser/renderer/Types';
import { CellData } from 'common/buffer/CellData';
import { AttributeData } from 'common/buffer/AttributeData';
import { WebglCharAtlas } from 'atlas/WebglCharAtlas';
import { throwIfFalsy } from '../WebglUtils';
+3 -1
View File
@@ -21,7 +21,7 @@ if (process.argv.length > 2) {
testFiles = process.argv.slice(2);
}
cp.spawnSync(
const run = cp.spawnSync(
path.resolve(__dirname, '../node_modules/.bin/mocha'),
testFiles,
{
@@ -30,3 +30,5 @@ cp.spawnSync(
stdio: 'inherit'
}
);
process.exit(run.status);
+11 -11
View File
@@ -79,7 +79,7 @@ class DECRQSS implements IDcsHandler {
return this._coreService.triggerDataEvent(`${C0.ESC}P1$r${style} q${C0.ESC}\\`);
default:
// 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}\\`);
}
}
@@ -144,16 +144,16 @@ export class InputHandler extends Disposable implements IInputHandler {
* custom fallback handlers
*/
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._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._logService.error('Unknown EXECUTE code: ', { code });
this._logService.debug('Unknown EXECUTE code: ', { code });
});
this._parser.setOscHandlerFallback((identifier: number, data: string) => {
this._logService.error('Unknown OSC code: ', { identifier, data });
this._logService.debug('Unknown OSC code: ', { identifier, data });
});
/**
@@ -1268,7 +1268,7 @@ export class InputHandler extends Disposable implements IInputHandler {
// this.cursorBlink = true;
break;
case 66:
this._logService.info('Serial port requested application keypad.');
this._logService.debug('Serial port requested application keypad.');
this._terminal.applicationKeypad = true;
if (this._terminal.viewport) {
this._terminal.viewport.syncScrollArea();
@@ -1296,7 +1296,7 @@ export class InputHandler extends Disposable implements IInputHandler {
if (this._selectionService) {
this._selectionService.disable();
}
this._logService.info('Binding to mouse events.');
this._logService.debug('Binding to mouse events.');
break;
case 1004: // send focusin/focusout events
// focusin: ^[[I
@@ -1471,7 +1471,7 @@ export class InputHandler extends Disposable implements IInputHandler {
// this.cursorBlink = false;
break;
case 66:
this._logService.info('Switching back to normal keypad.');
this._logService.debug('Switching back to normal keypad.');
this._terminal.applicationKeypad = false;
if (this._terminal.viewport) {
this._terminal.viewport.syncScrollArea();
@@ -1762,7 +1762,7 @@ export class InputHandler extends Disposable implements IInputHandler {
attr.bg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
attr.bg |= DEFAULT_ATTR_DATA.bg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
} else {
this._logService.error('Unknown SGR attribute: %d.', p);
this._logService.debug('Unknown SGR attribute: %d.', p);
}
}
}
@@ -1976,7 +1976,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* Enables the numeric keypad to send application sequences to the host.
*/
public keypadApplicationMode(): void {
this._logService.info('Serial port requested application keypad.');
this._logService.debug('Serial port requested application keypad.');
this._terminal.applicationKeypad = true;
if (this._terminal.viewport) {
this._terminal.viewport.syncScrollArea();
@@ -1989,7 +1989,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* Enables the keypad to send numeric characters to the host.
*/
public keypadNumericMode(): void {
this._logService.info('Switching back to normal keypad.');
this._logService.debug('Switching back to normal keypad.');
this._terminal.applicationKeypad = false;
if (this._terminal.viewport) {
this._terminal.viewport.syncScrollArea();
+32 -11
View File
@@ -94,6 +94,7 @@ describe('Terminal', () => {
expect(e.domEvent).to.be.an.instanceof(Object);
done();
});
(<any>term).textarea = { value: '' };
const evKeyDown = <KeyboardEvent>{
preventDefault: () => { },
stopPropagation: () => { },
@@ -555,12 +556,16 @@ describe('Terminal', () => {
afterEach(() => term.browser.isMac = originalIsMac);
it('should interfere with the alt key on keyDown', () => {
(<any>term)._keyDownHandled = false;
evKeyDown.altKey = true;
evKeyDown.keyCode = 81;
assert.equal(term.keyDown(evKeyDown), false);
term.keyDown(evKeyDown);
assert.equal((<any>term)._keyDownHandled, true);
(<any>term)._keyDownHandled = false;
evKeyDown.altKey = true;
evKeyDown.keyCode = 192;
assert.equal(term.keyDown(evKeyDown), false);
term.keyDown(evKeyDown);
assert.equal((<any>term)._keyDownHandled, true);
});
});
@@ -573,21 +578,29 @@ describe('Terminal', () => {
afterEach(() => term.browser.isMac = originalIsMac);
it('should not interfere with the alt key on keyDown', () => {
(<any>term)._keyDownHandled = false;
evKeyDown.altKey = true;
evKeyDown.keyCode = 81;
assert.equal(term.keyDown(evKeyDown), true);
term.keyDown(evKeyDown);
assert.equal((<any>term)._keyDownHandled, false);
(<any>term)._keyDownHandled = false;
evKeyDown.altKey = true;
evKeyDown.keyCode = 192;
assert.equal(term.keyDown(evKeyDown), true);
term.keyDown(evKeyDown);
assert.equal((<any>term)._keyDownHandled, false);
});
it('should interefere with the alt + arrow keys', () => {
it('should interfere with the alt + arrow keys', () => {
(<any>term)._keyDownHandled = false;
evKeyDown.altKey = true;
evKeyDown.keyCode = 37;
assert.equal(term.keyDown(evKeyDown), false);
term.keyDown(evKeyDown);
assert.equal((<any>term)._keyDownHandled, true);
(<any>term)._keyDownHandled = false;
evKeyDown.altKey = true;
evKeyDown.keyCode = 39;
assert.equal(term.keyDown(evKeyDown), false);
term.keyDown(evKeyDown);
assert.equal((<any>term)._keyDownHandled, true);
});
it('should emit key with alt + key on keyPress', (done) => {
@@ -639,24 +652,32 @@ describe('Terminal', () => {
afterEach(() => term.browser.isWindows = originalIsWindows);
it('should not interfere with the alt + ctrl key on keyDown', () => {
(<any>term)._keyDownHandled = false;
evKeyPress.altKey = true;
evKeyPress.ctrlKey = true;
evKeyPress.keyCode = 81;
assert.equal(term.keyDown(evKeyPress), true);
term.keyDown(evKeyPress);
assert.equal((<any>term)._keyDownHandled, false);
(<any>term)._keyDownHandled = false;
evKeyDown.altKey = true;
evKeyDown.ctrlKey = true;
evKeyDown.keyCode = 81;
assert.equal(term.keyDown(evKeyDown), true);
term.keyDown(evKeyDown);
assert.equal((<any>term)._keyDownHandled, false);
});
it('should interefere with the alt + ctrl + arrow keys', () => {
evKeyDown.altKey = true;
evKeyDown.ctrlKey = true;
(<any>term)._keyDownHandled = false;
evKeyDown.keyCode = 37;
assert.equal(term.keyDown(evKeyDown), false);
term.keyDown(evKeyDown);
assert.equal((<any>term)._keyDownHandled, true);
(<any>term)._keyDownHandled = false;
evKeyDown.keyCode = 39;
assert.equal(term.keyDown(evKeyDown), false);
term.keyDown(evKeyDown);
assert.equal((<any>term)._keyDownHandled, true);
});
it('should emit key with alt + ctrl + key on keyPress', (done) => {
+25 -13
View File
@@ -38,7 +38,6 @@ import { SoundService } from 'browser/services/SoundService';
import { MouseZoneManager } from 'browser/MouseZoneManager';
import { AccessibilityManager } from './AccessibilityManager';
import { ITheme, IMarker, IDisposable, ISelectionPosition } from 'xterm';
import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache';
import { DomRenderer } from './renderer/dom/DomRenderer';
import { IKeyboardEvent, KeyboardResultType, ICharset, IBufferLine, IAttributeData } from 'common/Types';
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
@@ -179,6 +178,13 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
// Store if user went browsing history in scrollback
private _userScrolling: boolean;
/**
* Records whether the keydown event has already been handled and triggered a data event, if so
* the keypress event should not trigger a data event but should still print to the textarea so
* screen readers will announce it.
*/
private _keyDownHandled: boolean = false;
private _inputHandler: InputHandler;
public linkifier: ILinkifier;
public viewport: IViewport;
@@ -246,13 +252,13 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._instantiationService.setService(IOptionsService, this.optionsService);
this._bufferService = this._instantiationService.createInstance(BufferService);
this._instantiationService.setService(IBufferService, this._bufferService);
this._logService = this._instantiationService.createInstance(LogService);
this._instantiationService.setService(ILogService, this._logService);
this._coreService = this._instantiationService.createInstance(CoreService, () => this.scrollToBottom());
this._instantiationService.setService(ICoreService, this._coreService);
this._coreService.onData(e => this._onData.fire(e));
this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService);
this._instantiationService.setService(IDirtyRowService, this._dirtyRowService);
this._logService = this._instantiationService.createInstance(LogService);
this._instantiationService.setService(ILogService, this._logService);
this._setupOptionsListeners();
this._setup();
@@ -264,8 +270,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._windowsMode.dispose();
this._windowsMode = undefined;
}
this._renderService.dispose();
this._customKeyEventHandler = null;
removeTerminalFromCache(this);
this.write = () => {};
if (this.element && this.element.parentNode) {
this.element.parentNode.removeChild(this.element);
@@ -685,7 +691,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
private _createRenderer(): IRenderer {
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);
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
}
@@ -1515,6 +1521,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
* @param ev The keydown event to be handled.
*/
protected _keyDown(event: KeyboardEvent): boolean {
this._keyDownHandled = false;
if (this._customKeyEventHandler && this._customKeyEventHandler(event) === false) {
return false;
}
@@ -1530,12 +1538,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this.updateCursorStyle(event);
// if (result.key === C0.DC3) { // XOFF
// this._writeStopped = true;
// } else if (result.key === C0.DC1) { // XON
// this._writeStopped = false;
// }
if (result.type === KeyboardResultType.PAGE_DOWN || result.type === KeyboardResultType.PAGE_UP) {
const scrollCount = this.rows - 1;
this.scrollLines(result.type === KeyboardResultType.PAGE_UP ? -scrollCount : scrollCount);
@@ -1559,11 +1561,17 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
return true;
}
// If ctrl+c or enter is being sent, clear out the textarea. This is done so that screen readers
// will announce deleted characters. This will not work 100% of the time but it should cover
// most scenarios.
if (result.key === C0.ETX || result.key === C0.CR) {
this.textarea.value = '';
}
this._keyDownHandled = true;
this._onKey.fire({ key: result.key, domEvent: event });
this.showCursor();
this._coreService.triggerDataEvent(result.key, true);
return this.cancel(event, true);
}
private _isThirdLevelShift(browser: IBrowser, ev: IKeyboardEvent): boolean {
@@ -1621,6 +1629,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
protected _keyPress(ev: KeyboardEvent): boolean {
let key;
if (this._keyDownHandled) {
return false;
}
if (this._customKeyEventHandler && this._customKeyEventHandler(ev) === false) {
return false;
}
+9
View File
@@ -24,6 +24,15 @@ export interface IColorSet {
ansi: IColor[];
}
export interface IPartialColorSet {
foreground: IColor;
background: IColor;
cursor?: IColor;
cursorAccent?: IColor;
selection?: IColor;
ansi: IColor[];
}
export interface IViewport extends IDisposable {
scrollBarWidth: number;
syncScrollArea(): void;
@@ -3,22 +3,22 @@
* @license MIT
*/
import { IRenderLayer } from './Types';
import { IRenderDimensions } from 'browser/renderer/Types';
import { ITerminal } from '../Types';
import { IRenderDimensions, IRenderLayer } from 'browser/renderer/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 'browser/renderer/atlas/Types';
import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
import { BaseCharAtlas } from './atlas/BaseCharAtlas';
import { acquireCharAtlas } from './atlas/CharAtlasCache';
import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas';
import { acquireCharAtlas } from 'browser/renderer/atlas/CharAtlasCache';
import { AttributeData } from 'common/buffer/AttributeData';
import { IColorSet } from 'browser/Types';
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 {
private _canvas: HTMLCanvasElement;
protected _ctx: CanvasRenderingContext2D;
protected _ctx!: CanvasRenderingContext2D;
private _scaledCharWidth: number = 0;
private _scaledCharHeight: number = 0;
private _scaledCellWidth: number = 0;
@@ -26,7 +26,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
private _scaledCharLeft: 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.
@@ -46,7 +46,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
id: string,
zIndex: number,
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.classList.add(`xterm-${id}-layer`);
@@ -63,25 +66,25 @@ export abstract class BaseRenderLayer implements IRenderLayer {
}
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
if (!this._alpha) {
this._clearAll();
}
}
public onOptionsChanged(terminal: ITerminal): void {}
public onBlur(terminal: ITerminal): void {}
public onFocus(terminal: ITerminal): void {}
public onCursorMove(terminal: ITerminal): void {}
public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {}
public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number], columnSelectMode: boolean = false): void {}
public onOptionsChanged(): void {}
public onBlur(): void {}
public onFocus(): void {}
public onCursorMove(): void {}
public onGridChanged(startRow: number, endRow: number): void {}
public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean = false): void {}
public setColors(terminal: ITerminal, colorSet: IColorSet): void {
this._refreshCharAtlas(terminal, colorSet);
public setColors(colorSet: IColorSet): void {
this._refreshCharAtlas(colorSet);
}
protected _setTransparency(terminal: ITerminal, alpha: boolean): void {
protected _setTransparency(alpha: boolean): void {
// Do nothing when alpha doesn't change
if (alpha === this._alpha) {
return;
@@ -96,24 +99,23 @@ export abstract class BaseRenderLayer implements IRenderLayer {
this._container.replaceChild(this._canvas, oldCanvas);
// Regenerate char atlas and force a full redraw
this._refreshCharAtlas(terminal, this._colors);
this.onGridChanged(terminal, 0, terminal.rows - 1);
this._refreshCharAtlas(this._colors);
this.onGridChanged(0, this._bufferService.rows - 1);
}
/**
* 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.
*/
private _refreshCharAtlas(terminal: ITerminal, colorSet: IColorSet): void {
private _refreshCharAtlas(colorSet: IColorSet): void {
if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) {
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();
}
public resize(terminal: ITerminal, dim: IRenderDimensions): void {
public resize(dim: IRenderDimensions): void {
this._scaledCellWidth = dim.scaledCellWidth;
this._scaledCellHeight = dim.scaledCellHeight;
this._scaledCharWidth = dim.scaledCharWidth;
@@ -130,10 +132,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
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.
@@ -233,16 +235,15 @@ export abstract class BaseRenderLayer implements IRenderLayer {
* Draws a truecolor character at the cell. The character will be clipped to
* ensure that it fits with the cell, including the cell to the right if it's
* a wide character. This uses the existing fillStyle on the context.
* @param terminal The terminal.
* @param cell The cell data for the character to draw.
* @param x The column to draw at.
* @param y The row to draw at.
* @param color The color of the character.
*/
protected _fillCharTrueColor(terminal: ITerminal, cell: CellData, x: number, y: number): void {
this._ctx.font = this._getFont(terminal, false, false);
protected _fillCharTrueColor(cell: CellData, x: number, y: number): void {
this._ctx.font = this._getFont(false, false);
this._ctx.textBaseline = 'middle';
this._clipRow(terminal, y);
this._clipRow(y);
this._ctx.fillText(
cell.getChars(),
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
* the character atlas to reduce draw time.
* @param terminal The terminal.
* @param chars The character or characters.
* @param code The character code.
* @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.
* @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
// Note: to avoid bad runtime JoinedCellData will be skipped
// in the cache handler itself (atlasDidDraw == false) and
// fall through to uncached later down below
if (cell.isFgRGB() || cell.isBgRGB()) {
this._drawUncachedChars(terminal, cell, x, y);
this._drawUncachedChars(cell, x, y);
return;
}
@@ -284,7 +284,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
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;
this._currentGlyphIdentifier.chars = cell.getChars() || WHITESPACE_CELL_CHAR;
@@ -302,7 +302,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
);
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
* clipped to ensure that they fit with the cell(s), including the cell to the
* right if the last character is a wide character.
* @param terminal The terminal.
* @param chars The character.
* @param width The width of the character.
* @param fg The foreground color, in the format stored within the attributes.
* @param x The column to draw at.
* @param y The row to draw at.
*/
private _drawUncachedChars(terminal: ITerminal, cell: ICellData, x: number, y: number): void {
private _drawUncachedChars(cell: ICellData, x: number, y: number): void {
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';
if (cell.isInverse()) {
@@ -337,14 +336,14 @@ export abstract class BaseRenderLayer implements IRenderLayer {
this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;
} else {
let fg = cell.getFgColor();
if (terminal.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {
if (this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {
fg += 8;
}
this._ctx.fillStyle = this._colors.ansi[fg].css;
}
}
this._clipRow(terminal, y);
this._clipRow(y);
// Apply alpha to dim the character
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.
* @param terminal The terminal.
* @param y The row to clip.
*/
private _clipRow(terminal: ITerminal, y: number): void {
private _clipRow(y: number): void {
this._ctx.beginPath();
this._ctx.rect(
0,
y * this._scaledCellHeight,
terminal.cols * this._scaledCellWidth,
this._bufferService.cols * this._scaledCellWidth,
this._scaledCellHeight);
this._ctx.clip();
}
/**
* Gets the current font.
* @param terminal The terminal.
* @param isBold If we should use the bold fontWeight.
*/
protected _getFont(terminal: ITerminal, isBold: boolean, isItalic: boolean): string {
const fontWeight = isBold ? terminal.options.fontWeightBold : terminal.options.fontWeight;
protected _getFont(isBold: boolean, isItalic: boolean): string {
const fontWeight = isBold ? this._optionsService.options.fontWeightBold : this._optionsService.options.fontWeight;
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
*/
import { ITerminal, ILinkifierAccessor } from '../Types';
import { IRenderDimensions } from 'browser/renderer/Types';
import { BaseRenderLayer } from './BaseRenderLayer';
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
import { is256Color } from './atlas/CharAtlasUtils';
import { IColorSet, ILinkifierEvent } from 'browser/Types';
import { is256Color } from 'browser/renderer/atlas/CharAtlasUtils';
import { IColorSet, ILinkifierEvent, ILinkifier } from 'browser/Types';
import { IBufferService, IOptionsService } from 'common/services/Services';
export class LinkRenderLayer extends BaseRenderLayer {
private _state: ILinkifierEvent = null;
private _state: ILinkifierEvent | undefined;
constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ILinkifierAccessor) {
super(container, 'link', zIndex, true, colors);
terminal.linkifier.onLinkHover(e => this._onLinkHover(e));
terminal.linkifier.onLinkLeave(e => this._onLinkLeave(e));
constructor(
container: HTMLElement,
zIndex: number,
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 {
super.resize(terminal, dim);
public resize(dim: IRenderDimensions): void {
super.resize(dim);
// 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();
}
@@ -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.y2, this._state.x2, 1);
this._state = null;
this._state = undefined;
}
}
private _onLinkHover(e: ILinkifierEvent): void {
if (e.fg === INVERTED_DEFAULT_COLOR) {
this._ctx.fillStyle = this._colors.background.css;
} else if (is256Color(e.fg)) {
} else if (e.fg && is256Color(e.fg)) {
// 256 color support
this._ctx.fillStyle = this._colors.ansi[e.fg].css;
} else {
+11
View File
@@ -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;
}
@@ -3,51 +3,58 @@
* @license MIT
*/
import { ITerminal } from '../Types';
import { IRenderDimensions } from 'browser/renderer/Types';
import { BaseRenderLayer } from './BaseRenderLayer';
import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer';
import { IColorSet } from 'browser/Types';
import { IBufferService, IOptionsService } from 'common/services/Services';
interface ISelectionState {
start: [number, number];
end: [number, number];
columnSelectMode: boolean;
ydisp: number;
start?: [number, number];
end?: [number, number];
columnSelectMode?: boolean;
ydisp?: number;
}
export class SelectionRenderLayer extends BaseRenderLayer {
private _state: ISelectionState;
private _state!: ISelectionState;
constructor(container: HTMLElement, zIndex: number, colors: IColorSet) {
super(container, 'selection', zIndex, true, colors);
constructor(
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();
}
private _clearState(): void {
this._state = {
start: null,
end: null,
columnSelectMode: null,
ydisp: null
start: undefined,
end: undefined,
columnSelectMode: undefined,
ydisp: undefined
};
}
public resize(terminal: ITerminal, dim: IRenderDimensions): void {
super.resize(terminal, dim);
public resize(dim: IRenderDimensions): void {
super.resize(dim);
// Resizing the canvas discards the contents of the canvas so clear state
this._clearState();
}
public reset(terminal: ITerminal): void {
public reset(): void {
if (this._state.start && this._state.end) {
this._clearState();
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
if (!this._didStateChange(start, end, columnSelectMode, terminal.buffer.ydisp)) {
if (!this._didStateChange(start, end, columnSelectMode, this._bufferService.buffer.ydisp)) {
return;
}
@@ -61,13 +68,13 @@ export class SelectionRenderLayer extends BaseRenderLayer {
}
// Translate from buffer position to viewport position
const viewportStartRow = start[1] - terminal.buffer.ydisp;
const viewportEndRow = end[1] - terminal.buffer.ydisp;
const viewportStartRow = start[1] - this._bufferService.buffer.ydisp;
const viewportEndRow = end[1] - this._bufferService.buffer.ydisp;
const viewportCappedStartRow = Math.max(viewportStartRow, 0);
const viewportCappedEndRow = Math.min(viewportEndRow, terminal.rows - 1);
const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1);
// No need to draw the selection
if (viewportCappedStartRow >= terminal.rows || viewportCappedEndRow < 0) {
if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) {
return;
}
@@ -81,17 +88,17 @@ export class SelectionRenderLayer extends BaseRenderLayer {
} else {
// Draw first row
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);
// Draw middle rows
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
if (viewportCappedStartRow !== viewportCappedEndRow) {
// 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);
}
}
@@ -100,7 +107,7 @@ export class SelectionRenderLayer extends BaseRenderLayer {
this._state.start = [start[0], start[1]];
this._state.end = [end[0], end[1]];
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 {
@@ -110,7 +117,7 @@ export class SelectionRenderLayer extends BaseRenderLayer {
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) {
return false;
}
@@ -4,15 +4,15 @@
*/
import { ICharacterJoinerRegistry, IRenderDimensions } from 'browser/renderer/Types';
import { ITerminal } from '../Types';
import { CharData, ICellData } from 'common/Types';
import { GridCache } from 'browser/renderer/GridCache';
import { BaseRenderLayer } from './BaseRenderLayer';
import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer';
import { AttributeData } from 'common/buffer/AttributeData';
import { NULL_CELL_CODE, Content } from 'common/buffer/Constants';
import { JoinedCellData } from 'browser/renderer/CharacterJoinerRegistry';
import { IColorSet } from 'browser/Types';
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
@@ -23,23 +23,32 @@ import { CellData } from 'common/buffer/CellData';
export class TextRenderLayer extends BaseRenderLayer {
private _state: GridCache<CharData>;
private _characterWidth: number;
private _characterFont: string;
private _characterWidth: number = 0;
private _characterFont: string = '';
private _characterOverlapCache: { [key: string]: boolean } = {};
private _characterJoinerRegistry: ICharacterJoinerRegistry;
private _workCell = new CellData();
constructor(container: HTMLElement, zIndex: number, colors: IColorSet, characterJoinerRegistry: ICharacterJoinerRegistry, alpha: boolean) {
super(container, 'text', zIndex, alpha, colors);
constructor(
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._characterJoinerRegistry = characterJoinerRegistry;
}
public resize(terminal: ITerminal, dim: IRenderDimensions): void {
super.resize(terminal, dim);
public resize(dim: IRenderDimensions): void {
super.resize(dim);
// 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) {
this._characterWidth = dim.scaledCharWidth;
this._characterFont = terminalFont;
@@ -47,16 +56,15 @@ export class TextRenderLayer extends BaseRenderLayer {
}
// Resizing the canvas discards the contents of the canvas so clear state
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._clearAll();
}
private _forEachCell(
terminal: ITerminal,
firstRow: number,
lastRow: number,
joinerRegistry: ICharacterJoinerRegistry | null,
@@ -67,11 +75,11 @@ export class TextRenderLayer extends BaseRenderLayer {
) => void
): void {
for (let y = firstRow; y <= lastRow; y++) {
const row = y + terminal.buffer.ydisp;
const line = terminal.buffer.lines.get(row);
const row = y + this._bufferService.buffer.ydisp;
const line = this._bufferService.buffer.lines.get(row);
const joinedRanges = joinerRegistry ? joinerRegistry.getJoinedCharacters(row) : [];
for (let x = 0; x < terminal.cols; x++) {
line.loadCell(x, this._workCell);
for (let x = 0; x < this._bufferService.cols; x++) {
line!.loadCell(x, this._workCell);
let cell = this._workCell;
// If true, indicates that the current character(s) to draw were joined.
@@ -89,14 +97,14 @@ export class TextRenderLayer extends BaseRenderLayer {
// and attributes of our input.
if (joinedRanges.length > 0 && x === joinedRanges[0][0]) {
isJoined = true;
const range = joinedRanges.shift();
const range = joinedRanges.shift()!;
// We already know the exact start and end column of the joined range,
// so we get the string and width representing it directly
cell = new JoinedCellData(
this._workCell,
line.translateToString(true, range[0], range[1]),
line!.translateToString(true, range[0], range[1]),
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
// already in the correct state.
// this._state.cache[x][y] = OVERLAP_OWNED_CHAR_DATA;
if (lastCharX < line.length - 1 && line.getCodePoint(lastCharX + 1) === NULL_CELL_CODE) {
if (lastCharX < line!.length - 1 && line!.getCodePoint(lastCharX + 1) === NULL_CELL_CODE) {
// patch width to 2
cell.content &= ~Content.WIDTH_MASK;
cell.content |= 2 << Content.WIDTH_SHIFT;
@@ -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
* 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 cols = terminal.cols;
const cols = this._bufferService.cols;
let startX: number = 0;
let startY: number = 0;
let prevFillStyle: string | null = null;
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,
// so we should too.
let nextFillStyle = null; // null represents default background color
@@ -176,15 +184,17 @@ export class TextRenderLayer extends BaseRenderLayer {
// don't need to draw anything.
startX = x;
startY = y;
} if (y !== startY) {
}
if (y !== startY) {
// our row changed, draw the previous row
ctx.fillStyle = prevFillStyle;
ctx.fillStyle = prevFillStyle ? prevFillStyle : '';
this._fillCells(startX, startY, cols - startX, 1);
startX = x;
startY = y;
} else if (prevFillStyle !== nextFillStyle) {
// our color changed, draw the previous characters in this row
ctx.fillStyle = prevFillStyle;
ctx.fillStyle = prevFillStyle ? prevFillStyle : '';
this._fillCells(startX, startY, x - startX, 1);
startX = x;
startY = y;
@@ -202,12 +212,12 @@ export class TextRenderLayer extends BaseRenderLayer {
ctx.restore();
}
private _drawForeground(terminal: ITerminal, firstRow: number, lastRow: number): void {
this._forEachCell(terminal, firstRow, lastRow, this._characterJoinerRegistry, (cell, x, y) => {
private _drawForeground(firstRow: number, lastRow: number): void {
this._forEachCell(firstRow, lastRow, this._characterJoinerRegistry, (cell, x, y) => {
if (cell.isInvisible()) {
return;
}
this._drawChars(terminal, cell, x, y);
this._drawChars(cell, x, y);
if (cell.isUnderline()) {
this._ctx.save();
@@ -226,7 +236,7 @@ export class TextRenderLayer extends BaseRenderLayer {
this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;
} else {
let fg = cell.getFgColor();
if (terminal.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {
if (this._optionsService.options.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {
fg += 8;
}
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
if (this._state.cache.length === 0) {
return;
@@ -249,13 +259,13 @@ export class TextRenderLayer extends BaseRenderLayer {
this._charAtlas.beginFrame();
}
this._clearCells(0, firstRow, terminal.cols, lastRow - firstRow + 1);
this._drawBackground(terminal, firstRow, lastRow);
this._drawForeground(terminal, firstRow, lastRow);
this._clearCells(0, firstRow, this._bufferService.cols, lastRow - firstRow + 1);
this._drawBackground(firstRow, lastRow);
this._drawForeground(firstRow, lastRow);
}
public onOptionsChanged(terminal: ITerminal): void {
this._setTransparency(terminal, terminal.options.allowTransparency);
public onOptionsChanged(): void {
this._setTransparency(this._optionsService.options.allowTransparency);
}
/**
+58
View File
@@ -56,3 +56,61 @@ export interface ICharacterJoinerRegistry {
deregisterCharacterJoiner(joinerId: number): boolean;
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
*/
import { IGlyphIdentifier } from './Types';
import { IDisposable } from 'xterm';
import { IGlyphIdentifier } from 'browser/renderer/atlas/Types';
import { IDisposable } from 'common/Types';
export abstract class BaseCharAtlas implements IDisposable {
private _didWarmUp: boolean = false;
@@ -3,19 +3,19 @@
* @license MIT
*/
import { ITerminal } from '../../Types';
import { generateConfig, configEquals } from './CharAtlasUtils';
import { BaseCharAtlas } from './BaseCharAtlas';
import { DynamicCharAtlas } from './DynamicCharAtlas';
import { ICharAtlasConfig } from './Types';
import { generateConfig, configEquals } from 'browser/renderer/atlas/CharAtlasUtils';
import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas';
import { DynamicCharAtlas } from 'browser/renderer/atlas/DynamicCharAtlas';
import { ICharAtlasConfig } from 'browser/renderer/atlas/Types';
import { IColorSet } from 'browser/Types';
import { ITerminalOptions } from 'common/services/Services';
interface ICharAtlasCacheEntry {
atlas: BaseCharAtlas;
config: ICharAtlasConfig;
// N.B. This implementation potentially holds onto copies of the terminal forever, so
// this may cause memory leaks.
ownedBy: ITerminal[];
ownedBy: number[];
}
const charAtlasCache: ICharAtlasCacheEntry[] = [];
@@ -23,26 +23,25 @@ const charAtlasCache: ICharAtlasCacheEntry[] = [];
/**
* Acquires a char atlas, either generating a new one or returning an existing
* one that is in use by another terminal.
* @param terminal The terminal.
* @param colors The colors to use.
*/
export function acquireCharAtlas(
terminal: ITerminal,
options: ITerminalOptions,
rendererId: number,
colors: IColorSet,
scaledCharWidth: number,
scaledCharHeight: number
): 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++) {
const entry = charAtlasCache[i];
const ownedByIndex = entry.ownedBy.indexOf(terminal);
const ownedByIndex = entry.ownedBy.indexOf(rendererId);
if (ownedByIndex >= 0) {
if (configEquals(entry.config, newConfig)) {
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) {
entry.atlas.dispose();
charAtlasCache.splice(i, 1);
@@ -57,8 +56,8 @@ export function acquireCharAtlas(
for (let i = 0; i < charAtlasCache.length; i++) {
const entry = charAtlasCache[i];
if (configEquals(entry.config, newConfig)) {
// Add the terminal to the cache entry and return
entry.ownedBy.push(terminal);
// Add the renderer to the cache entry and return
entry.ownedBy.push(rendererId);
return entry.atlas;
}
}
@@ -69,7 +68,7 @@ export function acquireCharAtlas(
newConfig
),
config: newConfig,
ownedBy: [terminal]
ownedBy: [rendererId]
};
charAtlasCache.push(newEntry);
return newEntry.atlas;
@@ -77,14 +76,13 @@ export function acquireCharAtlas(
/**
* 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++) {
const index = charAtlasCache[i].ownedBy.indexOf(terminal);
const index = charAtlasCache[i].ownedBy.indexOf(rendererId);
if (index !== -1) {
if (charAtlasCache[i].ownedBy.length === 1) {
// Remove the cache entry if it's the only terminal
// Remove the cache entry if it's the only renderer
charAtlasCache[i].atlas.dispose();
charAtlasCache.splice(i, 1);
} else {
@@ -3,19 +3,19 @@
* @license MIT
*/
import { ITerminal } from '../../Types';
import { ICharAtlasConfig } from './Types';
import { ICharAtlasConfig } from 'browser/renderer/atlas/Types';
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
const clonedColors = <IColorSet>{
const clonedColors = <IPartialColorSet>{
foreground: colors.foreground,
background: colors.background,
cursor: null,
cursorAccent: null,
selection: null,
cursor: undefined,
cursorAccent: undefined,
selection: undefined,
// For the static char atlas, we only use the first 16 colors, but we need all 256 for the
// dynamic character atlas.
ansi: colors.ansi.slice(0, 16)
@@ -24,11 +24,11 @@ export function generateConfig(scaledCharWidth: number, scaledCharHeight: number
devicePixelRatio: window.devicePixelRatio,
scaledCharWidth,
scaledCharHeight,
fontFamily: terminal.options.fontFamily,
fontSize: terminal.options.fontSize,
fontWeight: terminal.options.fontWeight,
fontWeightBold: terminal.options.fontWeightBold,
allowTransparency: terminal.options.allowTransparency,
fontFamily: options.fontFamily,
fontSize: options.fontSize,
fontWeight: options.fontWeight,
fontWeightBold: options.fontWeightBold,
allowTransparency: options.allowTransparency,
colors: clonedColors
};
}
@@ -3,13 +3,14 @@
* @license MIT
*/
import { IGlyphIdentifier, ICharAtlasConfig } from './Types';
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 { LRUMap } from 'browser/renderer/atlas/LRUMap';
import { isFirefox, isSafari } from 'common/Platform';
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,
// 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.
// It might also contain some characters with transparent backgrounds if allowTransparency is
// set.
this._cacheCtx = this._cacheCanvas.getContext('2d', {alpha: true});
this._cacheCtx = throwIfFalsy(this._cacheCanvas.getContext('2d', {alpha: true}));
const tmpCanvas = document.createElement('canvas');
tmpCanvas.width = this._config.scaledCharWidth;
tmpCanvas.height = this._config.scaledCharHeight;
this._tmpCtx = 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._height = Math.floor(TEXTURE_HEIGHT / this._config.scaledCharHeight);
@@ -144,7 +145,7 @@ export class DynamicCharAtlas extends BaseCharAtlas {
index = this._cacheMap.size;
} else {
// 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);
this._cacheMap.set(glyphKey, cacheValue);
@@ -186,7 +187,7 @@ export class DynamicCharAtlas extends BaseCharAtlas {
const cacheX = this._toCoordinateX(cacheValue.index);
const cacheY = this._toCoordinateY(cacheValue.index);
ctx.drawImage(
cacheValue.inBitmap ? this._bitmap : this._cacheCanvas,
cacheValue.inBitmap ? this._bitmap! : this._cacheCanvas,
cacheX,
cacheY,
this._config.scaledCharWidth,
@@ -3,8 +3,8 @@
* @license MIT
*/
import { FontWeight } from 'xterm';
import { IColorSet } from 'browser/Types';
import { FontWeight } from 'common/services/Services';
import { IPartialColorSet } from 'browser/Types';
export interface IGlyphIdentifier {
chars: string;
@@ -25,5 +25,5 @@ export interface ICharAtlasConfig {
scaledCharWidth: number;
scaledCharHeight: number;
allowTransparency: boolean;
colors: IColorSet;
colors: IPartialColorSet;
}
+4
View File
@@ -103,6 +103,10 @@ export class RenderService extends Disposable implements IRenderService {
this._onDimensionsChange.fire(this._renderer.dimensions);
}
public dispose(): void {
this._renderer.dispose();
}
public setRenderer(renderer: IRenderer): void {
// TODO: RenderCoordinator should be the only one to dispose the renderer
this._renderer.dispose();
+2 -1
View File
@@ -8,6 +8,7 @@ import { IRenderDimensions, IRenderer, CharacterJoinerHandler } from 'browser/re
import { IColorSet } from 'browser/Types';
import { ISelectionRedrawRequestEvent } from 'browser/selection/Types';
import { createDecorator } from 'common/services/ServiceRegistry';
import { IDisposable } from 'common/Types';
export const ICharSizeService = createDecorator<ICharSizeService>('CharSizeService');
export interface ICharSizeService {
@@ -31,7 +32,7 @@ export interface IMouseService {
}
export const IRenderService = createDecorator<IRenderService>('RenderService');
export interface IRenderService {
export interface IRenderService extends IDisposable {
serviceBrand: any;
onDimensionsChange: IEvent<IRenderDimensions>;
+3 -1
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { ICoreService, IOptionsService, IBufferService } from 'common/services/Services';
import { ICoreService, ILogService, IOptionsService, IBufferService } from 'common/services/Services';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { IDecPrivateModes } from 'common/Types';
import { clone } from 'common/Clone';
@@ -26,6 +26,7 @@ export class CoreService implements ICoreService {
// TODO: Move this into a service
private readonly _scrollToBottom: () => void,
@IBufferService private readonly _bufferService: IBufferService,
@ILogService private readonly _logService: ILogService,
@IOptionsService private readonly _optionsService: IOptionsService
) {
this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES);
@@ -53,6 +54,7 @@ export class CoreService implements ICoreService {
}
// Fire onData API
this._logService.debug('sending data', data);
this._onData.fire(data);
}
}

Some files were not shown because too many files have changed in this diff Show More