use iced::widget::{button, column, container, row, text};
use iced::{Element, Length, Task, Theme};
use iced_live_cast::{
CastView, Monitor, MonitorCapture, MonitorCaptureOptions, MonitorCaptureRuntimeError,
};
use std::env;
fn main() -> iced::Result {
iced::application(BasicExample::boot, BasicExample::update, BasicExample::view)
.theme(app_theme)
.run()
}
#[derive(Debug)]
struct BasicExample {
source: Option<Monitor>,
capture: Option<MonitorCapture>,
start_error: Option<String>,
runtime_error: Option<MonitorCaptureRuntimeError>,
}
#[derive(Debug, Clone)]
enum Message {
StartPressed,
TogglePlaybackPressed,
StopPressed,
RuntimeErrorReported(MonitorCaptureRuntimeError),
}
impl BasicExample {
fn boot() -> (Self, Task<Message>) {
let (source, start_error) = match selected_monitor() {
Ok(monitor) => (Some(monitor), None),
Err(error) => (None, Some(error)),
};
(
Self {
source,
capture: None,
start_error,
runtime_error: None,
},
Task::none(),
)
}
fn update(&mut self, message: Message) -> Task<Message> {
match message {
Message::StartPressed => {
let Some(source) = self.source else {
return Task::none();
};
match MonitorCaptureOptions::default()
.with_self_exclusion(true)
.start(source)
{
Ok(capture) => {
self.capture = Some(capture);
self.start_error = None;
self.runtime_error = None;
}
Err(error) => {
self.capture = None;
self.start_error = Some(error.to_string());
self.runtime_error = None;
}
}
}
Message::TogglePlaybackPressed => {
if let Some(capture) = &self.capture {
if capture.is_paused() {
capture.resume();
} else {
capture.pause();
}
}
}
Message::StopPressed => {
if let Some(capture) = &self.capture {
capture.stop();
}
self.capture = None;
self.runtime_error = None;
}
Message::RuntimeErrorReported(error) => {
self.runtime_error = Some(error);
}
}
Task::none()
}
fn view(&self) -> Element<'_, Message> {
let start = button(text("Start")).on_press(Message::StartPressed);
let pause_label = if self
.capture
.as_ref()
.is_some_and(|capture| capture.is_paused())
{
"Resume"
} else {
"Pause"
};
let pause = if self.capture.is_some() {
button(text(pause_label)).on_press(Message::TogglePlaybackPressed)
} else {
button(text(pause_label))
};
let stop = if self.capture.is_some() {
button(text("Stop")).on_press(Message::StopPressed)
} else {
button(text("Stop"))
};
let controls = row![start, pause, stop].spacing(12);
let status = text(status_line(self));
let source = text(source_line(self));
let preview: Element<'_, _> = if let Some(capture) = &self.capture {
container(
CastView::new(capture)
.on_error(|error| Some(Message::RuntimeErrorReported(error.clone())))
.width(Length::Fill)
.height(420),
)
.width(Length::Fill)
.height(420)
.into()
} else {
container(text("Press Start to begin live capture."))
.width(Length::Fill)
.height(420)
.center_x(Length::Fill)
.center_y(420)
.into()
};
container(
column![
text("iced_live_cast basic example").size(30),
source,
status,
controls,
preview
]
.spacing(16)
.max_width(920),
)
.width(Length::Fill)
.height(Length::Fill)
.center_x(Length::Fill)
.center_y(Length::Fill)
.padding(24)
.into()
}
}
fn app_theme(_: &BasicExample) -> Theme {
Theme::TokyoNight
}
fn selected_monitor() -> Result<Monitor, String> {
let monitors = Monitor::all().map_err(|error| error.to_string())?;
if monitors.is_empty() {
return Err(String::from("No monitors are currently available"));
}
let selected = if let Some(value) = env::args().nth(1) {
if let Ok(monitor_id) = value.parse::<u32>() {
monitors
.iter()
.find(|monitor| monitor.id().get() == monitor_id)
.copied()
.or_else(|| {
value.parse::<usize>().ok().and_then(|index| {
index
.checked_sub(1)
.and_then(|index| monitors.get(index))
.copied()
})
})
.ok_or_else(|| format!("No monitor matched `{value}`"))?
} else {
return Err(format!(
"Could not parse `{value}` as a monitor id or index"
));
}
} else {
monitors[0]
};
Ok(selected)
}
fn source_line(app: &BasicExample) -> String {
match &app.source {
Some(monitor) => format!("Selected {}", monitor),
None => String::from("No monitor selected"),
}
}
fn status_line(app: &BasicExample) -> String {
if let Some(error) = &app.start_error {
return format!("Start failed: {error}");
}
let Some(capture) = &app.capture else {
return String::from("Idle");
};
if let Some(error) = &app.runtime_error {
return format!("Streaming error: {error}");
}
if capture.is_stopped() {
return String::from("Stopped");
}
if capture.is_paused() {
return format!("Paused for {}", capture.monitor());
}
format!("Streaming for {}", capture.monitor())
}