@@ -138,7 +138,7 @@ use std::{
138
138
/// # Beware
139
139
///
140
140
/// Sometimes, depending on the platform and system configuration, launchers *can* block.
141
- /// If you want to be sure they don't, use [`that_in_background()`] instead.
141
+ /// If you want to be sure they don't, use [`that_in_background()`] or [`that_detached`] instead.
142
142
pub fn that ( path : impl AsRef < OsStr > ) -> io:: Result < ( ) > {
143
143
let mut last_err = None ;
144
144
for mut cmd in commands ( path) {
@@ -232,6 +232,33 @@ pub fn with_in_background<T: AsRef<OsStr>>(
232
232
thread:: spawn ( || with ( path, app) )
233
233
}
234
234
235
+ /// Open path with the default application using a detached process. which is useful if
236
+ /// the program ends up to be blocking or want to out-live your app
237
+ ///
238
+ /// See documentation of [`that()`] for more details.
239
+ pub fn that_detached ( path : impl AsRef < OsStr > ) -> io:: Result < ( ) > {
240
+ let mut last_err = None ;
241
+ for mut cmd in commands ( path) {
242
+ match cmd. spawn_detached ( ) {
243
+ Ok ( _) => {
244
+ return Ok ( ( ) ) ;
245
+ }
246
+ Err ( err) => last_err = Some ( err) ,
247
+ }
248
+ }
249
+ Err ( last_err. expect ( "no launcher worked, at least one error" ) )
250
+ }
251
+
252
+ /// Open path with the given application using a detached process, which is useful if
253
+ /// the program ends up to be blocking or want to out-live your app. Otherwise, prefer [`with()`] for
254
+ /// straightforward error handling.
255
+ ///
256
+ /// See documentation of [`with()`] for more details.
257
+ pub fn with_detached < T : AsRef < OsStr > > ( path : T , app : impl Into < String > ) -> io:: Result < ( ) > {
258
+ let mut cmd = with_command ( path, app) ;
259
+ cmd. spawn_detached ( )
260
+ }
261
+
235
262
trait IntoResult < T > {
236
263
fn into_result ( self , cmd : & Command ) -> T ;
237
264
}
@@ -251,6 +278,7 @@ impl IntoResult<io::Result<()>> for io::Result<std::process::ExitStatus> {
251
278
252
279
trait CommandExt {
253
280
fn status_without_output ( & mut self ) -> io:: Result < std:: process:: ExitStatus > ;
281
+ fn spawn_detached ( & mut self ) -> io:: Result < ( ) > ;
254
282
}
255
283
256
284
impl CommandExt for Command {
@@ -260,6 +288,43 @@ impl CommandExt for Command {
260
288
. stderr ( Stdio :: null ( ) )
261
289
. status ( )
262
290
}
291
+
292
+ fn spawn_detached ( & mut self ) -> io:: Result < ( ) > {
293
+ // This is pretty much lifted from the implementation in Alacritty:
294
+ // https://github.com/alacritty/alacritty/blob/b9c886872d1202fc9302f68a0bedbb17daa35335/alacritty/src/daemon.rs
295
+
296
+ self . stdin ( Stdio :: null ( ) )
297
+ . stdout ( Stdio :: null ( ) )
298
+ . stderr ( Stdio :: null ( ) ) ;
299
+
300
+ #[ cfg( unix) ]
301
+ unsafe {
302
+ use std:: os:: unix:: process:: CommandExt as _;
303
+
304
+ self . pre_exec ( move || {
305
+ match libc:: fork ( ) {
306
+ -1 => return Err ( io:: Error :: last_os_error ( ) ) ,
307
+ 0 => ( ) ,
308
+ _ => libc:: _exit ( 0 ) ,
309
+ }
310
+
311
+ if libc:: setsid ( ) == -1 {
312
+ return Err ( io:: Error :: last_os_error ( ) ) ;
313
+ }
314
+
315
+ Ok ( ( ) )
316
+ } ) ;
317
+ }
318
+ #[ cfg( windows) ]
319
+ {
320
+ use std:: os:: windows:: process:: CommandExt ;
321
+ const CREATE_NEW_PROCESS_GROUP : u32 = 0x00000200 ;
322
+ const CREATE_NO_WINDOW : u32 = 0x08000000 ;
323
+ self . creation_flags ( CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW ) ;
324
+ }
325
+
326
+ self . spawn ( ) . map ( |_| ( ) )
327
+ }
263
328
}
264
329
265
330
#[ cfg( windows) ]
0 commit comments