Skip to content

app: serve remote views on GET /remote - #730

Draft
foxnne wants to merge 2 commits into
mainfrom
app/remote-view-host
Draft

foxnne wants to merge 2 commits into
mainfrom
app/remote-view-host

Conversation

@foxnne

@foxnne foxnne commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator

Part of fizzyedit/work#40 (EMBEDDED_EDITOR_PLAN step 3, fizzyedit/work#32)

What changes

A remote client (an adapter in Blender, Unreal, …) can now connect to the control socket, GET /remote with Upgrade: fizzy-remote-view/1, and speak the sdk/remote protocol: open a view, send pointer/key/text input, ask for frames and get them. Without the upgrade header the answer is 426, with another method 405. The socket stays 0600 and same-user.

  • app/control/Link.zig + Server.zig: an upgraded connection becomes two byte queues. A reader and a writer task per connection do the waiting; the UI thread drains and fills the queues once per frame (Service.pump). No thread per view, and the UI thread never blocks.
  • app/remote/RemoteViewHost.zig: a ViewHost that the OS never shows. ViewHost's vtable is unchanged. Input waits in the host as dvui events until inject(win); keys and text only while the client says the view is focused. A view the client closes, or one that goes with a dropped connection, is dropped from its session.
  • app/remote/Session.zig / Service.zig: one conversation per client over Endpoint, and the registry. fizzy.remote.views (internal) lists the open views.
  • app/remote/encoder.zig: the stub, one quad filling the view.
  • app/remote/keys.zig: Key.code is read as a USB HID usage (the numbering SDL scancodes use). This is a decision I made, not one the plan states; please check it.
  • docs/CONTROL.md: the route and fizzy.remote.views.

SDK impact

  • None

(sdk_version untouched; sdk/remote is only imported by the app module, in build/sdk.zig.)

Verified

Tests in app/remote/tests.zig (fizzy-app-tests), among them a client on the real socket: create a view, send motion/click/key/text, ask a frame, drop the connection with a frame request in flight (the session and its view go, fizzy keeps running), then a second client served as before. One editor-facing body of ViewHost calls runs against a local fake host and the remote one. Input is injected into a dvui.testing window and checked at the view's offset. Server.zig has its own tests for the upgraded byte stream.

  • macOS:
  • Windows:
  • Linux: scripts/check-fmt.sh, build, build test, build test-integration (92/92 steps), build check-web, build test-sdk-version, build lint, build check-tests-reached
  • Web: check-web builds; the control server is native only

Follow-ups

  • The editor's frame loop does not draw remote views yet (TODO(seam) in Service.zig, EMBEDDED_EDITOR_PLAN step 4). Until it does, input waits in the host (up to 1024 events) and pump answers frame requests itself with the stub. I did not wire the Editor, so remote input cannot land on the main window at the wrong origin.
  • The real draw-list encoder replaces encoder.solidFill (TODO(seam)).
  • Touch, IME and clipboard messages are accepted and ignored for now.
  • The diff is a little over 800 lines outside tests and docs, counting Link/Server upgrade code that has to travel with the route.
  • I had no jj in this cloud session (its release download is blocked and the checkout has no .jj), so this branch was committed with git.

Generated by Claude Code

A client upgrades a connection on the control socket (Upgrade:
fizzy-remote-view/1) and speaks the sdk/remote protocol: it opens views,
sends pointer, key and text input, and asks for frames. Each view is a
RemoteViewHost, a ViewHost the OS never shows; its frame is a stub, one
quad filling the view.

The server hands an upgraded connection to the app as two byte queues
(control/Link), so the UI thread reads and writes once a frame and no
thread is needed per view. A view the client closes, or that goes with a
dropped connection, closes its window. fizzy.remote.views lists them.

Part of fizzyedit/work#40

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016mgAaDN3ndpHLtmoA72ZLQ
Fixes from reviewing the diff: a double free of the link when accepting
it ran out of memory; a close that could be missed by the writer task;
the link read after the app could free it; a slow peer that could not be
cut off (the link now shuts its socket down on overflow, and the stop
path shuts connections down before waiting on them); an unbounded
inbox. Input past the queue's cap now drops motion, never a press, a
release or a key-up, and a zero-sized resize is ignored.

Part of fizzyedit/work#40

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016mgAaDN3ndpHLtmoA72ZLQ

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants