@xterm/addon-webgl 0.20.0-beta.19 → 0.20.0-beta.191

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xterm/addon-webgl",
3
- "version": "0.20.0-beta.19",
3
+ "version": "0.20.0-beta.191",
4
4
  "author": {
5
5
  "name": "The xterm.js authors",
6
6
  "url": "https://xtermjs.org/"
@@ -17,14 +17,14 @@
17
17
  "xterm.js"
18
18
  ],
19
19
  "scripts": {
20
- "build": "../../node_modules/.bin/tsc -p .",
20
+ "build": "../../node_modules/.bin/tsgo -p .",
21
21
  "prepackage": "npm run build",
22
22
  "package": "../../node_modules/.bin/webpack",
23
23
  "prepublishOnly": "npm run package",
24
24
  "start": "node ../../demo/start"
25
25
  },
26
- "commit": "faeefb53bf22d26742afc417005d953aad4c4473",
26
+ "commit": "fe0952bb835155f17cbc8752bdf63ecde21d7c53",
27
27
  "peerDependencies": {
28
- "@xterm/xterm": "^6.1.0-beta.20"
28
+ "@xterm/xterm": "^6.1.0-beta.192"
29
29
  }
30
30
  }
@@ -7,6 +7,7 @@ import { ICellData } from 'common/Types';
7
7
  import { Terminal } from '@xterm/xterm';
8
8
  import { rgba } from 'common/Color';
9
9
  import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils';
10
+ import { blockPatternCodepoints } from './customGlyphs/CustomGlyphDefinitions';
10
11
 
11
12
  // Work variables to avoid garbage collection
12
13
  let $fg = 0;
@@ -42,7 +43,7 @@ export class CellColorResolver {
42
43
  * Resolves colors for the cell, putting the result into the shared {@link result}. This resolves
43
44
  * overrides, inverse and selection for the cell which can then be used to feed into the renderer.
44
45
  */
45
- public resolve(cell: ICellData, x: number, y: number, deviceCellWidth: number): void {
46
+ public resolve(cell: ICellData, x: number, y: number, deviceCellWidth: number, deviceCellHeight: number): void {
46
47
  this.result.bg = cell.bg;
47
48
  this.result.fg = cell.fg;
48
49
  this.result.ext = cell.bg & BgFlags.HAS_EXTENDED ? cell.extended.ext : 0;
@@ -63,7 +64,9 @@ export class CellColorResolver {
63
64
  const lineWidth = Math.max(1, Math.floor(this._optionService.rawOptions.fontSize * this._coreBrowserService.dpr / 15));
64
65
  $variantOffset = x * deviceCellWidth % (Math.round(lineWidth) * 2);
65
66
  }
66
-
67
+ if ($variantOffset === 0 && blockPatternCodepoints.has(code)) {
68
+ $variantOffset = ((x * deviceCellWidth) % 2) * 2 + ((y * deviceCellHeight) % 2);
69
+ }
67
70
  // Apply decorations on the bottom layer
68
71
  this._decorationService.forEachDecorationAtCell(x, y, 'bottom', d => {
69
72
  if (d.backgroundColorRGB) {
@@ -8,6 +8,7 @@ import { ITerminalOptions, Terminal } from '@xterm/xterm';
8
8
  import { ITerminal, ReadonlyColorSet } from 'browser/Types';
9
9
  import { ICharAtlasConfig, ITextureAtlas } from './Types';
10
10
  import { generateConfig, configEquals } from './CharAtlasUtils';
11
+ import type { ILogService } from 'common/services/Services';
11
12
 
12
13
  interface ITextureAtlasCacheEntry {
13
14
  atlas: ITextureAtlas;
@@ -67,8 +68,9 @@ export function acquireTextureAtlas(
67
68
  }
68
69
 
69
70
  const core: ITerminal = (terminal as any)._core;
71
+ const logService = (core as any)._logService as ILogService;
70
72
  const newEntry: ITextureAtlasCacheEntry = {
71
- atlas: new TextureAtlas(document, newConfig, core.unicodeService),
73
+ atlas: new TextureAtlas(document, newConfig, core.unicodeService, logService),
72
74
  config: newConfig,
73
75
  ownedBy: [terminal]
74
76
  };
@@ -3,12 +3,15 @@
3
3
  * @license MIT
4
4
  */
5
5
 
6
+ import { RendererConstants } from 'browser/renderer/shared/Constants';
6
7
  import { ICoreBrowserService } from 'browser/services/Services';
7
8
 
8
- /**
9
- * The time between cursor blinks.
10
- */
11
- const BLINK_INTERVAL = 600;
9
+ const enum Constants {
10
+ /**
11
+ * The time between cursor blinks.
12
+ */
13
+ BLINK_INTERVAL = 600,
14
+ }
12
15
 
13
16
  export class CursorBlinkStateManager {
14
17
  public isCursorVisible: boolean;
@@ -16,6 +19,8 @@ export class CursorBlinkStateManager {
16
19
  private _animationFrame: number | undefined;
17
20
  private _blinkStartTimeout: number | undefined;
18
21
  private _blinkInterval: number | undefined;
22
+ private _idleTimeout: number | undefined;
23
+ private _isIdlePaused: boolean = false;
19
24
 
20
25
  /**
21
26
  * The time at which the animation frame was restarted, this is used on the
@@ -31,6 +36,7 @@ export class CursorBlinkStateManager {
31
36
  this.isCursorVisible = true;
32
37
  if (this._coreBrowserService.isFocused) {
33
38
  this._restartInterval();
39
+ this._resetIdleTimer();
34
40
  }
35
41
  }
36
42
 
@@ -49,9 +55,16 @@ export class CursorBlinkStateManager {
49
55
  this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame);
50
56
  this._animationFrame = undefined;
51
57
  }
58
+ if (this._idleTimeout) {
59
+ this._coreBrowserService.window.clearTimeout(this._idleTimeout);
60
+ this._idleTimeout = undefined;
61
+ }
52
62
  }
53
63
 
54
64
  public restartBlinkAnimation(): void {
65
+ if (this._isIdlePaused) {
66
+ this._resetIdleTimer();
67
+ }
55
68
  if (this.isPaused) {
56
69
  return;
57
70
  }
@@ -67,7 +80,7 @@ export class CursorBlinkStateManager {
67
80
  }
68
81
  }
69
82
 
70
- private _restartInterval(timeToStart: number = BLINK_INTERVAL): void {
83
+ private _restartInterval(timeToStart: number = Constants.BLINK_INTERVAL): void {
71
84
  // Clear any existing interval
72
85
  if (this._blinkInterval) {
73
86
  this._coreBrowserService.window.clearInterval(this._blinkInterval);
@@ -82,7 +95,7 @@ export class CursorBlinkStateManager {
82
95
  // Check if another animation restart was requested while this was being
83
96
  // started
84
97
  if (this._animationTimeRestarted) {
85
- const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted);
98
+ const time = Constants.BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted);
86
99
  this._animationTimeRestarted = undefined;
87
100
  if (time > 0) {
88
101
  this._restartInterval(time);
@@ -103,7 +116,7 @@ export class CursorBlinkStateManager {
103
116
  if (this._animationTimeRestarted) {
104
117
  // calc time diff
105
118
  // Make restart interval do a setTimeout initially?
106
- const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted);
119
+ const time = Constants.BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted);
107
120
  this._animationTimeRestarted = undefined;
108
121
  this._restartInterval(time);
109
122
  return;
@@ -115,12 +128,13 @@ export class CursorBlinkStateManager {
115
128
  this._renderCallback();
116
129
  this._animationFrame = undefined;
117
130
  });
118
- }, BLINK_INTERVAL);
131
+ }, Constants.BLINK_INTERVAL);
119
132
  }, timeToStart);
120
133
  }
121
134
 
122
135
  public pause(): void {
123
136
  this.isCursorVisible = true;
137
+ this._isIdlePaused = false;
124
138
  if (this._blinkInterval) {
125
139
  this._coreBrowserService.window.clearInterval(this._blinkInterval);
126
140
  this._blinkInterval = undefined;
@@ -133,6 +147,10 @@ export class CursorBlinkStateManager {
133
147
  this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame);
134
148
  this._animationFrame = undefined;
135
149
  }
150
+ if (this._idleTimeout) {
151
+ this._coreBrowserService.window.clearTimeout(this._idleTimeout);
152
+ this._idleTimeout = undefined;
153
+ }
136
154
  }
137
155
 
138
156
  public resume(): void {
@@ -141,6 +159,47 @@ export class CursorBlinkStateManager {
141
159
 
142
160
  this._animationTimeRestarted = undefined;
143
161
  this._restartInterval();
162
+ this._resetIdleTimer();
144
163
  this.restartBlinkAnimation();
145
164
  }
165
+
166
+ /**
167
+ * Resets the idle timer. If the terminal is idle for the idle timeout period,
168
+ * the cursor blinking will stop.
169
+ */
170
+ private _resetIdleTimer(): void {
171
+ this._isIdlePaused = false;
172
+ if (this._idleTimeout) {
173
+ this._coreBrowserService.window.clearTimeout(this._idleTimeout);
174
+ }
175
+ this._idleTimeout = this._coreBrowserService.window.setTimeout(() => {
176
+ this._stopBlinkingDueToIdle();
177
+ }, RendererConstants.CURSOR_BLINK_IDLE_TIMEOUT);
178
+ }
179
+
180
+ /**
181
+ * Stops cursor blinking due to idle timeout.
182
+ */
183
+ private _stopBlinkingDueToIdle(): void {
184
+ // Make cursor visible and stop blinking
185
+ this.isCursorVisible = true;
186
+ this._isIdlePaused = true;
187
+ if (this._blinkInterval) {
188
+ this._coreBrowserService.window.clearInterval(this._blinkInterval);
189
+ this._blinkInterval = undefined;
190
+ }
191
+ if (this._blinkStartTimeout) {
192
+ this._coreBrowserService.window.clearTimeout(this._blinkStartTimeout);
193
+ this._blinkStartTimeout = undefined;
194
+ }
195
+ if (this._animationFrame) {
196
+ this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame);
197
+ this._animationFrame = undefined;
198
+ }
199
+ // Clear the idle timeout as we've already acted on it
200
+ this._coreBrowserService.window.clearTimeout(this._idleTimeout);
201
+ this._idleTimeout = undefined;
202
+ // Trigger a render to show the cursor in its final visible state
203
+ this._renderCallback();
204
+ }
146
205
  }
@@ -3,7 +3,7 @@
3
3
  * @license MIT
4
4
  */
5
5
 
6
- import { toDisposable, IDisposable } from 'vs/base/common/lifecycle';
6
+ import { toDisposable, IDisposable } from 'common/Lifecycle';
7
7
 
8
8
  export function observeDevicePixelDimensions(element: HTMLElement, parentWindow: Window & typeof globalThis, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable {
9
9
  // Observe any resizes to the element and extract the actual pixel size of the element if the
@@ -5,7 +5,7 @@
5
5
  import { TextureAtlas } from './TextureAtlas';
6
6
  import { IRenderDimensions } from 'browser/renderer/shared/Types';
7
7
  import { NULL_CELL_CODE } from 'common/buffer/Constants';
8
- import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
8
+ import { Disposable, toDisposable } from 'common/Lifecycle';
9
9
  import { Terminal } from '@xterm/xterm';
10
10
  import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject, type IRasterizedGlyph, type ITextureAtlas } from './Types';
11
11
  import { createProgram, GLTexture, PROJECTION_MATRIX } from './WebglUtils';
@@ -318,8 +318,8 @@ export class GlyphRenderer extends Disposable {
318
318
  public handleResize(): void {
319
319
  const gl = this._gl;
320
320
  gl.useProgram(this._program);
321
- gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
322
- gl.uniform2f(this._resolutionLocation, gl.canvas.width, gl.canvas.height);
321
+ gl.viewport(0, 0, this._dimensions.device.canvas.width, this._dimensions.device.canvas.height);
322
+ gl.uniform2f(this._resolutionLocation, this._dimensions.device.canvas.width, this._dimensions.device.canvas.height);
323
323
  this.clear();
324
324
  }
325
325
 
@@ -7,7 +7,7 @@ import { IRenderDimensions } from 'browser/renderer/shared/Types';
7
7
  import { IThemeService } from 'browser/services/Services';
8
8
  import { ReadonlyColorSet } from 'browser/Types';
9
9
  import { Attributes, FgFlags } from 'common/buffer/Constants';
10
- import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
10
+ import { Disposable, toDisposable } from 'common/Lifecycle';
11
11
  import { IColor } from 'common/Types';
12
12
  import { Terminal } from '@xterm/xterm';
13
13
  import { RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel';
@@ -14,8 +14,8 @@ import { IdleTaskQueue } from 'common/TaskQueue';
14
14
  import { IColor } from 'common/Types';
15
15
  import { AttributeData } from 'common/buffer/AttributeData';
16
16
  import { Attributes, DEFAULT_COLOR, DEFAULT_EXT, UnderlineStyle } from 'common/buffer/Constants';
17
- import { IUnicodeService } from 'common/services/Services';
18
- import { Emitter } from 'vs/base/common/event';
17
+ import { ILogService, IUnicodeService } from 'common/services/Services';
18
+ import { Emitter } from 'common/Event';
19
19
 
20
20
  /**
21
21
  * A shared object which is used to draw nothing for a particular cell.
@@ -88,7 +88,8 @@ export class TextureAtlas implements ITextureAtlas {
88
88
  constructor(
89
89
  private readonly _document: Document,
90
90
  private readonly _config: ICharAtlasConfig,
91
- private readonly _unicodeService: IUnicodeService
91
+ private readonly _unicodeService: IUnicodeService,
92
+ private readonly _logService: ILogService
92
93
  ) {
93
94
  this._createNewPage();
94
95
  this._tmpCanvas = createCanvas(
@@ -119,7 +120,7 @@ export class TextureAtlas implements ITextureAtlas {
119
120
 
120
121
  private _doWarmUp(): void {
121
122
  // Pre-fill with ASCII 33-126, this is not urgent and done in idle callbacks
122
- const queue = new IdleTaskQueue();
123
+ const queue = new IdleTaskQueue(this._logService);
123
124
  for (let i = 33; i < 126; i++) {
124
125
  queue.enqueue(() => {
125
126
  if (!this._cacheMap.get(i, DEFAULT_COLOR, DEFAULT_COLOR, DEFAULT_EXT)) {
@@ -176,6 +177,17 @@ export class TextureAtlas implements ITextureAtlas {
176
177
 
177
178
  // Gather details of the merge
178
179
  const mergingPages = pagesBySize.slice(sameSizeI, sameSizeI + 4);
180
+
181
+ // Only proceed with merge if we have exactly 4 same-sized pages. If not, we cannot
182
+ // effectively reduce page count and merging would cause issues.
183
+ if (mergingPages.length < 4 || mergingPages.some(p => p.canvas.width !== mergingPages[0].canvas.width)) {
184
+ const newPage = new AtlasPage(this._document, this._textureSize);
185
+ this._pages.push(newPage);
186
+ this._activePages.push(newPage);
187
+ this._onAddTextureAtlasCanvas.fire(newPage.canvas);
188
+ return newPage;
189
+ }
190
+
179
191
  const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a > b ? 1 : -1);
180
192
  const mergedPageIndex = this.pages.length - mergingPages.length;
181
193
 
@@ -399,7 +411,7 @@ export class TextureAtlas implements ITextureAtlas {
399
411
  const cache = this._getContrastCache(dim);
400
412
  const adjustedColor = cache.getColor(bg, fg);
401
413
  if (adjustedColor !== undefined) {
402
- return adjustedColor || undefined;
414
+ return adjustedColor ?? undefined;
403
415
  }
404
416
 
405
417
  const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, inverse);
@@ -514,7 +526,8 @@ export class TextureAtlas implements ITextureAtlas {
514
526
  // Draw custom characters if applicable
515
527
  let customGlyph = false;
516
528
  if (this._config.customGlyphs !== false) {
517
- customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.fontSize, this._config.devicePixelRatio, backgroundColor.css);
529
+ const variantOffset = this._workAttributeData.getUnderlineVariantOffset();
530
+ customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.deviceCharWidth, this._config.deviceCharHeight, this._config.fontSize, this._config.devicePixelRatio, backgroundColor.css, variantOffset);
518
531
  }
519
532
 
520
533
  // Whether to clear pixels based on a threshold difference between the glyph color and the
@@ -551,61 +564,67 @@ export class TextureAtlas implements ITextureAtlas {
551
564
  }
552
565
  this._tmpCtx.strokeStyle = this._getColorFromAnsiIndex(fg).css;
553
566
  }
567
+ this._tmpCtx.fillStyle = this._tmpCtx.strokeStyle;
554
568
 
555
569
  // Underline style/stroke
556
570
  this._tmpCtx.beginPath();
557
571
  const xLeft = padding;
558
- const yTop = Math.ceil(padding + this._config.deviceCharHeight) - yOffset - (restrictToCellHeight ? lineWidth * 2 : 0);
559
- const yMid = yTop + lineWidth;
560
- const yBot = yTop + lineWidth * 2;
572
+ const yTopDefault = Math.ceil(padding + this._config.deviceCharHeight) - yOffset - (restrictToCellHeight ? lineWidth * 2 : 0);
573
+ const yBotDefault = yTopDefault + lineWidth * 2;
561
574
  let nextOffset = this._workAttributeData.getUnderlineVariantOffset();
575
+ let yTop = 0;
576
+ let yBot = 0;
562
577
 
563
578
  for (let i = 0; i < chWidth; i++) {
579
+ let wasFilled = false;
564
580
  this._tmpCtx.save();
581
+ yTop = yTopDefault;
582
+ yBot = yBotDefault;
565
583
  const xChLeft = xLeft + i * this._config.deviceCellWidth;
566
584
  const xChRight = xLeft + (i + 1) * this._config.deviceCellWidth;
567
- const xChMid = xChLeft + this._config.deviceCellWidth / 2;
568
585
  switch (this._workAttributeData.extended.underlineStyle) {
569
586
  case UnderlineStyle.DOUBLE:
570
- this._tmpCtx.moveTo(xChLeft, yTop);
571
- this._tmpCtx.lineTo(xChRight, yTop);
572
- this._tmpCtx.moveTo(xChLeft, yBot);
573
- this._tmpCtx.lineTo(xChRight, yBot);
587
+ this._tmpCtx.moveTo(xChLeft, yTopDefault);
588
+ this._tmpCtx.lineTo(xChRight, yTopDefault);
589
+ this._tmpCtx.moveTo(xChLeft, yBotDefault);
590
+ this._tmpCtx.lineTo(xChRight, yBotDefault);
574
591
  break;
575
592
  case UnderlineStyle.CURLY:
576
- // Choose the bezier top and bottom based on the device pixel ratio, the curly line is
577
- // made taller when the line width is as otherwise it's not very clear otherwise.
578
- const yCurlyBot = lineWidth <= 1 ? yBot : Math.ceil(padding + this._config.deviceCharHeight - lineWidth / 2) - yOffset;
579
- const yCurlyTop = lineWidth <= 1 ? yTop : Math.ceil(padding + this._config.deviceCharHeight + lineWidth / 2) - yOffset;
580
- // Clip the left and right edges of the underline such that it can be drawn just outside
581
- // the edge of the cell to ensure a continuous stroke when there are multiple underlined
582
- // glyphs adjacent to one another.
593
+ yTop = this._config.deviceCharHeight + 1;
594
+ yBot = yTop + 3 * this._config.devicePixelRatio;
595
+
583
596
  const clipRegion = new Path2D();
584
597
  clipRegion.rect(xChLeft, yTop, this._config.deviceCellWidth, yBot - yTop);
585
598
  this._tmpCtx.clip(clipRegion);
586
- // Start 1/2 cell before and end 1/2 cells after to ensure a smooth curve with other
587
- // cells
588
- this._tmpCtx.moveTo(xChLeft - this._config.deviceCellWidth / 2, yMid);
589
- this._tmpCtx.bezierCurveTo(
590
- xChLeft - this._config.deviceCellWidth / 2, yCurlyTop,
591
- xChLeft, yCurlyTop,
592
- xChLeft, yMid
593
- );
594
- this._tmpCtx.bezierCurveTo(
595
- xChLeft, yCurlyBot,
596
- xChMid, yCurlyBot,
597
- xChMid, yMid
598
- );
599
- this._tmpCtx.bezierCurveTo(
600
- xChMid, yCurlyTop,
601
- xChRight, yCurlyTop,
602
- xChRight, yMid
603
- );
604
- this._tmpCtx.bezierCurveTo(
605
- xChRight, yCurlyBot,
606
- xChRight + this._config.deviceCellWidth / 2, yCurlyBot,
607
- xChRight + this._config.deviceCellWidth / 2, yMid
608
- );
599
+
600
+ // Draw a zigzag pattern, this is derived from the SVG used in monaco for the same
601
+ // style. The viewbox is 6x3 so scale it using that.
602
+ const cellW = this._config.deviceCellWidth;
603
+ const curlyH = (yBot - yTop);
604
+ const scaleX = cellW / 6;
605
+ const scaleY = curlyH / 3;
606
+
607
+ const polygons: number[][] = [
608
+ [0, 2, 1, 3, 2.4, 3, 0, 0.6],
609
+ [5.5, 0, 2.5, 3, 1.1, 3, 4.1, 0],
610
+ [4, 0, 6, 2, 6, 0.6, 5.4, 0],
611
+ ];
612
+
613
+ for (const polygon of polygons) {
614
+ this._tmpCtx.beginPath();
615
+ for (let i = 0; i < polygon.length; i += 2) {
616
+ const x = xChLeft + polygon[i] * scaleX;
617
+ const y = yBot - polygon[i + 1] * scaleY;
618
+ if (i === 0) {
619
+ this._tmpCtx.moveTo(x, y);
620
+ } else {
621
+ this._tmpCtx.lineTo(x, y);
622
+ }
623
+ }
624
+ this._tmpCtx.closePath();
625
+ this._tmpCtx.fill();
626
+ }
627
+ wasFilled = true;
609
628
  break;
610
629
  case UnderlineStyle.DOTTED:
611
630
  const offsetWidth = nextOffset === 0 ? 0 :
@@ -614,14 +633,14 @@ export class TextureAtlas implements ITextureAtlas {
614
633
  const isLineStart = nextOffset >= lineWidth ? false : true;
615
634
  if (isLineStart === false || offsetWidth === 0) {
616
635
  this._tmpCtx.setLineDash([Math.round(lineWidth), Math.round(lineWidth)]);
617
- this._tmpCtx.moveTo(xChLeft + offsetWidth, yTop);
618
- this._tmpCtx.lineTo(xChRight, yTop);
636
+ this._tmpCtx.moveTo(xChLeft + offsetWidth, yTopDefault);
637
+ this._tmpCtx.lineTo(xChRight, yTopDefault);
619
638
  } else {
620
639
  this._tmpCtx.setLineDash([Math.round(lineWidth), Math.round(lineWidth)]);
621
- this._tmpCtx.moveTo(xChLeft, yTop);
622
- this._tmpCtx.lineTo(xChLeft + offsetWidth, yTop);
623
- this._tmpCtx.moveTo(xChLeft + offsetWidth + lineWidth, yTop);
624
- this._tmpCtx.lineTo(xChRight, yTop);
640
+ this._tmpCtx.moveTo(xChLeft, yTopDefault);
641
+ this._tmpCtx.lineTo(xChLeft + offsetWidth, yTopDefault);
642
+ this._tmpCtx.moveTo(xChLeft + offsetWidth + lineWidth, yTopDefault);
643
+ this._tmpCtx.lineTo(xChRight, yTopDefault);
625
644
  }
626
645
  nextOffset = computeNextVariantOffset(xChRight - xChLeft, lineWidth, nextOffset);
627
646
  break;
@@ -634,16 +653,18 @@ export class TextureAtlas implements ITextureAtlas {
634
653
  const gap = Math.floor(gapRatio * xChWidth);
635
654
  const end = xChWidth - line - gap;
636
655
  this._tmpCtx.setLineDash([line, gap, end]);
637
- this._tmpCtx.moveTo(xChLeft, yTop);
638
- this._tmpCtx.lineTo(xChRight, yTop);
656
+ this._tmpCtx.moveTo(xChLeft, yTopDefault);
657
+ this._tmpCtx.lineTo(xChRight, yTopDefault);
639
658
  break;
640
659
  case UnderlineStyle.SINGLE:
641
660
  default:
642
- this._tmpCtx.moveTo(xChLeft, yTop);
643
- this._tmpCtx.lineTo(xChRight, yTop);
661
+ this._tmpCtx.moveTo(xChLeft, yTopDefault);
662
+ this._tmpCtx.lineTo(xChRight, yTopDefault);
644
663
  break;
645
664
  }
646
- this._tmpCtx.stroke();
665
+ if (!wasFilled) {
666
+ this._tmpCtx.stroke();
667
+ }
647
668
  this._tmpCtx.restore();
648
669
  }
649
670
  this._tmpCtx.restore();
package/src/Types.ts CHANGED
@@ -7,7 +7,7 @@ import { FontWeight } from '@xterm/xterm';
7
7
  import { IColorSet } from 'browser/Types';
8
8
  import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
9
9
  import { CursorInactiveStyle, CursorStyle, type IDisposable } from 'common/Types';
10
- import type { Event } from 'vs/base/common/event';
10
+ import type { IEvent } from 'common/Event';
11
11
 
12
12
  export interface IRenderModel {
13
13
  cells: Uint32Array;
@@ -58,8 +58,8 @@ export interface ICharAtlasConfig {
58
58
  export interface ITextureAtlas extends IDisposable {
59
59
  readonly pages: { canvas: HTMLCanvasElement, version: number }[];
60
60
 
61
- onAddTextureAtlasCanvas: Event<HTMLCanvasElement>;
62
- onRemoveTextureAtlasCanvas: Event<HTMLCanvasElement>;
61
+ onAddTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
62
+ onRemoveTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
63
63
 
64
64
  /**
65
65
  * Warm up the texture atlas, adding common glyphs to avoid slowing early frame.
package/src/WebglAddon.ts CHANGED
@@ -7,15 +7,14 @@ import type { ITerminalAddon, Terminal } from '@xterm/xterm';
7
7
  import type { IWebglAddonOptions, WebglAddon as IWebglApi } from '@xterm/addon-webgl';
8
8
  import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
9
9
  import { ITerminal } from 'browser/Types';
10
- import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
10
+ import { Disposable, toDisposable } from 'common/Lifecycle';
11
11
  import { getSafariVersion, isSafari } from 'common/Platform';
12
- import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services';
12
+ import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
13
13
  import { IWebGL2RenderingContext } from './Types';
14
14
  import { WebglRenderer } from './WebglRenderer';
15
- import { setTraceLogger } from 'common/services/LogService';
16
- import { Emitter, Event } from 'vs/base/common/event';
15
+ import { Emitter, EventUtils } from 'common/Event';
17
16
 
18
- export class WebglAddon extends Disposable implements ITerminalAddon , IWebglApi {
17
+ export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi {
19
18
  private _terminal?: Terminal;
20
19
  private _renderer?: WebglRenderer;
21
20
 
@@ -66,13 +65,8 @@ export class WebglAddon extends Disposable implements ITerminalAddon , IWebglApi
66
65
  const charSizeService: ICharSizeService = unsafeCore._charSizeService;
67
66
  const coreBrowserService: ICoreBrowserService = unsafeCore._coreBrowserService;
68
67
  const decorationService: IDecorationService = unsafeCore._decorationService;
69
- const logService: ILogService = unsafeCore._logService;
70
68
  const themeService: IThemeService = unsafeCore._themeService;
71
69
 
72
- // Set trace logger just in case it hasn't been yet which could happen when the addon is
73
- // bundled separately to the core module
74
- setTraceLogger(logService);
75
-
76
70
  this._renderer = this._register(new WebglRenderer(
77
71
  terminal,
78
72
  characterJoinerService,
@@ -85,10 +79,10 @@ export class WebglAddon extends Disposable implements ITerminalAddon , IWebglApi
85
79
  this._customGlyphs,
86
80
  this._preserveDrawingBuffer
87
81
  ));
88
- this._register(Event.forward(this._renderer.onContextLoss, this._onContextLoss));
89
- this._register(Event.forward(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas));
90
- this._register(Event.forward(this._renderer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas));
91
- this._register(Event.forward(this._renderer.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas));
82
+ this._register(EventUtils.forward(this._renderer.onContextLoss, this._onContextLoss));
83
+ this._register(EventUtils.forward(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas));
84
+ this._register(EventUtils.forward(this._renderer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas));
85
+ this._register(EventUtils.forward(this._renderer.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas));
92
86
  renderService.setRenderer(this._renderer);
93
87
 
94
88
  this._register(toDisposable(() => {