1 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 be255ee5f5 show press-to-paint in the panel, temporarily
The lag is felt in the browser and every number so far was measured somewhere
else. At x11vnc's own socket, press-to-paint is ~50ms: median 47ms over 8
synthetic presses, and 51ms with a second x11vnc running with X DAMAGE enabled
— so -noxdamage costs nothing and the comment defending it stands.

What that cannot include is the sidecar relay, the platform's socket relay and
the network, which is where the rest of the lag has to be. This prints the
end-to-end figure in the status row so the two can be subtracted.

Instrumentation, not a feature. Delete it and its row once the question is
settled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: f7a62308-0977-4cf4-aa29-c020853fd420
2026-08-29 08:17:38 +01:00
+25 -1
View File
@@ -82,6 +82,19 @@ export const MinesweeperPanel = () => {
const [frame, setFrame] = useState<{ width: number; height: number } | null>(null);
const [box, setBox] = useState<{ width: number; height: number; dpr: number; zoom: number } | null>(null);
const [status, setStatus] = useState<SessionStatus | null>(null);
/*
TEMPORARY INSTRUMENTATION — press-to-paint, measured where it is actually felt.
The same figure taken at x11vnc's own socket is ~50ms (median 47ms over 8 synthetic presses, and 51ms
with the X DAMAGE extension enabled, so `-noxdamage` is not costing anything). What that number cannot
include is the two relay hops and the network, and those are the only places the rest of the lag can
be. This is the end-to-end figure, so the two can be subtracted.
Cleared on the frame that follows a press, so it times the first paint after a click rather than every
repaint. Delete this and its row below once the question is settled.
*/
const pressAtRef = useRef<number | null>(null);
const [lagMs, setLagMs] = useState<number | null>(null);
const [error, setError] = useState<string | null>(null);
const [connected, setConnected] = useState(false);
const [busy, setBusy] = useState(false);
@@ -200,6 +213,10 @@ export const MinesweeperPanel = () => {
},
onFrame: (image, x, y) => {
canvasRef.current?.getContext('2d')?.putImageData(image, x, y);
if (pressAtRef.current !== null) {
setLagMs(Math.round(performance.now() - pressAtRef.current));
pressAtRef.current = null;
}
},
onCopyRect: (from, to) => {
// Same canvas as source and destination, which is what CopyRect is: the 2D context takes a
@@ -301,7 +318,10 @@ export const MinesweeperPanel = () => {
const rfb = rfbRef.current;
if (!rfb) return;
ev.preventDefault();
if (kind === 'down') ev.currentTarget.focus();
if (kind === 'down') {
ev.currentTarget.focus();
pressAtRef.current = performance.now();
}
if (kind !== 'move') {
const bit = BUTTON_MASK[ev.button] ?? 0;
@@ -351,6 +371,10 @@ export const MinesweeperPanel = () => {
game or a repair for when it stops behaving, and a footer is where neither gets read. */}
<div className="flex shrink-0 items-center gap-4 text-sm text-primary">
<span>{connected ? 'connected' : 'connecting…'}</span>
{/* Temporary — see `pressAtRef`. ~50ms of this is the game and the capture; the rest is the trip. */}
<span title="Press to painted pixels, measured in this browser. Source-side is ~50ms of it.">
lag {lagMs === null ? '—' : `${lagMs}ms`}
</span>
{/* Focus decides where keys go, and a canvas gives no hint that it can hold any. */}
<span>click the board to send it keys</span>
<button type="button" onClick={() => void refresh()} className="flex items-center gap-1.5 hover:underline">