lesavka: inherit mirrored source modes

This commit is contained in:
Brad Stein 2026-04-19 11:42:41 -03:00
parent fa2233f59e
commit ddbbf6b036
7 changed files with 136 additions and 38 deletions

View File

@ -4,7 +4,7 @@ path = "src/main.rs"
[package]
name = "lesavka_client"
version = "0.11.13"
version = "0.11.14"
edition = "2024"
[dependencies]

View File

@ -151,8 +151,12 @@ pub struct SnapshotReport {
impl SnapshotReport {
pub fn from_state(state: &LauncherState, log: &DiagnosticsLog, probe_command: String) -> Self {
let left_capture = state.capture_size_choice(0);
let right_capture = state.capture_size_choice(1);
let left_capture = state
.display_capture_size_choice(0)
.unwrap_or_else(|| state.capture_size_choice(0));
let right_capture = state
.display_capture_size_choice(1)
.unwrap_or_else(|| state.capture_size_choice(1));
let left_breakout = state.breakout_size_choice(0);
let right_breakout = state.breakout_size_choice(1);
let latest = log.latest();
@ -741,7 +745,9 @@ fn recommendations_for(state: &LauncherState, log: &DiagnosticsLog) -> Vec<Strin
#[cfg(test)]
mod tests {
use super::*;
use crate::launcher::state::{CaptureSizePreset, DeviceSelection, LauncherState};
use crate::launcher::state::{
CaptureSizePreset, DeviceSelection, FeedSourcePreset, LauncherState,
};
fn sample(n: u64) -> PerformanceSample {
PerformanceSample {
@ -895,6 +901,23 @@ mod tests {
assert!(text.contains("recommendations"));
}
#[test]
fn snapshot_report_uses_effective_mirrored_capture_profile() {
let mut state = LauncherState::new();
state.set_feed_source_preset(0, FeedSourcePreset::OtherEye);
state.set_capture_size_preset(1, CaptureSizePreset::P720);
let report = SnapshotReport::from_state(
&state,
&DiagnosticsLog::new(1),
quality_probe_command().to_string(),
);
assert_eq!(report.left_feed_source, "Right Eye (mirrored)");
assert!(report.left_capture_profile.contains("720p"));
assert!(report.left_capture_profile.contains("1280x720"));
}
#[test]
fn quality_probe_command_mentions_both_gates() {
let cmd = quality_probe_command();

View File

@ -213,23 +213,33 @@ fn refresh_eye_feed_controls(
state.feed_source_preset(monitor_id),
);
if state.feed_source_preset(monitor_id) != FeedSourcePreset::Off {
let choice = state.capture_size_choice(monitor_id);
super::ui_components::sync_capture_resolution_combo(
&widgets.display_panes[monitor_id].capture_resolution_combo,
state.capture_size_options(),
state.capture_size_preset(monitor_id),
);
let choice = state
.display_capture_size_choice(monitor_id)
.unwrap_or_else(|| state.capture_size_choice(monitor_id));
if state.feed_source_preset(monitor_id) == FeedSourcePreset::ThisEye {
super::ui_components::sync_capture_resolution_combo(
&widgets.display_panes[monitor_id].capture_resolution_combo,
state.capture_size_options(),
state.capture_size_preset(monitor_id),
);
} else {
super::ui_components::sync_capture_resolution_locked(
&widgets.display_panes[monitor_id].capture_resolution_combo,
state.capture_size_options(),
choice.preset,
);
}
super::ui_components::sync_capture_fps_combo(
&widgets.display_panes[monitor_id].capture_fps_combo,
state.capture_fps_options(),
state.capture_fps(monitor_id),
choice.fps,
true,
choice.fps,
);
super::ui_components::sync_capture_bitrate_combo(
&widgets.display_panes[monitor_id].capture_bitrate_combo,
state.capture_bitrate_options(),
state.capture_bitrate_kbit(monitor_id),
choice.max_bitrate_kbit,
true,
choice.max_bitrate_kbit,
);
@ -516,7 +526,9 @@ fn apply_preview_profiles(preview: &super::preview::LauncherPreview, state: &Lau
for monitor_id in 0..2 {
let enabled = state.feed_source_preset(monitor_id) != FeedSourcePreset::Off;
preview.set_monitor_enabled(monitor_id, enabled);
let capture = state.capture_size_choice(monitor_id);
let capture = state
.display_capture_size_choice(monitor_id)
.unwrap_or_else(|| state.capture_size_choice(monitor_id));
let source_monitor_id = state
.resolved_feed_monitor_id(monitor_id)
.unwrap_or(monitor_id);
@ -897,7 +909,7 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
let Some(preset) = CaptureSizePreset::from_id(active_id.as_str()) else {
return;
};
if state.borrow().feed_source_preset(monitor_id) == FeedSourcePreset::Off {
if state.borrow().feed_source_preset(monitor_id) != FeedSourcePreset::ThisEye {
return;
}
if state.borrow().capture_size_preset(monitor_id) == preset {
@ -908,7 +920,10 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
state.set_capture_size_preset(monitor_id, preset);
}
if let Some(preview) = preview.as_ref() {
let choice = state.borrow().capture_size_choice(monitor_id);
let choice = state
.borrow()
.display_capture_size_choice(monitor_id)
.unwrap_or_else(|| state.borrow().capture_size_choice(monitor_id));
let source_monitor_id = state
.borrow()
.resolved_feed_monitor_id(monitor_id)
@ -944,7 +959,7 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
let Ok(fps) = active_id.as_str().parse::<u32>() else {
return;
};
if state.borrow().feed_source_preset(monitor_id) == FeedSourcePreset::Off {
if state.borrow().feed_source_preset(monitor_id) != FeedSourcePreset::ThisEye {
return;
}
if state.borrow().capture_fps(monitor_id) == fps {
@ -955,7 +970,10 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
state.set_capture_fps(monitor_id, fps);
}
if let Some(preview) = preview.as_ref() {
let choice = state.borrow().capture_size_choice(monitor_id);
let choice = state
.borrow()
.display_capture_size_choice(monitor_id)
.unwrap_or_else(|| state.borrow().capture_size_choice(monitor_id));
let source_monitor_id = state
.borrow()
.resolved_feed_monitor_id(monitor_id)
@ -992,7 +1010,7 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
let Ok(max_bitrate_kbit) = active_id.as_str().parse::<u32>() else {
return;
};
if state.borrow().feed_source_preset(monitor_id) == FeedSourcePreset::Off {
if state.borrow().feed_source_preset(monitor_id) != FeedSourcePreset::ThisEye {
return;
}
if state.borrow().capture_bitrate_kbit(monitor_id) == max_bitrate_kbit {
@ -1003,7 +1021,10 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> {
state.set_capture_bitrate_kbit(monitor_id, max_bitrate_kbit);
}
if let Some(preview) = preview.as_ref() {
let choice = state.borrow().capture_size_choice(monitor_id);
let choice = state
.borrow()
.display_capture_size_choice(monitor_id)
.unwrap_or_else(|| state.borrow().capture_size_choice(monitor_id));
let source_monitor_id = state
.borrow()
.resolved_feed_monitor_id(monitor_id)
@ -2084,6 +2105,31 @@ mod tests {
preview.shutdown_all();
}
#[test]
fn mirrored_preview_profile_inherits_the_source_eye_mode() {
let preview = LauncherPreview::new("http://127.0.0.1:1".to_string()).unwrap();
let mut state = LauncherState::default();
state.set_feed_source_preset(0, FeedSourcePreset::OtherEye);
state.set_capture_size_preset(1, CaptureSizePreset::P720);
apply_preview_profiles(&preview, &state);
let inline = preview.profile_for_test(0, PreviewSurface::Inline).unwrap();
let window = preview.profile_for_test(0, PreviewSurface::Window).unwrap();
assert_eq!(inline.0, 1);
assert_eq!(window.0, 1);
assert_eq!(inline.3, 1280);
assert_eq!(inline.4, 720);
assert_eq!(inline.5, 60);
assert_eq!(inline.6, 12_000);
assert_eq!(window.3, 1280);
assert_eq!(window.4, 720);
assert_eq!(window.5, 60);
assert_eq!(window.6, 12_000);
preview.shutdown_all();
}
#[test]
fn off_preview_profile_disables_both_surfaces_instead_of_leaving_idle_feeds_running() {
let preview = LauncherPreview::new("http://127.0.0.1:1".to_string()).unwrap();

View File

@ -566,23 +566,33 @@ pub fn build_launcher_view(
state.feed_source_preset(1),
);
if state.feed_source_preset(0) != FeedSourcePreset::Off {
let choice = state.capture_size_choice(0);
sync_capture_resolution_combo(
&left_pane.capture_resolution_combo,
state.capture_size_options(),
state.capture_size_preset(0),
);
let choice = state
.display_capture_size_choice(0)
.unwrap_or_else(|| state.capture_size_choice(0));
if state.feed_source_preset(0) == FeedSourcePreset::ThisEye {
sync_capture_resolution_combo(
&left_pane.capture_resolution_combo,
state.capture_size_options(),
state.capture_size_preset(0),
);
} else {
sync_capture_resolution_locked(
&left_pane.capture_resolution_combo,
state.capture_size_options(),
choice.preset,
);
}
sync_capture_fps_combo(
&left_pane.capture_fps_combo,
state.capture_fps_options(),
state.capture_fps(0),
choice.fps,
true,
choice.fps,
);
sync_capture_bitrate_combo(
&left_pane.capture_bitrate_combo,
state.capture_bitrate_options(),
state.capture_bitrate_kbit(0),
choice.max_bitrate_kbit,
true,
choice.max_bitrate_kbit,
);
@ -592,23 +602,33 @@ pub fn build_launcher_view(
sync_capture_bitrate_disabled(&left_pane.capture_bitrate_combo);
}
if state.feed_source_preset(1) != FeedSourcePreset::Off {
let choice = state.capture_size_choice(1);
sync_capture_resolution_combo(
&right_pane.capture_resolution_combo,
state.capture_size_options(),
state.capture_size_preset(1),
);
let choice = state
.display_capture_size_choice(1)
.unwrap_or_else(|| state.capture_size_choice(1));
if state.feed_source_preset(1) == FeedSourcePreset::ThisEye {
sync_capture_resolution_combo(
&right_pane.capture_resolution_combo,
state.capture_size_options(),
state.capture_size_preset(1),
);
} else {
sync_capture_resolution_locked(
&right_pane.capture_resolution_combo,
state.capture_size_options(),
choice.preset,
);
}
sync_capture_fps_combo(
&right_pane.capture_fps_combo,
state.capture_fps_options(),
state.capture_fps(1),
choice.fps,
true,
choice.fps,
);
sync_capture_bitrate_combo(
&right_pane.capture_bitrate_combo,
state.capture_bitrate_options(),
state.capture_bitrate_kbit(1),
choice.max_bitrate_kbit,
true,
choice.max_bitrate_kbit,
);
@ -921,6 +941,15 @@ pub fn sync_capture_resolution_combo(
combo.set_sensitive(option_count > 1);
}
pub fn sync_capture_resolution_locked(
combo: &gtk::ComboBoxText,
options: Vec<CaptureSizeChoice>,
selected: CaptureSizePreset,
) {
sync_capture_resolution_combo(combo, options, selected);
combo.set_sensitive(false);
}
pub fn sync_capture_resolution_disabled(combo: &gtk::ComboBoxText) {
combo.remove_all();
combo.append(Some("off"), "Feed disabled");

View File

@ -1,6 +1,6 @@
[package]
name = "lesavka_common"
version = "0.11.13"
version = "0.11.14"
edition = "2024"
build = "build.rs"

View File

@ -17,6 +17,6 @@ mod tests {
#[test]
fn banner_includes_version() {
assert_eq!(banner("0.11.13"), "lesavka-common CLI (v0.11.13)");
assert_eq!(banner("0.11.14"), "lesavka-common CLI (v0.11.14)");
}
}

View File

@ -10,7 +10,7 @@ bench = false
[package]
name = "lesavka_server"
version = "0.11.13"
version = "0.11.14"
edition = "2024"
autobins = false