185 lines
7.3 KiB
Rust

use anyhow::Result;
#[cfg(not(coverage))]
use {
super::clipboard::send_clipboard_text_to_remote,
super::device_test::{DeviceTestController, DeviceTestKind},
super::devices::{CameraMode, DeviceCatalog},
super::diagnostics::{PerformanceSample, quality_probe_command},
super::launcher_clipboard_control_path,
super::launcher_focus_signal_path,
super::power::{fetch_capture_power, reset_usb_gadget, set_capture_power_mode},
super::state::{
BreakoutSizePreset, CapturePowerStatus, CaptureSizePreset, DisplaySurface,
FeedSourcePreset, InputRouting, LauncherState, MAX_AUDIO_GAIN_PERCENT,
MAX_MIC_GAIN_PERCENT,
},
super::ui_components::{
ConsoleLogLevel, build_launcher_view, sync_camera_quality_combo, sync_input_device_combo,
sync_stage_device_combo,
},
super::ui_runtime::{
RelayChild, append_session_log_for_level, apply_popout_window_size,
attach_child_log_streams, audio_gain_control_path, capture_swap_key, copy_plain_text,
copy_session_log, dock_all_displays_to_preview, dock_display_to_preview,
input_control_path, input_state_path, input_toggle_control_path, mic_gain_control_path,
next_input_routing, open_diagnostics_popout, open_popout_window, open_session_log_popout,
path_marker, present_popout_windows, read_input_routing_state, reap_exited_child,
refresh_launcher_ui, refresh_test_buttons, routing_name, selected_combo_value,
selected_server_addr, shutdown_launcher_runtime, spawn_client_process, stop_child_process,
toggle_key_label, update_test_action_result, uplink_camera_preview_path,
uplink_mic_level_path, write_audio_gain_request, write_input_routing_request,
write_input_toggle_key_request, write_mic_gain_request,
},
crate::handshake::{HandshakeProbe, probe},
crate::output::display::enumerate_monitors,
gtk::glib,
gtk::prelude::*,
lesavka_common::lesavka::CapturePowerCommand,
lesavka_common::process_metrics::ProcessCpuSampler,
std::cell::{Cell, RefCell},
std::collections::VecDeque,
std::process::Command,
std::rc::Rc,
std::time::{Duration, Instant},
};
include!("ui/message_and_network_state.rs");
#[cfg(not(coverage))]
include!("ui/control_requests.rs");
#[cfg(not(coverage))]
include!("ui/diagnostic_sampling.rs");
#[cfg(not(coverage))]
include!("ui/preview_profiles.rs");
#[cfg(not(coverage))]
include!("ui/activation_context.rs");
#[cfg(not(coverage))]
pub fn run_gui_launcher(server_addr: String) -> Result<()> {
let app = gtk::Application::builder()
.application_id("dev.lesavka.launcher")
.build();
let catalog = Rc::new(RefCell::new(DeviceCatalog::discover()));
let state = Rc::new(RefCell::new(LauncherState::new()));
state.borrow_mut().apply_catalog_defaults(&catalog.borrow());
let child_proc = Rc::new(RefCell::new(None::<RelayChild>));
let tests = Rc::new(RefCell::new(DeviceTestController::new()));
let server_addr = Rc::new(server_addr);
let focus_signal_path = Rc::new(launcher_focus_signal_path());
let clipboard_control_path = Rc::new(launcher_clipboard_control_path());
let input_control_path = Rc::new(input_control_path());
let input_state_path = Rc::new(input_state_path());
let input_toggle_control_path = Rc::new(input_toggle_control_path());
let _ = std::fs::remove_file(focus_signal_path.as_path());
let _ = std::fs::remove_file(clipboard_control_path.as_path());
let _ = std::fs::remove_file(input_control_path.as_path());
let _ = std::fs::remove_file(input_state_path.as_path());
let _ = std::fs::remove_file(input_toggle_control_path.as_path());
{
let child_proc = Rc::clone(&child_proc);
let focus_signal_path = Rc::clone(&focus_signal_path);
let clipboard_control_path = Rc::clone(&clipboard_control_path);
let input_control_path = Rc::clone(&input_control_path);
let input_state_path = Rc::clone(&input_state_path);
let input_toggle_control_path = Rc::clone(&input_toggle_control_path);
let tests = Rc::clone(&tests);
app.connect_shutdown(move |_| {
stop_child_process(&child_proc);
tests.borrow_mut().stop_all();
let _ = std::fs::remove_file(focus_signal_path.as_path());
let _ = std::fs::remove_file(clipboard_control_path.as_path());
let _ = std::fs::remove_file(input_control_path.as_path());
let _ = std::fs::remove_file(input_state_path.as_path());
let _ = std::fs::remove_file(input_toggle_control_path.as_path());
});
}
{
let catalog = Rc::clone(&catalog);
let state = Rc::clone(&state);
let child_proc = Rc::clone(&child_proc);
let tests = Rc::clone(&tests);
let server_addr = Rc::clone(&server_addr);
let focus_signal_path = Rc::clone(&focus_signal_path);
let input_control_path = Rc::clone(&input_control_path);
let input_state_path = Rc::clone(&input_state_path);
let input_toggle_control_path = Rc::clone(&input_toggle_control_path);
app.connect_activate(move |app| {
let ActivationContext {
window,
server_entry,
camera_combo,
camera_quality_combo,
microphone_combo,
speaker_combo,
keyboard_combo,
mouse_combo,
widgets,
preview,
popouts,
diagnostics_popout,
log_popout,
camera_quality_syncing,
power_tx,
power_rx,
power_request_in_flight,
relay_tx,
relay_rx,
relay_request_in_flight,
caps_tx,
caps_rx,
caps_request_in_flight,
diagnostics_network,
diagnostics_process,
next_power_probe,
next_diagnostics_probe,
next_diagnostics_sample,
preview_session_active,
clipboard_tx,
clipboard_rx,
log_tx,
log_rx,
} = include!("ui/activation_setup.rs");
include!("ui/stage_device_bindings.rs");
include!("ui/eye_display_bindings.rs");
include!("ui/media_device_bindings.rs");
let _: () = include!("ui/device_refresh_binding.rs");
include!("ui/relay_input_bindings.rs");
include!("ui/utility_button_bindings.rs");
include!("ui/local_test_bindings.rs");
include!("ui/power_display_key_bindings.rs");
let _: () = include!("ui/runtime_poll.rs");
window.present();
});
}
let _ = app.run_with_args::<&str>(&[]);
Ok(())
}
#[cfg(coverage)]
pub fn run_gui_launcher(_server_addr: String) -> Result<()> {
Ok(())
}
/// Keep the coverage stub aligned with the real preview activation rule.
#[cfg(coverage)]
fn session_preview_active(
state: &crate::launcher::state::LauncherState,
child_running: bool,
) -> bool {
(child_running || state.remote_active) && state.capture_power.mode != "forced-off"
}
#[cfg(all(test, not(coverage)))]
#[path = "tests/ui_preview_profiles.rs"]
mod tests;
#[cfg(all(test, coverage))]
#[path = "tests/ui_coverage.rs"]
mod tests;