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:
parent
ebe688c747
commit
d4e0dd8fb8
12 changed files with 184 additions and 138 deletions
30
Cargo.lock
generated
30
Cargo.lock
generated
|
|
@ -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",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
10
Cargo.toml
10
Cargo.toml
|
|
@ -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" }
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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)),
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>()?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue