@xterm/addon-webgl 0.20.0-beta.3 → 0.20.0-beta.300
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/README.md +1 -1
- package/lib/addon-webgl.js +1 -1
- package/lib/addon-webgl.js.map +1 -1
- package/lib/addon-webgl.mjs +5 -31
- package/lib/addon-webgl.mjs.map +4 -4
- package/package.json +4 -4
- package/src/CellColorResolver.ts +6 -3
- package/src/CharAtlasCache.ts +6 -3
- package/src/CharAtlasUtils.ts +5 -2
- package/src/CursorBlinkStateManager.ts +67 -8
- package/src/DevicePixelObserver.ts +1 -1
- package/src/GlyphRenderer.ts +60 -29
- package/src/RectangleRenderer.ts +15 -11
- package/src/RenderModel.ts +7 -5
- package/src/TextureAtlas.ts +148 -80
- package/src/Types.ts +9 -6
- package/src/WebglAddon.ts +17 -17
- package/src/WebglRenderer.ts +126 -37
- package/src/WebglUtils.ts +7 -6
- package/src/customGlyphs/CustomGlyphDefinitions.ts +1019 -0
- package/src/customGlyphs/CustomGlyphRasterizer.ts +768 -0
- package/src/customGlyphs/Types.ts +85 -0
- package/src/renderLayer/BaseRenderLayer.ts +1 -1
- package/typings/addon-webgl.d.ts +33 -3
- package/src/CustomGlyphs.ts +0 -914
package/src/WebglRenderer.ts
CHANGED
|
@@ -10,26 +10,32 @@ import { CursorBlinkStateManager } from './CursorBlinkStateManager';
|
|
|
10
10
|
import { observeDevicePixelDimensions } from './DevicePixelObserver';
|
|
11
11
|
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
|
|
12
12
|
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
|
13
|
-
import { CharData, IBufferLine, ICellData } from 'common/Types';
|
|
13
|
+
import { CharData, IBufferLine, ICellData } from 'common/buffer/Types';
|
|
14
14
|
import { AttributeData } from 'common/buffer/AttributeData';
|
|
15
15
|
import { CellData } from 'common/buffer/CellData';
|
|
16
|
-
import { Attributes, Content, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
|
|
17
|
-
import {
|
|
16
|
+
import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
|
|
17
|
+
import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager';
|
|
18
|
+
import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services';
|
|
18
19
|
import { Terminal } from '@xterm/xterm';
|
|
19
20
|
import { GlyphRenderer } from './GlyphRenderer';
|
|
20
21
|
import { RectangleRenderer } from './RectangleRenderer';
|
|
21
|
-
import { COMBINED_CHAR_BIT_MASK,
|
|
22
|
+
import { COMBINED_CHAR_BIT_MASK, RenderModel, RenderModelConstants } from './RenderModel';
|
|
22
23
|
import { IWebGL2RenderingContext, type ITextureAtlas } from './Types';
|
|
23
24
|
import { LinkRenderLayer } from './renderLayer/LinkRenderLayer';
|
|
24
25
|
import { IRenderLayer } from './renderLayer/Types';
|
|
25
|
-
import { Emitter,
|
|
26
|
-
import { addDisposableListener } from '
|
|
27
|
-
import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from '
|
|
26
|
+
import { Emitter, EventUtils } from 'common/Event';
|
|
27
|
+
import { addDisposableListener } from 'browser/Dom';
|
|
28
|
+
import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle';
|
|
28
29
|
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
|
|
29
30
|
|
|
31
|
+
const enum Constants {
|
|
32
|
+
MERGE_RETRY_LIMIT = 32
|
|
33
|
+
}
|
|
34
|
+
|
|
30
35
|
export class WebglRenderer extends Disposable implements IRenderer {
|
|
31
36
|
private _renderLayers: IRenderLayer[];
|
|
32
|
-
private _cursorBlinkStateManager: MutableDisposable<CursorBlinkStateManager> = new MutableDisposable();
|
|
37
|
+
private _cursorBlinkStateManager: MutableDisposable<CursorBlinkStateManager> = this._register(new MutableDisposable());
|
|
38
|
+
private _textBlinkStateManager: TextBlinkStateManager;
|
|
33
39
|
private _charAtlasDisposable = this._register(new MutableDisposable());
|
|
34
40
|
private _charAtlas: ITextureAtlas | undefined;
|
|
35
41
|
private _devicePixelRatio: number;
|
|
@@ -37,8 +43,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
37
43
|
private _observerDisposable = this._register(new MutableDisposable());
|
|
38
44
|
|
|
39
45
|
private _model: RenderModel = new RenderModel();
|
|
46
|
+
private _rowHasBlinkingCells: boolean[] = [];
|
|
47
|
+
private _rowHasBlinkingCellsCount: number = 0;
|
|
40
48
|
private _workCell: ICellData = new CellData();
|
|
41
|
-
private _workCell2: ICellData = new CellData();
|
|
42
49
|
private _cellColorResolver: CellColorResolver;
|
|
43
50
|
|
|
44
51
|
private _canvas: HTMLCanvasElement;
|
|
@@ -70,8 +77,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
70
77
|
private readonly _coreBrowserService: ICoreBrowserService,
|
|
71
78
|
private readonly _coreService: ICoreService,
|
|
72
79
|
private readonly _decorationService: IDecorationService,
|
|
80
|
+
private readonly _logService: ILogService,
|
|
73
81
|
private readonly _optionsService: IOptionsService,
|
|
74
82
|
private readonly _themeService: IThemeService,
|
|
83
|
+
private readonly _customGlyphs: boolean = true,
|
|
75
84
|
preserveDrawingBuffer?: boolean
|
|
76
85
|
) {
|
|
77
86
|
super();
|
|
@@ -104,23 +113,29 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
104
113
|
this._updateDimensions();
|
|
105
114
|
this._updateCursorBlink();
|
|
106
115
|
this._register(_optionsService.onOptionChange(() => this._handleOptionsChanged()));
|
|
116
|
+
this._textBlinkStateManager = this._register(new TextBlinkStateManager(
|
|
117
|
+
() => this._requestRedrawViewport(),
|
|
118
|
+
this._coreBrowserService,
|
|
119
|
+
this._optionsService
|
|
120
|
+
));
|
|
121
|
+
this._resetBlinkingRowState();
|
|
107
122
|
|
|
108
123
|
this._deviceMaxTextureSize = this._gl.getParameter(this._gl.MAX_TEXTURE_SIZE);
|
|
109
124
|
|
|
110
125
|
this._register(addDisposableListener(this._canvas, 'webglcontextlost', (e) => {
|
|
111
|
-
|
|
126
|
+
this._logService.debug('webglcontextlost event received');
|
|
112
127
|
// Prevent the default behavior in order to enable WebGL context restoration.
|
|
113
128
|
e.preventDefault();
|
|
114
129
|
// Wait a few seconds to see if the 'webglcontextrestored' event is fired.
|
|
115
130
|
// If not, dispatch the onContextLoss notification to observers.
|
|
116
131
|
this._contextRestorationTimeout = setTimeout(() => {
|
|
117
132
|
this._contextRestorationTimeout = undefined;
|
|
118
|
-
|
|
133
|
+
this._logService.warn('webgl context not restored; firing onContextLoss');
|
|
119
134
|
this._onContextLoss.fire(e);
|
|
120
135
|
}, 3000 /* ms */);
|
|
121
136
|
}));
|
|
122
137
|
this._register(addDisposableListener(this._canvas, 'webglcontextrestored', (e) => {
|
|
123
|
-
|
|
138
|
+
this._logService.warn('webglcontextrestored event received');
|
|
124
139
|
clearTimeout(this._contextRestorationTimeout);
|
|
125
140
|
this._contextRestorationTimeout = undefined;
|
|
126
141
|
// The texture atlas and glyph renderer must be fully reinitialized
|
|
@@ -135,6 +150,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
135
150
|
this._observerDisposable.value = observeDevicePixelDimensions(this._canvas, w, (w, h) => this._setCanvasDevicePixelDimensions(w, h));
|
|
136
151
|
}));
|
|
137
152
|
|
|
153
|
+
this._register(addDisposableListener(this._coreBrowserService.mainDocument, 'mousedown', () => this._cursorBlinkStateManager.value?.restartBlinkAnimation()));
|
|
154
|
+
|
|
138
155
|
this._core.screenElement!.appendChild(this._canvas);
|
|
139
156
|
|
|
140
157
|
[this._rectangleRenderer.value, this._glyphRenderer.value] = this._initializeWebGLState();
|
|
@@ -142,6 +159,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
142
159
|
this._isAttached = this._core.screenElement!.isConnected;
|
|
143
160
|
|
|
144
161
|
this._register(toDisposable(() => {
|
|
162
|
+
clearTimeout(this._contextRestorationTimeout);
|
|
163
|
+
this._contextRestorationTimeout = undefined;
|
|
145
164
|
for (const l of this._renderLayers) {
|
|
146
165
|
l.dispose();
|
|
147
166
|
}
|
|
@@ -175,6 +194,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
175
194
|
this._updateDimensions();
|
|
176
195
|
|
|
177
196
|
this._model.resize(this._terminal.cols, this._terminal.rows);
|
|
197
|
+
this._resetBlinkingRowState();
|
|
178
198
|
|
|
179
199
|
// Resize all render layers
|
|
180
200
|
for (const l of this._renderLayers) {
|
|
@@ -201,6 +221,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
201
221
|
// Force a full refresh. Resizing `_glyphRenderer` should clear it already,
|
|
202
222
|
// so there is no need to clear it again here.
|
|
203
223
|
this._clearModel(false);
|
|
224
|
+
|
|
225
|
+
// Render synchronously to avoid flicker when the canvas is cleared
|
|
226
|
+
this._onRequestRedraw.fire({ start: 0, end: this._terminal.rows - 1, sync: true });
|
|
204
227
|
}
|
|
205
228
|
|
|
206
229
|
public handleCharSizeChanged(): void {
|
|
@@ -225,6 +248,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
225
248
|
this._requestRedrawViewport();
|
|
226
249
|
}
|
|
227
250
|
|
|
251
|
+
public handleViewportVisibilityChange(isVisible: boolean): void {
|
|
252
|
+
this._textBlinkStateManager.setViewportVisible(isVisible);
|
|
253
|
+
}
|
|
254
|
+
|
|
228
255
|
public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
|
|
229
256
|
for (const l of this._renderLayers) {
|
|
230
257
|
l.handleSelectionChanged(this._terminal, start, end, columnSelectMode);
|
|
@@ -250,8 +277,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
250
277
|
* Initializes members dependent on WebGL context state.
|
|
251
278
|
*/
|
|
252
279
|
private _initializeWebGLState(): [RectangleRenderer, GlyphRenderer] {
|
|
253
|
-
this._rectangleRenderer.value = new RectangleRenderer(this._terminal, this._gl, this.dimensions, this._themeService);
|
|
254
|
-
this._glyphRenderer.value = new GlyphRenderer(this._terminal, this._gl, this.dimensions, this._optionsService);
|
|
280
|
+
this._rectangleRenderer.value = new RectangleRenderer(this._terminal, this._gl, this.dimensions, this._themeService, this._logService);
|
|
281
|
+
this._glyphRenderer.value = new GlyphRenderer(this._terminal, this._gl, this.dimensions, this._optionsService, this._logService);
|
|
255
282
|
|
|
256
283
|
// Update dimensions and acquire char atlas
|
|
257
284
|
this.handleCharSizeChanged();
|
|
@@ -278,13 +305,14 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
278
305
|
this.dimensions.device.char.width,
|
|
279
306
|
this.dimensions.device.char.height,
|
|
280
307
|
this._coreBrowserService.dpr,
|
|
281
|
-
this._deviceMaxTextureSize
|
|
308
|
+
this._deviceMaxTextureSize,
|
|
309
|
+
this._customGlyphs
|
|
282
310
|
);
|
|
283
311
|
if (this._charAtlas !== atlas) {
|
|
284
312
|
this._onChangeTextureAtlas.fire(atlas.pages[0].canvas);
|
|
285
313
|
this._charAtlasDisposable.value = combinedDisposable(
|
|
286
|
-
|
|
287
|
-
|
|
314
|
+
EventUtils.forward(atlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas),
|
|
315
|
+
EventUtils.forward(atlas.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas)
|
|
288
316
|
);
|
|
289
317
|
}
|
|
290
318
|
this._charAtlas = atlas;
|
|
@@ -316,6 +344,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
316
344
|
l.reset(this._terminal);
|
|
317
345
|
}
|
|
318
346
|
|
|
347
|
+
this._resetBlinkingRowState();
|
|
348
|
+
this._textBlinkStateManager.setNeedsBlinkInViewport(false);
|
|
349
|
+
|
|
319
350
|
this._cursorBlinkStateManager.value?.restartBlinkAnimation();
|
|
320
351
|
this._updateCursorBlink();
|
|
321
352
|
}
|
|
@@ -351,6 +382,19 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
351
382
|
this._updateModel(start, end);
|
|
352
383
|
}
|
|
353
384
|
|
|
385
|
+
// A mid-update atlas page merge invalidates vertex data and may not bump the host
|
|
386
|
+
// page's version, so re-run the update and force a full texture rebind.
|
|
387
|
+
let merged = false;
|
|
388
|
+
let mergeRetries = 0;
|
|
389
|
+
while (this._charAtlas && this._glyphRenderer.value.beginFrame() && mergeRetries++ < Constants.MERGE_RETRY_LIMIT) {
|
|
390
|
+
merged = true;
|
|
391
|
+
this._clearModel(true);
|
|
392
|
+
this._updateModel(0, this._terminal.rows - 1);
|
|
393
|
+
}
|
|
394
|
+
if (merged) {
|
|
395
|
+
this._glyphRenderer.value.invalidateAtlasTextures();
|
|
396
|
+
}
|
|
397
|
+
|
|
354
398
|
// Render
|
|
355
399
|
this._rectangleRenderer.value.renderBackgrounds();
|
|
356
400
|
this._glyphRenderer.value.render(this._model);
|
|
@@ -383,8 +427,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
383
427
|
let line: IBufferLine;
|
|
384
428
|
let joinedRanges: [number, number][];
|
|
385
429
|
let isJoined: boolean;
|
|
386
|
-
let skipJoinedCheckUntilX: number
|
|
387
|
-
let isValidJoinRange: boolean
|
|
430
|
+
let skipJoinedCheckUntilX: number;
|
|
431
|
+
let isValidJoinRange: boolean;
|
|
388
432
|
let lastCharX: number;
|
|
389
433
|
let range: [number, number];
|
|
390
434
|
let isCursorRow: boolean;
|
|
@@ -412,7 +456,19 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
412
456
|
|
|
413
457
|
for (y = start; y <= end; y++) {
|
|
414
458
|
row = y + terminal.buffer.ydisp;
|
|
415
|
-
|
|
459
|
+
const bufferLine = terminal.buffer.lines.get(row);
|
|
460
|
+
if (!bufferLine) {
|
|
461
|
+
this._model.lineLengths[y] = 0;
|
|
462
|
+
for (x = 0; x < terminal.cols; x++) {
|
|
463
|
+
j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL;
|
|
464
|
+
modelUpdated = true;
|
|
465
|
+
this._nullModelCell(x, y, j, 0, 0, 0);
|
|
466
|
+
}
|
|
467
|
+
this._setRowBlinkState(y, false);
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
line = bufferLine;
|
|
471
|
+
let rowHasBlinkingCells = false;
|
|
416
472
|
this._model.lineLengths[y] = 0;
|
|
417
473
|
isCursorRow = cursorY === row;
|
|
418
474
|
skipJoinedCheckUntilX = 0;
|
|
@@ -468,10 +524,14 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
468
524
|
|
|
469
525
|
chars = cell.getChars();
|
|
470
526
|
code = cell.getCode();
|
|
471
|
-
i = ((y * terminal.cols) + x) *
|
|
527
|
+
i = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL;
|
|
528
|
+
|
|
529
|
+
if (!rowHasBlinkingCells && cell.isBlink()) {
|
|
530
|
+
rowHasBlinkingCells = true;
|
|
531
|
+
}
|
|
472
532
|
|
|
473
533
|
// Load colors/resolve overrides into work colors
|
|
474
|
-
this._cellColorResolver.resolve(cell, x, row, this.dimensions.device.cell.width);
|
|
534
|
+
this._cellColorResolver.resolve(cell, x, row, this.dimensions.device.cell.width, this.dimensions.device.cell.height);
|
|
475
535
|
|
|
476
536
|
// Override colors for cursor cell
|
|
477
537
|
if (isCursorVisible && row === cursorY) {
|
|
@@ -499,15 +559,19 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
499
559
|
}
|
|
500
560
|
}
|
|
501
561
|
|
|
562
|
+
if (this._textBlinkStateManager.isEnabled && !this._textBlinkStateManager.isBlinkOn && cell.isBlink()) {
|
|
563
|
+
this._cellColorResolver.result.fg |= FgFlags.INVISIBLE;
|
|
564
|
+
}
|
|
565
|
+
|
|
502
566
|
if (code !== NULL_CELL_CODE) {
|
|
503
567
|
this._model.lineLengths[y] = x + 1;
|
|
504
568
|
}
|
|
505
569
|
|
|
506
570
|
// Nothing has changed, no updates needed
|
|
507
571
|
if (this._model.cells[i] === code &&
|
|
508
|
-
this._model.cells[i +
|
|
509
|
-
this._model.cells[i +
|
|
510
|
-
this._model.cells[i +
|
|
572
|
+
this._model.cells[i + RenderModelConstants.BG_OFFSET] === this._cellColorResolver.result.bg &&
|
|
573
|
+
this._model.cells[i + RenderModelConstants.FG_OFFSET] === this._cellColorResolver.result.fg &&
|
|
574
|
+
this._model.cells[i + RenderModelConstants.EXT_OFFSET] === this._cellColorResolver.result.ext) {
|
|
511
575
|
continue;
|
|
512
576
|
}
|
|
513
577
|
|
|
@@ -520,9 +584,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
520
584
|
|
|
521
585
|
// Cache the results in the model
|
|
522
586
|
this._model.cells[i] = code;
|
|
523
|
-
this._model.cells[i +
|
|
524
|
-
this._model.cells[i +
|
|
525
|
-
this._model.cells[i +
|
|
587
|
+
this._model.cells[i + RenderModelConstants.BG_OFFSET] = this._cellColorResolver.result.bg;
|
|
588
|
+
this._model.cells[i + RenderModelConstants.FG_OFFSET] = this._cellColorResolver.result.fg;
|
|
589
|
+
this._model.cells[i + RenderModelConstants.EXT_OFFSET] = this._cellColorResolver.result.ext;
|
|
526
590
|
|
|
527
591
|
width = cell.getWidth();
|
|
528
592
|
this._glyphRenderer.value!.updateCell(x, y, code, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, chars, width, lastBg);
|
|
@@ -533,23 +597,47 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
533
597
|
|
|
534
598
|
// Null out non-first cells
|
|
535
599
|
for (x++; x <= lastCharX; x++) {
|
|
536
|
-
j = ((y * terminal.cols) + x) *
|
|
537
|
-
this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0, 0);
|
|
538
|
-
this._model.cells[j] = NULL_CELL_CODE;
|
|
600
|
+
j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL;
|
|
539
601
|
// Don't re-resolve the cell color since multi-colored ligature backgrounds are not
|
|
540
602
|
// supported
|
|
541
|
-
this.
|
|
542
|
-
this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg;
|
|
543
|
-
this._model.cells[j + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext;
|
|
603
|
+
this._nullModelCell(x, y, j, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext);
|
|
544
604
|
}
|
|
545
605
|
x--; // Go back to the previous update cell for next iteration
|
|
546
606
|
}
|
|
547
607
|
}
|
|
608
|
+
this._setRowBlinkState(y, rowHasBlinkingCells);
|
|
548
609
|
}
|
|
549
610
|
if (modelUpdated) {
|
|
550
611
|
this._rectangleRenderer.value!.updateBackgrounds(this._model);
|
|
551
612
|
}
|
|
552
613
|
this._rectangleRenderer.value!.updateCursor(this._model);
|
|
614
|
+
this._updateTextBlinkState();
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
private _nullModelCell(x: number, y: number, cellIndex: number, bg: number, fg: number, ext: number): void {
|
|
618
|
+
this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, bg, fg, ext, NULL_CELL_CHAR, 0, 0);
|
|
619
|
+
this._model.cells[cellIndex] = NULL_CELL_CODE;
|
|
620
|
+
this._model.cells[cellIndex + RenderModelConstants.BG_OFFSET] = bg;
|
|
621
|
+
this._model.cells[cellIndex + RenderModelConstants.FG_OFFSET] = fg;
|
|
622
|
+
this._model.cells[cellIndex + RenderModelConstants.EXT_OFFSET] = ext;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
private _resetBlinkingRowState(): void {
|
|
626
|
+
this._rowHasBlinkingCells = new Array(this._terminal.rows).fill(false);
|
|
627
|
+
this._rowHasBlinkingCellsCount = 0;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
private _setRowBlinkState(row: number, hasBlinkingCells: boolean): void {
|
|
631
|
+
const previous = this._rowHasBlinkingCells[row];
|
|
632
|
+
if (previous === hasBlinkingCells) {
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
this._rowHasBlinkingCells[row] = hasBlinkingCells;
|
|
636
|
+
this._rowHasBlinkingCellsCount += hasBlinkingCells ? 1 : -1;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
private _updateTextBlinkState(): void {
|
|
640
|
+
this._textBlinkStateManager.setNeedsBlinkInViewport(this._rowHasBlinkingCellsCount > 0);
|
|
553
641
|
}
|
|
554
642
|
|
|
555
643
|
/**
|
|
@@ -591,10 +679,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
591
679
|
this.dimensions.device.canvas.height = this._terminal.rows * this.dimensions.device.cell.height;
|
|
592
680
|
this.dimensions.device.canvas.width = this._terminal.cols * this.dimensions.device.cell.width;
|
|
593
681
|
|
|
594
|
-
// The
|
|
682
|
+
// The size of the canvas on the page. It's important that this rounds to nearest integer
|
|
595
683
|
// and not ceils as browsers often have floating point precision issues where
|
|
596
684
|
// `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when
|
|
597
|
-
// it's actually 1.1. Ceiling may
|
|
685
|
+
// it's actually 1.1. Ceiling may cause blurriness as the backing canvas image is 1 pixel too
|
|
598
686
|
// large for the canvas element size.
|
|
599
687
|
this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / this._devicePixelRatio);
|
|
600
688
|
this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / this._devicePixelRatio);
|
|
@@ -615,7 +703,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
615
703
|
// the change as it's an exact multiple of the cell sizes.
|
|
616
704
|
this._canvas.width = width;
|
|
617
705
|
this._canvas.height = height;
|
|
618
|
-
|
|
706
|
+
// Render synchronously to avoid flicker when the canvas is cleared
|
|
707
|
+
this._onRequestRedraw.fire({ start: 0, end: this._terminal.rows - 1, sync: true });
|
|
619
708
|
}
|
|
620
709
|
|
|
621
710
|
private _requestRedrawViewport(): void {
|
package/src/WebglUtils.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
|
|
7
|
+
import type { ILogService } from 'common/services/Services';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* A matrix that when multiplies will translate 0-1 coordinates (left to right,
|
|
@@ -16,21 +17,21 @@ export const PROJECTION_MATRIX = new Float32Array([
|
|
|
16
17
|
-1, 1, 0, 1
|
|
17
18
|
]);
|
|
18
19
|
|
|
19
|
-
export function createProgram(gl: WebGLRenderingContext, vertexSource: string, fragmentSource: string): WebGLProgram | undefined {
|
|
20
|
+
export function createProgram(gl: WebGLRenderingContext, vertexSource: string, fragmentSource: string, logService: ILogService): WebGLProgram | undefined {
|
|
20
21
|
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)));
|
|
22
|
+
gl.attachShader(program, throwIfFalsy(createShader(gl, gl.VERTEX_SHADER, vertexSource, logService)));
|
|
23
|
+
gl.attachShader(program, throwIfFalsy(createShader(gl, gl.FRAGMENT_SHADER, fragmentSource, logService)));
|
|
23
24
|
gl.linkProgram(program);
|
|
24
25
|
const success = gl.getProgramParameter(program, gl.LINK_STATUS);
|
|
25
26
|
if (success) {
|
|
26
27
|
return program;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
logService.error(gl.getProgramInfoLog(program));
|
|
30
31
|
gl.deleteProgram(program);
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
export function createShader(gl: WebGLRenderingContext, type: number, source: string): WebGLShader | undefined {
|
|
34
|
+
export function createShader(gl: WebGLRenderingContext, type: number, source: string, logService: ILogService): WebGLShader | undefined {
|
|
34
35
|
const shader = throwIfFalsy(gl.createShader(type));
|
|
35
36
|
gl.shaderSource(shader, source);
|
|
36
37
|
gl.compileShader(shader);
|
|
@@ -39,7 +40,7 @@ export function createShader(gl: WebGLRenderingContext, type: number, source: st
|
|
|
39
40
|
return shader;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
logService.error(gl.getShaderInfoLog(shader));
|
|
43
44
|
gl.deleteShader(shader);
|
|
44
45
|
}
|
|
45
46
|
|