Skip to content

Commit

Permalink
fix(display): avoid block on dispatch; fixing app close
Browse files Browse the repository at this point in the history
  • Loading branch information
mmstick committed Jan 11, 2025
1 parent f0dd262 commit 7576ad1
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 28 deletions.
5 changes: 5 additions & 0 deletions cosmic-settings/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@ impl cosmic::Application for SettingsApp {
widgets
}

fn on_app_exit(&mut self) -> Option<Self::Message> {
self.pages.on_leave(self.active_page);
None
}

fn on_escape(&mut self) -> Task<Self::Message> {
if self.search_active {
self.search_active = false;
Expand Down
75 changes: 47 additions & 28 deletions cosmic-settings/src/pages/display/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ use once_cell::sync::Lazy;
use slab::Slab;
use slotmap::{Key, SecondaryMap, SlotMap};
use std::{collections::BTreeMap, process::ExitStatus, sync::Arc};
use tokio::sync::oneshot;
use tokio::task::JoinHandle;
use tracing::error;

static DPI_SCALES: &[u32] = &[50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300];
Expand Down Expand Up @@ -132,7 +134,7 @@ pub struct Page {
mirror_map: SecondaryMap<OutputKey, OutputKey>,
mirror_menu: widget::dropdown::multi::Model<String, Mirroring>,
active_display: OutputKey,
background_service: Option<tokio::task::JoinHandle<()>>,
background_service_cancel: Option<oneshot::Sender<()>>,
config: Config,
cache: ViewCache,
// context: Option<ContextDrawer>,
Expand Down Expand Up @@ -166,7 +168,7 @@ impl Default for Page {
mirror_map: SecondaryMap::new(),
mirror_menu: widget::dropdown::multi::model(),
active_display: OutputKey::default(),
background_service: None,
background_service_cancel: None,
config: Config::default(),
cache: ViewCache::default(),
// context: None,
Expand Down Expand Up @@ -246,51 +248,68 @@ impl page::Page<crate::pages::Message> for Page {
) -> Task<crate::pages::Message> {
use std::time::Duration;

if let Some(task) = self.background_service.take() {
task.abort();
use futures::pin_mut;

if let Some(canceller) = self.background_service_cancel.take() {
_ = canceller.send(());
}

#[cfg(feature = "wayland")]
{
let (tx, mut rx) = tachyonix::channel(4);
let (canceller, cancelled) = oneshot::channel();
let runtime = tokio::runtime::Handle::current();

// Spawns a background service to monitor for display state changes.
// This must be spawned onto its own thread because `*mut wayland_sys::client::wl_display` is not Send-able.
let runtime = tokio::runtime::Handle::current();
self.background_service = Some(tokio::task::spawn_blocking(move || {
runtime.block_on(async move {
let (tx, mut rx) = tachyonix::channel(200);
tokio::task::spawn_blocking(move || {
let dispatcher = async move {
let Ok((mut context, mut event_queue)) = cosmic_randr::connect(tx) else {
return;
};

while context.dispatch(&mut event_queue).await.is_ok() {
if sender.is_closed() {
break;
loop {
if context.dispatch(&mut event_queue).await.is_err() {
return;
}
'outer: while let Ok(message) = rx.try_recv() {
if let cosmic_randr::Message::ManagerDone = message {
if matches!(
tokio::time::timeout(
Duration::from_secs(1),
sender.send(pages::Message::Displays(Message::Refresh))
)
.await,
Err(_) | Ok(Err(_))
) {
break 'outer;
}
}
}
};

pin_mut!(dispatcher);
runtime.block_on(futures::future::select(cancelled, dispatcher));
});

// Forward messages from another thread to prevent the monitoring thread from blocking.
tokio::task::spawn(async move {
while let Ok(message) = rx.recv().await {
if sender.is_closed() {
return;
}

if let cosmic_randr::Message::ManagerDone = message {
if matches!(
tokio::time::timeout(
Duration::from_secs(1),
sender.send(pages::Message::Displays(Message::Refresh))
)
.await,
Err(_) | Ok(Err(_))
) {
return;
}
}
});
}));
}
});

self.background_service_cancel = Some(canceller);
}

cosmic::task::future(on_enter())
}

fn on_leave(&mut self) -> Task<crate::pages::Message> {
if let Some(task) = self.background_service.take() {
task.abort();
if let Some(canceller) = self.background_service_cancel.take() {
_ = canceller.send(());
}

Task::none()
Expand Down

0 comments on commit 7576ad1

Please sign in to comment.