Skip to content

Commit 5104351

Browse files
Retrieve new toplevels from cosmic-comp
Implements emitting a signal for `Background::RunningApplicationsChanged` in response to toplevel changes (created, destroyed, updated). `Background::GetAppState` should work now as well
1 parent 24c4359 commit 5104351

File tree

4 files changed

+103
-28
lines changed

4 files changed

+103
-28
lines changed

src/app.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,8 @@ impl cosmic::Application for CosmicPortal {
221221
subscription::Event::Config(config) => self.update(Msg::ConfigSubUpdate(config)),
222222
subscription::Event::Accent(_)
223223
| subscription::Event::IsDark(_)
224-
| subscription::Event::HighContrast(_) => cosmic::iced::Command::none(),
224+
| subscription::Event::HighContrast(_)
225+
| subscription::Event::BackgroundToplevels => cosmic::iced::Command::none(),
225226
subscription::Event::Init(tx) => {
226227
self.tx = Some(tx);
227228
Command::none()

src/background.rs

Lines changed: 74 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,102 @@
11
// SPDX-License-Identifier: GPL-3.0-only
22

3-
use std::collections::HashMap;
3+
use std::{
4+
collections::HashMap,
5+
sync::{Arc, Condvar, Mutex},
6+
};
47

8+
use ashpd::enumflags2::BitFlags;
59
use cosmic::{iced::window, widget};
10+
use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1;
611
use futures::{FutureExt, TryFutureExt};
712
use tokio::sync::mpsc::Sender;
813
use zbus::{fdo, object_server::SignalContext, zvariant};
914

10-
use crate::{app::CosmicPortal, fl, subscription, PortalResponse};
11-
12-
const POP_SHELL_DEST: &str = "com.System76.PopShell";
13-
const POP_SHELL_PATH: &str = "/com.System76.PopShell";
15+
use crate::{app::CosmicPortal, fl, subscription, wayland::WaylandHelper, PortalResponse};
1416

1517
/// Background portal backend
1618
///
1719
/// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.impl.portal.Background.html
1820
pub struct Background {
21+
wayland_helper: WaylandHelper,
1922
tx: Sender<subscription::Event>,
2023
}
2124

2225
impl Background {
23-
pub const fn new(tx: Sender<subscription::Event>) -> Self {
24-
Self { tx }
26+
pub fn new(wayland_helper: WaylandHelper, tx: Sender<subscription::Event>) -> Self {
27+
let toplevel_signal = wayland_helper.toplevel_signal();
28+
let toplevel_tx = tx.clone();
29+
std::thread::Builder::new()
30+
.name("background-toplevel-updates".into())
31+
.spawn(move || Background::toplevel_signal(toplevel_signal, toplevel_tx))
32+
.expect("Spawning toplevels update thread should succeed");
33+
34+
Self { wayland_helper, tx }
35+
}
36+
37+
/// Trigger [`Background::running_applications_changed`] on toplevel updates
38+
fn toplevel_signal(signal: Arc<(Mutex<bool>, Condvar)>, tx: Sender<subscription::Event>) {
39+
loop {
40+
let (lock, cvar) = &*signal;
41+
let mut updated = lock.lock().unwrap();
42+
43+
log::debug!("[background] Waiting for toplevel updates");
44+
while !*updated {
45+
updated = cvar.wait(updated).unwrap();
46+
}
47+
48+
log::debug!(
49+
"[background] Emitting RunningApplicationsChanged in response to toplevel updates"
50+
);
51+
debug_assert!(*updated);
52+
*updated = false;
53+
if let Err(e) = tx.blocking_send(subscription::Event::BackgroundToplevels) {
54+
log::warn!("[background] Failed sending event to trigger RunningApplicationsChanged: {e:?}");
55+
}
56+
}
2557
}
2658
}
2759

2860
#[zbus::interface(name = "org.freedesktop.impl.portal.Background")]
2961
impl Background {
30-
/// Current status on running apps
31-
async fn get_app_state(
32-
&self,
33-
// #[zbus(connection)] connection: &zbus::Connection,
34-
) -> fdo::Result<HashMap<String, AppStatus>> {
35-
// TODO: Subscribe to Wayland window open events for running apps
36-
log::warn!("[background] GetAppState is currently unimplemented");
37-
Ok(HashMap::default())
62+
/// Status on running apps (active, running, or background)
63+
async fn get_app_state(&self) -> fdo::Result<HashMap<String, AppStatus>> {
64+
let toplevels: HashMap<_, _> = self
65+
.wayland_helper
66+
.toplevels()
67+
.into_iter()
68+
.map(|(_, info)| {
69+
let status = if info
70+
.state
71+
.contains(&zcosmic_toplevel_handle_v1::State::Activated)
72+
{
73+
AppStatus::Active
74+
} else if !info.state.is_empty() {
75+
AppStatus::Running
76+
} else {
77+
// xxx Is this the correct way to determine if a program is running in the
78+
// background? If a toplevel exists but isn't running, activated, et cetera,
79+
// then it logically must be in the background (?)
80+
AppStatus::Background
81+
};
82+
83+
(info.app_id, status)
84+
})
85+
.collect();
86+
87+
log::debug!(
88+
"[background] GetAppState returning {} toplevels",
89+
toplevels.len()
90+
);
91+
#[cfg(debug_assertions)]
92+
log::trace!("[background] App status: {toplevels:#?}");
93+
94+
Ok(toplevels)
3895
}
3996

4097
/// Notifies the user that an app is running in the background
4198
async fn notify_background(
4299
&self,
43-
// #[zbus(connection)] connection: &zbus::Connection,
44-
#[zbus(signal_context)] context: SignalContext<'_>,
45100
handle: zvariant::ObjectPath<'_>,
46101
app_id: String,
47102
name: String,
@@ -139,15 +194,8 @@ impl Background {
139194
pub async fn running_applications_changed(context: &SignalContext<'_>) -> zbus::Result<()>;
140195
}
141196

142-
/// Information on running apps
143-
// #[derive(Clone, Debug, zvariant::SerializeDict, zvariant::Type)]
144-
// #[zvariant(signature = "a{sv}")]
145-
// pub struct GetAppState {
146-
// apps: HashMap<String, AppStatus>,
147-
// }
148-
149197
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, zvariant::Type)]
150-
#[zvariant(signature = "u")]
198+
#[zvariant(signature = "v")]
151199
pub enum AppStatus {
152200
/// No open windows
153201
Background = 0,
@@ -164,7 +212,7 @@ pub struct NotifyBackgroundResult {
164212
result: PermissionResponse,
165213
}
166214

167-
/// Response for apps requesting to run in the background
215+
/// Response for apps requesting to run in the background for [`Background::notify_background`]
168216
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, zvariant::Type)]
169217
#[zvariant(signature = "u")]
170218
pub enum PermissionResponse {

src/subscription.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub enum Event {
2525
CancelScreencast(zvariant::ObjectPath<'static>),
2626
Background(crate::background::Args),
2727
BackgroundGetAppPerm(String, Sender<crate::background::ConfigAppPerm>),
28+
BackgroundToplevels,
2829
Accent(Srgba),
2930
IsDark(bool),
3031
HighContrast(bool),
@@ -84,6 +85,7 @@ impl Debug for Event {
8485
.field(app_id)
8586
.field(tx)
8687
.finish(),
88+
Event::BackgroundToplevels => f.debug_tuple("BackgroundToplevels").finish(),
8789
Event::Accent(a) => a.fmt(f),
8890
Event::IsDark(t) => t.fmt(f),
8991
Event::HighContrast(c) => c.fmt(f),
@@ -144,7 +146,10 @@ pub(crate) async fn process_changes(
144146
let connection = zbus::ConnectionBuilder::session()?
145147
.name(DBUS_NAME)?
146148
.serve_at(DBUS_PATH, Access::new(wayland_helper.clone(), tx.clone()))?
147-
.serve_at(DBUS_PATH, Background::new(tx.clone()))?
149+
.serve_at(
150+
DBUS_PATH,
151+
Background::new(wayland_helper.clone(), tx.clone()),
152+
)?
148153
.serve_at(DBUS_PATH, FileChooser::new(tx.clone()))?
149154
.serve_at(
150155
DBUS_PATH,
@@ -199,6 +204,14 @@ pub(crate) async fn process_changes(
199204
log::error!("Error sending background config request event: {:?}", err);
200205
}
201206
}
207+
Event::BackgroundToplevels => {
208+
let background = conn
209+
.object_server()
210+
.interface::<_, Background>(DBUS_PATH)
211+
.await?;
212+
Background::running_applications_changed(background.signal_context())
213+
.await?;
214+
}
202215
Event::Accent(a) => {
203216
let object_server = conn.object_server();
204217
let iface_ref = object_server.interface::<_, Settings>(DBUS_PATH).await?;

src/wayland/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ struct WaylandHelperInner {
8787
output_infos: Mutex<HashMap<wl_output::WlOutput, OutputInfo>>,
8888
output_toplevels: Mutex<HashMap<wl_output::WlOutput, Vec<ZcosmicToplevelHandleV1>>>,
8989
toplevels: Mutex<Vec<(ZcosmicToplevelHandleV1, ToplevelInfo)>>,
90+
toplevel_update: Arc<(Mutex<bool>, Condvar)>,
9091
qh: QueueHandle<AppData>,
9192
screencopy_manager: zcosmic_screencopy_manager_v2::ZcosmicScreencopyManagerV2,
9293
output_source_manager: ZcosmicOutputImageSourceManagerV1,
@@ -167,6 +168,12 @@ impl AppData {
167168
.toplevels()
168169
.filter_map(|(handle, info)| Some((handle.clone(), info?.clone())))
169170
.collect();
171+
172+
// Signal that toplevels were updated; the actual updates are unimportant here
173+
let (lock, cvar) = &*self.wayland_helper.inner.toplevel_update;
174+
let mut updated = lock.lock().unwrap();
175+
*updated = true;
176+
cvar.notify_all();
170177
}
171178
}
172179

@@ -250,13 +257,15 @@ impl WaylandHelper {
250257
let screencopy_state = ScreencopyState::new(&globals, &qh);
251258
let shm_state = Shm::bind(&globals, &qh).unwrap();
252259
let zwp_dmabuf = globals.bind(&qh, 4..=4, sctk::globals::GlobalData).unwrap();
260+
let toplevel_update = Arc::new((Mutex::new(false), Condvar::new()));
253261
let wayland_helper = WaylandHelper {
254262
inner: Arc::new(WaylandHelperInner {
255263
conn,
256264
outputs: Mutex::new(Vec::new()),
257265
output_infos: Mutex::new(HashMap::new()),
258266
output_toplevels: Mutex::new(HashMap::new()),
259267
toplevels: Mutex::new(Vec::new()),
268+
toplevel_update,
260269
qh: qh.clone(),
261270
screencopy_manager: screencopy_state.screencopy_manager.clone(),
262271
output_source_manager: screencopy_state.output_source_manager.clone().unwrap(),
@@ -304,6 +313,10 @@ impl WaylandHelper {
304313
self.inner.toplevels.lock().unwrap().clone()
305314
}
306315

316+
pub fn toplevel_signal(&self) -> Arc<(Mutex<bool>, Condvar)> {
317+
Arc::clone(&self.inner.toplevel_update)
318+
}
319+
307320
pub fn output_info(&self, output: &wl_output::WlOutput) -> Option<OutputInfo> {
308321
self.inner.output_infos.lock().unwrap().get(output).cloned()
309322
}

0 commit comments

Comments
 (0)