mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge pull request #4151 from Tyriar/renderer_before_open
Allow renderer to be set before Terminal.open is called
This commit is contained in:
@@ -18,21 +18,23 @@ export class CanvasAddon implements ITerminalAddon {
|
||||
public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event;
|
||||
|
||||
public activate(terminal: Terminal): void {
|
||||
const core = (terminal as any)._core;
|
||||
if (!terminal.element) {
|
||||
throw new Error('Cannot activate CanvasAddon before Terminal.open');
|
||||
core.onWillOpen(() => this.activate(terminal));
|
||||
return;
|
||||
}
|
||||
this._terminal = terminal;
|
||||
const bufferService: IBufferService = (terminal as any)._core._bufferService;
|
||||
const renderService: IRenderService = (terminal as any)._core._renderService;
|
||||
const characterJoinerService: ICharacterJoinerService = (terminal as any)._core._characterJoinerService;
|
||||
const charSizeService: ICharSizeService = (terminal as any)._core._charSizeService;
|
||||
const coreService: ICoreService = (terminal as any)._core.coreService;
|
||||
const coreBrowserService: ICoreBrowserService = (terminal as any)._core._coreBrowserService;
|
||||
const decorationService: IDecorationService = (terminal as any)._core._decorationService;
|
||||
const optionsService: IOptionsService = (terminal as any)._core.optionsService;
|
||||
const colors: IColorSet = (terminal as any)._core._colorManager.colors;
|
||||
const screenElement: HTMLElement = (terminal as any)._core.screenElement;
|
||||
const linkifier = (terminal as any)._core.linkifier2;
|
||||
const bufferService: IBufferService = core._bufferService;
|
||||
const renderService: IRenderService = core._renderService;
|
||||
const characterJoinerService: ICharacterJoinerService = core._characterJoinerService;
|
||||
const charSizeService: ICharSizeService = core._charSizeService;
|
||||
const coreService: ICoreService = core.coreService;
|
||||
const coreBrowserService: ICoreBrowserService = core._coreBrowserService;
|
||||
const decorationService: IDecorationService = core._decorationService;
|
||||
const optionsService: IOptionsService = core.optionsService;
|
||||
const colors: IColorSet = core._colorManager.colors;
|
||||
const screenElement: HTMLElement = core.screenElement;
|
||||
const linkifier = core.linkifier2;
|
||||
this._renderer = new CanvasRenderer(terminal, colors, screenElement, linkifier, bufferService, charSizeService, optionsService, characterJoinerService, coreService, coreBrowserService, decorationService);
|
||||
forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas);
|
||||
renderService.setRenderer(this._renderer);
|
||||
|
||||
@@ -25,19 +25,21 @@ export class WebglAddon implements ITerminalAddon {
|
||||
) {}
|
||||
|
||||
public activate(terminal: Terminal): void {
|
||||
const core = (terminal as any)._core;
|
||||
if (!terminal.element) {
|
||||
throw new Error('Cannot activate WebglAddon before Terminal.open');
|
||||
core.onWillOpen(() => this.activate(terminal));
|
||||
return;
|
||||
}
|
||||
if (isSafari) {
|
||||
throw new Error('Webgl is not currently supported on Safari');
|
||||
}
|
||||
this._terminal = terminal;
|
||||
const renderService: IRenderService = (terminal as any)._core._renderService;
|
||||
const characterJoinerService: ICharacterJoinerService = (terminal as any)._core._characterJoinerService;
|
||||
const coreBrowserService: ICoreBrowserService = (terminal as any)._core._coreBrowserService;
|
||||
const coreService: ICoreService = (terminal as any)._core.coreService;
|
||||
const decorationService: IDecorationService = (terminal as any)._core._decorationService;
|
||||
const colors: IColorSet = (terminal as any)._core._colorManager.colors;
|
||||
const renderService: IRenderService = core._renderService;
|
||||
const characterJoinerService: ICharacterJoinerService = core._characterJoinerService;
|
||||
const coreBrowserService: ICoreBrowserService = core._coreBrowserService;
|
||||
const coreService: ICoreService = core.coreService;
|
||||
const decorationService: IDecorationService = core._decorationService;
|
||||
const colors: IColorSet = core._colorManager.colors;
|
||||
this._renderer = new WebglRenderer(terminal, colors, characterJoinerService, coreBrowserService, coreService, decorationService, this._preserveDrawingBuffer);
|
||||
forwardEvent(this._renderer.onContextLoss, this._onContextLoss);
|
||||
forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas);
|
||||
|
||||
+7
-11
@@ -270,18 +270,14 @@ function createTerminal(): void {
|
||||
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
|
||||
socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/';
|
||||
|
||||
term.open(terminalContainer);
|
||||
addons.fit.instance!.fit();
|
||||
try {
|
||||
typedTerm.loadAddon(addons.webgl.instance);
|
||||
setTimeout(() => {
|
||||
addTextureAtlas(addons.webgl.instance.textureAtlas);
|
||||
addons.webgl.instance.onChangeTextureAtlas(e => addTextureAtlas(e));
|
||||
}, 0);
|
||||
}
|
||||
catch {
|
||||
addons.webgl.instance = undefined;
|
||||
}
|
||||
typedTerm.loadAddon(addons.webgl.instance);
|
||||
setTimeout(() => {
|
||||
addTextureAtlas(addons.webgl.instance.textureAtlas);
|
||||
addons.webgl.instance.onChangeTextureAtlas(e => addTextureAtlas(e));
|
||||
}, 0);
|
||||
|
||||
term.open(terminalContainer);
|
||||
term.focus();
|
||||
|
||||
addDomListener(paddingElement, 'change', setPadding);
|
||||
|
||||
+16
-10
@@ -135,14 +135,16 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
private readonly _onBell = new EventEmitter<void>();
|
||||
public readonly onBell = this._onBell.event;
|
||||
|
||||
private readonly _onFocus = new EventEmitter<void>();
|
||||
public readonly onFocus = this._onFocus.event;
|
||||
private readonly _onBlur = new EventEmitter<void>();
|
||||
public readonly onBlur = this._onBlur.event;
|
||||
private readonly _onA11yCharEmitter = new EventEmitter<string>();
|
||||
public readonly onA11yChar = this._onA11yCharEmitter.event;
|
||||
private readonly _onA11yTabEmitter = new EventEmitter<number>();
|
||||
public readonly onA11yTab = this._onA11yTabEmitter.event;
|
||||
private _onFocus = new EventEmitter<void>();
|
||||
public get onFocus(): IEvent<void> { return this._onFocus.event; }
|
||||
private _onBlur = new EventEmitter<void>();
|
||||
public get onBlur(): IEvent<void> { return this._onBlur.event; }
|
||||
private _onA11yCharEmitter = new EventEmitter<string>();
|
||||
public get onA11yChar(): IEvent<string> { return this._onA11yCharEmitter.event; }
|
||||
private _onA11yTabEmitter = new EventEmitter<number>();
|
||||
public get onA11yTab(): IEvent<number> { return this._onA11yTabEmitter.event; }
|
||||
private _onWillOpen = new EventEmitter<HTMLElement>();
|
||||
public get onWillOpen(): IEvent<HTMLElement> { return this._onWillOpen.event; }
|
||||
|
||||
/**
|
||||
* Creates a new `Terminal` object.
|
||||
@@ -509,8 +511,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this._characterJoinerService = this._instantiationService.createInstance(CharacterJoinerService);
|
||||
this._instantiationService.setService(ICharacterJoinerService, this._characterJoinerService);
|
||||
|
||||
const renderer = this._createRenderer();
|
||||
this._renderService = this.register(this._instantiationService.createInstance(RenderService, renderer, this.rows, this.screenElement));
|
||||
this._renderService = this.register(this._instantiationService.createInstance(RenderService, this.rows, this.screenElement));
|
||||
this._instantiationService.setService(IRenderService, this._renderService);
|
||||
this.register(this._renderService.onRenderedViewportChange(e => this._onRender.fire(e)));
|
||||
this.onResize(e => this._renderService!.resize(e.cols, e.rows));
|
||||
@@ -523,6 +524,11 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
// Performance: Add viewport and helper elements from the fragment
|
||||
this.element.appendChild(fragment);
|
||||
|
||||
this._onWillOpen.fire(this.element);
|
||||
if (!this._renderService.hasRenderer()) {
|
||||
this._renderService.setRenderer(this._createRenderer());
|
||||
}
|
||||
|
||||
this._mouseService = this._instantiationService.createInstance(MouseService);
|
||||
this._instantiationService.setService(IMouseService, this._mouseService);
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ export class MockTerminal implements ITerminal {
|
||||
public onTitleChange!: IEvent<string>;
|
||||
public onBell!: IEvent<void>;
|
||||
public onScroll!: IEvent<number>;
|
||||
public onWillOpen!: IEvent<HTMLElement>;
|
||||
public onKey!: IEvent<{ key: string, domEvent: KeyboardEvent }>;
|
||||
public onRender!: IEvent<{ start: number, end: number }>;
|
||||
public onResize!: IEvent<{ cols: number, rows: number }>;
|
||||
@@ -400,6 +401,9 @@ export class MockRenderService implements IRenderService {
|
||||
public resize(cols: number, rows: number): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public hasRenderer(): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public setRenderer(renderer: IRenderer): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -23,6 +23,7 @@ export interface ITerminal extends IPublicTerminal, ICoreTerminal {
|
||||
onFocus: IEvent<void>;
|
||||
onA11yChar: IEvent<string>;
|
||||
onA11yTab: IEvent<number>;
|
||||
onWillOpen: IEvent<HTMLElement>;
|
||||
|
||||
cancel(ev: Event, force?: boolean): boolean | void;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ interface ISelectionState {
|
||||
export class RenderService extends Disposable implements IRenderService {
|
||||
public serviceBrand: undefined;
|
||||
|
||||
private _renderer: IRenderer | undefined;
|
||||
private _renderDebouncer: IRenderDebouncerWithCallback;
|
||||
private _screenDprMonitor: ScreenDprMonitor;
|
||||
private _pausedResizeTask = new DebouncedIdleTask();
|
||||
@@ -48,10 +49,9 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
private readonly _onRefreshRequest = new EventEmitter<{ start: number, end: number }>();
|
||||
public readonly onRefreshRequest = this._onRefreshRequest.event;
|
||||
|
||||
public get dimensions(): IRenderDimensions { return this._renderer.dimensions; }
|
||||
public get dimensions(): IRenderDimensions { return this._renderer!.dimensions; }
|
||||
|
||||
constructor(
|
||||
private _renderer: IRenderer,
|
||||
private _rowCount: number,
|
||||
screenElement: HTMLElement,
|
||||
@IOptionsService optionsService: IOptionsService,
|
||||
@@ -62,7 +62,7 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
) {
|
||||
super();
|
||||
|
||||
this.register({ dispose: () => this._renderer.dispose() });
|
||||
this.register({ dispose: () => this._renderer?.dispose() });
|
||||
|
||||
this._renderDebouncer = new RenderDebouncer(coreBrowserService.window, (start, end) => this._renderRows(start, end));
|
||||
this.register(this._renderDebouncer);
|
||||
@@ -83,7 +83,7 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
this.register(decorationService.onDecorationRemoved(() => this._fullRefresh()));
|
||||
|
||||
// No need to register this as renderer is explicitly disposed in RenderService.dispose
|
||||
this._renderer.onRequestRedraw(e => this.refreshRows(e.start, e.end, true));
|
||||
// this._renderer.onRequestRedraw(e => this.refreshRows(e.start, e.end, true));
|
||||
|
||||
// dprchange should handle this case, we need this as well for browsers that don't support the
|
||||
// matchMedia query.
|
||||
@@ -125,6 +125,9 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
}
|
||||
|
||||
private _renderRows(start: number, end: number): void {
|
||||
if (!this._renderer) {
|
||||
return;
|
||||
}
|
||||
this._renderer.renderRows(start, end);
|
||||
|
||||
// Update selection if needed
|
||||
@@ -147,12 +150,18 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
}
|
||||
|
||||
private _handleOptionsChanged(): void {
|
||||
if (!this._renderer) {
|
||||
return;
|
||||
}
|
||||
this._renderer.onOptionsChanged();
|
||||
this.refreshRows(0, this._rowCount - 1);
|
||||
this._fireOnCanvasResize();
|
||||
}
|
||||
|
||||
private _fireOnCanvasResize(): void {
|
||||
if (!this._renderer) {
|
||||
return;
|
||||
}
|
||||
// Don't fire the event if the dimensions haven't changed
|
||||
if (this._renderer.dimensions.canvasWidth === this._canvasWidth && this._renderer.dimensions.canvasHeight === this._canvasHeight) {
|
||||
return;
|
||||
@@ -164,9 +173,13 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public hasRenderer(): boolean {
|
||||
return !!this._renderer;
|
||||
}
|
||||
|
||||
public setRenderer(renderer: IRenderer): void {
|
||||
// TODO: RenderService should be the only one to dispose the renderer
|
||||
this._renderer.dispose();
|
||||
this._renderer?.dispose();
|
||||
this._renderer = renderer;
|
||||
this._renderer.onRequestRedraw(e => this.refreshRows(e.start, e.end, true));
|
||||
|
||||
@@ -188,11 +201,17 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
}
|
||||
|
||||
public clearTextureAtlas(): void {
|
||||
this._renderer?.clearTextureAtlas?.();
|
||||
if (!this._renderer) {
|
||||
return;
|
||||
}
|
||||
this._renderer.clearTextureAtlas?.();
|
||||
this._fullRefresh();
|
||||
}
|
||||
|
||||
public setColors(colors: IColorSet): void {
|
||||
if (!this._renderer) {
|
||||
return;
|
||||
}
|
||||
this._renderer.setColors(colors);
|
||||
this._fullRefresh();
|
||||
}
|
||||
@@ -202,13 +221,19 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
// when devicePixelRatio changes
|
||||
this._charSizeService.measure();
|
||||
|
||||
if (!this._renderer) {
|
||||
return;
|
||||
}
|
||||
this._renderer.onDevicePixelRatioChange();
|
||||
this.refreshRows(0, this._rowCount - 1);
|
||||
}
|
||||
|
||||
public onResize(cols: number, rows: number): void {
|
||||
if (!this._renderer) {
|
||||
return;
|
||||
}
|
||||
if (this._isPaused) {
|
||||
this._pausedResizeTask.set(() => this._renderer.onResize(cols, rows));
|
||||
this._pausedResizeTask.set(() => this._renderer!.onResize(cols, rows));
|
||||
} else {
|
||||
this._renderer.onResize(cols, rows);
|
||||
}
|
||||
@@ -217,29 +242,29 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
|
||||
// TODO: Is this useful when we have onResize?
|
||||
public onCharSizeChanged(): void {
|
||||
this._renderer.onCharSizeChanged();
|
||||
this._renderer?.onCharSizeChanged();
|
||||
}
|
||||
|
||||
public onBlur(): void {
|
||||
this._renderer.onBlur();
|
||||
this._renderer?.onBlur();
|
||||
}
|
||||
|
||||
public onFocus(): void {
|
||||
this._renderer.onFocus();
|
||||
this._renderer?.onFocus();
|
||||
}
|
||||
|
||||
public onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
|
||||
this._selectionState.start = start;
|
||||
this._selectionState.end = end;
|
||||
this._selectionState.columnSelectMode = columnSelectMode;
|
||||
this._renderer.onSelectionChanged(start, end, columnSelectMode);
|
||||
this._renderer?.onSelectionChanged(start, end, columnSelectMode);
|
||||
}
|
||||
|
||||
public onCursorMove(): void {
|
||||
this._renderer.onCursorMove();
|
||||
this._renderer?.onCursorMove();
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._renderer.clear();
|
||||
this._renderer?.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ export interface IRenderService extends IDisposable {
|
||||
refreshRows(start: number, end: number): void;
|
||||
clearTextureAtlas(): void;
|
||||
resize(cols: number, rows: number): void;
|
||||
hasRenderer(): boolean;
|
||||
setRenderer(renderer: IRenderer): void;
|
||||
setColors(colors: IColorSet): void;
|
||||
onDevicePixelRatioChange(): void;
|
||||
|
||||
Reference in New Issue
Block a user