@xterm/addon-webgl 0.20.0-beta.21 → 0.20.0-beta.210
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/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 +5 -2
- package/src/CharAtlasCache.ts +3 -1
- package/src/CursorBlinkStateManager.ts +67 -8
- package/src/DevicePixelObserver.ts +1 -1
- package/src/GlyphRenderer.ts +3 -3
- package/src/RectangleRenderer.ts +1 -1
- package/src/TextureAtlas.ts +76 -55
- package/src/Types.ts +3 -3
- package/src/WebglAddon.ts +8 -14
- package/src/WebglRenderer.ts +62 -10
- package/src/customGlyphs/CustomGlyphDefinitions.ts +75 -30
- package/src/customGlyphs/CustomGlyphRasterizer.ts +33 -13
- package/src/renderLayer/BaseRenderLayer.ts +1 -1
- package/typings/addon-webgl.d.ts +6 -3
package/src/WebglRenderer.ts
CHANGED
|
@@ -13,7 +13,8 @@ import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeS
|
|
|
13
13
|
import { CharData, IBufferLine, ICellData } from 'common/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';
|
|
16
|
+
import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
|
|
17
|
+
import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager';
|
|
17
18
|
import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
|
|
18
19
|
import { Terminal } from '@xterm/xterm';
|
|
19
20
|
import { GlyphRenderer } from './GlyphRenderer';
|
|
@@ -22,14 +23,15 @@ import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_EXT_OFFSET
|
|
|
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
|
|
|
30
31
|
export class WebglRenderer extends Disposable implements IRenderer {
|
|
31
32
|
private _renderLayers: IRenderLayer[];
|
|
32
|
-
private _cursorBlinkStateManager: MutableDisposable<CursorBlinkStateManager> = new MutableDisposable();
|
|
33
|
+
private _cursorBlinkStateManager: MutableDisposable<CursorBlinkStateManager> = this._register(new MutableDisposable());
|
|
34
|
+
private _textBlinkStateManager: TextBlinkStateManager;
|
|
33
35
|
private _charAtlasDisposable = this._register(new MutableDisposable());
|
|
34
36
|
private _charAtlas: ITextureAtlas | undefined;
|
|
35
37
|
private _devicePixelRatio: number;
|
|
@@ -37,8 +39,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
37
39
|
private _observerDisposable = this._register(new MutableDisposable());
|
|
38
40
|
|
|
39
41
|
private _model: RenderModel = new RenderModel();
|
|
42
|
+
private _rowHasBlinkingCells: boolean[] = [];
|
|
43
|
+
private _rowHasBlinkingCellsCount: number = 0;
|
|
40
44
|
private _workCell: ICellData = new CellData();
|
|
41
|
-
private _workCell2: ICellData = new CellData();
|
|
42
45
|
private _cellColorResolver: CellColorResolver;
|
|
43
46
|
|
|
44
47
|
private _canvas: HTMLCanvasElement;
|
|
@@ -105,6 +108,12 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
105
108
|
this._updateDimensions();
|
|
106
109
|
this._updateCursorBlink();
|
|
107
110
|
this._register(_optionsService.onOptionChange(() => this._handleOptionsChanged()));
|
|
111
|
+
this._textBlinkStateManager = this._register(new TextBlinkStateManager(
|
|
112
|
+
() => this._requestRedrawViewport(),
|
|
113
|
+
this._coreBrowserService,
|
|
114
|
+
this._optionsService
|
|
115
|
+
));
|
|
116
|
+
this._resetBlinkingRowState();
|
|
108
117
|
|
|
109
118
|
this._deviceMaxTextureSize = this._gl.getParameter(this._gl.MAX_TEXTURE_SIZE);
|
|
110
119
|
|
|
@@ -136,6 +145,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
136
145
|
this._observerDisposable.value = observeDevicePixelDimensions(this._canvas, w, (w, h) => this._setCanvasDevicePixelDimensions(w, h));
|
|
137
146
|
}));
|
|
138
147
|
|
|
148
|
+
this._register(addDisposableListener(this._coreBrowserService.mainDocument, 'mousedown', () => this._cursorBlinkStateManager.value?.restartBlinkAnimation()));
|
|
149
|
+
|
|
139
150
|
this._core.screenElement!.appendChild(this._canvas);
|
|
140
151
|
|
|
141
152
|
[this._rectangleRenderer.value, this._glyphRenderer.value] = this._initializeWebGLState();
|
|
@@ -176,6 +187,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
176
187
|
this._updateDimensions();
|
|
177
188
|
|
|
178
189
|
this._model.resize(this._terminal.cols, this._terminal.rows);
|
|
190
|
+
this._resetBlinkingRowState();
|
|
179
191
|
|
|
180
192
|
// Resize all render layers
|
|
181
193
|
for (const l of this._renderLayers) {
|
|
@@ -202,6 +214,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
202
214
|
// Force a full refresh. Resizing `_glyphRenderer` should clear it already,
|
|
203
215
|
// so there is no need to clear it again here.
|
|
204
216
|
this._clearModel(false);
|
|
217
|
+
|
|
218
|
+
// Render synchronously to avoid flicker when the canvas is cleared
|
|
219
|
+
this._onRequestRedraw.fire({ start: 0, end: this._terminal.rows - 1, sync: true });
|
|
205
220
|
}
|
|
206
221
|
|
|
207
222
|
public handleCharSizeChanged(): void {
|
|
@@ -226,6 +241,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
226
241
|
this._requestRedrawViewport();
|
|
227
242
|
}
|
|
228
243
|
|
|
244
|
+
public handleViewportVisibilityChange(isVisible: boolean): void {
|
|
245
|
+
this._textBlinkStateManager.setViewportVisible(isVisible);
|
|
246
|
+
}
|
|
247
|
+
|
|
229
248
|
public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
|
|
230
249
|
for (const l of this._renderLayers) {
|
|
231
250
|
l.handleSelectionChanged(this._terminal, start, end, columnSelectMode);
|
|
@@ -285,8 +304,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
285
304
|
if (this._charAtlas !== atlas) {
|
|
286
305
|
this._onChangeTextureAtlas.fire(atlas.pages[0].canvas);
|
|
287
306
|
this._charAtlasDisposable.value = combinedDisposable(
|
|
288
|
-
|
|
289
|
-
|
|
307
|
+
EventUtils.forward(atlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas),
|
|
308
|
+
EventUtils.forward(atlas.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas)
|
|
290
309
|
);
|
|
291
310
|
}
|
|
292
311
|
this._charAtlas = atlas;
|
|
@@ -318,6 +337,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
318
337
|
l.reset(this._terminal);
|
|
319
338
|
}
|
|
320
339
|
|
|
340
|
+
this._resetBlinkingRowState();
|
|
341
|
+
this._textBlinkStateManager.setNeedsBlinkInViewport(false);
|
|
342
|
+
|
|
321
343
|
this._cursorBlinkStateManager.value?.restartBlinkAnimation();
|
|
322
344
|
this._updateCursorBlink();
|
|
323
345
|
}
|
|
@@ -415,6 +437,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
415
437
|
for (y = start; y <= end; y++) {
|
|
416
438
|
row = y + terminal.buffer.ydisp;
|
|
417
439
|
line = terminal.buffer.lines.get(row)!;
|
|
440
|
+
let rowHasBlinkingCells = false;
|
|
418
441
|
this._model.lineLengths[y] = 0;
|
|
419
442
|
isCursorRow = cursorY === row;
|
|
420
443
|
skipJoinedCheckUntilX = 0;
|
|
@@ -472,8 +495,12 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
472
495
|
code = cell.getCode();
|
|
473
496
|
i = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
|
|
474
497
|
|
|
498
|
+
if (!rowHasBlinkingCells && cell.isBlink()) {
|
|
499
|
+
rowHasBlinkingCells = true;
|
|
500
|
+
}
|
|
501
|
+
|
|
475
502
|
// Load colors/resolve overrides into work colors
|
|
476
|
-
this._cellColorResolver.resolve(cell, x, row, this.dimensions.device.cell.width);
|
|
503
|
+
this._cellColorResolver.resolve(cell, x, row, this.dimensions.device.cell.width, this.dimensions.device.cell.height);
|
|
477
504
|
|
|
478
505
|
// Override colors for cursor cell
|
|
479
506
|
if (isCursorVisible && row === cursorY) {
|
|
@@ -501,6 +528,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
501
528
|
}
|
|
502
529
|
}
|
|
503
530
|
|
|
531
|
+
if (this._textBlinkStateManager.isEnabled && !this._textBlinkStateManager.isBlinkOn && cell.isBlink()) {
|
|
532
|
+
this._cellColorResolver.result.fg |= FgFlags.INVISIBLE;
|
|
533
|
+
}
|
|
534
|
+
|
|
504
535
|
if (code !== NULL_CELL_CODE) {
|
|
505
536
|
this._model.lineLengths[y] = x + 1;
|
|
506
537
|
}
|
|
@@ -547,11 +578,31 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
547
578
|
x--; // Go back to the previous update cell for next iteration
|
|
548
579
|
}
|
|
549
580
|
}
|
|
581
|
+
this._setRowBlinkState(y, rowHasBlinkingCells);
|
|
550
582
|
}
|
|
551
583
|
if (modelUpdated) {
|
|
552
584
|
this._rectangleRenderer.value!.updateBackgrounds(this._model);
|
|
553
585
|
}
|
|
554
586
|
this._rectangleRenderer.value!.updateCursor(this._model);
|
|
587
|
+
this._updateTextBlinkState();
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
private _resetBlinkingRowState(): void {
|
|
591
|
+
this._rowHasBlinkingCells = new Array(this._terminal.rows).fill(false);
|
|
592
|
+
this._rowHasBlinkingCellsCount = 0;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
private _setRowBlinkState(row: number, hasBlinkingCells: boolean): void {
|
|
596
|
+
const previous = this._rowHasBlinkingCells[row];
|
|
597
|
+
if (previous === hasBlinkingCells) {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
this._rowHasBlinkingCells[row] = hasBlinkingCells;
|
|
601
|
+
this._rowHasBlinkingCellsCount += hasBlinkingCells ? 1 : -1;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
private _updateTextBlinkState(): void {
|
|
605
|
+
this._textBlinkStateManager.setNeedsBlinkInViewport(this._rowHasBlinkingCellsCount > 0);
|
|
555
606
|
}
|
|
556
607
|
|
|
557
608
|
/**
|
|
@@ -617,7 +668,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|
|
617
668
|
// the change as it's an exact multiple of the cell sizes.
|
|
618
669
|
this._canvas.width = width;
|
|
619
670
|
this._canvas.height = height;
|
|
620
|
-
|
|
671
|
+
// Render synchronously to avoid flicker when the canvas is cleared
|
|
672
|
+
this._onRequestRedraw.fire({ start: 0, end: this._terminal.rows - 1, sync: true });
|
|
621
673
|
}
|
|
622
674
|
|
|
623
675
|
private _requestRedrawViewport(): void {
|
|
@@ -7,6 +7,35 @@ import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType,
|
|
|
7
7
|
|
|
8
8
|
/* eslint-disable max-len */
|
|
9
9
|
|
|
10
|
+
const enum Shapes {
|
|
11
|
+
/** │ */ TOP_TO_BOTTOM = 'M.5,0 L.5,1',
|
|
12
|
+
/** ─ */ LEFT_TO_RIGHT = 'M0,.5 L1,.5',
|
|
13
|
+
|
|
14
|
+
/** └ */ TOP_TO_RIGHT = 'M.5,0 L.5,.5 L1,.5',
|
|
15
|
+
/** ┘ */ TOP_TO_LEFT = 'M.5,0 L.5,.5 L0,.5',
|
|
16
|
+
/** ┐ */ LEFT_TO_BOTTOM = 'M0,.5 L.5,.5 L.5,1',
|
|
17
|
+
/** ┌ */ RIGHT_TO_BOTTOM = 'M0.5,1 L.5,.5 L1,.5',
|
|
18
|
+
|
|
19
|
+
/** ╵ */ MIDDLE_TO_TOP = 'M.5,.5 L.5,0',
|
|
20
|
+
/** ╴ */ MIDDLE_TO_LEFT = 'M.5,.5 L0,.5',
|
|
21
|
+
/** ╶ */ MIDDLE_TO_RIGHT = 'M.5,.5 L1,.5',
|
|
22
|
+
/** ╷ */ MIDDLE_TO_BOTTOM = 'M.5,.5 L.5,1',
|
|
23
|
+
|
|
24
|
+
/** ┴ */ T_TOP = 'M0,.5 L1,.5 M.5,.5 L.5,0',
|
|
25
|
+
/** ┤ */ T_LEFT = 'M.5,0 L.5,1 M.5,.5 L0,.5',
|
|
26
|
+
/** ├ */ T_RIGHT = 'M.5,0 L.5,1 M.5,.5 L1,.5',
|
|
27
|
+
/** ┬ */ T_BOTTOM = 'M0,.5 L1,.5 M.5,.5 L.5,1',
|
|
28
|
+
|
|
29
|
+
/** ┼ */ CROSS = 'M0,.5 L1,.5 M.5,0 L.5,1',
|
|
30
|
+
|
|
31
|
+
/** ╌ */ TWO_DASHES_HORIZONTAL = 'M.1,.5 L.4,.5 M.6,.5 L.9,.5', // .2 empty, .3 filled
|
|
32
|
+
/** ┄ */ THREE_DASHES_HORIZONTAL = 'M.0667,.5 L.2667,.5 M.4,.5 L.6,.5 M.7333,.5 L.9333,.5', // .1333 empty, .2 filled
|
|
33
|
+
/** ┉ */ FOUR_DASHES_HORIZONTAL = 'M.05,.5 L.2,.5 M.3,.5 L.45,.5 M.55,.5 L.7,.5 M.8,.5 L.95,.5', // .1 empty, .15 filled
|
|
34
|
+
/** ╎ */ TWO_DASHES_VERTICAL = 'M.5,.1 L.5,.4 M.5,.6 L.5,.9',
|
|
35
|
+
/** ┆ */ THREE_DASHES_VERTICAL = 'M.5,.0667 L.5,.2667 M.5,.4 L.5,.6 M.5,.7333 L.5,.9333',
|
|
36
|
+
/** ┊ */ FOUR_DASHES_VERTICAL = 'M.5,.05 L.5,.2 M.5,.3 L.5,.45 L.5,.55 M.5,.7 L.5,.95',
|
|
37
|
+
}
|
|
38
|
+
|
|
10
39
|
namespace GitBranchSymbolsParts {
|
|
11
40
|
// Lines
|
|
12
41
|
export const LINE_H: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_RIGHT, strokeWidth: 1 });
|
|
@@ -324,6 +353,29 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi
|
|
|
324
353
|
|
|
325
354
|
// #endregion
|
|
326
355
|
|
|
356
|
+
// #region Progress Indicators (EE00-EE0B)
|
|
357
|
+
|
|
358
|
+
// initially added in Fira Code and later Nerd Fonts
|
|
359
|
+
// https://github.com/ryanoasis/nerd-fonts/pull/1733
|
|
360
|
+
|
|
361
|
+
// Progress bars (EE00-EE05)
|
|
362
|
+
'\u{EE00}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.05 L.1,.05 L.1,.95 L1,.95 L1,1 L0,1 Z' }, // PROGRESS BAR EMPTY START
|
|
363
|
+
'\u{EE01}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.05 L0,.05 Z M0,.95 L1,.95 L1,1 L0,1 Z' }, // PROGRESS BAR EMPTY MIDDLE
|
|
364
|
+
'\u{EE02}': { type: CustomGlyphDefinitionType.PATH, data: 'M1,0 L0,0 L0,.05 L.9,.05 L.9,.95 L0,.95 L0,1 L1,1 Z' }, // PROGRESS BAR EMPTY END
|
|
365
|
+
'\u{EE03}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.05 L.1,.05 L.1,.95 L1,.95 L1,1 L0,1 Z M.25,.15 L1,.15 L1,.85 L.25,.85 Z' }, // PROGRESS BAR FILLED START
|
|
366
|
+
'\u{EE04}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.05 L0,.05 Z M0,.95 L1,.95 L1,1 L0,1 Z M0,.15 L1,.15 L1,.85 L0,.85 Z' }, // PROGRESS BAR FILLED MIDDLE
|
|
367
|
+
'\u{EE05}': { type: CustomGlyphDefinitionType.PATH, data: 'M1,0 L0,0 L0,.05 L.9,.05 L.9,.95 L0,.95 L0,1 L1,1 Z M0,.15 L.75,.15 L.75,.85 L0,.85 Z' }, // PROGRESS BAR FILLED END
|
|
368
|
+
|
|
369
|
+
// Progress spinners (EE06-EE0B) - 6-frame spinner animation using stroked arcs
|
|
370
|
+
'\u{EE06}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.6574,0.303 Q0.623,0.291,0.5852,0.285 T0.5082,0.279 T0.4311,0.285 T0.359,0.303 L0.3082,0.248 Q0.3525,0.232,0.4041,0.2235 T0.5082,0.215 T0.6123,0.2235 T0.7082,0.248 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SPINNER FRAME 1 (12 o'clock)
|
|
371
|
+
'\u{EE07}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.8557,0.582 L0.7656,0.551 Q0.7852,0.53,0.7951,0.507 T0.8049,0.46 Q0.8049,0.424,0.782,0.3905 T0.718,0.332 T0.6221,0.293 T0.5082,0.279 L0.5082,0.215 Q0.5607,0.215,0.6123,0.2235 T0.709,0.248 T0.7918,0.287 T0.8557,0.3375 T0.8959,0.3965 T0.9098,0.46 T0.8959,0.5235 T0.8557,0.582 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SPINNER FRAME 2 (2 o'clock)
|
|
372
|
+
'\u{EE08}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.5082,0.705 Q0.482,0.705,0.4557,0.703 T0.4049,0.697 L0.4311,0.635 Q0.4508,0.638,0.4697,0.6395 T0.5082,0.641 Q0.5672,0.641,0.6221,0.627 T0.718,0.588 T0.782,0.5295 T0.8049,0.46 Q0.8049,0.436,0.7951,0.413 T0.7656,0.369 L0.8557,0.338 Q0.882,0.365,0.8959,0.3965 T0.9098,0.46 T0.8959,0.5235 T0.8557,0.5825 T0.7918,0.633 T0.709,0.672 T0.6123,0.6965 T0.5082,0.705 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SPINNER FRAME 3 (4 o'clock)
|
|
373
|
+
'\u{EE09}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.5082,0.705 Q0.4557,0.705,0.4041,0.6965 T0.3074,0.672 T0.2246,0.633 T0.1607,0.5825 T0.1205,0.5235 T0.1066,0.46 L0.2115,0.46 Q0.2115,0.496,0.2344,0.5295 T0.2984,0.588 T0.3943,0.627 T0.5082,0.641 T0.6221,0.627 T0.718,0.588 T0.782,0.5295 T0.8049,0.46 L0.9098,0.46 Q0.9098,0.492,0.8959,0.5235 T0.8557,0.5825 T0.7918,0.633 T0.709,0.672 T0.6123,0.6965 T0.5082,0.705 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SPINNER FRAME 4 (6 o'clock)
|
|
374
|
+
'\u{EE0A}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.5082,0.705 Q0.4557,0.705,0.4041,0.6965 T0.3074,0.672 T0.2246,0.633 T0.1607,0.5825 T0.1205,0.5235 T0.1066,0.46 T0.1205,0.3965 T0.1607,0.338 L0.2508,0.369 Q0.2311,0.39,0.2213,0.413 T0.2115,0.46 Q0.2115,0.496,0.2344,0.5295 T0.2984,0.588 T0.3943,0.627 T0.5082,0.641 Q0.5279,0.641,0.5467,0.6395 T0.5852,0.635 L0.6115,0.697 Q0.5869,0.701,0.5607,0.703 T0.5082,0.705 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SPINNER FRAME 5 (8 o'clock)
|
|
375
|
+
'\u{EE0B}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.1607,0.582 Q0.1344,0.555,0.1205,0.5235 T0.1066,0.46 T0.1205,0.3965 T0.1607,0.3375 T0.2246,0.287 T0.3074,0.248 T0.4041,0.2235 T0.5082,0.215 L0.5082,0.279 Q0.4492,0.279,0.3943,0.293 T0.2984,0.332 T0.2344,0.3905 T0.2115,0.46 Q0.2115,0.484,0.2213,0.507 T0.2508,0.551 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SPINNER FRAME 6 (10 o'clock)
|
|
376
|
+
|
|
377
|
+
// #endregion
|
|
378
|
+
|
|
327
379
|
// #region Git Branch Symbols (F5D0-F60D)
|
|
328
380
|
|
|
329
381
|
// Initially added in Kitty (https://github.com/kovidgoyal/kitty/pull/7681)
|
|
@@ -621,8 +673,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi
|
|
|
621
673
|
] },
|
|
622
674
|
|
|
623
675
|
// Diagonal fill characters (1FB98-1FB99)
|
|
624
|
-
'\u{1FB98}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: '
|
|
625
|
-
'\u{1FB99}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: '
|
|
676
|
+
'\u{1FB98}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M-0.25,-0.25 L1.25,1.25 M-0.25,0 L1,1.25 M-0.25,0.25 L0.75,1.25 M-0.25,0.5 L0.5,1.25 M0,-0.25 L1.25,1 M0.25,-0.25 L1.25,0.75 M0.5,-0.25 L1.25,0.5 M-0.25,0.75 L0.25,1.25 M0.75,-0.25 L1.25,0.25', strokeWidth: 1 }, // UPPER LEFT TO LOWER RIGHT FILL
|
|
677
|
+
'\u{1FB99}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M-0.25,0.5 L0.5,-0.25 M-0.25,0.75 L0.75,-0.25 M-0.25,1 L1,-0.25 M-0.25,1.25 L1.25,-0.25 M0,1.25 L1.25,0 M0.25,1.25 L1.25,0.25 M0.5,1.25 L1.25,0.5 M-0.25,0.25 L0.25,-0.25 M0.75,1.25 L1.25,0.75', strokeWidth: 1 }, // UPPER RIGHT TO LOWER LEFT FILL
|
|
626
678
|
|
|
627
679
|
// Smooth mosaic terminal graphic characters (1FB9A-1FB9B)
|
|
628
680
|
'\u{1FB9A}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L.5,.5 L0,1 L1,1 L.5,.5 L1,0', type: CustomGlyphVectorType.FILL } }, // UPPER AND LOWER TRIANGULAR HALF BLOCK
|
|
@@ -797,6 +849,27 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi
|
|
|
797
849
|
// #endregion
|
|
798
850
|
};
|
|
799
851
|
|
|
852
|
+
export const blockPatternCodepoints = new Set<number>([
|
|
853
|
+
// Shade characters (2591-2593)
|
|
854
|
+
0x2591,
|
|
855
|
+
0x2592,
|
|
856
|
+
0x2593,
|
|
857
|
+
// Rectangular shade characters (1FB8C-1FB94)
|
|
858
|
+
0x1FB8C,
|
|
859
|
+
0x1FB8D,
|
|
860
|
+
0x1FB8E,
|
|
861
|
+
0x1FB8F,
|
|
862
|
+
0x1FB90,
|
|
863
|
+
0x1FB91,
|
|
864
|
+
0x1FB92,
|
|
865
|
+
0x1FB94,
|
|
866
|
+
// Triangular shade characters (1FB9C-1FB9F)
|
|
867
|
+
0x1FB9C,
|
|
868
|
+
0x1FB9D,
|
|
869
|
+
0x1FB9E,
|
|
870
|
+
0x1FB9F
|
|
871
|
+
]);
|
|
872
|
+
|
|
800
873
|
/**
|
|
801
874
|
* Generates a drawing function for sextant characters. Sextants are a 2x3 grid where each cell
|
|
802
875
|
* can be on or off.
|
|
@@ -944,31 +1017,3 @@ function segmentedDigit(pattern: number): string {
|
|
|
944
1017
|
return paths.join(' ');
|
|
945
1018
|
}
|
|
946
1019
|
|
|
947
|
-
const enum Shapes {
|
|
948
|
-
/** │ */ TOP_TO_BOTTOM = 'M.5,0 L.5,1',
|
|
949
|
-
/** ─ */ LEFT_TO_RIGHT = 'M0,.5 L1,.5',
|
|
950
|
-
|
|
951
|
-
/** └ */ TOP_TO_RIGHT = 'M.5,0 L.5,.5 L1,.5',
|
|
952
|
-
/** ┘ */ TOP_TO_LEFT = 'M.5,0 L.5,.5 L0,.5',
|
|
953
|
-
/** ┐ */ LEFT_TO_BOTTOM = 'M0,.5 L.5,.5 L.5,1',
|
|
954
|
-
/** ┌ */ RIGHT_TO_BOTTOM = 'M0.5,1 L.5,.5 L1,.5',
|
|
955
|
-
|
|
956
|
-
/** ╵ */ MIDDLE_TO_TOP = 'M.5,.5 L.5,0',
|
|
957
|
-
/** ╴ */ MIDDLE_TO_LEFT = 'M.5,.5 L0,.5',
|
|
958
|
-
/** ╶ */ MIDDLE_TO_RIGHT = 'M.5,.5 L1,.5',
|
|
959
|
-
/** ╷ */ MIDDLE_TO_BOTTOM = 'M.5,.5 L.5,1',
|
|
960
|
-
|
|
961
|
-
/** ┴ */ T_TOP = 'M0,.5 L1,.5 M.5,.5 L.5,0',
|
|
962
|
-
/** ┤ */ T_LEFT = 'M.5,0 L.5,1 M.5,.5 L0,.5',
|
|
963
|
-
/** ├ */ T_RIGHT = 'M.5,0 L.5,1 M.5,.5 L1,.5',
|
|
964
|
-
/** ┬ */ T_BOTTOM = 'M0,.5 L1,.5 M.5,.5 L.5,1',
|
|
965
|
-
|
|
966
|
-
/** ┼ */ CROSS = 'M0,.5 L1,.5 M.5,0 L.5,1',
|
|
967
|
-
|
|
968
|
-
/** ╌ */ TWO_DASHES_HORIZONTAL = 'M.1,.5 L.4,.5 M.6,.5 L.9,.5', // .2 empty, .3 filled
|
|
969
|
-
/** ┄ */ THREE_DASHES_HORIZONTAL = 'M.0667,.5 L.2667,.5 M.4,.5 L.6,.5 M.7333,.5 L.9333,.5', // .1333 empty, .2 filled
|
|
970
|
-
/** ┉ */ FOUR_DASHES_HORIZONTAL = 'M.05,.5 L.2,.5 M.3,.5 L.45,.5 M.55,.5 L.7,.5 M.8,.5 L.95,.5', // .1 empty, .15 filled
|
|
971
|
-
/** ╎ */ TWO_DASHES_VERTICAL = 'M.5,.1 L.5,.4 M.5,.6 L.5,.9',
|
|
972
|
-
/** ┆ */ THREE_DASHES_VERTICAL = 'M.5,.0667 L.5,.2667 M.5,.4 L.5,.6 M.5,.7333 L.5,.9333',
|
|
973
|
-
/** ┊ */ FOUR_DASHES_VERTICAL = 'M.5,.05 L.5,.2 M.5,.3 L.5,.45 L.5,.55 M.5,.7 L.5,.95',
|
|
974
|
-
}
|
|
@@ -22,14 +22,15 @@ export function tryDrawCustomGlyph(
|
|
|
22
22
|
deviceCharHeight: number,
|
|
23
23
|
fontSize: number,
|
|
24
24
|
devicePixelRatio: number,
|
|
25
|
-
backgroundColor?: string
|
|
25
|
+
backgroundColor?: string,
|
|
26
|
+
variantOffset: number = 0
|
|
26
27
|
): boolean {
|
|
27
28
|
const unifiedCharDefinition = customGlyphDefinitions[c];
|
|
28
29
|
if (unifiedCharDefinition) {
|
|
29
30
|
// Normalize to array for uniform handling
|
|
30
31
|
const parts = Array.isArray(unifiedCharDefinition) ? unifiedCharDefinition : [unifiedCharDefinition];
|
|
31
32
|
for (const part of parts) {
|
|
32
|
-
drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, backgroundColor);
|
|
33
|
+
drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, backgroundColor, variantOffset);
|
|
33
34
|
}
|
|
34
35
|
return true;
|
|
35
36
|
}
|
|
@@ -48,7 +49,8 @@ function drawDefinitionPart(
|
|
|
48
49
|
deviceCharHeight: number,
|
|
49
50
|
fontSize: number,
|
|
50
51
|
devicePixelRatio: number,
|
|
51
|
-
backgroundColor?: string
|
|
52
|
+
backgroundColor?: string,
|
|
53
|
+
variantOffset: number = 0
|
|
52
54
|
): void {
|
|
53
55
|
// Handle scaleType - adjust dimensions and offset when scaling to character area
|
|
54
56
|
let drawWidth = deviceCellWidth;
|
|
@@ -74,7 +76,7 @@ function drawDefinitionPart(
|
|
|
74
76
|
drawBlockVectorChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
|
|
75
77
|
break;
|
|
76
78
|
case CustomGlyphDefinitionType.BLOCK_PATTERN:
|
|
77
|
-
drawPatternChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
|
|
79
|
+
drawPatternChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, variantOffset);
|
|
78
80
|
break;
|
|
79
81
|
case CustomGlyphDefinitionType.PATH_FUNCTION:
|
|
80
82
|
drawPathFunctionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth);
|
|
@@ -437,7 +439,8 @@ function drawPatternChar(
|
|
|
437
439
|
xOffset: number,
|
|
438
440
|
yOffset: number,
|
|
439
441
|
deviceCellWidth: number,
|
|
440
|
-
deviceCellHeight: number
|
|
442
|
+
deviceCellHeight: number,
|
|
443
|
+
variantOffset: number = 0
|
|
441
444
|
): void {
|
|
442
445
|
let patternSet = cachedPatterns.get(charDefinition);
|
|
443
446
|
if (!patternSet) {
|
|
@@ -486,6 +489,15 @@ function drawPatternChar(
|
|
|
486
489
|
pattern = throwIfFalsy(ctx.createPattern(tmpCanvas, null));
|
|
487
490
|
patternSet.set(fillStyle, pattern);
|
|
488
491
|
}
|
|
492
|
+
// Apply pattern offset to ensure seamless tiling across cells when cell dimensions are odd.
|
|
493
|
+
// variantOffset encodes: bit 1 = x pixel shift, bit 0 = y pixel shift.
|
|
494
|
+
const dx = (variantOffset >> 1) & 1;
|
|
495
|
+
const dy = variantOffset & 1;
|
|
496
|
+
if (dx !== 0 || dy !== 0) {
|
|
497
|
+
pattern.setTransform(new DOMMatrix().translateSelf(-dx, -dy));
|
|
498
|
+
} else {
|
|
499
|
+
pattern.setTransform(new DOMMatrix());
|
|
500
|
+
}
|
|
489
501
|
ctx.fillStyle = pattern;
|
|
490
502
|
ctx.fillRect(xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
491
503
|
}
|
|
@@ -500,6 +512,11 @@ function drawPathFunctionCharacter(
|
|
|
500
512
|
devicePixelRatio: number,
|
|
501
513
|
strokeWidth?: number
|
|
502
514
|
): void {
|
|
515
|
+
ctx.save();
|
|
516
|
+
ctx.beginPath();
|
|
517
|
+
ctx.rect(xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
518
|
+
ctx.clip();
|
|
519
|
+
|
|
503
520
|
ctx.beginPath();
|
|
504
521
|
let actualInstructions: string;
|
|
505
522
|
if (typeof charDefinition === 'function') {
|
|
@@ -526,7 +543,7 @@ function drawPathFunctionCharacter(
|
|
|
526
543
|
if (!args[0] || !args[1]) {
|
|
527
544
|
continue;
|
|
528
545
|
}
|
|
529
|
-
f(ctx, translateArgs(args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, true, devicePixelRatio), state);
|
|
546
|
+
f(ctx, translateArgs(args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, true, devicePixelRatio, 0, 0, false), state);
|
|
530
547
|
state.lastCommand = type;
|
|
531
548
|
}
|
|
532
549
|
if (strokeWidth !== undefined) {
|
|
@@ -537,6 +554,7 @@ function drawPathFunctionCharacter(
|
|
|
537
554
|
ctx.fill();
|
|
538
555
|
}
|
|
539
556
|
ctx.closePath();
|
|
557
|
+
ctx.restore();
|
|
540
558
|
}
|
|
541
559
|
|
|
542
560
|
/**
|
|
@@ -685,7 +703,7 @@ const svgToCanvasInstructionMap: { [index: string]: (ctx: CanvasRenderingContext
|
|
|
685
703
|
}
|
|
686
704
|
};
|
|
687
705
|
|
|
688
|
-
function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, devicePixelRatio: number, leftPadding: number = 0, rightPadding: number = 0): number[] {
|
|
706
|
+
function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, devicePixelRatio: number, leftPadding: number = 0, rightPadding: number = 0, clampToCell: boolean = true): number[] {
|
|
689
707
|
const result = args.map(e => parseFloat(e) || parseInt(e));
|
|
690
708
|
|
|
691
709
|
if (result.length < 2) {
|
|
@@ -695,10 +713,11 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO
|
|
|
695
713
|
for (let x = 0; x < result.length; x += 2) {
|
|
696
714
|
// Translate from 0-1 to 0-cellWidth
|
|
697
715
|
result[x] *= cellWidth - (leftPadding * devicePixelRatio) - (rightPadding * devicePixelRatio);
|
|
698
|
-
//
|
|
699
|
-
//
|
|
716
|
+
// Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally
|
|
717
|
+
// clamp to the cell bounds.
|
|
700
718
|
if (doClamp && result[x] !== 0) {
|
|
701
|
-
|
|
719
|
+
const rounded = Math.round(result[x] + 0.5) - 0.5;
|
|
720
|
+
result[x] = clampToCell ? clamp(rounded, cellWidth, 0) : rounded;
|
|
702
721
|
}
|
|
703
722
|
// Apply the cell's offset (ie. x*cellWidth)
|
|
704
723
|
result[x] += xOffset + (leftPadding * devicePixelRatio);
|
|
@@ -707,10 +726,11 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO
|
|
|
707
726
|
for (let y = 1; y < result.length; y += 2) {
|
|
708
727
|
// Translate from 0-1 to 0-cellHeight
|
|
709
728
|
result[y] *= cellHeight;
|
|
710
|
-
//
|
|
711
|
-
//
|
|
729
|
+
// Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally
|
|
730
|
+
// clamp to the cell bounds.
|
|
712
731
|
if (doClamp && result[y] !== 0) {
|
|
713
|
-
|
|
732
|
+
const rounded = Math.round(result[y] + 0.5) - 0.5;
|
|
733
|
+
result[y] = clampToCell ? clamp(rounded, cellHeight, 0) : rounded;
|
|
714
734
|
}
|
|
715
735
|
// Apply the cell's offset (ie. x*cellHeight)
|
|
716
736
|
result[y] += yOffset;
|
|
@@ -7,7 +7,7 @@ import { ReadonlyColorSet } from 'browser/Types';
|
|
|
7
7
|
import { acquireTextureAtlas } from '../CharAtlasCache';
|
|
8
8
|
import { IRenderDimensions } from 'browser/renderer/shared/Types';
|
|
9
9
|
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
|
10
|
-
import { Disposable, toDisposable } from '
|
|
10
|
+
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
11
11
|
import { CellData } from 'common/buffer/CellData';
|
|
12
12
|
import { IOptionsService } from 'common/services/Services';
|
|
13
13
|
import { Terminal } from '@xterm/xterm';
|
package/typings/addon-webgl.d.ts
CHANGED
|
@@ -57,14 +57,17 @@ declare module '@xterm/addon-webgl' {
|
|
|
57
57
|
* unicode ranges:
|
|
58
58
|
*
|
|
59
59
|
* - Box Drawing (U+2500-U+257F)
|
|
60
|
-
* -
|
|
60
|
+
* - Block Elements (U+2580-U+259F)
|
|
61
61
|
* - Braille Patterns (U+2800-U+28FF)
|
|
62
|
-
* - Powerline Symbols (U+E0A0
|
|
62
|
+
* - Powerline Symbols (U+E0A0-U+E0D4, Private Use Area with widespread
|
|
63
63
|
* adoption)
|
|
64
|
+
* - Progress Indicators (U+EE00-U+EE0B, Private Use Area initially added in
|
|
65
|
+
* [Fira Code](https://github.com/tonsky/FiraCode) and later
|
|
66
|
+
* [Nerd Fonts](https://github.com/ryanoasis/nerd-fonts/pull/1733)).
|
|
64
67
|
* - Git Branch Symbols (U+F5D0-U+F60D, Private Use Area initially adopted
|
|
65
68
|
* in [Kitty in 2024](https://github.com/kovidgoyal/kitty/pull/7681) by
|
|
66
69
|
* author of [vim-flog](https://github.com/rbong/vim-flog))
|
|
67
|
-
* - Symbols for Legacy Computing (U+1FB00
|
|
70
|
+
* - Symbols for Legacy Computing (U+1FB00-U+1FBFF)
|
|
68
71
|
*
|
|
69
72
|
* This will typically result in better rendering with continuous lines,
|
|
70
73
|
* even when line height and letter spacing is used. The default is true.
|