@xterm/addon-webgl 0.20.0-beta.30 → 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.
@@ -4,9 +4,33 @@
4
4
  */
5
5
 
6
6
  import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
7
+ import type { ILogService } from 'common/services/Services';
7
8
  import { customGlyphDefinitions } from './CustomGlyphDefinitions';
8
9
  import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from './Types';
9
10
 
11
+ type PatternCanvas = HTMLCanvasElement | OffscreenCanvas;
12
+ type PatternCanvasFactory = (width: number, height: number) => PatternCanvas;
13
+
14
+ const createOffscreenPatternCanvas: PatternCanvasFactory | undefined = typeof OffscreenCanvas === 'undefined'
15
+ ? undefined
16
+ : (width, height) => new OffscreenCanvas(width, height);
17
+
18
+ const createDomPatternCanvas: PatternCanvasFactory = (width, height) => {
19
+ const canvas = document.createElement('canvas');
20
+ canvas.width = width;
21
+ canvas.height = height;
22
+ return canvas;
23
+ };
24
+
25
+ export function createPatternCanvas(
26
+ width: number,
27
+ height: number,
28
+ offscreenCanvasFactory: PatternCanvasFactory | undefined = createOffscreenPatternCanvas,
29
+ domCanvasFactory: PatternCanvasFactory = createDomPatternCanvas
30
+ ): PatternCanvas {
31
+ return offscreenCanvasFactory?.(width, height) ?? domCanvasFactory(width, height);
32
+ }
33
+
10
34
  /**
11
35
  * Try drawing a custom block element or box drawing character, returning whether it was
12
36
  * successfully drawn.
@@ -22,14 +46,16 @@ export function tryDrawCustomGlyph(
22
46
  deviceCharHeight: number,
23
47
  fontSize: number,
24
48
  devicePixelRatio: number,
25
- backgroundColor?: string
49
+ logService: ILogService,
50
+ backgroundColor?: string,
51
+ variantOffset: number = 0
26
52
  ): boolean {
27
53
  const unifiedCharDefinition = customGlyphDefinitions[c];
28
54
  if (unifiedCharDefinition) {
29
55
  // Normalize to array for uniform handling
30
56
  const parts = Array.isArray(unifiedCharDefinition) ? unifiedCharDefinition : [unifiedCharDefinition];
31
57
  for (const part of parts) {
32
- drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, backgroundColor);
58
+ drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, logService, backgroundColor, variantOffset);
33
59
  }
34
60
  return true;
35
61
  }
@@ -48,7 +74,9 @@ function drawDefinitionPart(
48
74
  deviceCharHeight: number,
49
75
  fontSize: number,
50
76
  devicePixelRatio: number,
51
- backgroundColor?: string
77
+ logService: ILogService,
78
+ backgroundColor?: string,
79
+ variantOffset: number = 0
52
80
  ): void {
53
81
  // Handle scaleType - adjust dimensions and offset when scaling to character area
54
82
  let drawWidth = deviceCellWidth;
@@ -74,10 +102,10 @@ function drawDefinitionPart(
74
102
  drawBlockVectorChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
75
103
  break;
76
104
  case CustomGlyphDefinitionType.BLOCK_PATTERN:
77
- drawPatternChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
105
+ drawPatternChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, variantOffset);
78
106
  break;
79
107
  case CustomGlyphDefinitionType.PATH_FUNCTION:
80
- drawPathFunctionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth);
108
+ drawPathFunctionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, logService, part.strokeWidth);
81
109
  break;
82
110
  case CustomGlyphDefinitionType.PATH:
83
111
  drawPathDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth);
@@ -86,7 +114,7 @@ function drawDefinitionPart(
86
114
  drawPathNegativeDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, backgroundColor);
87
115
  break;
88
116
  case CustomGlyphDefinitionType.VECTOR_SHAPE:
89
- drawVectorShape(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, fontSize, devicePixelRatio);
117
+ drawVectorShape(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, fontSize, devicePixelRatio, logService);
90
118
  break;
91
119
  case CustomGlyphDefinitionType.BRAILLE:
92
120
  drawBrailleCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
@@ -216,8 +244,8 @@ function drawPathDefinitionCharacter(
216
244
  const rx = parseFloat(args[0]) * deviceCellWidth;
217
245
  const ry = parseFloat(args[1]) * deviceCellHeight;
218
246
  const xAxisRotation = parseFloat(args[2]) * Math.PI / 180;
219
- const largeArcFlag = parseInt(args[3]);
220
- const sweepFlag = parseInt(args[4]);
247
+ const largeArcFlag = parseInt(args[3], 10);
248
+ const sweepFlag = parseInt(args[4], 10);
221
249
  const x = xOffset + parseFloat(args[5]) * deviceCellWidth;
222
250
  const y = yOffset + parseFloat(args[6]) * deviceCellHeight;
223
251
  drawSvgArc(ctx, currentX, currentY, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y);
@@ -437,7 +465,8 @@ function drawPatternChar(
437
465
  xOffset: number,
438
466
  yOffset: number,
439
467
  deviceCellWidth: number,
440
- deviceCellHeight: number
468
+ deviceCellHeight: number,
469
+ variantOffset: number = 0
441
470
  ): void {
442
471
  let patternSet = cachedPatterns.get(charDefinition);
443
472
  if (!patternSet) {
@@ -452,9 +481,9 @@ function drawPatternChar(
452
481
  if (!pattern) {
453
482
  const width = charDefinition[0].length;
454
483
  const height = charDefinition.length;
455
- const tmpCanvas = ctx.canvas.ownerDocument.createElement('canvas');
456
- tmpCanvas.width = width;
457
- tmpCanvas.height = height;
484
+ // The atlas canvas can be adopted into another document, so temporary resources must not use
485
+ // its mutable ownerDocument.
486
+ const tmpCanvas = createPatternCanvas(width, height);
458
487
  const tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d'));
459
488
  const imageData = new ImageData(width, height);
460
489
 
@@ -486,6 +515,15 @@ function drawPatternChar(
486
515
  pattern = throwIfFalsy(ctx.createPattern(tmpCanvas, null));
487
516
  patternSet.set(fillStyle, pattern);
488
517
  }
518
+ // Apply pattern offset to ensure seamless tiling across cells when cell dimensions are odd.
519
+ // variantOffset encodes: bit 1 = x pixel shift, bit 0 = y pixel shift.
520
+ const dx = (variantOffset >> 1) & 1;
521
+ const dy = variantOffset & 1;
522
+ if (dx !== 0 || dy !== 0) {
523
+ pattern.setTransform(new DOMMatrix().translateSelf(-dx, -dy));
524
+ } else {
525
+ pattern.setTransform(new DOMMatrix());
526
+ }
489
527
  ctx.fillStyle = pattern;
490
528
  ctx.fillRect(xOffset, yOffset, deviceCellWidth, deviceCellHeight);
491
529
  }
@@ -498,8 +536,14 @@ function drawPathFunctionCharacter(
498
536
  deviceCellWidth: number,
499
537
  deviceCellHeight: number,
500
538
  devicePixelRatio: number,
539
+ logService: ILogService,
501
540
  strokeWidth?: number
502
541
  ): void {
542
+ ctx.save();
543
+ ctx.beginPath();
544
+ ctx.rect(xOffset, yOffset, deviceCellWidth, deviceCellHeight);
545
+ ctx.clip();
546
+
503
547
  ctx.beginPath();
504
548
  let actualInstructions: string;
505
549
  if (typeof charDefinition === 'function') {
@@ -519,14 +563,14 @@ function drawPathFunctionCharacter(
519
563
  }
520
564
  const f = svgToCanvasInstructionMap[type];
521
565
  if (!f) {
522
- console.error(`Could not find drawing instructions for "${type}"`);
566
+ logService.error(`Could not find drawing instructions for "${type}"`);
523
567
  continue;
524
568
  }
525
569
  const args: string[] = instruction.substring(1).split(',');
526
570
  if (!args[0] || !args[1]) {
527
571
  continue;
528
572
  }
529
- f(ctx, translateArgs(args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, true, devicePixelRatio), state);
573
+ f(ctx, translateArgs(args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, true, devicePixelRatio, 0, 0, false), state);
530
574
  state.lastCommand = type;
531
575
  }
532
576
  if (strokeWidth !== undefined) {
@@ -537,6 +581,7 @@ function drawPathFunctionCharacter(
537
581
  ctx.fill();
538
582
  }
539
583
  ctx.closePath();
584
+ ctx.restore();
540
585
  }
541
586
 
542
587
  /**
@@ -580,7 +625,8 @@ function drawVectorShape(
580
625
  deviceCellWidth: number,
581
626
  deviceCellHeight: number,
582
627
  fontSize: number,
583
- devicePixelRatio: number
628
+ devicePixelRatio: number,
629
+ logService: ILogService
584
630
  ): void {
585
631
  // Clip the cell to make sure drawing doesn't occur beyond bounds
586
632
  const clipRegion = new Path2D();
@@ -601,7 +647,7 @@ function drawVectorShape(
601
647
  }
602
648
  const f = svgToCanvasInstructionMap[type];
603
649
  if (!f) {
604
- console.error(`Could not find drawing instructions for "${type}"`);
650
+ logService.error(`Could not find drawing instructions for "${type}"`);
605
651
  continue;
606
652
  }
607
653
  const args: string[] = instruction.substring(1).split(',');
@@ -685,7 +731,7 @@ const svgToCanvasInstructionMap: { [index: string]: (ctx: CanvasRenderingContext
685
731
  }
686
732
  };
687
733
 
688
- function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, devicePixelRatio: number, leftPadding: number = 0, rightPadding: number = 0): number[] {
734
+ 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
735
  const result = args.map(e => parseFloat(e) || parseInt(e));
690
736
 
691
737
  if (result.length < 2) {
@@ -695,10 +741,11 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO
695
741
  for (let x = 0; x < result.length; x += 2) {
696
742
  // Translate from 0-1 to 0-cellWidth
697
743
  result[x] *= cellWidth - (leftPadding * devicePixelRatio) - (rightPadding * devicePixelRatio);
698
- // Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp
699
- // line at 100% devicePixelRatio
744
+ // Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally
745
+ // clamp to the cell bounds.
700
746
  if (doClamp && result[x] !== 0) {
701
- result[x] = clamp(Math.round(result[x] + 0.5) - 0.5, cellWidth, 0);
747
+ const rounded = Math.round(result[x] + 0.5) - 0.5;
748
+ result[x] = clampToCell ? clamp(rounded, cellWidth, 0) : rounded;
702
749
  }
703
750
  // Apply the cell's offset (ie. x*cellWidth)
704
751
  result[x] += xOffset + (leftPadding * devicePixelRatio);
@@ -707,10 +754,11 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO
707
754
  for (let y = 1; y < result.length; y += 2) {
708
755
  // Translate from 0-1 to 0-cellHeight
709
756
  result[y] *= cellHeight;
710
- // Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp
711
- // line at 100% devicePixelRatio
757
+ // Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally
758
+ // clamp to the cell bounds.
712
759
  if (doClamp && result[y] !== 0) {
713
- result[y] = clamp(Math.round(result[y] + 0.5) - 0.5, cellHeight, 0);
760
+ const rounded = Math.round(result[y] + 0.5) - 0.5;
761
+ result[y] = clampToCell ? clamp(rounded, cellHeight, 0) : rounded;
714
762
  }
715
763
  // Apply the cell's offset (ie. x*cellHeight)
716
764
  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 'vs/base/common/lifecycle';
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';
@@ -23,12 +23,12 @@ declare module '@xterm/addon-webgl' {
23
23
  public readonly onChangeTextureAtlas: IEvent<HTMLCanvasElement>;
24
24
 
25
25
  /**
26
- * An event that is fired when the a new page is added to the texture atlas.
26
+ * An event that is fired when a new page is added to the texture atlas.
27
27
  */
28
28
  public readonly onAddTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
29
29
 
30
30
  /**
31
- * An event that is fired when the a page is removed from the texture atlas.
31
+ * An event that is fired when a page is removed from the texture atlas.
32
32
  */
33
33
  public readonly onRemoveTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
34
34
 
@@ -61,6 +61,9 @@ declare module '@xterm/addon-webgl' {
61
61
  * - Braille Patterns (U+2800-U+28FF)
62
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))