Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/browser/pixelRatio.ts
5241 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { getWindowId, onDidUnregisterWindow } from './dom.js';
7
import { Emitter, Event } from '../common/event.js';
8
import { Disposable, markAsSingleton, toDisposable } from '../common/lifecycle.js';
9
10
type BackingStoreContext = CanvasRenderingContext2D & {
11
webkitBackingStorePixelRatio?: number;
12
mozBackingStorePixelRatio?: number;
13
msBackingStorePixelRatio?: number;
14
oBackingStorePixelRatio?: number;
15
backingStorePixelRatio?: number;
16
};
17
18
/**
19
* See https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#monitoring_screen_resolution_or_zoom_level_changes
20
*/
21
class DevicePixelRatioMonitor extends Disposable {
22
23
private readonly _onDidChange = this._register(new Emitter<void>());
24
readonly onDidChange = this._onDidChange.event;
25
26
private readonly _listener: () => void;
27
private _mediaQueryList: MediaQueryList | null;
28
29
constructor(targetWindow: Window) {
30
super();
31
32
this._listener = () => this._handleChange(targetWindow, true);
33
this._mediaQueryList = null;
34
this._handleChange(targetWindow, false);
35
36
this._register(toDisposable(() => this._mediaQueryList?.removeEventListener('change', this._listener)));
37
}
38
39
private _handleChange(targetWindow: Window, fireEvent: boolean): void {
40
this._mediaQueryList?.removeEventListener('change', this._listener);
41
42
this._mediaQueryList = targetWindow.matchMedia(`(resolution: ${targetWindow.devicePixelRatio}dppx)`);
43
this._mediaQueryList.addEventListener('change', this._listener);
44
45
if (fireEvent) {
46
this._onDidChange.fire();
47
}
48
}
49
}
50
51
export interface IPixelRatioMonitor {
52
readonly value: number;
53
readonly onDidChange: Event<number>;
54
}
55
56
class PixelRatioMonitorImpl extends Disposable implements IPixelRatioMonitor {
57
58
private readonly _onDidChange = this._register(new Emitter<number>());
59
readonly onDidChange = this._onDidChange.event;
60
61
private _value: number;
62
63
get value(): number {
64
return this._value;
65
}
66
67
constructor(targetWindow: Window) {
68
super();
69
70
this._value = this._getPixelRatio(targetWindow);
71
72
const dprMonitor = this._register(new DevicePixelRatioMonitor(targetWindow));
73
this._register(dprMonitor.onDidChange(() => {
74
this._value = this._getPixelRatio(targetWindow);
75
this._onDidChange.fire(this._value);
76
}));
77
}
78
79
private _getPixelRatio(targetWindow: Window): number {
80
const ctx = document.createElement('canvas').getContext('2d') as BackingStoreContext | null;
81
const dpr = targetWindow.devicePixelRatio || 1;
82
const bsr = ctx?.webkitBackingStorePixelRatio ||
83
ctx?.mozBackingStorePixelRatio ||
84
ctx?.msBackingStorePixelRatio ||
85
ctx?.oBackingStorePixelRatio ||
86
ctx?.backingStorePixelRatio || 1;
87
return dpr / bsr;
88
}
89
}
90
91
class PixelRatioMonitorFacade {
92
93
private readonly mapWindowIdToPixelRatioMonitor = new Map<number, PixelRatioMonitorImpl>();
94
95
private _getOrCreatePixelRatioMonitor(targetWindow: Window): PixelRatioMonitorImpl {
96
const targetWindowId = getWindowId(targetWindow);
97
let pixelRatioMonitor = this.mapWindowIdToPixelRatioMonitor.get(targetWindowId);
98
if (!pixelRatioMonitor) {
99
pixelRatioMonitor = markAsSingleton(new PixelRatioMonitorImpl(targetWindow));
100
this.mapWindowIdToPixelRatioMonitor.set(targetWindowId, pixelRatioMonitor);
101
102
markAsSingleton(Event.once(onDidUnregisterWindow)(({ vscodeWindowId }) => {
103
if (vscodeWindowId === targetWindowId) {
104
pixelRatioMonitor?.dispose();
105
this.mapWindowIdToPixelRatioMonitor.delete(targetWindowId);
106
}
107
}));
108
}
109
return pixelRatioMonitor;
110
}
111
112
getInstance(targetWindow: Window): IPixelRatioMonitor {
113
return this._getOrCreatePixelRatioMonitor(targetWindow);
114
}
115
}
116
117
/**
118
* Returns the pixel ratio.
119
*
120
* This is useful for rendering <canvas> elements at native screen resolution or for being used as
121
* a cache key when storing font measurements. Fonts might render differently depending on resolution
122
* and any measurements need to be discarded for example when a window is moved from a monitor to another.
123
*/
124
export const PixelRatio = new PixelRatioMonitorFacade();
125
126