@@ -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 ( )
@@ -222,7 +218,7 @@ mod test {
222
218
let path = PathBuf :: from ( "tests/entries/unmatched-quotes.desktop" ) ;
223
219
let input = fs:: read_to_string ( & path) . unwrap ( ) ;
224
220
let de = DesktopEntry :: decode ( path. as_path ( ) , & input) . unwrap ( ) ;
225
- let result = de. launch ( None , & [ ] , None , & [ ] , false ) ;
221
+ let result = de. launch ( & [ ] , false ) ;
226
222
227
223
assert_that ! ( result)
228
224
. is_err ( )
@@ -234,7 +230,7 @@ mod test {
234
230
let path = PathBuf :: from ( "tests/entries/empty-exec.desktop" ) ;
235
231
let input = fs:: read_to_string ( & path) . unwrap ( ) ;
236
232
let de = DesktopEntry :: decode ( Path :: new ( path. as_path ( ) ) , & input) . unwrap ( ) ;
237
- let result = de. launch ( None , & [ ] , None , & [ ] , false ) ;
233
+ let result = de. launch ( & [ ] , false ) ;
238
234
239
235
assert_that ! ( result)
240
236
. is_err ( )
@@ -247,7 +243,7 @@ mod test {
247
243
let path = PathBuf :: from ( "tests/entries/alacritty-simple.desktop" ) ;
248
244
let input = fs:: read_to_string ( & path) . unwrap ( ) ;
249
245
let de = DesktopEntry :: decode ( path. as_path ( ) , & input) . unwrap ( ) ;
250
- let result = de. launch ( None , & [ ] , None , & [ ] , false ) ;
246
+ let result = de. launch ( & [ ] , false ) ;
251
247
252
248
assert_that ! ( result) . is_ok ( ) ;
253
249
}
@@ -258,7 +254,7 @@ mod test {
258
254
let path = PathBuf :: from ( "tests/entries/non-terminal-cmd.desktop" ) ;
259
255
let input = fs:: read_to_string ( & path) . unwrap ( ) ;
260
256
let de = DesktopEntry :: decode ( path. as_path ( ) , & input) . unwrap ( ) ;
261
- let result = de. launch ( None , & [ ] , None , & [ ] , false ) ;
257
+ let result = de. launch ( & [ ] , false ) ;
262
258
263
259
assert_that ! ( result) . is_ok ( ) ;
264
260
}
@@ -269,7 +265,43 @@ mod test {
269
265
let path = PathBuf :: from ( "tests/entries/non-terminal-cmd.desktop" ) ;
270
266
let input = fs:: read_to_string ( & path) . unwrap ( ) ;
271
267
let de = DesktopEntry :: decode ( path. as_path ( ) , & input) . unwrap ( ) ;
272
- let result = de. launch ( None , & [ ] , None , & [ ] , false ) ;
268
+ let result = de. launch ( & [ ] , false ) ;
269
+
270
+ assert_that ! ( result) . is_ok ( ) ;
271
+ }
272
+
273
+ #[ test]
274
+ #[ ignore = "Needs a desktop environment with nvim installed, run locally only" ]
275
+ fn should_launch_with_field_codes ( ) {
276
+ let path = PathBuf :: from ( "/usr/share/applications/nvim.desktop" ) ;
277
+ let input = fs:: read_to_string ( & path) . unwrap ( ) ;
278
+ let de = DesktopEntry :: decode ( path. as_path ( ) , & input) . unwrap ( ) ;
279
+ let result = de. launch ( & [ "src/lib.rs" ] , false ) ;
280
+
281
+ assert_that ! ( result) . is_ok ( ) ;
282
+ }
283
+
284
+ #[ test]
285
+ #[ ignore = "Needs a desktop environment with gnome Books installed, run locally only" ]
286
+ fn should_launch_with_dbus ( ) {
287
+ let path = PathBuf :: from ( "/usr/share/applications/org.gnome.Books.desktop" ) ;
288
+ let input = fs:: read_to_string ( & path) . unwrap ( ) ;
289
+ let de = DesktopEntry :: decode ( path. as_path ( ) , & input) . unwrap ( ) ;
290
+ let result = de. launch ( & [ ] , false ) ;
291
+
292
+ assert_that ! ( result) . is_ok ( ) ;
293
+ }
294
+
295
+ #[ test]
296
+ #[ ignore = "Needs a desktop environment with Nautilus installed, run locally only" ]
297
+ fn should_launch_with_dbus_and_field_codes ( ) {
298
+ let path = PathBuf :: from ( "/usr/share/applications/org.gnome.Nautilus.desktop" ) ;
299
+ let input = fs:: read_to_string ( & path) . unwrap ( ) ;
300
+ let de = DesktopEntry :: decode ( path. as_path ( ) , & input) . unwrap ( ) ;
301
+ let path = std:: env:: current_dir ( ) . unwrap ( ) ;
302
+ let path = path. to_string_lossy ( ) ;
303
+ let path = format ! ( "file:///{path}" ) ;
304
+ let result = de. launch ( & [ path. as_str ( ) ] , false ) ;
273
305
274
306
assert_that ! ( result) . is_ok ( ) ;
275
307
}
0 commit comments