react-x11 2.8.3 → 2.9.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.
@@ -574,13 +574,28 @@ export class CocoaWindow {
574
574
  return this.app._requestFrame(cb, this);
575
575
  }
576
576
 
577
- /** Push the backing surface at the WindowServer, if anything drew. */
577
+ /**
578
+ * Push the backing surface at the WindowServer, if anything drew — and
579
+ * tell the window node what it cost. The flip is cheap; the catch-up
580
+ * copy behind it is a damage-sized memcpy, and on a window whose every
581
+ * frame repaints most of itself that is a millisecond of the JS thread
582
+ * per frame that the flush never saw. The frame pacer prices the frame
583
+ * by the thread's time, so the present reports in (src/pacing.js,
584
+ * `WindowNode._notePresentCost`).
585
+ */
578
586
  present() {
579
- if (this._presenter) return; // layers upload as they sync
580
- if (!this._dirty || !this._surface || this.destroyed) return;
587
+ const started = performance.now();
588
+ if (!this._presentNow()) return;
589
+ this._reactX11Node?._notePresentCost?.(performance.now() - started);
590
+ }
591
+
592
+ /** The present itself: true when a frame reached the layer. */
593
+ _presentNow() {
594
+ if (this._presenter) return false; // layers upload as they sync
595
+ if (!this._dirty || !this._surface || this.destroyed) return false;
581
596
  // …and if anyone would see it. `_dirty` stays set, so the pump asks
582
597
  // again next tick and the frame goes out the moment the window is back.
583
- if (this._holdPresent || !this._visible()) return;
598
+ if (this._holdPresent || !this._visible()) return false;
584
599
  this._dirty = false;
585
600
  if (this._chain) {
586
601
  const shown = this._chain.back;
@@ -608,7 +623,7 @@ export class CocoaWindow {
608
623
  ]),
609
624
  );
610
625
  if (this._transparentWindow) this.app._shadowStale.add(this);
611
- return;
626
+ return true;
612
627
  }
613
628
  this._native.surfaceToLayer(this._surface, this._layer);
614
629
  // AppKit derives a transparent window's shadow from the content's
@@ -619,6 +634,7 @@ export class CocoaWindow {
619
634
  // the frame BEFORE this one and keeps the square rim for menus that
620
635
  // paint once and are only hovered after.
621
636
  if (this._transparentWindow) this.app._shadowStale.add(this);
637
+ return true;
622
638
  }
623
639
 
624
640
  snapshot(path) {
package/src/debug.js CHANGED
@@ -399,7 +399,7 @@ function createSession({ sink, path }) {
399
399
  }
400
400
  },
401
401
 
402
- frame({ rects, reasons, start, end, landed }) {
402
+ frame({ rects, reasons, start, end, landed, waited }) {
403
403
  frames += 1;
404
404
  const full = !rects;
405
405
  const area = full
@@ -421,7 +421,12 @@ function createSession({ sink, path }) {
421
421
  // timings cannot show. Either way it is paint-vs-everything-else.
422
422
  const wait =
423
423
  typeof landed === 'number' ? ` landed=${landed.toFixed(1)}ms` : '';
424
- line(`frame ${frames}: ${where}${why}${wait}`);
424
+ // How long the frame pacer held this frame's claim before letting
425
+ // the clock have it (src/pacing.js) — only under an adaptive
426
+ // `frameRate`, and only for a frame that was held, so the default
427
+ // path's lines are the lines they always were.
428
+ const held = waited > 0 ? ` waited=${waited.toFixed(1)}ms` : '';
429
+ line(`frame ${frames}: ${where}${why}${wait}${held}`);
425
430
  }
426
431
  record({
427
432
  name: 'frame',
@@ -437,6 +442,7 @@ function createSession({ sink, path }) {
437
442
  area,
438
443
  reasons: reasons ?? [],
439
444
  landedMs: typeof landed === 'number' ? +landed.toFixed(2) : undefined,
445
+ waitedMs: waited > 0 ? +waited.toFixed(2) : undefined,
440
446
  },
441
447
  });
442
448
  },