tui_lib 0.1.8

A Library to Be the base of a Tui Operation
Documentation
use crate::tui_errors::CError;
use crate::tui_events::TuiEvents;
use std::error::Error;
use std::ffi::{c_char, c_void};

mod unix;
use unix::constants::{F_GETFL, F_SETFL};
use unix::functions::{cfmakeraw, read as c_read};

use std::io::{stdin, stdout, Stdout, Write};
use std::os::unix::prelude::AsRawFd;

use self::unix::constants::{ONLCR, OPOST, O_NONBLOCK, STDOUT_FILENO, TCSADRAIN, TIOCGWINSZ};
use self::unix::functions::{fcntl, get_errno_error, ioctl, tcgetattr, tcsetattr};
use self::unix::structs::{Termios, Winsize};

use super::input_interface::InputInterfaceT;
use super::input_parser::ParseInput;
use super::output_interface::OutputInterfaceT;
use super::terminal_interface::TerminalTrait;

#[derive(Clone, Copy, Debug)]
pub struct InputInterface {
    input_fd: i32,
}

#[derive(Debug)]
pub struct OutputInterface {
    output_handle: Stdout,
}
pub struct TerminalManager {}

#[derive(Clone, Copy, Debug)]
pub struct TerminalState {
    termios_struct: Termios,
}

impl InputInterface {
    pub fn get_input_mode(&self) -> Result<Termios, CError> {
        let mut termios_struct: Termios = Termios::default();
        unsafe {
            if tcgetattr(self.input_fd.clone(), &mut termios_struct) == -1 {
                return Err(get_errno_error());
            }
        }
        return Ok(termios_struct);
    }
    pub fn set_input_mode(&self, mut termios_struct: Termios) -> Result<(), CError> {
        unsafe {
            if -1 == tcsetattr(self.input_fd, TCSADRAIN, &mut termios_struct) {
                return Err(get_errno_error());
            }
        }
        return Ok(());
    }

    pub fn get_raw_termios_struct(&self) -> Termios {
        let mut termios_struct: Termios = Termios::default();
        unsafe {
            cfmakeraw(&mut termios_struct);
            termios_struct.c_oflag |= ONLCR | OPOST;
        }
        return termios_struct;
    }

    fn read_char(&self) -> char {
        let mut buffer: [c_char; 1] = [0];
        unsafe {
            c_read(self.input_fd.clone(), buffer.as_mut_ptr() as *mut c_void, 1);
        };

        return (buffer[0] as u8) as char;
    }
}

impl InputInterfaceT for InputInterface {
    fn new() -> Result<InputInterface, Box<(dyn Error)>> {
        let input_fd: i32 = stdin().as_raw_fd();
        return Ok(InputInterface { input_fd: input_fd });
    }

    fn read_parsed(&self) -> TuiEvents {
        loop {
            let Some(input_char) = self.read_raw() else {
                return TuiEvents::Error;
            };
            let event = self.parse_input(input_char);
            let TuiEvents::Ignore = event else {
                return event;
            };
        }
    }

    fn read_raw(&self) -> Option<char> {
        return Some(self.read_char());
    }

    fn read_raw_immediate(&self) -> Option<char> {
        let input_interface = NonBlockInputInterface::new(self.input_fd);
        return input_interface.read_raw_immediate();
    }
}

impl ParseInput for InputInterface {}

struct NonBlockInputInterface {
    input_fd: i32,
    fd_flags: i32,
}

impl NonBlockInputInterface {
    fn new(input_fd: i32) -> NonBlockInputInterface {
        let fd_flags;
        unsafe {
            fd_flags = fcntl(input_fd.clone(), F_GETFL);
            fcntl(input_fd.clone(), F_SETFL, fd_flags | O_NONBLOCK)
        };
        return NonBlockInputInterface {
            input_fd: input_fd,
            fd_flags: fd_flags,
        };
    }
    fn read_raw_immediate(&self) -> Option<char> {
        let mut buffer: [c_char; 1] = [0];
        unsafe {
            if 1 != c_read(self.input_fd.clone(), buffer.as_mut_ptr() as *mut c_void, 1) {
                return None;
            }
        };

        return Some((buffer[0] as u8) as char);
    }
}

impl Drop for NonBlockInputInterface {
    fn drop(&mut self) {
        unsafe { fcntl(self.input_fd.clone(), F_SETFL, self.fd_flags) };
    }
}

impl OutputInterfaceT for OutputInterface {
    fn get_size(&self) -> Result<(u16, u16), CError> {
        let mut window_size: Winsize = Winsize::default();
        unsafe {
            if 0 != ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut window_size) {
                return Err(get_errno_error());
            }
        }
        return Ok((window_size.ws_col as u16, window_size.ws_row as u16));
    }
}

impl Write for OutputInterface {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        return self.output_handle.write(buf);
    }

    fn flush(&mut self) -> std::io::Result<()> {
        return self.output_handle.flush();
    }
}

impl TerminalTrait for TerminalManager {
    fn setup_terminal() -> Result<(InputInterface, OutputInterface, TerminalState), Box<dyn Error>>
    {
        let input_interface: InputInterface = InputInterface::new()?;
        let output_interface: OutputInterface = OutputInterface {
            output_handle: stdout(),
        };
        let terminal_state: TerminalState = TerminalState {
            termios_struct: input_interface.get_input_mode()?,
        };
        input_interface.set_input_mode(input_interface.get_raw_termios_struct())?;

        return Ok((input_interface, output_interface, terminal_state));
    }

    fn reset_terminal_settings(input_interface: &InputInterface, terminal_state: &TerminalState) {
        _ = input_interface.set_input_mode(terminal_state.termios_struct);
    }
}