Surface pixelpass screen-sharing from inside a peerspeak room. peerspeak owns voice, pixelpass owns pixels — they're never Cargo deps of each other; the contract is pixelpass's CLI flags + its `--output json` stdout stream. Modelled on Discord: multiple simultaneous sharers, a 🔴 Live badge + 👁 Watch on each sharing peer's card, and in-progress shares visible to late joiners. - New `src/screenshare` module: pure `parse_pixelpass_event` seam + `pixelpass_path` discovery (13 unit tests), async `spawn_host` (→ ticket) and `spawn_viewer` (→ parse connected{url} → open mpv, vlc fallback). No new deps. - Sharing rides presence: `PeerState.sharing: Option<ticket>` (serde-defaulted), so the existing gossip re-announce delivers the offer to late joiners for free and a PeerUpdated fires on start/stop — no separate gossip message needed. - core: Start/Stop/ViewShare commands; host + viewer children tracked in the session, killed on stop/leave (kill_on_drop backstop). Viewer limit left to pixelpass's bandwidth-measured cap. - UI: Share/Stop button (graceful "needs pixelpass" disabled state), Live badge + Watch on peer cards, Sharing badge on the self card. Verified by screenshot. - config: optional `pixelpass_path` override (hand-editable). Tests-green; the 2-machine gossip/remote path is not yet field-verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
77 lines
3.4 KiB
Rust
77 lines
3.4 KiB
Rust
use crate::config::NetworkMode;
|
|
use crate::network::PeerState;
|
|
use iroh::EndpointId;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum CoreCommand {
|
|
Join { name: String, ticket: String, input_device: Option<String>, output_device: Option<String>, echo_cancellation: bool },
|
|
Leave,
|
|
ToggleMute,
|
|
ToggleDeafen,
|
|
SetPttMode(bool),
|
|
SetPttActive(bool),
|
|
SetPeerVolume(EndpointId, f32),
|
|
/// Locally mute/unmute a peer: when muted, their audio is decoded (so levels
|
|
/// still show) but not mixed into our output.
|
|
SetPeerMuted(EndpointId, bool),
|
|
SetNoiseGateThreshold(f32),
|
|
/// App-internal capture gain (mic), applied before the gate/encode. 1.0 = unity.
|
|
SetInputVolume(f32),
|
|
/// App-internal playback gain on the mixed output. 1.0 = unity.
|
|
SetOutputVolume(f32),
|
|
/// Start/stop a standalone capture-only stream that reports the raw mic
|
|
/// level via [`UiEvent::MicLevel`], for gate calibration outside a call.
|
|
/// Ignored while a room session is active (the in-call meter covers that).
|
|
SetMicMonitor { enabled: bool, input_device: Option<String> },
|
|
/// Set the relay/discovery posture. Takes effect on the next room join,
|
|
/// since the endpoint is (re)built then.
|
|
SetNetworkMode(NetworkMode),
|
|
/// Start/stop recording the call to a local WAV (your mic + the incoming
|
|
/// mix). No-op start if already recording / not in a call.
|
|
SetRecording(bool),
|
|
/// Broadcast a room text-chat message. No-op when not in a call.
|
|
SendChat(String),
|
|
/// Set the pixelpass binary location (config override, empty = use `$PATH`).
|
|
/// Sent at startup so screen-share can resolve the binary.
|
|
SetPixelpassPath(Option<String>),
|
|
/// Start sharing our screen: spawn a pixelpass host and announce its ticket
|
|
/// on our presence so the room can watch. No-op when not in a call.
|
|
StartScreenShare,
|
|
/// Stop sharing our screen: kill the pixelpass host and clear the presence
|
|
/// ticket. No-op when not sharing.
|
|
StopScreenShare,
|
|
/// Watch a peer's screen share: spawn a pixelpass viewer for `ticket` and
|
|
/// open it in a local player.
|
|
ViewShare(String),
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum UiEvent {
|
|
RoomJoined { ticket: String, self_id: String },
|
|
RoomLeft,
|
|
PeerJoined { id: EndpointId, state: PeerState },
|
|
PeerLeft { id: EndpointId },
|
|
PeerConnectionFailed { id: EndpointId },
|
|
PeerUpdated { id: EndpointId, state: PeerState },
|
|
/// Audio link to a peer is being (re)established — show a connecting state.
|
|
PeerConnecting { id: EndpointId },
|
|
/// Audio link to a peer is up and carrying audio.
|
|
PeerConnected { id: EndpointId },
|
|
AudioLevels(Vec<(EndpointId, f32)>),
|
|
/// Raw (pre-gate, pre-mute) normalized RMS of the local mic, `0.0..=1.0`,
|
|
/// for the settings level meter. Throttled to ~10/sec.
|
|
MicLevel(f32),
|
|
/// Call recording started; carries the absolute WAV path being written.
|
|
RecordingStarted { path: String },
|
|
/// Call recording stopped; carries the finished WAV path.
|
|
RecordingStopped { path: String },
|
|
/// A room text-chat message arrived from a peer (never our own — local
|
|
/// messages are echoed by the UI on send).
|
|
ChatMessage { name: String, text: String },
|
|
/// Our own screen share started; the UI flips the Share button to "Stop".
|
|
ScreenShareStarted,
|
|
/// Our own screen share stopped (or failed to start).
|
|
ScreenShareStopped,
|
|
Error(String),
|
|
}
|