Commit graph

12 commits

Author SHA1 Message Date
iris
4814aaefdb fix: screen fit-to-window — lift flex auto-minimum on the canvas
PR #171 made relayoutCanvas() set canvas.style.width/height
explicitly, but the canvas is a flex item: flex items default to
min-width/min-height: auto, which resolves to the canvas's intrinsic
framebuffer resolution and clamps the JS-set display size right back
up to native — so fit mode still did nothing (#133, "still same
behavior").

Add min-width/min-height: 0 (+ flex: none) on the canvas in fit mode
so the explicit downscaled size actually sticks. Scoped to
#canvas-wrap.fit so non-fit mode keeps native size + scroll.

re #133
2026-05-21 19:40:17 +02:00
iris
dfee0574a5 fix: screen fit-to-window actually scales the canvas (#133)
The fit toggle relied on canvas.fit { max-width/max-height: 100% }.
The canvas is a flex item, and a flex item's automatic minimum size
(min-width/min-height: auto → the replaced element's intrinsic size)
overrides max-*, so an oversized desktop never shrank — it just got
centred and clipped, looking like the toggle did nothing.

Replace the CSS approach with explicit JS sizing: relayoutCanvas()
computes a scale factor (min of width/height ratios, capped at 1 so
it never upscales) and sets canvas.style.width/height in px,
preserving aspect ratio. Recomputed on fit toggle, window resize,
and framebuffer-size (ServerInit). Pointer mapping is unaffected —
sendPointer already derives scale from getBoundingClientRect().
2026-05-21 18:45:13 +02:00
iris
62aa3bb3ec agent icon: render on dashboard + per-agent web UI
Consumes the GET /icon endpoint from #139:

- Dashboard: each container card shows the agent's icon next to its
  name (26px). Loaded from <agent-url>/icon; onerror hides it for a
  stopped container whose web server isn't answering.
- Per-agent web UI: the agent's icon next to the page title (40px),
  and /icon as the favicon on the index, stats, and screen pages.

/icon always returns an image (configured SVG or the default
hyperhive logo), so no presence check is needed.

Closes #140
2026-05-21 15:36:58 +02:00
iris
305a32220b screen: add fit-to-window toggle
Adds a 'fit' toolbar toggle on the /screen VNC viewer. When on (the
default), the canvas is CSS-scaled down to fit the browser viewport
preserving aspect ratio — no more scrolling a desktop larger than the
window. When off, the canvas renders at native resolution with scroll.
The choice persists in localStorage.

The canvas's intrinsic resolution is never touched — only its display
size. sendPointer now rescales client coordinates by the canvas
display-vs-intrinsic ratio so clicks stay accurate in fit mode (this
also fixes a latent off-by-scale bug). Toolbar buttons share a .tbtn
class for consistent styling.

Issue #133 (fit toggle now; dynamic desktop resize tracked separately).
2026-05-20 22:56:19 +02:00
iris
a5ef10c10c screen: fix framebuffer height parse and stray byte consume
Two RFB protocol bugs in the /screen client:

1. ServerInit parsed framebuffer-height from byte offset 1 instead of
   2. RFB 3.8 ServerInit is width@0-1, height@2-3; u16be(b,1) mixes
   the low byte of width with the high byte of height. The wrong
   height went into the initial non-incremental FramebufferUpdate-
   Request; neatvnc feeds the client's width/height straight into
   pixman_region_union_rect un-clipped (src/server.c), so an
   out-of-range height corrupts the server damage region and pixman
   reports 'Invalid rectangle passed'.

2. FramebufferUpdate handler had a stray drainTo(1) after the 3-byte
   padding+nRects header, consuming the first byte of the first
   rectangle and desyncing every update.

Closes #128
2026-05-20 22:11:28 +02:00
iris
c10b4e26ef screen: fix MD5 rotation schedule and send root username
Two bugs found via the weston journal (issue #92):

1. MD5 rotation index used (j>>2 & 3) for the round number, which
   cycles every 4 steps instead of every 16. Verified against RFC 1321
   test vectors: md5("") was 7a1dce5b... instead of d41d8cd9... — the
   derived AES key was wrong, so the server decrypted the credentials
   to garbage. Fixed to j>>4.

2. weston's vnc_handle_auth calls getpwnam(username) and requires
   pw_uid == weston's own uid before PAM is consulted. We sent an empty
   username, which fails outright ("VNC: wrong user"). weston runs as
   root, so send username "root"; the empty password still passes via
   pam_permit.so on the weston-remote-access service.

Fixes #92
2026-05-20 21:47:27 +02:00
iris
e7d7aef1aa screen: fix Apple-DH response byte order
rfb_apple_dh_client_msg has encrypted_credentials at offset 0 and
public_key as a flexible array at offset 128. We were sending them
in the wrong order (pub_key first), so neatvnc decrypted the wrong
bytes as credentials and sent the wrong bytes as the DH public key,
causing a mismatched shared secret and SecurityResult=1.

Fixes #92
2026-05-20 20:53:30 +02:00
iris
3224178d2d screen: implement Apple-DH (type 30) auth for neatvnc 0.9 compatibility 2026-05-20 19:12:40 +02:00
iris
76061eb3ae screen: reject unsupported security types with clear error instead of mishandling 2026-05-20 17:49:26 +02:00
iris
86e4f41203 screen: add RFB debug log panel for handshake diagnosis 2026-05-20 17:13:35 +02:00
iris
de13e80082 screen: fix VeNCrypt security negotiation for weston VNC backend
weston's VNC backend (neatvnc) uses VeNCrypt (security type 19) as
the outer type even with --disable-transport-layer-security, offering
sub-type 1 (None, no TLS, no password) within it.

The old screen.html only handled type 1 (plain None) and type 2 (VNC
auth), causing 'auth failed' against weston.

Add VeNCrypt states to the RFB state machine:
- vencrypt-version: echo back server's major.minor
- vencrypt-subtypes: pick sub-type 1 (None) if available
- vencrypt-accept: check server's acceptance byte
Then falls through to the normal security-result / server-init path.
2026-05-20 15:01:35 +02:00
iris
2027e94432 harness: add /screen page and /screen/ws WebSocket VNC relay
Reads /etc/hyperhive/gui.json at startup to get the VNC port written
by the weston-vnc ExecStart script (issue #50). Adds:
- gui_vnc_port: Option<u16> on AppState
- gui_enabled: bool on StateSnapshot (for issue #52 screen link)
- GET /screen: serves a minimal RFB-over-WebSocket viewer (screen.html)
- GET /screen/ws: upgrades to WebSocket and byte-pumps to 127.0.0.1:<vnc_port>

The relay is a pure two-task byte pump (WS→TCP and TCP→WS), transparent
to any RFB variant including VeNCrypt. Returns 404 when gui is not
enabled.

screen.html is a self-contained RFB client: handshake, FramebufferUpdate
(Raw encoding), pointer and keyboard forwarding — enough to display the
desktop and interact with it. noVNC assets (issue #52) replace this.

Closes #51
2026-05-20 14:38:42 +02:00