diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index 3f49c825..f0ce4694 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -1230,8 +1230,11 @@ export class Terminal extends CoreTerminal implements ITerminal { for (let i = 1; i < this.rows; i++) { this.buffer.lines.push(this.buffer.getBlankLine(DEFAULT_ATTR_DATA)); } - this.refresh(0, this.rows - 1); + // IMPORTANT: Fire scroll event before viewport is reset. This ensures embedders get the clear + // scroll event and that the viewport's state will be valid for immediate writes. this._onScroll.fire({ position: this.buffer.ydisp, source: ScrollSource.TERMINAL }); + this.viewport?.reset(); + this.refresh(0, this.rows - 1); } /** @@ -1255,13 +1258,13 @@ export class Terminal extends CoreTerminal implements ITerminal { super.reset(); this._selectionService?.reset(); this._decorationService.reset(); + this.viewport?.reset(); // reattach this._customKeyEventHandler = customKeyEventHandler; // do a full screen refresh this.refresh(0, this.rows - 1); - this.viewport?.syncScrollArea(); } public clearTextureAtlas(): void { diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index c04c9af2..2015d046 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -324,6 +324,8 @@ export class MockViewport implements IViewport { public scrollLines(disp: number): void { this._onRequestScrollLines.fire({ amount: disp, suppressScrollEvent: false }); } + public reset(): void { + } } export class MockCompositionHelper implements ICompositionHelper { diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts index 42d56c06..5337a169 100644 --- a/src/browser/Types.d.ts +++ b/src/browser/Types.d.ts @@ -161,13 +161,14 @@ export interface IPartialColorSet { export interface IViewport extends IDisposable { scrollBarWidth: number; readonly onRequestScrollLines: IEvent<{ amount: number, suppressScrollEvent: boolean }>; - syncScrollArea(immediate?: boolean): void; + syncScrollArea(immediate?: boolean, force?: boolean): void; getLinesScrolled(ev: WheelEvent): number; getBufferElements(startLine: number, endLine?: number): { bufferElements: HTMLElement[], cursorElement?: HTMLElement }; handleWheel(ev: WheelEvent): boolean; handleTouchStart(ev: TouchEvent): void; handleTouchMove(ev: TouchEvent): boolean; scrollLines(disp: number): void; // todo api name? + reset(): void; } export interface ILinkifierEvent { diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index a30228a4..48e97341 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -81,13 +81,25 @@ export class Viewport extends Disposable implements IViewport { this.register(this._optionsService.onSpecificOptionChange('scrollback', () => this.syncScrollArea())); // Perform this async to ensure the ICharSizeService is ready. - setTimeout(() => this.syncScrollArea(), 0); + setTimeout(() => this.syncScrollArea()); } private _handleThemeChange(colors: ReadonlyColorSet): void { this._viewportElement.style.backgroundColor = colors.background.css; } + public reset(): void { + this._currentRowHeight = 0; + this._currentDeviceCellHeight = 0; + this._lastRecordedBufferLength = 0; + this._lastRecordedViewportHeight = 0; + this._lastRecordedBufferHeight = 0; + this._lastTouchY = 0; + this._lastScrollTop = 0; + // Sync on next animation frame to ensure the new terminal state is used + this._coreBrowserService.window.requestAnimationFrame(() => this.syncScrollArea()); + } + /** * Refreshes row height, setting line-height, viewport height and scroll area height if * necessary.