Generated reference › API — ICoreEssentials/Process
kind: generated#api#icoreessentials-process

API — ICoreEssentials/Process

The public contract of 3 header(s) under src/ICoreEssentials/Process — 3 class/struct definition(s), 47 declaration(s). Each section shows the header's banner and its public (and protected-virtual) surface exactly as the file writes it.

ICoreProcess.h#

src/ICoreEssentials/Process/ICoreProcess.h

⚠ FOUR OF THESE ARE NO LONGER USED BY THIS HEADER AND MUST STAY ANYWAY. H2.13 moved every body to the .cpp, so only <QByteArray>, <QProcess> and <QtGlobal> are still needed here -- by the public signatures (QByteArray out of the readAll family, QProcess for qt()'s pointee, qint64 for write()). <QPointer>, <QProcessEnvironment>, <QStringList> and <QTimer> went with the bodies that named them, and taking them out of this header BREAKS THE BUILD in files that have nothing to do with this class: 19 headers under UI/ use QPointer without including it and were being fed it through the umbrella, from here. Measured, not guessed -- ICoreWeakWidget.h, ICoreWeakObject.h, ICoreMenuBar.h and ICoreShortcut.h fail first.

Retiring them is a real cleanup and a SEPARATE one: it means adding the include to each header that leans on it, across rows this session does not own, and proving it with a full build. It is not something a header-surface

ICoreProcess#

ICoreProcess.h:102 · class · pImpl · 25 declaration(s)

ICoreProcess -- a child process: start it, read what it printed, learn how it ended.

class ICoreProcess {
public:
    // How a finished child ended. See the design note on why this is declared
    // here rather than aliased to QProcess::ExitStatus.
    enum class ExitStatus { Normal, Crash };

    ICoreProcess();

    // Deletes the child object unless deleteLater() already released it. Does
    // NOT kill a running child -- neither did the QProcess this replaces, and
    // the two owners that care take it down explicitly first.
    ~ICoreProcess();

    // Owns an OS resource; copying one is meaningless and no call site moves
    // one, so both are deleted rather than defined.
    ICoreProcess(const ICoreProcess&) = delete;
    ICoreProcess& operator=(const ICoreProcess&) = delete;

    // --- setup, all before start() ------------------------------------------

    void setWorkingDirectory(const ICoreString& dir);
    // Takes the snapshot of the parent's environment that ICoreProcessEnvironment
    // deliberately no longer takes for itself -- it went Qt-free and now records
    // only the overrides, in insert order. Replaying them here is what keeps
    // <QProcessEnvironment> inside this wrapper, which still owns a QProcess and
    // so has to speak Qt anyway; see that wrapper's header note. (The include
    // now sits in the .cpp, which is where the replay does.)
    void setEnvironment(const ICoreProcessEnvironment& env);

    // stderr folded into stdout. Named for the one mode the project uses --
    // see the design note.
    void setMergedChannels();

    // --- running ------------------------------------------------------------

    void start(const ICoreString& program, const ICoreStringList& arguments);

    // Fire-and-forget: starts a child that outlives this process and is never
    // waited on. Static because there is nothing left to hold afterwards.
    static bool startDetached(const ICoreString& program, const ICoreStringList& arguments);

    // Blocks. Returns false on timeout, which both synchronous call sites treat
    // as "the tool hung" and follow with kill().
    bool waitForFinished(int msecs);

    void terminate();   // polite: SIGTERM
    void kill();        // not polite: SIGKILL

    qint64 write(const ICoreByteArray& data);

    // --- results ------------------------------------------------------------

    bool isRunning() const;
    int exitCode() const;
    ExitStatus exitStatus() const;

    QByteArray readAll();
    QByteArray readAllStandardOutput();
    QByteArray readAllStandardError();
    ICoreString errorString() const;

    // --- lifetime -----------------------------------------------------------

    // Hands the child to the event loop and releases it. See the design note --
    // this is what makes destroying the wrapper from inside a signal handler
    // safe, and it is why the call sites that settle asynchronously call it.
    void deleteLater();

    // --- the connect seam ---------------------------------------------------

    // The wrapped object, for connect(), disconnect() and QPointer ONLY. See
    // the first design note: every call is a marker of what phase 2 owes, and
    // a qt() used for anything else is a member this wrapper is missing.
    QProcess* qt() const noexcept;

    // --- asynchronous results (PHASE 2) -------------------------------------
    //
    // The callback registration the design note above chartered to phase 2 and
    // deliberately left out of phase 1. It is here now because it is the last
    // thing standing between three service classes and a Qt-free spelling:
    // without it they cannot learn that a child printed or exited except
    // through qt(), and so had to keep a QObject base purely to be a connect
    // context.
    //
    // DELIVERY IS UNCHANGED, which was phase 1's stated worry. Each handler is
    // connected with the CHILD OBJECT ITSELF as the context, and a QProcess
    // emits on the thread it lives in -- so the connection is direct and the
    // handler runs synchronously inside the emission, exactly as it did when
    // the call sites passed their own same-thread object. What changes is only
    // who owns the subscription: this wrapper, which is also who owns the
    // child, instead of a separate object that had to remember to disconnect.
    //
    // Registering twice replaces the previous handler rather than adding a
    // second one -- these are callbacks, not a signal, and every call site
    // wants exactly one.

    // The child wrote to stdout (stderr too, under setMergedChannels).
    void onOutputReady(std::function<void()> fn);

    // The child ended. Never fires when it failed to START -- that is
    // onFailedToStart, and a call site that must settle exactly once has to
    // handle both, as the terminal session's `settled` flag does.
    void onFinished(std::function<void(int exitCode, ExitStatus status)> fn);

    void onFailedToStart(std::function<void()> fn);

    // Stop delivering, permanently. This is what the call sites' old
    // `qt()->disconnect(this)` meant: an owner that is abandoning a child, or
    // settling a command, and must not be called back afterwards. Safe to call
    // from inside a handler.
    void clearCallbacks();

    // Ask the child to stop, and kill it if it has not gone within `graceMs`.
    //
    // Folded in here because doing it correctly needs a weak handle to the
    // child -- a raw one could be matched by a LATER child landing on the same
    // address, and the delayed kill would then take down the wrong command.
    // Inside the wrapper there is only ever one child, so the guarded pointer
    // below cannot confuse two; a caller holding this by value gets that for
    // free. The kill is dropped if the child ends, or this wrapper dies, first.
    void terminateThenKill(int graceMs);

private:
    class Impl;                    // the two-line residue; state lives here
    std::unique_ptr<Impl> impl;
};

ICoreProcessEnvironment.h#

src/ICoreEssentials/Process/ICoreProcessEnvironment.h

ICoreProcessEnvironment -- the environment to start a child process with.

QT-FREE (phase 3). This header names no Qt type. <QProcessEnvironment> does not leave the layer, though -- it MOVES to ICoreProcess.h, which still owns a QProcess and so still has to speak Qt to hand it an environment. Read that as the honest accounting it is: this wrapper is done, the umbrella's include count is unchanged, and the line finally goes when ICoreProcess itself does.

WHAT THIS CLASS IS, and it is less than its name suggests. Its entire surface is a static named systemEnvironment() and an insert(). Nothing reads a value back, nothing removes one, nothing enumerates. Both call sites are the same three lines:

auto env = ICoreProcessEnvironment::systemEnvironment();

ICoreProcessEnvironment#

ICoreProcessEnvironment.h:68 · class · pImpl · 6 declaration(s)

class ICoreProcessEnvironment {
public:
    ~ICoreProcessEnvironment();

    // Copyable, as it always was. The copy is written out because a
    // unique_ptr<Impl> member deletes the implicit one; declaring it also
    // suppresses the implicit move, which is what this type already did once
    // its destructor became user-declared.
    ICoreProcessEnvironment(const ICoreProcessEnvironment& other);
    ICoreProcessEnvironment& operator=(const ICoreProcessEnvironment& other);

    // "Inherit the parent's environment." See the header note on why the copy
    // is not taken until the environment is actually handed to a child.
    [[nodiscard]] static ICoreProcessEnvironment systemEnvironment();

    void insert(const ICoreString& name, const ICoreString& value);

    // The overrides, in insert order, later inserts of a name last. This is
    // what ICoreProcess::setEnvironment replays onto the real environment; see
    // the note above for why it is public rather than a friend's reach.
    [[nodiscard]] std::vector<std::pair<ICoreString, ICoreString>> overrides() const;

private:
    class Impl;                    // the two-line residue; state lives here
    std::unique_ptr<Impl> impl;
};

ICorePty.h#

src/ICoreEssentials/Process/ICorePty.h

ICorePty#

ICorePty.h:72 · class · pImpl · 16 declaration(s)

ICorePty -- a child process running under a PSEUDO-TERMINAL: it believes it owns a real tty, so it turns its interactive behaviour on.

class ICorePty {
public:
    ICorePty();

    // Stops the reader thread, hangs up the pty and reaps the child. Safe
    // whether or not start() was ever called, and whether or not the child has
    // already exited.
    ~ICorePty();

    // Owns an OS resource and a thread; copying one is meaningless.
    ICorePty(const ICorePty&) = delete;
    ICorePty& operator=(const ICorePty&) = delete;

    // --- setup, all before start() ------------------------------------------

    void setWorkingDirectory(const ICoreString& dir);

    // The parent's environment plus these overrides, exactly as ICoreProcess
    // spells it. Note what a pty makes possible here: the child can be a LOGIN
    // shell, which builds its own PATH from the user's profile -- which is how
    // a GUI app launched from Finder stops having the bare launchd PATH.
    void setEnvironment(const ICoreProcessEnvironment& env);

    // The size the child is told the terminal is, in CELLS. Valid before AND
    // after start(): before, it seeds the pty; after, it issues TIOCSWINSZ,
    // which makes the kernel raise SIGWINCH on the foreground process group --
    // the signal a TUI redraws itself on.
    //
    // A child that starts at 0x0 draws into a phantom viewport, so this
    // defaults to 80x24 rather than to the kernel's zeroes. Call it from the
    // view's resize hook with the cell geometry, not the pixel geometry.
    void setViewport(int columns, int rows);

    // --- running ------------------------------------------------------------

    // Forks a child under a new pty and execs `program`. False on failure, with
    // errorString() set; the object stays usable and start() may be retried.
    //
    // ON POSIX `program` must be an ABSOLUTE PATH -- PATH is not searched.
    // That is a consequence of honouring setEnvironment(): the portable
    // PATH-searching exec spellings take the environment from the parent
    // instead, which would silently drop every override. Callers name a shell
    // they resolved themselves ($SHELL, /bin/sh), so this costs them nothing.
    //
    // ON WINDOWS there is no such rule: CreateProcessW searches PATH itself and
    // takes the child's environment as a separate argument, so the conflict
    // does not arise and a bare name works. Callers that want one behaviour on
    // both platforms should pass an absolute path.
    bool start(const ICoreString& program, const ICoreStringList& arguments);

    bool isRunning() const;

    // Bytes to the child's terminal input. This is where a keystroke goes --
    // as bytes, not as an event: the line discipline is what turns 0x03 into
    // SIGINT for the foreground process group, which is what makes Ctrl+C
    // interrupt the running command rather than the shell (see row T4.2).
    void write(const ICoreByteArray& data);

    // SIGHUP to the child. ADVISORY, and measured to be so: an interactive
    // shell survives it. What actually hangs a terminal up is closing the
    // master descriptor, which the destructor does -- so "close the terminal"
    // means destroy this object, not call this. Kept for callers that want to
    // nudge a child that has its own SIGHUP handling.
    //
    // ⚠ A NO-OP ON WINDOWS. There is no SIGHUP; the hangup there is the
    // destructor closing the pseudo-console. The nearest alternative, a
    // Ctrl+Break event, is a different signal with different semantics, so
    // this deliberately does nothing rather than pretending.
    void hangUp();

    // SIGKILL the child, or TerminateProcess on Windows. The blunt instrument,
    // for a child that ignored everything else. The destructor escalates to
    // this on its own.
    void kill();

    // --- results ------------------------------------------------------------

    ICoreString errorString() const;

    // --- asynchronous results, ALL DELIVERED ON THE GUI THREAD --------------
    //
    // Registering twice replaces the previous handler rather than adding a
    // second one -- these are callbacks, not signals, and every call site
    // wants exactly one. Matches ICoreProcess.

    // The child wrote. Chunks are COALESCED: a child writing fast produces
    // fewer, larger calls rather than one per read, so a build log cannot
    // flood the event queue (row T6.2 soaks exactly this). A chunk is a slice
    // of a byte stream and carries no alignment guarantee whatsoever -- it can
    // split a UTF-8 sequence or an escape sequence down the middle, and the
    // parser above must be resumable. It is never empty.
    void onBytesRead(std::function<void(const ICoreByteArray& bytes)> fn);

    // The child's end of the pty closed -- it exited, or the pty was hung up.
    // Raised exactly once, after the last onBytesRead.
    //
    // `exitCode` is the child's status, or -1 when it could not be collected.
    // `crashed` is true when a signal killed it rather than a return from
    // main -- which is the normal way a terminal's child dies when the window
    // is closed, so it is not by itself an error to report to the user.
    void onClosed(std::function<void(int exitCode, bool crashed)> fn);

    // Stop delivering, permanently. Safe to call from inside a handler, and
    // called for you by the destructor.
    void clearCallbacks();

private:
    class Impl;                    // the two-line residue; state lives here
    std::unique_ptr<Impl> impl;
};