2
2
3
3
use std:: {
4
4
borrow:: Cow ,
5
+ collections:: HashSet ,
6
+ hash:: { Hash , Hasher } ,
5
7
io,
6
8
path:: Path ,
7
9
sync:: { Arc , Condvar , Mutex } ,
@@ -21,7 +23,7 @@ use zbus::{fdo, object_server::SignalContext, zvariant};
21
23
use crate :: {
22
24
app:: CosmicPortal ,
23
25
config:: { self , background:: PermissionDialog } ,
24
- fl, subscription,
26
+ fl, subscription, systemd ,
25
27
wayland:: WaylandHelper ,
26
28
PortalResponse ,
27
29
} ;
@@ -80,7 +82,7 @@ impl Background {
80
82
/// The primary purpose of this function is to ease error handling.
81
83
async fn write_autostart (
82
84
autostart_entry : & Path ,
83
- desktop_entry : & freedesktop_desktop_entry:: DesktopEntry < ' _ > ,
85
+ desktop_entry : & freedesktop_desktop_entry:: DesktopEntry ,
84
86
) -> io:: Result < ( ) > {
85
87
let mut file = fs:: OpenOptions :: new ( )
86
88
. create ( true )
@@ -92,46 +94,64 @@ impl Background {
92
94
. await ?;
93
95
94
96
file. write_all ( desktop_entry. to_string ( ) . as_bytes ( ) ) . await ?;
95
- /// Shouldn't be needed, but the file never seemed to flush to disk until I did it manually
97
+ // Shouldn't be needed, but the file never seemed to flush to disk until I did it manually
96
98
file. flush ( ) . await
97
99
}
98
100
}
99
101
100
102
#[ zbus:: interface( name = "org.freedesktop.impl.portal.Background" ) ]
101
103
impl Background {
102
104
/// Status on running apps (active, running, or background)
103
- async fn get_app_state ( & self ) -> fdo:: Result < AppStates > {
104
- let apps: Vec < _ > = self
105
+ async fn get_app_state (
106
+ & self ,
107
+ #[ zbus( connection) ] connection : & zbus:: Connection ,
108
+ ) -> fdo:: Result < AppStates > {
109
+ let apps: HashSet < _ > = self
105
110
. wayland_helper
106
111
. toplevels ( )
107
112
. into_iter ( )
108
- . map ( |( _, info) | {
113
+ // Evaluate apps with open top levels first as our initial state
114
+ . map ( |info| {
109
115
let status = if info
110
116
. state
111
117
. contains ( & zcosmic_toplevel_handle_v1:: State :: Activated )
112
118
{
119
+ // Focused top levels
113
120
AppStatus :: Active
114
- } else if !info. state . is_empty ( ) {
115
- AppStatus :: Running
116
121
} else {
117
- // xxx Is this the correct way to determine if a program is running in the
118
- // background? If a toplevel exists but isn't running, activated, et cetera,
119
- // then it logically must be in the background (?)
120
- AppStatus :: Background
122
+ // Unfocused top levels
123
+ AppStatus :: Running
121
124
} ;
122
125
123
126
AppState {
124
127
app_id : info. app_id ,
125
128
status,
126
129
}
127
130
} )
131
+ . chain (
132
+ systemd:: Systemd1Proxy :: new ( connection)
133
+ . await ?
134
+ . list_units ( )
135
+ . await ?
136
+ . into_iter ( )
137
+ // Apps launched by COSMIC/Flatpak are considered to be running in the
138
+ // background by default as they don't have open top levels
139
+ . filter_map ( |unit| {
140
+ unit. cosmic_flatpak_name ( ) . map ( |app_id| AppState {
141
+ app_id : app_id. to_owned ( ) ,
142
+ status : AppStatus :: Background ,
143
+ } )
144
+ } ) ,
145
+ )
128
146
. collect ( ) ;
129
147
130
- log:: debug!( "GetAppState returning {} toplevels " , apps. len( ) ) ;
148
+ log:: debug!( "GetAppState is returning {} open apps " , apps. len( ) ) ;
131
149
#[ cfg( debug_assertions) ]
132
- log:: trace!( "App status : {apps:#?}" ) ;
150
+ log:: trace!( "App statuses : {apps:#?}" ) ;
133
151
134
- Ok ( AppStates { apps } )
152
+ Ok ( AppStates {
153
+ apps : apps. into_iter ( ) . collect ( ) ,
154
+ } )
135
155
}
136
156
137
157
/// Notifies the user that an app is running in the background
@@ -145,7 +165,10 @@ impl Background {
145
165
146
166
// Request a copy of the config from the main app instance
147
167
// This is also cleaner than storing the config because it's difficult to keep it
148
- // updated without synch primitives and we also avoid &mut self
168
+ // updated without synch primitives and we also avoid &mut self.
169
+ //
170
+ // &mut self with Zbus can lead to deadlocks.
171
+ // See: https://dbus2.github.io/zbus/faq.html#1-a-interface-method-that-takes-a-mut-self-argument-is-taking-too-long
149
172
let config = self . rx_conf . borrow ( ) . background ;
150
173
151
174
match config. default_perm {
@@ -192,7 +215,7 @@ impl Background {
192
215
///
193
216
/// Deprecated in terms of the portal but seemingly still in use
194
217
/// Spec: https://specifications.freedesktop.org/autostart-spec/latest/
195
- pub async fn enable_autostart (
218
+ async fn enable_autostart (
196
219
& self ,
197
220
appid : String ,
198
221
enable : bool ,
@@ -250,13 +273,13 @@ impl Background {
250
273
}
251
274
252
275
let mut autostart_fde = freedesktop_desktop_entry:: DesktopEntry {
253
- appid : Cow :: Borrowed ( & appid) ,
276
+ appid : appid. clone ( ) ,
254
277
path : Default :: default ( ) ,
255
278
groups : Default :: default ( ) ,
256
279
ubuntu_gettext_domain : None ,
257
280
} ;
258
- autostart_fde. add_desktop_entry ( "Type" , "Application" ) ;
259
- autostart_fde. add_desktop_entry ( "Name" , & appid) ;
281
+ autostart_fde. add_desktop_entry ( "Type" . into ( ) , "Application" . into ( ) ) ;
282
+ autostart_fde. add_desktop_entry ( "Name" . into ( ) , appid. clone ( ) ) ;
260
283
261
284
log:: debug!( "{appid} autostart command line: {exec:?}" ) ;
262
285
let exec = match shlex:: try_join ( exec. iter ( ) . map ( |term| term. as_str ( ) ) ) {
@@ -267,17 +290,17 @@ impl Background {
267
290
}
268
291
} ;
269
292
log:: debug!( "{appid} sanitized autostart command line: {exec}" ) ;
270
- autostart_fde. add_desktop_entry ( "Exec" , & exec) ;
293
+ autostart_fde. add_desktop_entry ( "Exec" . into ( ) , exec) ;
271
294
272
- /// xxx Replace with enumflags later when it's added as a dependency instead of adding
273
- /// it now for one bit (literally)
295
+ // TODO: Replace with enumflags later when it's added as a dependency instead of adding
296
+ // it now for one bit (literally)
274
297
let dbus_activation = flags & 0x1 == 1 ;
275
298
if dbus_activation {
276
- autostart_fde. add_desktop_entry ( "DBusActivatable" , "true" ) ;
299
+ autostart_fde. add_desktop_entry ( "DBusActivatable" . into ( ) , "true" . into ( ) ) ;
277
300
}
278
301
279
302
// GNOME and KDE both set this key
280
- autostart_fde. add_desktop_entry ( "X-Flatpak" , & appid) ;
303
+ autostart_fde. add_desktop_entry ( "X-Flatpak" . into ( ) , appid. clone ( ) ) ;
281
304
282
305
Self :: write_autostart ( & launch_entry, & autostart_fde)
283
306
. inspect_err ( |e| {
@@ -297,9 +320,9 @@ impl Background {
297
320
pub async fn running_applications_changed ( context : & SignalContext < ' _ > ) -> zbus:: Result < ( ) > ;
298
321
}
299
322
300
- #[ derive( Clone , Copy , Debug , PartialEq , Eq , serde:: Serialize , zvariant:: Type ) ]
323
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq , Hash , serde:: Serialize , zvariant:: Type ) ]
301
324
#[ zvariant( signature = "u" ) ]
302
- pub enum AppStatus {
325
+ enum AppStatus {
303
326
/// No open windows
304
327
Background = 0 ,
305
328
/// At least one opened window
@@ -314,17 +337,29 @@ struct AppStates {
314
337
apps : Vec < AppState > ,
315
338
}
316
339
317
- #[ derive( Clone , Debug , zvariant:: SerializeDict , zvariant:: Type ) ]
340
+ #[ derive( Clone , Debug , Eq , zvariant:: SerializeDict , zvariant:: Type ) ]
318
341
#[ zvariant( signature = "{sv}" ) ]
319
342
struct AppState {
320
343
app_id : String ,
321
344
status : AppStatus ,
322
345
}
323
346
347
+ impl Hash for AppState {
348
+ fn hash < H : Hasher > ( & self , state : & mut H ) {
349
+ state. write ( self . app_id . as_bytes ( ) ) ;
350
+ }
351
+ }
352
+
353
+ impl PartialEq for AppState {
354
+ fn eq ( & self , other : & Self ) -> bool {
355
+ self . app_id == other. app_id
356
+ }
357
+ }
358
+
324
359
/// Result vardict for [`Background::notify_background`]
325
360
#[ derive( Clone , Debug , zvariant:: SerializeDict , zvariant:: Type ) ]
326
361
#[ zvariant( signature = "a{sv}" ) ]
327
- pub struct NotifyBackgroundResult {
362
+ struct NotifyBackgroundResult {
328
363
result : PermissionResponse ,
329
364
}
330
365
@@ -376,7 +411,8 @@ pub(crate) fn view(portal: &CosmicPortal, id: window::Id) -> cosmic::Element<Msg
376
411
. unwrap_or ( "Invalid window id" ) ;
377
412
378
413
// TODO: Add cancel
379
- widget:: dialog ( fl ! ( "bg-dialog-title" ) )
414
+ widget:: dialog ( )
415
+ . title ( fl ! ( "bg-dialog-title" ) )
380
416
. body ( fl ! ( "bg-dialog-body" , appname = name) )
381
417
. icon ( widget:: icon:: from_name ( "dialog-warning-symbolic" ) . size ( 64 ) )
382
418
. primary_action (
@@ -401,7 +437,7 @@ pub(crate) fn view(portal: &CosmicPortal, id: window::Id) -> cosmic::Element<Msg
401
437
}
402
438
403
439
/// Update Background dialog args for a specific window
404
- pub fn update_args ( portal : & mut CosmicPortal , args : Args ) -> cosmic:: Command < crate :: app:: Msg > {
440
+ pub fn update_args ( portal : & mut CosmicPortal , args : Args ) -> cosmic:: Task < crate :: app:: Msg > {
405
441
if let Some ( old) = portal. background_prompts . insert ( args. id , args) {
406
442
// xxx Can this even happen?
407
443
log:: trace!(
@@ -412,10 +448,10 @@ pub fn update_args(portal: &mut CosmicPortal, args: Args) -> cosmic::Command<cra
412
448
)
413
449
}
414
450
415
- cosmic:: Command :: none ( )
451
+ cosmic:: Task :: none ( )
416
452
}
417
453
418
- pub fn update_msg ( portal : & mut CosmicPortal , msg : Msg ) -> cosmic:: Command < crate :: app:: Msg > {
454
+ pub fn update_msg ( portal : & mut CosmicPortal , msg : Msg ) -> cosmic:: Task < crate :: app:: Msg > {
419
455
match msg {
420
456
Msg :: Response { id, choice } => {
421
457
let Some ( Args {
@@ -426,7 +462,7 @@ pub fn update_msg(portal: &mut CosmicPortal, msg: Msg) -> cosmic::Command<crate:
426
462
} ) = portal. background_prompts . remove ( & id)
427
463
else {
428
464
log:: warn!( "Window {id:?} doesn't exist for some reason" ) ;
429
- return cosmic:: Command :: none ( ) ;
465
+ return cosmic:: Task :: none ( ) ;
430
466
} ;
431
467
432
468
log:: trace!(
@@ -455,7 +491,7 @@ pub fn update_msg(portal: &mut CosmicPortal, msg: Msg) -> cosmic::Command<crate:
455
491
} ) = portal. background_prompts . remove ( & id)
456
492
else {
457
493
log:: warn!( "Window {id:?} doesn't exist for some reason" ) ;
458
- return cosmic:: Command :: none ( ) ;
494
+ return cosmic:: Task :: none ( ) ;
459
495
} ;
460
496
461
497
log:: trace!(
@@ -472,5 +508,5 @@ pub fn update_msg(portal: &mut CosmicPortal, msg: Msg) -> cosmic::Command<crate:
472
508
}
473
509
}
474
510
475
- cosmic:: Command :: none ( )
511
+ cosmic:: Task :: none ( )
476
512
}
0 commit comments