The native GStreamer screen share path is gone: in a peer-to-peer mesh every
hardware and OS combination behaved differently, and keeping them in step cost
more than it saved. Screen sharing uses Chromium's own capture everywhere.
- Screen share bitrate is a setting: a stepper from 300 to 10,000 Kbps or
unlimited, with press-and-hold repeat and typed entry. Changes apply live,
including to a share going through the voice relay (debounced, so a hold
becomes one update). The default stays 1080p at 30 fps with an 8,000 Kbps cap.
- Black screen share tiles on GPU encoders: the sharer asks for a fresh
keyframe after negotiation, and the watchdog only re-adopts tracks a tile is
not already showing.
- Relay screen publishing is serialized and tied to the share it was started
for, so a stop, a new share or a relay restart can never publish a stale
producer, and a failed first publish retries on its own.
_setupSocketListeners was one 2,602-line function. Its statements are now six
_listen methods called in the original order, so every listener is registered
in the same order as before; no local variable crosses from one part to
another. The parts live in app-socket.js (session, connect and wake),
app-socket-channels.js (channels, messages, members, voice rosters) and
app-socket-events.js (everything else the server pushes).
Checked: all 1,124 other methods have identical source, the six parts rebuild
the old function exactly, and in a running app every one of the 108 events
the old function listened for still has its listener. Messages, edits,
deletes, reactions, pins, settings and reconnecting were exercised live.
Server-side signaling and codec negotiation for a native Desktop screen share path. Off unless the user opts in from Settings, and the toggle only appears inside Haven Desktop builds that carry the native helper. Browser and per-app audio capture are unchanged. Thanks to @bernardokcosta.