Skip to content

Commit

Permalink
Merge branch 'master' into new_parser
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyriar authored May 21, 2018
2 parents d8afdb1 + 0ad1080 commit c1f8225
Show file tree
Hide file tree
Showing 21 changed files with 739 additions and 77 deletions.
8 changes: 8 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Benjamin Woodruff <github@benjam.info>
Bill Church <billchurch@users.noreply.github.com>
Bob Reid <bobreid@Bobs-MacBook-Pro.local>
bottleofwater <nison.mael+bottleofwater@gmail.com>
Brandon Bayer <b@bayer.ws>
Brian Mock <brian@mockbrian.com>
Bruno Ribeiro <b.m.fernandes.ribeiro@gmail.com>
Bruno Ribeito <b.m.fernandes.ribeiro@gmail.com>
Expand Down Expand Up @@ -62,12 +63,16 @@ Jianhui Zhao <jianhuizhao329@gmail.com>
Joao Moreno <jomo@microsoft.com>
Joao Moreno <mail@joaomoreno.com>
Johannes Zellner <johannes@nebulon.de>
Jon Austin <jon.i.austin@gmail.com>
Jon Masters <jon.masters@sky.com>
Jörg Breitbart <jerch@rockborn.de>
jpoth <poth.john@gmail.com>
Justin Luk <jluk@users.noreply.github.com>
Justin Mecham <justin@mecham.me>
Kirill Merkushev <lanwen@yandex.ru>
Krasimir Tsonev <krasimir@outset.ws>
Ledion Bitincka <lbitincka@gmail.com>
Linus Unnebäck <linus@folkdatorn.se>
Luca <LucaT1@users.noreply.github.com>
Lucian Buzzo <lucian.buzzo@gmail.com>
Lukas Drgon <lukas.drgon@gmail.com>
Expand All @@ -88,8 +93,11 @@ npezza93 <npezza93@gmail.com>
Oleksandr Andriienko <oandriie@redhat.com>
Paris Kasidiaris <pariskasidiaris@gmail.com>
Paris Kasidiaris <paris@sourcelair.com>
Peng Xiao <pengxiao@outlook.com>
Peter Baumgarten <me@peterbaumgarten.com>
Philip Olson <philip.olson@protonmail.ch>
pro-src <34285059+pro-src@users.noreply.github.com>
pro-src <rodneyd.teal@gmail.com>
Rick Baker <rick@ricktbaker.com>
runarberg <runar@greenqloud.com>
Saad Malik <simfox3@gmail.com>
Expand Down
13 changes: 13 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ <h2>Options</h2>
<p>
<label><input type="checkbox" id="option-mac-option-is-meta"> macOptionIsMeta</label>
</p>
<p>
<label><input type="checkbox" id="option-transparency"> transparency</label>
</p>
<p>
<label>
cursorStyle
Expand Down Expand Up @@ -53,6 +56,16 @@ <h2>Options</h2>
<p>
<label>tabStopWidth <input type="number" id="option-tabstopwidth" value="8" /></label>
</p>
<p>
<label>
experimentalCharAtlas
<select id="option-experimental-char-atlas">
<option value="static" selected>static</option>
<option value="dynamic">dynamic</option>
<option value="none">none</option>
</select>
</label>
</p>
<div>
<h3>Size</h3>
<div>
Expand Down
16 changes: 13 additions & 3 deletions demo/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ var terminalContainer = document.getElementById('terminal-container'),
cursorStyle: document.querySelector('#option-cursor-style'),
macOptionIsMeta: document.querySelector('#option-mac-option-is-meta'),
scrollback: document.querySelector('#option-scrollback'),
transparency: document.querySelector('#option-transparency'),
tabstopwidth: document.querySelector('#option-tabstopwidth'),
experimentalCharAtlas: document.querySelector('#option-experimental-char-atlas'),
bellStyle: document.querySelector('#option-bell-style'),
screenReaderMode: document.querySelector('#option-screen-reader-mode')
},
Expand Down Expand Up @@ -74,21 +76,29 @@ actionElements.findPrevious.addEventListener('keypress', function (e) {
optionElements.cursorBlink.addEventListener('change', function () {
term.setOption('cursorBlink', optionElements.cursorBlink.checked);
});
optionElements.macOptionIsMeta.addEventListener('change', function () {
term.setOption('macOptionIsMeta', optionElements.macOptionIsMeta.checked);
});
optionElements.transparency.addEventListener('change', function () {
var checked = optionElements.transparency.checked;
term.setOption('allowTransparency', checked);
term.setOption('theme', checked ? {background: 'rgba(0, 0, 0, .5)'} : {});
});
optionElements.cursorStyle.addEventListener('change', function () {
term.setOption('cursorStyle', optionElements.cursorStyle.value);
});
optionElements.bellStyle.addEventListener('change', function () {
term.setOption('bellStyle', optionElements.bellStyle.value);
});
optionElements.macOptionIsMeta.addEventListener('change', function () {
term.setOption('macOptionIsMeta', optionElements.macOptionIsMeta.checked);
});
optionElements.scrollback.addEventListener('change', function () {
term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10));
});
optionElements.tabstopwidth.addEventListener('change', function () {
term.setOption('tabStopWidth', parseInt(optionElements.tabstopwidth.value, 10));
});
optionElements.experimentalCharAtlas.addEventListener('change', function () {
term.setOption('experimentalCharAtlas', optionElements.experimentalCharAtlas.value);
});
optionElements.screenReaderMode.addEventListener('change', function () {
term.setOption('screenReaderMode', optionElements.screenReaderMode.checked);
});
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "xterm",
"description": "Full xterm terminal, in your browser",
"version": "3.3.0",
"version": "3.4.0",
"main": "lib/Terminal.js",
"types": "typings/xterm.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
Expand Down
4 changes: 3 additions & 1 deletion src/Terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import { MouseZoneManager } from './input/MouseZoneManager';
import { AccessibilityManager } from './AccessibilityManager';
import { ScreenDprMonitor } from './utils/ScreenDprMonitor';
import { ITheme, ILocalizableStrings, IMarker, IDisposable } from 'xterm';
import { removeTerminalFromCache } from './renderer/atlas/CharAtlas';
import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache';

// reg + shift key mappings for digits and special chars
const KEYCODE_KEY_MAPPINGS = {
Expand Down Expand Up @@ -105,6 +105,7 @@ const DEFAULT_OPTIONS: ITerminalOptions = {
bellStyle: 'none',
drawBoldTextInBrightColors: true,
enableBold: true,
experimentalCharAtlas: 'static',
fontFamily: 'courier-new, courier, monospace',
fontSize: 15,
fontWeight: 'normal',
Expand Down Expand Up @@ -472,6 +473,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
this.charMeasure.measure(this.options);
}
break;
case 'experimentalCharAtlas':
case 'enableBold':
case 'letterSpacing':
case 'lineHeight':
Expand Down
65 changes: 16 additions & 49 deletions src/renderer/BaseRenderLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import { IRenderLayer, IColorSet, IRenderDimensions } from './Types';
import { CharData, ITerminal } from '../Types';
import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from './atlas/Types';
import { CHAR_ATLAS_CELL_SPACING } from '../shared/atlas/Types';
import { acquireCharAtlas } from './atlas/CharAtlas';
import BaseCharAtlas from './atlas/BaseCharAtlas';
import { acquireCharAtlas } from './atlas/CharAtlasCache';
import { CHAR_DATA_CHAR_INDEX } from '../Buffer';

export abstract class BaseRenderLayer implements IRenderLayer {
Expand All @@ -20,7 +20,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
private _scaledCharLeft: number = 0;
private _scaledCharTop: number = 0;

private _charAtlas: HTMLCanvasElement | ImageBitmap;
protected _charAtlas: BaseCharAtlas;

constructor(
private _container: HTMLElement,
Expand Down Expand Up @@ -83,13 +83,8 @@ export abstract class BaseRenderLayer implements IRenderLayer {
if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) {
return;
}
this._charAtlas = null;
const result = acquireCharAtlas(terminal, colorSet, this._scaledCharWidth, this._scaledCharHeight);
if (result instanceof HTMLCanvasElement) {
this._charAtlas = result;
} else {
result.then(bitmap => this._charAtlas = bitmap);
}
this._charAtlas = acquireCharAtlas(terminal, colorSet, this._scaledCharWidth, this._scaledCharHeight);
this._charAtlas.warmUp();
}

public resize(terminal: ITerminal, dim: IRenderDimensions): void {
Expand Down Expand Up @@ -243,46 +238,18 @@ export abstract class BaseRenderLayer implements IRenderLayer {
* @param bold Whether the text is bold.
*/
protected drawChar(terminal: ITerminal, char: string, code: number, width: number, x: number, y: number, fg: number, bg: number, bold: boolean, dim: boolean, italic: boolean): void {
const isAscii = code < 256;
// A color is basic if it is one of the 4 bit ANSI colors.
const isBasicColor = fg < 16;
const isDefaultColor = fg >= 256;
const isDefaultBackground = bg >= 256;
const drawInBrightColor = (terminal.options.drawBoldTextInBrightColors && bold && fg < 8);
if (this._charAtlas && isAscii && (isBasicColor || isDefaultColor) && isDefaultBackground && !italic) {
this._ctx.save(); // we may set globalAlpha, so we need to be able to restore
let colorIndex: number;
if (isDefaultColor) {
colorIndex = (bold && terminal.options.enableBold ? 1 : 0);
} else {
colorIndex = 2 + fg + (bold && terminal.options.enableBold ? 16 : 0) + (drawInBrightColor ? 8 : 0);
}

// ImageBitmap's draw about twice as fast as from a canvas
const charAtlasCellWidth = this._scaledCharWidth + CHAR_ATLAS_CELL_SPACING;
const charAtlasCellHeight = this._scaledCharHeight + CHAR_ATLAS_CELL_SPACING;

// Apply alpha to dim the character
if (dim) {
this._ctx.globalAlpha = DIM_OPACITY;
}

this._ctx.drawImage(this._charAtlas,
code * charAtlasCellWidth,
colorIndex * charAtlasCellHeight,
charAtlasCellWidth,
this._scaledCharHeight,
x * this._scaledCellWidth + this._scaledCharLeft,
y * this._scaledCellHeight + this._scaledCharTop,
charAtlasCellWidth,
this._scaledCharHeight);
this._ctx.restore();
} else {
this._drawUncachedChar(terminal, char, width, fg + (drawInBrightColor ? 8 : 0), x, y, bold && terminal.options.enableBold, dim, italic);
const drawInBrightColor = terminal.options.drawBoldTextInBrightColors && bold && fg < 8;
fg += drawInBrightColor ? 8 : 0;
const atlasDidDraw = this._charAtlas && this._charAtlas.draw(
this._ctx,
{char, code, bg, fg, bold: bold && terminal.options.enableBold, dim, italic},
x * this._scaledCellWidth + this._scaledCharLeft,
y * this._scaledCellHeight + this._scaledCharTop
);

if (!atlasDidDraw) {
this._drawUncachedChar(terminal, char, width, fg, x, y, bold && terminal.options.enableBold, dim, italic);
}
// This draws the atlas (for debugging purposes)
// this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
// this._ctx.drawImage(this._charAtlas, 0, 0);
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/renderer/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ export class Renderer extends EventEmitter implements IRenderer {
}

public onOptionsChanged(): void {
this.colorManager.allowTransparency = this._terminal.options.allowTransparency;
this._runOperation(l => l.onOptionsChanged(this._terminal));
}

Expand Down
2 changes: 2 additions & 0 deletions src/renderer/TextRenderLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ export class TextRenderLayer extends BaseRenderLayer {
return;
}

this._charAtlas.beginFrame();

this.clearCells(0, firstRow, terminal.cols, lastRow - firstRow + 1);
this._drawBackground(terminal, firstRow, lastRow);
this._drawForeground(terminal, firstRow, lastRow);
Expand Down
53 changes: 53 additions & 0 deletions src/renderer/atlas/BaseCharAtlas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/

import { IGlyphIdentifier } from './Types';

export default abstract class BaseCharAtlas {
private _didWarmUp: boolean = false;

/**
* Perform any work needed to warm the cache before it can be used. May be called multiple times.
* Implement _doWarmUp instead if you only want to get called once.
*/
public warmUp(): void {
if (!this._didWarmUp) {
this._doWarmUp();
this._didWarmUp = true;
}
}

/**
* Perform any work needed to warm the cache before it can be used. Used by the default
* implementation of warmUp(), and will only be called once.
*/
protected _doWarmUp(): void { }

/**
* Called when we start drawing a new frame.
*
* TODO: We rely on this getting called by TextRenderLayer. This should really be called by
* Renderer instead, but we need to make Renderer the source-of-truth for the char atlas, instead
* of BaseRenderLayer.
*/
public beginFrame(): void { }

/**
* May be called before warmUp finishes, however it is okay for the implementation to
* do nothing and return false in that case.
*
* @param ctx Where to draw the character onto.
* @param glyph Information about what to draw
* @param x The position on the context to start drawing at
* @param y The position on the context to start drawing at
* @returns The success state. True if we drew the character.
*/
public abstract draw(
ctx: CanvasRenderingContext2D,
glyph: IGlyphIdentifier,
x: number,
y: number
): boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,40 @@
import { ITerminal } from '../../Types';
import { IColorSet } from '../Types';
import { ICharAtlasConfig } from '../../shared/atlas/Types';
import { generateCharAtlas } from '../../shared/atlas/CharAtlasGenerator';
import { generateConfig, configEquals } from './CharAtlasUtils';
import BaseCharAtlas from './BaseCharAtlas';
import DynamicCharAtlas from './DynamicCharAtlas';
import NoneCharAtlas from './NoneCharAtlas';
import StaticCharAtlas from './StaticCharAtlas';

const charAtlasImplementations = {
'none': NoneCharAtlas,
'static': StaticCharAtlas,
'dynamic': DynamicCharAtlas
};

interface ICharAtlasCacheEntry {
bitmap: HTMLCanvasElement | Promise<ImageBitmap>;
atlas: BaseCharAtlas;
config: ICharAtlasConfig;
// N.B. This implementation potentially holds onto copies of the terminal forever, so
// this may cause memory leaks.
ownedBy: ITerminal[];
}

let charAtlasCache: ICharAtlasCacheEntry[] = [];
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, colors: IColorSet, scaledCharWidth: number, scaledCharHeight: number): HTMLCanvasElement | Promise<ImageBitmap> {
export function acquireCharAtlas(
terminal: ITerminal,
colors: IColorSet,
scaledCharWidth: number,
scaledCharHeight: number
): BaseCharAtlas {
const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, terminal, colors);

// TODO: Currently if a terminal changes configs it will not free the entry reference (until it's disposed)
Expand All @@ -34,7 +50,7 @@ export function acquireCharAtlas(terminal: ITerminal, colors: IColorSet, scaledC
const ownedByIndex = entry.ownedBy.indexOf(terminal);
if (ownedByIndex >= 0) {
if (configEquals(entry.config, newConfig)) {
return entry.bitmap;
return entry.atlas;
}
// The configs differ, release the terminal from the entry
if (entry.ownedBy.length === 1) {
Expand All @@ -52,24 +68,20 @@ export function acquireCharAtlas(terminal: ITerminal, colors: IColorSet, scaledC
if (configEquals(entry.config, newConfig)) {
// Add the terminal to the cache entry and return
entry.ownedBy.push(terminal);
return entry.bitmap;
return entry.atlas;
}
}

const canvasFactory = (width: number, height: number) => {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
return canvas;
};

const newEntry: ICharAtlasCacheEntry = {
bitmap: generateCharAtlas(window, canvasFactory, newConfig),
atlas: new charAtlasImplementations[terminal.options.experimentalCharAtlas](
document,
newConfig
),
config: newConfig,
ownedBy: [terminal]
};
charAtlasCache.push(newEntry);
return newEntry.bitmap;
return newEntry.atlas;
}

/**
Expand Down
Loading

0 comments on commit c1f8225

Please sign in to comment.