feat(launcher): guide staging flow and capture modes
This commit is contained in:
parent
e15afc2ebd
commit
e7dcfd2fd5
@ -21,6 +21,7 @@ pub enum DeviceTestKind {
|
|||||||
Speaker,
|
Speaker,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct DeviceTestController {
|
pub struct DeviceTestController {
|
||||||
camera: Option<LocalCameraPreview>,
|
camera: Option<LocalCameraPreview>,
|
||||||
selected_camera: Option<String>,
|
selected_camera: Option<String>,
|
||||||
@ -28,17 +29,6 @@ pub struct DeviceTestController {
|
|||||||
speaker: Option<Child>,
|
speaker: Option<Child>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DeviceTestController {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
camera: None,
|
|
||||||
selected_camera: None,
|
|
||||||
microphone: None,
|
|
||||||
speaker: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DeviceTestController {
|
impl DeviceTestController {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
@ -227,7 +217,9 @@ impl LocalCameraPreview {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.set_status(match self.selected_device.as_deref() {
|
self.set_status(match self.selected_device.as_deref() {
|
||||||
Some(camera) => format!("Selected {camera}. Click Start Preview to verify it here."),
|
Some(camera) => format!(
|
||||||
|
"Selected {camera}. Start Preview to confirm framing here before you launch the relay."
|
||||||
|
),
|
||||||
None => CAMERA_PREVIEW_IDLE.to_string(),
|
None => CAMERA_PREVIEW_IDLE.to_string(),
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -255,7 +247,7 @@ impl LocalCameraPreview {
|
|||||||
let running = Arc::clone(&self.running);
|
let running = Arc::clone(&self.running);
|
||||||
let token = generation.fetch_add(1, Ordering::AcqRel) + 1;
|
let token = generation.fetch_add(1, Ordering::AcqRel) + 1;
|
||||||
running.store(true, Ordering::Release);
|
running.store(true, Ordering::Release);
|
||||||
self.set_status(format!("Starting preview for {selected}..."));
|
self.set_status(format!("Starting local preview for {selected}..."));
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
if let Err(err) = run_camera_preview_feed(
|
if let Err(err) = run_camera_preview_feed(
|
||||||
@ -266,12 +258,11 @@ impl LocalCameraPreview {
|
|||||||
status_text.clone(),
|
status_text.clone(),
|
||||||
generation.clone(),
|
generation.clone(),
|
||||||
running.clone(),
|
running.clone(),
|
||||||
) {
|
) && generation.load(Ordering::Acquire) == token
|
||||||
if generation.load(Ordering::Acquire) == token {
|
{
|
||||||
running.store(false, Ordering::Release);
|
running.store(false, Ordering::Release);
|
||||||
if let Ok(mut status) = status_text.lock() {
|
if let Ok(mut status) = status_text.lock() {
|
||||||
*status = format!("Camera preview failed: {err}");
|
*status = format!("Camera preview failed: {err}");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -285,7 +276,9 @@ impl LocalCameraPreview {
|
|||||||
*latest = None;
|
*latest = None;
|
||||||
}
|
}
|
||||||
self.set_status(match self.selected_device.as_deref() {
|
self.set_status(match self.selected_device.as_deref() {
|
||||||
Some(camera) => format!("Preview stopped. {camera} is still selected."),
|
Some(camera) => {
|
||||||
|
format!("Local preview stopped. {camera} stays selected for the next relay launch.")
|
||||||
|
}
|
||||||
None => CAMERA_PREVIEW_IDLE.to_string(),
|
None => CAMERA_PREVIEW_IDLE.to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -327,16 +320,15 @@ fn run_camera_preview_feed(
|
|||||||
.context("starting in-launcher camera preview pipeline")?;
|
.context("starting in-launcher camera preview pipeline")?;
|
||||||
|
|
||||||
if let Ok(mut status) = status_text.lock() {
|
if let Ok(mut status) = status_text.lock() {
|
||||||
*status = format!("Previewing {selected} locally.");
|
*status = format!("Local preview live for {selected}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
while running.load(Ordering::Acquire) && generation.load(Ordering::Acquire) == token {
|
while running.load(Ordering::Acquire) && generation.load(Ordering::Acquire) == token {
|
||||||
if let Some(sample) = appsink.try_pull_sample(gst::ClockTime::from_mseconds(250)) {
|
if let Some(sample) = appsink.try_pull_sample(gst::ClockTime::from_mseconds(250))
|
||||||
if let Some(frame) = sample_to_frame(&sample) {
|
&& let Some(frame) = sample_to_frame(&sample)
|
||||||
if let Ok(mut slot) = latest.lock() {
|
&& let Ok(mut slot) = latest.lock()
|
||||||
*slot = Some(frame);
|
{
|
||||||
}
|
*slot = Some(frame);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,9 +355,9 @@ fn build_camera_preview_pipeline(device: &str) -> Result<(gst::Pipeline, gst_app
|
|||||||
.expect("camera preview appsink");
|
.expect("camera preview appsink");
|
||||||
appsink.set_caps(Some(
|
appsink.set_caps(Some(
|
||||||
&gst::Caps::builder("video/x-raw")
|
&gst::Caps::builder("video/x-raw")
|
||||||
.field("format", &"RGBA")
|
.field("format", "RGBA")
|
||||||
.field("width", &CAMERA_PREVIEW_WIDTH)
|
.field("width", CAMERA_PREVIEW_WIDTH)
|
||||||
.field("height", &CAMERA_PREVIEW_HEIGHT)
|
.field("height", CAMERA_PREVIEW_HEIGHT)
|
||||||
.build(),
|
.build(),
|
||||||
));
|
));
|
||||||
Ok((pipeline, appsink))
|
Ok((pipeline, appsink))
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use anyhow::Result;
|
|||||||
#[cfg(not(coverage))]
|
#[cfg(not(coverage))]
|
||||||
use {
|
use {
|
||||||
super::clipboard::send_clipboard_to_remote,
|
super::clipboard::send_clipboard_to_remote,
|
||||||
super::device_test::DeviceTestController,
|
super::device_test::{DeviceTestController, DeviceTestKind},
|
||||||
super::devices::DeviceCatalog,
|
super::devices::DeviceCatalog,
|
||||||
super::diagnostics::quality_probe_command,
|
super::diagnostics::quality_probe_command,
|
||||||
super::launcher_focus_signal_path,
|
super::launcher_focus_signal_path,
|
||||||
@ -121,11 +121,18 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
|
|||||||
let camera_combo_read = camera_combo.clone();
|
let camera_combo_read = camera_combo.clone();
|
||||||
camera_combo.connect_changed(move |_| {
|
camera_combo.connect_changed(move |_| {
|
||||||
let selected = selected_combo_value(&camera_combo_read);
|
let selected = selected_combo_value(&camera_combo_read);
|
||||||
|
let preview_was_running =
|
||||||
|
tests.borrow_mut().is_running(DeviceTestKind::Camera);
|
||||||
state.borrow_mut().select_camera(selected.clone());
|
state.borrow_mut().select_camera(selected.clone());
|
||||||
if let Err(err) = tests.borrow_mut().set_camera_selection(selected.as_deref()) {
|
if let Err(err) = tests.borrow_mut().set_camera_selection(selected.as_deref()) {
|
||||||
widgets
|
widgets
|
||||||
.status_label
|
.status_label
|
||||||
.set_text(&format!("Camera preview update failed: {err}"));
|
.set_text(&format!("Camera preview update failed: {err}"));
|
||||||
|
} else if preview_was_running {
|
||||||
|
widgets.status_label.set_text(&format!(
|
||||||
|
"Local camera preview switched to {}.",
|
||||||
|
selected.as_deref().unwrap_or("auto")
|
||||||
|
));
|
||||||
}
|
}
|
||||||
refresh_launcher_ui(&widgets, &state.borrow(), child_proc.borrow().is_some());
|
refresh_launcher_ui(&widgets, &state.borrow(), child_proc.borrow().is_some());
|
||||||
refresh_test_buttons(&widgets, &mut tests.borrow_mut());
|
refresh_test_buttons(&widgets, &mut tests.borrow_mut());
|
||||||
@ -136,13 +143,20 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
|
|||||||
let state = Rc::clone(&state);
|
let state = Rc::clone(&state);
|
||||||
let widgets = widgets.clone();
|
let widgets = widgets.clone();
|
||||||
let child_proc = Rc::clone(&child_proc);
|
let child_proc = Rc::clone(&child_proc);
|
||||||
|
let tests = Rc::clone(&tests);
|
||||||
let microphone_combo = microphone_combo.clone();
|
let microphone_combo = microphone_combo.clone();
|
||||||
let microphone_combo_read = microphone_combo.clone();
|
let microphone_combo_read = microphone_combo.clone();
|
||||||
microphone_combo.connect_changed(move |_| {
|
microphone_combo.connect_changed(move |_| {
|
||||||
state
|
state
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.select_microphone(selected_combo_value(µphone_combo_read));
|
.select_microphone(selected_combo_value(µphone_combo_read));
|
||||||
|
if tests.borrow_mut().is_running(DeviceTestKind::Microphone) {
|
||||||
|
widgets.status_label.set_text(
|
||||||
|
"Microphone selection changed. Restart Monitor Mic to audition the new input.",
|
||||||
|
);
|
||||||
|
}
|
||||||
refresh_launcher_ui(&widgets, &state.borrow(), child_proc.borrow().is_some());
|
refresh_launcher_ui(&widgets, &state.borrow(), child_proc.borrow().is_some());
|
||||||
|
refresh_test_buttons(&widgets, &mut tests.borrow_mut());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,13 +164,23 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
|
|||||||
let state = Rc::clone(&state);
|
let state = Rc::clone(&state);
|
||||||
let widgets = widgets.clone();
|
let widgets = widgets.clone();
|
||||||
let child_proc = Rc::clone(&child_proc);
|
let child_proc = Rc::clone(&child_proc);
|
||||||
|
let tests = Rc::clone(&tests);
|
||||||
let speaker_combo = speaker_combo.clone();
|
let speaker_combo = speaker_combo.clone();
|
||||||
let speaker_combo_read = speaker_combo.clone();
|
let speaker_combo_read = speaker_combo.clone();
|
||||||
speaker_combo.connect_changed(move |_| {
|
speaker_combo.connect_changed(move |_| {
|
||||||
state
|
state
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.select_speaker(selected_combo_value(&speaker_combo_read));
|
.select_speaker(selected_combo_value(&speaker_combo_read));
|
||||||
|
let speaker_running = tests.borrow_mut().is_running(DeviceTestKind::Speaker);
|
||||||
|
let microphone_running =
|
||||||
|
tests.borrow_mut().is_running(DeviceTestKind::Microphone);
|
||||||
|
if speaker_running || microphone_running {
|
||||||
|
widgets.status_label.set_text(
|
||||||
|
"Speaker selection changed. Restart the local audio tests to hear the new output.",
|
||||||
|
);
|
||||||
|
}
|
||||||
refresh_launcher_ui(&widgets, &state.borrow(), child_proc.borrow().is_some());
|
refresh_launcher_ui(&widgets, &state.borrow(), child_proc.borrow().is_some());
|
||||||
|
refresh_test_buttons(&widgets, &mut tests.borrow_mut());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,10 +227,23 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
|
|||||||
Ok(child) => {
|
Ok(child) => {
|
||||||
*child_proc.borrow_mut() = Some(child);
|
*child_proc.borrow_mut() = Some(child);
|
||||||
let _ = state.borrow_mut().start_remote();
|
let _ = state.borrow_mut().start_remote();
|
||||||
widgets_handle.status_label.set_text(&format!(
|
let routing = routing_name(state.borrow().routing);
|
||||||
"Relay started. Inputs are routed to {}.",
|
let power_mode = state.borrow().capture_power.mode.clone();
|
||||||
routing_name(state.borrow().routing)
|
let message = match power_mode.as_str() {
|
||||||
));
|
"forced-off" => format!(
|
||||||
|
"Relay started with inputs routed to {}, but capture is forced off. Return capture to Auto or Force On when you want remote video.",
|
||||||
|
routing
|
||||||
|
),
|
||||||
|
"forced-on" => format!(
|
||||||
|
"Relay started with inputs routed to {}. Capture is being held awake for staging.",
|
||||||
|
routing
|
||||||
|
),
|
||||||
|
_ => format!(
|
||||||
|
"Relay started with inputs routed to {}. Capture will wake automatically for previews and live session demand.",
|
||||||
|
routing
|
||||||
|
),
|
||||||
|
};
|
||||||
|
widgets_handle.status_label.set_text(&message);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
widgets_handle
|
widgets_handle
|
||||||
@ -231,7 +268,9 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
|
|||||||
stop_button.connect_clicked(move |_| {
|
stop_button.connect_clicked(move |_| {
|
||||||
stop_child_process(&child_proc);
|
stop_child_process(&child_proc);
|
||||||
let _ = state.borrow_mut().stop_remote();
|
let _ = state.borrow_mut().stop_remote();
|
||||||
widgets_handle.status_label.set_text("Relay stopped.");
|
widgets_handle
|
||||||
|
.status_label
|
||||||
|
.set_text("Relay stopped. Local staging remains available.");
|
||||||
refresh_launcher_ui(&widgets_handle, &state.borrow(), false);
|
refresh_launcher_ui(&widgets_handle, &state.borrow(), false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -351,8 +390,8 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
|
|||||||
&widgets_handle,
|
&widgets_handle,
|
||||||
&mut tests.borrow_mut(),
|
&mut tests.borrow_mut(),
|
||||||
result,
|
result,
|
||||||
"Camera preview started inside the launcher.",
|
"Camera preview started inside the launcher. This stays local until you start the relay.",
|
||||||
"Camera preview stopped.",
|
"Camera preview stopped. The selected camera stays staged for the next launch.",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -374,7 +413,7 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
|
|||||||
&widgets_handle,
|
&widgets_handle,
|
||||||
&mut tests.borrow_mut(),
|
&mut tests.borrow_mut(),
|
||||||
result,
|
result,
|
||||||
"Microphone monitor started.",
|
"Microphone monitor started locally through the selected speaker.",
|
||||||
"Microphone monitor stopped.",
|
"Microphone monitor stopped.",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -394,7 +433,7 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
|
|||||||
&widgets_handle,
|
&widgets_handle,
|
||||||
&mut tests.borrow_mut(),
|
&mut tests.borrow_mut(),
|
||||||
result,
|
result,
|
||||||
"Speaker test tone started.",
|
"Speaker tone started locally.",
|
||||||
"Speaker test tone stopped.",
|
"Speaker test tone stopped.",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -547,7 +586,9 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
|
|||||||
let child_running = reap_exited_child(&child_proc);
|
let child_running = reap_exited_child(&child_proc);
|
||||||
if !child_running && state.borrow().remote_active {
|
if !child_running && state.borrow().remote_active {
|
||||||
let _ = state.borrow_mut().stop_remote();
|
let _ = state.borrow_mut().stop_remote();
|
||||||
widgets.status_label.set_text("Relay ended.");
|
widgets
|
||||||
|
.status_label
|
||||||
|
.set_text("Relay ended. Local staging remains available.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let next_state_marker = path_marker(input_state_path.as_path());
|
let next_state_marker = path_marker(input_state_path.as_path());
|
||||||
@ -593,9 +634,9 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
|
|||||||
let mode = power.mode.clone();
|
let mode = power.mode.clone();
|
||||||
state.borrow_mut().set_capture_power(power);
|
state.borrow_mut().set_capture_power(power);
|
||||||
widgets.status_label.set_text(match mode.as_str() {
|
widgets.status_label.set_text(match mode.as_str() {
|
||||||
"forced-on" => "Capture feeds forced on.",
|
"forced-on" => "Capture feeds forced on. Remote eyes stay awake even if previews or the relay stop.",
|
||||||
"forced-off" => "Capture feeds forced off.",
|
"forced-off" => "Capture feeds forced off. Remote eye previews and session video stay dark until you switch back.",
|
||||||
_ => "Capture feeds returned to automatic mode.",
|
_ => "Capture feeds returned to automatic mode. Live previews and relay demand will wake them as needed.",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
PowerMessage::Command(Err(err)) => {
|
PowerMessage::Command(Err(err)) => {
|
||||||
|
|||||||
@ -39,6 +39,10 @@ pub struct LauncherWidgets {
|
|||||||
pub status_label: gtk::Label,
|
pub status_label: gtk::Label,
|
||||||
pub summary: SummaryWidgets,
|
pub summary: SummaryWidgets,
|
||||||
pub power_detail: gtk::Label,
|
pub power_detail: gtk::Label,
|
||||||
|
pub launch_plan_title: gtk::Label,
|
||||||
|
pub launch_plan_summary: gtk::Label,
|
||||||
|
pub launch_plan_detail: gtk::Label,
|
||||||
|
pub local_test_detail: gtk::Label,
|
||||||
pub display_panes: [DisplayPaneWidgets; 2],
|
pub display_panes: [DisplayPaneWidgets; 2],
|
||||||
pub start_button: gtk::Button,
|
pub start_button: gtk::Button,
|
||||||
pub stop_button: gtk::Button,
|
pub stop_button: gtk::Button,
|
||||||
@ -151,8 +155,14 @@ pub fn build_launcher_view(
|
|||||||
));
|
));
|
||||||
let start_button = gtk::Button::with_label("Start Relay");
|
let start_button = gtk::Button::with_label("Start Relay");
|
||||||
start_button.add_css_class("suggested-action");
|
start_button.add_css_class("suggested-action");
|
||||||
|
start_button.set_tooltip_text(Some(
|
||||||
|
"Launch the relay using the staged devices and current input routing.",
|
||||||
|
));
|
||||||
let stop_button = gtk::Button::with_label("Stop Relay");
|
let stop_button = gtk::Button::with_label("Stop Relay");
|
||||||
stop_button.add_css_class("destructive-action");
|
stop_button.add_css_class("destructive-action");
|
||||||
|
stop_button.set_tooltip_text(Some(
|
||||||
|
"Stop the live relay session. Local staging and previews stay available.",
|
||||||
|
));
|
||||||
server_row.append(&server_entry);
|
server_row.append(&server_entry);
|
||||||
server_row.append(&start_button);
|
server_row.append(&start_button);
|
||||||
server_row.append(&stop_button);
|
server_row.append(&stop_button);
|
||||||
@ -225,7 +235,7 @@ pub fn build_launcher_view(
|
|||||||
|
|
||||||
let (devices_panel, devices_body) = build_panel("Device Staging");
|
let (devices_panel, devices_body) = build_panel("Device Staging");
|
||||||
let devices_intro = gtk::Label::new(Some(
|
let devices_intro = gtk::Label::new(Some(
|
||||||
"Lock in the exact local camera, microphone, and speaker you want before you launch the relay.",
|
"Choose the exact local camera, microphone, and speaker the next relay launch should inherit.",
|
||||||
));
|
));
|
||||||
devices_intro.add_css_class("dim-label");
|
devices_intro.add_css_class("dim-label");
|
||||||
devices_intro.set_wrap(true);
|
devices_intro.set_wrap(true);
|
||||||
@ -320,6 +330,37 @@ pub fn build_launcher_view(
|
|||||||
devices_body.append(&preview_shell);
|
devices_body.append(&preview_shell);
|
||||||
sidebar.append(&devices_panel);
|
sidebar.append(&devices_panel);
|
||||||
|
|
||||||
|
let (plan_panel, plan_body) = build_panel("Launch Plan");
|
||||||
|
let launch_plan_title = gtk::Label::new(Some("Stage locally, then start the relay."));
|
||||||
|
launch_plan_title.add_css_class("title-4");
|
||||||
|
launch_plan_title.set_halign(gtk::Align::Start);
|
||||||
|
launch_plan_title.set_wrap(true);
|
||||||
|
let launch_plan_summary =
|
||||||
|
gtk::Label::new(Some("Camera: auto\nMicrophone: auto\nSpeaker: auto"));
|
||||||
|
launch_plan_summary.add_css_class("launch-plan-summary");
|
||||||
|
launch_plan_summary.set_halign(gtk::Align::Start);
|
||||||
|
launch_plan_summary.set_xalign(0.0);
|
||||||
|
launch_plan_summary.set_wrap(true);
|
||||||
|
let local_test_detail = gtk::Label::new(Some(
|
||||||
|
"Local checks are idle. Use Start Preview, Monitor Mic, or Play Tone before you launch.",
|
||||||
|
));
|
||||||
|
local_test_detail.add_css_class("dim-label");
|
||||||
|
local_test_detail.set_halign(gtk::Align::Start);
|
||||||
|
local_test_detail.set_xalign(0.0);
|
||||||
|
local_test_detail.set_wrap(true);
|
||||||
|
let launch_plan_detail = gtk::Label::new(Some(
|
||||||
|
"Automatic capture mode will wake the remote feeds when previews or the live relay ask for them.",
|
||||||
|
));
|
||||||
|
launch_plan_detail.add_css_class("dim-label");
|
||||||
|
launch_plan_detail.set_halign(gtk::Align::Start);
|
||||||
|
launch_plan_detail.set_xalign(0.0);
|
||||||
|
launch_plan_detail.set_wrap(true);
|
||||||
|
plan_body.append(&launch_plan_title);
|
||||||
|
plan_body.append(&launch_plan_summary);
|
||||||
|
plan_body.append(&local_test_detail);
|
||||||
|
plan_body.append(&launch_plan_detail);
|
||||||
|
sidebar.append(&plan_panel);
|
||||||
|
|
||||||
let (actions_panel, actions_body) = build_panel("Remote Actions");
|
let (actions_panel, actions_body) = build_panel("Remote Actions");
|
||||||
let actions_row = gtk::Box::new(gtk::Orientation::Horizontal, 8);
|
let actions_row = gtk::Box::new(gtk::Orientation::Horizontal, 8);
|
||||||
let clipboard_button = gtk::Button::with_label("Send Clipboard");
|
let clipboard_button = gtk::Button::with_label("Send Clipboard");
|
||||||
@ -336,11 +377,18 @@ pub fn build_launcher_view(
|
|||||||
sidebar.append(&actions_panel);
|
sidebar.append(&actions_panel);
|
||||||
|
|
||||||
let stage_header = gtk::Box::new(gtk::Orientation::Horizontal, 8);
|
let stage_header = gtk::Box::new(gtk::Orientation::Horizontal, 8);
|
||||||
let stage_title = gtk::Label::new(Some("Remote Eyes"));
|
let stage_title = gtk::Label::new(Some("Remote Eye Feeds"));
|
||||||
stage_title.add_css_class("title-4");
|
stage_title.add_css_class("title-4");
|
||||||
stage_title.set_halign(gtk::Align::Start);
|
stage_title.set_halign(gtk::Align::Start);
|
||||||
stage_header.append(&stage_title);
|
stage_header.append(&stage_title);
|
||||||
|
let stage_note = gtk::Label::new(Some(
|
||||||
|
"These are the live server-side eye feeds. In Auto mode, open eye previews and active relay sessions count as capture demand.",
|
||||||
|
));
|
||||||
|
stage_note.add_css_class("dim-label");
|
||||||
|
stage_note.set_wrap(true);
|
||||||
|
stage_note.set_xalign(0.0);
|
||||||
stage.append(&stage_header);
|
stage.append(&stage_header);
|
||||||
|
stage.append(&stage_note);
|
||||||
|
|
||||||
let display_row = gtk::Box::new(gtk::Orientation::Horizontal, 16);
|
let display_row = gtk::Box::new(gtk::Orientation::Horizontal, 16);
|
||||||
display_row.set_hexpand(true);
|
display_row.set_hexpand(true);
|
||||||
@ -387,6 +435,10 @@ pub fn build_launcher_view(
|
|||||||
shortcut_value,
|
shortcut_value,
|
||||||
},
|
},
|
||||||
power_detail,
|
power_detail,
|
||||||
|
launch_plan_title,
|
||||||
|
launch_plan_summary,
|
||||||
|
launch_plan_detail,
|
||||||
|
local_test_detail,
|
||||||
display_panes: [left_pane.clone(), right_pane.clone()],
|
display_panes: [left_pane.clone(), right_pane.clone()],
|
||||||
start_button: start_button.clone(),
|
start_button: start_button.clone(),
|
||||||
stop_button: stop_button.clone(),
|
stop_button: stop_button.clone(),
|
||||||
@ -482,6 +534,13 @@ pub fn install_css(window: >k::ApplicationWindow) {
|
|||||||
label.status-line {
|
label.status-line {
|
||||||
opacity: 0.88;
|
opacity: 0.88;
|
||||||
}
|
}
|
||||||
|
label.launch-plan-summary {
|
||||||
|
font-family: monospace;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
entry.server-entry {
|
entry.server-entry {
|
||||||
min-height: 38px;
|
min-height: 38px;
|
||||||
}
|
}
|
||||||
@ -489,6 +548,11 @@ pub fn install_css(window: >k::ApplicationWindow) {
|
|||||||
min-height: 36px;
|
min-height: 36px;
|
||||||
padding: 0 14px;
|
padding: 0 14px;
|
||||||
}
|
}
|
||||||
|
button.pill-toggle-active {
|
||||||
|
background: rgba(91, 179, 162, 0.2);
|
||||||
|
border-color: rgba(91, 179, 162, 0.45);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
if let Some(display) = gtk::gdk::Display::default() {
|
if let Some(display) = gtk::gdk::Display::default() {
|
||||||
|
|||||||
@ -52,7 +52,21 @@ pub fn refresh_launcher_ui(widgets: &LauncherWidgets, state: &LauncherState, chi
|
|||||||
widgets
|
widgets
|
||||||
.power_detail
|
.power_detail
|
||||||
.set_text(&capture_power_detail(&state.capture_power));
|
.set_text(&capture_power_detail(&state.capture_power));
|
||||||
|
widgets
|
||||||
|
.launch_plan_title
|
||||||
|
.set_text(&launch_plan_title(state, child_running));
|
||||||
|
widgets
|
||||||
|
.launch_plan_summary
|
||||||
|
.set_text(&launch_plan_summary(state));
|
||||||
|
widgets
|
||||||
|
.launch_plan_detail
|
||||||
|
.set_text(&launch_plan_detail(state, child_running));
|
||||||
|
|
||||||
|
widgets.start_button.set_label(if child_running {
|
||||||
|
"Relay Running"
|
||||||
|
} else {
|
||||||
|
"Start Relay"
|
||||||
|
});
|
||||||
widgets.start_button.set_sensitive(!child_running);
|
widgets.start_button.set_sensitive(!child_running);
|
||||||
widgets.stop_button.set_sensitive(child_running);
|
widgets.stop_button.set_sensitive(child_running);
|
||||||
widgets.clipboard_button.set_sensitive(child_running);
|
widgets.clipboard_button.set_sensitive(child_running);
|
||||||
@ -71,6 +85,7 @@ pub fn refresh_launcher_ui(widgets: &LauncherWidgets, state: &LauncherState, chi
|
|||||||
widgets.power_off_button.set_sensitive(
|
widgets.power_off_button.set_sensitive(
|
||||||
power_available && !matches!(state.capture_power.mode.as_str(), "forced-off"),
|
power_available && !matches!(state.capture_power.mode.as_str(), "forced-off"),
|
||||||
);
|
);
|
||||||
|
sync_power_mode_button_styles(widgets, state.capture_power.mode.as_str());
|
||||||
|
|
||||||
for monitor_id in 0..2 {
|
for monitor_id in 0..2 {
|
||||||
refresh_display_pane(
|
refresh_display_pane(
|
||||||
@ -81,27 +96,32 @@ pub fn refresh_launcher_ui(widgets: &LauncherWidgets, state: &LauncherState, chi
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh_test_buttons(widgets: &LauncherWidgets, tests: &mut DeviceTestController) {
|
pub fn refresh_test_buttons(widgets: &LauncherWidgets, tests: &mut DeviceTestController) {
|
||||||
widgets
|
let camera_running = tests.is_running(DeviceTestKind::Camera);
|
||||||
.camera_test_button
|
let microphone_running = tests.is_running(DeviceTestKind::Microphone);
|
||||||
.set_label(if tests.is_running(DeviceTestKind::Camera) {
|
let speaker_running = tests.is_running(DeviceTestKind::Speaker);
|
||||||
"Stop Preview"
|
|
||||||
} else {
|
widgets.camera_test_button.set_label(if camera_running {
|
||||||
"Start Preview"
|
"Stop Preview"
|
||||||
});
|
} else {
|
||||||
|
"Start Preview"
|
||||||
|
});
|
||||||
widgets
|
widgets
|
||||||
.microphone_test_button
|
.microphone_test_button
|
||||||
.set_label(if tests.is_running(DeviceTestKind::Microphone) {
|
.set_label(if microphone_running {
|
||||||
"Stop Monitor"
|
"Stop Monitor"
|
||||||
} else {
|
} else {
|
||||||
"Monitor Mic"
|
"Monitor Mic"
|
||||||
});
|
});
|
||||||
widgets
|
widgets.speaker_test_button.set_label(if speaker_running {
|
||||||
.speaker_test_button
|
"Stop Tone"
|
||||||
.set_label(if tests.is_running(DeviceTestKind::Speaker) {
|
} else {
|
||||||
"Stop Tone"
|
"Play Tone"
|
||||||
} else {
|
});
|
||||||
"Play Tone"
|
widgets.local_test_detail.set_text(&local_test_detail(
|
||||||
});
|
camera_running,
|
||||||
|
microphone_running,
|
||||||
|
speaker_running,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_test_action_result(
|
pub fn update_test_action_result(
|
||||||
@ -140,7 +160,7 @@ pub fn open_popout_window(
|
|||||||
|
|
||||||
let window = gtk::ApplicationWindow::builder()
|
let window = gtk::ApplicationWindow::builder()
|
||||||
.application(app)
|
.application(app)
|
||||||
.title(&format!(
|
.title(format!(
|
||||||
"Lesavka {}",
|
"Lesavka {}",
|
||||||
widgets.display_panes[monitor_id].title
|
widgets.display_panes[monitor_id].title
|
||||||
))
|
))
|
||||||
@ -310,6 +330,137 @@ pub fn capture_power_detail(power: &CapturePowerStatus) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Summarizes the current staging state as a short operator-facing heading.
|
||||||
|
fn launch_plan_title(state: &LauncherState, child_running: bool) -> String {
|
||||||
|
if child_running || state.remote_active {
|
||||||
|
return match state.capture_power.mode.as_str() {
|
||||||
|
"forced-off" => "Relay live, but capture is intentionally dark.".to_string(),
|
||||||
|
"forced-on" => "Relay live with capture held awake.".to_string(),
|
||||||
|
_ => "Relay live with automatic capture management.".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
match state.capture_power.mode.as_str() {
|
||||||
|
"forced-off" => "Staging mode is holding capture off.".to_string(),
|
||||||
|
"forced-on" => "Capture is pre-warmed for staging.".to_string(),
|
||||||
|
_ => "Stage locally, then start the relay.".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shows the exact devices the next relay launch will inherit.
|
||||||
|
fn launch_plan_summary(state: &LauncherState) -> String {
|
||||||
|
format!(
|
||||||
|
"Camera: {}\nMicrophone: {}\nSpeaker: {}",
|
||||||
|
selected_device_label(state.devices.camera.as_deref()),
|
||||||
|
selected_device_label(state.devices.microphone.as_deref()),
|
||||||
|
selected_device_label(state.devices.speaker.as_deref())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Explains the consequence of the current capture and session state.
|
||||||
|
fn launch_plan_detail(state: &LauncherState, child_running: bool) -> String {
|
||||||
|
if child_running || state.remote_active {
|
||||||
|
return match state.capture_power.mode.as_str() {
|
||||||
|
"forced-off" => format!(
|
||||||
|
"Inputs are routed to {}. Return capture to Auto or Force On when you want the remote eyes and session video to wake up.",
|
||||||
|
capitalize(routing_name(state.routing))
|
||||||
|
),
|
||||||
|
"forced-on" => format!(
|
||||||
|
"Inputs are routed to {}. The relay host is keeping the capture feeds up even without preview demand.",
|
||||||
|
capitalize(routing_name(state.routing))
|
||||||
|
),
|
||||||
|
_ => format!(
|
||||||
|
"Inputs are routed to {}. Live eye previews and session demand will wake capture automatically as needed.",
|
||||||
|
capitalize(routing_name(state.routing))
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if !state.capture_power.available {
|
||||||
|
return format!(
|
||||||
|
"Capture power status from {} is unavailable right now. You can still stage devices locally before you try the relay host again.",
|
||||||
|
state.capture_power.unit
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
match state.capture_power.mode.as_str() {
|
||||||
|
"forced-off" => {
|
||||||
|
"Remote eye previews and the next relay session will stay dark until you return to Auto or Force On."
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
"forced-on" => {
|
||||||
|
"The relay host is already holding capture awake, which is useful for preflight framing checks before the session starts."
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
"Automatic capture mode wakes the remote feeds only while eye previews or the live relay session actually need them."
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reports which local staging checks are active right now.
|
||||||
|
fn local_test_detail(
|
||||||
|
camera_running: bool,
|
||||||
|
microphone_running: bool,
|
||||||
|
speaker_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 active.is_empty() {
|
||||||
|
"Local checks are idle. Use Start Preview, Monitor Mic, or Play Tone before you launch."
|
||||||
|
.to_string()
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"Local checks running: {}. Stop them whenever staging is complete.",
|
||||||
|
active.join(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Formats a selected device for the launch-plan summary.
|
||||||
|
fn selected_device_label(value: Option<&str>) -> String {
|
||||||
|
value
|
||||||
|
.map(compact_device_name)
|
||||||
|
.unwrap_or_else(|| "auto".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prefer the basename for `/dev/...` entries while keeping Pulse names intact.
|
||||||
|
fn compact_device_name(value: &str) -> String {
|
||||||
|
let trimmed = value.trim();
|
||||||
|
if trimmed.is_empty() {
|
||||||
|
return "auto".to_string();
|
||||||
|
}
|
||||||
|
trimmed.rsplit('/').next().unwrap_or(trimmed).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn capitalize(value: &str) -> String {
|
pub fn capitalize(value: &str) -> String {
|
||||||
let mut chars = value.chars();
|
let mut chars = value.chars();
|
||||||
match chars.next() {
|
match chars.next() {
|
||||||
@ -455,3 +606,55 @@ pub fn next_input_routing(routing: InputRouting) -> InputRouting {
|
|||||||
InputRouting::Local => InputRouting::Remote,
|
InputRouting::Local => InputRouting::Remote,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn launch_plan_summary_compacts_selected_devices() {
|
||||||
|
let mut state = LauncherState::new();
|
||||||
|
state.select_camera(Some(
|
||||||
|
"/dev/v4l/by-id/usb-Logitech_C920-video-index0".to_string(),
|
||||||
|
));
|
||||||
|
state.select_microphone(Some("alsa_input.usb-focusrite".to_string()));
|
||||||
|
state.select_speaker(Some("alsa_output.studio".to_string()));
|
||||||
|
|
||||||
|
let summary = launch_plan_summary(&state);
|
||||||
|
assert!(summary.contains("usb-Logitech_C920-video-index0"));
|
||||||
|
assert!(summary.contains("alsa_input.usb-focusrite"));
|
||||||
|
assert!(summary.contains("alsa_output.studio"));
|
||||||
|
assert!(!summary.contains("/dev/v4l/by-id/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn launch_plan_detail_calls_out_forced_off_sessions() {
|
||||||
|
let mut state = LauncherState::new();
|
||||||
|
state.set_capture_power(CapturePowerStatus {
|
||||||
|
available: true,
|
||||||
|
enabled: false,
|
||||||
|
unit: "relay.service".to_string(),
|
||||||
|
detail: "inactive/dead".to_string(),
|
||||||
|
active_leases: 0,
|
||||||
|
mode: "forced-off".to_string(),
|
||||||
|
});
|
||||||
|
state.start_remote();
|
||||||
|
|
||||||
|
let detail = launch_plan_detail(&state, true);
|
||||||
|
assert!(detail.contains("Return capture to Auto or Force On"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn local_test_detail_mentions_idle_and_running_modes() {
|
||||||
|
assert!(local_test_detail(false, false, false).contains("idle"));
|
||||||
|
let running = local_test_detail(true, true, false);
|
||||||
|
assert!(running.contains("camera preview"));
|
||||||
|
assert!(running.contains("mic monitor"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compact_device_name_prefers_basename_when_available() {
|
||||||
|
assert_eq!(compact_device_name("/dev/video0"), "video0");
|
||||||
|
assert_eq!(compact_device_name("alsa_input.usb"), "alsa_input.usb");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,273 +1,272 @@
|
|||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"client/src/app.rs": {
|
"client/src/app.rs": {
|
||||||
"loc": 546,
|
|
||||||
"clippy_warnings": 42,
|
"clippy_warnings": 42,
|
||||||
"doc_debt": 10
|
"doc_debt": 10,
|
||||||
|
"loc": 546
|
||||||
},
|
},
|
||||||
"client/src/app_support.rs": {
|
"client/src/app_support.rs": {
|
||||||
"loc": 128,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 3,
|
"doc_debt": 3,
|
||||||
"clippy_warnings": 0
|
"loc": 128
|
||||||
},
|
},
|
||||||
"client/src/handshake.rs": {
|
"client/src/handshake.rs": {
|
||||||
"loc": 194,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 3,
|
"doc_debt": 3,
|
||||||
"clippy_warnings": 0
|
"loc": 194
|
||||||
},
|
},
|
||||||
"client/src/input/camera.rs": {
|
"client/src/input/camera.rs": {
|
||||||
"loc": 372,
|
|
||||||
"clippy_warnings": 38,
|
"clippy_warnings": 38,
|
||||||
"doc_debt": 7
|
"doc_debt": 7,
|
||||||
|
"loc": 372
|
||||||
},
|
},
|
||||||
"client/src/input/inputs.rs": {
|
"client/src/input/inputs.rs": {
|
||||||
"loc": 673,
|
|
||||||
"clippy_warnings": 42,
|
"clippy_warnings": 42,
|
||||||
"doc_debt": 16
|
"doc_debt": 16,
|
||||||
|
"loc": 673
|
||||||
},
|
},
|
||||||
"client/src/input/keyboard.rs": {
|
"client/src/input/keyboard.rs": {
|
||||||
"loc": 580,
|
|
||||||
"clippy_warnings": 24,
|
"clippy_warnings": 24,
|
||||||
"doc_debt": 18
|
"doc_debt": 18,
|
||||||
|
"loc": 580
|
||||||
},
|
},
|
||||||
"client/src/input/keymap.rs": {
|
"client/src/input/keymap.rs": {
|
||||||
"loc": 196,
|
|
||||||
"clippy_warnings": 8,
|
"clippy_warnings": 8,
|
||||||
"doc_debt": 0
|
"doc_debt": 0,
|
||||||
|
"loc": 196
|
||||||
},
|
},
|
||||||
"client/src/input/microphone.rs": {
|
"client/src/input/microphone.rs": {
|
||||||
"loc": 166,
|
|
||||||
"clippy_warnings": 17,
|
"clippy_warnings": 17,
|
||||||
"doc_debt": 2
|
"doc_debt": 2,
|
||||||
|
"loc": 166
|
||||||
},
|
},
|
||||||
"client/src/input/mod.rs": {
|
"client/src/input/mod.rs": {
|
||||||
"loc": 8,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 0,
|
"doc_debt": 0,
|
||||||
"clippy_warnings": 0
|
"loc": 8
|
||||||
},
|
},
|
||||||
"client/src/input/mouse.rs": {
|
"client/src/input/mouse.rs": {
|
||||||
"loc": 317,
|
|
||||||
"clippy_warnings": 40,
|
"clippy_warnings": 40,
|
||||||
"doc_debt": 8
|
"doc_debt": 8,
|
||||||
|
"loc": 317
|
||||||
},
|
},
|
||||||
"client/src/launcher/clipboard.rs": {
|
"client/src/launcher/clipboard.rs": {
|
||||||
"loc": 177,
|
|
||||||
"clippy_warnings": 2,
|
"clippy_warnings": 2,
|
||||||
"doc_debt": 1
|
"doc_debt": 1,
|
||||||
|
"loc": 177
|
||||||
},
|
},
|
||||||
"client/src/launcher/device_test.rs": {
|
"client/src/launcher/device_test.rs": {
|
||||||
"loc": 454,
|
"clippy_warnings": 22,
|
||||||
"clippy_warnings": 36,
|
"doc_debt": 20,
|
||||||
"doc_debt": 20
|
"loc": 446
|
||||||
},
|
},
|
||||||
"client/src/launcher/devices.rs": {
|
"client/src/launcher/devices.rs": {
|
||||||
"loc": 158,
|
|
||||||
"clippy_warnings": 6,
|
"clippy_warnings": 6,
|
||||||
"doc_debt": 3
|
"doc_debt": 3,
|
||||||
|
"loc": 158
|
||||||
},
|
},
|
||||||
"client/src/launcher/diagnostics.rs": {
|
"client/src/launcher/diagnostics.rs": {
|
||||||
"loc": 175,
|
|
||||||
"clippy_warnings": 17,
|
"clippy_warnings": 17,
|
||||||
"doc_debt": 3
|
"doc_debt": 3,
|
||||||
|
"loc": 175
|
||||||
},
|
},
|
||||||
"client/src/launcher/mod.rs": {
|
"client/src/launcher/mod.rs": {
|
||||||
"loc": 195,
|
|
||||||
"clippy_warnings": 6,
|
"clippy_warnings": 6,
|
||||||
"doc_debt": 4
|
"doc_debt": 4,
|
||||||
|
"loc": 195
|
||||||
},
|
},
|
||||||
"client/src/launcher/power.rs": {
|
"client/src/launcher/power.rs": {
|
||||||
"loc": 69,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 1,
|
"doc_debt": 1,
|
||||||
"clippy_warnings": 0
|
"loc": 69
|
||||||
},
|
},
|
||||||
"client/src/launcher/preview.rs": {
|
"client/src/launcher/preview.rs": {
|
||||||
"loc": 293,
|
|
||||||
"clippy_warnings": 20,
|
"clippy_warnings": 20,
|
||||||
"doc_debt": 6
|
"doc_debt": 6,
|
||||||
|
"loc": 293
|
||||||
},
|
},
|
||||||
"client/src/launcher/state.rs": {
|
"client/src/launcher/state.rs": {
|
||||||
"loc": 360,
|
|
||||||
"clippy_warnings": 14,
|
"clippy_warnings": 14,
|
||||||
"doc_debt": 15
|
"doc_debt": 15,
|
||||||
|
"loc": 360
|
||||||
},
|
},
|
||||||
"client/src/launcher/ui.rs": {
|
"client/src/launcher/ui.rs": {
|
||||||
"loc": 654,
|
"clippy_warnings": 10,
|
||||||
"clippy_warnings": 4,
|
"doc_debt": 1,
|
||||||
"doc_debt": 1
|
"loc": 695
|
||||||
},
|
},
|
||||||
"client/src/launcher/ui_components.rs": {
|
"client/src/launcher/ui_components.rs": {
|
||||||
"loc": 615,
|
|
||||||
"clippy_warnings": 8,
|
"clippy_warnings": 8,
|
||||||
"doc_debt": 4
|
"doc_debt": 4,
|
||||||
|
"loc": 679
|
||||||
},
|
},
|
||||||
"client/src/launcher/ui_runtime.rs": {
|
"client/src/launcher/ui_runtime.rs": {
|
||||||
"loc": 457,
|
|
||||||
"clippy_warnings": 10,
|
"clippy_warnings": 10,
|
||||||
"doc_debt": 18
|
"doc_debt": 20,
|
||||||
|
"loc": 660
|
||||||
},
|
},
|
||||||
"client/src/layout.rs": {
|
"client/src/layout.rs": {
|
||||||
"loc": 78,
|
|
||||||
"clippy_warnings": 6,
|
"clippy_warnings": 6,
|
||||||
"doc_debt": 0
|
"doc_debt": 0,
|
||||||
|
"loc": 78
|
||||||
},
|
},
|
||||||
"client/src/lib.rs": {
|
"client/src/lib.rs": {
|
||||||
"loc": 14,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 0,
|
"doc_debt": 0,
|
||||||
"clippy_warnings": 0
|
"loc": 14
|
||||||
},
|
},
|
||||||
"client/src/main.rs": {
|
"client/src/main.rs": {
|
||||||
"loc": 96,
|
|
||||||
"clippy_warnings": 2,
|
"clippy_warnings": 2,
|
||||||
"doc_debt": 2
|
"doc_debt": 2,
|
||||||
|
"loc": 96
|
||||||
},
|
},
|
||||||
"client/src/output/audio.rs": {
|
"client/src/output/audio.rs": {
|
||||||
"loc": 195,
|
|
||||||
"clippy_warnings": 37,
|
"clippy_warnings": 37,
|
||||||
"doc_debt": 5
|
"doc_debt": 5,
|
||||||
|
"loc": 195
|
||||||
},
|
},
|
||||||
"client/src/output/display.rs": {
|
"client/src/output/display.rs": {
|
||||||
"loc": 81,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 0,
|
"doc_debt": 0,
|
||||||
"clippy_warnings": 0
|
"loc": 81
|
||||||
},
|
},
|
||||||
"client/src/output/layout.rs": {
|
"client/src/output/layout.rs": {
|
||||||
"loc": 155,
|
|
||||||
"clippy_warnings": 4,
|
"clippy_warnings": 4,
|
||||||
"doc_debt": 2
|
"doc_debt": 2,
|
||||||
|
"loc": 155
|
||||||
},
|
},
|
||||||
"client/src/output/mod.rs": {
|
"client/src/output/mod.rs": {
|
||||||
"loc": 6,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 0,
|
"doc_debt": 0,
|
||||||
"clippy_warnings": 0
|
"loc": 6
|
||||||
},
|
},
|
||||||
"client/src/output/video.rs": {
|
"client/src/output/video.rs": {
|
||||||
"loc": 547,
|
|
||||||
"clippy_warnings": 36,
|
"clippy_warnings": 36,
|
||||||
"doc_debt": 4
|
"doc_debt": 4,
|
||||||
|
"loc": 547
|
||||||
},
|
},
|
||||||
"client/src/paste.rs": {
|
"client/src/paste.rs": {
|
||||||
"loc": 46,
|
|
||||||
"clippy_warnings": 2,
|
"clippy_warnings": 2,
|
||||||
"doc_debt": 1
|
"doc_debt": 1,
|
||||||
|
"loc": 46
|
||||||
},
|
},
|
||||||
"common/src/bin/cli.rs": {
|
"common/src/bin/cli.rs": {
|
||||||
"loc": 3,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 0,
|
"doc_debt": 0,
|
||||||
"clippy_warnings": 0
|
"loc": 3
|
||||||
},
|
},
|
||||||
"common/src/cli.rs": {
|
"common/src/cli.rs": {
|
||||||
"loc": 22,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 0,
|
"doc_debt": 0,
|
||||||
"clippy_warnings": 0
|
"loc": 22
|
||||||
},
|
},
|
||||||
"common/src/hid.rs": {
|
"common/src/hid.rs": {
|
||||||
"loc": 134,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 2,
|
"doc_debt": 2,
|
||||||
"clippy_warnings": 0
|
"loc": 134
|
||||||
},
|
},
|
||||||
"common/src/lib.rs": {
|
"common/src/lib.rs": {
|
||||||
"loc": 22,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 0,
|
"doc_debt": 0,
|
||||||
"clippy_warnings": 0
|
"loc": 22
|
||||||
},
|
},
|
||||||
"common/src/paste.rs": {
|
"common/src/paste.rs": {
|
||||||
"loc": 95,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 2,
|
"doc_debt": 2,
|
||||||
"clippy_warnings": 0
|
"loc": 95
|
||||||
},
|
},
|
||||||
"server/src/audio.rs": {
|
"server/src/audio.rs": {
|
||||||
"loc": 386,
|
|
||||||
"clippy_warnings": 37,
|
"clippy_warnings": 37,
|
||||||
"doc_debt": 7
|
"doc_debt": 7,
|
||||||
|
"loc": 386
|
||||||
},
|
},
|
||||||
"server/src/bin/lesavka-uvc.real.inc": {
|
"server/src/bin/lesavka-uvc.real.inc": {
|
||||||
"clippy_warnings": 31,
|
"clippy_warnings": 31
|
||||||
"doc_debt": 0
|
|
||||||
},
|
},
|
||||||
"server/src/bin/lesavka-uvc.rs": {
|
"server/src/bin/lesavka-uvc.rs": {
|
||||||
"loc": 710,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 17,
|
"doc_debt": 17,
|
||||||
"clippy_warnings": 0
|
"loc": 710
|
||||||
},
|
},
|
||||||
"server/src/camera.rs": {
|
"server/src/camera.rs": {
|
||||||
"loc": 392,
|
|
||||||
"clippy_warnings": 12,
|
"clippy_warnings": 12,
|
||||||
"doc_debt": 11
|
"doc_debt": 11,
|
||||||
|
"loc": 392
|
||||||
},
|
},
|
||||||
"server/src/camera_runtime.rs": {
|
"server/src/camera_runtime.rs": {
|
||||||
"loc": 200,
|
|
||||||
"clippy_warnings": 10,
|
"clippy_warnings": 10,
|
||||||
"doc_debt": 5
|
"doc_debt": 5,
|
||||||
|
"loc": 200
|
||||||
},
|
},
|
||||||
"server/src/capture_power.rs": {
|
"server/src/capture_power.rs": {
|
||||||
"loc": 338,
|
|
||||||
"clippy_warnings": 10,
|
"clippy_warnings": 10,
|
||||||
"doc_debt": 7
|
"doc_debt": 7,
|
||||||
|
"loc": 338
|
||||||
},
|
},
|
||||||
"server/src/gadget.rs": {
|
"server/src/gadget.rs": {
|
||||||
"loc": 327,
|
|
||||||
"clippy_warnings": 30,
|
"clippy_warnings": 30,
|
||||||
"doc_debt": 7
|
"doc_debt": 7,
|
||||||
|
"loc": 327
|
||||||
},
|
},
|
||||||
"server/src/handshake.rs": {
|
"server/src/handshake.rs": {
|
||||||
"loc": 40,
|
|
||||||
"clippy_warnings": 2,
|
"clippy_warnings": 2,
|
||||||
"doc_debt": 1
|
"doc_debt": 1,
|
||||||
|
"loc": 40
|
||||||
},
|
},
|
||||||
"server/src/lib.rs": {
|
"server/src/lib.rs": {
|
||||||
"loc": 14,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 0,
|
"doc_debt": 0,
|
||||||
"clippy_warnings": 0
|
"loc": 14
|
||||||
},
|
},
|
||||||
"server/src/main.rs": {
|
"server/src/main.rs": {
|
||||||
"loc": 572,
|
|
||||||
"clippy_warnings": 10,
|
"clippy_warnings": 10,
|
||||||
"doc_debt": 13
|
"doc_debt": 13,
|
||||||
|
"loc": 572
|
||||||
},
|
},
|
||||||
"server/src/paste.rs": {
|
"server/src/paste.rs": {
|
||||||
"loc": 207,
|
|
||||||
"clippy_warnings": 6,
|
"clippy_warnings": 6,
|
||||||
"doc_debt": 3
|
"doc_debt": 3,
|
||||||
|
"loc": 207
|
||||||
},
|
},
|
||||||
"server/src/runtime_support.rs": {
|
"server/src/runtime_support.rs": {
|
||||||
"loc": 387,
|
|
||||||
"clippy_warnings": 14,
|
"clippy_warnings": 14,
|
||||||
"doc_debt": 8
|
"doc_debt": 8,
|
||||||
|
"loc": 387
|
||||||
},
|
},
|
||||||
"server/src/uvc_control/model.rs": {
|
"server/src/uvc_control/model.rs": {
|
||||||
"loc": 510,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 11,
|
"doc_debt": 11,
|
||||||
"clippy_warnings": 0
|
"loc": 510
|
||||||
},
|
},
|
||||||
"server/src/uvc_control/protocol.rs": {
|
"server/src/uvc_control/protocol.rs": {
|
||||||
"loc": 403,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 11,
|
"doc_debt": 11,
|
||||||
"clippy_warnings": 0
|
"loc": 403
|
||||||
},
|
},
|
||||||
"server/src/uvc_runtime.rs": {
|
"server/src/uvc_runtime.rs": {
|
||||||
"loc": 241,
|
|
||||||
"clippy_warnings": 4,
|
"clippy_warnings": 4,
|
||||||
"doc_debt": 5
|
"doc_debt": 5,
|
||||||
|
"loc": 241
|
||||||
},
|
},
|
||||||
"server/src/video.rs": {
|
"server/src/video.rs": {
|
||||||
"loc": 344,
|
|
||||||
"clippy_warnings": 25,
|
"clippy_warnings": 25,
|
||||||
"doc_debt": 2
|
"doc_debt": 2,
|
||||||
|
"loc": 343
|
||||||
},
|
},
|
||||||
"server/src/video_sinks.rs": {
|
"server/src/video_sinks.rs": {
|
||||||
"loc": 559,
|
|
||||||
"clippy_warnings": 78,
|
"clippy_warnings": 78,
|
||||||
"doc_debt": 11
|
"doc_debt": 11,
|
||||||
|
"loc": 559
|
||||||
},
|
},
|
||||||
"server/src/video_support.rs": {
|
"server/src/video_support.rs": {
|
||||||
"loc": 236,
|
|
||||||
"clippy_warnings": 8,
|
"clippy_warnings": 8,
|
||||||
"doc_debt": 6
|
"doc_debt": 6,
|
||||||
|
"loc": 236
|
||||||
},
|
},
|
||||||
"testing/src/lib.rs": {
|
"testing/src/lib.rs": {
|
||||||
"loc": 10,
|
"clippy_warnings": 0,
|
||||||
"doc_debt": 0,
|
"doc_debt": 0,
|
||||||
"clippy_warnings": 0
|
"loc": 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,168 +1,168 @@
|
|||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"client/src/app.rs": {
|
"client/src/app.rs": {
|
||||||
"loc": 546,
|
"line_percent": 95.1219512195122,
|
||||||
"line_percent": 95.1219512195122
|
"loc": 546
|
||||||
},
|
},
|
||||||
"client/src/app_support.rs": {
|
"client/src/app_support.rs": {
|
||||||
"loc": 128,
|
"line_percent": 100.0,
|
||||||
"line_percent": 100.0
|
"loc": 128
|
||||||
},
|
},
|
||||||
"client/src/handshake.rs": {
|
"client/src/handshake.rs": {
|
||||||
"loc": 194,
|
"line_percent": 96.15384615384616,
|
||||||
"line_percent": 96.15384615384616
|
"loc": 194
|
||||||
},
|
},
|
||||||
"client/src/input/camera.rs": {
|
"client/src/input/camera.rs": {
|
||||||
"loc": 372,
|
"line_percent": 98.42931937172776,
|
||||||
"line_percent": 98.42931937172776
|
"loc": 372
|
||||||
},
|
},
|
||||||
"client/src/input/inputs.rs": {
|
"client/src/input/inputs.rs": {
|
||||||
"loc": 673,
|
"line_percent": 97.55102040816327,
|
||||||
"line_percent": 97.55102040816327
|
"loc": 673
|
||||||
},
|
},
|
||||||
"client/src/input/keyboard.rs": {
|
"client/src/input/keyboard.rs": {
|
||||||
"loc": 580,
|
"line_percent": 95.9409594095941,
|
||||||
"line_percent": 95.9409594095941
|
"loc": 580
|
||||||
},
|
},
|
||||||
"client/src/input/keymap.rs": {
|
"client/src/input/keymap.rs": {
|
||||||
"loc": 196,
|
"line_percent": 100.0,
|
||||||
"line_percent": 100.0
|
"loc": 196
|
||||||
},
|
},
|
||||||
"client/src/input/microphone.rs": {
|
"client/src/input/microphone.rs": {
|
||||||
"loc": 166,
|
"line_percent": 95.94594594594594,
|
||||||
"line_percent": 95.94594594594594
|
"loc": 166
|
||||||
},
|
},
|
||||||
"client/src/input/mouse.rs": {
|
"client/src/input/mouse.rs": {
|
||||||
"loc": 317,
|
"line_percent": 97.32142857142857,
|
||||||
"line_percent": 97.32142857142857
|
"loc": 317
|
||||||
},
|
},
|
||||||
"client/src/launcher/clipboard.rs": {
|
"client/src/launcher/clipboard.rs": {
|
||||||
"loc": 177,
|
"line_percent": 98.0,
|
||||||
"line_percent": 98.0
|
"loc": 177
|
||||||
},
|
},
|
||||||
"client/src/launcher/devices.rs": {
|
"client/src/launcher/devices.rs": {
|
||||||
"loc": 158,
|
"line_percent": 98.13084112149532,
|
||||||
"line_percent": 98.13084112149532
|
"loc": 158
|
||||||
},
|
},
|
||||||
"client/src/launcher/diagnostics.rs": {
|
"client/src/launcher/diagnostics.rs": {
|
||||||
"loc": 175,
|
"line_percent": 97.14285714285714,
|
||||||
"line_percent": 97.14285714285714
|
"loc": 175
|
||||||
},
|
},
|
||||||
"client/src/launcher/mod.rs": {
|
"client/src/launcher/mod.rs": {
|
||||||
"loc": 195,
|
"line_percent": 95.23809523809523,
|
||||||
"line_percent": 95.23809523809523
|
"loc": 195
|
||||||
},
|
},
|
||||||
"client/src/launcher/state.rs": {
|
"client/src/launcher/state.rs": {
|
||||||
"loc": 360,
|
"line_percent": 98.29059829059828,
|
||||||
"line_percent": 98.29059829059828
|
"loc": 360
|
||||||
},
|
},
|
||||||
"client/src/launcher/ui.rs": {
|
"client/src/launcher/ui.rs": {
|
||||||
"loc": 654,
|
"line_percent": 100.0,
|
||||||
"line_percent": 100.0
|
"loc": 695
|
||||||
},
|
},
|
||||||
"client/src/layout.rs": {
|
"client/src/layout.rs": {
|
||||||
"loc": 78,
|
"line_percent": 97.72727272727273,
|
||||||
"line_percent": 97.72727272727273
|
"loc": 78
|
||||||
},
|
},
|
||||||
"client/src/main.rs": {
|
"client/src/main.rs": {
|
||||||
"loc": 96,
|
"line_percent": 97.0873786407767,
|
||||||
"line_percent": 97.0873786407767
|
"loc": 96
|
||||||
},
|
},
|
||||||
"client/src/output/audio.rs": {
|
"client/src/output/audio.rs": {
|
||||||
"loc": 195,
|
"line_percent": 98.78048780487805,
|
||||||
"line_percent": 98.78048780487805
|
"loc": 195
|
||||||
},
|
},
|
||||||
"client/src/output/display.rs": {
|
"client/src/output/display.rs": {
|
||||||
"loc": 81,
|
"line_percent": 97.61904761904762,
|
||||||
"line_percent": 97.61904761904762
|
"loc": 81
|
||||||
},
|
},
|
||||||
"client/src/output/layout.rs": {
|
"client/src/output/layout.rs": {
|
||||||
"loc": 155,
|
"line_percent": 98.9795918367347,
|
||||||
"line_percent": 98.9795918367347
|
"loc": 155
|
||||||
},
|
},
|
||||||
"client/src/output/video.rs": {
|
"client/src/output/video.rs": {
|
||||||
"loc": 547,
|
"line_percent": 96.22641509433963,
|
||||||
"line_percent": 96.22641509433963
|
"loc": 547
|
||||||
},
|
},
|
||||||
"client/src/paste.rs": {
|
"client/src/paste.rs": {
|
||||||
"loc": 46,
|
"line_percent": 96.29629629629629,
|
||||||
"line_percent": 96.29629629629629
|
"loc": 46
|
||||||
},
|
},
|
||||||
"common/src/bin/cli.rs": {
|
"common/src/bin/cli.rs": {
|
||||||
"loc": 3,
|
"line_percent": 100.0,
|
||||||
"line_percent": 100.0
|
"loc": 3
|
||||||
},
|
},
|
||||||
"common/src/cli.rs": {
|
"common/src/cli.rs": {
|
||||||
"loc": 22,
|
"line_percent": 100.0,
|
||||||
"line_percent": 100.0
|
"loc": 22
|
||||||
},
|
},
|
||||||
"common/src/hid.rs": {
|
"common/src/hid.rs": {
|
||||||
"loc": 134,
|
"line_percent": 100.0,
|
||||||
"line_percent": 100.0
|
"loc": 134
|
||||||
},
|
},
|
||||||
"common/src/lib.rs": {
|
"common/src/lib.rs": {
|
||||||
"loc": 22,
|
"line_percent": 100.0,
|
||||||
"line_percent": 100.0
|
"loc": 22
|
||||||
},
|
},
|
||||||
"common/src/paste.rs": {
|
"common/src/paste.rs": {
|
||||||
"loc": 95,
|
"line_percent": 100.0,
|
||||||
"line_percent": 100.0
|
"loc": 95
|
||||||
},
|
},
|
||||||
"server/src/audio.rs": {
|
"server/src/audio.rs": {
|
||||||
"loc": 386,
|
"line_percent": 100.0,
|
||||||
"line_percent": 98.9010989010989
|
"loc": 386
|
||||||
},
|
},
|
||||||
"server/src/bin/lesavka-uvc.rs": {
|
"server/src/bin/lesavka-uvc.rs": {
|
||||||
"loc": 710,
|
"line_percent": 96.35535307517085,
|
||||||
"line_percent": 96.35535307517085
|
"loc": 710
|
||||||
},
|
},
|
||||||
"server/src/camera.rs": {
|
"server/src/camera.rs": {
|
||||||
"loc": 392,
|
"line_percent": 99.09909909909909,
|
||||||
"line_percent": 99.09909909909909
|
"loc": 392
|
||||||
},
|
},
|
||||||
"server/src/camera_runtime.rs": {
|
"server/src/camera_runtime.rs": {
|
||||||
"loc": 200,
|
"line_percent": 100.0,
|
||||||
"line_percent": 100.0
|
"loc": 200
|
||||||
},
|
},
|
||||||
"server/src/capture_power.rs": {
|
"server/src/capture_power.rs": {
|
||||||
"loc": 338,
|
"line_percent": 100.0,
|
||||||
"line_percent": 100.0
|
"loc": 338
|
||||||
},
|
},
|
||||||
"server/src/gadget.rs": {
|
"server/src/gadget.rs": {
|
||||||
"loc": 327,
|
"line_percent": 96.875,
|
||||||
"line_percent": 96.875
|
"loc": 327
|
||||||
},
|
},
|
||||||
"server/src/handshake.rs": {
|
"server/src/handshake.rs": {
|
||||||
"loc": 40,
|
"line_percent": 100.0,
|
||||||
"line_percent": 100.0
|
"loc": 40
|
||||||
},
|
},
|
||||||
"server/src/main.rs": {
|
"server/src/main.rs": {
|
||||||
"loc": 572,
|
"line_percent": 95.33333333333334,
|
||||||
"line_percent": 95.33333333333334
|
"loc": 572
|
||||||
},
|
},
|
||||||
"server/src/paste.rs": {
|
"server/src/paste.rs": {
|
||||||
"loc": 207,
|
"line_percent": 97.12230215827337,
|
||||||
"line_percent": 97.12230215827337
|
"loc": 207
|
||||||
},
|
},
|
||||||
"server/src/runtime_support.rs": {
|
"server/src/runtime_support.rs": {
|
||||||
"loc": 387,
|
"line_percent": 96.42857142857143,
|
||||||
"line_percent": 96.42857142857143
|
"loc": 387
|
||||||
},
|
},
|
||||||
"server/src/uvc_runtime.rs": {
|
"server/src/uvc_runtime.rs": {
|
||||||
"loc": 241,
|
"line_percent": 97.14285714285714,
|
||||||
"line_percent": 97.14285714285714
|
"loc": 241
|
||||||
},
|
},
|
||||||
"server/src/video.rs": {
|
"server/src/video.rs": {
|
||||||
"loc": 344,
|
"line_percent": 100.0,
|
||||||
"line_percent": 100.0
|
"loc": 343
|
||||||
},
|
},
|
||||||
"server/src/video_sinks.rs": {
|
"server/src/video_sinks.rs": {
|
||||||
"loc": 559,
|
"line_percent": 100.0,
|
||||||
"line_percent": 100.0
|
"loc": 559
|
||||||
},
|
},
|
||||||
"server/src/video_support.rs": {
|
"server/src/video_support.rs": {
|
||||||
"loc": 236,
|
"line_percent": 96.03174603174604,
|
||||||
"line_percent": 96.03174603174604
|
"loc": 236
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user