pandora-kit 0.5.3

Interactive TUI toolkit for the Hefesto framework
Documentation
use std::io::IsTerminal;
use std::os::fd::AsRawFd;

/// Ensures stdin (fd 0) points to the terminal.
pub fn ensure_terminal_stdin() {
    if std::io::stdin().is_terminal() {
        return;
    }
    let tty = match std::fs::OpenOptions::new()
        .read(true)
        .write(true)
        .open("/dev/tty")
    {
        Ok(f) => f,
        Err(_) => return,
    };
    unsafe {
        libc::dup2(tty.as_raw_fd(), libc::STDIN_FILENO);
    }
}

/// Runs `f` with stdout temporarily redirected to `/dev/tty`.
///
/// Crossterm's cursor position query (`\x1b[6n` for DSR) hardcodes
/// `io::stdout()` instead of using the backend's writer. When stdout
/// is a file or pipe (e.g. `pan menu > result.txt`), the query goes
/// to that file and the terminal never responds, causing a 2-second
/// timeout. This function redirects fd 1 → `/dev/tty`, runs `f`,
/// then restores fd 1 to the original destination.
pub fn with_terminal_stdout<F, R>(f: F) -> R
where
    F: FnOnce() -> R,
{
    if std::io::stdout().is_terminal() {
        return f();
    }
    let saved = unsafe { libc::dup(libc::STDOUT_FILENO) };
    if let Ok(tty) = std::fs::OpenOptions::new().write(true).open("/dev/tty") {
        unsafe {
            libc::dup2(tty.as_raw_fd(), libc::STDOUT_FILENO);
        }
    }
    let result = f();
    if saved >= 0 {
        unsafe {
            libc::dup2(saved, libc::STDOUT_FILENO);
            libc::close(saved);
        }
    }
    result
}