remote trackpad and keyboard server
git clone https://git.lucas.co/cce-remote.git
docs: comments name the cursor ring, not the deleted tap mapping
6e6915c removed mapToWindow() and view-mode tap-to-spot, but four comments
still explained the surrounding machinery in terms of it — one naming the
deleted function outright. The mechanisms they describe are all still
load-bearing; only their consumer changed, from mapping a touch point into
the window to placing the cyan cursor ring:
- the #screenwrap CSS note on why the zoom transform sits on the wrapper
(getBoundingClientRect reflects a uniform ancestor transform)
- the view-mode header on what winRect is polled for
- the zoom/pan note, which named mapToWindow
- the marker note, which described placeMarker as "the inverse of the tap
mapping" — there is no longer a forward mapping for it to invert
The same claim in CLAUDE.md's scar-tissue list goes with them. Comments only;
no behavior change, tests unchanged at 25.
Co-Authored-By: Claude Opus 5 <[email protected]>
CLAUDE.md | 8 ++++----
index.html | 17 +++++++++--------
2 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/CLAUDE.md b/CLAUDE.md
index d6e6ec2..2811364 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -218,10 +218,10 @@ Four of its non-obvious constructs are scar tissue. Do not "clean them up":
empty field, so without something to delete, backspace silently does nothing.
`beforeinput` is used throughout because iOS `keydown` reports keyCode 229.
- **The zoom/pan transform lives on `#screenwrap`, not the frame element.** Uniform
- ancestor transform means `getBoundingClientRect` reflects it, keeping tap mapping
- correct while zoomed. (It also used to dodge an iOS bug where a transformed
- multipart-MJPEG `<img>` stopped repainting; the view is a `<canvas>` since the
- 2026-08-22 rework, but the structure stays.)
+ ancestor transform means `getBoundingClientRect` reflects it, keeping the cursor
+ ring correctly placed while zoomed. (It also used to dodge an iOS bug where a
+ transformed multipart-MJPEG `<img>` stopped repainting; the view is a `<canvas>`
+ since the 2026-08-22 rework, but the structure stays.)
- **The `overflow: hidden` clip lives on `#pad`, the non-transformed ancestor.** A clip
on the transformed element scales with its own content and clips nothing.
- **The stream self-heals: reconnect on WS close plus a 30s no-frame watchdog** (the
diff --git a/index.html b/index.html
index 22eaed8..424f656 100644
--- a/index.html
+++ b/index.html
@@ -32,9 +32,10 @@
#clicks button:active, #bar button:active { background: #2c5f69; }
#kb { position: absolute; opacity: 0.02; width: 1px; height: 1px; left: -10px; top: -10px; }
/* Zoom/pan transform lives on the WRAPPER: getBoundingClientRect then
- reflects it, so tap mapping stays correct zoomed. (Historically this also
- dodged an iOS bug where a transformed multipart-MJPEG <img> stopped
- repainting; the live view is a canvas now, but the structure stays.) */
+ reflects it, so the cursor ring stays correctly placed while zoomed.
+ (Historically this also dodged an iOS bug where a transformed
+ multipart-MJPEG <img> stopped repainting; the live view is a canvas
+ now, but the structure stays.) */
#screenwrap { display: none; position: absolute; inset: 0; z-index: 2;
transform-origin: 0 0; border-radius: 12px; overflow: hidden; }
#pad.view #screenwrap { display: block; }
@@ -160,8 +161,8 @@ let dragging = false, dragTimer = null, pendDx = 0, pendDy = 0, pendSy = 0, flus
// ── Window view mode: live view of the focused window. Input is identical
// to the trackpad — the picture is feedback, not a touchscreen.
// Frames arrive as JPEG binaries on a dedicated /wstream WebSocket, drawn to
-// the canvas; winRect for tap mapping is kept fresh by polling `wl` over the
-// input WS (the stream follows focus server-side).
+// the canvas; winRect, which places the cursor ring, is kept fresh by polling
+// `wl` over the input WS (the stream follows focus server-side).
//
// Delivery is ACK-CLOCKED: after rendering a frame we send "n", and only then
// does the server send its NEWEST frame. At most one frame is ever in flight,
@@ -215,7 +216,7 @@ function updateRect(wins) {
}
// View zoom/pan (two-finger pinch/drag in view mode): a pure client-side
// transform of the streamed image. Uniform scale+translate means
-// getBoundingClientRect reflects it, so mapToWindow stays correct zoomed.
+// getBoundingClientRect reflects it, so placeMarker stays correct zoomed.
let vZoom = 1, vPanX = 0, vPanY = 0, pinch = null;
const screenWrap = document.getElementById("screenwrap");
function applyViewTransform() {
@@ -230,8 +231,8 @@ function clampPan() {
}
// The compositor's frames carry no cursor (they're surface textures), so
-// view mode draws its own marker: poll pointer-location over the WS and
-// place the ring via the inverse of the tap mapping.
+// view mode draws its own marker: poll pointer-location over the WS and map
+// that layout position through the contain-fit letterbox onto the canvas.
const vcursor = document.getElementById("vcursor");
let ptr = null; // last known pointer position in layout px (from ploc + prediction)
function placeMarker(lx, ly) {