mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Move parts of webgl renderer to public API
This commit is contained in:
@@ -32,6 +32,7 @@ export class Terminal implements ITerminalApi {
|
||||
public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; }
|
||||
|
||||
public get element(): HTMLElement { return this._core.element; }
|
||||
public get screenElement(): HTMLElement { return this._core.screenElement; }
|
||||
public get textarea(): HTMLTextAreaElement { return this._core.textarea; }
|
||||
public get rows(): number { return this._core.rows; }
|
||||
public get cols(): number { return this._core.cols; }
|
||||
|
||||
@@ -15,6 +15,7 @@ export class LinkRenderLayer extends BaseRenderLayer {
|
||||
|
||||
constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ILinkifierAccessor) {
|
||||
super(container, 'link', zIndex, true, colors);
|
||||
// TODO: Need to expose link-related renderer API
|
||||
terminal.linkifier.onLinkHover(e => this._onLinkHover(e));
|
||||
terminal.linkifier.onLinkLeave(e => this._onLinkLeave(e));
|
||||
}
|
||||
|
||||
@@ -5,16 +5,14 @@
|
||||
|
||||
import { createProgram, PROJECTION_MATRIX } from './WebglUtils';
|
||||
import { IRenderDimensions } from '../Types';
|
||||
import { ITerminal } from '../../Types';
|
||||
import WebglCharAtlas from './atlas/WebglCharAtlas';
|
||||
import { IWebGL2RenderingContext, IWebGLVertexArrayObject, IRenderModel, IRasterizedGlyph } from './Types';
|
||||
import { INDICIES_PER_CELL } from './WebglRenderer';
|
||||
import { COMBINED_CHAR_BIT_MASK } from './RenderModel';
|
||||
import { fill, slice } from './TypedArray';
|
||||
import { NULL_CELL_CODE, WHITESPACE_CELL_CODE, CHAR_DATA_CHAR_INDEX } from '../../core/buffer/BufferLine';
|
||||
import { IBufferLine } from '../../core/Types';
|
||||
import { NULL_CELL_CODE, WHITESPACE_CELL_CODE } from '../../core/buffer/BufferLine';
|
||||
import { getLuminance } from './ColorUtils';
|
||||
import { IColorSet } from 'xterm';
|
||||
import { IColorSet, Terminal, IBufferLine } from 'xterm';
|
||||
|
||||
interface IVertices {
|
||||
attributes: Float32Array;
|
||||
@@ -97,7 +95,7 @@ export class GlyphRenderer {
|
||||
};
|
||||
|
||||
constructor(
|
||||
private _terminal: ITerminal,
|
||||
private _terminal: Terminal,
|
||||
private _colors: IColorSet,
|
||||
private _gl: IWebGL2RenderingContext,
|
||||
private _dimensions: IRenderDimensions
|
||||
@@ -185,9 +183,9 @@ export class GlyphRenderer {
|
||||
|
||||
let rasterizedGlyph: IRasterizedGlyph;
|
||||
if (chars && chars.length > 1) {
|
||||
rasterizedGlyph = this._atlas.getRasterizedGlyphCombinedChar(chars, attr, bg, fg, this._terminal.options.enableBold);
|
||||
rasterizedGlyph = this._atlas.getRasterizedGlyphCombinedChar(chars, attr, bg, fg, this._terminal.getOption('enableBold'));
|
||||
} else {
|
||||
rasterizedGlyph = this._atlas.getRasterizedGlyph(code, attr, bg, fg, this._terminal.options.enableBold);
|
||||
rasterizedGlyph = this._atlas.getRasterizedGlyph(code, attr, bg, fg, this._terminal.getOption('enableBold'));
|
||||
}
|
||||
|
||||
// Fill empty if no glyph was found
|
||||
@@ -252,8 +250,8 @@ export class GlyphRenderer {
|
||||
|
||||
private _updateSelectionRange(startCol: number, endCol: number, y: number, model: IRenderModel, bg: number, fg: number): void {
|
||||
const terminal = this._terminal;
|
||||
const row = y + terminal.buffer.ydisp;
|
||||
let line: IBufferLine;
|
||||
const row = y + terminal.buffer.viewportY;
|
||||
let line: IBufferLine | undefined;
|
||||
for (let x = startCol; x < endCol; x++) {
|
||||
const offset = (y * this._terminal.cols + x) * INDICIES_PER_CELL;
|
||||
// Because the cache uses attr as a lookup key it needs to contain the selection colors as well
|
||||
@@ -262,10 +260,9 @@ export class GlyphRenderer {
|
||||
const code = model.cells[offset];
|
||||
if (code & COMBINED_CHAR_BIT_MASK) {
|
||||
if (!line) {
|
||||
line = terminal.buffer.lines.get(row);
|
||||
line = terminal.buffer.getLine(row);
|
||||
}
|
||||
const charData = line.get(x);
|
||||
const chars = charData[CHAR_DATA_CHAR_INDEX];
|
||||
const chars = line.getCell(x).char;
|
||||
this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], attr, bg, fg, chars);
|
||||
} else {
|
||||
this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], attr, bg, fg);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ITerminal } from '../../Types';
|
||||
import { IRenderDimensions } from '../Types';
|
||||
import { createProgram, expandFloat32Array, PROJECTION_MATRIX } from './WebglUtils';
|
||||
import { IRenderModel, IWebGLVertexArrayObject, IWebGL2RenderingContext, ISelectionRenderModel } from './Types';
|
||||
@@ -11,7 +10,7 @@ import { fill } from './TypedArray';
|
||||
import { INVERTED_DEFAULT_COLOR } from './atlas/Types';
|
||||
import { is256Color } from './atlas/CharAtlasUtils';
|
||||
import { DEFAULT_COLOR } from '../../common/Types';
|
||||
import { IColorSet, IColor } from 'xterm';
|
||||
import { IColorSet, IColor, Terminal } from 'xterm';
|
||||
|
||||
const enum VertexAttribLocations {
|
||||
POSITION = 0,
|
||||
@@ -76,7 +75,7 @@ export class RectangleRenderer {
|
||||
};
|
||||
|
||||
constructor(
|
||||
private _terminal: ITerminal,
|
||||
private _terminal: Terminal,
|
||||
private _colors: IColorSet,
|
||||
private _gl: IWebGL2RenderingContext,
|
||||
private _dimensions: IRenderDimensions
|
||||
|
||||
@@ -17,7 +17,7 @@ import { RenderModel, COMBINED_CHAR_BIT_MASK } from './RenderModel';
|
||||
import { Disposable } from '../../common/Lifecycle';
|
||||
import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CODE } from '../../core/buffer/BufferLine';
|
||||
import { DEFAULT_COLOR } from '../../common/Types';
|
||||
import { IColorSet } from 'xterm';
|
||||
import { IColorSet, Terminal } from 'xterm';
|
||||
import { getLuminance } from './ColorUtils';
|
||||
|
||||
export const INDICIES_PER_CELL = 4;
|
||||
@@ -36,16 +36,20 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
|
||||
public dimensions: IRenderDimensions;
|
||||
|
||||
private _core: ITerminal;
|
||||
|
||||
constructor(
|
||||
private _terminal: ITerminal,
|
||||
private _terminal: Terminal,
|
||||
private _colors: IColorSet
|
||||
) {
|
||||
super();
|
||||
|
||||
this._core = (this._terminal as any)._core;
|
||||
|
||||
this._applyBgLuminanceBasedSelection();
|
||||
|
||||
this._renderLayers = [
|
||||
new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._terminal),
|
||||
new LinkRenderLayer(this._terminal.screenElement, 2, this._colors, this._core),
|
||||
new CursorRenderLayer(this._terminal.screenElement, 3, this._colors)
|
||||
];
|
||||
this.dimensions = {
|
||||
@@ -102,8 +106,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
|
||||
// Clear layers and force a full render
|
||||
this._renderLayers.forEach(l => {
|
||||
l.setColors(this._terminal, this._colors);
|
||||
l.reset(this._terminal);
|
||||
l.setColors(this._core, this._colors);
|
||||
l.reset(this._core);
|
||||
});
|
||||
|
||||
this._rectangleRenderer.setColors();
|
||||
@@ -117,7 +121,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
// and the terminal needs to refreshed
|
||||
if (this._devicePixelRatio !== window.devicePixelRatio) {
|
||||
this._devicePixelRatio = window.devicePixelRatio;
|
||||
this.onResize(this._terminal.cols, this._terminal.rows);
|
||||
this.onResize(this._core.cols, this._core.rows);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,11 +129,11 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
// Update character and canvas dimensions
|
||||
this._updateDimensions(devicePixelRatio);
|
||||
|
||||
this._model.resize(this._terminal.cols, this._terminal.rows);
|
||||
this._model.resize(this._core.cols, this._core.rows);
|
||||
this._rectangleRenderer.onResize();
|
||||
|
||||
// Resize all render layers
|
||||
this._renderLayers.forEach(l => l.resize(this._terminal, this.dimensions));
|
||||
this._renderLayers.forEach(l => l.resize(this._core, this.dimensions));
|
||||
|
||||
// Resize the canvas
|
||||
this._canvas.width = this.dimensions.scaledCanvasWidth;
|
||||
@@ -151,15 +155,15 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
public onBlur(): void {
|
||||
this._renderLayers.forEach(l => l.onBlur(this._terminal));
|
||||
this._renderLayers.forEach(l => l.onBlur(this._core));
|
||||
}
|
||||
|
||||
public onFocus(): void {
|
||||
this._renderLayers.forEach(l => l.onFocus(this._terminal));
|
||||
this._renderLayers.forEach(l => l.onFocus(this._core));
|
||||
}
|
||||
|
||||
public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean): void {
|
||||
this._renderLayers.forEach(l => l.onSelectionChanged(this._terminal, start, end, columnSelectMode));
|
||||
this._renderLayers.forEach(l => l.onSelectionChanged(this._core, start, end, columnSelectMode));
|
||||
|
||||
this._updateSelectionModel(start, end);
|
||||
|
||||
@@ -167,15 +171,15 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
this._glyphRenderer.updateSelection(this._model, columnSelectMode);
|
||||
|
||||
// TODO: #2102 Should this move to RenderCoordinator?
|
||||
this._terminal.refresh(0, this._terminal.rows - 1);
|
||||
this._core.refresh(0, this._core.rows - 1);
|
||||
}
|
||||
|
||||
public onCursorMove(): void {
|
||||
this._renderLayers.forEach(l => l.onCursorMove(this._terminal));
|
||||
this._renderLayers.forEach(l => l.onCursorMove(this._core));
|
||||
}
|
||||
|
||||
public onOptionsChanged(): void {
|
||||
this._renderLayers.forEach(l => l.onOptionsChanged(this._terminal));
|
||||
this._renderLayers.forEach(l => l.onOptionsChanged(this._core));
|
||||
this._updateDimensions();
|
||||
this._refreshCharAtlas();
|
||||
}
|
||||
@@ -200,7 +204,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._renderLayers.forEach(l => l.reset(this._terminal));
|
||||
this._renderLayers.forEach(l => l.reset(this._core));
|
||||
}
|
||||
|
||||
public registerCharacterJoiner(handler: CharacterJoinerHandler): number {
|
||||
@@ -213,7 +217,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
|
||||
public renderRows(start: number, end: number): void {
|
||||
// Update render layers
|
||||
this._renderLayers.forEach(l => l.onGridChanged(this._terminal, start, end));
|
||||
this._renderLayers.forEach(l => l.onGridChanged(this._core, start, end));
|
||||
|
||||
// Tell renderer the frame is beginning
|
||||
if (this._glyphRenderer.beginFrame()) {
|
||||
@@ -229,7 +233,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
private _updateModel(start: number, end: number): void {
|
||||
const terminal = this._terminal;
|
||||
const terminal = this._core;
|
||||
|
||||
for (let y = start; y <= end; y++) {
|
||||
const row = y + terminal.buffer.ydisp;
|
||||
@@ -288,7 +292,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
private _updateSelectionModel(start: [number, number], end: [number, number]): void {
|
||||
const terminal = this._terminal;
|
||||
const terminal = this._core;
|
||||
|
||||
// Selection does not exist
|
||||
if (!start || !end || (start[0] === end[0] && start[1] === end[1])) {
|
||||
@@ -322,7 +326,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
*/
|
||||
private _updateDimensions(devicePixelRatio: number = window.devicePixelRatio): void {
|
||||
// Perform a new measure if the CharMeasure dimensions are not yet available
|
||||
if (!this._terminal.charMeasure.width || !this._terminal.charMeasure.height) {
|
||||
if (!this._core.charMeasure.width || !this._core.charMeasure.height) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -333,34 +337,34 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
|
||||
// NOTE: ceil fixes sometime, floor does others :s
|
||||
|
||||
this.dimensions.scaledCharWidth = Math.floor(this._terminal.charMeasure.width * devicePixelRatio);
|
||||
this.dimensions.scaledCharWidth = Math.floor(this._core.charMeasure.width * devicePixelRatio);
|
||||
|
||||
// Calculate the scaled character height. Height is ceiled in case
|
||||
// devicePixelRatio is a floating point number in order to ensure there is
|
||||
// enough space to draw the character to the cell.
|
||||
this.dimensions.scaledCharHeight = Math.ceil(this._terminal.charMeasure.height * devicePixelRatio);
|
||||
this.dimensions.scaledCharHeight = Math.ceil(this._core.charMeasure.height * devicePixelRatio);
|
||||
|
||||
// Calculate the scaled cell height, if lineHeight is not 1 then the value
|
||||
// will be floored because since lineHeight can never be lower then 1, there
|
||||
// is a guarentee that the scaled line height will always be larger than
|
||||
// scaled char height.
|
||||
this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._terminal.options.lineHeight);
|
||||
this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._core.options.lineHeight);
|
||||
|
||||
// Calculate the y coordinate within a cell that text should draw from in
|
||||
// order to draw in the center of a cell.
|
||||
this.dimensions.scaledCharTop = this._terminal.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2);
|
||||
this.dimensions.scaledCharTop = this._core.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2);
|
||||
|
||||
// Calculate the scaled cell width, taking the letterSpacing into account.
|
||||
this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._terminal.options.letterSpacing);
|
||||
this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._core.options.letterSpacing);
|
||||
|
||||
// Calculate the x coordinate with a cell that text should draw from in
|
||||
// order to draw in the center of a cell.
|
||||
this.dimensions.scaledCharLeft = Math.floor(this._terminal.options.letterSpacing / 2);
|
||||
this.dimensions.scaledCharLeft = Math.floor(this._core.options.letterSpacing / 2);
|
||||
|
||||
// Recalculate the canvas dimensions; scaled* define the actual number of
|
||||
// pixel in the canvas
|
||||
this.dimensions.scaledCanvasHeight = this._terminal.rows * this.dimensions.scaledCellHeight;
|
||||
this.dimensions.scaledCanvasWidth = this._terminal.cols * this.dimensions.scaledCellWidth;
|
||||
this.dimensions.scaledCanvasHeight = this._core.rows * this.dimensions.scaledCellHeight;
|
||||
this.dimensions.scaledCanvasWidth = this._core.cols * this.dimensions.scaledCellWidth;
|
||||
|
||||
// The the size of the canvas on the page. It's very important that this
|
||||
// rounds to nearest integer and not ceils as browsers often set
|
||||
|
||||
@@ -16,8 +16,7 @@ export class WebglRendererAddon implements ITerminalAddon {
|
||||
throw new Error('Cannot activate WebglRendererAddon before Terminal.open');
|
||||
}
|
||||
this._terminal = terminal;
|
||||
const core = (terminal as any)._core;
|
||||
this._terminal.setRenderer(new WebglRenderer(core, core._colorManager.colors));
|
||||
this._terminal.setRenderer(new WebglRenderer(terminal, (terminal as any)._core._colorManager.colors));
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
|
||||
@@ -3,19 +3,18 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ITerminal } from '../../../Types';
|
||||
import { generateConfig, configEquals } from './CharAtlasUtils';
|
||||
import BaseCharAtlas from './BaseCharAtlas';
|
||||
import WebglCharAtlas from './WebglCharAtlas';
|
||||
import { ICharAtlasConfig } from './Types';
|
||||
import { IColorSet } from 'xterm';
|
||||
import { IColorSet, Terminal } from 'xterm';
|
||||
|
||||
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: Terminal[];
|
||||
}
|
||||
|
||||
const charAtlasCache: ICharAtlasCacheEntry[] = [];
|
||||
@@ -27,7 +26,7 @@ const charAtlasCache: ICharAtlasCacheEntry[] = [];
|
||||
* @param colors The colors to use.
|
||||
*/
|
||||
export function acquireCharAtlas(
|
||||
terminal: ITerminal,
|
||||
terminal: Terminal,
|
||||
colors: IColorSet,
|
||||
scaledCharWidth: number,
|
||||
scaledCharHeight: number
|
||||
@@ -76,7 +75,7 @@ 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(terminal: Terminal): void {
|
||||
for (let i = 0; i < charAtlasCache.length; i++) {
|
||||
const index = charAtlasCache[i].ownedBy.indexOf(terminal);
|
||||
if (index !== -1) {
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ITerminal } from '../../../Types';
|
||||
import { ICharAtlasConfig } from './Types';
|
||||
import { DEFAULT_COLOR } from '../../../common/Types';
|
||||
import { IColorSet } from 'xterm';
|
||||
import { IColorSet, Terminal, FontWeight } from 'xterm';
|
||||
|
||||
export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, terminal: ITerminal, colors: IColorSet): ICharAtlasConfig {
|
||||
export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, terminal: Terminal, colors: IColorSet): ICharAtlasConfig {
|
||||
// null out some fields that don't matter
|
||||
const clonedColors: IColorSet = {
|
||||
foreground: colors.foreground,
|
||||
@@ -21,15 +20,14 @@ export function generateConfig(scaledCharWidth: number, scaledCharHeight: number
|
||||
ansi: colors.ansi.slice(0, 16)
|
||||
};
|
||||
return {
|
||||
type: terminal.options.experimentalCharAtlas,
|
||||
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: terminal.getOption('fontFamily'),
|
||||
fontSize: terminal.getOption('fontSize'),
|
||||
fontWeight: terminal.getOption('fontWeight') as FontWeight,
|
||||
fontWeightBold: terminal.getOption('fontWeightBold') as FontWeight,
|
||||
allowTransparency: terminal.getOption('allowTransparency'),
|
||||
colors: clonedColors
|
||||
};
|
||||
}
|
||||
@@ -40,8 +38,7 @@ export function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return a.type === b.type &&
|
||||
a.devicePixelRatio === b.devicePixelRatio &&
|
||||
return a.devicePixelRatio === b.devicePixelRatio &&
|
||||
a.fontFamily === b.fontFamily &&
|
||||
a.fontSize === b.fontSize &&
|
||||
a.fontWeight === b.fontWeight &&
|
||||
|
||||
@@ -21,7 +21,6 @@ export interface IGlyphIdentifier {
|
||||
}
|
||||
|
||||
export interface ICharAtlasConfig {
|
||||
type: 'none' | 'static' | 'dynamic' | 'webgl';
|
||||
devicePixelRatio: number;
|
||||
fontSize: number;
|
||||
fontFamily: string;
|
||||
|
||||
Vendored
+5
@@ -895,6 +895,11 @@ declare module 'xterm' {
|
||||
* (EXPERIMENTAL)
|
||||
*/
|
||||
setRenderer(renderer: any): void;
|
||||
screenElement: HTMLElement;
|
||||
}
|
||||
|
||||
export namespace Renderer {
|
||||
const DEFAULT_COLOR: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user