Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove TPUT fallback when fetching terminal size. #422

Open
TimonPost opened this issue Apr 21, 2020 · 1 comment
Open

Remove TPUT fallback when fetching terminal size. #422

TimonPost opened this issue Apr 21, 2020 · 1 comment

Comments

@TimonPost
Copy link
Member

TimonPost commented Apr 21, 2020

Senario

Currently, TPUT is used in cases were /dev/tty fails us. TPUT was introduced in #283 because crossterm was unable to fetch the terminal size in a subshell. However, this was a temporary solution and should be removed in a future version. The idea of TPUT was first discussed in #276 and further communicated in discord.

Why
TPUT works but should be removed because:

  • Is a subprocess
  • Is used for terminal resize events, async executors would have to wait too long.
  • (unresearched claim, but in another thread, it was mentioned ncurses was called by it)
  • (unresearched claim, but in another thread, it was mentioned that the binary size would decrease after removing this)

Current Implementation
The current implementation tries to use /dev/tty in all possible cases where it is possible. If this is not possible it uses STDOUT_FILENO.

    let file = File::open("/dev/tty").map(|file| (FileDesc::new(file.into_raw_fd(), true)));
    let fd = if let Ok(file) = &file {
        file.raw_fd()
    } else {
        // Fallback to libc::STDOUT_FILENO if /dev/tty is missing
        STDOUT_FILENO
    };

Then it will try to use the normal ioctl to fetch the terminal size with the handle from above. If this is not possible 'then' it will use TPUT for it.

    if let Ok(true) = wrap_with_result(unsafe { ioctl(fd, TIOCGWINSZ.into(), &mut size) }) {
        Ok((size.ws_col, size.ws_row))
    } else {
        tput_size().ok_or_else(|| std::io::Error::last_os_error().into())
    }

(https://github.com/crossterm-rs/crossterm/blob/master/src/terminal/sys/unix.rs#L26)

Example Code

Some code that fetches terminal size using different file descriptors. It could be useful in the search for a new approach.

Updated sample code

use libc::{ioctl, winsize, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO, TIOCGWINSZ};
use std::os::unix::io::AsRawFd;

fn main() {
    let mut size = winsize {
        ws_row: 0,
        ws_col: 0,
        ws_xpixel: 0,
        ws_ypixel: 0,
    };

    let file = std::fs::OpenOptions::new()
        .read(true)
        .write(true)
        .open("/dev/tty")
        .unwrap();

    unsafe { ioctl(file.as_raw_fd(), TIOCGWINSZ.into(), &mut size) };
    println!("/dev/tty cols: {}, lines: {}", size.ws_col, size.ws_row);

    unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ.into(), &mut size) };
    println!("stdout cols: {}, lines: {}", size.ws_col, size.ws_row);

    unsafe { ioctl(STDERR_FILENO, TIOCGWINSZ.into(), &mut size) };
    println!("stderr cols: {}, lines: {}", size.ws_col, size.ws_row);

    unsafe { ioctl(STDIN_FILENO, TIOCGWINSZ.into(), &mut size) };
    println!("stdin cols: {}, lines: {}", size.ws_col, size.ws_row);
}

Improved Scenario

The new scenario works on macOS and Linux and is able to run in both a subshell and normal terminal. If this succeeds the new approach can be merged and released.

@wooster0
Copy link

How about using https://crates.io/crates/terminfo instead? Is that an option?

december1981 pushed a commit to december1981/crossterm that referenced this issue Oct 26, 2023
* RUST-1699: Add serde_with 3.x integration
Co-authored-by: Abraham Egnor <abraham.egnor@mongodb.com>
Co-authored-by: Isabel Atkinson <isabel.atkinson@mongodb.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants