@flighthq/screen 0.5.1-next.704.19c91ec → 0.5.1-next.730.f29a5aa
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/screen.js +2 -2
- package/dist/screen.js.map +1 -1
- package/package.json +4 -4
- package/src/screen.test.ts +543 -89
package/README.md
CHANGED
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/screen`. The `@fligh
|
|
|
13
13
|
This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
|
|
14
14
|
|
|
15
15
|
- [Flight project](https://github.com/flighthq/flight)
|
|
16
|
-
- [Source for @flighthq/screen@0.5.1-next.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/screen@0.5.1-next.730.f29a5aa](https://github.com/flighthq/flight/tree/f29a5aab5db192284ce5cadf08ffae2649a598ea/packages/screen)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/f29a5aab5db192284ce5cadf08ffae2649a598ea/LICENSE.md)
|
package/dist/screen.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createEntity } from '@flighthq/entity/contract';
|
|
1
|
+
import { createEntity, stripEntityRuntime } from '@flighthq/entity/contract';
|
|
2
2
|
import { clearSignal, createSignal, emitSignal } from '@flighthq/signals/contract';
|
|
3
3
|
export function attachScreenPermissionChange(host, permissionChange) {
|
|
4
4
|
detachScreenPermissionChange(permissionChange);
|
|
@@ -217,7 +217,7 @@ const _permissionSubscriptions = new WeakMap();
|
|
|
217
217
|
const _signalSubscriptions = new WeakMap();
|
|
218
218
|
const _scratchPoint = { x: 0, y: 0 };
|
|
219
219
|
function copyScreenInfo(src, dst) {
|
|
220
|
-
Object.assign(dst, src);
|
|
220
|
+
Object.assign(dst, stripEntityRuntime(src));
|
|
221
221
|
}
|
|
222
222
|
function fillDefaultScreenInfo(out) {
|
|
223
223
|
copyScreenInfo(createScreenInfo(), out);
|
package/dist/screen.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screen.js","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"screen.js","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/screen",
|
|
3
|
-
"version": "0.5.1-next.
|
|
3
|
+
"version": "0.5.1-next.730.f29a5aa",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/entity": "0.5.1-next.
|
|
42
|
-
"@flighthq/signals": "0.5.1-next.
|
|
43
|
-
"@flighthq/types": "0.5.1-next.
|
|
41
|
+
"@flighthq/entity": "0.5.1-next.730.f29a5aa",
|
|
42
|
+
"@flighthq/signals": "0.5.1-next.730.f29a5aa",
|
|
43
|
+
"@flighthq/types": "0.5.1-next.730.f29a5aa"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"typescript": "^5.3.0"
|
package/src/screen.test.ts
CHANGED
|
@@ -1,166 +1,620 @@
|
|
|
1
|
+
import { attachEntityBinding, createEntity, getEntityBinding } from '@flighthq/entity/contract';
|
|
1
2
|
import { connectSignal } from '@flighthq/signals/contract';
|
|
3
|
+
import type { HasScreenQuery, ScreenChangeEvent, ScreenInfo, ScreenPermissionState } from '@flighthq/types/contract';
|
|
2
4
|
import { EntityRuntimeKey } from '@flighthq/types/contract';
|
|
3
5
|
import { describe, expect, it, vi } from 'vitest';
|
|
4
6
|
|
|
5
|
-
import * as screenContract from './screen';
|
|
6
7
|
import {
|
|
8
|
+
attachScreenPermissionChange,
|
|
7
9
|
attachScreenSignals,
|
|
8
10
|
createScreenInfo,
|
|
9
11
|
createScreenMode,
|
|
10
12
|
createScreenPermissionChange,
|
|
11
13
|
createScreenSignals,
|
|
14
|
+
detachScreenPermissionChange,
|
|
12
15
|
detachScreenSignals,
|
|
16
|
+
dipToScreenPoint,
|
|
17
|
+
dipToScreenRect,
|
|
13
18
|
disposeScreenPermissionChange,
|
|
14
19
|
disposeScreenSignals,
|
|
20
|
+
getPrimaryScreen,
|
|
21
|
+
getScreenBounds,
|
|
22
|
+
getScreenById,
|
|
23
|
+
getScreenContainingRect,
|
|
15
24
|
getScreenCurrentMode,
|
|
25
|
+
getScreenCursorPosition,
|
|
26
|
+
getScreenCursorScreen,
|
|
27
|
+
getScreenDetailPermission,
|
|
28
|
+
getScreenNearestPoint,
|
|
29
|
+
getScreenNearestRect,
|
|
16
30
|
getScreens,
|
|
31
|
+
getScreenWorkArea,
|
|
32
|
+
requestScreenDetails,
|
|
33
|
+
screenToDipPoint,
|
|
34
|
+
screenToDipRect,
|
|
17
35
|
} from './screen';
|
|
18
36
|
|
|
19
37
|
describe('attachScreenPermissionChange', () => {
|
|
20
|
-
it('
|
|
38
|
+
it('forwards permission states from the supplied host', () => {
|
|
39
|
+
const harness = createPermissionChangeHarness();
|
|
40
|
+
const permissionChange = createScreenPermissionChange();
|
|
41
|
+
const listener = vi.fn();
|
|
42
|
+
connectSignal(permissionChange.onChange, listener);
|
|
43
|
+
|
|
44
|
+
attachScreenPermissionChange(harness.host, permissionChange);
|
|
45
|
+
harness.emit('granted');
|
|
46
|
+
|
|
47
|
+
expect(listener).toHaveBeenCalledWith('granted');
|
|
48
|
+
});
|
|
21
49
|
});
|
|
50
|
+
|
|
22
51
|
describe('attachScreenSignals', () => {
|
|
23
|
-
it('
|
|
52
|
+
it('routes added, removed, and metrics events to their matching signals', () => {
|
|
53
|
+
const harness = createScreenChangeHarness();
|
|
54
|
+
const signals = createScreenSignals();
|
|
55
|
+
const added = vi.fn();
|
|
56
|
+
const metricsChanged = vi.fn();
|
|
57
|
+
const removed = vi.fn();
|
|
58
|
+
connectSignal(signals.onScreenAdded, added);
|
|
59
|
+
connectSignal(signals.onScreenMetricsChanged, metricsChanged);
|
|
60
|
+
connectSignal(signals.onScreenRemoved, removed);
|
|
61
|
+
const screen = createTestScreen(7, 100, 200, 800, 600);
|
|
62
|
+
const metricsEvent: ScreenChangeEvent = {
|
|
63
|
+
kind: 'ScreenMetricsChanged',
|
|
64
|
+
screen,
|
|
65
|
+
changedMetrics: { bounds: true, orientation: false, scaleFactor: true, workArea: false },
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
attachScreenSignals(harness.host, signals);
|
|
69
|
+
harness.emit({ kind: 'ScreenAdded', screen, changedMetrics: null });
|
|
70
|
+
harness.emit({ kind: 'ScreenRemoved', screen, changedMetrics: null });
|
|
71
|
+
harness.emit(metricsEvent);
|
|
72
|
+
|
|
73
|
+
expect(added).toHaveBeenCalledWith(screen);
|
|
74
|
+
expect(removed).toHaveBeenCalledWith(screen);
|
|
75
|
+
expect(metricsChanged).toHaveBeenCalledWith(metricsEvent);
|
|
76
|
+
});
|
|
24
77
|
});
|
|
78
|
+
|
|
25
79
|
describe('createScreenInfo', () => {
|
|
26
|
-
it('
|
|
80
|
+
it('creates an entity with documented defaults and sentinels', () => {
|
|
81
|
+
const screen = createScreenInfo();
|
|
82
|
+
|
|
83
|
+
expect(EntityRuntimeKey in screen).toBe(true);
|
|
84
|
+
expect(Object.keys(screen)).toHaveLength(25);
|
|
85
|
+
expect(screen).toMatchObject({
|
|
86
|
+
id: 0,
|
|
87
|
+
x: 0,
|
|
88
|
+
y: 0,
|
|
89
|
+
width: 0,
|
|
90
|
+
height: 0,
|
|
91
|
+
workWidth: 0,
|
|
92
|
+
workHeight: 0,
|
|
93
|
+
scaleFactor: 1,
|
|
94
|
+
isPrimary: false,
|
|
95
|
+
rotation: -1,
|
|
96
|
+
orientation: 'Landscape',
|
|
97
|
+
refreshRate: -1,
|
|
98
|
+
colorDepth: -1,
|
|
99
|
+
pixelDepth: -1,
|
|
100
|
+
physicalWidth: -1,
|
|
101
|
+
physicalHeight: -1,
|
|
102
|
+
isHdr: false,
|
|
103
|
+
colorSpace: 'srgb',
|
|
104
|
+
maxLuminance: -1,
|
|
105
|
+
depthPerComponent: -1,
|
|
106
|
+
dpi: -1,
|
|
107
|
+
label: '',
|
|
108
|
+
internal: false,
|
|
109
|
+
touchSupport: 'unknown',
|
|
110
|
+
monochrome: false,
|
|
111
|
+
});
|
|
112
|
+
});
|
|
27
113
|
});
|
|
114
|
+
|
|
28
115
|
describe('createScreenMode', () => {
|
|
29
|
-
it('
|
|
116
|
+
it('creates an entity with unknown mode sentinels', () => {
|
|
117
|
+
const mode = createScreenMode();
|
|
118
|
+
|
|
119
|
+
expect(EntityRuntimeKey in mode).toBe(true);
|
|
120
|
+
expect(mode).toMatchObject({ width: 0, height: 0, refreshRate: -1, colorDepth: -1, pixelFormat: '' });
|
|
121
|
+
});
|
|
30
122
|
});
|
|
123
|
+
|
|
31
124
|
describe('createScreenPermissionChange', () => {
|
|
32
|
-
it('
|
|
125
|
+
it('creates an entity with an empty permission signal', () => {
|
|
126
|
+
const permissionChange = createScreenPermissionChange();
|
|
127
|
+
|
|
128
|
+
expect(EntityRuntimeKey in permissionChange).toBe(true);
|
|
129
|
+
expect(permissionChange.onChange.data).toBeNull();
|
|
130
|
+
});
|
|
33
131
|
});
|
|
132
|
+
|
|
34
133
|
describe('createScreenSignals', () => {
|
|
35
|
-
it('
|
|
134
|
+
it('creates an entity with three independent empty signals', () => {
|
|
135
|
+
const signals = createScreenSignals();
|
|
136
|
+
|
|
137
|
+
expect(EntityRuntimeKey in signals).toBe(true);
|
|
138
|
+
expect(signals.onScreenAdded).not.toBe(signals.onScreenRemoved);
|
|
139
|
+
expect(signals.onScreenAdded.data).toBeNull();
|
|
140
|
+
expect(signals.onScreenMetricsChanged.data).toBeNull();
|
|
141
|
+
expect(signals.onScreenRemoved.data).toBeNull();
|
|
142
|
+
});
|
|
36
143
|
});
|
|
144
|
+
|
|
37
145
|
describe('detachScreenPermissionChange', () => {
|
|
38
|
-
it('
|
|
146
|
+
it('unsubscribes an attachment once and remains repeatable', () => {
|
|
147
|
+
const harness = createPermissionChangeHarness();
|
|
148
|
+
const permissionChange = createScreenPermissionChange();
|
|
149
|
+
attachScreenPermissionChange(harness.host, permissionChange);
|
|
150
|
+
|
|
151
|
+
detachScreenPermissionChange(permissionChange);
|
|
152
|
+
detachScreenPermissionChange(permissionChange);
|
|
153
|
+
|
|
154
|
+
expect(harness.unsubscribe).toHaveBeenCalledOnce();
|
|
155
|
+
});
|
|
39
156
|
});
|
|
157
|
+
|
|
40
158
|
describe('detachScreenSignals', () => {
|
|
41
|
-
it('
|
|
159
|
+
it('unsubscribes an attachment once and remains repeatable', () => {
|
|
160
|
+
const harness = createScreenChangeHarness();
|
|
161
|
+
const signals = createScreenSignals();
|
|
162
|
+
attachScreenSignals(harness.host, signals);
|
|
163
|
+
|
|
164
|
+
detachScreenSignals(signals);
|
|
165
|
+
detachScreenSignals(signals);
|
|
166
|
+
|
|
167
|
+
expect(harness.unsubscribe).toHaveBeenCalledOnce();
|
|
168
|
+
});
|
|
42
169
|
});
|
|
170
|
+
|
|
43
171
|
describe('dipToScreenPoint', () => {
|
|
44
|
-
it('
|
|
172
|
+
it('converts from desktop DIPs to screen-local pixels and returns out', () => {
|
|
173
|
+
const screen = createTestScreen(1, 100, 50, 800, 600, { scaleFactor: 2 });
|
|
174
|
+
const out = { x: -1, y: -1 };
|
|
175
|
+
|
|
176
|
+
expect(dipToScreenPoint(screen, { x: 112, y: 73 }, out)).toBe(out);
|
|
177
|
+
expect(out).toEqual({ x: 24, y: 46 });
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('is safe when out aliases the input point', () => {
|
|
181
|
+
const screen = createTestScreen(1, 100, 50, 800, 600, { scaleFactor: 2 });
|
|
182
|
+
const point = { x: 112, y: 73 };
|
|
183
|
+
|
|
184
|
+
dipToScreenPoint(screen, point, point);
|
|
185
|
+
|
|
186
|
+
expect(point).toEqual({ x: 24, y: 46 });
|
|
187
|
+
});
|
|
45
188
|
});
|
|
189
|
+
|
|
46
190
|
describe('dipToScreenRect', () => {
|
|
47
|
-
it('
|
|
191
|
+
it('converts position and extent to screen-local pixels and returns out', () => {
|
|
192
|
+
const screen = createTestScreen(1, 100, 50, 800, 600, { scaleFactor: 2 });
|
|
193
|
+
const out = { x: -1, y: -1, width: -1, height: -1 };
|
|
194
|
+
|
|
195
|
+
expect(dipToScreenRect(screen, { x: 112, y: 73, width: 30, height: 40 }, out)).toBe(out);
|
|
196
|
+
expect(out).toEqual({ x: 24, y: 46, width: 60, height: 80 });
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('is safe when out aliases the input rectangle', () => {
|
|
200
|
+
const screen = createTestScreen(1, 100, 50, 800, 600, { scaleFactor: 2 });
|
|
201
|
+
const rect = { x: 112, y: 73, width: 30, height: 40 };
|
|
202
|
+
|
|
203
|
+
dipToScreenRect(screen, rect, rect);
|
|
204
|
+
|
|
205
|
+
expect(rect).toEqual({ x: 24, y: 46, width: 60, height: 80 });
|
|
206
|
+
});
|
|
48
207
|
});
|
|
208
|
+
|
|
49
209
|
describe('disposeScreenPermissionChange', () => {
|
|
50
|
-
it('
|
|
210
|
+
it('detaches the host and clears the signal', () => {
|
|
211
|
+
const harness = createPermissionChangeHarness();
|
|
212
|
+
const permissionChange = createScreenPermissionChange();
|
|
213
|
+
connectSignal(permissionChange.onChange, vi.fn());
|
|
214
|
+
attachScreenPermissionChange(harness.host, permissionChange);
|
|
215
|
+
|
|
216
|
+
disposeScreenPermissionChange(permissionChange);
|
|
217
|
+
|
|
218
|
+
expect(harness.unsubscribe).toHaveBeenCalledOnce();
|
|
219
|
+
expect(permissionChange.onChange.data).toBeNull();
|
|
220
|
+
});
|
|
51
221
|
});
|
|
222
|
+
|
|
52
223
|
describe('disposeScreenSignals', () => {
|
|
53
|
-
it('
|
|
224
|
+
it('detaches the host and clears every signal', () => {
|
|
225
|
+
const harness = createScreenChangeHarness();
|
|
226
|
+
const signals = createScreenSignals();
|
|
227
|
+
connectSignal(signals.onScreenAdded, vi.fn());
|
|
228
|
+
connectSignal(signals.onScreenMetricsChanged, vi.fn());
|
|
229
|
+
connectSignal(signals.onScreenRemoved, vi.fn());
|
|
230
|
+
attachScreenSignals(harness.host, signals);
|
|
231
|
+
|
|
232
|
+
disposeScreenSignals(signals);
|
|
233
|
+
|
|
234
|
+
expect(harness.unsubscribe).toHaveBeenCalledOnce();
|
|
235
|
+
expect(signals.onScreenAdded.data).toBeNull();
|
|
236
|
+
expect(signals.onScreenMetricsChanged.data).toBeNull();
|
|
237
|
+
expect(signals.onScreenRemoved.data).toBeNull();
|
|
238
|
+
});
|
|
54
239
|
});
|
|
240
|
+
|
|
55
241
|
describe('getPrimaryScreen', () => {
|
|
56
|
-
it('
|
|
242
|
+
it('delegates to the host with the caller-owned output', () => {
|
|
243
|
+
const primary = createTestScreen(3, 10, 20, 1920, 1080, { isPrimary: true });
|
|
244
|
+
const host = createScreenQueryHost([primary]);
|
|
245
|
+
const out = createScreenInfo();
|
|
246
|
+
|
|
247
|
+
expect(getPrimaryScreen(host, out)).toBe(out);
|
|
248
|
+
expect(host.screen.query.getPrimaryScreen).toHaveBeenCalledWith(out);
|
|
249
|
+
expect(out).toMatchObject({ id: 3, x: 10, y: 20, width: 1920, height: 1080, isPrimary: true });
|
|
250
|
+
});
|
|
57
251
|
});
|
|
252
|
+
|
|
58
253
|
describe('getScreenBounds', () => {
|
|
59
|
-
it('
|
|
254
|
+
it('extracts desktop bounds into and returns the supplied output', () => {
|
|
255
|
+
const screen = createTestScreen(1, -1280, 120, 1280, 720);
|
|
256
|
+
const out = { x: 0, y: 0, width: 0, height: 0 };
|
|
257
|
+
|
|
258
|
+
expect(getScreenBounds(screen, out)).toBe(out);
|
|
259
|
+
expect(out).toEqual({ x: -1280, y: 120, width: 1280, height: 720 });
|
|
260
|
+
});
|
|
60
261
|
});
|
|
262
|
+
|
|
61
263
|
describe('getScreenById', () => {
|
|
62
|
-
it('
|
|
264
|
+
it('copies a matching screen into out and returns null without a match', () => {
|
|
265
|
+
const first = createTestScreen(10, 0, 0, 100, 100);
|
|
266
|
+
const second = createTestScreen(20, 300, 0, 200, 150, { label: 'external', scaleFactor: 2 });
|
|
267
|
+
const host = createScreenQueryHost([first, second]);
|
|
268
|
+
const out = createScreenInfo();
|
|
269
|
+
|
|
270
|
+
expect(getScreenById(host, 20, out)).toBe(out);
|
|
271
|
+
expect(Object.entries(out)).toEqual(Object.entries(second));
|
|
272
|
+
expect(getScreenById(host, 99, out)).toBeNull();
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('preserves the destination runtime while copying public screen data', () => {
|
|
276
|
+
const source = createTestScreen(20, 300, 40, 200, 150, { label: 'source' });
|
|
277
|
+
const out = createTestScreen(99, 0, 0, 1, 1, { label: 'out' });
|
|
278
|
+
const sourceBinding = { owner: 'source' };
|
|
279
|
+
const outBinding = { owner: 'out' };
|
|
280
|
+
attachEntityBinding(source, sourceBinding);
|
|
281
|
+
attachEntityBinding(out, outBinding);
|
|
282
|
+
const sourceRuntime = source[EntityRuntimeKey];
|
|
283
|
+
const outRuntime = out[EntityRuntimeKey];
|
|
284
|
+
|
|
285
|
+
expect(getScreenById(createScreenQueryHost([source]), source.id, out)).toBe(out);
|
|
286
|
+
|
|
287
|
+
expect(Object.entries(out)).toEqual(Object.entries(source));
|
|
288
|
+
expect(out[EntityRuntimeKey]).toBe(outRuntime);
|
|
289
|
+
expect(out[EntityRuntimeKey]).not.toBe(sourceRuntime);
|
|
290
|
+
expect(getEntityBinding(out)).toBe(outBinding);
|
|
291
|
+
expect(getEntityBinding(source)).toBe(sourceBinding);
|
|
292
|
+
});
|
|
63
293
|
});
|
|
294
|
+
|
|
64
295
|
describe('getScreenContainingRect', () => {
|
|
65
|
-
it('
|
|
296
|
+
it('selects the screen with the largest overlap area', () => {
|
|
297
|
+
const left = createTestScreen(1, 0, 0, 100, 100);
|
|
298
|
+
const lowerRight = createTestScreen(2, 100, 80, 100, 100);
|
|
299
|
+
const host = createScreenQueryHost([left, lowerRight]);
|
|
300
|
+
|
|
301
|
+
const result = getScreenContainingRect(host, { x: 50, y: 50, width: 100, height: 60 }, createScreenInfo());
|
|
302
|
+
|
|
303
|
+
expect(result.id).toBe(1);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('falls back to the screen nearest the rectangle center when none overlap', () => {
|
|
307
|
+
const host = createScreenQueryHost(createSeparatedScreens());
|
|
308
|
+
|
|
309
|
+
const result = getScreenContainingRect(host, { x: 230, y: 20, width: 20, height: 20 }, createScreenInfo());
|
|
310
|
+
|
|
311
|
+
expect(result.id).toBe(2);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('fills the documented default when no screens exist', () => {
|
|
315
|
+
const out = createTestScreen(99, 10, 20, 30, 40);
|
|
316
|
+
|
|
317
|
+
expect(getScreenContainingRect(createScreenQueryHost([]), { x: 0, y: 0, width: 10, height: 10 }, out)).toBe(out);
|
|
318
|
+
expect(Object.entries(out)).toEqual(Object.entries(createScreenInfo()));
|
|
319
|
+
});
|
|
66
320
|
});
|
|
321
|
+
|
|
67
322
|
describe('getScreenCurrentMode', () => {
|
|
68
|
-
it('
|
|
323
|
+
it('derives current dimensions and display metrics while clearing unknown format', () => {
|
|
324
|
+
const screen = createTestScreen(1, 0, 0, 1920, 1080, { refreshRate: 144, colorDepth: 30 });
|
|
325
|
+
const out = createScreenMode();
|
|
326
|
+
out.pixelFormat = 'stale';
|
|
327
|
+
|
|
328
|
+
expect(getScreenCurrentMode(screen, out)).toBe(out);
|
|
329
|
+
expect(out).toMatchObject({ width: 1920, height: 1080, refreshRate: 144, colorDepth: 30, pixelFormat: '' });
|
|
330
|
+
});
|
|
69
331
|
});
|
|
332
|
+
|
|
70
333
|
describe('getScreenCursorPosition', () => {
|
|
71
|
-
it('
|
|
334
|
+
it('delegates to the host with the caller-owned output', () => {
|
|
335
|
+
const host = createScreenQueryHost([], { x: -20, y: 45 });
|
|
336
|
+
const out = { x: 0, y: 0 };
|
|
337
|
+
|
|
338
|
+
expect(getScreenCursorPosition(host, out)).toBe(out);
|
|
339
|
+
expect(out).toEqual({ x: -20, y: 45 });
|
|
340
|
+
expect(host.screen.query.getCursorPosition).toHaveBeenCalledWith(out);
|
|
341
|
+
});
|
|
72
342
|
});
|
|
343
|
+
|
|
73
344
|
describe('getScreenCursorScreen', () => {
|
|
74
|
-
it('
|
|
345
|
+
it('resolves the host cursor position to its nearest screen', () => {
|
|
346
|
+
const host = createScreenQueryHost(createSeparatedScreens(), { x: 340, y: 25 });
|
|
347
|
+
const out = createScreenInfo();
|
|
348
|
+
|
|
349
|
+
expect(getScreenCursorScreen(host, out)).toBe(out);
|
|
350
|
+
expect(out.id).toBe(2);
|
|
351
|
+
});
|
|
75
352
|
});
|
|
353
|
+
|
|
76
354
|
describe('getScreenDetailPermission', () => {
|
|
77
|
-
it('
|
|
355
|
+
it('returns the permission state supplied by the host', async () => {
|
|
356
|
+
const host = createScreenDetailsHost('granted', true);
|
|
357
|
+
|
|
358
|
+
await expect(getScreenDetailPermission(host)).resolves.toBe('granted');
|
|
359
|
+
expect(host.screen.details.queryPermission).toHaveBeenCalledOnce();
|
|
360
|
+
});
|
|
78
361
|
});
|
|
362
|
+
|
|
79
363
|
describe('getScreenNearestPoint', () => {
|
|
80
|
-
it('
|
|
364
|
+
it('uses half-open containment before distance', () => {
|
|
365
|
+
const horizontalHost = createScreenQueryHost([
|
|
366
|
+
createTestScreen(1, 0, 0, 100, 100),
|
|
367
|
+
createTestScreen(2, 100, 0, 200, 100),
|
|
368
|
+
]);
|
|
369
|
+
const verticalHost = createScreenQueryHost([
|
|
370
|
+
createTestScreen(1, 0, 0, 100, 100),
|
|
371
|
+
createTestScreen(2, 0, 100, 100, 200),
|
|
372
|
+
]);
|
|
373
|
+
|
|
374
|
+
expect(getScreenNearestPoint(horizontalHost, { x: 20, y: 30 }, createScreenInfo()).id).toBe(1);
|
|
375
|
+
expect(getScreenNearestPoint(horizontalHost, { x: 100, y: 30 }, createScreenInfo()).id).toBe(2);
|
|
376
|
+
expect(getScreenNearestPoint(verticalHost, { x: 30, y: 100 }, createScreenInfo()).id).toBe(2);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('selects the nearest screen center when the point is outside every screen', () => {
|
|
380
|
+
const host = createScreenQueryHost(createSeparatedScreens());
|
|
381
|
+
const diagonalHost = createScreenQueryHost([
|
|
382
|
+
createTestScreen(1, 0, 0, 100, 100),
|
|
383
|
+
createTestScreen(2, 300, 200, 100, 100),
|
|
384
|
+
]);
|
|
385
|
+
|
|
386
|
+
expect(getScreenNearestPoint(host, { x: 180, y: 50 }, createScreenInfo()).id).toBe(1);
|
|
387
|
+
expect(getScreenNearestPoint(host, { x: 240, y: 50 }, createScreenInfo()).id).toBe(2);
|
|
388
|
+
expect(getScreenNearestPoint(diagonalHost, { x: 200, y: 100 }, createScreenInfo()).id).toBe(1);
|
|
389
|
+
expect(getScreenNearestPoint(diagonalHost, { x: 200, y: 180 }, createScreenInfo()).id).toBe(2);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('keeps the first screen when center distances tie', () => {
|
|
393
|
+
const host = createScreenQueryHost(createSeparatedScreens());
|
|
394
|
+
|
|
395
|
+
expect(getScreenNearestPoint(host, { x: 200, y: 50 }, createScreenInfo()).id).toBe(1);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('fills the documented default when no screens exist', () => {
|
|
399
|
+
const out = createTestScreen(99, 10, 20, 30, 40);
|
|
400
|
+
|
|
401
|
+
expect(getScreenNearestPoint(createScreenQueryHost([]), { x: 5, y: 5 }, out)).toBe(out);
|
|
402
|
+
expect(Object.entries(out)).toEqual(Object.entries(createScreenInfo()));
|
|
403
|
+
});
|
|
81
404
|
});
|
|
405
|
+
|
|
82
406
|
describe('getScreenNearestRect', () => {
|
|
83
|
-
it('
|
|
407
|
+
it('prefers a screen that fully contains the rectangle', () => {
|
|
408
|
+
const host = createScreenQueryHost(createSeparatedScreens());
|
|
409
|
+
|
|
410
|
+
const result = getScreenNearestRect(host, { x: 320, y: 20, width: 40, height: 50 }, createScreenInfo());
|
|
411
|
+
|
|
412
|
+
expect(result.id).toBe(2);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it('falls back to the screen nearest the rectangle center', () => {
|
|
416
|
+
const host = createScreenQueryHost(createSeparatedScreens());
|
|
417
|
+
|
|
418
|
+
const result = getScreenNearestRect(host, { x: 170, y: 20, width: 20, height: 20 }, createScreenInfo());
|
|
419
|
+
|
|
420
|
+
expect(result.id).toBe(1);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('uses center selection rather than largest overlap when no screen fully contains the rectangle', () => {
|
|
424
|
+
const left = createTestScreen(1, 0, 0, 100, 100);
|
|
425
|
+
const right = createTestScreen(2, 100, 0, 200, 100);
|
|
426
|
+
const host = createScreenQueryHost([left, right]);
|
|
427
|
+
const rect = { x: 50, y: 10, width: 100, height: 50 };
|
|
428
|
+
|
|
429
|
+
expect(getScreenContainingRect(host, rect, createScreenInfo()).id).toBe(1);
|
|
430
|
+
expect(getScreenNearestRect(host, rect, createScreenInfo()).id).toBe(2);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it('honors inclusive rectangle boundaries and full extents when testing containment', () => {
|
|
434
|
+
const boundsHost = createScreenQueryHost([
|
|
435
|
+
createTestScreen(1, 0, 0, 100, 100),
|
|
436
|
+
createTestScreen(2, -10, -10, 200, 200),
|
|
437
|
+
]);
|
|
438
|
+
const horizontalExtentHost = createScreenQueryHost([
|
|
439
|
+
createTestScreen(1, 0, 0, 80, 100),
|
|
440
|
+
createTestScreen(2, 30, 0, 100, 100),
|
|
441
|
+
]);
|
|
442
|
+
const verticalExtentHost = createScreenQueryHost([
|
|
443
|
+
createTestScreen(1, 0, 0, 100, 80),
|
|
444
|
+
createTestScreen(2, 0, 30, 100, 100),
|
|
445
|
+
]);
|
|
446
|
+
|
|
447
|
+
expect(getScreenNearestRect(boundsHost, { x: 0, y: 0, width: 100, height: 100 }, createScreenInfo()).id).toBe(1);
|
|
448
|
+
expect(
|
|
449
|
+
getScreenNearestRect(horizontalExtentHost, { x: 40, y: 0, width: 60, height: 100 }, createScreenInfo()).id,
|
|
450
|
+
).toBe(2);
|
|
451
|
+
expect(
|
|
452
|
+
getScreenNearestRect(verticalExtentHost, { x: 0, y: 40, width: 100, height: 60 }, createScreenInfo()).id,
|
|
453
|
+
).toBe(2);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('fills the documented default when no screens exist', () => {
|
|
457
|
+
const out = createTestScreen(99, 10, 20, 30, 40);
|
|
458
|
+
|
|
459
|
+
expect(getScreenNearestRect(createScreenQueryHost([]), { x: 0, y: 0, width: 10, height: 10 }, out)).toBe(out);
|
|
460
|
+
expect(Object.entries(out)).toEqual(Object.entries(createScreenInfo()));
|
|
461
|
+
});
|
|
84
462
|
});
|
|
463
|
+
|
|
85
464
|
describe('getScreens', () => {
|
|
86
|
-
it('
|
|
465
|
+
it('delegates enumeration into and returns the caller-owned array', () => {
|
|
466
|
+
const screen = createTestScreen(5, 0, 0, 1920, 1080);
|
|
467
|
+
const host = createScreenQueryHost([screen]);
|
|
468
|
+
const out: ScreenInfo[] = [];
|
|
469
|
+
|
|
470
|
+
expect(getScreens(host, out)).toBe(out);
|
|
471
|
+
expect(out).toEqual([screen]);
|
|
472
|
+
expect(host.screen.query.getScreens).toHaveBeenCalledWith(out);
|
|
473
|
+
});
|
|
87
474
|
});
|
|
475
|
+
|
|
88
476
|
describe('getScreenWorkArea', () => {
|
|
89
|
-
it('
|
|
477
|
+
it('extracts work dimensions at the screen desktop origin', () => {
|
|
478
|
+
const screen = createTestScreen(1, 100, -50, 1920, 1080, { workWidth: 1900, workHeight: 1040 });
|
|
479
|
+
const out = { x: 0, y: 0, width: 0, height: 0 };
|
|
480
|
+
|
|
481
|
+
expect(getScreenWorkArea(screen, out)).toBe(out);
|
|
482
|
+
expect(out).toEqual({ x: 100, y: -50, width: 1900, height: 1040 });
|
|
483
|
+
});
|
|
90
484
|
});
|
|
485
|
+
|
|
91
486
|
describe('requestScreenDetails', () => {
|
|
92
|
-
it('
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
expect(
|
|
97
|
-
expect(EntityRuntimeKey in createScreenMode()).toBe(true);
|
|
98
|
-
expect(EntityRuntimeKey in createScreenPermissionChange()).toBe(true);
|
|
99
|
-
expect(EntityRuntimeKey in createScreenSignals()).toBe(true);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('attaches and detaches display events through the supplied host', () => {
|
|
103
|
-
const unsubscribe = vi.fn();
|
|
104
|
-
let listener: ((event: any) => void) | undefined;
|
|
105
|
-
const host = {
|
|
106
|
-
screen: {
|
|
107
|
-
change: {
|
|
108
|
-
[EntityRuntimeKey]: {},
|
|
109
|
-
subscribe(next: typeof listener) {
|
|
110
|
-
listener = next;
|
|
111
|
-
return unsubscribe;
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
} as any;
|
|
116
|
-
const signals = createScreenSignals();
|
|
117
|
-
const added = vi.fn();
|
|
118
|
-
connectSignal(signals.onScreenAdded, added);
|
|
119
|
-
attachScreenSignals(host, signals);
|
|
120
|
-
listener?.({ kind: 'ScreenAdded', screen: createScreenInfo(), changedMetrics: null });
|
|
121
|
-
expect(added).toHaveBeenCalledOnce();
|
|
122
|
-
detachScreenSignals(signals);
|
|
123
|
-
expect(unsubscribe).toHaveBeenCalledOnce();
|
|
487
|
+
it('returns whether the host granted detailed screen access', async () => {
|
|
488
|
+
const host = createScreenDetailsHost('prompt', true);
|
|
489
|
+
|
|
490
|
+
await expect(requestScreenDetails(host)).resolves.toBe(true);
|
|
491
|
+
expect(host.screen.details.request).toHaveBeenCalledOnce();
|
|
124
492
|
});
|
|
493
|
+
});
|
|
125
494
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
expect(
|
|
133
|
-
|
|
134
|
-
|
|
495
|
+
describe('screenToDipPoint', () => {
|
|
496
|
+
it('converts screen-local pixels to desktop DIPs and returns out', () => {
|
|
497
|
+
const screen = createTestScreen(1, 100, 50, 800, 600, { scaleFactor: 2 });
|
|
498
|
+
const out = { x: -1, y: -1 };
|
|
499
|
+
|
|
500
|
+
expect(screenToDipPoint(screen, { x: 24, y: 46 }, out)).toBe(out);
|
|
501
|
+
expect(out).toEqual({ x: 112, y: 73 });
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it('is safe when out aliases the input point', () => {
|
|
505
|
+
const screen = createTestScreen(1, 100, 50, 800, 600, { scaleFactor: 2 });
|
|
506
|
+
const point = { x: 24, y: 46 };
|
|
507
|
+
|
|
508
|
+
screenToDipPoint(screen, point, point);
|
|
135
509
|
|
|
136
|
-
|
|
137
|
-
connectSignal(permission.onChange, vi.fn());
|
|
138
|
-
disposeScreenPermissionChange(permission);
|
|
139
|
-
expect(permission.onChange.data).toBeNull();
|
|
510
|
+
expect(point).toEqual({ x: 112, y: 73 });
|
|
140
511
|
});
|
|
141
512
|
});
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
expect(
|
|
513
|
+
|
|
514
|
+
describe('screenToDipRect', () => {
|
|
515
|
+
it('converts screen-local position and extent to desktop DIPs and returns out', () => {
|
|
516
|
+
const screen = createTestScreen(1, 100, 50, 800, 600, { scaleFactor: 2 });
|
|
517
|
+
const out = { x: -1, y: -1, width: -1, height: -1 };
|
|
518
|
+
|
|
519
|
+
expect(screenToDipRect(screen, { x: 24, y: 46, width: 60, height: 80 }, out)).toBe(out);
|
|
520
|
+
expect(out).toEqual({ x: 112, y: 73, width: 30, height: 40 });
|
|
150
521
|
});
|
|
151
522
|
|
|
152
|
-
it('
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
523
|
+
it('is safe when out aliases the input rectangle', () => {
|
|
524
|
+
const screen = createTestScreen(1, 100, 50, 800, 600, { scaleFactor: 2 });
|
|
525
|
+
const rect = { x: 24, y: 46, width: 60, height: 80 };
|
|
526
|
+
|
|
527
|
+
screenToDipRect(screen, rect, rect);
|
|
528
|
+
|
|
529
|
+
expect(rect).toEqual({ x: 112, y: 73, width: 30, height: 40 });
|
|
157
530
|
});
|
|
158
531
|
});
|
|
159
532
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
533
|
+
function createPermissionChangeHarness() {
|
|
534
|
+
let listener: ((state: ScreenPermissionState) => void) | undefined;
|
|
535
|
+
const unsubscribe = vi.fn();
|
|
536
|
+
const host = {
|
|
537
|
+
screen: {
|
|
538
|
+
permissionChange: createEntity({
|
|
539
|
+
subscribe(next: (state: ScreenPermissionState) => void): () => void {
|
|
540
|
+
listener = next;
|
|
541
|
+
return unsubscribe;
|
|
542
|
+
},
|
|
543
|
+
}),
|
|
544
|
+
},
|
|
545
|
+
};
|
|
546
|
+
return { host, emit: (state: ScreenPermissionState) => listener?.(state), unsubscribe };
|
|
547
|
+
}
|
|
163
548
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
549
|
+
function createScreenChangeHarness() {
|
|
550
|
+
let listener: ((event: Readonly<ScreenChangeEvent>) => void) | undefined;
|
|
551
|
+
const unsubscribe = vi.fn();
|
|
552
|
+
const host = {
|
|
553
|
+
screen: {
|
|
554
|
+
change: createEntity({
|
|
555
|
+
subscribe(next: (event: Readonly<ScreenChangeEvent>) => void): () => void {
|
|
556
|
+
listener = next;
|
|
557
|
+
return unsubscribe;
|
|
558
|
+
},
|
|
559
|
+
}),
|
|
560
|
+
},
|
|
561
|
+
};
|
|
562
|
+
return { host, emit: (event: Readonly<ScreenChangeEvent>) => listener?.(event), unsubscribe };
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function createScreenDetailsHost(permission: ScreenPermissionState, requestResult: boolean) {
|
|
566
|
+
return {
|
|
567
|
+
screen: {
|
|
568
|
+
details: createEntity({
|
|
569
|
+
queryPermission: vi.fn(async () => permission),
|
|
570
|
+
request: vi.fn(async () => requestResult),
|
|
571
|
+
}),
|
|
572
|
+
},
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function createScreenQueryHost(
|
|
577
|
+
screens: readonly ScreenInfo[],
|
|
578
|
+
cursor: Readonly<{ x: number; y: number }> = { x: 0, y: 0 },
|
|
579
|
+
): HasScreenQuery {
|
|
580
|
+
return {
|
|
581
|
+
screen: {
|
|
582
|
+
query: createEntity({
|
|
583
|
+
getCursorPosition: vi.fn((out: { x: number; y: number }) => {
|
|
584
|
+
out.x = cursor.x;
|
|
585
|
+
out.y = cursor.y;
|
|
586
|
+
return out;
|
|
587
|
+
}),
|
|
588
|
+
getPrimaryScreen: vi.fn((out: ScreenInfo) => {
|
|
589
|
+
const primary = screens.find((screen) => screen.isPrimary) ?? screens[0];
|
|
590
|
+
if (primary !== undefined) Object.assign(out, primary);
|
|
591
|
+
return out;
|
|
592
|
+
}),
|
|
593
|
+
getScreens: vi.fn((out: ScreenInfo[]) => {
|
|
594
|
+
out.length = 0;
|
|
595
|
+
out.push(...screens);
|
|
596
|
+
return out;
|
|
597
|
+
}),
|
|
598
|
+
}),
|
|
599
|
+
},
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function createSeparatedScreens(): ScreenInfo[] {
|
|
604
|
+
return [createTestScreen(1, 0, 0, 100, 100), createTestScreen(2, 300, 0, 100, 100)];
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function createTestScreen(
|
|
608
|
+
id: number,
|
|
609
|
+
x: number,
|
|
610
|
+
y: number,
|
|
611
|
+
width: number,
|
|
612
|
+
height: number,
|
|
613
|
+
overrides: Partial<ScreenInfo> = {},
|
|
614
|
+
): ScreenInfo {
|
|
615
|
+
return Object.assign(
|
|
616
|
+
createScreenInfo(),
|
|
617
|
+
{ id, x, y, width, height, workWidth: width, workHeight: height },
|
|
618
|
+
overrides,
|
|
619
|
+
);
|
|
620
|
+
}
|