react-x11 2.55.0 → 2.55.1

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/docs/elements.md CHANGED
@@ -1948,9 +1948,10 @@ frame.
1948
1948
  </glarea>
1949
1949
  ```
1950
1950
 
1951
- - **Cocoa**: one transparent layer over the whole surface, composited by
1951
+ - **Cocoa**: one transparent layer per child, as big as the child,
1952
+ stacked above the surface in the children's order and composited by
1952
1953
  Core Animation — a translucent fill, antialiased edge or shadow blends
1953
- with the GL frame.
1954
+ with the GL frame and with the children under it.
1954
1955
  - **X11**: one child window per child, stacked above the surface in the
1955
1956
  children's order and cut (SHAPE) to where the child painted half its
1956
1957
  alpha or more; the GL frame shows everywhere else. A translucent pixel
@@ -1959,10 +1960,11 @@ frame.
1959
1960
  (`react-x11/test`'s server) a pane is the child's whole rectangle,
1960
1961
  `clearColor` where unpainted.
1961
1962
 
1962
- A child that only moves (its `left` or `top`) has its pixels copied, not
1963
- repainted — only uncovered strips are drawn, and panes it passes over are
1964
- untouched — so content panned over a scene is cheap; scroll panes among the
1965
- children scroll the same way.
1963
+ A child that only moves (its `left` or `top`) is not repainted — on Cocoa
1964
+ its layer moves, and on X11 its pixels are copied and only uncovered strips
1965
+ are drawn; panes it passes over are untouched either way — so content
1966
+ panned over a scene is cheap; scroll panes among the children scroll the
1967
+ same way.
1966
1968
  [`REACT_X11_NO_SCROLL_BLIT=1`](debugging.md#react_x11_no_scroll_blit1)
1967
1969
  turns the copies off.
1968
1970
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-x11",
3
- "version": "2.55.0",
3
+ "version": "2.55.1",
4
4
  "description": "react renderer with X11 as a target",
5
5
  "main": "./src/index.js",
6
6
  "bin": {
package/src/cocoa/app.js CHANGED
@@ -508,6 +508,17 @@ export class CocoaApp {
508
508
  return new CocoaOverlayPane(this, attributes);
509
509
  }
510
510
 
511
+ /**
512
+ * That a `<glarea>`'s children each take a pane of their own here, as on
513
+ * X11, rather than one pane over the whole surface: Core Animation stacks
514
+ * the layers (`CocoaOverlayPane.stackAt`), and each is only as big as its
515
+ * child, so a minimap's viewport moving pushes the minimap's pixels and
516
+ * not the surface's (src/gloverlay.js).
517
+ */
518
+ get overlaysPerChild() {
519
+ return true;
520
+ }
521
+
511
522
  /**
512
523
  * The Frame host seam (src/frame/index.js): a pane's composited region
513
524
  * in this window. Its presence is what routes <Frame> to the shared-
@@ -19,6 +19,10 @@ import { BackendContext2D } from '../backend/context2d.js';
19
19
  import { withoutActions } from './quiet.js';
20
20
 
21
21
  export const OVERLAY_Z = 1e7 + 1;
22
+ /** How far above the last each pane of a surface is stacked (`stackAt`): a
23
+ * thousand of them stay under the next whole number, so above every GL
24
+ * layer and every surface's panes above every GL frame, as before. */
25
+ const OVERLAY_STEP = 1 / 1024;
22
26
 
23
27
  export class CocoaOverlayPane {
24
28
  constructor(app, options) {
@@ -43,6 +47,8 @@ export class CocoaOverlayPane {
43
47
  // show, only whatever the render server makes of that.
44
48
  this._presented = false;
45
49
  this._hidden = false;
50
+ // where it is among its surface's panes, a child each (`stackAt`)
51
+ this._z = OVERLAY_Z;
46
52
  this.setState({
47
53
  x: options.x ?? 0,
48
54
  y: options.y ?? 0,
@@ -66,11 +72,20 @@ export class CocoaOverlayPane {
66
72
  const s = this.scale;
67
73
  this._setLayerProps({
68
74
  frame: [rect.x / s, rect.y / s, rect.width / s, rect.height / s],
69
- zPosition: OVERLAY_Z,
75
+ zPosition: this._z,
70
76
  hidden: this._hidden || !this._presented,
71
77
  });
72
78
  }
73
79
 
80
+ /** Stacked `index` panes up from the bottom of its surface's — one a
81
+ * child, in the children's order (src/gloverlay.js, `restack`). */
82
+ stackAt(index) {
83
+ const z = OVERLAY_Z + Math.min(index, 1023) * OVERLAY_STEP;
84
+ if (z === this._z) return;
85
+ this._z = z;
86
+ this._setLayerProps({ zPosition: z });
87
+ }
88
+
74
89
  map() {
75
90
  this._hidden = false;
76
91
  if (this._presented) this._setLayerProps({ hidden: false });
package/src/gloverlay.js CHANGED
@@ -17,10 +17,18 @@
17
17
  // translucent child over a GL surface?
18
18
  //
19
19
  // - **The Cocoa backend: yes.** Core Animation composites every layer, so
20
- // one pane holds everything — a transparent bitmap layer over the whole
21
- // surface, above the GL layer — and a translucent background, an
22
- // antialiased edge or a shadow blends with the GL frame under it.
23
- // `app.createOverlayPane` is how a backend says it composites.
20
+ // a pane is a transparent bitmap layer above the GL layer, and a
21
+ // translucent background, an antialiased edge or a shadow blends with the
22
+ // GL frame under it. `app.createOverlayPane` is how a backend says it
23
+ // composites. Where it can stack its panes as well
24
+ // (`app.overlaysPerChild`), a pane is one per child, as on X11, holding
25
+ // that child alone — the layers under it show through — and as big as
26
+ // what the child reaches: a layer's contents are a copy of its bitmap,
27
+ // colour-matched to the display as it is pushed, and one the size of the
28
+ // surface pushed again at every step a minimap's viewport moved was a
29
+ // quarter of a core on a Mac. A child that only moved moves its layer,
30
+ // and nothing is painted. Otherwise one pane holds every child, over the
31
+ // whole surface.
24
32
  // - **X11: no.** A child window is opaque without a compositor, and a
25
33
  // compositor would not change that: it redirects top-level windows, not
26
34
  // the children inside one. So a pane is a plain child window just big
@@ -746,8 +754,9 @@ function askShape(app) {
746
754
  */
747
755
  class Pane {
748
756
  /**
749
- * @param node the child an X11 pane is kept by and holds, up to — null for
750
- * a composited pane, which holds every child
757
+ * @param node the child a pane is kept by and holds — up to it on X11,
758
+ * alone where it is composited — and null for a composited pane over
759
+ * the whole surface, which holds every child
751
760
  * @param overlay the panes it is one of, which `canBlit` asks about
752
761
  */
753
762
  constructor(wnd, rect, transparent, node = null, overlay = null) {
@@ -842,7 +851,8 @@ class Pane {
842
851
  };
843
852
  if (!this.wnd.scrollRegion(local, dx, dy)) return false;
844
853
  this.blitted = true;
845
- const panes = this.node ? this.overlay?.panes : null;
854
+ // composited, a pane over it holds none of it
855
+ const panes = this.node && !this.transparent ? this.overlay?.panes : null;
846
856
  if (panes) {
847
857
  for (let i = panes.indexOf(this) + 1; i < panes.length; i++) {
848
858
  panes[i].owe(rect);
@@ -867,8 +877,11 @@ export class GlOverlay {
867
877
  constructor(area) {
868
878
  this.area = area;
869
879
  this.app = area.app;
870
- // one pane over the whole surface, where the backend composites it
880
+ // panes the backend composites, transparent, rather than child windows
871
881
  this.composited = typeof this.app?.createOverlayPane === 'function';
882
+ // a pane a child: X11's, and a compositor's that stacks its panes; one
883
+ // pane over the whole surface elsewhere
884
+ this.perChild = !this.composited || this.app?.overlaysPerChild === true;
872
885
  // bottom to top: the children's order on X11, one pane each
873
886
  this.panes = [];
874
887
  // X11: each child's pane, kept by the child for as long as it has one
@@ -908,7 +921,7 @@ export class GlOverlay {
908
921
  const visible = shown(area) && abs.width > 0 && abs.height > 0;
909
922
  // the surface's own rect, rounded the way its window's is
910
923
  const surface = visible ? area._geometry() : null;
911
- if (!this.composited) return this._syncChildren(surface);
924
+ if (this.perChild) return this._syncChildren(surface);
912
925
  const rects = surface && area.paintOrder().length ? [surface] : [];
913
926
  let changed = false;
914
927
  while (this.panes.length > rects.length) {
@@ -929,13 +942,14 @@ export class GlOverlay {
929
942
  }
930
943
 
931
944
  /**
932
- * `sync` on X11: a pane per child, kept by the child. A child that is new
933
- * gets one, a child that went takes its with it, and one whose reach moved
934
- * has its pane placed there — and painted whole, unless the move was the
935
- * child's own and nothing else, which the window's move carries
936
- * (`_settleMove` repaints what it does not). A change of order restacks
937
- * them, and repaints whichever overlap another: what a pane holds is what
938
- * is under its child.
945
+ * `sync` on X11, and where panes composite a child each: a pane per
946
+ * child, kept by the child. A child that is new gets one, a child that
947
+ * went takes its with it, and one whose reach moved has its pane placed
948
+ * there — and painted whole, unless the move was the child's own and
949
+ * nothing else, which the window's move carries (`_settleMove` repaints
950
+ * what it does not). A change of order restacks them, and on X11 repaints
951
+ * whichever overlap another: what an X11 pane holds is what is under its
952
+ * child.
939
953
  */
940
954
  _syncChildren(surface) {
941
955
  const regions = surface ? childRegions(this.area, surface) : [];
@@ -981,9 +995,10 @@ export class GlOverlay {
981
995
  this.panes = next;
982
996
  if (reordered) {
983
997
  this.restack();
984
- if (!made) {
998
+ if (!made && !this.composited) {
985
999
  // what each pane holds is its child and what is under it: a new
986
- // order is new contents wherever two of them meet
1000
+ // order is new contents wherever two of them meet — on X11, where a
1001
+ // pane is opaque; a composited one holds its child alone
987
1002
  for (const pane of next) {
988
1003
  if (
989
1004
  next.some(
@@ -1041,7 +1056,11 @@ export class GlOverlay {
1041
1056
  * Cocoa backend the layer's zPosition is the whole of it.
1042
1057
  */
1043
1058
  restack() {
1044
- if (this.composited) return;
1059
+ if (this.composited) {
1060
+ // a composited pane a child is stacked by the backend, in order
1061
+ this.panes.forEach((pane, i) => pane.wnd.stackAt?.(i));
1062
+ return;
1063
+ }
1045
1064
  const X = this.app?.X;
1046
1065
  if (typeof X?.ConfigureWindow !== 'function') return;
1047
1066
  let below = this.area.window?.id ?? null;
@@ -1085,7 +1104,7 @@ export class GlOverlay {
1085
1104
  if (!root || root.destroyed || root._paneDamage === FULL_DAMAGE) {
1086
1105
  return true;
1087
1106
  }
1088
- if (!this.composited) {
1107
+ if (this.perChild) {
1089
1108
  // Each child's move is its own pane's to settle: carried by the
1090
1109
  // window's move where its pane went with it, moved on the pane where
1091
1110
  // the pane stayed — cut to the surface, say, which a box bigger than
@@ -1135,6 +1154,9 @@ export class GlOverlay {
1135
1154
  * - The panes under it hold nothing of it, and are owed nothing.
1136
1155
  */
1137
1156
  _settleCarried({ node, reach, dx, dy }, reaches) {
1157
+ // a composited pane holds its child alone, and the layer's move carried
1158
+ // all of it: nothing is owed anywhere
1159
+ if (this.composited) return;
1138
1160
  const panes = this.panes;
1139
1161
  const at = panes.indexOf(this.byNode.get(node));
1140
1162
  const own = panes[at];
@@ -1231,10 +1253,15 @@ export class GlOverlay {
1231
1253
  for (const piece of outside(was, dest)) add(piece);
1232
1254
  if (now) for (const piece of outside(now, dest)) add(piece);
1233
1255
  // the children whose pixels the pane holds: every other one on a pane
1234
- // over the whole surface, and on X11 the ones under this child — the
1235
- // panes over it are owed the copy by `Pane.blit`
1256
+ // over the whole surface, on X11 the ones under this child — the panes
1257
+ // over it are owed the copy by `Pane.blit` — and none on a composited
1258
+ // pane a child
1236
1259
  const order = area.paintOrder();
1237
- const upTo = own ? order.indexOf(node) : order.length;
1260
+ const upTo = !own
1261
+ ? order.length
1262
+ : this.composited
1263
+ ? 0
1264
+ : order.indexOf(node);
1238
1265
  for (let i = 0; i < upTo; i++) {
1239
1266
  const other = order[i];
1240
1267
  if (other === node) continue;
@@ -1324,8 +1351,13 @@ export class GlOverlay {
1324
1351
  }
1325
1352
  root._paintDamage = pass;
1326
1353
  // an X11 pane holds its child and what is under it, and never
1327
- // what is over it — the pane over it holds that (see the header)
1328
- area._paintChildren(ctx, pane.node);
1354
+ // what is over it — the pane over it holds that (see the header);
1355
+ // a composited pane a child holds its child alone
1356
+ area._paintChildren(
1357
+ ctx,
1358
+ pane.node,
1359
+ pane.transparent && pane.node !== null,
1360
+ );
1329
1361
  } finally {
1330
1362
  root._paintDamage = null;
1331
1363
  ctx.restore();
@@ -337,9 +337,10 @@ export class NodePaint {
337
337
  /**
338
338
  * The children, in paint order — or those up to and including `upTo`: an
339
339
  * X11 pane over a `<glarea>` holds its own child and what is under it,
340
- * never what is over it (src/gloverlay.js, `_paintPane`).
340
+ * never what is over it (src/gloverlay.js, `_paintPane`) — or `upTo` alone
341
+ * (`only`), which a composited pane a child holds.
341
342
  */
342
- _paintChildren(ctx, upTo = null) {
343
+ _paintChildren(ctx, upTo = null, only = false) {
343
344
  // A retained presenter replays a node's `paint` into a visual of that
344
345
  // node's own — its children have visuals of their own, so it sets this
345
346
  // for the duration of the call (src/cocoa/presenter.js, `paintSelf`).
@@ -348,7 +349,7 @@ export class NodePaint {
348
349
  let order = this.paintOrder();
349
350
  if (upTo !== null) {
350
351
  const at = order.indexOf(upTo);
351
- if (at !== -1) order = order.slice(0, at + 1);
352
+ if (at !== -1) order = only ? [upTo] : order.slice(0, at + 1);
352
353
  }
353
354
  if (order.length === 0) return;
354
355
  if (!this.clipsChildren()) {