From 72d946e089a4014e510f5d60751c391444e5f891 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 29 Dec 2017 13:54:01 -0800 Subject: [PATCH] Use a more robust solution to changing devicePixelRatio --- src/Terminal.ts | 5 --- src/renderer/Renderer.ts | 5 +++ src/utils/ScreenDprMonitor.ts | 62 +++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 src/utils/ScreenDprMonitor.ts diff --git a/src/Terminal.ts b/src/Terminal.ts index 3026c9a3..dd2bf405 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -637,11 +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()); - const rendererResizeListener = () => this.renderer.onWindowResize(window.devicePixelRatio); - // Listen for window zoom - window.addEventListener('resize', () => rendererResizeListener); - // Listen for monitor DPI change - window.matchMedia('screen and (-webkit-min-device-pixel-ratio: 1.5)').addListener(rendererResizeListener); 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 36727eac..422d424c 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; public colorManager: ColorManager; public dimensions: IRenderDimensions; @@ -53,6 +55,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)); } public onWindowResize(devicePixelRatio: number): void { diff --git a/src/utils/ScreenDprMonitor.ts b/src/utils/ScreenDprMonitor.ts new file mode 100644 index 00000000..5c5f762c --- /dev/null +++ b/src/utils/ScreenDprMonitor.ts @@ -0,0 +1,62 @@ +/** + * 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 _minMediaMatchList: MediaQueryList; + private _maxMediaMatchList: 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._minMediaMatchList) { + this._minMediaMatchList.removeListener(this._outerListener); + } + if (this._maxMediaMatchList) { + this._maxMediaMatchList.removeListener(this._outerListener); + } + // Add listeners for new DPR + this._currentDevicePixelRatio = window.devicePixelRatio; + this._minMediaMatchList = window.matchMedia(`screen and (-webkit-min-device-pixel-ratio: ${window.devicePixelRatio})`); + this._maxMediaMatchList = window.matchMedia(`screen and (-webkit-max-device-pixel-ratio: ${window.devicePixelRatio})`); + this._minMediaMatchList.addListener(this._outerListener); + this._maxMediaMatchList.addListener(this._outerListener); + } + + public clearListener(): void { + if (!this._listener) { + return; + } + this._minMediaMatchList.removeListener(this._outerListener); + this._maxMediaMatchList.removeListener(this._outerListener); + this._listener = null; + this._outerListener = null; + } +}