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/wayland-backend.md
CHANGED
|
@@ -3,14 +3,17 @@
|
|
|
3
3
|
**Status: a working backend, opt-in.** react-x11's own reconciler renders a
|
|
4
4
|
window on a real Wayland compositor (mutter/GNOME, sway, labwc) with
|
|
5
5
|
decorations — the compositor's where it draws them, client-side where it
|
|
6
|
-
does not — text, shadows, paths, images, live partial repaints, pointer
|
|
7
|
-
keyboard input decoded against the compositor's keymap,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
no X server and no
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
implements
|
|
6
|
+
does not — text, shadows, paths, images, live partial repaints, pointer,
|
|
7
|
+
touch, pen and keyboard input decoded against the compositor's keymap, an
|
|
8
|
+
input method, a clipboard, drag and drop, popups, layer-shell docks and
|
|
9
|
+
panels, and a screen capture with an eyedropper on top — drawn on the GPU
|
|
10
|
+
and handed over as dma-buf, on **Node or Bun**, with no X server and no
|
|
11
|
+
Xwayland anywhere in the path.
|
|
12
|
+
|
|
13
|
+
It implements the research RFC in [design/wayland.md](../design/wayland.md);
|
|
14
|
+
where the two disagree, this page is what ran. How it is built, what was
|
|
15
|
+
measured and what is left are in the
|
|
16
|
+
[design notes](../design/wayland-backend.md).
|
|
14
17
|
|
|
15
18
|
```bash
|
|
16
19
|
REACT_X11_BACKEND=wayland node your-app.mjs # x11-dri >= 0.9 (native socket)
|
|
@@ -24,7 +27,9 @@ The two packages it needs beyond react-x11's own are optional dependencies,
|
|
|
24
27
|
installed with it: [`@windowkit/wayland`](https://github.com/windowkit/wayland),
|
|
25
28
|
the protocol layer — a fork of `wayland-client` that passes file descriptors
|
|
26
29
|
— and [`x11-dri`](https://github.com/sidorares/node-x11-dri) 0.9 or later,
|
|
27
|
-
the GPU context and, on Node, the socket
|
|
30
|
+
the GPU context and, on Node, the socket: Wayland passes file descriptors
|
|
31
|
+
(the keymap, shm pools, clipboard pipes) and Node's own sockets cannot. Under
|
|
32
|
+
Bun the socket is node-x11's `bun:ffi` transport instead.
|
|
28
33
|
|
|
29
34
|
The backend is never chosen by `'auto'`: X11 stays the default on Linux
|
|
30
35
|
because the remote case ([remote.md](remote.md)) is the flagship reason this
|
|
@@ -40,36 +45,79 @@ appeared" proves nothing. Three checks that do:
|
|
|
40
45
|
```bash
|
|
41
46
|
env -u DISPLAY REACT_X11_BACKEND=wayland node app.mjs # works only natively
|
|
42
47
|
```
|
|
43
|
-
- **Ask the app.** `root.app.backend === 'wayland'`, and `root.app.X` is
|
|
44
|
-
stand-in
|
|
45
|
-
|
|
48
|
+
- **Ask the app.** `root.app.backend === 'wayland'`, and `root.app.X` is a
|
|
49
|
+
stand-in — no `display.screen`, no `X.require`. The examples print the
|
|
50
|
+
transport they connected through (`connected via x11-dri` or
|
|
46
51
|
`fdpass-bun`) and never import an X module at all.
|
|
47
52
|
- **Ask the desktop.** `xlsclients` and `xwininfo -root -tree` list Xwayland
|
|
48
53
|
clients; a native window is absent from both. GNOME's Looking Glass
|
|
49
54
|
(`Alt+F2`, `lg`, Windows tab) shows it as a `MetaWindowWayland`, and its
|
|
50
|
-
frame is this backend's own titlebar
|
|
51
|
-
|
|
55
|
+
frame is this backend's own titlebar rather than mutter's Adwaita frame,
|
|
56
|
+
which only Xwayland windows get.
|
|
52
57
|
|
|
53
58
|
## What runs
|
|
54
59
|
|
|
55
|
-
| piece |
|
|
56
|
-
| ------------------ |
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
| decorations |
|
|
65
|
-
| layer surfaces |
|
|
66
|
-
| screen capture |
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
| screens
|
|
70
|
-
|
|
|
71
|
-
|
|
72
|
-
|
|
60
|
+
| piece | what an app gets |
|
|
61
|
+
| ------------------ | --------------------------------------------------------------------------------------- |
|
|
62
|
+
| windows and popups | xdg toplevels and popups, fractional scale ([below](#windows-what-maps-what-bends)) |
|
|
63
|
+
| 2d context | rects, rounded rects, paths, exact clips, gradients, patterns, images, text, glyph runs |
|
|
64
|
+
| `<glarea>` | GLES drawing into a rectangle of the window, its children composited over it |
|
|
65
|
+
| offscreen surfaces | the paint cache and scroll blits; a held `Surface` context draws at any time |
|
|
66
|
+
| input | pointer, keyboard decoded against the compositor's keymap, key repeat |
|
|
67
|
+
| touch and tablet | fingers and pens drive the pointer, with pen fields; raw touches |
|
|
68
|
+
| input methods | the compositor's IME (`zwp_text_input_v3`) into the composition events |
|
|
69
|
+
| decorations | the compositor's frame where it offers one, client-side otherwise |
|
|
70
|
+
| layer surfaces | docks, panels, wallpapers, overlays — wlr-layer-shell |
|
|
71
|
+
| screen capture | wlr-screencopy / ext-image-copy-capture; the eyedropper |
|
|
72
|
+
| clipboard | clipboard and primary selection |
|
|
73
|
+
| drag and drop | both directions, in-app drops keep `e.items` by reference; no drag icon yet |
|
|
74
|
+
| screens, keyboard | `useScreens()` and `useKeyboardState()` ([system.md](system.md#on-wayland)) |
|
|
75
|
+
| tests | `test/wayland/`: an in-process compositor, no display needed |
|
|
76
|
+
|
|
77
|
+
The keymap is the compositor's own, so AltGr and levels 3 and 4 work
|
|
78
|
+
because the keymap says which modifier they are, not because one is
|
|
79
|
+
assumed. Compositors do not repeat keys; the backend repeats them at the
|
|
80
|
+
seat's advertised rate.
|
|
81
|
+
|
|
82
|
+
**Touch and pen.** A finger or a tablet tool drives the same mouse events
|
|
83
|
+
as a mouse, and only those events carry `pointerType` (`'touch'`, `'pen'`,
|
|
84
|
+
`'eraser'`, `'mouse'`), `pressure`, `tiltX`/`tiltY`, `rotation`,
|
|
85
|
+
`distance` and `tangentialPressure`, in the DOM's names and ranges
|
|
86
|
+
([events.md](events.md#event-object)); a mouse event is unchanged. Only
|
|
87
|
+
the first finger of a gesture emulates the pointer. Every finger also
|
|
88
|
+
arrives raw on the window as `touchstart`/`touchmove`/`touchend`/
|
|
89
|
+
`touchcancel` (the X11 backend's XI2 names, with `touchId`), each with the
|
|
90
|
+
active `touches`, batched per touch frame. Barrel buttons map as the
|
|
91
|
+
desktop does — BTN_STYLUS is button 3, BTN_STYLUS2 button 2 — and a tool
|
|
92
|
+
leaving proximity releases whatever it still held.
|
|
93
|
+
|
|
94
|
+
**Input methods.** IBus or whatever the compositor runs reaches
|
|
95
|
+
`<textinput>` and `onCompositionStart`/`Update`/`End` exactly as a dead key
|
|
96
|
+
does. Keys the IME consumes never arrive as key events. A `sensitive`
|
|
97
|
+
field offers the IME no surrounding text and says `sensitive_data`;
|
|
98
|
+
`inputMode` on a `<textinput>` becomes the content purpose (`email`, `url`,
|
|
99
|
+
`tel`, …).
|
|
100
|
+
|
|
101
|
+
**`<glarea>`** is a rectangle of the window's own GPU target, not a child
|
|
102
|
+
surface: the area gets the shared GLES context with `viewport`, `scissor`
|
|
103
|
+
and `bindFramebuffer(null)` re-based on its rect, the tree's 2D is drawn
|
|
104
|
+
under it and the panes its children paint on are blended over it.
|
|
105
|
+
|
|
106
|
+
**Perspective images.** `ctx.drawImageProjected(image, m)` draws an image
|
|
107
|
+
through a 3×3 projection (a plane turned out of the page, as CSS
|
|
108
|
+
`rotateY()` in a `perspective` turns one) as one exact quad. It answers
|
|
109
|
+
`false` and draws nothing where a corner is behind the viewer, for the
|
|
110
|
+
caller to draw the part in front some other way. Only this backend has it
|
|
111
|
+
(XRender on glamor folds the image, CoreGraphics and Direct2D draw through
|
|
112
|
+
an affine matrix alone): test with `'drawImageProjected' in ctx` and tile
|
|
113
|
+
where it is absent.
|
|
114
|
+
|
|
115
|
+
**Glyph runs.** `ctx.drawGlyphs(op, src, positioned)`, for callers that
|
|
116
|
+
place their own glyphs (a terminal grid, a tabular column), is spelled as on
|
|
117
|
+
X11 and Cocoa: the op `ctx.Render.PictOp.Over`, the source
|
|
118
|
+
`ctx.createSolidPicture(r, g, b, a)` — or, as Cocoa also takes, a CSS
|
|
119
|
+
colour string, a premultiplied `[r, g, b, a]`, or `null` for the fill style
|
|
120
|
+
in force.
|
|
73
121
|
|
|
74
122
|
### Running the examples
|
|
75
123
|
|
|
@@ -86,276 +134,67 @@ Wayland counterpart. `examples/viewer3d.jsx` draws its model here through
|
|
|
86
134
|
its direct half, GLES on the window's own GPU target — the same half X11
|
|
87
135
|
uses under `NTK_GL_POLICY=direct`.
|
|
88
136
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
137
|
+
Environment variables, for desktops with no screenshot API and for
|
|
138
|
+
debugging:
|
|
139
|
+
|
|
140
|
+
- `REACT_X11_WAYLAND_TRACE=1` writes a line per presented frame.
|
|
141
|
+
- `REACT_X11_WAYLAND_SNAPSHOT=/tmp/frame.png` writes the backing target of
|
|
142
|
+
the first window at its 30th present (`REACT_X11_WAYLAND_SNAPSHOT_AT`
|
|
143
|
+
changes which) — the pixels the compositor was handed, read back from the
|
|
144
|
+
GPU.
|
|
145
|
+
- `REACT_X11_WAYLAND_FRAME_STALL_MS` is how long a frame callback may go
|
|
146
|
+
unanswered before the window counts as not shown (2000; `0` turns that
|
|
147
|
+
off) — see
|
|
148
|
+
[A surface the compositor stops showing stops drawing](#a-surface-the-compositor-stops-showing-stops-drawing).
|
|
149
|
+
- `REACT_X11_GL_DEVICE` names the DRM node to render on.
|
|
98
150
|
|
|
99
151
|
The GPU context is made on `glPolicy.devicePath`, else on
|
|
100
152
|
`REACT_X11_GL_DEVICE`, else on the first render node, and a device named
|
|
101
153
|
either way is the only one tried. A machine with no render node at all — a
|
|
102
154
|
GitHub-hosted runner, a Hyper-V or VirtualBox guest — usually still has a
|
|
103
|
-
card node Mesa renders on in software
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
`swap()` is 0.07 ms and `glFinish()` is ~10 ms — the same at 64×64 as at
|
|
147
|
-
720×480. Size-independent means host round-trip latency, not fill rate; this
|
|
148
|
-
is virgl in a VM. On bare metal the number should mostly disappear and must
|
|
149
|
-
be re-measured there before any conclusion about the backend's performance is
|
|
150
|
-
drawn from it.
|
|
151
|
-
|
|
152
|
-
The 2d context on the UI demo: 252 quads, 239 glyphs and 2 paths in **23
|
|
153
|
-
draw calls**.
|
|
154
|
-
|
|
155
|
-
## The design, where it is not the obvious one
|
|
156
|
-
|
|
157
|
-
**A backing target, not the swapchain, is what gets painted.** react-x11's
|
|
158
|
-
renderer repaints dirty rectangles into a persistent surface, as it would an
|
|
159
|
-
X pixmap. A GBM swapchain rotates, so the buffer a frame lands in last showed
|
|
160
|
-
the frame before last, and a partial repaint into it leaves stale pixels
|
|
161
|
-
everywhere else. So the renderer paints into a persistent render target
|
|
162
|
-
(`target.js`) and the changed rectangles are copied into whichever buffer
|
|
163
|
-
comes round. GBM names the buffer only _after_ the swap, so the copy uses the
|
|
164
|
-
union of what every known buffer still owes — and everything, until a full
|
|
165
|
-
rotation has passed without meeting a new buffer. The tests pin the rule
|
|
166
|
-
(`swapchain.test.js`); a React counter ticking at 400 ms presents frames
|
|
167
|
-
with **one** damage rectangle.
|
|
168
|
-
|
|
169
|
-
**Analytic shapes first, stencil second.** Rounded rectangles — most of a
|
|
170
|
-
UI — are signed distance fields in the fragment shader: antialiased without
|
|
171
|
-
multisampling, and every box in a window batches into one draw. Arbitrary
|
|
172
|
-
paths (`<svg>`, `ctx.fill(path2d)`) are flattened by ntk's own `flattenPath`
|
|
173
|
-
and filled stencil-then-cover on the target's stencil attachment. Strokes
|
|
174
|
-
are the union of segment quads and join polygons under the non-zero rule.
|
|
175
|
-
Box shadows are a feathered SDF (`ctx.fillShadow`, which `boxpaint.js` now
|
|
176
|
-
prefers when a context has it) rather than a CPU blur through the paint
|
|
177
|
-
cache.
|
|
178
|
-
|
|
179
|
-
**An image in perspective is one quad.** `drawImageProjected(image, m)`
|
|
180
|
-
draws an image through a 3×3 projection — a plane turned out of the page,
|
|
181
|
-
as CSS `rotateY()` in a `perspective` turns one — with each corner's texture
|
|
182
|
-
coordinates over its w and the fragment dividing them back, which is exact
|
|
183
|
-
across the quad. A context that can only draw through an affine matrix has
|
|
184
|
-
to cut such a plane into tiles, each drawn through the matrix nearest the
|
|
185
|
-
projection over it and clipped to it, and here every tile is a batch of its
|
|
186
|
-
own because the clip is a scissor: `<Html>` drew Zen Garden 219's sidebar
|
|
187
|
-
as 258 of them a frame, at 11 frames a second on a virgl VM, where seven
|
|
188
|
-
tiles ran at 25. X11 has no such call on purpose: RENDER takes a projective
|
|
189
|
-
picture transform, but glamor — Xwayland's, and most Xorg desktops' —
|
|
190
|
-
divides at the corners and folds the image along its diagonal, and pixman
|
|
191
|
-
runs out of 16.16 precision on a plane of any size. The method answers
|
|
192
|
-
false and draws nothing where a corner is behind the viewer, for the caller
|
|
193
|
-
to draw the part in front some other way.
|
|
194
|
-
|
|
195
|
-
**The ack is deferred to the frame that adopts it.** `xdg_surface.configure`
|
|
196
|
-
is acknowledged immediately before the commit of a frame painted at the
|
|
197
|
-
configured size, not on arrival — acking early tells the compositor the
|
|
198
|
-
next commit is at the new size, and a buffer painted at the old one is then
|
|
199
|
-
shown stretched.
|
|
200
|
-
|
|
201
|
-
**Everything the renderer sees is content-relative device pixels.** The
|
|
202
|
-
surface is larger than the content by the client-side frame, and the
|
|
203
|
-
compositor speaks logical pixels; the two conversions happen in
|
|
204
|
-
`backendwindow.js` and `input.js` and nowhere else.
|
|
205
|
-
|
|
206
|
-
**The keymap is parsed, not dlopen'd.** The compositor sends an XKB keymap
|
|
207
|
-
as a file descriptor; `xkb.js` reads keycodes, types (modifier → level
|
|
208
|
-
maps), symbols per group, and resolves virtual modifiers like `LevelThree`
|
|
209
|
-
to real ones through `interpret` + `modifier_map` — so AltGr works because
|
|
210
|
-
the keymap says which bit it is, not because Mod5 was assumed. The output is
|
|
211
|
-
both the X core `keycode2keysyms` table the accelerator code already speaks
|
|
212
|
-
and a full `decode()` for levels 3 and 4. Key repeat is synthesised at the
|
|
213
|
-
seat's advertised rate; compositors do not repeat.
|
|
214
|
-
|
|
215
|
-
**`useKeyboardState()` is the seat's.** There is no XKB extension to ask, so
|
|
216
|
-
the locks and the group come from `wl_keyboard.modifiers` — Num Lock being
|
|
217
|
-
whichever bit the keymap binds `NumLock` to — and the layout codes from the
|
|
218
|
-
keymap's own `xkb_symbols` name, which libxkbcommon writes from the include
|
|
219
|
-
string the rules built: `pc_us_ru_2_inet(evdev)` is `['us', 'ru']`. A keymap
|
|
220
|
-
not built from the rules has no codes to give, and `layouts` stays empty.
|
|
221
|
-
The protocol's one limit: modifiers go to the focused client only, so a lock
|
|
222
|
-
toggled while another app has the keyboard shows when focus comes back.
|
|
223
|
-
|
|
224
|
-
**The input method is told at the end of the frame.** `zwp_text_input_v3`
|
|
225
|
-
wants to know which field has focus, what kind of text it holds, where its
|
|
226
|
-
caret is and what surrounds it, and every one of those changes repaints the
|
|
227
|
-
field — so the backend window calls `textInput.sync()` once per frame after
|
|
228
|
-
the renderer has painted, when the layout is current, and only what changed
|
|
229
|
-
since the last commit is sent. There is no hook into the event manager and
|
|
230
|
-
nothing per field. What comes back — `preedit_string`, `commit_string`,
|
|
231
|
-
`delete_surrounding_text`, applied together on `done` in the order the spec
|
|
232
|
-
fixes — lands on the same `CompositionStart/Update/End` a dead key raises,
|
|
233
|
-
so `<textinput>` and an application's `onCompositionUpdate` cannot tell IBus
|
|
234
|
-
from `compose.js`. Keys the IME consumes never reach `wl_keyboard.key`, so
|
|
235
|
-
key delivery is untouched; a key that arrives is one the IME declined, and
|
|
236
|
-
the client-side composer has its usual go at it. A `sensitive` field offers
|
|
237
|
-
no surrounding text and says `sensitive_data`; `inputMode` on a
|
|
238
|
-
`<textinput>` becomes the content purpose (`email`, `url`, `tel`, …).
|
|
239
|
-
|
|
240
|
-
**A finger and a pen are the pointer.** On X11 a tablet is an XI2 slave of
|
|
241
|
-
the master pointer and a touch emulates it, so every control in the tree is
|
|
242
|
-
written against `mousedown`/`mousemove`/`mouseup` and nothing else. The
|
|
243
|
-
Wayland seat keeps `wl_touch` and the tablet tools as devices of their own,
|
|
244
|
-
so `touch.js` and `tablet.js` make them the pointer here: the first finger
|
|
245
|
-
of a gesture and a tool in proximity emit the seat's own `enter`/`motion`/
|
|
246
|
-
`buttonpress`/`buttonrelease`/`leave`, carrying the touch `down` or tool
|
|
247
|
-
`down` serial (what `xdg_toplevel.move` wants when the press lands on the
|
|
248
|
-
titlebar), and `input.js` routes them through exactly the path the mouse
|
|
249
|
-
takes — frame hit-testing, popup grabs, the cursor, which a tool in
|
|
250
|
-
proximity follows through cursor-shape's tablet device. The emulated events
|
|
251
|
-
alone carry `pointerType` (`'touch'`, `'pen'`, `'eraser'`, `'mouse'`),
|
|
252
|
-
`pressure`, `tiltX`/`tiltY`, `rotation`, `distance` and
|
|
253
|
-
`tangentialPressure` in the DOM's names and ranges, so a mouse event is
|
|
254
|
-
unchanged and the X11 backend never sees a field it did not send. A second
|
|
255
|
-
finger emulates nothing; every finger also arrives raw on the window as
|
|
256
|
-
`touchstart`/`touchmove`/`touchend`/`touchcancel` — ntk's XI2 names, with
|
|
257
|
-
its `touchId` — each with the active `touches`, batched at `wl_touch.frame`.
|
|
258
|
-
Barrel buttons map as the desktop does: BTN_STYLUS is button 3, BTN_STYLUS2
|
|
259
|
-
button 2; `proximity_out` releases whatever is still held.
|
|
260
|
-
|
|
261
|
-
**`createWindow` is synchronous.** React's commit phase cannot await, and
|
|
262
|
-
that is where windows are realised. Nothing about Wayland needs the
|
|
263
|
-
asynchrony — a request is one-way and the client allocates ids — so the
|
|
264
|
-
library grew a synchronous namespace (`Wl_interface.$`) and the only
|
|
265
|
-
genuinely asynchronous step, binding globals, happens once in `open()`.
|
|
266
|
-
|
|
267
|
-
**The monitors are a fold over the registry.** Each `wl_output` is its own
|
|
268
|
-
global, so `outputs.js` binds every one (through a second `wl_registry`,
|
|
269
|
-
because the library files globals by interface name and keeps one) and
|
|
270
|
-
publishes the layout into `screens.js` the way the cocoa backend does:
|
|
271
|
-
names from `wl_output.name` (`DP-1`, `eDP-1`), logical rects from
|
|
272
|
-
xdg_output where the compositor has it and from the mode over the scale
|
|
273
|
-
where it does not, converted to the renderer's device pixels at
|
|
274
|
-
`app.scale`. Hot-plug is `global`/`global_remove` on that registry. Two
|
|
275
|
-
things the protocol does not have are stood in for: a **primary** (the
|
|
276
|
-
output at the origin), and a **work area** — `xdg_toplevel.configure_bounds`
|
|
277
|
-
is the monitor less its panels, sent to a window ahead of its configure, so
|
|
278
|
-
a window's bounds become the usable rect of the output it has entered, and
|
|
279
|
-
every monitor carries its own rect so no head is clamped by another's. The
|
|
280
|
-
densest output also seeds `app.scale` before the first window exists, which
|
|
281
|
-
is the difference between a first layout at 2x and one corrected a frame
|
|
282
|
-
later; and where a compositor offers neither fractional-scale-v1 nor
|
|
283
|
-
`preferred_buffer_scale`, a surface's buffer scale is that of the output it
|
|
284
|
-
entered.
|
|
285
|
-
|
|
286
|
-
**A `<glarea>` is a rectangle of the backing target.** On X11 it is a child
|
|
287
|
-
window and on macOS a sublayer; a subsurface here would be a second
|
|
288
|
-
swapchain, a second frame clock and a second dma-buf import per frame for
|
|
289
|
-
what is already one GL context drawing into one target. So `glarea.js`
|
|
290
|
-
hands the area the shared GLES table with `viewport`, `scissor` and
|
|
291
|
-
`bindFramebuffer(null)` re-based on its rect (the Cocoa backend's own
|
|
292
|
-
trick, prototype-delegated), and the frame loop orders the three layers:
|
|
293
|
-
the tree's 2D is flushed under, the area draws over, and the panes its
|
|
294
|
-
children were painted on — offscreen targets, `createOverlayPane` — are
|
|
295
|
-
blended over that, translucent. One thing a shared target needs that a
|
|
296
|
-
child window never did: when the tree repaints under a static scene, the
|
|
297
|
-
scene is asked for its frame before the present, so the background never
|
|
298
|
-
shows through for a frame.
|
|
299
|
-
|
|
300
|
-
**One GL device, several contexts, and whoever draws takes it.** A window's
|
|
301
|
-
2d context, the pane a `<glarea>`'s children are painted on and every
|
|
302
|
-
offscreen `Surface` are the same GLES context, differing only in which
|
|
303
|
-
framebuffer they bind and what they left the blend, scissor and stencil
|
|
304
|
-
state saying — unlike an XRender context, which is an encoder aimed at a
|
|
305
|
-
drawable, or a Cocoa one, which carries its state in its bitmap. So
|
|
306
|
-
`device.js` gives the device an owner: a context claims it before it
|
|
307
|
-
touches GL, and claiming is where the target is bound and the state the
|
|
308
|
-
shader was written against is put back. That is what makes ntk's advice —
|
|
309
|
-
hold a surface's context and draw into it whenever you like — true here
|
|
310
|
-
too, where a held context used to draw wherever the device was last pointed
|
|
311
|
-
and say nothing about it (#566); it is also why a node that painted into a
|
|
312
|
-
surface in the middle of the frame does not have to restore the window's
|
|
313
|
-
context afterwards. Foreign GL is the exception it cannot see: a
|
|
314
|
-
`<glarea>` draws through entry points nobody tracks, so the frame loop
|
|
315
|
-
still calls `restoreGLState()` after one.
|
|
316
|
-
|
|
317
|
-
**Paths are antialiased on the CPU, and clips are exact on the stencil.**
|
|
318
|
-
A solid fill that fits the budget (512×512 after cutting to the clip) goes
|
|
319
|
-
through ntk's analytic coverage rasteriser — the same pixels the X11
|
|
320
|
-
backend's local raster and every glyph get — into a per-frame mask atlas,
|
|
321
|
-
drawn like a glyph; gradients and bigger shapes stay on stencil-then-cover.
|
|
322
|
-
`clip()` with anything but one axis-aligned rectangle sets bit 7 of the
|
|
323
|
-
stencil where the shape is (the low seven bits stay the winding count) and
|
|
324
|
-
every batch under it tests that bit; nested clips intersect in place,
|
|
325
|
-
`restore()` re-renders the restored list. The scissor still narrows to the
|
|
326
|
-
bounds, so the test is only paid inside them. `test/wayland/context2d-gpu.test.js`
|
|
327
|
-
reads the pixels back on any machine with a render node.
|
|
328
|
-
|
|
329
|
-
**The glyph seam is public, and spelled the same way on every backend.**
|
|
330
|
-
`drawGlyphs(op, src, positioned)` is how `TextLayout.draw` reaches a
|
|
331
|
-
context, and ntk documents it for callers that place their own glyphs too —
|
|
332
|
-
a terminal grid, a tabular column. The source in that call is spelled
|
|
333
|
-
`ctx.createSolidPicture(r, g, b, a)` and the op `ctx.Render.PictOp.Over`,
|
|
334
|
-
so both of those answer here as they do on X11 and on Cocoa. Implementing
|
|
335
|
-
`drawGlyphs` alone was not half a seam but a silent one: callers
|
|
336
|
-
feature-detected, the test came back false, and the frame drew with every
|
|
337
|
-
cell in its colour and no text in any of them rather than throwing (issue
|
|
338
|
-
#565). What a solid _is_ stays backend-private — an XRender `Picture` on
|
|
339
|
-
X11, the premultiplied colour itself here — but a CSS colour string, a
|
|
340
|
-
premultiplied `[r, g, b, a]` and `null` (meaning the fill style in force)
|
|
341
|
-
are all accepted as sources as well, which is the set the Cocoa backend
|
|
342
|
-
takes.
|
|
343
|
-
|
|
344
|
-
### A surface the compositor stops showing stops drawing
|
|
345
|
-
|
|
346
|
-
After the first present the frame loop has exactly one source of liveness:
|
|
347
|
-
`wl_surface.frame`. A compositor is entitled never to send one to a surface
|
|
348
|
-
it is not showing — covered, on another workspace, minimised, the screen
|
|
349
|
-
locked — and that is the right way to drive a client, because painting into a
|
|
350
|
-
surface nobody sees is work for nothing. There is no timer-driven fallback
|
|
351
|
-
here and there should not be one.
|
|
352
|
-
|
|
353
|
-
What it costs is that the silence is **not an error**. Nothing rejects, the
|
|
354
|
-
frame stays armed, and every later repaint request returns at its guard while
|
|
355
|
-
React goes on re-rendering; from outside the process, an app that has drawn
|
|
356
|
-
one frame and gone quiet is indistinguishable from a hang (#567 — 135 state
|
|
357
|
-
ticks, one present, exit code 0). So the loop is watched (`framewatch.js`),
|
|
358
|
-
and the parked state is reported twice over:
|
|
155
|
+
card node Mesa renders on in software, so the backend then tries each
|
|
156
|
+
`/dev/dri/card*` in turn; headless weston with its GL renderer accepts what
|
|
157
|
+
it makes on a hosted runner.
|
|
158
|
+
|
|
159
|
+
## Windows: what maps, what bends
|
|
160
|
+
|
|
161
|
+
The compositor owns placement, stacking and focus, so some of what a
|
|
162
|
+
`<window>` asks for on X11 means less here:
|
|
163
|
+
|
|
164
|
+
- **Position.** A toplevel cannot place itself: `x`/`y` on a `<window>` are
|
|
165
|
+
ignored, not an error, and the compositor's placement is final. A
|
|
166
|
+
toplevel's own position reads `0, 0`; no client knows where it is on
|
|
167
|
+
screen.
|
|
168
|
+
- **Popups** are placed relative to their parent window, and the compositor
|
|
169
|
+
slides and flips them to stay on screen — the policy `anchorRect` gets
|
|
170
|
+
from react-x11 on X11. A press outside a grabbing popup, or Escape, is
|
|
171
|
+
the compositor's to act on; it hides the popup and the tree hears
|
|
172
|
+
`onDismiss` as it would on X11.
|
|
173
|
+
- **Focus and raising** are the compositor's to give. `focus()` asks
|
|
174
|
+
through xdg-activation and is ignored without a recent user interaction;
|
|
175
|
+
attention requests go the same way; `raise()` and `lower()` do nothing.
|
|
176
|
+
- **States.** Maximised, fullscreen and minimised map; there is no
|
|
177
|
+
un-minimise request. Always-on-top, sticky and workspaces have no
|
|
178
|
+
equivalent.
|
|
179
|
+
- **Size.** A window sizes itself, except while the compositor imposes a
|
|
180
|
+
size — maximised, tiled, or mid-resize — when the size it asks for is
|
|
181
|
+
not applied.
|
|
182
|
+
- **Type and class.** `windowType` matters only where layer-shell exists
|
|
183
|
+
([below](#layer-surfaces-docks-panels-wallpapers)); the window's class
|
|
184
|
+
(`app_id`) is fixed at creation.
|
|
185
|
+
- **A window the compositor is not showing does not draw** — next section.
|
|
186
|
+
|
|
187
|
+
## A surface the compositor stops showing stops drawing
|
|
188
|
+
|
|
189
|
+
After the first present a window draws only when the compositor sends it a
|
|
190
|
+
frame callback, and a compositor is entitled never to send one to a
|
|
191
|
+
surface it is not showing — covered, on another workspace, minimised, the
|
|
192
|
+
screen locked. There is no timer-driven fallback, on purpose: painting
|
|
193
|
+
into a surface nobody sees is work for nothing.
|
|
194
|
+
|
|
195
|
+
The silence is **not an error**: nothing rejects, and React goes on
|
|
196
|
+
re-rendering into a window that does not draw, which from outside looks
|
|
197
|
+
like a hang. So the stall is reported twice over:
|
|
359
198
|
|
|
360
199
|
- **On stderr, once per window, in development**, when the window still had
|
|
361
200
|
something to draw. One line, naming the compositor as the reason and saying
|
|
@@ -365,26 +204,19 @@ and the parked state is reported twice over:
|
|
|
365
204
|
simulating rather than feeding a loop nobody is turning, which is what
|
|
366
205
|
every map, chart and game on this backend wants anyway.
|
|
367
206
|
|
|
368
|
-
Nothing
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
watch off entirely, timer, warning and all.
|
|
207
|
+
Nothing has to be done to recover: the pending frame callback fires the
|
|
208
|
+
moment the surface is shown again, drawing resumes and `presenting` flips
|
|
209
|
+
back. The deadline is two seconds; `REACT_X11_WAYLAND_FRAME_STALL_MS`
|
|
210
|
+
changes it, and `0` turns the watch off entirely, timer, warning and all.
|
|
373
211
|
|
|
374
212
|
## Decorations: whose frame
|
|
375
213
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
is adopted on the same ack as the size it arrived with: 'server' switches
|
|
383
|
-
the client-side frame off (insets to zero, no titlebar painted, the content
|
|
384
|
-
grows into the whole surface, which the tree sees as a resize), 'client'
|
|
385
|
-
leaves it on. Before the first frame, where the compositor imposed no size,
|
|
386
|
-
the surface is refitted to the content the tree asked for, so a floating
|
|
387
|
-
window comes up the size it was declared and not a titlebar taller.
|
|
214
|
+
The backend draws its own titlebar where the compositor will not (mutter),
|
|
215
|
+
and asks for the compositor's frame where `zxdg_decoration_manager_v1` is
|
|
216
|
+
offered — wlroots (sway, labwc), KDE. When the compositor agrees, the
|
|
217
|
+
client-side frame goes away and the content grows into the whole surface,
|
|
218
|
+
which the tree sees as a resize; a window not yet shown comes up the size
|
|
219
|
+
it was declared, not a titlebar taller.
|
|
388
220
|
|
|
389
221
|
`createRoot({ decorations })` says what to ask for: `undefined`/`true`/
|
|
390
222
|
`'server'` prefer the compositor's frame, `'client'` never asks for it, and
|
|
@@ -396,8 +228,8 @@ said `client_side`.
|
|
|
396
228
|
## Layer surfaces: docks, panels, wallpapers
|
|
397
229
|
|
|
398
230
|
Where the compositor advertises `zwlr_layer_shell_v1`, a `<window>` whose
|
|
399
|
-
`windowType` is one of these is not an `xdg_toplevel` but a layer surface
|
|
400
|
-
|
|
231
|
+
`windowType` is one of these is not an `xdg_toplevel` but a layer surface,
|
|
232
|
+
the same `wl_surface`, frame loop and input underneath:
|
|
401
233
|
|
|
402
234
|
| `windowType` | layer | anchored to | exclusive zone |
|
|
403
235
|
| ---------------- | ------------ | ------------------------------------------------- | -------------- |
|
|
@@ -423,56 +255,46 @@ with 56px reserved; anchoring it to both ends of the edge stretches it:
|
|
|
423
255
|
(a number, `'auto'` for the thickness, `-1`), `margin`,
|
|
424
256
|
`keyboardInteractivity` (`'none'`, `'on-demand'`, `'exclusive'`),
|
|
425
257
|
`namespace`, `output` — and `layerShell={false}` keeps a dock-typed window
|
|
426
|
-
an ordinary toplevel. Popups from a layer surface work (a dock's menus)
|
|
427
|
-
`
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
layer-shell — GNOME — the same JSX is a toplevel, as before.
|
|
258
|
+
an ordinary toplevel. Popups from a layer surface work (a dock's menus). A
|
|
259
|
+
stretched axis is the compositor's: `resize()` on it is not granted, and
|
|
260
|
+
the configured size wins. Without layer-shell — GNOME — the same JSX is a
|
|
261
|
+
toplevel.
|
|
431
262
|
|
|
432
263
|
## Screen capture, and the eyedropper
|
|
433
264
|
|
|
434
|
-
`app.screenCapture`
|
|
435
|
-
|
|
265
|
+
`app.screenCapture` is there when the compositor offers either
|
|
266
|
+
`ext_image_copy_capture_manager_v1` (wlroots 0.19+, KDE 6.2+) or
|
|
436
267
|
`zwlr_screencopy_manager_v1` (sway 1.10 has this one), and null otherwise
|
|
437
|
-
(GNOME). `capture()`
|
|
438
|
-
|
|
439
|
-
of it
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
last; `capture({ output })` and `layerShell.output` take another proxy, but
|
|
461
|
-
enumerating them waits on `outputs.js`. And the portal's `parent_window`
|
|
462
|
-
handle comes from **xdg-foreign**: the first dialog a toplevel opens exports
|
|
463
|
-
it (`zxdg_exporter_v2`, kept for the window's lifetime), and the portal gets
|
|
464
|
-
`wayland:<handle>` — modal to the window and stacked over it, as `x11:<xid>`
|
|
465
|
-
is on X11. A compositor without the protocol, or a popup, leaves the handle
|
|
466
|
-
empty and the dialog floats; an `x11:` handle never goes out from this
|
|
467
|
-
backend, because a Wayland window's id names no X window.
|
|
268
|
+
(GNOME). `capture({ output, cursor })` resolves to one frame of the output
|
|
269
|
+
as straight RGBA, `{ width, height, data }`; `pixelAt(x, y)` to one device
|
|
270
|
+
pixel of it, as 0–255 channels. Each call is a fresh capture of the whole
|
|
271
|
+
output — for many pixels, `capture()` once; for anything per frame, not at
|
|
272
|
+
all.
|
|
273
|
+
|
|
274
|
+
`pickColor()` is the eyedropper, the way every wlroots colour picker works:
|
|
275
|
+
it freezes the output into a capture, shows it as a full-screen
|
|
276
|
+
layer-shell overlay that takes the keyboard, and reads the pixel under the
|
|
277
|
+
click — so it needs layer-shell as well as a capture protocol. Escape
|
|
278
|
+
cancels; Return, space and KP_Enter pick at the pointer. It answers
|
|
279
|
+
`{ r, g, b }` in 0–1. `useEyedropper()` uses it below the Screenshot portal
|
|
280
|
+
(the desktop's own, user-consented picker — GNOME's only route, and what
|
|
281
|
+
sway with xdg-desktop-portal-wlr uses too), so `useEyedropper().supported`
|
|
282
|
+
is true on a bare sway. Where the compositor has neither the portal's
|
|
283
|
+
picker nor capture plus layer-shell, `supported` is false: leave the
|
|
284
|
+
eyedropper button undrawn.
|
|
285
|
+
|
|
286
|
+
Two limits. With several outputs, the one captured by default is the one
|
|
287
|
+
the compositor advertised last; `capture({ output })` and
|
|
288
|
+
`layerShell.output` take another `wl_output` proxy. And a portal dialog (file chooser, colour picker) is modal
|
|
289
|
+
to its window only where the compositor has **xdg-foreign**; without it, or
|
|
290
|
+
from a popup, the dialog floats.
|
|
468
291
|
|
|
469
292
|
## Under a second compositor
|
|
470
293
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
refuses:
|
|
294
|
+
The backend runs on sway and labwc as well as mutter, nested
|
|
295
|
+
(`WLR_BACKENDS=wayland`) or headless (`WLR_BACKENDS=headless`). Headless
|
|
296
|
+
sway plus `grim` is a way to screenshot an app with no desktop at all —
|
|
297
|
+
`grim` needs wlr-screencopy, which mutter refuses:
|
|
476
298
|
|
|
477
299
|
```bash
|
|
478
300
|
WLR_BACKENDS=headless sway -c sway.conf & # output * resolution 1280x800
|
|
@@ -480,83 +302,26 @@ WAYLAND_DISPLAY=wayland-1 REACT_X11_BACKEND=wayland node --import tsx examples/s
|
|
|
480
302
|
WAYLAND_DISPLAY=wayland-1 grim shot.png
|
|
481
303
|
```
|
|
482
304
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
it
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
- **`x11-dri` `UnixSocket`** (>= 0.9): a `uv_poll` over an `AF_UNIX` socket,
|
|
502
|
-
`recvmsg`/`sendmsg` with `SCM_RIGHTS`, no thread. Works on Node. Two
|
|
503
|
-
things it had to get right that a first version did not: N-API values made
|
|
504
|
-
from a libuv callback need a handle scope, and callbacks from the loop must
|
|
505
|
-
go through `napi_make_callback` — `napi_call_function` from there runs the
|
|
506
|
-
callback but never drains the promise-job queue, so a client awaiting its
|
|
507
|
-
first reply hangs until an unrelated timer turns the loop. `pipe()`,
|
|
508
|
-
`socketpair()` and `memfdCreate()` came with it.
|
|
509
|
-
- **node-x11's `fdpass-bun.js`**: `bun:ffi` to libc, with a reader thread.
|
|
510
|
-
Bun only. Bun _exports_ libuv's symbol names but several are stubs that
|
|
511
|
-
abort the process (`uv_poll_init` among them), so the native socket is
|
|
512
|
-
never attempted under Bun; there is nothing to probe for safely. Its
|
|
513
|
-
reader thread gave up on a healthy socket when `poll(2)` was interrupted
|
|
514
|
-
sixty-four times in a row, and JavaScriptCore's thread-suspend signal
|
|
515
|
-
(SIGPWR, `tgkill`ed to every JS thread, the poller included) during
|
|
516
|
-
**WebAssembly compilation** — yoga-layout's wasm tiering up ~0.3 s into
|
|
517
|
-
any react-x11 app — produces exactly that burst, as
|
|
518
|
-
`strace -f -e ppoll,tgkill` shows. EINTR is a retry since x11 4.2.1
|
|
519
|
-
([sidorares/node-x11#297](https://github.com/sidorares/node-x11/pull/297)).
|
|
520
|
-
|
|
521
|
-
## What is left
|
|
522
|
-
|
|
523
|
-
- **Multisampling.** Stencil-then-cover (gradients, shapes past the CPU
|
|
524
|
-
budget) and stencil clips are aliased at the edge. MSAA is the fix, and it
|
|
525
|
-
is not the few lines it looks like: GLES 3.0 cannot `blitFramebuffer` _into_
|
|
526
|
-
a multisampled framebuffer, so the scroll blits and the partial repaints the
|
|
527
|
-
backing target exists for would have to be re-expressed as textured draws,
|
|
528
|
-
and every read of the target (a present, `getImageData`, a `drawImage` of a
|
|
529
|
-
surface) as a resolve first. virgl here reports `MAX_SAMPLES` of 1, so none
|
|
530
|
-
of it could be validated; it waits for a machine that can run it.
|
|
531
|
-
- **`<Frame>` panes.** The out-of-process pane path (`createPaneHost`) is
|
|
532
|
-
macOS's and Windows' today — the pane presents an IOSurface id there and a
|
|
533
|
-
composition surface handle on Windows. Linux wants the same over a memfd or
|
|
534
|
-
dma-buf fd on the pane channel; until then `<Frame>` here degrades the way
|
|
535
|
-
it does without embedding.
|
|
536
|
-
- **Drag-and-drop.** Wired (`src/wayland/dnd.js`): the drop side answers
|
|
537
|
-
another application's drag over `wl_data_device` enter/motion/leave/drop,
|
|
538
|
-
and the source side offers a payload and hands the gesture to `start_drag`,
|
|
539
|
-
with an in-app drop keeping `e.items` by reference. What is left is the drag
|
|
540
|
-
_icon_: `start_drag` is given a bare (invisible) surface, and rendering the
|
|
541
|
-
tree's `<popup dragPreview>` into an icon surface the frame loop paints is a
|
|
542
|
-
follow-up.
|
|
543
|
-
- **Input methods, the rest of it.** The preedit's cursor is drawn (a heavier
|
|
544
|
-
underline under the segment being converted) but nothing is done with a
|
|
545
|
-
hidden cursor beyond putting the caret at the end, and the only content
|
|
546
|
-
hints sent are `multiline` and `sensitive_data` — no
|
|
547
|
-
`spellcheck`/`auto_capitalization`, which the tree has no notion of.
|
|
548
|
-
- **Tablet pads.** The buttons, rings and strips on the tablet itself
|
|
549
|
-
(`zwp_tablet_pad_v2`) are accepted and ignored: the tree has no vocabulary
|
|
550
|
-
for them.
|
|
551
|
-
- **`useScreens().source` reads `'test'`**, as it does on macOS and Windows:
|
|
552
|
-
all three backends publish through `setScreensForTests`, the one seam
|
|
553
|
-
`screens.js` has, and it stamps the source. A `source` argument on it is the
|
|
554
|
-
fix.
|
|
555
|
-
- **`fillText` only sees the first font family** of a `font` shorthand and
|
|
305
|
+
## Not supported yet
|
|
306
|
+
|
|
307
|
+
- **Multisampling.** Paths filled with a gradient, solid paths larger than
|
|
308
|
+
512×512 after clipping, and clips to anything but one axis-aligned
|
|
309
|
+
rectangle have aliased edges; rounded rectangles, smaller solid paths and
|
|
310
|
+
text are antialiased.
|
|
311
|
+
- **`<Frame>` panes.** A `<Frame>` fails at once: its `fallback` renders and
|
|
312
|
+
no pane is started ([frame.md](frame.md)).
|
|
313
|
+
- **The drag icon.** Drag and drop works both ways, but the drag shows no
|
|
314
|
+
icon: `<popup dragPreview>` is not drawn.
|
|
315
|
+
- **Input methods, the rest of it.** A preedit that hides its cursor puts
|
|
316
|
+
the caret at the end, and the only content hints sent are `multiline`
|
|
317
|
+
and `sensitive_data`.
|
|
318
|
+
- **Tablet pads** — the buttons, rings and strips on the tablet itself are
|
|
319
|
+
ignored.
|
|
320
|
+
- **`useScreens().source` reads `'test'`**, as it does on macOS and Windows.
|
|
321
|
+
- **`fillText`** uses only the first family of a `font` shorthand, and
|
|
556
322
|
strokes text as a fill.
|
|
557
|
-
- **`ext-image-copy-capture`**
|
|
558
|
-
|
|
559
|
-
GNOME neither. wlroots 0.19 (sway 1.11) is where to try it.
|
|
323
|
+
- **`ext-image-copy-capture`** has not been run against a compositor yet
|
|
324
|
+
(sway 1.10 offers only wlr-screencopy).
|
|
560
325
|
|
|
561
326
|
## Things that are gone, not missing
|
|
562
327
|
|
|
@@ -565,27 +330,3 @@ screen coordinates, `GetImage`, XEmbed/`<foreign>`, and `ssh -X` are
|
|
|
565
330
|
compositor-private by design. Panels and docks are `layer-shell`, and a
|
|
566
331
|
screen read is `screencopy` — both wlroots and KDE, neither GNOME, where the
|
|
567
332
|
same JSX is a toplevel and the same pick is the portal's.
|
|
568
|
-
|
|
569
|
-
## Changes outside `src/wayland/`
|
|
570
|
-
|
|
571
|
-
- `src/Reconciler.js` — a `'wayland'` rung in `resolveBackend()`. Additive;
|
|
572
|
-
`'auto'` never selects it.
|
|
573
|
-
- `src/nodes/boxpaint.js` — prefers `ctx.fillShadow` when a context has it.
|
|
574
|
-
- `src/nodes/preedit.js` — the composition may carry a cursor
|
|
575
|
-
(`cursorBegin`/`cursorEnd` on the composition event); without one the
|
|
576
|
-
caret is at the end of the preedit, as before. `src/events.js` —
|
|
577
|
-
`_composition` passes those extra fields through. Additive on both
|
|
578
|
-
backends.
|
|
579
|
-
- `src/screencolor.js` — rung 3 of the eyedropper's ladder, the Wayland
|
|
580
|
-
backend's own picker, guarded by `app.backend === 'wayland'`; and the
|
|
581
|
-
portal's `parent_window` handle is an xdg-foreign export on that backend
|
|
582
|
-
(`portalParentFor` in `src/portal.js`, shared with the file dialog),
|
|
583
|
-
where an X window id would be a lie.
|
|
584
|
-
- [`@windowkit/wayland`](https://github.com/windowkit/wayland) 3.1.1 — a
|
|
585
|
-
fork of [`wayland-client`](https://github.com/sdumetz/node-wayland-client)
|
|
586
|
-
3.0.0: fd send/receive, the `$` synchronous request namespace, callback
|
|
587
|
-
requests returning their `done` payload, and `null` for `allow-null`
|
|
588
|
-
arguments.
|
|
589
|
-
- `x11-dri` 0.9.0 — `UnixSocket`, `pipe`, `socketpair`, `memfdCreate`; a
|
|
590
|
-
self-test that passes a real descriptor through a socketpair.
|
|
591
|
-
- `x11` 4.2.1 — `fdpass-bun.js` retries a `poll(2)` that EINTR interrupted.
|