pub fn gpio_power_label(power: &CapturePowerStatus) -> String { if !power.available { return "Unavailable".to_string(); } if !power.enabled { return "Power Off".to_string(); } match power.detected_devices { 0 => "No Eyes".to_string(), 1 => "1 Eye".to_string(), count => format!("{count} Eyes"), } } pub fn capture_power_detail(power: &CapturePowerStatus) -> String { if !power.available { return format!("{} is unavailable: {}", power.unit, power.detail); } let detected = if power.enabled { format!(" • {}", gpio_detection_detail(power.detected_devices)) } else { String::new() }; match power.mode.as_str() { "forced-on" => format!( "{} • awake • {}{} • leases {}", power.unit, power.detail, detected, power.active_leases ), "forced-off" => format!( "{} • dark • {}{} • leases {}", power.unit, power.detail, detected, power.active_leases ), _ => format!( "{} • auto • {}{} • leases {}", power.unit, power.detail, detected, power.active_leases ), } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum StatusLightState { Idle, Live, Connected, Warning, Caution, } impl StatusLightState { fn from_active(active: bool) -> Self { if active { Self::Live } else { Self::Idle } } fn css_class(self) -> &'static str { match self { Self::Idle => "status-light-idle", Self::Live => "status-light-live", Self::Connected => "status-light-connected", Self::Warning => "status-light-warning", Self::Caution => "status-light-caution", } } } fn server_light_state(state: &LauncherState, relay_live: bool) -> StatusLightState { if !state.server_available || !server_version_known(state) { StatusLightState::Idle } else if server_versions_match(state) { if relay_live { StatusLightState::Connected } else { StatusLightState::Live } } else if relay_live { StatusLightState::Caution } else { StatusLightState::Warning } } /// Confirms the server reported a usable version before claiming compatibility. fn server_version_known(state: &LauncherState) -> bool { state .server_version .as_deref() .map(normalize_version) .is_some_and(|version| !version.is_empty()) } /// Compares the reachable server version against this client build. fn server_versions_match(state: &LauncherState) -> bool { state .server_version .as_deref() .map(normalize_version) .is_some_and(|server_version| server_version == normalize_version(crate::VERSION)) } /// Compares versions independent of whether the source included a leading `v`. fn normalize_version(version: &str) -> &str { version.trim().trim_start_matches('v') } /// Show the connected server version, or an explicit unknown marker when disconnected. fn server_version_label(state: &LauncherState) -> String { if !state.server_available { return "???".to_string(); } let version = state .server_version .as_deref() .map(str::trim) .filter(|version| !version.is_empty()); match version { Some(version) if version.starts_with('v') => version.to_string(), Some(version) => format!("v{version}"), None => "???".to_string(), } } /// Summarize whether the composite USB gadget appears reachable to the host. fn recovery_usb_health(state: &LauncherState) -> (StatusLightState, String) { if !state.server_available { return (StatusLightState::Idle, "Offline".to_string()); } if matches!(state.server_camera_output.as_deref(), Some("uvc")) { return (StatusLightState::Live, "Enumerated".to_string()); } if let Some(output) = state.server_camera_output.as_deref() { return (StatusLightState::Warning, output.to_ascii_uppercase()); } if state.server_camera.is_none() && state.server_microphone.is_none() { return (StatusLightState::Caution, "Unknown".to_string()); } if state.server_camera == Some(false) && state.server_microphone == Some(false) { return (StatusLightState::Warning, "Missing".to_string()); } (StatusLightState::Caution, "Partial".to_string()) } /// Summarize whether the UAC microphone/audio function is advertised by the relay. fn recovery_uac_health( state: &LauncherState, relay_live: bool, stream: Option<&crate::uplink_telemetry::UpstreamStreamTelemetry>, ) -> (StatusLightState, String) { if !state.server_available { return (StatusLightState::Idle, "Offline".to_string()); } if state.server_microphone == Some(false) { return (StatusLightState::Warning, "Missing".to_string()); } if state.server_microphone.is_none() { return (StatusLightState::Caution, "Unknown".to_string()); } if !relay_live { return (StatusLightState::Live, "Ready".to_string()); } if !state.channels.microphone { return (StatusLightState::Idle, "Paused".to_string()); } media_stream_health(stream, MediaStreamKind::Microphone) } /// Summarize whether the UVC camera function is advertised with the expected codec. fn recovery_uvc_health( state: &LauncherState, relay_live: bool, stream: Option<&crate::uplink_telemetry::UpstreamStreamTelemetry>, ) -> (StatusLightState, String) { if !state.server_available { return (StatusLightState::Idle, "Offline".to_string()); } let codec = state .server_camera_codec .as_deref() .map(|value| value.to_ascii_uppercase()) .unwrap_or_else(|| "READY".to_string()); if state.server_camera == Some(false) { return (StatusLightState::Warning, "Missing".to_string()); } if state.server_camera.is_none() { return (StatusLightState::Caution, "Unknown".to_string()); } if !matches!(state.server_camera_output.as_deref(), Some("uvc")) { let value = state .server_camera_output .as_deref() .map(|output| format!("{}/{}", output.to_ascii_uppercase(), codec)) .unwrap_or(codec); return (StatusLightState::Caution, value); } if !relay_live { return (StatusLightState::Live, codec); } if !state.channels.camera { return (StatusLightState::Idle, "Paused".to_string()); } let (health, label) = media_stream_health(stream, MediaStreamKind::Camera); if matches!(health, StatusLightState::Live) { (health, codec) } else { (health, label) } } #[derive(Clone, Copy)] enum MediaStreamKind { Camera, Microphone, } /// Converts live uplink telemetry into the small, glanceable UAC/UVC chip state. fn media_stream_health( stream: Option<&crate::uplink_telemetry::UpstreamStreamTelemetry>, kind: MediaStreamKind, ) -> (StatusLightState, String) { let Some(stream) = stream else { return match kind { MediaStreamKind::Camera => (StatusLightState::Caution, "No Frames".to_string()), MediaStreamKind::Microphone => (StatusLightState::Caution, "No Flow".to_string()), }; }; if !stream.enabled { return (StatusLightState::Idle, "Paused".to_string()); } if !stream.last_error.trim().is_empty() { return (StatusLightState::Warning, "Error".to_string()); } if !stream.connected { return match kind { MediaStreamKind::Camera => (StatusLightState::Caution, "No Frames".to_string()), MediaStreamKind::Microphone => (StatusLightState::Caution, "No Flow".to_string()), }; } if stream.packets_streamed == 0 { let label = if stream.packets_enqueued > 0 { "Queued" } else { match kind { MediaStreamKind::Camera => "No Frames", MediaStreamKind::Microphone => "No Flow", } }; return (StatusLightState::Caution, label.to_string()); } let (delivery_budget_ms, enqueue_budget_ms, queue_pressure, healthy_label) = match kind { MediaStreamKind::Camera => (250.0, 250.0, 24, "Frames"), MediaStreamKind::Microphone => (180.0, 120.0, 12, "Flowing"), }; if stream.latest_delivery_age_ms > delivery_budget_ms || stream.latest_enqueue_age_ms > enqueue_budget_ms { return (StatusLightState::Caution, "Lagging".to_string()); } if stream.queue_depth >= queue_pressure { return (StatusLightState::Caution, "Dropping".to_string()); } (StatusLightState::Live, healthy_label.to_string()) } fn gpio_light_state(power: &CapturePowerStatus) -> StatusLightState { if !power.available || !power.enabled { return StatusLightState::Idle; } match power.detected_devices { 0 => StatusLightState::Warning, 1 => StatusLightState::Caution, _ => StatusLightState::Live, } } fn gpio_detection_detail(detected_devices: u32) -> String { match detected_devices { 0 => "no eyes detected".to_string(), 1 => "1 eye detected".to_string(), count => format!("{count} eyes detected"), } } /// Highlights the currently active capture mode so it reads like a segmented control. fn sync_power_mode_button_styles(widgets: &LauncherWidgets, mode: &str) { for button in [ &widgets.power_auto_button, &widgets.power_on_button, &widgets.power_off_button, ] { button.remove_css_class("pill-toggle-active"); } match mode { "forced-on" => widgets.power_on_button.add_css_class("pill-toggle-active"), "forced-off" => widgets.power_off_button.add_css_class("pill-toggle-active"), _ => widgets .power_auto_button .add_css_class("pill-toggle-active"), } } /// Reports which local staging checks are active right now. fn local_test_detail( camera_running: bool, microphone_running: bool, speaker_running: bool, microphone_replay_running: bool, ) -> String { let mut active = Vec::new(); if camera_running { active.push("camera preview"); } if microphone_running { active.push("mic monitor"); } if speaker_running { active.push("speaker tone"); } if microphone_replay_running { active.push("mic replay"); } if active.is_empty() { "Local checks are idle. Use Start Preview, Monitor Mic, Replay, or Play Tone before you launch." .to_string() } else { format!( "Local checks running: {}. Stop them whenever staging is complete.", active.join(", ") ) } } fn install_popout_drag(window: >k::ApplicationWindow, widget: &impl IsA) { let drag = gtk::GestureClick::new(); drag.set_button(0); let native = window.clone(); drag.connect_pressed(move |gesture, _press, x, y| { let Some(device) = gesture.current_event_device() else { return; }; let Some(surface) = native.surface() else { return; }; let Some(toplevel) = surface.dynamic_cast_ref::() else { return; }; let timestamp = gesture .current_event() .map(|event| event.time()) .unwrap_or(0); toplevel.begin_move(&device, 1, x, y, timestamp); }); widget.add_controller(drag); } fn apply_popout_window_geometry( window: >k::ApplicationWindow, root: >k::Box, picture: >k::Picture, size: BreakoutSizeChoice, display_limit: super::state::PreviewSourceSize, ) { picture.set_size_request(size.width, size.height); root.set_size_request(size.width, size.height); window.set_default_size(size.width, size.height); if should_cover_display(size, display_limit) { fullscreen_on_largest_monitor(window); } else { window.unfullscreen(); } } fn schedule_popout_window_geometry( window: gtk::ApplicationWindow, root: gtk::Box, picture: gtk::Picture, size: BreakoutSizeChoice, display_limit: super::state::PreviewSourceSize, ) { for delay_ms in [0_u64, 25, 150] { let window = window.clone(); let root = root.clone(); let picture = picture.clone(); glib::timeout_add_local_once(std::time::Duration::from_millis(delay_ms), move || { apply_popout_window_geometry(&window, &root, &picture, size, display_limit); window.present(); }); } } fn fullscreen_on_largest_monitor(window: >k::ApplicationWindow) { let Some(display) = gdk::Display::default() else { window.fullscreen(); return; }; let monitors = display.monitors(); let monitor = (0..monitors.n_items()) .filter_map(|idx| monitors.item(idx)) .filter_map(|obj| obj.downcast::().ok()) .max_by_key(|monitor| { let geometry = monitor.geometry(); let scale = monitor.scale_factor().max(1); geometry.width().max(1) as i64 * scale as i64 * geometry.height().max(1) as i64 * scale as i64 }); if let Some(monitor) = monitor.as_ref() { window.fullscreen_on_monitor(monitor); } else { window.fullscreen(); } } fn should_cover_display( size: BreakoutSizeChoice, display_limit: super::state::PreviewSourceSize, ) -> bool { matches!(size.preset, super::state::BreakoutSizePreset::FillDisplay) || (size.width >= display_limit.width.max(1) as i32 && size.height >= display_limit.height.max(1) as i32) } pub fn present_popout_windows(popouts: &Rc; 2]>>) { for handle in popouts.borrow().iter().flatten() { handle.window.present(); } }