react-x11 2.39.0 → 2.41.0
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/package.json +3 -3
- package/src/acceleratorhooks.js +24 -8
- package/src/accelerators.js +26 -9
- package/src/backend/context2d.js +292 -35
- package/src/backend/filter.js +285 -0
- package/src/cocoa/app.js +25 -7
- package/src/cocoa/globalmenu.js +32 -1
- package/src/cocoa/keymap.js +69 -12
- package/src/cocoa/panehost.js +161 -13
- package/src/cocoa/paneplayer.js +101 -24
- package/src/cocoa/panewindow.js +325 -44
- package/src/cocoa/window.js +9 -1
- package/src/components/Menu.js +57 -11
- package/src/components/PasswordInput.js +11 -6
- package/src/editmenu.js +9 -2
- package/src/events.js +36 -0
- package/src/frame/index.js +64 -18
- package/src/globalmenu.js +21 -3
- package/src/index.d.ts +4 -1
- package/src/keysyms.d.ts +30 -0
- package/src/keysyms.js +29 -0
- package/src/menuitem.js +63 -17
- package/src/node.d.ts +23 -0
- package/src/nodes/editmenupopup.js +21 -2
- package/src/nodes/textarea.js +88 -16
- package/src/nodes/textinput.js +27 -6
- package/src/nodes/textkeys.js +322 -0
- package/src/textselection.js +7 -7
- package/src/types/components.d.ts +8 -2
- package/src/types/elements.d.ts +1 -1
- package/src/types/events.d.ts +27 -5
- package/src/types/nodes.d.ts +6 -0
- package/src/win32/fonts.js +521 -44
- package/src/win32/panewindow.js +25 -2
package/src/cocoa/panehost.js
CHANGED
|
@@ -12,9 +12,25 @@
|
|
|
12
12
|
// And the players its `<video src>`s ask for, which a process with no
|
|
13
13
|
// AppKit would never hear from: each is a player here, its events and its
|
|
14
14
|
// frames sent back (`HostedPlayer`, src/cocoa/paneplayer.js).
|
|
15
|
+
import { now } from '../nodes/animation.js';
|
|
15
16
|
import { HostedPlayer } from './paneplayer.js';
|
|
16
17
|
import { withoutActions } from './quiet.js';
|
|
17
18
|
|
|
19
|
+
/** The live-resize handshake's budget when the app names none
|
|
20
|
+
* (`RESIZE_WAIT_MS`, src/cocoa/app.js). */
|
|
21
|
+
const RESIZE_WAIT_MS = 50;
|
|
22
|
+
|
|
23
|
+
/** How many sizes asked of the pane are remembered until a frame of one
|
|
24
|
+
* answers them (`_asked`): a drag asks one a frame. */
|
|
25
|
+
const ASKED_KEPT = 16;
|
|
26
|
+
|
|
27
|
+
/** How long after the last size asked a new one starts a gesture of its
|
|
28
|
+
* own, which finds the pane keeping up until it shows otherwise: a pane
|
|
29
|
+
* whose layout at a width it was not dragged to took long — a long page
|
|
30
|
+
* laid out whole, as the first step of a drag is — keeps up with the
|
|
31
|
+
* drag itself. */
|
|
32
|
+
const GESTURE_GAP_MS = 500;
|
|
33
|
+
|
|
18
34
|
/** One shared surface on glass: a sublayer of a window's root layer, its
|
|
19
35
|
* contents the IOSurface the pane last presented into it. */
|
|
20
36
|
class PaneLayer {
|
|
@@ -29,6 +45,82 @@ class PaneLayer {
|
|
|
29
45
|
});
|
|
30
46
|
this.destroyed = false;
|
|
31
47
|
this._rect = null;
|
|
48
|
+
// How the frame the layer holds sits in it while one of the size the
|
|
49
|
+
// layer was just given is on its way (`_gravityNow`): the gravity set,
|
|
50
|
+
// Core Animation's own until one is, the sizes asked of the pane with
|
|
51
|
+
// when each was asked, and whether the pane has fallen behind them.
|
|
52
|
+
this._gravity = 'resize';
|
|
53
|
+
this._contentsScale = null;
|
|
54
|
+
this._asked = [];
|
|
55
|
+
this._askedAt = null;
|
|
56
|
+
this._behind = false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The gravity a pane's last frame sits in its layer by, until a frame of
|
|
61
|
+
* the layer's new size lands. The host gives the layer its new size the
|
|
62
|
+
* moment the window lays out, and the pane's frame of that size comes a
|
|
63
|
+
* pane frame later: Core Animation's default, `resize`, stretches the
|
|
64
|
+
* frame it has to the new bounds meanwhile, and a page's left column was
|
|
65
|
+
* drawn scaled a little at every step of a window dragged wider, and then
|
|
66
|
+
* at its size again. A pane that keeps up has its frame anchored at the
|
|
67
|
+
* top left instead, at its size, what it does not cover yet showing the
|
|
68
|
+
* window under it for a pane frame; one that falls behind the budget is
|
|
69
|
+
* stretched, which is the better picture for the long wait — a pane
|
|
70
|
+
* repainting at three frames a second shows a squeezed page, not a page
|
|
71
|
+
* and a growing strip. The budget is the window's live-resize handshake's
|
|
72
|
+
* (`createRoot({ cocoa: { resizeWait } })`), how long a frame of a new
|
|
73
|
+
* size may take before the old one is shown in its place: 0 stretches
|
|
74
|
+
* always, as before. A bridge that predates `contentsGravity` ignores it,
|
|
75
|
+
* and stretches.
|
|
76
|
+
*/
|
|
77
|
+
_gravityNow() {
|
|
78
|
+
const budget = this.app._resizeWait ?? RESIZE_WAIT_MS;
|
|
79
|
+
if (!(budget > 0)) return 'resize';
|
|
80
|
+
// a size asked longer ago than the budget, and no frame of it yet
|
|
81
|
+
const oldest = this._asked[0];
|
|
82
|
+
if (oldest && now() - oldest.at > budget) this._behind = true;
|
|
83
|
+
return this._behind ? 'resize' : 'topLeft';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** The props that set the gravity, where it changed. */
|
|
87
|
+
_gravityProps() {
|
|
88
|
+
const gravity = this._gravityNow();
|
|
89
|
+
if (gravity === this._gravity) return null;
|
|
90
|
+
this._gravity = gravity;
|
|
91
|
+
// a frame anchored and larger than the layer would hang out of it
|
|
92
|
+
return { contentsGravity: gravity, masksToBounds: gravity !== 'resize' };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The props that say how many of the pane's pixels make a point of the
|
|
97
|
+
* layer, where that changed: the window's scale, which the pane draws
|
|
98
|
+
* at. A frame stretched to the bounds never asked, and one anchored at
|
|
99
|
+
* its size is shown at it — pixels over `contentsScale` points, so at
|
|
100
|
+
* the default of 1 a page at 2x came out twice its size.
|
|
101
|
+
*/
|
|
102
|
+
_scaleProps() {
|
|
103
|
+
const scale = this.wnd.scale ?? 1;
|
|
104
|
+
if (scale === this._contentsScale) return null;
|
|
105
|
+
this._contentsScale = scale;
|
|
106
|
+
return { contentsScale: scale };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** A frame of `width` by `height` device px landed: what it answers of
|
|
110
|
+
* the sizes asked, and how long it took. */
|
|
111
|
+
_answered(width, height) {
|
|
112
|
+
const asked = this._asked;
|
|
113
|
+
// the pane rounds the logical size it was sent back to device pixels
|
|
114
|
+
const slack = Math.max(2, this.wnd.scale ?? 1);
|
|
115
|
+
const at = asked.findIndex(
|
|
116
|
+
(a) =>
|
|
117
|
+
Math.abs(a.width - width) <= slack &&
|
|
118
|
+
Math.abs(a.height - height) <= slack,
|
|
119
|
+
);
|
|
120
|
+
if (at < 0) return;
|
|
121
|
+
this._behind =
|
|
122
|
+
now() - asked[at].at > (this.app._resizeWait ?? RESIZE_WAIT_MS);
|
|
123
|
+
asked.splice(0, at + 1);
|
|
32
124
|
}
|
|
33
125
|
|
|
34
126
|
/** Geometry in device px, the node's abs — points at the layer. */
|
|
@@ -46,6 +138,17 @@ class PaneLayer {
|
|
|
46
138
|
return;
|
|
47
139
|
}
|
|
48
140
|
this._rect = { ...rect };
|
|
141
|
+
// a size the pane is asked for, answered by the first frame of it
|
|
142
|
+
if (prev && (prev.width !== rect.width || prev.height !== rect.height)) {
|
|
143
|
+
const at = now();
|
|
144
|
+
if (at - (this._askedAt ?? -Infinity) >= GESTURE_GAP_MS) {
|
|
145
|
+
this._asked = [];
|
|
146
|
+
this._behind = false;
|
|
147
|
+
}
|
|
148
|
+
this._askedAt = at;
|
|
149
|
+
if (this._asked.length === ASKED_KEPT) this._asked.shift();
|
|
150
|
+
this._asked.push({ width: rect.width, height: rect.height, at });
|
|
151
|
+
}
|
|
49
152
|
// Actions off: the layer is ours, not a presenter's, so no frame's
|
|
50
153
|
// transaction covers it, and a bare set tweens the pane into place at
|
|
51
154
|
// mount and after every resize — the trap CocoaGLArea._setLayerProps
|
|
@@ -57,6 +160,8 @@ class PaneLayer {
|
|
|
57
160
|
frame: [rect.x / s, rect.y / s, rect.width / s, rect.height / s],
|
|
58
161
|
zPosition: 1e7,
|
|
59
162
|
hidden: false,
|
|
163
|
+
...this._scaleProps(),
|
|
164
|
+
...this._gravityProps(),
|
|
60
165
|
});
|
|
61
166
|
} finally {
|
|
62
167
|
native.txCommit();
|
|
@@ -67,18 +172,22 @@ class PaneLayer {
|
|
|
67
172
|
* A pane-present landed: scan out of the named shared surface.
|
|
68
173
|
*
|
|
69
174
|
* A present names a buffer the pane may since have retired. The channel
|
|
70
|
-
* is a queue
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
175
|
+
* is a queue, so a pane-rect that changes the pane's size (or its scale —
|
|
176
|
+
* the host window moved to another display during startup, which is how
|
|
177
|
+
* this was first hit) can cross a present already in flight: the pane
|
|
178
|
+
* rebuilds its ring on that rect (`CocoaPaneWindow._ensureSurface`)
|
|
179
|
+
* before the host has looked the id up. The id is all the present
|
|
180
|
+
* carries, and the system gives a freed id to the next surface made, so
|
|
181
|
+
* the pane keeps such a buffer until the host says it has looked it up
|
|
182
|
+
* (`pane-shown`, which `CocoaPaneHost` sends once it has) — the lookup finds
|
|
183
|
+
* the frame the present named, a size behind, and the full frame on the
|
|
184
|
+
* fresh ring is queued behind it. Where the buffer is gone anyway — a
|
|
185
|
+
* pane that freed it for want of an answer, or one that died with
|
|
186
|
+
* presents in the channel — the present is dropped, and the layer keeps
|
|
187
|
+
* the frame it already holds a reference to. Nothing else here throws;
|
|
188
|
+
* any other error is the bug it says it is.
|
|
80
189
|
*/
|
|
81
|
-
present(iosurfaceId) {
|
|
190
|
+
present(iosurfaceId, size = null) {
|
|
82
191
|
if (this.destroyed) return false;
|
|
83
192
|
try {
|
|
84
193
|
this._native.setLayerContentsIOSurface(this.layer, iosurfaceId);
|
|
@@ -86,6 +195,17 @@ class PaneLayer {
|
|
|
86
195
|
if (!/IOSurfaceLookup/.test(err?.message ?? '')) throw err;
|
|
87
196
|
return false;
|
|
88
197
|
}
|
|
198
|
+
// how long the pane took to a frame of a size it was asked for, which
|
|
199
|
+
// is what the gravity follows (`_gravityNow`)
|
|
200
|
+
if (size) {
|
|
201
|
+
this._answered(size.width, size.height);
|
|
202
|
+
const gravity = this._gravityProps();
|
|
203
|
+
if (gravity) {
|
|
204
|
+
withoutActions(this._native, () =>
|
|
205
|
+
this._native.setLayerProps(this.layer, gravity),
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
89
209
|
return true;
|
|
90
210
|
}
|
|
91
211
|
|
|
@@ -144,6 +264,28 @@ export class CocoaPaneHost extends PaneLayer {
|
|
|
144
264
|
this._players = new Map();
|
|
145
265
|
}
|
|
146
266
|
|
|
267
|
+
/**
|
|
268
|
+
* The pane's own present, scanned out (`PaneLayer.present`), and the pane
|
|
269
|
+
* told: whatever the buffer it named holds is the layer's now, or was
|
|
270
|
+
* dropped, so the pane may free it (`CocoaPaneWindow._releaseRing`).
|
|
271
|
+
*/
|
|
272
|
+
present(iosurfaceId, size = null) {
|
|
273
|
+
const shown = super.present(iosurfaceId, size);
|
|
274
|
+
this._acknowledge(size?.seq, null);
|
|
275
|
+
return shown;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** `pane-shown`: the host has looked up the presents to `seq` of the
|
|
279
|
+
* pane's own window, or of the one `window` names. */
|
|
280
|
+
_acknowledge(seq, window) {
|
|
281
|
+
if (this.destroyed || seq == null) return;
|
|
282
|
+
this._send?.({
|
|
283
|
+
type: 'pane-shown',
|
|
284
|
+
seq,
|
|
285
|
+
...(window != null && { window }),
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
147
289
|
/**
|
|
148
290
|
* A `pane-player` message: one of the pane's `<video src>`s made, steered
|
|
149
291
|
* or let go of its player. A player that cannot be made says so the way
|
|
@@ -259,9 +401,15 @@ export class CocoaPaneHost extends PaneLayer {
|
|
|
259
401
|
/** A pane-present naming one of the pane's other windows. */
|
|
260
402
|
presentWindow(msg) {
|
|
261
403
|
const entry = this._windows.get(msg.window);
|
|
262
|
-
if (!entry || this.destroyed)
|
|
404
|
+
if (!entry || this.destroyed) {
|
|
405
|
+
this._acknowledge(msg.seq, msg.window);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
263
408
|
entry.layer.setRect({ x: 0, y: 0, width: msg.width, height: msg.height });
|
|
264
|
-
|
|
409
|
+
const shown = entry.layer.present(msg.id);
|
|
410
|
+
// its ring is the pane's own ring, retired the same way
|
|
411
|
+
this._acknowledge(msg.seq, msg.window);
|
|
412
|
+
if (!shown) return;
|
|
265
413
|
entry.presented = true;
|
|
266
414
|
// a transparent window's shadow is the shape of what it shows
|
|
267
415
|
entry.wnd._shadowAfterFlip?.();
|
package/src/cocoa/paneplayer.js
CHANGED
|
@@ -18,6 +18,15 @@
|
|
|
18
18
|
// other: the host writes the buffer after the one it last named, so the one
|
|
19
19
|
// the pane is told about is not written again for two frames — some 66ms at
|
|
20
20
|
// 30fps, against a pane that copies within a display frame of hearing.
|
|
21
|
+
//
|
|
22
|
+
// A buffer is named by its IOSurface id, and the system gives a freed id to
|
|
23
|
+
// the next surface anyone makes, in either process. So the pane answers
|
|
24
|
+
// each frame once it has looked the buffer up (`seen`), the host keeps a
|
|
25
|
+
// buffer of a ring it retired until the frame naming it is answered
|
|
26
|
+
// (`HostedPlayer._freeRing`), and the pane keeps nothing a lookup found at
|
|
27
|
+
// another size than the frame says (`PanePlayer._lookUp`) — the pane's own
|
|
28
|
+
// presents, the other way round, are kept the same way
|
|
29
|
+
// (`CocoaPaneWindow._releaseRing`).
|
|
21
30
|
|
|
22
31
|
import { PlayerState } from './player.js';
|
|
23
32
|
|
|
@@ -113,27 +122,13 @@ export class PanePlayer extends PlayerState {
|
|
|
113
122
|
}
|
|
114
123
|
|
|
115
124
|
/** The host named a buffer with a new frame in it. */
|
|
116
|
-
_frame({ surface, width, height, time }) {
|
|
125
|
+
_frame({ surface, width, height, time, seq }) {
|
|
117
126
|
if (this.released) return;
|
|
118
|
-
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
) {
|
|
124
|
-
this._dropBuffers();
|
|
125
|
-
this._bufferSize = { width, height };
|
|
126
|
-
}
|
|
127
|
-
let handle = this._buffers.get(surface);
|
|
128
|
-
if (!handle) {
|
|
129
|
-
try {
|
|
130
|
-
handle = this._native.surfaceFromIOSurfaceID(surface, 1).handle;
|
|
131
|
-
} catch {
|
|
132
|
-
// freed before it got here: the next frame is in another buffer
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
this._buffers.set(surface, handle);
|
|
136
|
-
}
|
|
127
|
+
const handle = this._lookUp(surface, width, height);
|
|
128
|
+
// this frame's lookup is done, whatever it found: the host may free the
|
|
129
|
+
// buffer, which it kept for it (`HostedPlayer._freeRing`)
|
|
130
|
+
if (seq != null) this._post({ op: 'seen', seq });
|
|
131
|
+
if (!handle) return;
|
|
137
132
|
this._shown = { handle, width, height, time };
|
|
138
133
|
this._fresh = true;
|
|
139
134
|
// a playing `<video>` asks every display frame; a paused one asks when
|
|
@@ -148,6 +143,40 @@ export class PanePlayer extends PlayerState {
|
|
|
148
143
|
}
|
|
149
144
|
}
|
|
150
145
|
|
|
146
|
+
/**
|
|
147
|
+
* The buffer the host named, looked up the first time it is named; null
|
|
148
|
+
* where the lookup found none, or found a surface of another size than
|
|
149
|
+
* the frame, which is not the frame's buffer but whatever the id has gone
|
|
150
|
+
* to since (`HostedPlayer._freeRing` says how) — never kept, and never
|
|
151
|
+
* copied out of.
|
|
152
|
+
*/
|
|
153
|
+
_lookUp(surface, width, height) {
|
|
154
|
+
// a ring of another size is a new ring: the old one's buffers are the
|
|
155
|
+
// host's to free, and these handles only keep them alive
|
|
156
|
+
if (
|
|
157
|
+
this._bufferSize?.width !== width ||
|
|
158
|
+
this._bufferSize?.height !== height
|
|
159
|
+
) {
|
|
160
|
+
this._dropBuffers();
|
|
161
|
+
this._bufferSize = { width, height };
|
|
162
|
+
}
|
|
163
|
+
const known = this._buffers.get(surface);
|
|
164
|
+
if (known) return known;
|
|
165
|
+
let found;
|
|
166
|
+
try {
|
|
167
|
+
found = this._native.surfaceFromIOSurfaceID(surface, 1);
|
|
168
|
+
} catch {
|
|
169
|
+
// freed before it got here: the next frame is in another buffer
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
if (found.width !== width || found.height !== height) {
|
|
173
|
+
this._native.releaseSurface?.(found.handle);
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
this._buffers.set(surface, found.handle);
|
|
177
|
+
return found.handle;
|
|
178
|
+
}
|
|
179
|
+
|
|
151
180
|
/** As `CocoaPlayer.copyFrame`: the newest frame into `surface`, or null
|
|
152
181
|
* when the host has sent nothing since the last copy. */
|
|
153
182
|
copyFrame(surface) {
|
|
@@ -212,6 +241,12 @@ export class HostedPlayer {
|
|
|
212
241
|
this._next = 0;
|
|
213
242
|
this._want = 0;
|
|
214
243
|
this._pull = null;
|
|
244
|
+
// Each frame's number, the newest the pane has looked up (`seen`), and
|
|
245
|
+
// the buffers of retired rings a frame it has not looked up yet names:
|
|
246
|
+
// freed as it does (`_freeRing`).
|
|
247
|
+
this._seq = 0;
|
|
248
|
+
this._seenSeq = 0;
|
|
249
|
+
this._retired = [];
|
|
215
250
|
this._off = this.player._subscribe((ev) => this._event(ev));
|
|
216
251
|
}
|
|
217
252
|
|
|
@@ -221,6 +256,7 @@ export class HostedPlayer {
|
|
|
221
256
|
if (player.released) return;
|
|
222
257
|
if (msg.op === 'set') player.set(msg.settings);
|
|
223
258
|
else if (msg.op === 'seek') player.seek(msg.seconds);
|
|
259
|
+
else if (msg.op === 'seen') this._seen(msg.seq);
|
|
224
260
|
else if (msg.op === 'release') this.release();
|
|
225
261
|
}
|
|
226
262
|
|
|
@@ -274,6 +310,9 @@ export class HostedPlayer {
|
|
|
274
310
|
);
|
|
275
311
|
if (got?.written) {
|
|
276
312
|
this._next = (this._next + 1) % RING;
|
|
313
|
+
// the buffer is named in the channel until the pane says otherwise
|
|
314
|
+
// (`_freeRing`)
|
|
315
|
+
buffer.namedSeq = ++this._seq;
|
|
277
316
|
this._send({
|
|
278
317
|
type: 'pane-player-frame',
|
|
279
318
|
player: this.id,
|
|
@@ -281,6 +320,7 @@ export class HostedPlayer {
|
|
|
281
320
|
width: got.width,
|
|
282
321
|
height: got.height,
|
|
283
322
|
time: got.time,
|
|
323
|
+
seq: this._seq,
|
|
284
324
|
});
|
|
285
325
|
if (!player.playing) this._want = 0;
|
|
286
326
|
} else if (got?.width > 0 && got?.height > 0) {
|
|
@@ -316,13 +356,46 @@ export class HostedPlayer {
|
|
|
316
356
|
return this._ring[this._next];
|
|
317
357
|
}
|
|
318
358
|
|
|
359
|
+
/**
|
|
360
|
+
* Let the ring go — all but a buffer a frame names that the pane has not
|
|
361
|
+
* looked up yet. The pane finds a buffer by its IOSurface id, and the
|
|
362
|
+
* system hands a freed id to the next surface anyone makes: the ring of
|
|
363
|
+
* the new size this same call is about to make, the pane's own ring,
|
|
364
|
+
* another process's surface. A frame still in the channel named the id,
|
|
365
|
+
* not the surface, so the pane copied out of whatever had it by the time
|
|
366
|
+
* it looked — the next ring's buffer, unwritten, shown until the next
|
|
367
|
+
* frame came, which for a paused player is the next seek. So that buffer
|
|
368
|
+
* is kept (`_retired`) until the pane says it has looked the frame up
|
|
369
|
+
* (`seen`, `_seen`). One, as a rule, since the pane answers as it reads,
|
|
370
|
+
* and never more than a ring's worth: a pane that stops answering has the
|
|
371
|
+
* oldest freed anyway, the one it is likeliest to have looked up already.
|
|
372
|
+
*/
|
|
319
373
|
_freeRing() {
|
|
374
|
+
const ring = this._ring;
|
|
375
|
+
this._ring = [];
|
|
320
376
|
const release = this._native.releaseSurface;
|
|
321
|
-
if (typeof release
|
|
322
|
-
|
|
323
|
-
|
|
377
|
+
if (typeof release !== 'function') return;
|
|
378
|
+
for (const buffer of ring) {
|
|
379
|
+
if (buffer.namedSeq > this._seenSeq) this._retired.push(buffer);
|
|
380
|
+
else release.call(this._native, buffer.handle);
|
|
324
381
|
}
|
|
325
|
-
this.
|
|
382
|
+
while (this._retired.length > RING) {
|
|
383
|
+
release.call(this._native, this._retired.shift().handle);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** The pane has looked up every frame up to `seq`: the retired buffers
|
|
388
|
+
* they named go. */
|
|
389
|
+
_seen(seq) {
|
|
390
|
+
if (!(seq > this._seenSeq)) return;
|
|
391
|
+
this._seenSeq = seq;
|
|
392
|
+
if (this._retired.length === 0) return;
|
|
393
|
+
const release = this._native.releaseSurface;
|
|
394
|
+
this._retired = this._retired.filter((buffer) => {
|
|
395
|
+
if (buffer.namedSeq > seq) return true;
|
|
396
|
+
release.call(this._native, buffer.handle);
|
|
397
|
+
return false;
|
|
398
|
+
});
|
|
326
399
|
}
|
|
327
400
|
|
|
328
401
|
release() {
|
|
@@ -332,5 +405,9 @@ export class HostedPlayer {
|
|
|
332
405
|
this._want = 0;
|
|
333
406
|
this._syncPull();
|
|
334
407
|
this._freeRing();
|
|
408
|
+
// and what it kept: the pane's player is gone, or the pane is
|
|
409
|
+
for (const buffer of this._retired.splice(0)) {
|
|
410
|
+
this._native.releaseSurface(buffer.handle);
|
|
411
|
+
}
|
|
335
412
|
}
|
|
336
413
|
}
|