@@ -4,21 +4,29 @@ use crate::DesktopEntry;
4
4
use std:: convert:: TryFrom ;
5
5
use std:: path:: PathBuf ;
6
6
use std:: process:: Command ;
7
+ use zbus:: blocking:: Connection ;
7
8
9
+ mod dbus;
8
10
pub mod error;
9
11
mod graphics;
10
12
11
13
impl DesktopEntry < ' _ > {
12
14
/// Execute the given desktop entry `Exec` key with either the default gpu or
13
15
/// the alternative one if available.
14
- pub fn launch (
15
- & self ,
16
- filename : Option < & str > ,
17
- filenames : & [ & str ] ,
18
- url : Option < & str > ,
19
- urls : & [ & str ] ,
20
- prefer_non_default_gpu : bool ,
21
- ) -> Result < ( ) , ExecError > {
16
+ pub fn launch ( & self , uris : & [ & str ] , prefer_non_default_gpu : bool ) -> Result < ( ) , ExecError > {
17
+ match Connection :: session ( ) {
18
+ Ok ( conn) => {
19
+ if self . is_bus_actionable ( & conn) {
20
+ self . dbus_launch ( & conn, uris, prefer_non_default_gpu)
21
+ } else {
22
+ self . shell_launch ( uris, prefer_non_default_gpu)
23
+ }
24
+ }
25
+ Err ( _) => self . shell_launch ( uris, prefer_non_default_gpu) ,
26
+ }
27
+ }
28
+
29
+ fn shell_launch ( & self , uris : & [ & str ] , prefer_non_default_gpu : bool ) -> Result < ( ) , ExecError > {
22
30
let exec = self . exec ( ) ;
23
31
if exec. is_none ( ) {
24
32
return Err ( ExecError :: MissingExecKey ( self . path ) ) ;
@@ -42,7 +50,7 @@ impl DesktopEntry<'_> {
42
50
exec_args. push ( arg) ;
43
51
}
44
52
45
- let exec_args = self . get_args ( filename , filenames , url , urls , exec_args) ;
53
+ let exec_args = self . get_args ( uris , exec_args) ;
46
54
47
55
if exec_args. is_empty ( ) {
48
56
return Err ( ExecError :: EmptyExecString ) ;
@@ -63,8 +71,8 @@ impl DesktopEntry<'_> {
63
71
cmd
64
72
}
65
73
. args ( args)
66
- . output ( ) ?
67
- . status
74
+ . spawn ( ) ?
75
+ . try_wait ( ) ?
68
76
} else {
69
77
let mut cmd = Command :: new ( shell) ;
70
78
@@ -74,44 +82,33 @@ impl DesktopEntry<'_> {
74
82
cmd
75
83
}
76
84
. args ( & [ "-c" , & exec_args] )
77
- . output ( ) ?
78
- . status
85
+ . spawn ( ) ?
86
+ . try_wait ( ) ?
79
87
} ;
80
88
81
- if !status. success ( ) {
82
- return Err ( ExecError :: NonZeroStatusCode {
83
- status : status. code ( ) ,
84
- exec : exec. to_string ( ) ,
85
- } ) ;
89
+ if let Some ( status) = status {
90
+ if !status. success ( ) {
91
+ return Err ( ExecError :: NonZeroStatusCode {
92
+ status : status. code ( ) ,
93
+ exec : exec. to_string ( ) ,
94
+ } ) ;
95
+ }
86
96
}
87
97
88
98
Ok ( ( ) )
89
99
}
90
100
91
101
// Replace field code with their values and ignore deprecated and unknown field codes
92
- fn get_args (
93
- & self ,
94
- filename : Option < & str > ,
95
- filenames : & [ & str ] ,
96
- url : Option < & str > ,
97
- urls : & [ & str ] ,
98
- exec_args : Vec < ArgOrFieldCode > ,
99
- ) -> Vec < String > {
102
+ fn get_args ( & self , uris : & [ & str ] , exec_args : Vec < ArgOrFieldCode > ) -> Vec < String > {
100
103
exec_args
101
104
. iter ( )
102
105
. filter_map ( |arg| match arg {
103
- ArgOrFieldCode :: SingleFileName => filename. map ( |filename| filename. to_string ( ) ) ,
104
- ArgOrFieldCode :: FileList => {
105
- if !filenames. is_empty ( ) {
106
- Some ( filenames. join ( " " ) )
107
- } else {
108
- None
109
- }
106
+ ArgOrFieldCode :: SingleFileName | ArgOrFieldCode :: SingleUrl => {
107
+ uris. get ( 0 ) . map ( |filename| filename. to_string ( ) )
110
108
}
111
- ArgOrFieldCode :: SingleUrl => url. map ( |url| url. to_string ( ) ) ,
112
- ArgOrFieldCode :: UrlList => {
113
- if !urls. is_empty ( ) {
114
- Some ( urls. join ( " " ) )
109
+ ArgOrFieldCode :: FileList | ArgOrFieldCode :: UrlList => {
110
+ if !uris. is_empty ( ) {
111
+ Some ( uris. join ( " " ) )
115
112
} else {
116
113
None
117
114
}
@@ -129,7 +126,6 @@ impl DesktopEntry<'_> {
129
126
ArgOrFieldCode :: DesktopFileLocation => {
130
127
Some ( self . path . to_string_lossy ( ) . to_string ( ) )
131
128
}
132
- // Ignore deprecated field-codes
133
129
ArgOrFieldCode :: Arg ( arg) => Some ( arg. to_string ( ) ) ,
134
130
} )
135
131
. collect ( )
@@ -227,7 +223,7 @@ mod test {
227
223
let path = PathBuf :: from ( "tests/entries/unmatched-quotes.desktop" ) ;
228
224
let input = fs:: read_to_string ( & path) . unwrap ( ) ;
229
225
let de = DesktopEntry :: decode ( path. as_path ( ) , & input) . unwrap ( ) ;
230
- let result = de. launch ( None , & [ ] , None , & [ ] , false ) ;
226
+ let result = de. launch ( & [ ] , false ) ;
231
227
232
228
assert_that ! ( result)
233
229
. is_err ( )
@@ -239,7 +235,7 @@ mod test {
239
235
let path = PathBuf :: from ( "tests/entries/empty-exec.desktop" ) ;
240
236
let input = fs:: read_to_string ( & path) . unwrap ( ) ;
241
237
let de = DesktopEntry :: decode ( Path :: new ( path. as_path ( ) ) , & input) . unwrap ( ) ;
242
- let result = de. launch ( None , & [ ] , None , & [ ] , false ) ;
238
+ let result = de. launch ( & [ ] , false ) ;
243
239
244
240
assert_that ! ( result)
245
241
. is_err ( )
@@ -252,7 +248,7 @@ mod test {
252
248
let path = PathBuf :: from ( "tests/entries/alacritty-simple.desktop" ) ;
253
249
let input = fs:: read_to_string ( & path) . unwrap ( ) ;
254
250
let de = DesktopEntry :: decode ( path. as_path ( ) , & input) . unwrap ( ) ;
255
- let result = de. launch ( None , & [ ] , None , & [ ] , false ) ;
251
+ let result = de. launch ( & [ ] , false ) ;
256
252
257
253
assert_that ! ( result) . is_ok ( ) ;
258
254
}
@@ -263,7 +259,7 @@ mod test {
263
259
let path = PathBuf :: from ( "tests/entries/non-terminal-cmd.desktop" ) ;
264
260
let input = fs:: read_to_string ( & path) . unwrap ( ) ;
265
261
let de = DesktopEntry :: decode ( path. as_path ( ) , & input) . unwrap ( ) ;
266
- let result = de. launch ( None , & [ ] , None , & [ ] , false ) ;
262
+ let result = de. launch ( & [ ] , false ) ;
267
263
268
264
assert_that ! ( result) . is_ok ( ) ;
269
265
}
@@ -274,7 +270,43 @@ mod test {
274
270
let path = PathBuf :: from ( "tests/entries/non-terminal-cmd.desktop" ) ;
275
271
let input = fs:: read_to_string ( & path) . unwrap ( ) ;
276
272
let de = DesktopEntry :: decode ( path. as_path ( ) , & input) . unwrap ( ) ;
277
- let result = de. launch ( None , & [ ] , None , & [ ] , false ) ;
273
+ let result = de. launch ( & [ ] , false ) ;
274
+
275
+ assert_that ! ( result) . is_ok ( ) ;
276
+ }
277
+
278
+ #[ test]
279
+ #[ ignore = "Needs a desktop environment with nvim installed, run locally only" ]
280
+ fn should_launch_with_field_codes ( ) {
281
+ let path = PathBuf :: from ( "/usr/share/applications/nvim.desktop" ) ;
282
+ let input = fs:: read_to_string ( & path) . unwrap ( ) ;
283
+ let de = DesktopEntry :: decode ( path. as_path ( ) , & input) . unwrap ( ) ;
284
+ let result = de. launch ( & [ "src/lib.rs" ] , false ) ;
285
+
286
+ assert_that ! ( result) . is_ok ( ) ;
287
+ }
288
+
289
+ #[ test]
290
+ #[ ignore = "Needs a desktop environment with gnome Books installed, run locally only" ]
291
+ fn should_launch_with_dbus ( ) {
292
+ let path = PathBuf :: from ( "/usr/share/applications/org.gnome.Books.desktop" ) ;
293
+ let input = fs:: read_to_string ( & path) . unwrap ( ) ;
294
+ let de = DesktopEntry :: decode ( path. as_path ( ) , & input) . unwrap ( ) ;
295
+ let result = de. launch ( & [ ] , false ) ;
296
+
297
+ assert_that ! ( result) . is_ok ( ) ;
298
+ }
299
+
300
+ #[ test]
301
+ #[ ignore = "Needs a desktop environment with Nautilus installed, run locally only" ]
302
+ fn should_launch_with_dbus_and_field_codes ( ) {
303
+ let path = PathBuf :: from ( "/usr/share/applications/org.gnome.Nautilus.desktop" ) ;
304
+ let input = fs:: read_to_string ( & path) . unwrap ( ) ;
305
+ let de = DesktopEntry :: decode ( path. as_path ( ) , & input) . unwrap ( ) ;
306
+ let path = std:: env:: current_dir ( ) . unwrap ( ) ;
307
+ let path = path. to_string_lossy ( ) ;
308
+ let path = format ! ( "file:///{path}" ) ;
309
+ let result = de. launch ( & [ path. as_str ( ) ] , false ) ;
278
310
279
311
assert_that ! ( result) . is_ok ( ) ;
280
312
}
0 commit comments