From e6e7e3e83578535a93093982026fab44d6066450 Mon Sep 17 00:00:00 2001 From: Xyverle Date: Tue, 1 Apr 2025 23:29:14 -0400 Subject: [PATCH] add convenience constants & example --- examples/test.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/ansi.rs | 36 ++++++++++++++++++++++++++++++++++++ src/input.rs | 4 ++++ src/unix_input.rs | 35 ++++++++++++++++++++--------------- 4 files changed, 106 insertions(+), 15 deletions(-) create mode 100644 examples/test.rs diff --git a/examples/test.rs b/examples/test.rs new file mode 100644 index 0000000..1f8b3da --- /dev/null +++ b/examples/test.rs @@ -0,0 +1,46 @@ +#![warn(clippy::all, clippy::pedantic)] + +use std::{time::Duration, io}; +use neutuino::ansi::{COLORS_BG, COLORS_FG, STYLE_BOLD, STYLE_ITALIC, STYLE_RESET, STYLE_UNDERLINE, move_cursor_to_column, set_window_title}; +use neutuino::os::{enable_ansi, get_terminal_size}; +use neutuino::input::{poll_input, Event, KeyEvent}; + + +fn print_line_style_reset(string: &str) { + println!("{}{}{}", string, STYLE_RESET, move_cursor_to_column(0)); +} + +fn main() -> io::Result<()> { + let all_styles = format!("{STYLE_BOLD}{STYLE_ITALIC}{STYLE_UNDERLINE}"); + let _raw_terminal = neutuino::os::RawTerminal::new()?; + enable_ansi()?; + + println!("q to quit{}", move_cursor_to_column(0)); + let next = |x: usize| (x + 1) % COLORS_FG.len(); + + let terminal_size = get_terminal_size()?; + let terminal_size_str = format!("{terminal_size:?}"); + print!("{}", set_window_title(terminal_size_str).unwrap()); + + let mut counter = 0; + + loop { + let mut input = Err(io::ErrorKind::Other.into()); + while input.is_err() { + input = poll_input(Duration::new(1, 0)); + } + let input = input.unwrap(); + let string = format!("{input:?}"); + print_line_style_reset(&format!( + "{all_styles}{}{}{string}", + COLORS_FG[counter], + COLORS_BG[next(counter)] + )); + // q to quit + if input == Event::Key(KeyEvent::Char('q')) { + break; + } + counter = next(counter); + } + Ok(()) +} diff --git a/src/ansi.rs b/src/ansi.rs index a8493d7..6800479 100644 --- a/src/ansi.rs +++ b/src/ansi.rs @@ -208,3 +208,39 @@ pub const COLOR_WHITE_BG: &str = "\x1b[47m"; pub const COLOR_DEFAULT_FG: &str = "\x1b[39m"; /// Makes characters sent to the screen have a default background pub const COLOR_DEFAULT_BG: &str = "\x1b[49m"; + +pub const COLORS_FG: [&str; 9] = [ + COLOR_BLACK_FG, + COLOR_RED_FG, + COLOR_GREEN_FG, + COLOR_YELLOW_FG, + COLOR_BLUE_FG, + COLOR_MAGENTA_FG, + COLOR_CYAN_FG, + COLOR_WHITE_FG, + COLOR_DEFAULT_FG, +]; + +pub const COLORS_BG: [&str; 9] = [ + COLOR_BLACK_BG, + COLOR_RED_BG, + COLOR_GREEN_BG, + COLOR_YELLOW_BG, + COLOR_BLUE_BG, + COLOR_MAGENTA_BG, + COLOR_CYAN_BG, + COLOR_WHITE_BG, + COLOR_DEFAULT_BG, +]; + +pub const COLORS: [(&str, &str); 9] = [ + (COLOR_BLACK_FG, COLOR_BLACK_BG), + (COLOR_RED_FG, COLOR_RED_BG), + (COLOR_GREEN_FG, COLOR_GREEN_BG), + (COLOR_YELLOW_FG, COLOR_YELLOW_BG), + (COLOR_BLUE_FG, COLOR_BLUE_BG), + (COLOR_MAGENTA_FG, COLOR_MAGENTA_BG), + (COLOR_CYAN_FG, COLOR_CYAN_BG), + (COLOR_WHITE_FG, COLOR_WHITE_BG), + (COLOR_DEFAULT_FG, COLOR_DEFAULT_BG), +]; diff --git a/src/input.rs b/src/input.rs index 0fdb6c6..b6cd9fa 100644 --- a/src/input.rs +++ b/src/input.rs @@ -16,6 +16,7 @@ mod windows_input; #[cfg(windows)] pub use windows_input::*; +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Event { Key(KeyEvent), Mouse(MouseEvent), @@ -23,6 +24,7 @@ pub enum Event { FocusLost, } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum KeyEvent { Backspace, Up, @@ -44,12 +46,14 @@ pub enum KeyEvent { Null, } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum MouseEvent { Press(MouseButton, u16, u16), Release(u16, u16), Hold(u16, u16), } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum MouseButton { Left, Right, diff --git a/src/unix_input.rs b/src/unix_input.rs index 385ebc7..dca16e4 100644 --- a/src/unix_input.rs +++ b/src/unix_input.rs @@ -32,18 +32,21 @@ impl Iterator for ReadIterator { type Item = io::Result; fn next(&mut self) -> Option { - let bytes_read = unsafe { read(self.fd, &raw mut self.buf as *mut c_void, 1) }; + let bytes_read = unsafe { read(self.fd, (&raw mut self.buf).cast::(), 1) }; - if bytes_read > 0 { - Some(Ok(self.buf)) - } else if bytes_read == 0 { - None - } else { - Some(Err(io::Error::last_os_error())) + match bytes_read { + 1.. => Some(Ok(self.buf)), + 0 => None, + _ => Some(Err(io::Error::last_os_error())), } } } +/// Attempts to fetch input from stdin +/// +/// # Errors +/// If the timeout has expired or +/// there was an error getting the data pub fn poll_input(timeout: Duration) -> io::Result { let mut fds = [PollFD { fd: STDIN_FILENO, @@ -51,6 +54,7 @@ pub fn poll_input(timeout: Duration) -> io::Result { revents: 0, }]; let result = unsafe { + #[allow(clippy::cast_possible_truncation)] poll( fds.as_mut_ptr(), fds.len() as c_ulong, @@ -59,14 +63,15 @@ pub fn poll_input(timeout: Duration) -> io::Result { }; let mut read_iter = ReadIterator::new(STDIN_FILENO); - if result > 0 { - let item = read_iter.next().ok_or(io::ErrorKind::InvalidData)??; - try_parse_event(item, &mut read_iter) - } else if result == 0 { - // The function timed out. - Err(io::ErrorKind::TimedOut.into()) - } else { - Err(io::Error::last_os_error()) + let timed_out: io::Error = io::ErrorKind::TimedOut.into(); + + match result { + 1.. => { + let item = read_iter.next().ok_or(timed_out)??; + try_parse_event(item, &mut read_iter) + } + 0 => Err(timed_out), + _ => Err(io::Error::last_os_error()), } }