react-x11 2.13.0 → 2.15.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.
Files changed (70) hide show
  1. package/package.json +11 -8
  2. package/src/Reconciler.js +34 -21
  3. package/src/components/Select.js +8 -2
  4. package/src/events.js +8 -2
  5. package/src/glnodes.js +15 -3
  6. package/src/index.d.ts +5 -0
  7. package/src/nodes/boxpaint.js +9 -0
  8. package/src/nodes/preedit.js +64 -14
  9. package/src/ntk.js +10 -1
  10. package/src/scale.js +52 -22
  11. package/src/screencolor.js +104 -17
  12. package/src/types/system.d.ts +14 -1
  13. package/src/wayland/app.js +574 -0
  14. package/src/wayland/backendwindow.js +1199 -0
  15. package/src/wayland/clipboard.js +326 -0
  16. package/src/wayland/connection.js +485 -0
  17. package/src/wayland/context2d.js +2290 -0
  18. package/src/wayland/decorations.js +476 -0
  19. package/src/wayland/device.js +55 -0
  20. package/src/wayland/dmabuf.js +89 -0
  21. package/src/wayland/dnd.js +581 -0
  22. package/src/wayland/fdutil.js +108 -0
  23. package/src/wayland/framestyle.js +257 -0
  24. package/src/wayland/framewatch.js +190 -0
  25. package/src/wayland/glarea.js +372 -0
  26. package/src/wayland/glcontext.js +415 -0
  27. package/src/wayland/glyphatlas.js +237 -0
  28. package/src/wayland/input.js +417 -0
  29. package/src/wayland/keysymnames.js +35 -0
  30. package/src/wayland/layershell.js +363 -0
  31. package/src/wayland/outputs.js +601 -0
  32. package/src/wayland/protocols/cursor-shape-v1.json +1 -0
  33. package/src/wayland/protocols/ext-idle-notify-v1.json +1 -0
  34. package/src/wayland/protocols/ext-image-capture-source-v1.json +1 -0
  35. package/src/wayland/protocols/ext-image-copy-capture-v1.json +1 -0
  36. package/src/wayland/protocols/fractional-scale-v1.json +1 -0
  37. package/src/wayland/protocols/index.json +131 -0
  38. package/src/wayland/protocols/kde-server-decoration.json +1 -0
  39. package/src/wayland/protocols/keyboard-shortcuts-inhibit-unstable-v1.json +1 -0
  40. package/src/wayland/protocols/linux-dmabuf-v1.json +1 -0
  41. package/src/wayland/protocols/pointer-constraints-unstable-v1.json +1 -0
  42. package/src/wayland/protocols/presentation-time.json +1 -0
  43. package/src/wayland/protocols/primary-selection-unstable-v1.json +1 -0
  44. package/src/wayland/protocols/relative-pointer-unstable-v1.json +1 -0
  45. package/src/wayland/protocols/tablet-v2.json +1 -0
  46. package/src/wayland/protocols/text-input-unstable-v3.json +1 -0
  47. package/src/wayland/protocols/viewporter.json +1 -0
  48. package/src/wayland/protocols/wayland.json +1 -0
  49. package/src/wayland/protocols/wlr-layer-shell-unstable-v1.json +1 -0
  50. package/src/wayland/protocols/wlr-screencopy-unstable-v1.json +1 -0
  51. package/src/wayland/protocols/xdg-activation-v1.json +1 -0
  52. package/src/wayland/protocols/xdg-decoration-unstable-v1.json +1 -0
  53. package/src/wayland/protocols/xdg-output-unstable-v1.json +1 -0
  54. package/src/wayland/protocols/xdg-shell.json +1 -0
  55. package/src/wayland/protocols/xdg-toplevel-icon-v1.json +1 -0
  56. package/src/wayland/readback.js +99 -0
  57. package/src/wayland/screencopy.js +584 -0
  58. package/src/wayland/seat.js +584 -0
  59. package/src/wayland/shm.js +226 -0
  60. package/src/wayland/ssd.js +234 -0
  61. package/src/wayland/surface.js +141 -0
  62. package/src/wayland/swapchain.js +411 -0
  63. package/src/wayland/tablet.js +522 -0
  64. package/src/wayland/target.js +269 -0
  65. package/src/wayland/text.js +113 -0
  66. package/src/wayland/textinput.js +671 -0
  67. package/src/wayland/touch.js +284 -0
  68. package/src/wayland/window.js +854 -0
  69. package/src/wayland/xkb.js +425 -0
  70. package/src/windowstate.js +41 -1
@@ -1,7 +1,7 @@
1
1
  // Sample one pixel from the screen — the eyedropper, through whatever this
2
2
  // machine actually has.
3
3
  //
4
- // The file dialog's ladder again (docs/filedialog.md), three rungs:
4
+ // The file dialog's ladder again (docs/filedialog.md), four rungs:
5
5
  //
6
6
  // 1. **the system sampler** — `NSColorSampler` on the cocoa backend
7
7
  // (src/cocoa/screencolor.js). macOS draws the loupe out of process, so
@@ -11,24 +11,31 @@
11
11
  // 2. **the portal** — `org.freedesktop.portal.Screenshot.PickColor`. The
12
12
  // desktop draws its own magnifier and hands back the colour, which is
13
13
  // also the only route that works under a compositor that would refuse a
14
- // root read, and the only route Wayland has at all. Needs version 2 of
14
+ // root read, and GNOME's only route on Wayland. Needs version 2 of
15
15
  // the Screenshot interface — XFCE ships none, GNOME and KDE ship 2 —
16
16
  // so the gate is the interface's `version` property, not `hasService()`.
17
- // 3. **X11** — grab the pointer with a crosshair, wait for the click,
17
+ // 3. **the Wayland backend's own picker** — where the compositor offers a
18
+ // screen capture protocol and layer-shell (wlroots: sway, labwc, …)
19
+ // but no portal, the backend freezes the output into a capture, shows
20
+ // it as an overlay and takes the click on that
21
+ // (src/wayland/screencopy.js). Below the portal because the portal is
22
+ // the desktop's picker and user-consented; guarded by the backend's
23
+ // name because the capability is a Wayland object through and through.
24
+ // 4. **X11** — grab the pointer with a crosshair, wait for the click,
18
25
  // `GetImage` a 1×1 at it, decode by the server's own pixel layout.
19
26
  // Reached under a bare WM, over ssh, on XQuartz: everywhere there is a
20
27
  // display and nothing else, which is the case react-x11 exists for.
21
28
  //
22
- // Rungs 1 and 2 are the same shape — ask the system, it draws the picker,
23
- // it hands back an sRGB triple — which is why the sampler goes on top of the
24
- // portal rather than under the crosshair: where the OS will do this for us,
25
- // it does it better, and `hexFromPortalColor()` converts for both.
29
+ // Rungs 1 to 3 are the same shape — ask, a picker is drawn, an sRGB triple
30
+ // comes back — which is why the sampler goes on top of the portal rather
31
+ // than under the crosshair: where the OS will do this for us, it does it
32
+ // better, and `hexFromPortalColor()` converts for all three.
26
33
  //
27
34
  // There is no rung to *draw*, because the thing being read — the whole
28
35
  // screen — is precisely what an application cannot draw itself. So unlike
29
36
  // `useFileDialog()`, `useEyedropper()` adds no rung; it adds the binding a
30
37
  // component wants (`picking`, `supported`, the owner window) over these
31
- // three.
38
+ // four.
32
39
  //
33
40
  // Which means the ladder really can run out, and where it does the floor has
34
41
  // to be the typed rejection rather than a crash. A cocoa app on a bridge
@@ -83,8 +90,9 @@ export class NoScreenColorError extends Error {
83
90
  message ??
84
91
  'no way to sample a colour from the screen — no cocoa app with a ' +
85
92
  'system sampler, no Screenshot portal with PickColor (interface ' +
86
- 'version 2) on the session bus, and no X connection was given for ' +
87
- 'the fallback. Pass `app` (from createRoot() or useApp()), or use ' +
93
+ 'version 2) on the session bus, no Wayland compositor with screen ' +
94
+ 'capture and layer-shell, and no X connection was given for the ' +
95
+ 'fallback. Pass `app` (from createRoot() or useApp()), or use ' +
88
96
  'useEyedropper().'
89
97
  }`,
90
98
  { cause },
@@ -212,7 +220,14 @@ async function portalPick(opts, ref) {
212
220
  // options)` — no title — which is the whole reason portalRequest takes a
213
221
  // signature now.
214
222
  signature: 'sa{sv}',
215
- args: [parentWindowHandle(windowIdOf(opts.parentWindow))],
223
+ // The handle names an X window. A Wayland toplevel has none to name —
224
+ // an xdg-foreign export would be the equivalent — so there the dialog
225
+ // floats, which the portal allows.
226
+ args: [
227
+ appFor(opts)?.backend === 'wayland'
228
+ ? ''
229
+ : parentWindowHandle(windowIdOf(opts.parentWindow)),
230
+ ],
216
231
  options: {},
217
232
  signal: opts.signal,
218
233
  });
@@ -237,7 +252,49 @@ async function portalCanPick(ref) {
237
252
  }
238
253
 
239
254
  // --------------------------------------------------------------------------
240
- // Rung 3: X11
255
+ // Rung 3: the Wayland backend's own picker
256
+ // --------------------------------------------------------------------------
257
+
258
+ /**
259
+ * The Wayland app whose compositor can freeze and show the screen, or null.
260
+ *
261
+ * Guarded by the backend's name, unlike the sampler: `screenCapture` is the
262
+ * capability, but it is a Wayland object through and through — the pick is
263
+ * a layer-shell overlay over a screencopy frame (src/wayland/screencopy.js)
264
+ * — and `canPick` is the compositor's answer, not the app's. GNOME's is no:
265
+ * it advertises neither protocol, and the portal above is its route.
266
+ */
267
+ function waylandApp(opts) {
268
+ const app = appFor(opts);
269
+ return app?.backend === 'wayland' && app.screenCapture?.canPick ? app : null;
270
+ }
271
+
272
+ /**
273
+ * Freeze, show, click — the sampler's contract: `'#rrggbb'`, null on Escape,
274
+ * a rejection on abort. Unlike the sampler the overlay is ours, and it is
275
+ * down before the promise settles either way, so an abort leaves nothing on
276
+ * screen.
277
+ */
278
+ function waylandPick(opts, app) {
279
+ const signal = opts.signal;
280
+ if (signal?.aborted) {
281
+ return Promise.reject(signal.reason ?? new PortalCancelledError());
282
+ }
283
+ return app.screenCapture.pickColor({ signal }).then((color) => {
284
+ if (color == null) return null;
285
+ const hex = hexFromPortalColor([color.r, color.g, color.b]);
286
+ if (!hex) {
287
+ throw new Error(
288
+ 'react-x11: the Wayland screen pick answered without a colour — ' +
289
+ `expected sRGB { r, g, b } in 0–1, got ${JSON.stringify(color)}.`,
290
+ );
291
+ }
292
+ return hex;
293
+ });
294
+ }
295
+
296
+ // --------------------------------------------------------------------------
297
+ // Rung 4: X11
241
298
  // --------------------------------------------------------------------------
242
299
 
243
300
  // x11.eventMask bits, spelled out the way xsettings.js spells its one. No
@@ -680,6 +737,16 @@ function canGrabOn(app) {
680
737
  * fixes, and only one of them is the caller's.
681
738
  */
682
739
  function noX11Reason(app, backend) {
740
+ if (app?.backend === 'wayland') {
741
+ return (
742
+ 'this tree renders through the Wayland backend, and its compositor ' +
743
+ 'offers neither a Screenshot portal with PickColor nor a screen ' +
744
+ 'capture protocol (wlr-screencopy, ext-image-copy-capture) together ' +
745
+ 'with wlr-layer-shell to draw the picker on. ' +
746
+ '`useEyedropper().supported` is false, which is the signal to leave ' +
747
+ 'the eyedropper button undrawn (docs/wayland-backend.md).'
748
+ );
749
+ }
683
750
  if (app) {
684
751
  return (
685
752
  'this tree does not render through an X connection — a cocoa-backend ' +
@@ -710,15 +777,17 @@ function noX11Reason(app, backend) {
710
777
  * Screenshot interface at version 2 — the probe reads the interface's
711
778
  * `version` property, because `hasService()` cannot see which interfaces a
712
779
  * portal's backends actually provide (XFCE's provides no Screenshot at
713
- * all). `'x11'` needs a connection to answer with, so pass `app` (or a
714
- * `parentWindow` that resolves to one) and one that can actually grab,
715
- * which a cocoa-backend app cannot. With none of the three the honest
716
- * answer is `null`.
780
+ * all). `'wayland'` is a Wayland-backend app (`app`, or a `parentWindow`
781
+ * in one) whose compositor advertises a screen capture protocol and
782
+ * layer-shell wlroots without a portal. `'x11'` needs a connection to
783
+ * answer with, so pass `app` (or a `parentWindow` that resolves to one) —
784
+ * and one that can actually grab, which a cocoa-backend app cannot. With
785
+ * none of the four the honest answer is `null`.
717
786
  *
718
787
  * Acquires a bus reference and releases it, so it is cheap but not free —
719
788
  * `useEyedropper().supported` caches it for you.
720
789
  *
721
- * @returns {Promise<'cocoa'|'portal'|'x11'|null>}
790
+ * @returns {Promise<'cocoa'|'portal'|'wayland'|'x11'|null>}
722
791
  */
723
792
  export async function screenColorBackend(options = {}) {
724
793
  const backend = options.backend;
@@ -737,6 +806,10 @@ export async function screenColorBackend(options = {}) {
737
806
  }
738
807
  if (backend === 'portal') return null;
739
808
  }
809
+ if (!backend || backend === 'wayland') {
810
+ if (waylandApp(options)) return 'wayland';
811
+ if (backend === 'wayland') return null;
812
+ }
740
813
  return canGrabOn(appFor(options)) ? 'x11' : null;
741
814
  }
742
815
 
@@ -775,6 +848,20 @@ async function runPick(opts) {
775
848
  }
776
849
  }
777
850
 
851
+ const wantWayland = !opts.backend || opts.backend === 'wayland';
852
+ if (wantWayland) {
853
+ const app = waylandApp(opts);
854
+ if (app) return await waylandPick(opts, app);
855
+ if (opts.backend === 'wayland') {
856
+ throw new NoScreenColorError(
857
+ "backend: 'wayland' — no screen pick here: this tree does not " +
858
+ 'render through the Wayland backend, or its compositor offers ' +
859
+ 'neither a screen capture protocol (wlr-screencopy, ' +
860
+ 'ext-image-copy-capture) nor wlr-layer-shell.',
861
+ );
862
+ }
863
+ }
864
+
778
865
  const app = appFor(opts);
779
866
  if (canGrabOn(app)) return x11Pick(opts, app);
780
867
  throw new NoScreenColorError(noX11Reason(app, opts.backend));
@@ -100,7 +100,8 @@ export function useScale(): number;
100
100
  export interface WindowState {
101
101
  /** This window has the keyboard. */
102
102
  readonly focused: boolean;
103
- /** **The one to branch on**: not minimized, and not fully covered. */
103
+ /** **The one to branch on**: not minimized, not fully covered, and still
104
+ * being given frames to draw. */
104
105
  readonly visible: boolean;
105
106
  /**
106
107
  * Fully covered by other windows — **always false when a compositing
@@ -109,6 +110,18 @@ export interface WindowState {
109
110
  * entirely visible. Prefer `visible`.
110
111
  */
111
112
  readonly obscured: boolean;
113
+ /**
114
+ * The display is still scheduling frames for this window.
115
+ *
116
+ * False on the **Wayland** backend once the compositor has stopped sending
117
+ * frame callbacks — a surface it is not showing gets none, so nothing the
118
+ * app draws reaches the screen until it starts again. It is how that
119
+ * backend answers the question `obscured` answers on a bare X server, it
120
+ * folds into `visible` the same way, and it takes a couple of seconds of
121
+ * silence to be sure. Always true where the backend has no such signal:
122
+ * X11, Cocoa, and the headless mock.
123
+ */
124
+ readonly presenting: boolean;
112
125
  /** `_NET_WM_STATE_HIDDEN`: iconified, or shaded away. */
113
126
  readonly minimized: boolean;
114
127
  /** Both axes. One axis alone shows up in `states`. */