Merge remote-tracking branch 'upstream/master' into pr/JasonXJ/4742
This commit is contained in:
@@ -7,10 +7,10 @@ import * as Strings from 'browser/LocalizableStrings';
|
||||
import { ITerminal, IRenderDebouncer } from 'browser/Types';
|
||||
import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer';
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
|
||||
import { IRenderService } from 'browser/services/Services';
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
|
||||
import { IBuffer } from 'common/buffer/Types';
|
||||
import { IInstantiationService } from 'common/services/Services';
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
|
||||
const MAX_ROWS_TO_READ = 20;
|
||||
|
||||
@@ -35,8 +35,6 @@ export class AccessibilityManager extends Disposable {
|
||||
private _liveRegionLineCount: number = 0;
|
||||
private _liveRegionDebouncer: IRenderDebouncer;
|
||||
|
||||
private _screenDprMonitor: ScreenDprMonitor;
|
||||
|
||||
private _topBoundaryFocusListener: (e: FocusEvent) => void;
|
||||
private _bottomBoundaryFocusListener: (e: FocusEvent) => void;
|
||||
|
||||
@@ -55,13 +53,15 @@ export class AccessibilityManager extends Disposable {
|
||||
|
||||
constructor(
|
||||
private readonly _terminal: ITerminal,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
|
||||
@IRenderService private readonly _renderService: IRenderService
|
||||
) {
|
||||
super();
|
||||
this._accessibilityContainer = document.createElement('div');
|
||||
this._accessibilityContainer = this._coreBrowserService.mainDocument.createElement('div');
|
||||
this._accessibilityContainer.classList.add('xterm-accessibility');
|
||||
|
||||
this._rowContainer = document.createElement('div');
|
||||
this._rowContainer = this._coreBrowserService.mainDocument.createElement('div');
|
||||
this._rowContainer.setAttribute('role', 'list');
|
||||
this._rowContainer.classList.add('xterm-accessibility-tree');
|
||||
this._rowElements = [];
|
||||
@@ -78,7 +78,7 @@ export class AccessibilityManager extends Disposable {
|
||||
this._refreshRowsDimensions();
|
||||
this._accessibilityContainer.appendChild(this._rowContainer);
|
||||
|
||||
this._liveRegion = document.createElement('div');
|
||||
this._liveRegion = this._coreBrowserService.mainDocument.createElement('div');
|
||||
this._liveRegion.classList.add('live-region');
|
||||
this._liveRegion.setAttribute('aria-live', 'assertive');
|
||||
this._accessibilityContainer.appendChild(this._liveRegion);
|
||||
@@ -116,13 +116,7 @@ export class AccessibilityManager extends Disposable {
|
||||
this.register(this._terminal.onBlur(() => this._clearLiveRegion()));
|
||||
this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));
|
||||
this.register(addDisposableDomListener(document, 'selectionchange', () => this._handleSelectionChange()));
|
||||
|
||||
this._screenDprMonitor = new ScreenDprMonitor(window);
|
||||
this.register(this._screenDprMonitor);
|
||||
this._screenDprMonitor.setListener(() => this._refreshRowsDimensions());
|
||||
// This shouldn't be needed on modern browsers but is present in case the
|
||||
// media query that drives the ScreenDprMonitor isn't supported
|
||||
this.register(addDisposableDomListener(window, 'resize', () => this._refreshRowsDimensions()));
|
||||
this.register(this._coreBrowserService.onDprChange(() => this._refreshRowsDimensions()));
|
||||
|
||||
this._refreshRows();
|
||||
this.register(toDisposable(() => {
|
||||
@@ -389,7 +383,7 @@ export class AccessibilityManager extends Disposable {
|
||||
}
|
||||
|
||||
private _createAccessibilityTreeNode(): HTMLElement {
|
||||
const element = document.createElement('div');
|
||||
const element = this._coreBrowserService.mainDocument.createElement('div');
|
||||
element.setAttribute('role', 'listitem');
|
||||
element.tabIndex = -1;
|
||||
this._refreshRowDimensions(element);
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
|
||||
export type ScreenDprListener = (newDevicePixelRatio?: number, oldDevicePixelRatio?: number) => void;
|
||||
|
||||
/**
|
||||
* The screen device pixel ratio monitor allows listening for when the
|
||||
* window.devicePixelRatio value changes. This is done not with polling but with
|
||||
* the use of window.matchMedia to watch media queries. When the event fires,
|
||||
* the listener will be reattached using a different media query to ensure that
|
||||
* any further changes will register.
|
||||
*
|
||||
* The listener should fire on both window zoom changes and switching to a
|
||||
* monitor with a different DPI.
|
||||
*/
|
||||
export class ScreenDprMonitor extends Disposable {
|
||||
private _currentDevicePixelRatio: number;
|
||||
private _outerListener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | undefined;
|
||||
private _listener: ScreenDprListener | undefined;
|
||||
private _resolutionMediaMatchList: MediaQueryList | undefined;
|
||||
|
||||
constructor(private _parentWindow: Window) {
|
||||
super();
|
||||
this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio;
|
||||
this.register(toDisposable(() => {
|
||||
this.clearListener();
|
||||
}));
|
||||
}
|
||||
|
||||
public setListener(listener: ScreenDprListener): void {
|
||||
if (this._listener) {
|
||||
this.clearListener();
|
||||
}
|
||||
this._listener = listener;
|
||||
this._outerListener = () => {
|
||||
if (!this._listener) {
|
||||
return;
|
||||
}
|
||||
this._listener(this._parentWindow.devicePixelRatio, this._currentDevicePixelRatio);
|
||||
this._updateDpr();
|
||||
};
|
||||
this._updateDpr();
|
||||
}
|
||||
|
||||
private _updateDpr(): void {
|
||||
if (!this._outerListener) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear listeners for old DPR
|
||||
this._resolutionMediaMatchList?.removeListener(this._outerListener);
|
||||
|
||||
// Add listeners for new DPR
|
||||
this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio;
|
||||
this._resolutionMediaMatchList = this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`);
|
||||
this._resolutionMediaMatchList.addListener(this._outerListener);
|
||||
}
|
||||
|
||||
public clearListener(): void {
|
||||
if (!this._resolutionMediaMatchList || !this._listener || !this._outerListener) {
|
||||
return;
|
||||
}
|
||||
this._resolutionMediaMatchList.removeListener(this._outerListener);
|
||||
this._resolutionMediaMatchList = undefined;
|
||||
this._listener = undefined;
|
||||
this._outerListener = undefined;
|
||||
}
|
||||
}
|
||||
+28
-14
@@ -54,13 +54,10 @@ import { evaluateKeyboardEvent } from 'common/input/Keyboard';
|
||||
import { toRgbString } from 'common/input/XParseColor';
|
||||
import { DecorationService } from 'common/services/DecorationService';
|
||||
import { IDecorationService } from 'common/services/Services';
|
||||
import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker } from 'xterm';
|
||||
import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker } from '@xterm/xterm';
|
||||
import { WindowsOptionsReportType } from '../common/InputHandler';
|
||||
import { AccessibilityManager } from './AccessibilityManager';
|
||||
|
||||
// Let it work inside Node.js for automated testing purposes.
|
||||
const document: Document = (typeof window !== 'undefined') ? window.document : null as any;
|
||||
|
||||
export class Terminal extends CoreTerminal implements ITerminal {
|
||||
public textarea: HTMLTextAreaElement | undefined;
|
||||
public element: HTMLElement | undefined;
|
||||
@@ -397,7 +394,19 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this._logService.debug('Terminal.open was called on an element that was not attached to the DOM');
|
||||
}
|
||||
|
||||
this._document = parent.ownerDocument!;
|
||||
// If the terminal is already opened
|
||||
if (this.element?.ownerDocument.defaultView && this._coreBrowserService) {
|
||||
// Adjust the window if needed
|
||||
if (this.element.ownerDocument.defaultView !== this._coreBrowserService.window) {
|
||||
this._coreBrowserService.window = this.element.ownerDocument.defaultView;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._document = parent.ownerDocument;
|
||||
if (this.options.documentOverride && this.options.documentOverride instanceof Document) {
|
||||
this._document = this.optionsService.rawOptions.documentOverride as Document;
|
||||
}
|
||||
|
||||
// Create main element container
|
||||
this.element = this._document.createElement('div');
|
||||
@@ -408,25 +417,25 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
|
||||
// Performance: Use a document fragment to build the terminal
|
||||
// viewport and helper elements detached from the DOM
|
||||
const fragment = document.createDocumentFragment();
|
||||
this._viewportElement = document.createElement('div');
|
||||
const fragment = this._document.createDocumentFragment();
|
||||
this._viewportElement = this._document.createElement('div');
|
||||
this._viewportElement.classList.add('xterm-viewport');
|
||||
fragment.appendChild(this._viewportElement);
|
||||
|
||||
this._viewportScrollArea = document.createElement('div');
|
||||
this._viewportScrollArea = this._document.createElement('div');
|
||||
this._viewportScrollArea.classList.add('xterm-scroll-area');
|
||||
this._viewportElement.appendChild(this._viewportScrollArea);
|
||||
|
||||
this.screenElement = document.createElement('div');
|
||||
this.screenElement = this._document.createElement('div');
|
||||
this.screenElement.classList.add('xterm-screen');
|
||||
// Create the container that will hold helpers like the textarea for
|
||||
// capturing DOM Events. Then produce the helpers.
|
||||
this._helperContainer = document.createElement('div');
|
||||
this._helperContainer = this._document.createElement('div');
|
||||
this._helperContainer.classList.add('xterm-helpers');
|
||||
this.screenElement.appendChild(this._helperContainer);
|
||||
fragment.appendChild(this.screenElement);
|
||||
|
||||
this.textarea = document.createElement('textarea');
|
||||
this.textarea = this._document.createElement('textarea');
|
||||
this.textarea.classList.add('xterm-helper-textarea');
|
||||
this.textarea.setAttribute('aria-label', Strings.promptLabel);
|
||||
if (!Browser.isChromeOS) {
|
||||
@@ -441,7 +450,12 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
|
||||
// Register the core browser service before the generic textarea handlers are registered so it
|
||||
// handles them first. Otherwise the renderers may use the wrong focus state.
|
||||
this._coreBrowserService = this._instantiationService.createInstance(CoreBrowserService, this.textarea, this._document.defaultView ?? window);
|
||||
this._coreBrowserService = this.register(this._instantiationService.createInstance(CoreBrowserService,
|
||||
this.textarea,
|
||||
parent.ownerDocument.defaultView ?? window,
|
||||
// Force unsafe null in node.js environment for tests
|
||||
this._document ?? (typeof window !== 'undefined') ? window.document : null as any
|
||||
));
|
||||
this._instantiationService.setService(ICoreBrowserService, this._coreBrowserService);
|
||||
|
||||
this.register(addDisposableDomListener(this.textarea, 'focus', (ev: KeyboardEvent) => this._handleTextAreaFocus(ev)));
|
||||
@@ -463,7 +477,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this.register(this._renderService.onRenderedViewportChange(e => this._onRender.fire(e)));
|
||||
this.onResize(e => this._renderService!.resize(e.cols, e.rows));
|
||||
|
||||
this._compositionView = document.createElement('div');
|
||||
this._compositionView = this._document.createElement('div');
|
||||
this._compositionView.classList.add('composition-view');
|
||||
this._compositionHelper = this._instantiationService.createInstance(CompositionHelper, this.textarea, this._compositionView);
|
||||
this._helperContainer.appendChild(this._compositionView);
|
||||
@@ -561,7 +575,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
}
|
||||
|
||||
private _createRenderer(): IRenderer {
|
||||
return this._instantiationService.createInstance(DomRenderer, this._document!, this.element!, this.screenElement!, this._viewportElement!, this._helperContainer!, this.linkifier2);
|
||||
return this._instantiationService.createInstance(DomRenderer, this, this._document!, this.element!, this.screenElement!, this._viewportElement!, this._helperContainer!, this.linkifier2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import * as pty from 'node-pty';
|
||||
import { Terminal } from 'browser/Terminal';
|
||||
import { IDisposable } from 'xterm';
|
||||
import { IDisposable } from '@xterm/xterm';
|
||||
|
||||
// all test files expect terminal in 80x25
|
||||
const COLS = 80;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration } from 'xterm';
|
||||
import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration } from '@xterm/xterm';
|
||||
import { IEvent, EventEmitter } from 'common/EventEmitter';
|
||||
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
|
||||
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
|
||||
@@ -350,11 +350,16 @@ export class MockCompositionHelper implements ICompositionHelper {
|
||||
}
|
||||
|
||||
export class MockCoreBrowserService implements ICoreBrowserService {
|
||||
public onDprChange = new EventEmitter<number>().event;
|
||||
public onWindowChange = new EventEmitter<Window & typeof globalThis, void>().event;
|
||||
public serviceBrand: undefined;
|
||||
public isFocused: boolean = true;
|
||||
public get window(): Window & typeof globalThis {
|
||||
throw Error('Window object not available in tests');
|
||||
}
|
||||
public get mainDocument(): Document {
|
||||
throw Error('Document object not available in tests');
|
||||
}
|
||||
public dpr: number = 1;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -6,7 +6,7 @@
|
||||
import { IEvent } from 'common/EventEmitter';
|
||||
import { CharData, IColor, ICoreTerminal, ITerminalOptions } from 'common/Types';
|
||||
import { IBuffer } from 'common/buffer/Types';
|
||||
import { IDisposable, Terminal as ITerminalApi } from 'xterm';
|
||||
import { IDisposable, Terminal as ITerminalApi } from '@xterm/xterm';
|
||||
import { IMouseService, IRenderService } from './services/Services';
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
import { IRenderService } from 'browser/services/Services';
|
||||
import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
import { IBufferService, IDecorationService, IInternalDecoration } from 'common/services/Services';
|
||||
|
||||
@@ -19,6 +18,7 @@ export class BufferDecorationRenderer extends Disposable {
|
||||
constructor(
|
||||
private readonly _screenElement: HTMLElement,
|
||||
@IBufferService private readonly _bufferService: IBufferService,
|
||||
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
|
||||
@IDecorationService private readonly _decorationService: IDecorationService,
|
||||
@IRenderService private readonly _renderService: IRenderService
|
||||
) {
|
||||
@@ -33,7 +33,7 @@ export class BufferDecorationRenderer extends Disposable {
|
||||
this._dimensionsChanged = true;
|
||||
this._queueRefresh();
|
||||
}));
|
||||
this.register(addDisposableDomListener(window, 'resize', () => this._queueRefresh()));
|
||||
this.register(this._coreBrowserService.onDprChange(() => this._queueRefresh()));
|
||||
this.register(this._bufferService.buffers.onBufferActivate(() => {
|
||||
this._altBufferIsActive = this._bufferService.buffer === this._bufferService.buffers.alt;
|
||||
}));
|
||||
@@ -70,7 +70,7 @@ export class BufferDecorationRenderer extends Disposable {
|
||||
}
|
||||
|
||||
private _createElement(decoration: IInternalDecoration): HTMLElement {
|
||||
const element = document.createElement('div');
|
||||
const element = this._coreBrowserService.mainDocument.createElement('div');
|
||||
element.classList.add('xterm-decoration');
|
||||
element.classList.toggle('xterm-decoration-top-layer', decoration?.options?.layer === 'top');
|
||||
element.style.width = `${Math.round((decoration.options.width || 1) * this._renderService.dimensions.css.cell.width)}px`;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore';
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
|
||||
@@ -52,10 +51,10 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
@IDecorationService private readonly _decorationService: IDecorationService,
|
||||
@IRenderService private readonly _renderService: IRenderService,
|
||||
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||
@ICoreBrowserService private readonly _coreBrowseService: ICoreBrowserService
|
||||
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService
|
||||
) {
|
||||
super();
|
||||
this._canvas = document.createElement('canvas');
|
||||
this._canvas = this._coreBrowserService.mainDocument.createElement('canvas');
|
||||
this._canvas.classList.add('xterm-decoration-overview-ruler');
|
||||
this._refreshCanvasDimensions();
|
||||
this._viewportElement.parentElement?.insertBefore(this._canvas, this._viewportElement);
|
||||
@@ -112,7 +111,7 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
// overview ruler width changed
|
||||
this.register(this._optionsService.onSpecificOptionChange('overviewRulerWidth', () => this._queueRefresh(true)));
|
||||
// device pixel ratio changed
|
||||
this.register(addDisposableDomListener(this._coreBrowseService.window, 'resize', () => this._queueRefresh(true)));
|
||||
this.register(this._coreBrowserService.onDprChange(() => this._queueRefresh(true)));
|
||||
// set the canvas dimensions
|
||||
this._queueRefresh(true);
|
||||
}
|
||||
@@ -135,11 +134,11 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
}
|
||||
|
||||
private _refreshDrawHeightConstants(): void {
|
||||
drawHeight.full = Math.round(2 * this._coreBrowseService.dpr);
|
||||
drawHeight.full = Math.round(2 * this._coreBrowserService.dpr);
|
||||
// Calculate actual pixels per line
|
||||
const pixelsPerLine = this._canvas.height / this._bufferService.buffer.lines.length;
|
||||
// Clamp actual pixels within a range
|
||||
const nonFullHeight = Math.round(Math.max(Math.min(pixelsPerLine, 12), 6) * this._coreBrowseService.dpr);
|
||||
const nonFullHeight = Math.round(Math.max(Math.min(pixelsPerLine, 12), 6) * this._coreBrowserService.dpr);
|
||||
drawHeight.left = nonFullHeight;
|
||||
drawHeight.center = nonFullHeight;
|
||||
drawHeight.right = nonFullHeight;
|
||||
@@ -157,9 +156,9 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
|
||||
private _refreshCanvasDimensions(): void {
|
||||
this._canvas.style.width = `${this._width}px`;
|
||||
this._canvas.width = Math.round(this._width * this._coreBrowseService.dpr);
|
||||
this._canvas.width = Math.round(this._width * this._coreBrowserService.dpr);
|
||||
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
|
||||
this._canvas.height = Math.round(this._screenElement.clientHeight * this._coreBrowseService.dpr);
|
||||
this._canvas.height = Math.round(this._screenElement.clientHeight * this._coreBrowserService.dpr);
|
||||
this._refreshDrawConstants();
|
||||
this._refreshColorZonePadding();
|
||||
}
|
||||
@@ -211,7 +210,7 @@ export class OverviewRulerRenderer extends Disposable {
|
||||
if (this._animationFrame !== undefined) {
|
||||
return;
|
||||
}
|
||||
this._animationFrame = this._coreBrowseService.window.requestAnimationFrame(() => {
|
||||
this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
|
||||
this._refreshDecorations();
|
||||
this._animationFrame = undefined;
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ import { AddonManager } from 'common/public/AddonManager';
|
||||
import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi';
|
||||
import { ParserApi } from 'common/public/ParserApi';
|
||||
import { UnicodeApi } from 'common/public/UnicodeApi';
|
||||
import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from 'xterm';
|
||||
import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm';
|
||||
|
||||
/**
|
||||
* The set of options that only have an effect when set in the Terminal constructor.
|
||||
|
||||
@@ -7,9 +7,10 @@ import { DomRendererRowFactory, RowCss } from 'browser/renderer/dom/DomRendererR
|
||||
import { WidthCache } from 'browser/renderer/dom/WidthCache';
|
||||
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants';
|
||||
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
|
||||
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
|
||||
import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel';
|
||||
import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
||||
import { ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
||||
import { ILinkifier2, ILinkifierEvent, ReadonlyColorSet } from 'browser/Types';
|
||||
import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from 'browser/Types';
|
||||
import { color } from 'common/Color';
|
||||
import { EventEmitter } from 'common/EventEmitter';
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
@@ -25,7 +26,6 @@ const SELECTION_CLASS = 'xterm-selection';
|
||||
|
||||
let nextTerminalId = 1;
|
||||
|
||||
|
||||
/**
|
||||
* A fallback renderer for when canvas is slow. This is not meant to be
|
||||
* particularly fast or feature complete, more just stable and usable for when
|
||||
@@ -41,12 +41,14 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
private _rowElements: HTMLElement[] = [];
|
||||
private _selectionContainer: HTMLElement;
|
||||
private _widthCache: WidthCache;
|
||||
private _selectionRenderModel: ISelectionRenderModel = createSelectionRenderModel();
|
||||
|
||||
public dimensions: IRenderDimensions;
|
||||
|
||||
public readonly onRequestRedraw = this.register(new EventEmitter<IRequestRedrawEvent>()).event;
|
||||
|
||||
constructor(
|
||||
private readonly _terminal: ITerminal,
|
||||
private readonly _document: Document,
|
||||
private readonly _element: HTMLElement,
|
||||
private readonly _screenElement: HTMLElement,
|
||||
@@ -291,6 +293,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
public handleResize(cols: number, rows: number): void {
|
||||
this._refreshRowElements(cols, rows);
|
||||
this._updateDimensions();
|
||||
this.handleSelectionChanged(this._selectionRenderModel.selectionStart, this._selectionRenderModel.selectionEnd, this._selectionRenderModel.columnSelectMode);
|
||||
}
|
||||
|
||||
public handleCharSizeChanged(): void {
|
||||
@@ -320,11 +323,13 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
return;
|
||||
}
|
||||
|
||||
this._selectionRenderModel.update(this._terminal, start, end, columnSelectMode);
|
||||
|
||||
// Translate from buffer position to viewport position
|
||||
const viewportStartRow = start[1] - this._bufferService.buffer.ydisp;
|
||||
const viewportEndRow = end[1] - this._bufferService.buffer.ydisp;
|
||||
const viewportCappedStartRow = Math.max(viewportStartRow, 0);
|
||||
const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1);
|
||||
const viewportStartRow = this._selectionRenderModel.viewportStartRow;
|
||||
const viewportEndRow = this._selectionRenderModel.viewportEndRow;
|
||||
const viewportCappedStartRow = this._selectionRenderModel.viewportCappedStartRow;
|
||||
const viewportCappedEndRow = this._selectionRenderModel.viewportCappedEndRow;
|
||||
|
||||
// No need to draw the selection
|
||||
if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) {
|
||||
@@ -365,10 +370,16 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
*/
|
||||
private _createSelectionElement(row: number, colStart: number, colEnd: number, rowCount: number = 1): HTMLElement {
|
||||
const element = this._document.createElement('div');
|
||||
const left = colStart * this.dimensions.css.cell.width;
|
||||
let width = this.dimensions.css.cell.width * (colEnd - colStart);
|
||||
if (left + width > this.dimensions.css.canvas.width) {
|
||||
width = this.dimensions.css.canvas.width - left;
|
||||
}
|
||||
|
||||
element.style.height = `${rowCount * this.dimensions.css.cell.height}px`;
|
||||
element.style.top = `${row * this.dimensions.css.cell.height}px`;
|
||||
element.style.left = `${colStart * this.dimensions.css.cell.width}px`;
|
||||
element.style.width = `${this.dimensions.css.cell.width * (colEnd - colStart)}px`;
|
||||
element.style.left = `${left}px`;
|
||||
element.style.width = `${width}px`;
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
||||
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
||||
import { ReadonlyColorSet } from 'browser/Types';
|
||||
import { Attributes, BgFlags, FgFlags } from 'common/buffer/Constants';
|
||||
import { IDecorationService } from 'common/services/Services';
|
||||
import { Attributes, BgFlags, ExtFlags, FgFlags, NULL_CELL_CODE, UnderlineStyle } from 'common/buffer/Constants';
|
||||
import { IDecorationService, IOptionsService } from 'common/services/Services';
|
||||
import { ICellData } from 'common/Types';
|
||||
import { Terminal } from 'xterm';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
|
||||
// Work variables to avoid garbage collection
|
||||
let $fg = 0;
|
||||
@@ -13,6 +13,7 @@ let $hasFg = false;
|
||||
let $hasBg = false;
|
||||
let $isSelected = false;
|
||||
let $colors: ReadonlyColorSet | undefined;
|
||||
let $variantOffset = 0;
|
||||
|
||||
export class CellColorResolver {
|
||||
/**
|
||||
@@ -27,6 +28,7 @@ export class CellColorResolver {
|
||||
|
||||
constructor(
|
||||
private readonly _terminal: Terminal,
|
||||
private readonly _optionService: IOptionsService,
|
||||
private readonly _selectionRenderModel: ISelectionRenderModel,
|
||||
private readonly _decorationService: IDecorationService,
|
||||
private readonly _coreBrowserService: ICoreBrowserService,
|
||||
@@ -38,7 +40,7 @@ export class CellColorResolver {
|
||||
* Resolves colors for the cell, putting the result into the shared {@link result}. This resolves
|
||||
* overrides, inverse and selection for the cell which can then be used to feed into the renderer.
|
||||
*/
|
||||
public resolve(cell: ICellData, x: number, y: number): void {
|
||||
public resolve(cell: ICellData, x: number, y: number, deviceCellWidth: number): void {
|
||||
this.result.bg = cell.bg;
|
||||
this.result.fg = cell.fg;
|
||||
this.result.ext = cell.bg & BgFlags.HAS_EXTENDED ? cell.extended.ext : 0;
|
||||
@@ -52,6 +54,13 @@ export class CellColorResolver {
|
||||
$hasFg = false;
|
||||
$isSelected = false;
|
||||
$colors = this._themeService.colors;
|
||||
$variantOffset = 0;
|
||||
|
||||
const code = cell.getCode();
|
||||
if (code !== NULL_CELL_CODE && cell.extended.underlineStyle === UnderlineStyle.DOTTED) {
|
||||
const lineWidth = Math.max(1, Math.floor(this._optionService.rawOptions.fontSize * this._coreBrowserService.dpr / 15));
|
||||
$variantOffset = x * deviceCellWidth % (Math.round(lineWidth) * 2);
|
||||
}
|
||||
|
||||
// Apply decorations on the bottom layer
|
||||
this._decorationService.forEachDecorationAtCell(x, y, 'bottom', d => {
|
||||
@@ -133,5 +142,9 @@ export class CellColorResolver {
|
||||
// Use the override if it exists
|
||||
this.result.bg = $hasBg ? $bg : this.result.bg;
|
||||
this.result.fg = $hasFg ? $fg : this.result.fg;
|
||||
|
||||
// Reset overrides variantOffset
|
||||
this.result.ext &= ~ExtFlags.VARIANT_OFFSET;
|
||||
this.result.ext |= ($variantOffset << 29) & ExtFlags.VARIANT_OFFSET;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { TextureAtlas } from 'browser/renderer/shared/TextureAtlas';
|
||||
import { ITerminalOptions, Terminal } from 'xterm';
|
||||
import { ITerminalOptions, Terminal } from '@xterm/xterm';
|
||||
import { ITerminal, ReadonlyColorSet } from 'browser/Types';
|
||||
import { ICharAtlasConfig, ITextureAtlas } from 'browser/renderer/shared/Types';
|
||||
import { generateConfig, configEquals } from 'browser/renderer/shared/CharAtlasUtils';
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { ICharAtlasConfig } from './Types';
|
||||
import { Attributes } from 'common/buffer/Constants';
|
||||
import { ITerminalOptions } from 'xterm';
|
||||
import { ITerminalOptions } from '@xterm/xterm';
|
||||
import { IColorSet, ReadonlyColorSet } from 'browser/Types';
|
||||
import { NULL_COLOR } from 'common/Color';
|
||||
|
||||
|
||||
@@ -474,7 +474,7 @@ function drawPatternChar(
|
||||
if (!pattern) {
|
||||
const width = charDefinition[0].length;
|
||||
const height = charDefinition.length;
|
||||
const tmpCanvas = document.createElement('canvas');
|
||||
const tmpCanvas = ctx.canvas.ownerDocument.createElement('canvas');
|
||||
tmpCanvas.width = width;
|
||||
tmpCanvas.height = height;
|
||||
const tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d'));
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2023 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { computeNextVariantOffset } from 'browser/renderer/shared/RendererUtils';
|
||||
import { assert } from 'chai';
|
||||
|
||||
describe('RendererUtils', () => {
|
||||
it('computeNextVariantOffset', () => {
|
||||
const cellWidth = 11;
|
||||
const doubleCellWidth = 22;
|
||||
let line = 1;
|
||||
let variantOffset = 0;
|
||||
|
||||
// should line 1
|
||||
// =,_,=_,=_,
|
||||
let cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth];
|
||||
let result = [1, 0, 0, 0];
|
||||
for (let index = 0; index < cells.length; index++) {
|
||||
const cell = cells[index];
|
||||
variantOffset = computeNextVariantOffset(cell, line, variantOffset);
|
||||
assert.equal(variantOffset, result[index]);
|
||||
}
|
||||
|
||||
// should line 2
|
||||
// ==__==__==_,_==__==__==,__==__==__==__==__==__,==__==__==__==__==__==,
|
||||
line = 2;
|
||||
variantOffset = 0;
|
||||
cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth];
|
||||
result = [3, 2, 0 ,2];
|
||||
for (let index = 0; index < cells.length; index++) {
|
||||
const cell = cells[index];
|
||||
variantOffset = computeNextVariantOffset(cell, line, variantOffset);
|
||||
assert.equal(variantOffset, result[index]);
|
||||
}
|
||||
|
||||
// should line 3
|
||||
// ===___===__,_===___===_,__===___===___===___==,=___===___===___===___,
|
||||
line = 3;
|
||||
variantOffset = 0;
|
||||
cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth];
|
||||
result = [5, 4, 2, 0];
|
||||
for (let index = 0; index < cells.length; index++) {
|
||||
const cell = cells[index];
|
||||
variantOffset = computeNextVariantOffset(cell, line, variantOffset);
|
||||
assert.equal(variantOffset, result[index]);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -56,3 +56,7 @@ function createDimension(): IDimensions {
|
||||
height: 0
|
||||
};
|
||||
}
|
||||
|
||||
export function computeNextVariantOffset(cellWidth: number, lineWidth: number, currentOffset: number = 0): number {
|
||||
return (cellWidth - (Math.round(lineWidth) * 2 - currentOffset)) % (Math.round(lineWidth) * 2);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ITerminal } from 'browser/Types';
|
||||
import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
||||
import { Terminal } from 'xterm';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
|
||||
class SelectionRenderModel implements ISelectionRenderModel {
|
||||
public hasSelection!: boolean;
|
||||
@@ -35,7 +36,7 @@ class SelectionRenderModel implements ISelectionRenderModel {
|
||||
this.selectionEnd = undefined;
|
||||
}
|
||||
|
||||
public update(terminal: Terminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {
|
||||
public update(terminal: ITerminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {
|
||||
this.selectionStart = start;
|
||||
this.selectionEnd = end;
|
||||
// Selection does not exist
|
||||
@@ -45,8 +46,9 @@ class SelectionRenderModel implements ISelectionRenderModel {
|
||||
}
|
||||
|
||||
// Translate from buffer position to viewport position
|
||||
const viewportStartRow = start[1] - terminal.buffer.active.viewportY;
|
||||
const viewportEndRow = end[1] - terminal.buffer.active.viewportY;
|
||||
const viewportY = terminal.buffers.active.ydisp;
|
||||
const viewportStartRow = start[1] - viewportY;
|
||||
const viewportEndRow = end[1] - viewportY;
|
||||
const viewportCappedStartRow = Math.max(viewportStartRow, 0);
|
||||
const viewportCappedEndRow = Math.min(viewportEndRow, terminal.rows - 1);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { IColorContrastCache } from 'browser/Types';
|
||||
import { DIM_OPACITY, TEXT_BASELINE } from 'browser/renderer/shared/Constants';
|
||||
import { tryDrawCustomChar } from 'browser/renderer/shared/CustomGlyphs';
|
||||
import { excludeFromContrastRatioDemands, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
|
||||
import { computeNextVariantOffset, excludeFromContrastRatioDemands, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
|
||||
import { IBoundingBox, ICharAtlasConfig, IRasterizedGlyph, ITextureAtlas } from 'browser/renderer/shared/Types';
|
||||
import { NULL_COLOR, color, rgba } from 'common/Color';
|
||||
import { EventEmitter } from 'common/EventEmitter';
|
||||
@@ -545,6 +545,7 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
const yTop = Math.ceil(padding + this._config.deviceCharHeight) - yOffset - (restrictToCellHeight ? lineWidth * 2 : 0);
|
||||
const yMid = yTop + lineWidth;
|
||||
const yBot = yTop + lineWidth * 2;
|
||||
let nextOffset = this._workAttributeData.getUnderlineVariantOffset();
|
||||
|
||||
for (let i = 0; i < chWidth; i++) {
|
||||
this._tmpCtx.save();
|
||||
@@ -594,9 +595,22 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
);
|
||||
break;
|
||||
case UnderlineStyle.DOTTED:
|
||||
this._tmpCtx.setLineDash([Math.round(lineWidth), Math.round(lineWidth)]);
|
||||
this._tmpCtx.moveTo(xChLeft, yTop);
|
||||
this._tmpCtx.lineTo(xChRight, yTop);
|
||||
const offsetWidth = nextOffset === 0 ? 0 :
|
||||
(nextOffset >= lineWidth ? lineWidth * 2 - nextOffset : lineWidth - nextOffset);
|
||||
// a line and a gap.
|
||||
const isLineStart = nextOffset >= lineWidth ? false : true;
|
||||
if (isLineStart === false || offsetWidth === 0) {
|
||||
this._tmpCtx.setLineDash([Math.round(lineWidth), Math.round(lineWidth)]);
|
||||
this._tmpCtx.moveTo(xChLeft + offsetWidth, yTop);
|
||||
this._tmpCtx.lineTo(xChRight, yTop);
|
||||
} else {
|
||||
this._tmpCtx.setLineDash([Math.round(lineWidth), Math.round(lineWidth)]);
|
||||
this._tmpCtx.moveTo(xChLeft, yTop);
|
||||
this._tmpCtx.lineTo(xChLeft + offsetWidth, yTop);
|
||||
this._tmpCtx.moveTo(xChLeft + offsetWidth + lineWidth, yTop);
|
||||
this._tmpCtx.lineTo(xChRight, yTop);
|
||||
}
|
||||
nextOffset = computeNextVariantOffset(xChRight - xChLeft, lineWidth, nextOffset);
|
||||
break;
|
||||
case UnderlineStyle.DASHED:
|
||||
this._tmpCtx.setLineDash([this._config.devicePixelRatio * 4, this._config.devicePixelRatio * 3]);
|
||||
|
||||
+3
-3
@@ -3,8 +3,8 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { FontWeight, Terminal } from 'xterm';
|
||||
import { IColorSet } from 'browser/Types';
|
||||
import { FontWeight, Terminal } from '@xterm/xterm';
|
||||
import { IColorSet, ITerminal } from 'browser/Types';
|
||||
import { IDisposable } from 'common/Types';
|
||||
import { IEvent } from 'common/EventEmitter';
|
||||
|
||||
@@ -168,6 +168,6 @@ export interface ISelectionRenderModel {
|
||||
readonly selectionStart: [number, number] | undefined;
|
||||
readonly selectionEnd: [number, number] | undefined;
|
||||
clear(): void;
|
||||
update(terminal: Terminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode?: boolean): void;
|
||||
update(terminal: ITerminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode?: boolean): void;
|
||||
isCellSelected(terminal: Terminal, x: number, y: number): boolean;
|
||||
}
|
||||
|
||||
@@ -3,22 +3,49 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle';
|
||||
import { ICoreBrowserService } from './Services';
|
||||
import { EventEmitter, forwardEvent } from 'common/EventEmitter';
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
|
||||
export class CoreBrowserService implements ICoreBrowserService {
|
||||
export class CoreBrowserService extends Disposable implements ICoreBrowserService {
|
||||
public serviceBrand: undefined;
|
||||
|
||||
private _isFocused = false;
|
||||
private _cachedIsFocused: boolean | undefined = undefined;
|
||||
private _screenDprMonitor = new ScreenDprMonitor(this._window);
|
||||
|
||||
private readonly _onDprChange = this.register(new EventEmitter<number>());
|
||||
public readonly onDprChange = this._onDprChange.event;
|
||||
private readonly _onWindowChange = this.register(new EventEmitter<Window & typeof globalThis>());
|
||||
public readonly onWindowChange = this._onWindowChange.event;
|
||||
|
||||
constructor(
|
||||
private _textarea: HTMLTextAreaElement,
|
||||
public readonly window: Window & typeof globalThis
|
||||
private _window: Window & typeof globalThis,
|
||||
public readonly mainDocument: Document
|
||||
) {
|
||||
super();
|
||||
|
||||
// Monitor device pixel ratio
|
||||
this.register(this.onWindowChange(w => this._screenDprMonitor.setWindow(w)));
|
||||
this.register(forwardEvent(this._screenDprMonitor.onDprChange, this._onDprChange));
|
||||
|
||||
this._textarea.addEventListener('focus', () => this._isFocused = true);
|
||||
this._textarea.addEventListener('blur', () => this._isFocused = false);
|
||||
}
|
||||
|
||||
public get window(): Window & typeof globalThis {
|
||||
return this._window;
|
||||
}
|
||||
|
||||
public set window(value: Window & typeof globalThis) {
|
||||
if (this._window !== value) {
|
||||
this._window = value;
|
||||
this._onWindowChange.fire(this._window);
|
||||
}
|
||||
}
|
||||
|
||||
public get dpr(): number {
|
||||
return this.window.devicePixelRatio;
|
||||
}
|
||||
@@ -31,3 +58,80 @@ export class CoreBrowserService implements ICoreBrowserService {
|
||||
return this._cachedIsFocused;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The screen device pixel ratio monitor allows listening for when the
|
||||
* window.devicePixelRatio value changes. This is done not with polling but with
|
||||
* the use of window.matchMedia to watch media queries. When the event fires,
|
||||
* the listener will be reattached using a different media query to ensure that
|
||||
* any further changes will register.
|
||||
*
|
||||
* The listener should fire on both window zoom changes and switching to a
|
||||
* monitor with a different DPI.
|
||||
*/
|
||||
class ScreenDprMonitor extends Disposable {
|
||||
private _currentDevicePixelRatio: number;
|
||||
private _outerListener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | undefined;
|
||||
private _resolutionMediaMatchList: MediaQueryList | undefined;
|
||||
private _windowResizeListener = this.register(new MutableDisposable());
|
||||
|
||||
private readonly _onDprChange = this.register(new EventEmitter<number>());
|
||||
public readonly onDprChange = this._onDprChange.event;
|
||||
|
||||
constructor(private _parentWindow: Window) {
|
||||
super();
|
||||
|
||||
// Initialize listener and dpr value
|
||||
this._outerListener = () => this._setDprAndFireIfDiffers();
|
||||
this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio;
|
||||
this._updateDpr();
|
||||
|
||||
// Monitor active window resize
|
||||
this._setWindowResizeListener();
|
||||
|
||||
// Setup additional disposables
|
||||
this.register(toDisposable(() => this.clearListener()));
|
||||
}
|
||||
|
||||
|
||||
public setWindow(parentWindow: Window): void {
|
||||
this._parentWindow = parentWindow;
|
||||
this._setWindowResizeListener();
|
||||
this._setDprAndFireIfDiffers();
|
||||
}
|
||||
|
||||
private _setWindowResizeListener(): void {
|
||||
this._windowResizeListener.value = addDisposableDomListener(this._parentWindow, 'resize', () => this._setDprAndFireIfDiffers());
|
||||
}
|
||||
|
||||
private _setDprAndFireIfDiffers(): void {
|
||||
if (this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) {
|
||||
this._onDprChange.fire(this._parentWindow.devicePixelRatio);
|
||||
}
|
||||
this._updateDpr();
|
||||
}
|
||||
|
||||
private _updateDpr(): void {
|
||||
if (!this._outerListener) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear listeners for old DPR
|
||||
this._resolutionMediaMatchList?.removeListener(this._outerListener);
|
||||
|
||||
// Add listeners for new DPR
|
||||
this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio;
|
||||
this._resolutionMediaMatchList = this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`);
|
||||
this._resolutionMediaMatchList.addListener(this._outerListener);
|
||||
}
|
||||
|
||||
public clearListener(): void {
|
||||
if (!this._resolutionMediaMatchList || !this._outerListener) {
|
||||
return;
|
||||
}
|
||||
this._resolutionMediaMatchList.removeListener(this._outerListener);
|
||||
this._resolutionMediaMatchList = undefined;
|
||||
this._outerListener = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,14 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
import { RenderDebouncer } from 'browser/RenderDebouncer';
|
||||
import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
|
||||
import { IRenderDebouncerWithCallback } from 'browser/Types';
|
||||
import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types';
|
||||
import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
|
||||
import { EventEmitter } from 'common/EventEmitter';
|
||||
import { Disposable, MutableDisposable } from 'common/Lifecycle';
|
||||
import { DebouncedIdleTask } from 'common/TaskQueue';
|
||||
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
|
||||
import { IBufferService, IDecorationService, IInstantiationService, IOptionsService } from 'common/services/Services';
|
||||
|
||||
interface ISelectionState {
|
||||
start: [number, number] | undefined;
|
||||
@@ -25,7 +23,6 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
|
||||
private _renderer: MutableDisposable<IRenderer> = this.register(new MutableDisposable());
|
||||
private _renderDebouncer: IRenderDebouncerWithCallback;
|
||||
private _screenDprMonitor: ScreenDprMonitor;
|
||||
private _pausedResizeTask = new DebouncedIdleTask();
|
||||
|
||||
private _isPaused: boolean = false;
|
||||
@@ -59,6 +56,7 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
@IDecorationService decorationService: IDecorationService,
|
||||
@IBufferService bufferService: IBufferService,
|
||||
@ICoreBrowserService coreBrowserService: ICoreBrowserService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService
|
||||
) {
|
||||
super();
|
||||
@@ -66,9 +64,7 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
this._renderDebouncer = new RenderDebouncer(coreBrowserService.window, (start, end) => this._renderRows(start, end));
|
||||
this.register(this._renderDebouncer);
|
||||
|
||||
this._screenDprMonitor = new ScreenDprMonitor(coreBrowserService.window);
|
||||
this._screenDprMonitor.setListener(() => this.handleDevicePixelRatioChange());
|
||||
this.register(this._screenDprMonitor);
|
||||
this.register(coreBrowserService.onDprChange(() => this.handleDevicePixelRatioChange()));
|
||||
|
||||
this.register(bufferService.onResize(() => this._fullRefresh()));
|
||||
this.register(bufferService.buffers.onBufferActivate(() => this._renderer.value?.clear()));
|
||||
@@ -104,10 +100,6 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
'cursorStyle'
|
||||
], () => this.refreshRows(bufferService.buffer.y, bufferService.buffer.y, true)));
|
||||
|
||||
// dprchange should handle this case, we need this as well for browsers that don't support the
|
||||
// matchMedia query.
|
||||
this.register(addDisposableDomListener(coreBrowserService.window, 'resize', () => this.handleDevicePixelRatioChange()));
|
||||
|
||||
this.register(themeService.onChangeColors(() => this._fullRefresh()));
|
||||
|
||||
// Detect whether IntersectionObserver is detected and enable renderer pause
|
||||
|
||||
@@ -28,12 +28,21 @@ export interface ICoreBrowserService {
|
||||
serviceBrand: undefined;
|
||||
|
||||
readonly isFocused: boolean;
|
||||
|
||||
readonly onDprChange: IEvent<number>;
|
||||
readonly onWindowChange: IEvent<Window & typeof globalThis>;
|
||||
|
||||
/**
|
||||
* Parent window that the terminal is rendered into. DOM and rendering APIs
|
||||
* (e.g. requestAnimationFrame) should be invoked in the context of this
|
||||
* window.
|
||||
* Gets or sets the parent window that the terminal is rendered into. DOM and rendering APIs (e.g.
|
||||
* requestAnimationFrame) should be invoked in the context of this window. This should be set when
|
||||
* the window hosting the xterm.js instance changes.
|
||||
*/
|
||||
readonly window: Window & typeof globalThis;
|
||||
window: Window & typeof globalThis;
|
||||
/**
|
||||
* The document of the primary window to be used to create elements when working with multiple
|
||||
* windows. This is defined by the documentOverride setting.
|
||||
*/
|
||||
readonly mainDocument: Document;
|
||||
/**
|
||||
* Helper for getting the devicePixelRatio of the parent window.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user