@xterm/addon-webgl 0.17.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Copyright (c) 2018 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
7
+
8
+ /**
9
+ * A matrix that when multiplies will translate 0-1 coordinates (left to right,
10
+ * top to bottom) to clip space.
11
+ */
12
+ export const PROJECTION_MATRIX = new Float32Array([
13
+ 2, 0, 0, 0,
14
+ 0, -2, 0, 0,
15
+ 0, 0, 1, 0,
16
+ -1, 1, 0, 1
17
+ ]);
18
+
19
+ export function createProgram(gl: WebGLRenderingContext, vertexSource: string, fragmentSource: string): WebGLProgram | undefined {
20
+ const program = throwIfFalsy(gl.createProgram());
21
+ gl.attachShader(program, throwIfFalsy(createShader(gl, gl.VERTEX_SHADER, vertexSource)));
22
+ gl.attachShader(program, throwIfFalsy(createShader(gl, gl.FRAGMENT_SHADER, fragmentSource)));
23
+ gl.linkProgram(program);
24
+ const success = gl.getProgramParameter(program, gl.LINK_STATUS);
25
+ if (success) {
26
+ return program;
27
+ }
28
+
29
+ console.error(gl.getProgramInfoLog(program));
30
+ gl.deleteProgram(program);
31
+ }
32
+
33
+ export function createShader(gl: WebGLRenderingContext, type: number, source: string): WebGLShader | undefined {
34
+ const shader = throwIfFalsy(gl.createShader(type));
35
+ gl.shaderSource(shader, source);
36
+ gl.compileShader(shader);
37
+ const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
38
+ if (success) {
39
+ return shader;
40
+ }
41
+
42
+ console.error(gl.getShaderInfoLog(shader));
43
+ gl.deleteShader(shader);
44
+ }
45
+
46
+ export function expandFloat32Array(source: Float32Array, max: number): Float32Array {
47
+ const newLength = Math.min(source.length * 2, max);
48
+ const newArray = new Float32Array(newLength);
49
+ for (let i = 0; i < source.length; i++) {
50
+ newArray[i] = source[i];
51
+ }
52
+ return newArray;
53
+ }
54
+
55
+ export class GLTexture {
56
+ public texture: WebGLTexture;
57
+ public version: number;
58
+
59
+ constructor(texture: WebGLTexture) {
60
+ this.texture = texture;
61
+ this.version = -1;
62
+ }
63
+ }
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { ReadonlyColorSet } from 'browser/Types';
7
+ import { acquireTextureAtlas } from 'browser/renderer/shared/CharAtlasCache';
8
+ import { TEXT_BASELINE } from 'browser/renderer/shared/Constants';
9
+ import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
10
+ import { IRenderDimensions, ITextureAtlas } from 'browser/renderer/shared/Types';
11
+ import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
12
+ import { Disposable, toDisposable } from 'common/Lifecycle';
13
+ import { CellData } from 'common/buffer/CellData';
14
+ import { IOptionsService } from 'common/services/Services';
15
+ import { Terminal } from '@xterm/xterm';
16
+ import { IRenderLayer } from './Types';
17
+
18
+ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer {
19
+ private _canvas: HTMLCanvasElement;
20
+ protected _ctx!: CanvasRenderingContext2D;
21
+ private _deviceCharWidth: number = 0;
22
+ private _deviceCharHeight: number = 0;
23
+ private _deviceCellWidth: number = 0;
24
+ private _deviceCellHeight: number = 0;
25
+ private _deviceCharLeft: number = 0;
26
+ private _deviceCharTop: number = 0;
27
+
28
+ protected _charAtlas: ITextureAtlas | undefined;
29
+
30
+ constructor(
31
+ terminal: Terminal,
32
+ private _container: HTMLElement,
33
+ id: string,
34
+ zIndex: number,
35
+ private _alpha: boolean,
36
+ protected readonly _coreBrowserService: ICoreBrowserService,
37
+ protected readonly _optionsService: IOptionsService,
38
+ protected readonly _themeService: IThemeService
39
+ ) {
40
+ super();
41
+ this._canvas = this._coreBrowserService.mainDocument.createElement('canvas');
42
+ this._canvas.classList.add(`xterm-${id}-layer`);
43
+ this._canvas.style.zIndex = zIndex.toString();
44
+ this._initCanvas();
45
+ this._container.appendChild(this._canvas);
46
+ this.register(this._themeService.onChangeColors(e => {
47
+ this._refreshCharAtlas(terminal, e);
48
+ this.reset(terminal);
49
+ }));
50
+ this.register(toDisposable(() => {
51
+ this._canvas.remove();
52
+ }));
53
+ }
54
+
55
+ private _initCanvas(): void {
56
+ this._ctx = throwIfFalsy(this._canvas.getContext('2d', { alpha: this._alpha }));
57
+ // Draw the background if this is an opaque layer
58
+ if (!this._alpha) {
59
+ this._clearAll();
60
+ }
61
+ }
62
+
63
+ public handleBlur(terminal: Terminal): void {}
64
+ public handleFocus(terminal: Terminal): void {}
65
+ public handleCursorMove(terminal: Terminal): void {}
66
+ public handleGridChanged(terminal: Terminal, startRow: number, endRow: number): void {}
67
+ public handleSelectionChanged(terminal: Terminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {}
68
+
69
+ protected _setTransparency(terminal: Terminal, alpha: boolean): void {
70
+ // Do nothing when alpha doesn't change
71
+ if (alpha === this._alpha) {
72
+ return;
73
+ }
74
+
75
+ // Create new canvas and replace old one
76
+ const oldCanvas = this._canvas;
77
+ this._alpha = alpha;
78
+ // Cloning preserves properties
79
+ this._canvas = this._canvas.cloneNode() as HTMLCanvasElement;
80
+ this._initCanvas();
81
+ this._container.replaceChild(this._canvas, oldCanvas);
82
+
83
+ // Regenerate char atlas and force a full redraw
84
+ this._refreshCharAtlas(terminal, this._themeService.colors);
85
+ this.handleGridChanged(terminal, 0, terminal.rows - 1);
86
+ }
87
+
88
+ /**
89
+ * Refreshes the char atlas, aquiring a new one if necessary.
90
+ * @param terminal The terminal.
91
+ * @param colorSet The color set to use for the char atlas.
92
+ */
93
+ private _refreshCharAtlas(terminal: Terminal, colorSet: ReadonlyColorSet): void {
94
+ if (this._deviceCharWidth <= 0 && this._deviceCharHeight <= 0) {
95
+ return;
96
+ }
97
+ this._charAtlas = acquireTextureAtlas(terminal, this._optionsService.rawOptions, colorSet, this._deviceCellWidth, this._deviceCellHeight, this._deviceCharWidth, this._deviceCharHeight, this._coreBrowserService.dpr);
98
+ this._charAtlas.warmUp();
99
+ }
100
+
101
+ public resize(terminal: Terminal, dim: IRenderDimensions): void {
102
+ this._deviceCellWidth = dim.device.cell.width;
103
+ this._deviceCellHeight = dim.device.cell.height;
104
+ this._deviceCharWidth = dim.device.char.width;
105
+ this._deviceCharHeight = dim.device.char.height;
106
+ this._deviceCharLeft = dim.device.char.left;
107
+ this._deviceCharTop = dim.device.char.top;
108
+ this._canvas.width = dim.device.canvas.width;
109
+ this._canvas.height = dim.device.canvas.height;
110
+ this._canvas.style.width = `${dim.css.canvas.width}px`;
111
+ this._canvas.style.height = `${dim.css.canvas.height}px`;
112
+
113
+ // Draw the background if this is an opaque layer
114
+ if (!this._alpha) {
115
+ this._clearAll();
116
+ }
117
+
118
+ this._refreshCharAtlas(terminal, this._themeService.colors);
119
+ }
120
+
121
+ public abstract reset(terminal: Terminal): void;
122
+
123
+ /**
124
+ * Fills a 1px line (2px on HDPI) at the bottom of the cell. This uses the
125
+ * existing fillStyle on the context.
126
+ * @param x The column to fill.
127
+ * @param y The row to fill.
128
+ */
129
+ protected _fillBottomLineAtCells(x: number, y: number, width: number = 1): void {
130
+ this._ctx.fillRect(
131
+ x * this._deviceCellWidth,
132
+ (y + 1) * this._deviceCellHeight - this._coreBrowserService.dpr - 1 /* Ensure it's drawn within the cell */,
133
+ width * this._deviceCellWidth,
134
+ this._coreBrowserService.dpr);
135
+ }
136
+
137
+ /**
138
+ * Clears the entire canvas.
139
+ */
140
+ protected _clearAll(): void {
141
+ if (this._alpha) {
142
+ this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
143
+ } else {
144
+ this._ctx.fillStyle = this._themeService.colors.background.css;
145
+ this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Clears 1+ cells completely.
151
+ * @param x The column to start at.
152
+ * @param y The row to start at.
153
+ * @param width The number of columns to clear.
154
+ * @param height The number of rows to clear.
155
+ */
156
+ protected _clearCells(x: number, y: number, width: number, height: number): void {
157
+ if (this._alpha) {
158
+ this._ctx.clearRect(
159
+ x * this._deviceCellWidth,
160
+ y * this._deviceCellHeight,
161
+ width * this._deviceCellWidth,
162
+ height * this._deviceCellHeight);
163
+ } else {
164
+ this._ctx.fillStyle = this._themeService.colors.background.css;
165
+ this._ctx.fillRect(
166
+ x * this._deviceCellWidth,
167
+ y * this._deviceCellHeight,
168
+ width * this._deviceCellWidth,
169
+ height * this._deviceCellHeight);
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Draws a truecolor character at the cell. The character will be clipped to
175
+ * ensure that it fits with the cell, including the cell to the right if it's
176
+ * a wide character. This uses the existing fillStyle on the context.
177
+ * @param terminal The terminal.
178
+ * @param cell The cell data for the character to draw.
179
+ * @param x The column to draw at.
180
+ * @param y The row to draw at.
181
+ */
182
+ protected _fillCharTrueColor(terminal: Terminal, cell: CellData, x: number, y: number): void {
183
+ this._ctx.font = this._getFont(terminal, false, false);
184
+ this._ctx.textBaseline = TEXT_BASELINE;
185
+ this._clipCell(x, y, cell.getWidth());
186
+ this._ctx.fillText(
187
+ cell.getChars(),
188
+ x * this._deviceCellWidth + this._deviceCharLeft,
189
+ y * this._deviceCellHeight + this._deviceCharTop + this._deviceCharHeight);
190
+ }
191
+
192
+ /**
193
+ * Clips a cell to ensure no pixels will be drawn outside of it.
194
+ * @param x The column to clip.
195
+ * @param y The row to clip.
196
+ * @param width The number of columns to clip.
197
+ */
198
+ private _clipCell(x: number, y: number, width: number): void {
199
+ this._ctx.beginPath();
200
+ this._ctx.rect(
201
+ x * this._deviceCellWidth,
202
+ y * this._deviceCellHeight,
203
+ width * this._deviceCellWidth,
204
+ this._deviceCellHeight);
205
+ this._ctx.clip();
206
+ }
207
+
208
+ /**
209
+ * Gets the current font.
210
+ * @param terminal The terminal.
211
+ * @param isBold If we should use the bold fontWeight.
212
+ */
213
+ protected _getFont(terminal: Terminal, isBold: boolean, isItalic: boolean): string {
214
+ const fontWeight = isBold ? terminal.options.fontWeightBold : terminal.options.fontWeight;
215
+ const fontStyle = isItalic ? 'italic' : '';
216
+
217
+ return `${fontStyle} ${fontWeight} ${terminal.options.fontSize! * this._coreBrowserService.dpr}px ${terminal.options.fontFamily}`;
218
+ }
219
+ }
220
+
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { is256Color } from 'browser/renderer/shared/CharAtlasUtils';
7
+ import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants';
8
+ import { IRenderDimensions } from 'browser/renderer/shared/Types';
9
+ import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
10
+ import { ILinkifier2, ILinkifierEvent } from 'browser/Types';
11
+ import { IOptionsService } from 'common/services/Services';
12
+ import { Terminal } from '@xterm/xterm';
13
+ import { BaseRenderLayer } from './BaseRenderLayer';
14
+
15
+ export class LinkRenderLayer extends BaseRenderLayer {
16
+ private _state: ILinkifierEvent | undefined;
17
+
18
+ constructor(
19
+ container: HTMLElement,
20
+ zIndex: number,
21
+ terminal: Terminal,
22
+ linkifier2: ILinkifier2,
23
+ coreBrowserService: ICoreBrowserService,
24
+ optionsService: IOptionsService,
25
+ themeService: IThemeService
26
+ ) {
27
+ super(terminal, container, 'link', zIndex, true, coreBrowserService, optionsService, themeService);
28
+
29
+ this.register(linkifier2.onShowLinkUnderline(e => this._handleShowLinkUnderline(e)));
30
+ this.register(linkifier2.onHideLinkUnderline(e => this._handleHideLinkUnderline(e)));
31
+ }
32
+
33
+ public resize(terminal: Terminal, dim: IRenderDimensions): void {
34
+ super.resize(terminal, dim);
35
+ // Resizing the canvas discards the contents of the canvas so clear state
36
+ this._state = undefined;
37
+ }
38
+
39
+ public reset(terminal: Terminal): void {
40
+ this._clearCurrentLink();
41
+ }
42
+
43
+ private _clearCurrentLink(): void {
44
+ if (this._state) {
45
+ this._clearCells(this._state.x1, this._state.y1, this._state.cols - this._state.x1, 1);
46
+ const middleRowCount = this._state.y2 - this._state.y1 - 1;
47
+ if (middleRowCount > 0) {
48
+ this._clearCells(0, this._state.y1 + 1, this._state.cols, middleRowCount);
49
+ }
50
+ this._clearCells(0, this._state.y2, this._state.x2, 1);
51
+ this._state = undefined;
52
+ }
53
+ }
54
+
55
+ private _handleShowLinkUnderline(e: ILinkifierEvent): void {
56
+ if (e.fg === INVERTED_DEFAULT_COLOR) {
57
+ this._ctx.fillStyle = this._themeService.colors.background.css;
58
+ } else if (e.fg !== undefined && is256Color(e.fg)) {
59
+ // 256 color support
60
+ this._ctx.fillStyle = this._themeService.colors.ansi[e.fg!].css;
61
+ } else {
62
+ this._ctx.fillStyle = this._themeService.colors.foreground.css;
63
+ }
64
+
65
+ if (e.y1 === e.y2) {
66
+ // Single line link
67
+ this._fillBottomLineAtCells(e.x1, e.y1, e.x2 - e.x1);
68
+ } else {
69
+ // Multi-line link
70
+ this._fillBottomLineAtCells(e.x1, e.y1, e.cols - e.x1);
71
+ for (let y = e.y1 + 1; y < e.y2; y++) {
72
+ this._fillBottomLineAtCells(0, y, e.cols);
73
+ }
74
+ this._fillBottomLineAtCells(0, e.y2, e.x2);
75
+ }
76
+ this._state = e;
77
+ }
78
+
79
+ private _handleHideLinkUnderline(e: ILinkifierEvent): void {
80
+ this._clearCurrentLink();
81
+ }
82
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { IDisposable, Terminal } from '@xterm/xterm';
7
+ import { IRenderDimensions } from 'browser/renderer/shared/Types';
8
+
9
+ export interface IRenderLayer extends IDisposable {
10
+ /**
11
+ * Called when the terminal loses focus.
12
+ */
13
+ handleBlur(terminal: Terminal): void;
14
+
15
+ /**
16
+ * Called when the terminal gets focus.
17
+ */
18
+ handleFocus(terminal: Terminal): void;
19
+
20
+ /**
21
+ * Called when the cursor is moved.
22
+ */
23
+ handleCursorMove(terminal: Terminal): void;
24
+
25
+ /**
26
+ * Called when the data in the grid has changed (or needs to be rendered
27
+ * again).
28
+ */
29
+ handleGridChanged(terminal: Terminal, startRow: number, endRow: number): void;
30
+
31
+ /**
32
+ * Calls when the selection changes.
33
+ */
34
+ handleSelectionChanged(terminal: Terminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void;
35
+
36
+ /**
37
+ * Registers a handler to join characters to render as a group
38
+ */
39
+ registerCharacterJoiner?(handler: (text: string) => [number, number][]): void;
40
+
41
+ /**
42
+ * Deregisters the specified character joiner handler
43
+ */
44
+ deregisterCharacterJoiner?(joinerId: number): void;
45
+
46
+ /**
47
+ * Resize the render layer.
48
+ */
49
+ resize(terminal: Terminal, dim: IRenderDimensions): void;
50
+
51
+ /**
52
+ * Clear the state of the render layer.
53
+ */
54
+ reset(terminal: Terminal): void;
55
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { Terminal, ITerminalAddon, IEvent } from '@xterm/xterm';
7
+
8
+ declare module '@xterm/addon-webgl' {
9
+ /**
10
+ * An xterm.js addon that provides search functionality.
11
+ */
12
+ export class WebglAddon implements ITerminalAddon {
13
+ public textureAtlas?: HTMLCanvasElement;
14
+
15
+ /**
16
+ * An event that is fired when the renderer loses its canvas context.
17
+ */
18
+ public readonly onContextLoss: IEvent<void>;
19
+
20
+ /**
21
+ * An event that is fired when the texture atlas of the renderer changes.
22
+ */
23
+ public readonly onChangeTextureAtlas: IEvent<HTMLCanvasElement>;
24
+
25
+ /**
26
+ * An event that is fired when the a new page is added to the texture atlas.
27
+ */
28
+ public readonly onAddTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
29
+
30
+ constructor(preserveDrawingBuffer?: boolean);
31
+
32
+ /**
33
+ * Activates the addon.
34
+ * @param terminal The terminal the addon is being loaded in.
35
+ */
36
+ public activate(terminal: Terminal): void;
37
+
38
+ /**
39
+ * Disposes the addon.
40
+ */
41
+ public dispose(): void;
42
+
43
+ /**
44
+ * Clears the terminal's texture atlas and triggers a redraw.
45
+ */
46
+ public clearTextureAtlas(): void;
47
+ }
48
+ }