diff --git a/src/Terminal.ts b/src/Terminal.ts index 7abb32b7..f95cc0ea 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -637,7 +637,6 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT this.on('resize', () => this.renderer.onResize(this.cols, this.rows, false)); this.on('blur', () => this.renderer.onBlur()); this.on('focus', () => this.renderer.onFocus()); - window.addEventListener('resize', () => this.renderer.onWindowResize(window.devicePixelRatio)); this.charMeasure.on('charsizechanged', () => this.renderer.onResize(this.cols, this.rows, true)); this.renderer.on('resize', (dimensions) => this.viewport.syncScrollArea()); diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 52d47adc..31547da4 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -13,6 +13,7 @@ import { BaseRenderLayer } from './BaseRenderLayer'; import { IRenderLayer, IColorSet, IRenderer, IRenderDimensions } from './Interfaces'; import { LinkRenderLayer } from './LinkRenderLayer'; import { EventEmitter } from '../EventEmitter'; +import { ScreenDprMonitor } from '../utils/ScreenDprMonitor'; export class Renderer extends EventEmitter implements IRenderer { /** A queue of the rows to be refreshed */ @@ -21,6 +22,7 @@ export class Renderer extends EventEmitter implements IRenderer { private _renderLayers: IRenderLayer[]; private _devicePixelRatio: number; + private _screenDprMonitor: ScreenDprMonitor; private _isPaused: boolean = false; private _needsFullRefresh: boolean = false; @@ -56,6 +58,9 @@ export class Renderer extends EventEmitter implements IRenderer { this._devicePixelRatio = window.devicePixelRatio; this._updateDimensions(); + this._screenDprMonitor = new ScreenDprMonitor(); + this._screenDprMonitor.setListener(() => this.onWindowResize(window.devicePixelRatio)); + // Detect whether IntersectionObserver is detected and enable renderer pause // and resume based on terminal visibility if so if ('IntersectionObserver' in window) { diff --git a/src/utils/ScreenDprMonitor.ts b/src/utils/ScreenDprMonitor.ts new file mode 100644 index 00000000..15f3ac00 --- /dev/null +++ b/src/utils/ScreenDprMonitor.ts @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +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 { + private _currentDevicePixelRatio: number; + private _outerListener: MediaQueryListListener; + private _listener: ScreenDprListener; + private _resolutionMediaMatchList: MediaQueryList; + + public setListener(listener: ScreenDprListener): void { + if (this._listener) { + this.clearListener(); + } + this._listener = listener; + this._outerListener = () => { + this._listener(window.devicePixelRatio, this._currentDevicePixelRatio); + this._updateDpr(); + }; + this._updateDpr(); + } + + private _updateDpr(): void { + // Clear listeners for old DPR + if (this._resolutionMediaMatchList) { + this._resolutionMediaMatchList.removeListener(this._outerListener); + } + // Add listeners for new DPR + this._currentDevicePixelRatio = window.devicePixelRatio; + this._resolutionMediaMatchList = window.matchMedia(`screen and (resolution: ${window.devicePixelRatio}dppx)`); + this._resolutionMediaMatchList.addListener(this._outerListener); + } + + public clearListener(): void { + if (!this._listener) { + return; + } + this._resolutionMediaMatchList.removeListener(this._outerListener); + this._listener = null; + this._outerListener = null; + } +}