1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//! This module contains all `unix` specific terminal related logic.

pub use libc::{c_int, termios as Termios};

use std::{io, mem};

static mut ORIGINAL_TERMINAL_MODE: Option<Termios> = None;
pub static mut RAW_MODE_ENABLED: bool = false;

fn unwrap(t: i32) -> io::Result<()> {
    if t == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

/// Transform the given mode into an raw mode (non-canonical) mode.
pub fn raw_terminal_attr(termios: &mut Termios) {
    extern "C" {
        pub fn cfmakeraw(termptr: *mut Termios);
    }
    unsafe { cfmakeraw(termios) }
}

pub fn get_terminal_attr() -> io::Result<Termios> {
    extern "C" {
        pub fn tcgetattr(fd: c_int, termptr: *mut Termios) -> c_int;
    }
    unsafe {
        let mut termios = mem::zeroed();
        unwrap(tcgetattr(0, &mut termios))?;
        Ok(termios)
    }
}

pub fn set_terminal_attr(termios: &Termios) -> io::Result<()> {
    extern "C" {
        pub fn tcsetattr(fd: c_int, opt: c_int, termptr: *const Termios) -> c_int;
    }
    unwrap(unsafe { tcsetattr(0, 0, termios) }).and(Ok(()))
}

pub fn into_raw_mode() -> io::Result<()> {
    let mut ios = get_terminal_attr()?;
    let prev_ios = ios;

    unsafe {
        if ORIGINAL_TERMINAL_MODE.is_none() {
            ORIGINAL_TERMINAL_MODE = Some(prev_ios.clone());
        }

        RAW_MODE_ENABLED = true;
    }
    raw_terminal_attr(&mut ios);
    set_terminal_attr(&ios)?;
    Ok(())
}

pub fn disable_raw_mode() -> io::Result<()> {
    unsafe {
        if ORIGINAL_TERMINAL_MODE.is_some() {
            set_terminal_attr(&ORIGINAL_TERMINAL_MODE.unwrap())?;

            RAW_MODE_ENABLED = false;
        }
    }
    Ok(())
}