222 lines
7.1 KiB
Rust
222 lines
7.1 KiB
Rust
fn largest_monitor_size() -> (u32, u32) {
|
|
let (width, height) = enumerate_monitors()
|
|
.into_iter()
|
|
.max_by_key(|monitor| {
|
|
effective_monitor_width(monitor) as u64 * effective_monitor_height(monitor) as u64
|
|
})
|
|
.map(|monitor| {
|
|
(
|
|
effective_monitor_width(&monitor),
|
|
effective_monitor_height(&monitor),
|
|
)
|
|
})
|
|
.unwrap_or((1920, 1080));
|
|
(width.max(2), height.max(2))
|
|
}
|
|
|
|
#[cfg(not(coverage))]
|
|
fn largest_monitor_physical_size() -> (u32, u32) {
|
|
if let Some((width, height)) = probe_kscreen_display_size() {
|
|
return (width, height);
|
|
}
|
|
normalize_breakout_limit(largest_monitor_size().0, largest_monitor_size().1)
|
|
}
|
|
|
|
#[cfg(not(coverage))]
|
|
fn probe_kscreen_display_size() -> Option<(u32, u32)> {
|
|
let output = Command::new("kscreen-doctor").arg("-o").output().ok()?;
|
|
if !output.status.success() {
|
|
return None;
|
|
}
|
|
let text = String::from_utf8(output.stdout).ok()?;
|
|
let mut best = None;
|
|
for line in text.lines() {
|
|
if !line.contains("Modes:") {
|
|
continue;
|
|
}
|
|
let active = line
|
|
.split_whitespace()
|
|
.find(|token| token.contains('*') && token.contains('x'))?;
|
|
let dims = active
|
|
.trim_matches(|ch: char| ch == '*' || ch == '!')
|
|
.split('@')
|
|
.next()?;
|
|
let (width, height) = dims.split_once('x')?;
|
|
let width = width.parse::<u32>().ok()?;
|
|
let height = height.parse::<u32>().ok()?;
|
|
if best
|
|
.map(|(best_w, best_h)| width as u64 * height as u64 > best_w as u64 * best_h as u64)
|
|
.unwrap_or(true)
|
|
{
|
|
best = Some((width, height));
|
|
}
|
|
}
|
|
best
|
|
}
|
|
|
|
#[cfg(not(coverage))]
|
|
fn effective_monitor_width(monitor: &crate::output::display::MonitorInfo) -> u32 {
|
|
let scale = monitor.scale_factor.max(1) as u32;
|
|
(monitor.geometry.width().max(1) as u32).saturating_mul(scale)
|
|
}
|
|
|
|
#[cfg(not(coverage))]
|
|
fn effective_monitor_height(monitor: &crate::output::display::MonitorInfo) -> u32 {
|
|
let scale = monitor.scale_factor.max(1) as u32;
|
|
(monitor.geometry.height().max(1) as u32).saturating_mul(scale)
|
|
}
|
|
|
|
#[cfg(not(coverage))]
|
|
fn normalize_breakout_limit(width: u32, height: u32) -> (u32, u32) {
|
|
const STANDARD_SIZES: &[(u32, u32)] = &[
|
|
(3840, 2160),
|
|
(2560, 1440),
|
|
(1920, 1080),
|
|
(1600, 900),
|
|
(1366, 768),
|
|
(1280, 720),
|
|
(960, 540),
|
|
];
|
|
|
|
STANDARD_SIZES
|
|
.iter()
|
|
.copied()
|
|
.find(|(candidate_w, candidate_h)| *candidate_w <= width && *candidate_h <= height)
|
|
.unwrap_or((width.max(2), height.max(2)))
|
|
}
|
|
|
|
#[cfg(not(coverage))]
|
|
fn launcher_default_size(width: u32, height: u32) -> (i32, i32) {
|
|
let max_width = width.saturating_sub(48).max(640) as i32;
|
|
let max_height = height.saturating_sub(72).max(520) as i32;
|
|
(1380.min(max_width), 860.min(max_height))
|
|
}
|
|
|
|
#[cfg(not(coverage))]
|
|
fn rebind_inline_preview(
|
|
preview: &super::preview::LauncherPreview,
|
|
widgets: &super::ui_components::LauncherWidgets,
|
|
state: &LauncherState,
|
|
monitor_id: usize,
|
|
) {
|
|
if let Some(binding) = widgets.display_panes[monitor_id]
|
|
.preview_binding
|
|
.borrow_mut()
|
|
.take()
|
|
{
|
|
binding.close();
|
|
}
|
|
if state.feed_source_preset(monitor_id) == FeedSourcePreset::Off {
|
|
widgets.display_panes[monitor_id]
|
|
.picture
|
|
.set_paintable(Option::<>k::gdk::Paintable>::None);
|
|
widgets.display_panes[monitor_id]
|
|
.stream_status
|
|
.set_text("Feed disabled.");
|
|
return;
|
|
}
|
|
let binding = preview.install_on_picture(
|
|
monitor_id,
|
|
super::preview::PreviewSurface::Inline,
|
|
&widgets.display_panes[monitor_id].picture,
|
|
&widgets.display_panes[monitor_id].stream_status,
|
|
);
|
|
*widgets.display_panes[monitor_id]
|
|
.preview_binding
|
|
.borrow_mut() = binding;
|
|
}
|
|
|
|
#[cfg(not(coverage))]
|
|
fn rebind_popout_preview(
|
|
preview: &super::preview::LauncherPreview,
|
|
popouts: &Rc<RefCell<[Option<super::ui_components::PopoutWindowHandle>; 2]>>,
|
|
state: &LauncherState,
|
|
monitor_id: usize,
|
|
) {
|
|
let mut popouts = popouts.borrow_mut();
|
|
let Some(handle) = popouts.get_mut(monitor_id).and_then(|slot| slot.as_mut()) else {
|
|
return;
|
|
};
|
|
handle.binding.close();
|
|
if state.feed_source_preset(monitor_id) == FeedSourcePreset::Off {
|
|
handle
|
|
.picture
|
|
.set_paintable(Option::<>k::gdk::Paintable>::None);
|
|
handle.status_label.set_text("Feed disabled.");
|
|
return;
|
|
}
|
|
if let Some(binding) = preview.install_on_picture(
|
|
monitor_id,
|
|
super::preview::PreviewSurface::Window,
|
|
&handle.picture,
|
|
&handle.status_label,
|
|
) {
|
|
handle.binding = binding;
|
|
}
|
|
let capture = state
|
|
.display_capture_size_choice(monitor_id)
|
|
.unwrap_or_else(|| state.capture_size_choice(monitor_id));
|
|
handle
|
|
.frame
|
|
.set_ratio(capture.preset.display_aspect_ratio());
|
|
}
|
|
|
|
#[cfg(not(coverage))]
|
|
fn apply_preview_profiles(preview: &super::preview::LauncherPreview, state: &LauncherState) {
|
|
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
|
|
.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);
|
|
let breakout = state.breakout_size_choice(monitor_id);
|
|
preview.set_capture_profile(
|
|
monitor_id,
|
|
source_monitor_id,
|
|
capture.width,
|
|
capture.height,
|
|
capture.fps,
|
|
capture.max_bitrate_kbit,
|
|
);
|
|
preview.set_breakout_profile(monitor_id, breakout.width, breakout.height);
|
|
}
|
|
}
|
|
|
|
#[cfg(not(coverage))]
|
|
fn sync_preview_profiles(
|
|
preview: &super::preview::LauncherPreview,
|
|
widgets: &super::ui_components::LauncherWidgets,
|
|
popouts: &Rc<RefCell<[Option<super::ui_components::PopoutWindowHandle>; 2]>>,
|
|
state: &LauncherState,
|
|
) {
|
|
apply_preview_profiles(preview, state);
|
|
for monitor_id in 0..2 {
|
|
rebind_inline_preview(preview, widgets, state, monitor_id);
|
|
rebind_popout_preview(preview, popouts, state, monitor_id);
|
|
}
|
|
}
|
|
|
|
#[cfg(not(coverage))]
|
|
fn disconnected_capture_note(mode: &str) -> &'static str {
|
|
match mode {
|
|
"forced-on" => "Relay disconnected. Capture is still forced on for staging.",
|
|
"forced-off" => {
|
|
"Relay disconnected. Capture stays intentionally dark until you return to Auto or Force On."
|
|
}
|
|
_ => {
|
|
"Relay disconnected. The server will hold capture briefly, then let it return to standby."
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Keeps remote eye previews tied to a live session while respecting forced-off staging.
|
|
fn session_preview_active(
|
|
state: &crate::launcher::state::LauncherState,
|
|
child_running: bool,
|
|
) -> bool {
|
|
(child_running || state.remote_active) && state.capture_power.mode != "forced-off"
|
|
}
|