fix: make all subscriptions resistant to being restarted

many of the errors we've been seeing the last few days are because of subscriptions which are restarting
This commit is contained in:
Ashley Wulber 2024-01-18 21:02:35 -05:00 committed by Ashley Wulber
parent ebe688c747
commit d4e0dd8fb8
12 changed files with 184 additions and 138 deletions

30
Cargo.lock generated
View file

@ -768,6 +768,7 @@ dependencies = [
"serde", "serde",
"shlex", "shlex",
"tokio", "tokio",
"tracing",
"tracing-log", "tracing-log",
"tracing-subscriber", "tracing-subscriber",
"url", "url",
@ -971,6 +972,7 @@ dependencies = [
"nix 0.27.1", "nix 0.27.1",
"once_cell", "once_cell",
"rust-embed 6.8.1", "rust-embed 6.8.1",
"tokio",
"tracing", "tracing",
"tracing-log", "tracing-log",
"tracing-subscriber", "tracing-subscriber",
@ -990,6 +992,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-config" name = "cosmic-config"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"atomicwrites", "atomicwrites",
"cosmic-config-derive", "cosmic-config-derive",
@ -1007,6 +1010,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-config-derive" name = "cosmic-config-derive"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"quote", "quote",
"syn 1.0.109", "syn 1.0.109",
@ -1094,7 +1098,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-text" name = "cosmic-text"
version = "0.10.0" version = "0.10.0"
source = "git+https://github.com/pop-os/cosmic-text.git?branch=refactor#dd4c4cbbe2d5ed5046054b5361a6eeead50e0bb0" source = "git+https://github.com/pop-os/cosmic-text.git#6aadfaddac7ae68c3f97c0b9b2fa75033374a650"
dependencies = [ dependencies = [
"bitflags 2.4.2", "bitflags 2.4.2",
"fontdb", "fontdb",
@ -1106,6 +1110,7 @@ dependencies = [
"self_cell 1.0.3", "self_cell 1.0.3",
"swash", "swash",
"sys-locale", "sys-locale",
"ttf-parser",
"unicode-bidi", "unicode-bidi",
"unicode-linebreak", "unicode-linebreak",
"unicode-script", "unicode-script",
@ -1115,6 +1120,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-theme" name = "cosmic-theme"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"almost", "almost",
"cosmic-config", "cosmic-config",
@ -1128,6 +1134,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-time" name = "cosmic-time"
version = "0.4.0" version = "0.4.0"
source = "git+https://github.com/pop-os/cosmic-time#4dc1fcec44aa7471a8e707fa391f9882d23250d7"
dependencies = [ dependencies = [
"float-cmp", "float-cmp",
"libcosmic", "libcosmic",
@ -2242,8 +2249,8 @@ dependencies = [
[[package]] [[package]]
name = "glyphon" name = "glyphon"
version = "0.3.0" version = "0.4.1"
source = "git+https://github.com/jackpot51/glyphon.git?branch=refactor#c28dc99c86b6b598633e6623096b21632f266976" source = "git+https://github.com/jackpot51/glyphon.git#abb70c0fda8cf1a5dfc314c1c778103d7ba951e6"
dependencies = [ dependencies = [
"cosmic-text", "cosmic-text",
"etagere", "etagere",
@ -2538,6 +2545,7 @@ dependencies = [
[[package]] [[package]]
name = "iced" name = "iced"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"iced_accessibility", "iced_accessibility",
"iced_core", "iced_core",
@ -2552,6 +2560,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_accessibility" name = "iced_accessibility"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"accesskit", "accesskit",
"accesskit_unix", "accesskit_unix",
@ -2560,6 +2569,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_core" name = "iced_core"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"iced_accessibility", "iced_accessibility",
@ -2577,6 +2587,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_futures" name = "iced_futures"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"futures", "futures",
"iced_core", "iced_core",
@ -2589,6 +2600,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_graphics" name = "iced_graphics"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"bytemuck", "bytemuck",
@ -2611,6 +2623,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_renderer" name = "iced_renderer"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"iced_graphics", "iced_graphics",
"iced_tiny_skia", "iced_tiny_skia",
@ -2623,6 +2636,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_runtime" name = "iced_runtime"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"iced_accessibility", "iced_accessibility",
"iced_core", "iced_core",
@ -2634,6 +2648,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_sctk" name = "iced_sctk"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"enum-repr", "enum-repr",
"float-cmp", "float-cmp",
@ -2657,6 +2672,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_style" name = "iced_style"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"iced_core", "iced_core",
"once_cell", "once_cell",
@ -2666,6 +2682,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_tiny_skia" name = "iced_tiny_skia"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"cosmic-text", "cosmic-text",
@ -2683,6 +2700,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_wgpu" name = "iced_wgpu"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"bytemuck", "bytemuck",
@ -2702,6 +2720,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_widget" name = "iced_widget"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"iced_renderer", "iced_renderer",
"iced_runtime", "iced_runtime",
@ -2957,6 +2976,7 @@ checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]] [[package]]
name = "libcosmic" name = "libcosmic"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#994e93d6d2f90f947d56376094eb19877d708063"
dependencies = [ dependencies = [
"apply", "apply",
"ashpd", "ashpd",
@ -3141,9 +3161,9 @@ dependencies = [
[[package]] [[package]]
name = "lru" name = "lru"
version = "0.11.1" version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21" checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7"
dependencies = [ dependencies = [
"hashbrown 0.14.3", "hashbrown 0.14.3",
] ]

View file

@ -44,10 +44,10 @@ tracing-log = "0.2.0"
lto = "thin" lto = "thin"
# lto = "fat" # lto = "fat"
[patch."https://github.com/pop-os/cosmic-time"] # [patch."https://github.com/pop-os/cosmic-time"]
cosmic-time = { path = "../cosmic-time" } # cosmic-time = { path = "../cosmic-time" }
[patch."https://github.com/pop-os/libcosmic"] # [patch."https://github.com/pop-os/libcosmic"]
libcosmic = { path = "../libcosmic" } # libcosmic = { git = "https://github.com/pop-os/libcosmic//", branch = "refactor-config-watch" }
cosmic-config = { path = "../libcosmic/cosmic-config" } # cosmic-config = { git = "https://github.com/pop-os/libcosmic//", branch = "refactor-config-watch" }
[patch."https://github.com/Smithay/client-toolkit"] [patch."https://github.com/Smithay/client-toolkit"]
sctk = { git = "https://github.com/smithay/client-toolkit//", package = "smithay-client-toolkit", rev = "e63ab5f" } sctk = { git = "https://github.com/smithay/client-toolkit//", package = "smithay-client-toolkit", rev = "e63ab5f" }

View file

@ -16,6 +16,7 @@ once_cell = "1.9"
xdg = "2.4" xdg = "2.4"
tracing-subscriber.workspace = true tracing-subscriber.workspace = true
tracing-log.workspace = true tracing-log.workspace = true
tracing.workspace = true
nix = "0.26" nix = "0.26"
shlex = "1.1.0" shlex = "1.1.0"
anyhow = "1.0" anyhow = "1.0"

View file

@ -1122,7 +1122,7 @@ impl cosmic::Application for CosmicAppList {
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
Subscription::batch(vec![ Subscription::batch(vec![
wayland_subscription(self.subscription_ctr).map(Message::Wayland), wayland_subscription().map(Message::Wayland),
listen_with(|e, _| match e { listen_with(|e, _| match e {
cosmic::iced_runtime::core::Event::PlatformSpecific( cosmic::iced_runtime::core::Event::PlatformSpecific(
event::PlatformSpecific::Wayland(event::wayland::Event::Seat(e, seat)), event::PlatformSpecific::Wayland(event::wayland::Event::Seat(e, seat)),

View file

@ -11,29 +11,31 @@ use futures::{
channel::mpsc::{unbounded, UnboundedReceiver}, channel::mpsc::{unbounded, UnboundedReceiver},
SinkExt, StreamExt, SinkExt, StreamExt,
}; };
use once_cell::sync::Lazy;
use std::{fmt::Debug, hash::Hash, thread::JoinHandle}; use std::{fmt::Debug, hash::Hash, thread::JoinHandle};
use tokio::sync::Mutex;
use crate::wayland_handler::wayland_handler; use crate::wayland_handler::wayland_handler;
pub fn wayland_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>( pub static WAYLAND_RX: Lazy<Mutex<Option<UnboundedReceiver<WaylandUpdate>>>> =
id: I, Lazy::new(|| Mutex::new(None));
) -> iced::Subscription<WaylandUpdate> {
subscription::channel(id, 50, move |mut output| async move {
let mut state = State::Ready;
loop { pub fn wayland_subscription() -> iced::Subscription<WaylandUpdate> {
state = start_listening(state, &mut output).await; subscription::channel(
} std::any::TypeId::of::<WaylandUpdate>(),
}) 50,
move |mut output| async move {
let mut state = State::Waiting;
loop {
state = start_listening(state, &mut output).await;
}
},
)
} }
pub enum State { pub enum State {
Ready, Waiting,
Waiting(
UnboundedReceiver<WaylandUpdate>,
calloop::channel::Sender<WaylandRequest>,
JoinHandle<()>,
),
Finished, Finished,
} }
@ -42,28 +44,28 @@ async fn start_listening(
output: &mut futures::channel::mpsc::Sender<WaylandUpdate>, output: &mut futures::channel::mpsc::Sender<WaylandUpdate>,
) -> State { ) -> State {
match state { match state {
State::Ready => { State::Waiting => {
let (calloop_tx, calloop_rx) = calloop::channel::channel(); let mut guard = WAYLAND_RX.lock().await;
let (toplevel_tx, toplevel_rx) = unbounded(); let rx = {
let handle = std::thread::spawn(move || { if guard.is_none() {
wayland_handler(toplevel_tx, calloop_rx); let (calloop_tx, calloop_rx) = calloop::channel::channel();
}); let (toplevel_tx, toplevel_rx) = unbounded();
let tx = calloop_tx.clone(); let _ = std::thread::spawn(move || {
_ = output.send(WaylandUpdate::Init(tx)).await; wayland_handler(toplevel_tx, calloop_rx);
State::Waiting(toplevel_rx, calloop_tx, handle) });
} *guard = Some(toplevel_rx);
State::Waiting(mut rx, tx, handle) => { _ = output.send(WaylandUpdate::Init(calloop_tx)).await;
if handle.is_finished() { }
_ = output.send(WaylandUpdate::Finished).await; guard.as_mut().unwrap()
return State::Finished; };
}
match rx.next().await { match rx.next().await {
Some(u) => { Some(u) => {
_ = output.send(u).await; _ = output.send(u).await;
State::Waiting(rx, tx, handle) State::Waiting
} }
None => { None => {
_ = output.send(WaylandUpdate::Finished).await; _ = output.send(WaylandUpdate::Finished).await;
tracing::error!("Wayland handler thread died");
State::Finished State::Finished
} }
} }

View file

@ -43,8 +43,7 @@ mod pulse;
const VERSION: &str = env!("CARGO_PKG_VERSION"); const VERSION: &str = env!("CARGO_PKG_VERSION");
#[tokio::main(flavor = "current_thread")] pub fn main() -> cosmic::iced::Result {
pub async fn main() -> cosmic::iced::Result {
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
let _ = tracing_log::LogTracer::init(); let _ = tracing_log::LogTracer::init();

View file

@ -4,6 +4,7 @@ use std::{rc::Rc, thread};
extern crate libpulse_binding as pulse; extern crate libpulse_binding as pulse;
use cosmic::iced::{self, subscription}; use cosmic::iced::{self, subscription};
use cosmic::iced_futures::futures::{self, SinkExt}; use cosmic::iced_futures::futures::{self, SinkExt};
use cosmic_time::once_cell::sync::Lazy;
//use futures::channel::mpsc; //use futures::channel::mpsc;
use libpulse_binding::{ use libpulse_binding::{
callbacks::ListResult, callbacks::ListResult,
@ -16,6 +17,10 @@ use libpulse_binding::{
proplist::Proplist, proplist::Proplist,
volume::ChannelVolumes, volume::ChannelVolumes,
}; };
use tokio::sync::{mpsc, Mutex};
pub static FROM_PULSE: Lazy<Mutex<Option<(mpsc::Receiver<Message>, mpsc::Sender<Message>)>>> =
Lazy::new(|| Mutex::new(None));
pub fn connect() -> iced::Subscription<Event> { pub fn connect() -> iced::Subscription<Event> {
struct SomeWorker; struct SomeWorker;
@ -24,7 +29,7 @@ pub fn connect() -> iced::Subscription<Event> {
std::any::TypeId::of::<SomeWorker>(), std::any::TypeId::of::<SomeWorker>(),
50, 50,
move |mut output| async move { move |mut output| async move {
let mut state = State::Init; let mut state = State::Connecting;
loop { loop {
state = start_listening(state, &mut output).await; state = start_listening(state, &mut output).await;
@ -38,34 +43,50 @@ async fn start_listening(
output: &mut futures::channel::mpsc::Sender<Event>, output: &mut futures::channel::mpsc::Sender<Event>,
) -> State { ) -> State {
match state { match state {
State::Init => {
let PulseHandle {
to_pulse,
from_pulse,
} = PulseHandle::new();
_ = output.send(Event::Init(Connection(to_pulse))).await;
State::Connecting(from_pulse)
}
// Waiting for Connection to succeed // Waiting for Connection to succeed
State::Connecting(mut from_pulse) => match from_pulse.recv().await { State::Connecting => {
Some(Message::Connected) => { let mut guard = FROM_PULSE.lock().await;
_ = output.send(Event::Connected).await; let (from_pulse, to_pulse) = {
State::Connected(from_pulse) if guard.is_none() {
} let PulseHandle {
Some(Message::Disconnected) => { to_pulse,
_ = output.send(Event::Disconnected).await; from_pulse,
} = PulseHandle::new();
_ = output.send(Event::Init(Connection(to_pulse.clone()))).await;
State::Connecting(from_pulse) *guard = Some((from_pulse, to_pulse));
}
guard.as_mut().unwrap()
};
to_pulse
.send(Message::UpdateConnection)
.await
.expect("Failed to request connection update");
match from_pulse.recv().await {
Some(Message::Connected) => {
_ = output.send(Event::Connected).await;
State::Connected
}
Some(Message::Disconnected) => {
_ = output.send(Event::Disconnected).await;
State::Connecting
}
Some(m) => {
tracing::error!("Unexpected message: {:?}", m);
State::Connecting
}
None => {
panic!("Pulse Sender dropped, something has gone wrong!");
}
} }
Some(m) => { }
panic!("Unexpected message: {:?}", m); State::Connected => {
} let mut guard = FROM_PULSE.lock().await;
None => { let Some((from_pulse, _)) = guard.as_mut() else {
panic!("Pulse Sender dropped, something has gone wrong!"); return State::Connecting;
} };
},
State::Connected(mut from_pulse) => {
// This is where we match messages from the pulse server to pass to the gui // This is where we match messages from the pulse server to pass to the gui
match from_pulse.recv().await { match from_pulse.recv().await {
Some(Message::SetSinks(sinks)) => { Some(Message::SetSinks(sinks)) => {
@ -73,35 +94,35 @@ async fn start_listening(
.send(Event::MessageReceived(Message::SetSinks(sinks))) .send(Event::MessageReceived(Message::SetSinks(sinks)))
.await; .await;
State::Connected(from_pulse) State::Connected
} }
Some(Message::SetSources(sources)) => { Some(Message::SetSources(sources)) => {
_ = output _ = output
.send(Event::MessageReceived(Message::SetSources(sources))) .send(Event::MessageReceived(Message::SetSources(sources)))
.await; .await;
State::Connected(from_pulse) State::Connected
} }
Some(Message::SetDefaultSink(sink)) => { Some(Message::SetDefaultSink(sink)) => {
_ = output _ = output
.send(Event::MessageReceived(Message::SetDefaultSink(sink))) .send(Event::MessageReceived(Message::SetDefaultSink(sink)))
.await; .await;
State::Connected(from_pulse) State::Connected
} }
Some(Message::SetDefaultSource(source)) => { Some(Message::SetDefaultSource(source)) => {
_ = output _ = output
.send(Event::MessageReceived(Message::SetDefaultSource(source))) .send(Event::MessageReceived(Message::SetDefaultSource(source)))
.await; .await;
State::Connected(from_pulse) State::Connected
} }
Some(Message::Disconnected) => { Some(Message::Disconnected) => {
_ = output.send(Event::Disconnected).await; _ = output.send(Event::Disconnected).await;
State::Connecting(from_pulse) State::Connecting
} }
None => { None => {
_ = output.send(Event::Disconnected).await; _ = output.send(Event::Disconnected).await;
State::Connecting(from_pulse) State::Connecting
} }
_ => State::Connected(from_pulse), _ => State::Connected,
} }
} }
} }
@ -109,9 +130,8 @@ async fn start_listening(
// #[derive(Debug)] // #[derive(Debug)]
enum State { enum State {
Init, Connecting,
Connecting(tokio::sync::mpsc::Receiver<Message>), Connected,
Connected(tokio::sync::mpsc::Receiver<Message>),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -123,7 +143,7 @@ pub enum Event {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Connection(tokio::sync::mpsc::Sender<Message>); pub struct Connection(mpsc::Sender<Message>);
impl Connection { impl Connection {
pub fn send(&mut self, message: Message) { pub fn send(&mut self, message: Message) {
@ -160,10 +180,7 @@ impl PulseHandle {
pub fn new() -> Self { pub fn new() -> Self {
let (to_pulse, mut to_pulse_recv) = tokio::sync::mpsc::channel(10); let (to_pulse, mut to_pulse_recv) = tokio::sync::mpsc::channel(10);
let (from_pulse_send, from_pulse) = tokio::sync::mpsc::channel(10); let (from_pulse_send, from_pulse) = tokio::sync::mpsc::channel(10);
// get initial connection status
to_pulse
.try_send(Message::UpdateConnection)
.expect("Failed to send initial connection update message");
// this thread should complete by pushing a completed message, // this thread should complete by pushing a completed message,
// or fail message. This should never complete/fail without pushing // or fail message. This should never complete/fail without pushing
// a message. This lets the iced subscription go to sleep while init // a message. This lets the iced subscription go to sleep while init
@ -197,7 +214,6 @@ impl PulseHandle {
.await .await
{ {
tracing::error!("ERROR! {}", err); tracing::error!("ERROR! {}", err);
break;
} }
} }
Err(_) => Self::send_disconnected(&from_pulse_send).await, Err(_) => Self::send_disconnected(&from_pulse_send).await,
@ -215,7 +231,6 @@ impl PulseHandle {
.await .await
{ {
tracing::error!("ERROR! {}", err); tracing::error!("ERROR! {}", err);
break;
} }
} }
Err(e) => { Err(e) => {
@ -235,7 +250,6 @@ impl PulseHandle {
from_pulse_send.send(Message::SetSinks(sinks)).await from_pulse_send.send(Message::SetSinks(sinks)).await
{ {
tracing::error!("ERROR! {}", err); tracing::error!("ERROR! {}", err);
break;
} }
} }
Err(_) => Self::send_disconnected(&from_pulse_send).await, Err(_) => Self::send_disconnected(&from_pulse_send).await,
@ -252,7 +266,6 @@ impl PulseHandle {
from_pulse_send.send(Message::SetSources(sinks)).await from_pulse_send.send(Message::SetSources(sinks)).await
{ {
tracing::error!("ERROR! {}", err); tracing::error!("ERROR! {}", err);
break;
} }
} }
Err(_) => Self::send_disconnected(&from_pulse_send).await, Err(_) => Self::send_disconnected(&from_pulse_send).await,
@ -278,13 +291,13 @@ impl PulseHandle {
server.is_some() server.is_some()
); );
if let Some(mut cur_server) = server.take() { if let Some(mut cur_server) = server.take() {
tracing::trace!("getting server info...");
if cur_server.get_server_info().is_err() { if cur_server.get_server_info().is_err() {
tracing::warn!("got error, server must be disconnected..."); tracing::warn!("got error, server must be disconnected...");
Self::send_disconnected(&from_pulse_send).await; Self::send_disconnected(&from_pulse_send).await;
} else { } else {
tracing::trace!("got server info, still connected..."); tracing::info!("got server info, still connected...");
server = Some(cur_server); server = Some(cur_server);
Self::send_connected(&from_pulse_send).await;
} }
} else { } else {
match PulseServer::connect().and_then(|server| server.init()) { match PulseServer::connect().and_then(|server| server.init()) {
@ -298,6 +311,7 @@ impl PulseHandle {
"Failed to connect to server: {:?}", "Failed to connect to server: {:?}",
err err
); );
Self::send_disconnected(&from_pulse_send).await;
} }
} }
} }

View file

@ -15,6 +15,7 @@ use cosmic::iced::{
use cosmic::iced_core::alignment::Horizontal; use cosmic::iced_core::alignment::Horizontal;
use cosmic::Command; use cosmic::Command;
use cosmic::iced_futures::futures::executor::block_on;
use cosmic::iced_style::application; use cosmic::iced_style::application;
use cosmic::iced_widget::{scrollable, Column}; use cosmic::iced_widget::{scrollable, Column};
@ -26,11 +27,11 @@ use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::PathBuf; use std::path::PathBuf;
use subscriptions::notifications::NotificationsAppletProxy;
use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::Sender;
use tracing::info; use tracing::info;
#[tokio::main(flavor = "current_thread")] pub fn main() -> cosmic::iced::Result {
pub async fn main() -> cosmic::iced::Result {
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
let _ = tracing_log::LogTracer::init(); let _ = tracing_log::LogTracer::init();
// Prepare i18n // Prepare i18n
@ -43,7 +44,6 @@ pub async fn main() -> cosmic::iced::Result {
static DO_NOT_DISTURB: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique); static DO_NOT_DISTURB: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
#[derive(Default)]
struct Notifications { struct Notifications {
core: cosmic::app::Core, core: cosmic::app::Core,
config: NotificationsConfig, config: NotificationsConfig,
@ -55,6 +55,7 @@ struct Notifications {
dbus_sender: Option<Sender<subscriptions::dbus::Input>>, dbus_sender: Option<Sender<subscriptions::dbus::Input>>,
cards: Vec<(id::Cards, Vec<Notification>, bool, String, String, String)>, cards: Vec<(id::Cards, Vec<Notification>, bool, String, String, String)>,
token_tx: Option<calloop::channel::Sender<TokenRequest>>, token_tx: Option<calloop::channel::Sender<TokenRequest>>,
proxy: NotificationsAppletProxy<'static>,
} }
impl Notifications { impl Notifications {
@ -132,7 +133,14 @@ impl cosmic::Application for Notifications {
core, core,
config_helper: helper, config_helper: helper,
config, config,
..Default::default() icon_name: Default::default(),
popup: None,
timeline: Default::default(),
dbus_sender: Default::default(),
cards: Vec::new(),
token_tx: Default::default(),
proxy: block_on(crate::subscriptions::notifications::get_proxy())
.expect("Failed to get proxy"),
}; };
_self.update_icon(); _self.update_icon();
(_self, Command::none()) (_self, Command::none())
@ -152,25 +160,20 @@ impl cosmic::Application for Notifications {
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
Subscription::batch(vec![ Subscription::batch(vec![
config_subscription::<u64, NotificationsConfig>( self.core
0, .watch_config(cosmic_notifications_config::ID.into())
cosmic_notifications_config::ID.into(), .map(|res| {
NotificationsConfig::version(), for err in res.errors {
)
.map(|(_, res)| match res {
Ok(config) => Message::Config(config),
Err((errors, config)) => {
for err in errors {
tracing::error!("{:?}", err); tracing::error!("{:?}", err);
} }
Message::Config(config) Message::Config(res.config)
} }),
}),
self.timeline self.timeline
.as_subscription() .as_subscription()
.map(|(_, now)| Message::Frame(now)), .map(|(_, now)| Message::Frame(now)),
subscriptions::dbus::proxy().map(Message::DbusEvent), subscriptions::dbus::proxy().map(Message::DbusEvent),
subscriptions::notifications::notifications().map(Message::NotificationEvent), subscriptions::notifications::notifications(self.proxy.clone())
.map(Message::NotificationEvent),
activation_token_subscription(0).map(Message::Token), activation_token_subscription(0).map(Message::Token),
]) ])
} }

View file

@ -22,7 +22,7 @@ pub enum State {
Finished, Finished,
} }
pub fn notifications() -> Subscription<Notification> { pub fn notifications(proxy: NotificationsAppletProxy<'static>) -> Subscription<Notification> {
struct SomeWorker; struct SomeWorker;
subscription::channel( subscription::channel(
@ -107,7 +107,7 @@ trait NotificationsApplet {
) -> zbus::Result<()>; ) -> zbus::Result<()>;
} }
async fn get_proxy() -> anyhow::Result<NotificationsAppletProxy<'static>> { pub async fn get_proxy() -> anyhow::Result<NotificationsAppletProxy<'static>> {
let raw_fd = std::env::var("COSMIC_NOTIFICATIONS")?; let raw_fd = std::env::var("COSMIC_NOTIFICATIONS")?;
let raw_fd = raw_fd.parse::<RawFd>()?; let raw_fd = raw_fd.parse::<RawFd>()?;

View file

@ -16,6 +16,7 @@ once_cell = "1.9"
futures = "0.3.21" futures = "0.3.21"
xdg = "2.4.0" xdg = "2.4.0"
anyhow = "1.0" anyhow = "1.0"
tokio = "1.35"
# Application i18n # Application i18n
i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-requester"] } i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-requester"] }
i18n-embed-fl = "0.6.4" i18n-embed-fl = "0.6.4"

View file

@ -222,7 +222,7 @@ impl cosmic::Application for IcedWorkspacesApplet {
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
Subscription::batch(vec![ Subscription::batch(vec![
workspaces(0).map(Message::WorkspaceUpdate), workspaces().map(Message::WorkspaceUpdate),
event::listen_with(|e, _| match e { event::listen_with(|e, _| match e {
Mouse(mouse::Event::WheelScrolled { delta }) => Some(Message::WheelScrolled(delta)), Mouse(mouse::Event::WheelScrolled { delta }) => Some(Message::WheelScrolled(delta)),
_ => None, _ => None,

View file

@ -5,7 +5,11 @@ use cosmic::iced::{
futures::{channel::mpsc, SinkExt, StreamExt}, futures::{channel::mpsc, SinkExt, StreamExt},
subscription, subscription,
}; };
use std::hash::Hash; use once_cell::sync::Lazy;
use tokio::sync::Mutex;
pub static WAYLAND_RX: Lazy<Mutex<Option<mpsc::Receiver<WorkspaceList>>>> =
Lazy::new(|| Mutex::new(None));
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum WorkspacesUpdate { pub enum WorkspacesUpdate {
@ -14,16 +18,18 @@ pub enum WorkspacesUpdate {
Errored, Errored,
} }
pub fn workspaces<I: 'static + Hash + Copy + Send + Sync>( pub fn workspaces() -> iced::Subscription<WorkspacesUpdate> {
id: I, subscription::channel(
) -> iced::Subscription<WorkspacesUpdate> { std::any::TypeId::of::<WorkspacesUpdate>(),
subscription::channel(id, 50, move |mut output| async move { 50,
let mut state = State::Ready; move |mut output| async move {
let mut state = State::Waiting;
loop { loop {
state = start_listening(state, &mut output).await; state = start_listening(state, &mut output).await;
} }
}) },
)
} }
async fn start_listening( async fn start_listening(
@ -31,22 +37,23 @@ async fn start_listening(
output: &mut futures::channel::mpsc::Sender<WorkspacesUpdate>, output: &mut futures::channel::mpsc::Sender<WorkspacesUpdate>,
) -> State { ) -> State {
match state { match state {
State::Ready => { State::Waiting => {
if let Ok(watcher) = WorkspacesWatcher::new() { let mut guard = WAYLAND_RX.lock().await;
_ = output let rx = {
.send(WorkspacesUpdate::Started(watcher.get_sender())) if guard.is_none() {
.await; if let Ok(WorkspacesWatcher { rx, tx }) = WorkspacesWatcher::new() {
State::Waiting(watcher) *guard = Some(rx);
} else { _ = output.send(WorkspacesUpdate::Started(tx)).await;
_ = output.send(WorkspacesUpdate::Errored).await; } else {
_ = output.send(WorkspacesUpdate::Errored).await;
State::Error return State::Error;
} }
} }
State::Waiting(mut t) => { guard.as_mut().unwrap()
if let Some(w) = t.workspaces().await { };
if let Some(w) = rx.next().await {
_ = output.send(WorkspacesUpdate::Workspaces(w)).await; _ = output.send(WorkspacesUpdate::Workspaces(w)).await;
State::Waiting(t) State::Waiting
} else { } else {
_ = output.send(WorkspacesUpdate::Errored).await; _ = output.send(WorkspacesUpdate::Errored).await;
State::Error State::Error
@ -57,8 +64,7 @@ async fn start_listening(
} }
pub enum State { pub enum State {
Ready, Waiting,
Waiting(WorkspacesWatcher),
Error, Error,
} }