Skip to content

Commit eb3de3e

Browse files
authored
Merge pull request #4151 from Tyriar/renderer_before_open
Allow renderer to be set before Terminal.open is called
2 parents a060c64 + ffa4d92 commit eb3de3e

File tree

8 files changed

+90
-53
lines changed

8 files changed

+90
-53
lines changed

addons/xterm-addon-canvas/src/CanvasAddon.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,23 @@ export class CanvasAddon implements ITerminalAddon {
1818
public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event;
1919

2020
public activate(terminal: Terminal): void {
21+
const core = (terminal as any)._core;
2122
if (!terminal.element) {
22-
throw new Error('Cannot activate CanvasAddon before Terminal.open');
23+
core.onWillOpen(() => this.activate(terminal));
24+
return;
2325
}
2426
this._terminal = terminal;
25-
const bufferService: IBufferService = (terminal as any)._core._bufferService;
26-
const renderService: IRenderService = (terminal as any)._core._renderService;
27-
const characterJoinerService: ICharacterJoinerService = (terminal as any)._core._characterJoinerService;
28-
const charSizeService: ICharSizeService = (terminal as any)._core._charSizeService;
29-
const coreService: ICoreService = (terminal as any)._core.coreService;
30-
const coreBrowserService: ICoreBrowserService = (terminal as any)._core._coreBrowserService;
31-
const decorationService: IDecorationService = (terminal as any)._core._decorationService;
32-
const optionsService: IOptionsService = (terminal as any)._core.optionsService;
33-
const colors: IColorSet = (terminal as any)._core._colorManager.colors;
34-
const screenElement: HTMLElement = (terminal as any)._core.screenElement;
35-
const linkifier = (terminal as any)._core.linkifier2;
27+
const bufferService: IBufferService = core._bufferService;
28+
const renderService: IRenderService = core._renderService;
29+
const characterJoinerService: ICharacterJoinerService = core._characterJoinerService;
30+
const charSizeService: ICharSizeService = core._charSizeService;
31+
const coreService: ICoreService = core.coreService;
32+
const coreBrowserService: ICoreBrowserService = core._coreBrowserService;
33+
const decorationService: IDecorationService = core._decorationService;
34+
const optionsService: IOptionsService = core.optionsService;
35+
const colors: IColorSet = core._colorManager.colors;
36+
const screenElement: HTMLElement = core.screenElement;
37+
const linkifier = core.linkifier2;
3638
this._renderer = new CanvasRenderer(terminal, colors, screenElement, linkifier, bufferService, charSizeService, optionsService, characterJoinerService, coreService, coreBrowserService, decorationService);
3739
forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas);
3840
renderService.setRenderer(this._renderer);

addons/xterm-addon-webgl/src/WebglAddon.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,21 @@ export class WebglAddon implements ITerminalAddon {
2525
) {}
2626

2727
public activate(terminal: Terminal): void {
28+
const core = (terminal as any)._core;
2829
if (!terminal.element) {
29-
throw new Error('Cannot activate WebglAddon before Terminal.open');
30+
core.onWillOpen(() => this.activate(terminal));
31+
return;
3032
}
3133
if (isSafari) {
3234
throw new Error('Webgl is not currently supported on Safari');
3335
}
3436
this._terminal = terminal;
35-
const renderService: IRenderService = (terminal as any)._core._renderService;
36-
const characterJoinerService: ICharacterJoinerService = (terminal as any)._core._characterJoinerService;
37-
const coreBrowserService: ICoreBrowserService = (terminal as any)._core._coreBrowserService;
38-
const coreService: ICoreService = (terminal as any)._core.coreService;
39-
const decorationService: IDecorationService = (terminal as any)._core._decorationService;
40-
const colors: IColorSet = (terminal as any)._core._colorManager.colors;
37+
const renderService: IRenderService = core._renderService;
38+
const characterJoinerService: ICharacterJoinerService = core._characterJoinerService;
39+
const coreBrowserService: ICoreBrowserService = core._coreBrowserService;
40+
const coreService: ICoreService = core.coreService;
41+
const decorationService: IDecorationService = core._decorationService;
42+
const colors: IColorSet = core._colorManager.colors;
4143
this._renderer = new WebglRenderer(terminal, colors, characterJoinerService, coreBrowserService, coreService, decorationService, this._preserveDrawingBuffer);
4244
forwardEvent(this._renderer.onContextLoss, this._onContextLoss);
4345
forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas);

demo/client.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -270,18 +270,14 @@ function createTerminal(): void {
270270
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
271271
socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/';
272272

273-
term.open(terminalContainer);
274273
addons.fit.instance!.fit();
275-
try {
276-
typedTerm.loadAddon(addons.webgl.instance);
277-
setTimeout(() => {
278-
addTextureAtlas(addons.webgl.instance.textureAtlas);
279-
addons.webgl.instance.onChangeTextureAtlas(e => addTextureAtlas(e));
280-
}, 0);
281-
}
282-
catch {
283-
addons.webgl.instance = undefined;
284-
}
274+
typedTerm.loadAddon(addons.webgl.instance);
275+
setTimeout(() => {
276+
addTextureAtlas(addons.webgl.instance.textureAtlas);
277+
addons.webgl.instance.onChangeTextureAtlas(e => addTextureAtlas(e));
278+
}, 0);
279+
280+
term.open(terminalContainer);
285281
term.focus();
286282

287283
addDomListener(paddingElement, 'change', setPadding);

src/browser/Terminal.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,16 @@ export class Terminal extends CoreTerminal implements ITerminal {
135135
private readonly _onBell = new EventEmitter<void>();
136136
public readonly onBell = this._onBell.event;
137137

138-
private readonly _onFocus = new EventEmitter<void>();
139-
public readonly onFocus = this._onFocus.event;
140-
private readonly _onBlur = new EventEmitter<void>();
141-
public readonly onBlur = this._onBlur.event;
142-
private readonly _onA11yCharEmitter = new EventEmitter<string>();
143-
public readonly onA11yChar = this._onA11yCharEmitter.event;
144-
private readonly _onA11yTabEmitter = new EventEmitter<number>();
145-
public readonly onA11yTab = this._onA11yTabEmitter.event;
138+
private _onFocus = new EventEmitter<void>();
139+
public get onFocus(): IEvent<void> { return this._onFocus.event; }
140+
private _onBlur = new EventEmitter<void>();
141+
public get onBlur(): IEvent<void> { return this._onBlur.event; }
142+
private _onA11yCharEmitter = new EventEmitter<string>();
143+
public get onA11yChar(): IEvent<string> { return this._onA11yCharEmitter.event; }
144+
private _onA11yTabEmitter = new EventEmitter<number>();
145+
public get onA11yTab(): IEvent<number> { return this._onA11yTabEmitter.event; }
146+
private _onWillOpen = new EventEmitter<HTMLElement>();
147+
public get onWillOpen(): IEvent<HTMLElement> { return this._onWillOpen.event; }
146148

147149
/**
148150
* Creates a new `Terminal` object.
@@ -509,8 +511,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
509511
this._characterJoinerService = this._instantiationService.createInstance(CharacterJoinerService);
510512
this._instantiationService.setService(ICharacterJoinerService, this._characterJoinerService);
511513

512-
const renderer = this._createRenderer();
513-
this._renderService = this.register(this._instantiationService.createInstance(RenderService, renderer, this.rows, this.screenElement));
514+
this._renderService = this.register(this._instantiationService.createInstance(RenderService, this.rows, this.screenElement));
514515
this._instantiationService.setService(IRenderService, this._renderService);
515516
this.register(this._renderService.onRenderedViewportChange(e => this._onRender.fire(e)));
516517
this.onResize(e => this._renderService!.resize(e.cols, e.rows));
@@ -523,6 +524,11 @@ export class Terminal extends CoreTerminal implements ITerminal {
523524
// Performance: Add viewport and helper elements from the fragment
524525
this.element.appendChild(fragment);
525526

527+
this._onWillOpen.fire(this.element);
528+
if (!this._renderService.hasRenderer()) {
529+
this._renderService.setRenderer(this._createRenderer());
530+
}
531+
526532
this._mouseService = this._instantiationService.createInstance(MouseService);
527533
this._instantiationService.setService(IMouseService, this._mouseService);
528534

src/browser/TestUtils.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export class MockTerminal implements ITerminal {
4141
public onTitleChange!: IEvent<string>;
4242
public onBell!: IEvent<void>;
4343
public onScroll!: IEvent<number>;
44+
public onWillOpen!: IEvent<HTMLElement>;
4445
public onKey!: IEvent<{ key: string, domEvent: KeyboardEvent }>;
4546
public onRender!: IEvent<{ start: number, end: number }>;
4647
public onResize!: IEvent<{ cols: number, rows: number }>;
@@ -400,6 +401,9 @@ export class MockRenderService implements IRenderService {
400401
public resize(cols: number, rows: number): void {
401402
throw new Error('Method not implemented.');
402403
}
404+
public hasRenderer(): boolean {
405+
throw new Error('Method not implemented.');
406+
}
403407
public setRenderer(renderer: IRenderer): void {
404408
throw new Error('Method not implemented.');
405409
}

src/browser/Types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export interface ITerminal extends IPublicTerminal, ICoreTerminal {
2323
onFocus: IEvent<void>;
2424
onA11yChar: IEvent<string>;
2525
onA11yTab: IEvent<number>;
26+
onWillOpen: IEvent<HTMLElement>;
2627

2728
cancel(ev: Event, force?: boolean): boolean | void;
2829
}

src/browser/services/RenderService.ts

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ interface ISelectionState {
2323
export class RenderService extends Disposable implements IRenderService {
2424
public serviceBrand: undefined;
2525

26+
private _renderer: IRenderer | undefined;
2627
private _renderDebouncer: IRenderDebouncerWithCallback;
2728
private _screenDprMonitor: ScreenDprMonitor;
2829
private _pausedResizeTask = new DebouncedIdleTask();
@@ -48,10 +49,9 @@ export class RenderService extends Disposable implements IRenderService {
4849
private readonly _onRefreshRequest = new EventEmitter<{ start: number, end: number }>();
4950
public readonly onRefreshRequest = this._onRefreshRequest.event;
5051

51-
public get dimensions(): IRenderDimensions { return this._renderer.dimensions; }
52+
public get dimensions(): IRenderDimensions { return this._renderer!.dimensions; }
5253

5354
constructor(
54-
private _renderer: IRenderer,
5555
private _rowCount: number,
5656
screenElement: HTMLElement,
5757
@IOptionsService optionsService: IOptionsService,
@@ -62,7 +62,7 @@ export class RenderService extends Disposable implements IRenderService {
6262
) {
6363
super();
6464

65-
this.register({ dispose: () => this._renderer.dispose() });
65+
this.register({ dispose: () => this._renderer?.dispose() });
6666

6767
this._renderDebouncer = new RenderDebouncer(coreBrowserService.window, (start, end) => this._renderRows(start, end));
6868
this.register(this._renderDebouncer);
@@ -83,7 +83,7 @@ export class RenderService extends Disposable implements IRenderService {
8383
this.register(decorationService.onDecorationRemoved(() => this._fullRefresh()));
8484

8585
// No need to register this as renderer is explicitly disposed in RenderService.dispose
86-
this._renderer.onRequestRedraw(e => this.refreshRows(e.start, e.end, true));
86+
// this._renderer.onRequestRedraw(e => this.refreshRows(e.start, e.end, true));
8787

8888
// dprchange should handle this case, we need this as well for browsers that don't support the
8989
// matchMedia query.
@@ -125,6 +125,9 @@ export class RenderService extends Disposable implements IRenderService {
125125
}
126126

127127
private _renderRows(start: number, end: number): void {
128+
if (!this._renderer) {
129+
return;
130+
}
128131
this._renderer.renderRows(start, end);
129132

130133
// Update selection if needed
@@ -147,12 +150,18 @@ export class RenderService extends Disposable implements IRenderService {
147150
}
148151

149152
private _handleOptionsChanged(): void {
153+
if (!this._renderer) {
154+
return;
155+
}
150156
this._renderer.onOptionsChanged();
151157
this.refreshRows(0, this._rowCount - 1);
152158
this._fireOnCanvasResize();
153159
}
154160

155161
private _fireOnCanvasResize(): void {
162+
if (!this._renderer) {
163+
return;
164+
}
156165
// Don't fire the event if the dimensions haven't changed
157166
if (this._renderer.dimensions.canvasWidth === this._canvasWidth && this._renderer.dimensions.canvasHeight === this._canvasHeight) {
158167
return;
@@ -164,9 +173,13 @@ export class RenderService extends Disposable implements IRenderService {
164173
super.dispose();
165174
}
166175

176+
public hasRenderer(): boolean {
177+
return !!this._renderer;
178+
}
179+
167180
public setRenderer(renderer: IRenderer): void {
168181
// TODO: RenderService should be the only one to dispose the renderer
169-
this._renderer.dispose();
182+
this._renderer?.dispose();
170183
this._renderer = renderer;
171184
this._renderer.onRequestRedraw(e => this.refreshRows(e.start, e.end, true));
172185

@@ -188,11 +201,17 @@ export class RenderService extends Disposable implements IRenderService {
188201
}
189202

190203
public clearTextureAtlas(): void {
191-
this._renderer?.clearTextureAtlas?.();
204+
if (!this._renderer) {
205+
return;
206+
}
207+
this._renderer.clearTextureAtlas?.();
192208
this._fullRefresh();
193209
}
194210

195211
public setColors(colors: IColorSet): void {
212+
if (!this._renderer) {
213+
return;
214+
}
196215
this._renderer.setColors(colors);
197216
this._fullRefresh();
198217
}
@@ -202,13 +221,19 @@ export class RenderService extends Disposable implements IRenderService {
202221
// when devicePixelRatio changes
203222
this._charSizeService.measure();
204223

224+
if (!this._renderer) {
225+
return;
226+
}
205227
this._renderer.onDevicePixelRatioChange();
206228
this.refreshRows(0, this._rowCount - 1);
207229
}
208230

209231
public onResize(cols: number, rows: number): void {
232+
if (!this._renderer) {
233+
return;
234+
}
210235
if (this._isPaused) {
211-
this._pausedResizeTask.set(() => this._renderer.onResize(cols, rows));
236+
this._pausedResizeTask.set(() => this._renderer!.onResize(cols, rows));
212237
} else {
213238
this._renderer.onResize(cols, rows);
214239
}
@@ -217,29 +242,29 @@ export class RenderService extends Disposable implements IRenderService {
217242

218243
// TODO: Is this useful when we have onResize?
219244
public onCharSizeChanged(): void {
220-
this._renderer.onCharSizeChanged();
245+
this._renderer?.onCharSizeChanged();
221246
}
222247

223248
public onBlur(): void {
224-
this._renderer.onBlur();
249+
this._renderer?.onBlur();
225250
}
226251

227252
public onFocus(): void {
228-
this._renderer.onFocus();
253+
this._renderer?.onFocus();
229254
}
230255

231256
public onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
232257
this._selectionState.start = start;
233258
this._selectionState.end = end;
234259
this._selectionState.columnSelectMode = columnSelectMode;
235-
this._renderer.onSelectionChanged(start, end, columnSelectMode);
260+
this._renderer?.onSelectionChanged(start, end, columnSelectMode);
236261
}
237262

238263
public onCursorMove(): void {
239-
this._renderer.onCursorMove();
264+
this._renderer?.onCursorMove();
240265
}
241266

242267
public clear(): void {
243-
this._renderer.clear();
268+
this._renderer?.clear();
244269
}
245270
}

src/browser/services/Services.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export interface IRenderService extends IDisposable {
7171
refreshRows(start: number, end: number): void;
7272
clearTextureAtlas(): void;
7373
resize(cols: number, rows: number): void;
74+
hasRenderer(): boolean;
7475
setRenderer(renderer: IRenderer): void;
7576
setColors(colors: IColorSet): void;
7677
onDevicePixelRatioChange(): void;

0 commit comments

Comments
 (0)