@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.
- package/LICENSE +19 -0
- package/README.md +41 -0
- package/lib/addon-webgl.js +2 -0
- package/lib/addon-webgl.js.map +1 -0
- package/package.json +28 -0
- package/src/GlyphRenderer.ts +381 -0
- package/src/RectangleRenderer.ts +382 -0
- package/src/RenderModel.ts +41 -0
- package/src/TypedArray.ts +32 -0
- package/src/Types.d.ts +33 -0
- package/src/WebglAddon.ts +93 -0
- package/src/WebglRenderer.ts +646 -0
- package/src/WebglUtils.ts +63 -0
- package/src/renderLayer/BaseRenderLayer.ts +220 -0
- package/src/renderLayer/LinkRenderLayer.ts +82 -0
- package/src/renderLayer/Types.ts +55 -0
- package/typings/addon-webgl.d.ts +48 -0
|
@@ -0,0 +1,382 @@
|
|
|
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
|
+
import { IRenderDimensions } from 'browser/renderer/shared/Types';
|
|
8
|
+
import { IThemeService } from 'browser/services/Services';
|
|
9
|
+
import { ReadonlyColorSet } from 'browser/Types';
|
|
10
|
+
import { Attributes, FgFlags } from 'common/buffer/Constants';
|
|
11
|
+
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
12
|
+
import { IColor } from 'common/Types';
|
|
13
|
+
import { Terminal } from '@xterm/xterm';
|
|
14
|
+
import { RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel';
|
|
15
|
+
import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject } from './Types';
|
|
16
|
+
import { createProgram, expandFloat32Array, PROJECTION_MATRIX } from './WebglUtils';
|
|
17
|
+
|
|
18
|
+
const enum VertexAttribLocations {
|
|
19
|
+
POSITION = 0,
|
|
20
|
+
SIZE = 1,
|
|
21
|
+
COLOR = 2,
|
|
22
|
+
UNIT_QUAD = 3
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const vertexShaderSource = `#version 300 es
|
|
26
|
+
layout (location = ${VertexAttribLocations.POSITION}) in vec2 a_position;
|
|
27
|
+
layout (location = ${VertexAttribLocations.SIZE}) in vec2 a_size;
|
|
28
|
+
layout (location = ${VertexAttribLocations.COLOR}) in vec4 a_color;
|
|
29
|
+
layout (location = ${VertexAttribLocations.UNIT_QUAD}) in vec2 a_unitquad;
|
|
30
|
+
|
|
31
|
+
uniform mat4 u_projection;
|
|
32
|
+
|
|
33
|
+
out vec4 v_color;
|
|
34
|
+
|
|
35
|
+
void main() {
|
|
36
|
+
vec2 zeroToOne = a_position + (a_unitquad * a_size);
|
|
37
|
+
gl_Position = u_projection * vec4(zeroToOne, 0.0, 1.0);
|
|
38
|
+
v_color = a_color;
|
|
39
|
+
}`;
|
|
40
|
+
|
|
41
|
+
const fragmentShaderSource = `#version 300 es
|
|
42
|
+
precision lowp float;
|
|
43
|
+
|
|
44
|
+
in vec4 v_color;
|
|
45
|
+
|
|
46
|
+
out vec4 outColor;
|
|
47
|
+
|
|
48
|
+
void main() {
|
|
49
|
+
outColor = v_color;
|
|
50
|
+
}`;
|
|
51
|
+
|
|
52
|
+
const INDICES_PER_RECTANGLE = 8;
|
|
53
|
+
const BYTES_PER_RECTANGLE = INDICES_PER_RECTANGLE * Float32Array.BYTES_PER_ELEMENT;
|
|
54
|
+
|
|
55
|
+
const INITIAL_BUFFER_RECTANGLE_CAPACITY = 20 * INDICES_PER_RECTANGLE;
|
|
56
|
+
|
|
57
|
+
class Vertices {
|
|
58
|
+
public attributes: Float32Array;
|
|
59
|
+
public count: number;
|
|
60
|
+
|
|
61
|
+
constructor() {
|
|
62
|
+
this.attributes = new Float32Array(INITIAL_BUFFER_RECTANGLE_CAPACITY);
|
|
63
|
+
this.count = 0;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Work variables to avoid garbage collection
|
|
68
|
+
let $rgba = 0;
|
|
69
|
+
let $x1 = 0;
|
|
70
|
+
let $y1 = 0;
|
|
71
|
+
let $r = 0;
|
|
72
|
+
let $g = 0;
|
|
73
|
+
let $b = 0;
|
|
74
|
+
let $a = 0;
|
|
75
|
+
|
|
76
|
+
export class RectangleRenderer extends Disposable {
|
|
77
|
+
|
|
78
|
+
private _program: WebGLProgram;
|
|
79
|
+
private _vertexArrayObject: IWebGLVertexArrayObject;
|
|
80
|
+
private _attributesBuffer: WebGLBuffer;
|
|
81
|
+
private _projectionLocation: WebGLUniformLocation;
|
|
82
|
+
private _bgFloat!: Float32Array;
|
|
83
|
+
private _cursorFloat!: Float32Array;
|
|
84
|
+
|
|
85
|
+
private _vertices: Vertices = new Vertices();
|
|
86
|
+
private _verticesCursor: Vertices = new Vertices();
|
|
87
|
+
|
|
88
|
+
constructor(
|
|
89
|
+
private _terminal: Terminal,
|
|
90
|
+
private _gl: IWebGL2RenderingContext,
|
|
91
|
+
private _dimensions: IRenderDimensions,
|
|
92
|
+
private readonly _themeService: IThemeService
|
|
93
|
+
) {
|
|
94
|
+
super();
|
|
95
|
+
|
|
96
|
+
const gl = this._gl;
|
|
97
|
+
|
|
98
|
+
this._program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource));
|
|
99
|
+
this.register(toDisposable(() => gl.deleteProgram(this._program)));
|
|
100
|
+
|
|
101
|
+
// Uniform locations
|
|
102
|
+
this._projectionLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_projection'));
|
|
103
|
+
|
|
104
|
+
// Create and set the vertex array object
|
|
105
|
+
this._vertexArrayObject = gl.createVertexArray();
|
|
106
|
+
gl.bindVertexArray(this._vertexArrayObject);
|
|
107
|
+
|
|
108
|
+
// Setup a_unitquad, this defines the 4 vertices of a rectangle
|
|
109
|
+
const unitQuadVertices = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]);
|
|
110
|
+
const unitQuadVerticesBuffer = gl.createBuffer();
|
|
111
|
+
this.register(toDisposable(() => gl.deleteBuffer(unitQuadVerticesBuffer)));
|
|
112
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, unitQuadVerticesBuffer);
|
|
113
|
+
gl.bufferData(gl.ARRAY_BUFFER, unitQuadVertices, gl.STATIC_DRAW);
|
|
114
|
+
gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD);
|
|
115
|
+
gl.vertexAttribPointer(VertexAttribLocations.UNIT_QUAD, 2, this._gl.FLOAT, false, 0, 0);
|
|
116
|
+
|
|
117
|
+
// Setup the unit quad element array buffer, this points to indices in
|
|
118
|
+
// unitQuadVertices to allow is to draw 2 triangles from the vertices via a
|
|
119
|
+
// triangle strip
|
|
120
|
+
const unitQuadElementIndices = new Uint8Array([0, 1, 2, 3]);
|
|
121
|
+
const elementIndicesBuffer = gl.createBuffer();
|
|
122
|
+
this.register(toDisposable(() => gl.deleteBuffer(elementIndicesBuffer)));
|
|
123
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementIndicesBuffer);
|
|
124
|
+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, unitQuadElementIndices, gl.STATIC_DRAW);
|
|
125
|
+
|
|
126
|
+
// Setup attributes
|
|
127
|
+
this._attributesBuffer = throwIfFalsy(gl.createBuffer());
|
|
128
|
+
this.register(toDisposable(() => gl.deleteBuffer(this._attributesBuffer)));
|
|
129
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer);
|
|
130
|
+
gl.enableVertexAttribArray(VertexAttribLocations.POSITION);
|
|
131
|
+
gl.vertexAttribPointer(VertexAttribLocations.POSITION, 2, gl.FLOAT, false, BYTES_PER_RECTANGLE, 0);
|
|
132
|
+
gl.vertexAttribDivisor(VertexAttribLocations.POSITION, 1);
|
|
133
|
+
gl.enableVertexAttribArray(VertexAttribLocations.SIZE);
|
|
134
|
+
gl.vertexAttribPointer(VertexAttribLocations.SIZE, 2, gl.FLOAT, false, BYTES_PER_RECTANGLE, 2 * Float32Array.BYTES_PER_ELEMENT);
|
|
135
|
+
gl.vertexAttribDivisor(VertexAttribLocations.SIZE, 1);
|
|
136
|
+
gl.enableVertexAttribArray(VertexAttribLocations.COLOR);
|
|
137
|
+
gl.vertexAttribPointer(VertexAttribLocations.COLOR, 4, gl.FLOAT, false, BYTES_PER_RECTANGLE, 4 * Float32Array.BYTES_PER_ELEMENT);
|
|
138
|
+
gl.vertexAttribDivisor(VertexAttribLocations.COLOR, 1);
|
|
139
|
+
|
|
140
|
+
this._updateCachedColors(_themeService.colors);
|
|
141
|
+
this.register(this._themeService.onChangeColors(e => {
|
|
142
|
+
this._updateCachedColors(e);
|
|
143
|
+
this._updateViewportRectangle();
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public renderBackgrounds(): void {
|
|
148
|
+
this._renderVertices(this._vertices);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
public renderCursor(): void {
|
|
152
|
+
this._renderVertices(this._verticesCursor);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private _renderVertices(vertices: Vertices): void {
|
|
156
|
+
const gl = this._gl;
|
|
157
|
+
|
|
158
|
+
gl.useProgram(this._program);
|
|
159
|
+
|
|
160
|
+
gl.bindVertexArray(this._vertexArrayObject);
|
|
161
|
+
|
|
162
|
+
gl.uniformMatrix4fv(this._projectionLocation, false, PROJECTION_MATRIX);
|
|
163
|
+
|
|
164
|
+
// Bind attributes buffer and draw
|
|
165
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer);
|
|
166
|
+
gl.bufferData(gl.ARRAY_BUFFER, vertices.attributes, gl.DYNAMIC_DRAW);
|
|
167
|
+
gl.drawElementsInstanced(this._gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_BYTE, 0, vertices.count);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public handleResize(): void {
|
|
171
|
+
this._updateViewportRectangle();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public setDimensions(dimensions: IRenderDimensions): void {
|
|
175
|
+
this._dimensions = dimensions;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private _updateCachedColors(colors: ReadonlyColorSet): void {
|
|
179
|
+
this._bgFloat = this._colorToFloat32Array(colors.background);
|
|
180
|
+
this._cursorFloat = this._colorToFloat32Array(colors.cursor);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private _updateViewportRectangle(): void {
|
|
184
|
+
// Set first rectangle that clears the screen
|
|
185
|
+
this._addRectangleFloat(
|
|
186
|
+
this._vertices.attributes,
|
|
187
|
+
0,
|
|
188
|
+
0,
|
|
189
|
+
0,
|
|
190
|
+
this._terminal.cols * this._dimensions.device.cell.width,
|
|
191
|
+
this._terminal.rows * this._dimensions.device.cell.height,
|
|
192
|
+
this._bgFloat
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
public updateBackgrounds(model: IRenderModel): void {
|
|
197
|
+
const terminal = this._terminal;
|
|
198
|
+
const vertices = this._vertices;
|
|
199
|
+
|
|
200
|
+
// Declare variable ahead of time to avoid garbage collection
|
|
201
|
+
let rectangleCount = 1;
|
|
202
|
+
let y: number;
|
|
203
|
+
let x: number;
|
|
204
|
+
let currentStartX: number;
|
|
205
|
+
let currentBg: number;
|
|
206
|
+
let currentFg: number;
|
|
207
|
+
let currentInverse: boolean;
|
|
208
|
+
let modelIndex: number;
|
|
209
|
+
let bg: number;
|
|
210
|
+
let fg: number;
|
|
211
|
+
let inverse: boolean;
|
|
212
|
+
let offset: number;
|
|
213
|
+
|
|
214
|
+
for (y = 0; y < terminal.rows; y++) {
|
|
215
|
+
currentStartX = -1;
|
|
216
|
+
currentBg = 0;
|
|
217
|
+
currentFg = 0;
|
|
218
|
+
currentInverse = false;
|
|
219
|
+
for (x = 0; x < terminal.cols; x++) {
|
|
220
|
+
modelIndex = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
|
|
221
|
+
bg = model.cells[modelIndex + RENDER_MODEL_BG_OFFSET];
|
|
222
|
+
fg = model.cells[modelIndex + RENDER_MODEL_FG_OFFSET];
|
|
223
|
+
inverse = !!(fg & FgFlags.INVERSE);
|
|
224
|
+
if (bg !== currentBg || (fg !== currentFg && (currentInverse || inverse))) {
|
|
225
|
+
// A rectangle needs to be drawn if going from non-default to another color
|
|
226
|
+
if (currentBg !== 0 || (currentInverse && currentFg !== 0)) {
|
|
227
|
+
offset = rectangleCount++ * INDICES_PER_RECTANGLE;
|
|
228
|
+
this._updateRectangle(vertices, offset, currentFg, currentBg, currentStartX, x, y);
|
|
229
|
+
}
|
|
230
|
+
currentStartX = x;
|
|
231
|
+
currentBg = bg;
|
|
232
|
+
currentFg = fg;
|
|
233
|
+
currentInverse = inverse;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Finish rectangle if it's still going
|
|
237
|
+
if (currentBg !== 0 || (currentInverse && currentFg !== 0)) {
|
|
238
|
+
offset = rectangleCount++ * INDICES_PER_RECTANGLE;
|
|
239
|
+
this._updateRectangle(vertices, offset, currentFg, currentBg, currentStartX, terminal.cols, y);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
vertices.count = rectangleCount;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
public updateCursor(model: IRenderModel): void {
|
|
246
|
+
const vertices = this._verticesCursor;
|
|
247
|
+
const cursor = model.cursor;
|
|
248
|
+
if (!cursor || cursor.style === 'block') {
|
|
249
|
+
vertices.count = 0;
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
let offset: number;
|
|
254
|
+
let rectangleCount = 0;
|
|
255
|
+
|
|
256
|
+
if (cursor.style === 'bar' || cursor.style === 'outline') {
|
|
257
|
+
// Left edge
|
|
258
|
+
offset = rectangleCount++ * INDICES_PER_RECTANGLE;
|
|
259
|
+
this._addRectangleFloat(
|
|
260
|
+
vertices.attributes,
|
|
261
|
+
offset,
|
|
262
|
+
cursor.x * this._dimensions.device.cell.width,
|
|
263
|
+
cursor.y * this._dimensions.device.cell.height,
|
|
264
|
+
cursor.style === 'bar' ? cursor.dpr * cursor.cursorWidth : cursor.dpr,
|
|
265
|
+
this._dimensions.device.cell.height,
|
|
266
|
+
this._cursorFloat
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
if (cursor.style === 'underline' || cursor.style === 'outline') {
|
|
270
|
+
// Bottom edge
|
|
271
|
+
offset = rectangleCount++ * INDICES_PER_RECTANGLE;
|
|
272
|
+
this._addRectangleFloat(
|
|
273
|
+
vertices.attributes,
|
|
274
|
+
offset,
|
|
275
|
+
cursor.x * this._dimensions.device.cell.width,
|
|
276
|
+
(cursor.y + 1) * this._dimensions.device.cell.height - cursor.dpr,
|
|
277
|
+
cursor.width * this._dimensions.device.cell.width,
|
|
278
|
+
cursor.dpr,
|
|
279
|
+
this._cursorFloat
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
if (cursor.style === 'outline') {
|
|
283
|
+
// Top edge
|
|
284
|
+
offset = rectangleCount++ * INDICES_PER_RECTANGLE;
|
|
285
|
+
this._addRectangleFloat(
|
|
286
|
+
vertices.attributes,
|
|
287
|
+
offset,
|
|
288
|
+
cursor.x * this._dimensions.device.cell.width,
|
|
289
|
+
cursor.y * this._dimensions.device.cell.height,
|
|
290
|
+
cursor.width * this._dimensions.device.cell.width,
|
|
291
|
+
cursor.dpr,
|
|
292
|
+
this._cursorFloat
|
|
293
|
+
);
|
|
294
|
+
// Right edge
|
|
295
|
+
offset = rectangleCount++ * INDICES_PER_RECTANGLE;
|
|
296
|
+
this._addRectangleFloat(
|
|
297
|
+
vertices.attributes,
|
|
298
|
+
offset,
|
|
299
|
+
(cursor.x + cursor.width) * this._dimensions.device.cell.width - cursor.dpr,
|
|
300
|
+
cursor.y * this._dimensions.device.cell.height,
|
|
301
|
+
cursor.dpr,
|
|
302
|
+
this._dimensions.device.cell.height,
|
|
303
|
+
this._cursorFloat
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
vertices.count = rectangleCount;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
private _updateRectangle(vertices: Vertices, offset: number, fg: number, bg: number, startX: number, endX: number, y: number): void {
|
|
311
|
+
if (fg & FgFlags.INVERSE) {
|
|
312
|
+
switch (fg & Attributes.CM_MASK) {
|
|
313
|
+
case Attributes.CM_P16:
|
|
314
|
+
case Attributes.CM_P256:
|
|
315
|
+
$rgba = this._themeService.colors.ansi[fg & Attributes.PCOLOR_MASK].rgba;
|
|
316
|
+
break;
|
|
317
|
+
case Attributes.CM_RGB:
|
|
318
|
+
$rgba = (fg & Attributes.RGB_MASK) << 8;
|
|
319
|
+
break;
|
|
320
|
+
case Attributes.CM_DEFAULT:
|
|
321
|
+
default:
|
|
322
|
+
$rgba = this._themeService.colors.foreground.rgba;
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
switch (bg & Attributes.CM_MASK) {
|
|
326
|
+
case Attributes.CM_P16:
|
|
327
|
+
case Attributes.CM_P256:
|
|
328
|
+
$rgba = this._themeService.colors.ansi[bg & Attributes.PCOLOR_MASK].rgba;
|
|
329
|
+
break;
|
|
330
|
+
case Attributes.CM_RGB:
|
|
331
|
+
$rgba = (bg & Attributes.RGB_MASK) << 8;
|
|
332
|
+
break;
|
|
333
|
+
case Attributes.CM_DEFAULT:
|
|
334
|
+
default:
|
|
335
|
+
$rgba = this._themeService.colors.background.rgba;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (vertices.attributes.length < offset + 4) {
|
|
340
|
+
vertices.attributes = expandFloat32Array(vertices.attributes, this._terminal.rows * this._terminal.cols * INDICES_PER_RECTANGLE);
|
|
341
|
+
}
|
|
342
|
+
$x1 = startX * this._dimensions.device.cell.width;
|
|
343
|
+
$y1 = y * this._dimensions.device.cell.height;
|
|
344
|
+
$r = (($rgba >> 24) & 0xFF) / 255;
|
|
345
|
+
$g = (($rgba >> 16) & 0xFF) / 255;
|
|
346
|
+
$b = (($rgba >> 8 ) & 0xFF) / 255;
|
|
347
|
+
$a = 1;
|
|
348
|
+
|
|
349
|
+
this._addRectangle(vertices.attributes, offset, $x1, $y1, (endX - startX) * this._dimensions.device.cell.width, this._dimensions.device.cell.height, $r, $g, $b, $a);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private _addRectangle(array: Float32Array, offset: number, x1: number, y1: number, width: number, height: number, r: number, g: number, b: number, a: number): void {
|
|
353
|
+
array[offset ] = x1 / this._dimensions.device.canvas.width;
|
|
354
|
+
array[offset + 1] = y1 / this._dimensions.device.canvas.height;
|
|
355
|
+
array[offset + 2] = width / this._dimensions.device.canvas.width;
|
|
356
|
+
array[offset + 3] = height / this._dimensions.device.canvas.height;
|
|
357
|
+
array[offset + 4] = r;
|
|
358
|
+
array[offset + 5] = g;
|
|
359
|
+
array[offset + 6] = b;
|
|
360
|
+
array[offset + 7] = a;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private _addRectangleFloat(array: Float32Array, offset: number, x1: number, y1: number, width: number, height: number, color: Float32Array): void {
|
|
364
|
+
array[offset ] = x1 / this._dimensions.device.canvas.width;
|
|
365
|
+
array[offset + 1] = y1 / this._dimensions.device.canvas.height;
|
|
366
|
+
array[offset + 2] = width / this._dimensions.device.canvas.width;
|
|
367
|
+
array[offset + 3] = height / this._dimensions.device.canvas.height;
|
|
368
|
+
array[offset + 4] = color[0];
|
|
369
|
+
array[offset + 5] = color[1];
|
|
370
|
+
array[offset + 6] = color[2];
|
|
371
|
+
array[offset + 7] = color[3];
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
private _colorToFloat32Array(color: IColor): Float32Array {
|
|
375
|
+
return new Float32Array([
|
|
376
|
+
((color.rgba >> 24) & 0xFF) / 255,
|
|
377
|
+
((color.rgba >> 16) & 0xFF) / 255,
|
|
378
|
+
((color.rgba >> 8 ) & 0xFF) / 255,
|
|
379
|
+
((color.rgba ) & 0xFF) / 255
|
|
380
|
+
]);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ICursorRenderModel, IRenderModel } from './Types';
|
|
7
|
+
import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
|
8
|
+
import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel';
|
|
9
|
+
|
|
10
|
+
export const RENDER_MODEL_INDICIES_PER_CELL = 4;
|
|
11
|
+
export const RENDER_MODEL_BG_OFFSET = 1;
|
|
12
|
+
export const RENDER_MODEL_FG_OFFSET = 2;
|
|
13
|
+
export const RENDER_MODEL_EXT_OFFSET = 3;
|
|
14
|
+
|
|
15
|
+
export const COMBINED_CHAR_BIT_MASK = 0x80000000;
|
|
16
|
+
|
|
17
|
+
export class RenderModel implements IRenderModel {
|
|
18
|
+
public cells: Uint32Array;
|
|
19
|
+
public lineLengths: Uint32Array;
|
|
20
|
+
public selection: ISelectionRenderModel;
|
|
21
|
+
public cursor?: ICursorRenderModel;
|
|
22
|
+
|
|
23
|
+
constructor() {
|
|
24
|
+
this.cells = new Uint32Array(0);
|
|
25
|
+
this.lineLengths = new Uint32Array(0);
|
|
26
|
+
this.selection = createSelectionRenderModel();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public resize(cols: number, rows: number): void {
|
|
30
|
+
const indexCount = cols * rows * RENDER_MODEL_INDICIES_PER_CELL;
|
|
31
|
+
if (indexCount !== this.cells.length) {
|
|
32
|
+
this.cells = new Uint32Array(indexCount);
|
|
33
|
+
this.lineLengths = new Uint32Array(rows);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public clear(): void {
|
|
38
|
+
this.cells.fill(0, 0);
|
|
39
|
+
this.lineLengths.fill(0, 0);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | Int8Array | Int16Array | Int32Array | Float32Array | Float64Array;
|
|
7
|
+
|
|
8
|
+
export function slice<T extends TypedArray>(array: T, start?: number, end?: number): T {
|
|
9
|
+
// all modern engines that support .slice
|
|
10
|
+
if (array.slice) {
|
|
11
|
+
return array.slice(start, end) as T;
|
|
12
|
+
}
|
|
13
|
+
return sliceFallback(array, start, end);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function sliceFallback<T extends TypedArray>(array: T, start: number = 0, end: number = array.length): T {
|
|
17
|
+
if (start < 0) {
|
|
18
|
+
start = (array.length + start) % array.length;
|
|
19
|
+
}
|
|
20
|
+
if (end >= array.length) {
|
|
21
|
+
end = array.length;
|
|
22
|
+
} else {
|
|
23
|
+
end = (array.length + end) % array.length;
|
|
24
|
+
}
|
|
25
|
+
start = Math.min(start, end);
|
|
26
|
+
|
|
27
|
+
const result: T = new (array.constructor as any)(end - start);
|
|
28
|
+
for (let i = 0; i < end - start; ++i) {
|
|
29
|
+
result[i] = array[i + start];
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
package/src/Types.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
|
7
|
+
import { CursorInactiveStyle, CursorStyle } from 'common/Types';
|
|
8
|
+
|
|
9
|
+
export interface IRenderModel {
|
|
10
|
+
cells: Uint32Array;
|
|
11
|
+
lineLengths: Uint32Array;
|
|
12
|
+
selection: ISelectionRenderModel;
|
|
13
|
+
cursor?: ICursorRenderModel;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ICursorRenderModel {
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
width: number;
|
|
20
|
+
style: CursorStyle | CursorInactiveStyle;
|
|
21
|
+
cursorWidth: number;
|
|
22
|
+
dpr: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface IWebGL2RenderingContext extends WebGLRenderingContext {
|
|
26
|
+
vertexAttribDivisor(index: number, divisor: number): void;
|
|
27
|
+
createVertexArray(): IWebGLVertexArrayObject;
|
|
28
|
+
bindVertexArray(vao: IWebGLVertexArrayObject): void;
|
|
29
|
+
drawElementsInstanced(mode: number, count: number, type: number, offset: number, instanceCount: number): void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface IWebGLVertexArrayObject {
|
|
33
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
|
|
7
|
+
import { ITerminal } from 'browser/Types';
|
|
8
|
+
import { EventEmitter, forwardEvent } from 'common/EventEmitter';
|
|
9
|
+
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
10
|
+
import { getSafariVersion, isSafari } from 'common/Platform';
|
|
11
|
+
import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services';
|
|
12
|
+
import { ITerminalAddon, Terminal } from '@xterm/xterm';
|
|
13
|
+
import { WebglRenderer } from './WebglRenderer';
|
|
14
|
+
import { setTraceLogger } from 'common/services/LogService';
|
|
15
|
+
|
|
16
|
+
export class WebglAddon extends Disposable implements ITerminalAddon {
|
|
17
|
+
private _terminal?: Terminal;
|
|
18
|
+
private _renderer?: WebglRenderer;
|
|
19
|
+
|
|
20
|
+
private readonly _onChangeTextureAtlas = this.register(new EventEmitter<HTMLCanvasElement>());
|
|
21
|
+
public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event;
|
|
22
|
+
private readonly _onAddTextureAtlasCanvas = this.register(new EventEmitter<HTMLCanvasElement>());
|
|
23
|
+
public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event;
|
|
24
|
+
private readonly _onRemoveTextureAtlasCanvas = this.register(new EventEmitter<HTMLCanvasElement>());
|
|
25
|
+
public readonly onRemoveTextureAtlasCanvas = this._onRemoveTextureAtlasCanvas.event;
|
|
26
|
+
private readonly _onContextLoss = this.register(new EventEmitter<void>());
|
|
27
|
+
public readonly onContextLoss = this._onContextLoss.event;
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
private _preserveDrawingBuffer?: boolean
|
|
31
|
+
) {
|
|
32
|
+
if (isSafari && getSafariVersion() < 16) {
|
|
33
|
+
throw new Error('Webgl2 is only supported on Safari 16 and above');
|
|
34
|
+
}
|
|
35
|
+
super();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public activate(terminal: Terminal): void {
|
|
39
|
+
const core = (terminal as any)._core as ITerminal;
|
|
40
|
+
if (!terminal.element) {
|
|
41
|
+
this.register(core.onWillOpen(() => this.activate(terminal)));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this._terminal = terminal;
|
|
46
|
+
const coreService: ICoreService = core.coreService;
|
|
47
|
+
const optionsService: IOptionsService = core.optionsService;
|
|
48
|
+
|
|
49
|
+
const unsafeCore = core as any;
|
|
50
|
+
const renderService: IRenderService = unsafeCore._renderService;
|
|
51
|
+
const characterJoinerService: ICharacterJoinerService = unsafeCore._characterJoinerService;
|
|
52
|
+
const charSizeService: ICharSizeService = unsafeCore._charSizeService;
|
|
53
|
+
const coreBrowserService: ICoreBrowserService = unsafeCore._coreBrowserService;
|
|
54
|
+
const decorationService: IDecorationService = unsafeCore._decorationService;
|
|
55
|
+
const logService: ILogService = unsafeCore._logService;
|
|
56
|
+
const themeService: IThemeService = unsafeCore._themeService;
|
|
57
|
+
|
|
58
|
+
// Set trace logger just in case it hasn't been yet which could happen when the addon is
|
|
59
|
+
// bundled separately to the core module
|
|
60
|
+
setTraceLogger(logService);
|
|
61
|
+
|
|
62
|
+
this._renderer = this.register(new WebglRenderer(
|
|
63
|
+
terminal,
|
|
64
|
+
characterJoinerService,
|
|
65
|
+
charSizeService,
|
|
66
|
+
coreBrowserService,
|
|
67
|
+
coreService,
|
|
68
|
+
decorationService,
|
|
69
|
+
optionsService,
|
|
70
|
+
themeService,
|
|
71
|
+
this._preserveDrawingBuffer
|
|
72
|
+
));
|
|
73
|
+
this.register(forwardEvent(this._renderer.onContextLoss, this._onContextLoss));
|
|
74
|
+
this.register(forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas));
|
|
75
|
+
this.register(forwardEvent(this._renderer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas));
|
|
76
|
+
this.register(forwardEvent(this._renderer.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas));
|
|
77
|
+
renderService.setRenderer(this._renderer);
|
|
78
|
+
|
|
79
|
+
this.register(toDisposable(() => {
|
|
80
|
+
const renderService: IRenderService = (this._terminal as any)._core._renderService;
|
|
81
|
+
renderService.setRenderer((this._terminal as any)._core._createRenderer());
|
|
82
|
+
renderService.handleResize(terminal.cols, terminal.rows);
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public get textureAtlas(): HTMLCanvasElement | undefined {
|
|
87
|
+
return this._renderer?.textureAtlas;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public clearTextureAtlas(): void {
|
|
91
|
+
this._renderer?.clearTextureAtlas();
|
|
92
|
+
}
|
|
93
|
+
}
|