Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

69 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

conpty-oxide

CI CodeQL crates.io docs.rs

Windows pseudoconsole (ConPTY) sessions for Rust: spawn a process under a pseudoconsole, control it, and read its terminal output, with equivalent blocking and Tokio APIs. Windows-only; requires Windows 10 version 1809 or later and Rust 1.75 or later.

Usage

The default blocking feature enables the synchronous API:

[dependencies]
conpty-oxide = "0.1"

Run a process and wait for its exit status; output is drained and discarded concurrently:

use conpty_oxide::blocking::Command;

fn main() -> conpty_oxide::Result<()> {
    let status = Command::new("cmd.exe")
        .args(["/d", "/c", "echo", "hello"])
        .spawn()?
        .wait()?;
    assert!(status.success());
    Ok(())
}

Capture the raw terminal output instead:

use conpty_oxide::blocking::Command;

fn main() -> conpty_oxide::Result<()> {
    let output = Command::new("cmd.exe")
        .args(["/d", "/c", "echo", "hello"])
        .spawn()?
        .collect_output()?;

    assert!(output.status().success());
    print!("{}", String::from_utf8_lossy(output.as_bytes()));
    Ok(())
}

For interactive use, split the session into independently owned input, output, child, and control handles:

use conpty_oxide::blocking::Command;

fn main() -> conpty_oxide::Result<()> {
    let parts = Command::new("cmd.exe").spawn()?.into_parts();
    // Move parts.output to a reader thread while the current thread drives
    // parts.input, parts.child, and parts.controller.
    drop(parts);
    Ok(())
}

Tokio

Disable default features and enable tokio:

[dependencies.conpty-oxide]
version = "0.1"
default-features = false
features = ["tokio"]

[dependencies.tokio]
version = "1"
features = ["io-util", "macros", "rt-multi-thread"]

The asynchronous API mirrors the blocking one:

use conpty_oxide::tokio::Command;

#[tokio::main]
async fn main() -> conpty_oxide::Result<()> {
    let status = Command::new("cmd.exe")
        .args(["/d", "/c", "exit", "0"])
        .spawn()?
        .wait()
        .await?;
    assert!(status.success());
    Ok(())
}

Behavior

  • A session is bounded by its root process. Once the root exits, descendants remaining in the session Job are terminated, and the reported status is the root's real exit status. This also holds after into_parts.
  • Dropping an unfinished Session or Child terminates the whole Job.
  • wait discards output with bounded memory. collect_output buffers every unread byte. The lower-level Child::wait waits on the root alone and requires the caller to drain output concurrently.
  • ConPTY input and output must be serviced concurrently; a full output pipe stalls the session (see Microsoft's ConPTY guidance). wait and collect_output handle this internally.
  • Output is a single raw UTF-8/VT byte stream. ConPTY has no separate stdout and stderr channels.
  • Dropping or shutting down input ends the terminal session; it is not a way to deliver stdin EOF to the child.
  • Backend selection prefers a validated conpty.dll/OpenConsole.exe pair next to the executable and falls back to the system ConPTY.

Feature flags

  • blocking (default) — synchronous API.
  • tokio — asynchronous API on Tokio.
  • tracing — instrumentation through the tracing crate.

The features can be combined.

Non-goals

VT parsing, terminal widgets, expect-style automation, shell protocols, and cross-platform PTY abstraction are out of scope. This crate is the Windows-specific session layer that such tools can build on.

Links

License

Licensed under either Apache-2.0 or MIT, at your option.

About

Correctness-first Windows ConPTY library with blocking and Tokio APIs

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages