1
1
// SPDX-License-Identifier: GPL-3.0-only
2
2
3
- use std:: collections:: HashMap ;
3
+ use std:: {
4
+ collections:: HashMap ,
5
+ sync:: { Arc , Condvar , Mutex } ,
6
+ } ;
4
7
8
+ use ashpd:: enumflags2:: BitFlags ;
5
9
use cosmic:: { iced:: window, widget} ;
10
+ use cosmic_protocols:: toplevel_info:: v1:: client:: zcosmic_toplevel_handle_v1;
6
11
use futures:: { FutureExt , TryFutureExt } ;
7
12
use tokio:: sync:: mpsc:: Sender ;
8
13
use zbus:: { fdo, object_server:: SignalContext , zvariant} ;
9
14
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 } ;
14
16
15
17
/// Background portal backend
16
18
///
17
19
/// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.impl.portal.Background.html
18
20
pub struct Background {
21
+ wayland_helper : WaylandHelper ,
19
22
tx : Sender < subscription:: Event > ,
20
23
}
21
24
22
25
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
+ }
25
57
}
26
58
}
27
59
28
60
#[ zbus:: interface( name = "org.freedesktop.impl.portal.Background" ) ]
29
61
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)
38
95
}
39
96
40
97
/// Notifies the user that an app is running in the background
41
98
async fn notify_background (
42
99
& self ,
43
- // #[zbus(connection)] connection: &zbus::Connection,
44
- #[ zbus( signal_context) ] context : SignalContext < ' _ > ,
45
100
handle : zvariant:: ObjectPath < ' _ > ,
46
101
app_id : String ,
47
102
name : String ,
@@ -139,15 +194,8 @@ impl Background {
139
194
pub async fn running_applications_changed ( context : & SignalContext < ' _ > ) -> zbus:: Result < ( ) > ;
140
195
}
141
196
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
-
149
197
#[ derive( Clone , Copy , Debug , PartialEq , Eq , serde:: Serialize , zvariant:: Type ) ]
150
- #[ zvariant( signature = "u " ) ]
198
+ #[ zvariant( signature = "v " ) ]
151
199
pub enum AppStatus {
152
200
/// No open windows
153
201
Background = 0 ,
@@ -164,7 +212,7 @@ pub struct NotifyBackgroundResult {
164
212
result : PermissionResponse ,
165
213
}
166
214
167
- /// Response for apps requesting to run in the background
215
+ /// Response for apps requesting to run in the background for [`Background::notify_background`]
168
216
#[ derive( Clone , Copy , Debug , PartialEq , Eq , serde:: Serialize , zvariant:: Type ) ]
169
217
#[ zvariant( signature = "u" ) ]
170
218
pub enum PermissionResponse {
0 commit comments