react-x11 2.21.0 → 2.21.2

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 (2) hide show
  1. package/package.json +2 -2
  2. package/src/win32/app.js +39 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-x11",
3
- "version": "2.21.0",
3
+ "version": "2.21.2",
4
4
  "description": "react renderer with X11 as a target",
5
5
  "main": "./src/index.js",
6
6
  "files": [
@@ -110,7 +110,7 @@
110
110
  "optionalDependencies": {
111
111
  "@windowkit/appkit": "^0.13.0",
112
112
  "@windowkit/wayland": "^3.1.1",
113
- "@windowkit/win32": "^0.0.3",
113
+ "@windowkit/win32": "^0.0.4",
114
114
  "dbus-native": "^0.15.1",
115
115
  "x11-dri": "^0.9.0"
116
116
  },
package/src/win32/app.js CHANGED
@@ -48,14 +48,24 @@ import { Win32Window } from './window.js';
48
48
  // when that is unavailable.
49
49
  const FRAME_INTERVAL_MS = 1000 / 60;
50
50
 
51
- // How long a promised compositor tick may take before the timer takes over
52
- // for the rest of the session. A tick that is asked for and never arrives
53
- // would stop every frame in the application for good — an app frozen with a
54
- // clean event loop, which is the worst kind to be told about — so the clock
55
- // is watched. One timer per frame costs nothing: what was wrong with a timer
56
- // here was never its cost, only its granularity.
51
+ // How long a promised compositor tick may take, while the clock has been
52
+ // ticking, before a timer answers the frame in its place. A tick that is
53
+ // asked for and never arrives would stop every frame in the application for
54
+ // good — an app frozen with a clean event loop, which is the worst kind to be
55
+ // told about — so the clock is watched. One timer per frame costs nothing:
56
+ // what was wrong with a timer here was never its cost, only its granularity.
57
57
  const CLOCK_WATCHDOG_MS = 250;
58
58
 
59
+ // How long since the clock last ticked before it is not trusted to be
60
+ // ticking. A request made after a quiet spell waited 0.3–3 s for its tick
61
+ // (measured on a 180 Hz panel: 5 of 8 cold starts of the Flow bench, and the
62
+ // first frame after most pauses between its phases), so such a frame races a
63
+ // frame interval's timer, and the tick it asked for, however late, says the
64
+ // clock is back. It used to be the watchdog's to catch, and a watchdog that
65
+ // fired latched the timer for the rest of the session — 34 frames a second,
66
+ // alternating one system tick and two, from then on.
67
+ const CLOCK_IDLE_MS = 50;
68
+
59
69
  /**
60
70
  * A pointer event's position on the screen, which X carries as `rootx`/
61
71
  * `rooty` and Windows does not: WM_MOUSEMOVE and the button messages are all
@@ -88,9 +98,9 @@ export class Win32App {
88
98
  /** A frame has been asked for and not yet delivered — one outstanding
89
99
  * request at a time, whether the clock or the timer is answering it. */
90
100
  this._framePending = false;
91
- /** The compositor promised a tick and did not deliver one. Latched: the
92
- * timer answers every frame from then on. */
93
- this._clockLost = false;
101
+ /** When the compositor's clock last ticked: within `CLOCK_IDLE_MS` it is
102
+ * trusted to answer the next frame, and otherwise a timer races it. */
103
+ this._clockAt = -Infinity;
94
104
  this._closed = false;
95
105
  this._atoms = new Map();
96
106
  this._appearanceListeners = new Set();
@@ -473,14 +483,22 @@ export class Win32App {
473
483
  this._framePending = true;
474
484
  // One tick, from the compositor if this Windows has a clock to wait on.
475
485
  // It answers false when it has not, and then — and only then — a timer.
476
- if (!this._clockLost && this._native.frameClockRequest?.()) {
477
- this._frameTimer = setTimeout(() => {
478
- this._frameTimer = null;
479
- if (!this._framePending || this._closed) return;
480
- this._clockLost = true;
481
- this._framePending = false;
482
- this._tickFrames();
483
- }, CLOCK_WATCHDOG_MS);
486
+ if (this._native.frameClockRequest?.()) {
487
+ // A clock that has been ticking answers this frame, and the timer only
488
+ // watches for a tick that never comes. One that has not ticked lately
489
+ // may be asleep, so the timer races it at a frame interval — and a
490
+ // clock that never ticks again paces the app exactly as the timer
491
+ // alone would, which is what a lost clock used to be latched into.
492
+ const ticking = performance.now() - this._clockAt < CLOCK_IDLE_MS;
493
+ this._frameTimer = setTimeout(
494
+ () => {
495
+ this._frameTimer = null;
496
+ if (!this._framePending || this._closed) return;
497
+ this._framePending = false;
498
+ this._tickFrames();
499
+ },
500
+ ticking ? CLOCK_WATCHDOG_MS : FRAME_INTERVAL_MS,
501
+ );
484
502
  this._frameTimer.unref?.();
485
503
  return;
486
504
  }
@@ -546,8 +564,11 @@ export class Win32App {
546
564
  switch (event.type) {
547
565
  // The compositor is ready for a frame. Carries no id and belongs to no
548
566
  // window: it is the clock, and every window that asked for a frame in
549
- // the meantime is paced by this one tick.
567
+ // the meantime is paced by this one tick. A tick that a timer beat to
568
+ // its frame still says the clock is awake again, and answers whatever
569
+ // frame has been asked for since.
550
570
  case 'frame-clock':
571
+ this._clockAt = performance.now();
551
572
  if (this._frameTimer) clearTimeout(this._frameTimer);
552
573
  this._frameTimer = null;
553
574
  this._framePending = false;