However, I am fetching some limited system information to display it in my "about" page, namely: OS name, graphics backend and adapter. I do so with the help of iced's built-in system feature, that returns a task. This task runs perfectly and fetches the necessary information when a window is opened on boot, however it does not work when the app is started in the background (without an initial window). The task does not return when a window is opened later either. Somehow it seems like system info is tied to opening a window on startup, and if not, the task system::information() returns fails silently in the background, or hangs and never yields.
Here is a MRE, without --background, the widgets are populated with correct system info, with --background, when the window is opened from the tray icon, "no data" is displayed, and it stays that way indefinitely:
use iced::{
Alignment,
Length::Fill,
futures::SinkExt,
widget::{button, column, text},
};
use trayicon::{MenuBuilder, TrayIcon, TrayIconBuilder};
fn main() -> iced::Result {
let args: Vec<String> = std::env::args().collect();
println!("{:?}", args);
let nowindow = args.contains(&"--background".to_string());
iced::daemon(move || App::new(nowindow), App::update, App::view).run()
}
#[derive(Debug, Clone)]
enum Message {
Start,
Sysinfo(iced::system::Information),
OpenWindow,
CloseWindow,
Exit,
}
#[derive(Clone, Eq, PartialEq, Debug)]
enum TrayIconEvent {
Open,
Exit,
}
struct App {
window_id: Option<iced::window::Id>,
_tray_icon: TrayIcon<TrayIconEvent>,
tray_icon_receiver: tokio::sync::broadcast::Receiver<TrayIconEvent>,
info: Option<iced::system::Information>,
}
impl App {
fn new(nowindow: bool) -> (Self, iced::Task<Message>) {
let (window_id, open_task): (Option<iced::window::Id>, iced::Task<_>) = if nowindow {
(None, iced::Task::done(Message::Start))
} else {
let (id, t) = iced::window::open(iced::window::Settings::default());
(Some(id), t.map(|_| Message::Start))
};
let (tray_sender, tray_receiver) = tokio::sync::broadcast::channel(1);
(
Self {
window_id,
_tray_icon: create_tray_icon(tray_sender),
tray_icon_receiver: tray_receiver,
info: None,
},
open_task,
)
}
fn update(&mut self, message: Message) -> iced::Task<Message> {
match message {
Message::Start => {
return iced::Task::batch([
iced::Task::stream(tray_icon_events(self.tray_icon_receiver.resubscribe()))
.map(|v| v),
iced::system::information().map(Message::Sysinfo),
]);
}
Message::Sysinfo(inf) => self.info = Some(inf),
Message::OpenWindow => {
if self.window_id.is_none() {
let (id, open_task) = iced::window::open(iced::window::Settings::default());
self.window_id = Some(id);
return open_task.discard();
}
}
Message::CloseWindow => {
if let Some(id) = self.window_id {
self.window_id = None;
return iced::window::close(id);
}
}
Message::Exit => return iced::exit(),
}
iced::Task::none()
}
fn view(&self, _id: iced::window::Id) -> iced::Element<'_, Message> {
let info = if let Some(inf) = self.info.as_ref() {
(
inf.system_name.clone().unwrap_or_default(),
inf.graphics_backend.clone(),
inf.graphics_adapter.clone(),
)
} else {
(
"no data".to_string(),
"no data".to_string(),
"no data".to_string(),
)
};
column![
button("Close window").on_press(Message::CloseWindow),
text("Environment information")
.align_x(Alignment::Center)
.width(Fill)
.size(20),
text(format!("Operating system: {}", info.0)),
text(format!("Graphics backend: {}", info.1)),
text(format!("Graphics adapter: {}", info.2)),
]
.padding(12)
.spacing(12)
.into()
}
}
fn create_tray_icon(
tray_sender: tokio::sync::broadcast::Sender<TrayIconEvent>,
) -> TrayIcon<TrayIconEvent> {
TrayIconBuilder::new()
.sender(move |e: &TrayIconEvent| {
let _ = tray_sender.send(e.clone());
})
.title("NowindowTest")
.icon_from_buffer(include_bytes!("../icon.ico"))
.on_click(TrayIconEvent::Open)
.menu(
MenuBuilder::new()
.item("Open", TrayIconEvent::Open)
.item("Exit", TrayIconEvent::Exit),
)
.build()
.unwrap()
}
fn tray_icon_events(
mut rec_clone: tokio::sync::broadcast::Receiver<TrayIconEvent>,
) -> impl iced::futures::Stream<Item = Message> {
iced::stream::channel(1, async move |mut out| {
loop {
match rec_clone.recv().await {
Ok(v) => match v {
TrayIconEvent::Open => {
let _ = out.send(Message::OpenWindow).await;
}
TrayIconEvent::Exit => {
let _ = out.send(Message::Exit).await;
}
},
Err(_) => {
break;
}
}
}
})
}
Task yields system info even without initial window.
Is your issue REALLY a bug?
Is there an existing issue for this?
Is this issue related to iced?
What happened?
I use this crate to create a tray icon for my app, and I implemented a functionality to start on system boot with this crate. In case my app is started with system boot, I do not create a window, rather let the user decide when to open one from the tray icon (so that my app runs in the background). An argument (--background) signals whether to open a window or not. This works fine, just for context.
However, I am fetching some limited system information to display it in my "about" page, namely: OS name, graphics backend and adapter. I do so with the help of iced's built-in system feature, that returns a task. This task runs perfectly and fetches the necessary information when a window is opened on boot, however it does not work when the app is started in the background (without an initial window). The task does not return when a window is opened later either. Somehow it seems like system info is tied to opening a window on startup, and if not, the task system::information() returns fails silently in the background, or hangs and never yields.
Here is a MRE, without --background, the widgets are populated with correct system info, with --background, when the window is opened from the tray icon, "no data" is displayed, and it stays that way indefinitely:
What is the expected behavior?
Task yields system info even without initial window.
Version
crates.io release
Operating System
Windows
Do you have any log output?