Skip to main content

DetachedHandoff

Struct DetachedHandoff 

Source
pub struct DetachedHandoff<Msg> { /* private fields */ }
Expand description

Hands the terminal to a program until it writes its first line on standard output, then takes the screen back and leaves the program running in the background as a LiveChild.

Everything up to the first line is a Handoff: the screen is released, the notice printed, the program gets a process group of its own that is the terminal’s foreground, so it can ask on the terminal and the keys’ signals (Ctrl-C at a password prompt) reach it and not the application. Unlike a handoff the program’s standard input and output are pipes to the application; only its standard error is the terminal. pkexec and sudo ask on the controlling terminal itself, not on standard input, so they still can.

The first line is the program saying it is ready: the terminal’s foreground goes back to the application, the screen is taken back and drawn again in full, and DetachedOutcome::Detached arrives with the child and that line. Every later line arrives through DetachedHandoff::on_line, and ChildLine::Ended after the last.

A program that ends before its first line, such as pkexec after a cancelled or wrong password (codes 126 and 127), gives DetachedOutcome::Finished as a handoff would.

use qframe::prelude::*;
use qframe::runtime::{ChildLine, DetachedHandoff, DetachedOutcome, LiveChild};

enum Msg {
    Start,
    Started(DetachedOutcome),
    Helper(ChildLine),
}

fn update(helper: &mut Option<LiveChild>, msg: Msg) -> Command<Msg> {
    match msg {
        // The helper asks for the password through pkexec, then prints `ready` and serves
        // one request per line until its input ends.
        Msg::Start => Command::handoff_detached(
            DetachedHandoff::new("pkexec", Msg::Started)
                .args(["/usr/lib/example/helper", "--serve"])
                .notice("Asking for permission to manage packages…")
                .on_line(Msg::Helper),
        ),
        Msg::Started(DetachedOutcome::Detached { child, first_line: _ }) => {
            let _ = child.write_line("list-updates");
            *helper = Some(child);
            Command::none()
        }
        Msg::Started(_) | Msg::Helper(_) => Command::none(),
    }
}

Implementations§

Source§

impl<Msg: Send + 'static> DetachedHandoff<Msg>

Source

pub fn new( program: impl Into<OsString>, on_start: impl FnOnce(DetachedOutcome) -> Msg + Send + 'static, ) -> Self

Runs program, delivering on_start(outcome) once the application has the screen back: after the program’s first line, or after its end when it wrote none.

Source

pub fn arg(self, arg: impl Into<OsString>) -> Self

Adds one argument.

Source

pub fn args(self, args: impl IntoIterator<Item = impl Into<OsString>>) -> Self

Adds several arguments, in order.

Source

pub fn dir(self, dir: impl Into<PathBuf>) -> Self

Runs the program in dir instead of the application’s working directory.

Source

pub fn env(self, key: impl Into<OsString>, value: impl Into<OsString>) -> Self

Sets an environment variable for the program. The rest of the environment is inherited.

Source

pub fn notice(self, text: impl Into<String>) -> Self

A line printed on the cleared screen before the program starts, so the user knows why the application stepped aside.

Source

pub fn pause(self, pause: bool) -> Self

Waits for a key press when the program ends without a first line, so what it wrote on the terminal — why a password was refused — can be read. A program that detaches never waits for it. Off by default.

Source

pub fn on_line( self, message: impl Fn(ChildLine) -> Msg + Send + Sync + 'static, ) -> Self

Turns the program’s later output into messages: each line after the first as ChildLine::Line, then ChildLine::Ended once its output closed and it ended. The lines keep coming for the program’s whole life, however long after the handoff. Without it the output is read and dropped.

Auto Trait Implementations§

§

impl<Msg> !RefUnwindSafe for DetachedHandoff<Msg>

§

impl<Msg> !Sync for DetachedHandoff<Msg>

§

impl<Msg> !UnwindSafe for DetachedHandoff<Msg>

§

impl<Msg> Freeze for DetachedHandoff<Msg>
where Box<dyn FnOnce(DetachedOutcome) -> Msg + Send>: Freeze, Option<Arc<dyn Fn(ChildLine) -> Msg + Send + Sync>>: Freeze,

§

impl<Msg> Send for DetachedHandoff<Msg>
where Box<dyn FnOnce(DetachedOutcome) -> Msg + Send>: Send, Option<Arc<dyn Fn(ChildLine) -> Msg + Send + Sync>>: Send,

§

impl<Msg> Unpin for DetachedHandoff<Msg>
where Box<dyn FnOnce(DetachedOutcome) -> Msg + Send>: Unpin, Option<Arc<dyn Fn(ChildLine) -> Msg + Send + Sync>>: Unpin,

§

impl<Msg> UnsafeUnpin for DetachedHandoff<Msg>
where Box<dyn FnOnce(DetachedOutcome) -> Msg + Send>: UnsafeUnpin, Option<Arc<dyn Fn(ChildLine) -> Msg + Send + Sync>>: UnsafeUnpin,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.