react-x11 2.52.0 → 2.53.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/README.md +10 -10
- package/docs/README.md +160 -314
- package/docs/accessibility.md +176 -229
- package/docs/appearance.md +77 -176
- package/docs/click-to-component.md +18 -51
- package/docs/clipboard.md +40 -59
- package/docs/components.md +518 -1036
- package/docs/dbus.md +61 -133
- package/docs/debugging.md +152 -245
- package/docs/desktop-calendar.md +51 -88
- package/docs/desktop.md +222 -347
- package/docs/devtools.md +50 -167
- package/docs/drag-and-drop.md +126 -204
- package/docs/ecosystem/animation.md +91 -125
- package/docs/ecosystem/data-fetching.md +38 -53
- package/docs/ecosystem/dev-tooling.md +159 -202
- package/docs/ecosystem/forms.md +61 -97
- package/docs/ecosystem/headless.md +64 -89
- package/docs/ecosystem/i18n.md +18 -21
- package/docs/ecosystem/icons.md +93 -126
- package/docs/ecosystem/layout.md +42 -52
- package/docs/ecosystem/routing.md +18 -29
- package/docs/ecosystem/state.md +71 -88
- package/docs/ecosystem/testing.md +131 -157
- package/docs/ecosystem/theming.md +119 -165
- package/docs/ecosystem.md +145 -179
- package/docs/elements.md +1091 -1774
- package/docs/embedding.md +83 -132
- package/docs/events.md +412 -695
- package/docs/extending.md +784 -1326
- package/docs/eyedropper.md +63 -122
- package/docs/filedialog.md +71 -107
- package/docs/frame.md +195 -339
- package/docs/gl.md +65 -62
- package/docs/globalmenu.md +63 -146
- package/docs/macos.md +531 -3234
- package/docs/notifications.md +33 -47
- package/docs/packaging.md +166 -309
- package/docs/permissions.md +9 -14
- package/docs/react-features.md +81 -106
- package/docs/remote.md +32 -76
- package/docs/scale.md +72 -186
- package/docs/styling.md +329 -547
- package/docs/system.md +200 -280
- package/docs/testing.md +63 -83
- package/docs/typescript.md +6 -48
- package/docs/uri-schemes.md +67 -107
- package/docs/wayland-backend.md +200 -459
- package/docs/windows-integrations.md +75 -131
- package/docs/windows.md +343 -1367
- package/package.json +3 -2
- package/src/Reconciler.js +2 -2
- package/src/a11y.js +1 -1
- package/src/backend/context2d.js +3 -3
- package/src/bootstrap.js +1 -1
- package/src/cocoa/app.js +6 -6
- package/src/cocoa/bezels.js +1 -1
- package/src/cocoa/fonts.js +1 -1
- package/src/cocoa/globalmenu.js +1 -1
- package/src/cocoa/keymap.js +1 -1
- package/src/cocoa/panewindow.js +1 -1
- package/src/cocoa/player.js +1 -1
- package/src/cocoa/presenter.js +2 -2
- package/src/cocoa/promotion.js +1 -1
- package/src/cocoa/relaunch.js +1 -1
- package/src/cocoa/threaded.js +1 -1
- package/src/cocoa/video.js +1 -1
- package/src/cocoa/window.js +4 -4
- package/src/components/Menu.js +7 -2
- package/src/components/Switch.js +1 -1
- package/src/components/native.js +1 -1
- package/src/dnd.js +2 -2
- package/src/glnodes.js +1 -1
- package/src/grid.js +1 -1
- package/src/layouts.js +1 -1
- package/src/nodes/animation.js +1 -1
- package/src/nodes/invalidate.js +1 -1
- package/src/nodes/layouthost.js +1 -1
- package/src/nodes/video.js +19 -8
- package/src/nodes/window/droptarget.js +1 -1
- package/src/pacing.js +1 -1
- package/src/refresh/index.js +2 -1
- package/src/types/video.d.ts +5 -2
- package/src/videobackend.js +28 -0
- package/src/videoframes.js +29 -16
- package/src/wayland/app.js +27 -0
- package/src/wayland/video.js +376 -0
- package/src/win32/a11y.js +1 -1
- package/src/win32/app.js +2 -2
- package/src/win32/fonts.js +1 -1
- package/src/win32/glarea.js +4 -4
- package/src/win32/overlay.js +1 -1
- package/src/win32/panewindow.js +3 -3
- package/src/win32/shell.js +1 -1
- package/src/win32/window.js +2 -2
- package/src/windowid.js +1 -1
- package/src/xvideo.js +189 -0
- package/docs/architecture/accessibility.md +0 -470
- package/docs/architecture/animation.md +0 -707
- package/docs/architecture/container-queries.md +0 -525
- package/docs/architecture/custom-layout.md +0 -334
- package/docs/architecture/drag-and-drop.md +0 -941
- package/docs/architecture/element-layer-contents.md +0 -622
- package/docs/architecture/frame-pacing.md +0 -255
- package/docs/architecture/grid-layout.md +0 -358
- package/docs/architecture/protocol-efficiency.md +0 -357
- package/docs/architecture/video.md +0 -1149
- package/docs/architecture/windowed-regions.md +0 -854
- package/docs/glx.md +0 -137
- package/docs/wayland.md +0 -545
- package/docs/windows-embedding.md +0 -271
- package/docs/windows-gl.md +0 -221
package/docs/frame.md
CHANGED
|
@@ -12,61 +12,53 @@ import { Frame } from 'react-x11';
|
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
`<Frame>` mounts a module of your application in a **process of its own** and
|
|
15
|
-
embeds its output here, laid out like any other child
|
|
16
|
-
iframe
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- **Windows** — the same shape as Cocoa with a **DirectComposition surface
|
|
35
|
-
handle** in the IOSurface's place: the pane makes the handle, draws into it
|
|
36
|
-
through a composition swapchain, and duplicates it into the host, which
|
|
37
|
-
gives it to a visual in its own window. The pane has no HWND.
|
|
38
|
-
[windows-embedding.md](windows-embedding.md) has the reasoning; the one
|
|
39
|
-
thing worth knowing from here is that the handle is named **once**, not per
|
|
40
|
-
frame — after that the compositor scans out of whatever the pane last
|
|
41
|
-
presented, so a pane frame costs no message, no fence and no copy.
|
|
42
|
-
|
|
43
|
-
Either way the host owns layout and hit testing and the pane owns its
|
|
44
|
-
drawing, which is the line that makes this **CPU offloading, not
|
|
45
|
-
isolation**.
|
|
46
|
-
|
|
47
|
-
What the process boundary buys is the part no amount of careful coding buys
|
|
48
|
-
in-process:
|
|
49
|
-
|
|
50
|
-
- **Parallelism where this framework is CPU-bound.** The pane's
|
|
51
|
-
reconciliation, layout, text shaping and painting run on another core. A
|
|
52
|
-
pane that grinds for 400ms does not delay the shell's input handling by a
|
|
53
|
-
microsecond — `npm run examples:frame` makes the difference visible with a
|
|
54
|
-
ticker you can watch freeze, or not.
|
|
15
|
+
embeds its output here, laid out like any other child — this package's
|
|
16
|
+
iframe, on X11, Cocoa and win32. On the [Wayland backend](wayland-backend.md)
|
|
17
|
+
a `<Frame>` fails at once: `fallback` renders and no pane is started.
|
|
18
|
+
|
|
19
|
+
- **X11** — the pane renders into a real window of its own, which the host
|
|
20
|
+
embeds over [`<foreign>`](embedding.md).
|
|
21
|
+
- **Cocoa** — the pane has no `NSWindow` and runs no AppKit: it paints into
|
|
22
|
+
shared `IOSurface`s the host shows in a layer of its window, and every other
|
|
23
|
+
window it makes (a menu, a dropdown's sheet, a tooltip, a dialog) the host
|
|
24
|
+
shows in an `NSWindow` of its own.
|
|
25
|
+
- **Windows** — the same, with a DirectComposition surface; the pane has no
|
|
26
|
+
HWND.
|
|
27
|
+
|
|
28
|
+
The host owns layout and hit testing, the pane its drawing: **CPU
|
|
29
|
+
offloading, not isolation**. What the process boundary buys:
|
|
30
|
+
|
|
31
|
+
- **Parallelism.** The pane's reconciliation, layout, text shaping and
|
|
32
|
+
painting run on another core; a pane that grinds for 400ms does not delay
|
|
33
|
+
the shell's input (`npm run examples:frame` shows it with a ticker).
|
|
55
34
|
- **Crash isolation.** An uncaught throw, a native-module crash, an OOM: the
|
|
56
|
-
pane dies,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
pauses are its own.
|
|
62
|
-
|
|
63
|
-
And what it does not buy, said once here and again in
|
|
64
|
-
[security.md](security.md): **it is not a security boundary.** On X11 the
|
|
35
|
+
pane dies, `fallback` renders, `restart()` respawns.
|
|
36
|
+
- **Leak isolation.** A leaking pane is a killable leak, bounded by the heap
|
|
37
|
+
its `execArgv` gives it, and its GC pauses are its own.
|
|
38
|
+
|
|
39
|
+
**It is not a security boundary** ([security.md](security.md)). On X11 the
|
|
65
40
|
pane holds a full-privilege connection to the same X server — it can read
|
|
66
41
|
keys, take screenshots, move windows — and on Cocoa it is an ordinary child
|
|
67
|
-
process
|
|
68
|
-
|
|
69
|
-
|
|
42
|
+
process with your file system and your network. `<Frame>` contains a pane's
|
|
43
|
+
_failures_, not its intentions.
|
|
44
|
+
|
|
45
|
+
| prop | |
|
|
46
|
+
| --------------------- | ------------------------------------------------------------------------ |
|
|
47
|
+
| `src` | the pane module: a `URL` or an absolute path |
|
|
48
|
+
| `props` | data for the pane; functions in it become stubs |
|
|
49
|
+
| `style` | the pane's rect; `backgroundColor` shows while no pane frame does |
|
|
50
|
+
| `fallback` | element or `({ error, restart }) => …` for a pane that died or never ran |
|
|
51
|
+
| `bridge` | bridged contexts: `true` (default), `false`, or a list of keys |
|
|
52
|
+
| `display` | X display for the pane; default the inherited `$DISPLAY` |
|
|
53
|
+
| `execArgv`, `env` | flags and environment for the pane's process |
|
|
54
|
+
| `watchdog` | ms unanswered before the pane counts as stuck; 15000, or `false` |
|
|
55
|
+
| `focusable` | whether the pane joins the tab order, like any `<foreign>` |
|
|
56
|
+
| `onStarted`, `onExit` | the pane mounted; its process ended |
|
|
57
|
+
| `transport` | replaces the fork |
|
|
58
|
+
| `ref` | `{ restart(), pid }` |
|
|
59
|
+
|
|
60
|
+
Design notes (the display boundary per backend, and how a Cocoa pane's
|
|
61
|
+
frames, windows and videos travel): [design/frame.md](../design/frame.md).
|
|
70
62
|
|
|
71
63
|
## The pane module
|
|
72
64
|
|
|
@@ -97,27 +89,24 @@ if (!isFramed() && !process.env.REACT_X11_NO_AUTORUN) {
|
|
|
97
89
|
}
|
|
98
90
|
```
|
|
99
91
|
|
|
100
|
-
The pane process inherits the parent's `execArgv`, which
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
92
|
+
The pane process inherits the parent's `execArgv`, which carries a dev loader
|
|
93
|
+
(tsx, the refresh loader) into it during development; a bundled application
|
|
94
|
+
forks plain JavaScript and needs none. It connects to the display the host
|
|
95
|
+
names in `display`, or `$DISPLAY`. What else it is started with is the
|
|
96
|
+
`<Frame>`'s to say — see
|
|
105
97
|
[The pane process](#the-pane-process-flags-and-environment).
|
|
106
98
|
|
|
107
|
-
On macOS
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
pane was another `node` in the Dock beside its host, one for each tab of the
|
|
119
|
-
components' browser example. A root that names `cocoa.activationPolicy`
|
|
120
|
-
itself still has the one it names.
|
|
99
|
+
On macOS:
|
|
100
|
+
|
|
101
|
+
- A pane stays on its process's main thread, where an app moves onto a
|
|
102
|
+
worker ([macos.md](macos.md#js-on-a-worker-a-ui-thread-of-the-bridges-own)):
|
|
103
|
+
it has no AppKit for that thread to keep, and its IPC channel is that
|
|
104
|
+
thread's (a worker has no `process.send`). That holds under
|
|
105
|
+
`--import react-x11/cocoa-main` too, which a pane inherits from its host.
|
|
106
|
+
- A pane launches with AppKit's `prohibited` activation policy: no Dock tile,
|
|
107
|
+
no ⌘-Tab entry, never the active app — its host's window has the focus and
|
|
108
|
+
forwards what the pane is owed. A root that names `cocoa.activationPolicy`
|
|
109
|
+
itself still has the one it names.
|
|
121
110
|
|
|
122
111
|
## Props: a bag of data, and stubs for the functions in it
|
|
123
112
|
|
|
@@ -130,17 +119,16 @@ travel in the **same message**, so a theme flip and the state change that
|
|
|
130
119
|
caused it land in the pane as one commit rather than a torn pair.
|
|
131
120
|
|
|
132
121
|
Functions anywhere in the bag become **fire-and-forget stubs** on the pane
|
|
133
|
-
side: calling one sends its arguments back and returns `undefined`.
|
|
134
|
-
arguments are
|
|
135
|
-
|
|
136
|
-
|
|
122
|
+
side: calling one sends its arguments back and returns `undefined`. Functions
|
|
123
|
+
inside the arguments are dropped and the data around them kept, so a pane can
|
|
124
|
+
call `onPick(item, ev)` the way it would call any handler, though `ev` is
|
|
125
|
+
full of methods.
|
|
137
126
|
|
|
138
127
|
Callback identity follows function identity: a handler the parent keeps
|
|
139
128
|
stable (`useCallback`) keeps its id across updates, and an id from the
|
|
140
|
-
previous update still lands — one update of grace, which
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
wrong function.
|
|
129
|
+
previous update still lands — one update of grace, which covers a click
|
|
130
|
+
racing a props change. A stub stashed outside the props flow and called two
|
|
131
|
+
updates later is dropped with a warning, not delivered to the wrong function.
|
|
144
132
|
|
|
145
133
|
Two rules of thumb that keep the wire small:
|
|
146
134
|
|
|
@@ -154,25 +142,18 @@ Two rules of thumb that keep the wire small:
|
|
|
154
142
|
|
|
155
143
|
## Context: bridged explicitly, theme by default
|
|
156
144
|
|
|
157
|
-
React context cannot cross a process boundary, and most of it
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
palette rather than the pane process's desktop, and the window is the top
|
|
170
|
-
of the node tree every `$token` beneath it resolves through — so a theme
|
|
171
|
-
toggle in the host moves the pane's background and its static text, not
|
|
172
|
-
only what re-renders through `useTheme()`. With no provider above the
|
|
173
|
-
frame, nothing is bridged and the pane follows the desktop by itself: same
|
|
174
|
-
answer, no bridge. A pane that wants its own look mounts its own provider,
|
|
175
|
-
which wins below the bridged one the way an inner provider always wins.
|
|
145
|
+
React context cannot cross a process boundary, and most of it need not:
|
|
146
|
+
appearance, locale, desktop settings and fonts resolve identically on both
|
|
147
|
+
sides. The rule: **bridge what the app defines; let each process resolve what
|
|
148
|
+
the desktop defines.**
|
|
149
|
+
|
|
150
|
+
The theme crosses **by default**: a `<ThemeProvider>` above a `<Frame>` wraps
|
|
151
|
+
the pane's **window** as a real `ThemeProvider` — `useTheme()` and `$token`
|
|
152
|
+
styles alike — from the pane's very first commit, so the pane's background
|
|
153
|
+
and static text follow the app's palette and its toggles, not the pane
|
|
154
|
+
process's desktop. With no provider above the frame, nothing is bridged and
|
|
155
|
+
the pane follows the desktop by itself. A pane's own provider wins below the
|
|
156
|
+
bridged one, as an inner provider always does.
|
|
176
157
|
|
|
177
158
|
For a context of your own, create it with `createFrameContext` in a module
|
|
178
159
|
**both sides import** — which the pane already does, since its components
|
|
@@ -202,20 +183,18 @@ re-bridges automatically, because the pane's providers republish what they
|
|
|
202
183
|
received.
|
|
203
184
|
|
|
204
185
|
**Bridged values are data.** A value that will not structured-clone is
|
|
205
|
-
dropped with a warning naming the key
|
|
186
|
+
dropped with a warning naming the key. That is deliberate: a bridged
|
|
206
187
|
`{ state, dispatch }` would hand live RPC stubs to every pane under the
|
|
207
|
-
provider
|
|
208
|
-
incarnation
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
shape.
|
|
188
|
+
provider, invisibly, and a restarted pane would leave it unclear which
|
|
189
|
+
incarnation holds which stub. Dispatchers travel in `props`, where the wiring
|
|
190
|
+
is visible. `serialize`/`revive` options on `createFrameContext` cover a
|
|
191
|
+
value that is data in spirit but a class in shape.
|
|
212
192
|
|
|
213
|
-
`bridge` is the off switch
|
|
214
|
-
|
|
193
|
+
`bridge` is the off switch: `bridge={false}` bridges nothing,
|
|
194
|
+
`bridge={['react-x11:theme', 'session']}` is an allowlist.
|
|
215
195
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
boundary has this; nothing here pretends otherwise.
|
|
196
|
+
The pane commits an IPC hop after the host, so a theme flip repaints the
|
|
197
|
+
shell a frame before the panes.
|
|
219
198
|
|
|
220
199
|
## Lifecycle, from both sides
|
|
221
200
|
|
|
@@ -241,35 +220,31 @@ The pane's view:
|
|
|
241
220
|
- **`useFrameClose(handler)`** — the host is letting the pane go: the
|
|
242
221
|
`<Frame>` unmounted, or the host app is exiting. The handler may return a
|
|
243
222
|
promise and may still call callback props — flushing through one is what
|
|
244
|
-
it is for. The patience is
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
- **`isFramed()`** — see above.
|
|
223
|
+
it is for. The patience is bounded: the host asks by message, escalates to
|
|
224
|
+
SIGTERM at 1.5s, SIGKILL at 4s. Not called on a crash; anything that must
|
|
225
|
+
survive a crash belongs on disk before the close.
|
|
226
|
+
- **`isFramed()`** — see [The pane module](#the-pane-module).
|
|
249
227
|
- A host that dies without asking takes the IPC channel with it, and the
|
|
250
228
|
pane exits on the disconnect — no orphans either way.
|
|
251
229
|
|
|
252
230
|
## Sizing, stacking, input
|
|
253
231
|
|
|
254
|
-
The embedded pane is a [`<foreign>`](embedding.md), and its rules apply
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
232
|
+
The embedded pane is a [`<foreign>`](embedding.md), and its rules apply. The
|
|
233
|
+
rect comes from `style` and yoga like any child — there is no content-driven
|
|
234
|
+
sizing across the boundary. On X11 the pane's window is a real X child
|
|
235
|
+
window, so **host-drawn content cannot overlap it**; a HUD or a
|
|
258
236
|
"reconnecting…" banner belongs in a sibling `<popup>`. Pointer events over
|
|
259
237
|
the pane are the pane's without the host ever seeing them; keys follow
|
|
260
|
-
[embedding.md's focus rules](embedding.md#focus-and-who-gets-the-keys) —
|
|
261
|
-
|
|
238
|
+
[embedding.md's focus rules](embedding.md#focus-and-who-gets-the-keys) — the
|
|
239
|
+
host's handlers see every key first, and what they do not consume is
|
|
262
240
|
forwarded.
|
|
263
241
|
|
|
264
|
-
On Cocoa and Windows the pointer
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
pane's own window carries its cursor, as any window does. Keys go over as
|
|
271
|
-
the box's default action, as `<foreign>` forwards them, so a chord the host
|
|
272
|
-
answered above the frame never reaches the pane, and with their modifiers.
|
|
242
|
+
On Cocoa and Windows the pane has no window for the pointer to be over: the
|
|
243
|
+
host hit-tests the frame's box and forwards the input. Keys go over, with
|
|
244
|
+
their modifiers, as the box's default action, as `<foreign>` forwards them, so
|
|
245
|
+
a chord the host answered above the frame never reaches the pane. The cursor
|
|
246
|
+
the pane's tree names is the box's default cursor, so a `cursor` in the
|
|
247
|
+
frame's `style` still wins. On X11 the pane's own window carries its cursor.
|
|
273
248
|
|
|
274
249
|
**Tab goes through a pane as through any subtree.** The frame is one stop in
|
|
275
250
|
the host's order, and the pane's own order hangs off it: Tab arriving at the
|
|
@@ -278,164 +253,61 @@ Tab while the frame has the focus is the pane's; and the pane running off an
|
|
|
278
253
|
end of its order hands Tab back, and the host goes on from the frame. The
|
|
279
254
|
pane lets go of its focus as it does, so the stop it was at shows no ring
|
|
280
255
|
while the host's next one does. A modal open in the pane keeps Tab going
|
|
281
|
-
round inside it.
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
**A pane is drawn at the host's scale.** A pane process reads the screens
|
|
291
|
-
when it starts, and the host read them when it did. A desk that changed in
|
|
292
|
-
between gave the two different answers: a page forked for a new tab after
|
|
293
|
-
the desk went to 1x (a mirror set for a projector) was laid out at 1x in a
|
|
294
|
-
frame the host showed at 2x. Everything in it came out half the size of the
|
|
295
|
-
tabs beside it, and every click it was sent landed twice as far in. So the
|
|
296
|
-
host says what scale its window is drawn at, in the hello and in every
|
|
297
|
-
`pane-rect` (`displayScale`), and a Cocoa or Windows pane takes that one
|
|
298
|
-
instead of its own. Its window, its fonts and the windows it opens are made
|
|
299
|
-
at it from the first commit, and a later `pane-rect` that names another
|
|
300
|
-
scale lays the pane out again in the new unit. A `<box scale>` around the
|
|
301
|
-
`<Frame>` is not part of it: the frame's box grows with the zoom, and the
|
|
302
|
-
pane lays out at the window's scale in the room it was given.
|
|
256
|
+
round inside it.
|
|
257
|
+
|
|
258
|
+
**A pane is drawn at the host's scale.** On Cocoa and Windows the host tells
|
|
259
|
+
the pane the scale its window is drawn at, and the pane's window, fonts and
|
|
260
|
+
the windows it opens are made at that scale from the first commit — even if
|
|
261
|
+
the screens changed between the host's start and the pane's — and laid out
|
|
262
|
+
again when it changes. A `<box scale>` around the `<Frame>` is not part of
|
|
263
|
+
it: the frame's box grows with the zoom, and the pane lays out at the
|
|
264
|
+
window's scale in the room it was given.
|
|
303
265
|
|
|
304
266
|
**A pane's other windows.** On X11 a `<popup>` in a pane is a real window on
|
|
305
|
-
the pane's own connection.
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
-
|
|
313
|
-
|
|
314
|
-
again when the box or the window moves. A popup the pane anchors to one of
|
|
315
|
-
its nodes is placed against it exactly as a window's popup is
|
|
316
|
-
(`windowOrigin`, src/anchor.js), and the host puts the window where the
|
|
317
|
-
pane says.
|
|
318
|
-
- **The window's life goes up, its input comes down.** `pane-window`
|
|
319
|
-
messages make, map, move, resize, grab with and destroy it, and its
|
|
320
|
-
`pane-present`s and `pane-cursor`s name it. Input on the host's window for
|
|
321
|
-
it goes back as `pane-event`s naming it. A grab is the host's, so a press
|
|
322
|
-
anywhere in the host's windows reaches the menu outside its bounds, which
|
|
323
|
-
is the dismissal. A managed window — a `<Dialog>` — also sends back the
|
|
267
|
+
the pane's own connection. On Cocoa the host shows each window the pane makes
|
|
268
|
+
besides its own — a `<popup>`, a menu, a `<Select>`'s list, a `<Dialog>` — in
|
|
269
|
+
an `NSWindow` of its own:
|
|
270
|
+
|
|
271
|
+
- A popup the pane anchors to one of its nodes is placed against it exactly
|
|
272
|
+
as a window's popup is, and placed again when the frame's box or the window
|
|
273
|
+
moves.
|
|
274
|
+
- A grab is the host's, so a press anywhere in the host's windows reaches the
|
|
275
|
+
menu outside its bounds, which is the dismissal. A `<Dialog>` gets the
|
|
324
276
|
user's moves and resizes and its close button.
|
|
325
|
-
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
(
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
live-resize budget, `createRoot({ cocoa: { resizeWait } })` (50 ms by
|
|
360
|
-
default), has its last frame shown at its size from the top left instead:
|
|
361
|
-
cropped on an axis the layer shrank on, and on one it grew on, its last
|
|
362
|
-
pixel carried over the rest — the last column across a window dragged
|
|
363
|
-
wider, the last row down one dragged taller. A page's edge is mostly its
|
|
364
|
-
background, so the strip continues it, a gradient included, where the
|
|
365
|
-
`<Frame>`'s own background showed through before. Where the edge crosses
|
|
366
|
-
content, a bottom row through a line of text, that content runs down the
|
|
367
|
-
strip in thin lines for the frame it lasts. One that falls behind the
|
|
368
|
-
budget is stretched across, which is the better picture of a long wait: a
|
|
369
|
-
width moves where a page's lines break, and a squeezed page is nearer to the
|
|
370
|
-
page laid out again than one cut off with a strip beside it. Down the pane
|
|
371
|
-
it stays at its size, cropped or its last row carried down, since a height
|
|
372
|
-
moves nothing a page lays out, and stretched down it was the page drawn too
|
|
373
|
-
tall for as long as the wait lasted. Behind is
|
|
374
|
-
judged by the frames that land: two in a row later than the budget, or none
|
|
375
|
-
at all for longer than it, and a frame well inside it shows the next at its
|
|
376
|
-
size again. A size the pane is never sent, because a newer one replaced it
|
|
377
|
-
while a frame was being painted, is no frame late, and a fast drag of a
|
|
378
|
-
page that keeps up stays at its size rather than taking one look at every
|
|
379
|
-
step and the other at every frame. Each gesture starts out at its size, and
|
|
380
|
-
`resizeWait: 0` stretches always, both ways, as before. An app can choose
|
|
381
|
-
apart from the budget, which is also how long AppKit holds the window's
|
|
382
|
-
edge for a frame: `createRoot({ cocoa: { paneStretch } })`, or
|
|
383
|
-
`REACT_X11_COCOA_PANE_STRETCH`, is `behind` for all of the above, `never`
|
|
384
|
-
for a last frame kept at its size and its edge carried over however late
|
|
385
|
-
the pane is, and `always` for one stretched both ways. And the pane hears that the window is being resized
|
|
386
|
-
live — AppKit's drag of its edge, from its begin to its end, which a pane
|
|
387
|
-
running no AppKit has no window to hear — with every size and on its own
|
|
388
|
-
where only that changes (`pane-rect`'s `live`, `pane-live`): its window's
|
|
389
|
-
`liveResizing` is the host's, so core defers measuring its content floors
|
|
390
|
-
for the drag's length and catches up once it ends, as a window of its own
|
|
391
|
-
does, and an element can put off what it would rather do once the drag is
|
|
392
|
-
over.
|
|
393
|
-
|
|
394
|
-
**A pane's frames are named by id, and the id outlives nothing.** On Cocoa
|
|
395
|
-
a present carries the IOSurface's id, and the host looks it up when it
|
|
396
|
-
reads the message. A resize has the pane make a new ring of buffers, and
|
|
397
|
-
the system gives a freed id to the next surface anyone makes — the pane's
|
|
398
|
-
next ring, another tab's, the host's own window. A present still in the
|
|
399
|
-
channel when its ring was freed was looked up as whichever surface had
|
|
400
|
-
the id by then: the pane's next frame cleared or half drawn, or another
|
|
401
|
-
tab's page, on glass for a frame at every few steps of a drag. So the host
|
|
402
|
-
answers each present it has looked up (`pane-shown`), and the pane keeps a
|
|
403
|
-
buffer it retired until the present naming it is answered: that is one
|
|
404
|
-
buffer as a rule, and never more than a ring of them, the oldest freed
|
|
405
|
-
first if the host stops answering. A pane's video comes down the same way
|
|
406
|
-
round: the pane answers each frame once it has looked its buffer up, and
|
|
407
|
-
the host keeps a buffer of a ring it retired — a stream that switched
|
|
408
|
-
renditions — until the frame naming it is answered. A lookup that finds a
|
|
409
|
-
surface of another size than its frame says is not copied out of at all.
|
|
410
|
-
|
|
411
|
-
**A pane draws only into a buffer the host is done with.** The ring holds
|
|
412
|
-
three buffers so that one present can be on its way while the next frame
|
|
413
|
-
draws: one on the layer, one named in the channel, one free. Each is made
|
|
414
|
-
the first time a frame takes it, copied over from the frame before: a drag
|
|
415
|
-
gives each size one frame, and a ring made whole at each was two buffers
|
|
416
|
-
made and cleared for nothing, 4.4 ms of every 12 ms frame of a drag of Zen
|
|
417
|
-
Garden 101 in the browser example on a Mac. A host two
|
|
418
|
-
presents behind — busy with a long frame of its own, a breakpoint's layout,
|
|
419
|
-
a window resize — left the next frame nothing but the buffer on the layer or
|
|
420
|
-
the one a present still in the channel named, and the frame drew into it
|
|
421
|
-
anyway: the host then showed a frame half drawn. So a frame takes a buffer
|
|
422
|
-
only once the host has answered a present after the last one that named it,
|
|
423
|
-
and waits on the clock for the next answer when there is none. An answer
|
|
424
|
-
says the host has looked the present up, not that the screen shows it, and
|
|
425
|
-
a host back from a long frame has the buffer it showed throughout still on
|
|
426
|
-
glass until its UI thread commits: so of the buffers the host is done with,
|
|
427
|
-
a frame takes one the WindowServer has let go of too, and waits for one, a
|
|
428
|
-
frame interval at most after the answer. A host that has not answered for
|
|
429
|
-
250 ms no longer holds the pane up.
|
|
430
|
-
|
|
431
|
-
The same answer gates the frame an input gets on the spot
|
|
432
|
-
(`flushPendingFrames`): a pane paints on an input only once its last present
|
|
433
|
-
is answered and a frame interval has passed since it went, and otherwise the
|
|
434
|
-
next frame on the clock answers it. The interval costs an input a few
|
|
435
|
-
milliseconds at the display's rate, and is what keeps a pane at one frame a
|
|
436
|
-
refresh: without it, input faster than the display had a frame each — 245 a
|
|
437
|
-
second on a 120Hz panel, at 250 inputs a second — and the frames that went
|
|
438
|
-
faster than the WindowServer let go of buffers drew into ones it still read.
|
|
277
|
+
- The host window's focus is sent to the pane as `focus`/`blur`, so a menu
|
|
278
|
+
that closes when its window loses focus (`useDismissOnWindowBlur`) closes
|
|
279
|
+
when the user goes to another application.
|
|
280
|
+
- A pane that exits or unmounts takes every window the host shows for it,
|
|
281
|
+
and their grab.
|
|
282
|
+
|
|
283
|
+
**A pane's videos.** On Cocoa the host plays a pane's `<video src>` for it, so
|
|
284
|
+
`useSupports('mediaPlayback')` is true in a pane whose host can play. A pane's
|
|
285
|
+
video is always drawn — it has no layer to lift it onto — so it costs the copy
|
|
286
|
+
a covered video costs in a window, twice: into a shared ring and out. A host
|
|
287
|
+
over a bridge with no player plays nothing, and the pane refuses `src` as X11
|
|
288
|
+
does. A pane that exits takes its players, and their sound, with it.
|
|
289
|
+
|
|
290
|
+
**A pane resized.** On Cocoa the pane's frame at a new size comes a pane frame
|
|
291
|
+
after the host's window lays out. What shows in between is set by
|
|
292
|
+
`createRoot({ cocoa: { paneStretch } })` or `REACT_X11_COCOA_PANE_STRETCH`:
|
|
293
|
+
|
|
294
|
+
- **`'behind'`** (the default) — while the pane keeps within the window's
|
|
295
|
+
live-resize budget, `createRoot({ cocoa: { resizeWait } })` (50 ms by
|
|
296
|
+
default), its last frame is shown at its size from the top left: cropped on
|
|
297
|
+
an axis the layer shrank on, its last column or row carried across one it
|
|
298
|
+
grew on. A pane two frames in a row later than the budget, or with none for
|
|
299
|
+
longer than it, is stretched across to its new width until a frame lands
|
|
300
|
+
well inside the budget; down the pane it stays at its size. Each gesture
|
|
301
|
+
starts out at its size, and `resizeWait: 0` stretches always, both ways.
|
|
302
|
+
- **`'never'`** — at its size, its edge carried over, however late the pane
|
|
303
|
+
is.
|
|
304
|
+
- **`'always'`** — stretched both ways.
|
|
305
|
+
|
|
306
|
+
A carried edge usually continues the page's background; where it crosses
|
|
307
|
+
content, that content runs down the strip in thin lines for a frame. The
|
|
308
|
+
pane's window's `liveResizing` is the host's, so content floors are measured
|
|
309
|
+
once the drag ends, as in a window of its own, and an element can put off
|
|
310
|
+
work until then too.
|
|
439
311
|
|
|
440
312
|
`backgroundColor` in the frame's `style` is what shows before the pane's
|
|
441
313
|
first frame and after one dies — the same server-painted rectangle
|
|
@@ -468,27 +340,25 @@ it inherits:
|
|
|
468
340
|
`REACT_X11_FRAME` is how a pane knows it is one (`isFramed`), so the two
|
|
469
341
|
are set after it and cannot be overridden here.
|
|
470
342
|
|
|
471
|
-
Either is read by value:
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
343
|
+
Either is read by value: the same flags in a new array — one written in
|
|
344
|
+
place, as above, at every render — keep the pane it was, and flags or
|
|
345
|
+
variables that say something else restart it, as a new `src` does. Something
|
|
346
|
+
a fork cannot take — a flag that is not a string — fails as a `spawn` error
|
|
347
|
+
and starts nothing.
|
|
476
348
|
|
|
477
349
|
## A pane that stops answering
|
|
478
350
|
|
|
479
351
|
A pane whose event loop is stuck — a loop that never ends, a page's script
|
|
480
|
-
with no timeout around it —
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
is ended from outside, SIGTERM and then SIGKILL, since it cannot read an
|
|
491
|
-
unmount. `onExit` reports it with `expected: false`.
|
|
352
|
+
with no timeout around it — would otherwise stay `running`: it is sent
|
|
353
|
+
updates it never reads, its window keeps showing its last frame, and on X11
|
|
354
|
+
it holds the keyboard while the pointer is over it.
|
|
355
|
+
|
|
356
|
+
So the host asks. About once a second it sends the pane a `ping`, which the
|
|
357
|
+
pane answers from its event loop. A pane that has not answered for
|
|
358
|
+
`watchdog` ms — 15 seconds by default — goes the way a crashed one goes: the
|
|
359
|
+
frame fails with phase `unresponsive`, `fallback` is shown and can
|
|
360
|
+
`restart()` it, and the process is ended from outside, SIGTERM and then
|
|
361
|
+
SIGKILL, since it cannot read an unmount. `onExit` reports it with `expected: false`.
|
|
492
362
|
|
|
493
363
|
```jsx
|
|
494
364
|
<Frame
|
|
@@ -508,19 +378,17 @@ Three things keep it from ending a pane that is fine:
|
|
|
508
378
|
|
|
509
379
|
- **The host's own late tick is no news of the pane's.** Where the host's
|
|
510
380
|
timer comes late — the machine slept, or the host itself was stuck — the
|
|
511
|
-
count starts again from that tick
|
|
512
|
-
was asking for as time the pane did not answer.
|
|
381
|
+
count starts again from that tick.
|
|
513
382
|
- **A pane under the inspector is not watched.** `--inspect`,
|
|
514
383
|
`--inspect-brk` or `--inspect-wait`, in its own `execArgv`, in this
|
|
515
384
|
process's that it inherits, or in `NODE_OPTIONS`: a debugger stops a pane
|
|
516
385
|
on purpose. The flags that only set the inspector up, which Node's test
|
|
517
|
-
runner hands every test process,
|
|
386
|
+
runner hands every test process, do not count.
|
|
518
387
|
- **`watchdog={false}`** watches nothing, for a pane whose work is long
|
|
519
388
|
and synchronous by design.
|
|
520
389
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
not answering a question it does not know.
|
|
390
|
+
A pane of an older react-x11 that does not know the ping fails the handshake,
|
|
391
|
+
which says why, rather than being ended for not answering.
|
|
524
392
|
|
|
525
393
|
## What a pane costs
|
|
526
394
|
|
|
@@ -533,48 +401,36 @@ not.
|
|
|
533
401
|
|
|
534
402
|
## The transport seam
|
|
535
403
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
env }` — and a transport that still forks the pane, its own way (under a
|
|
548
|
-
sandbox, with another runtime), forks the entry the default one does, which
|
|
549
|
-
is on the exports map to be resolved: `fileURLToPath(import.meta.resolve(
|
|
404
|
+
`transport` replaces the fork wholesale: it is the door to running a pane
|
|
405
|
+
somewhere other than a child process without `<Frame>` learning about it
|
|
406
|
+
(react-x11's own tests run a real pane in-process over a loopback pair this
|
|
407
|
+
way). A transport is handed what the fork would be — `{ src, display,
|
|
408
|
+
execArgv, env }` — and returns the parent's end of the wire: `send`,
|
|
409
|
+
`onMessage`, `onExit`, and optionally `kill` and `pid` (`FrameTransport` in
|
|
410
|
+
the types).
|
|
411
|
+
|
|
412
|
+
A transport that still forks the pane, its own way (under a sandbox, with
|
|
413
|
+
another runtime), forks the entry the default one does, which is on the
|
|
414
|
+
exports map to be resolved: `fileURLToPath(import.meta.resolve(
|
|
550
415
|
'react-x11/frame/child'))`. It is a process's entry, not a module to import:
|
|
551
416
|
outside a fork it says so and exits.
|
|
552
417
|
|
|
553
418
|
## Known gaps
|
|
554
419
|
|
|
555
|
-
Named so they are not rediscovered:
|
|
556
|
-
|
|
557
420
|
- **Accessibility**: a pane registers on the AT-SPI bus as its own
|
|
558
421
|
application. Orca reads it, and Tab crosses the boundary, but the tree is
|
|
559
422
|
not unified under the host's.
|
|
560
423
|
- **DevTools**: a pane is its own React tree; `REACT_X11_DEVTOOLS=1` in the
|
|
561
424
|
pane process attaches a separate DevTools, not a merged one.
|
|
562
|
-
- **No intrinsic size**: the host decides the rect
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
anchors is placed against the screen's corner, the second half of what
|
|
571
|
-
#824 fixed on Cocoa. Not run there.
|
|
425
|
+
- **No intrinsic size**: the host decides the rect.
|
|
426
|
+
- **A window a pane opens in its first commit is placed against a guess**,
|
|
427
|
+
since the host has not laid the pane out yet. A popup anchored to a node is
|
|
428
|
+
placed again before anyone sees it; a `<Dialog>` open from the start is
|
|
429
|
+
centred where the pane guessed it was.
|
|
430
|
+
- **Windows** panes do not know where their frame is on the screen yet: a
|
|
431
|
+
popup a Windows pane anchors is placed against the screen's corner. Not run
|
|
432
|
+
there.
|
|
572
433
|
- **A pane hears its window's focus, not its frame's.** Tab out of a pane
|
|
573
434
|
takes its focus with it, but a click on the host does not: the stop the
|
|
574
435
|
pane had focused keeps its focus, and its ring, while the host's is
|
|
575
|
-
elsewhere.
|
|
576
|
-
waiting for an embedder — see [embedding.md](embedding.md)) speaks
|
|
577
|
-
XEmbed's Tab and no more of its focus handshake — `FOCUS_CURRENT`,
|
|
578
|
-
`FOCUS_OUT` and window activation are left to the forwarding rules above —
|
|
579
|
-
and on Cocoa and Windows the pane is sent the host window's `focus` and
|
|
580
|
-
`blur` alone.
|
|
436
|
+
elsewhere.
|