@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/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 +61 -9
- package/src/customGlyphs/CustomGlyphDefinitions.ts +230 -64
- package/src/customGlyphs/CustomGlyphRasterizer.ts +68 -23
- package/src/customGlyphs/Types.ts +16 -0
- package/src/renderLayer/BaseRenderLayer.ts +1 -1
- package/typings/addon-webgl.d.ts +12 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xterm/addon-webgl",
|
|
3
|
-
"version": "0.20.0-beta.
|
|
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/
|
|
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": "
|
|
26
|
+
"commit": "fe0952bb835155f17cbc8752bdf63ecde21d7c53",
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"@xterm/xterm": "^6.1.0-beta.
|
|
28
|
+
"@xterm/xterm": "^6.1.0-beta.192"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/src/CellColorResolver.ts
CHANGED
|
@@ -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) {
|
package/src/CharAtlasCache.ts
CHANGED
|
@@ -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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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 '
|
|
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
|
package/src/GlyphRenderer.ts
CHANGED
|
@@ -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 '
|
|
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,
|
|
322
|
-
gl.uniform2f(this._resolutionLocation,
|
|
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
|
|
package/src/RectangleRenderer.ts
CHANGED
|
@@ -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 '
|
|
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';
|
package/src/TextureAtlas.ts
CHANGED
|
@@ -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 '
|
|
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
|
|
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
|
-
|
|
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
|
|
559
|
-
const
|
|
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,
|
|
571
|
-
this._tmpCtx.lineTo(xChRight,
|
|
572
|
-
this._tmpCtx.moveTo(xChLeft,
|
|
573
|
-
this._tmpCtx.lineTo(xChRight,
|
|
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
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
-
|
|
587
|
-
//
|
|
588
|
-
|
|
589
|
-
this.
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
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,
|
|
618
|
-
this._tmpCtx.lineTo(xChRight,
|
|
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,
|
|
622
|
-
this._tmpCtx.lineTo(xChLeft + offsetWidth,
|
|
623
|
-
this._tmpCtx.moveTo(xChLeft + offsetWidth + lineWidth,
|
|
624
|
-
this._tmpCtx.lineTo(xChRight,
|
|
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,
|
|
638
|
-
this._tmpCtx.lineTo(xChRight,
|
|
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,
|
|
643
|
-
this._tmpCtx.lineTo(xChRight,
|
|
661
|
+
this._tmpCtx.moveTo(xChLeft, yTopDefault);
|
|
662
|
+
this._tmpCtx.lineTo(xChRight, yTopDefault);
|
|
644
663
|
break;
|
|
645
664
|
}
|
|
646
|
-
|
|
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 {
|
|
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:
|
|
62
|
-
onRemoveTextureAtlasCanvas:
|
|
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 '
|
|
10
|
+
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
11
11
|
import { getSafariVersion, isSafari } from 'common/Platform';
|
|
12
|
-
import { ICoreService, IDecorationService,
|
|
12
|
+
import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
|
|
13
13
|
import { IWebGL2RenderingContext } from './Types';
|
|
14
14
|
import { WebglRenderer } from './WebglRenderer';
|
|
15
|
-
import {
|
|
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
|
|
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(
|
|
89
|
-
this._register(
|
|
90
|
-
this._register(
|
|
91
|
-
this._register(
|
|
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(() => {
|