Finish stuff up

This commit is contained in:
2025-06-22 12:13:03 -04:00
parent 6e4039bba6
commit 160414819e
5 changed files with 61 additions and 97 deletions

View File

@@ -9,7 +9,8 @@ This project is still highly work in progress and it will be a decent while unti
- [x] Output (Unix) - [x] Output (Unix)
- [x] Output (Windows) - [x] Output (Windows)
- [x] Input (Unix) (Appears to work, more testing needed) - [x] Input (Unix) (Appears to work, more testing needed)
- [ ] Input (Windows) (WIP) - [x] Input (Windows) (Appears to work, more testing needed)
- [ ] Input (Kitty)
- [ ] Events (Focus reporting, Bracketed-paste) (Unix) - [ ] Events (Focus reporting, Bracketed-paste) (Unix)
- [ ] Events (Focus reporting, Bracketed-paste) (Windows) - [ ] Events (Focus reporting, Bracketed-paste) (Windows)
- [ ] Mouse input (Unix) - [ ] Mouse input (Unix)

View File

@@ -1,8 +1,8 @@
#![warn(clippy::all, clippy::pedantic)] #![warn(clippy::all, clippy::pedantic)]
use neutuino::prelude::*; use neutuino::prelude::*;
use std::{io, time::Duration};
use std::io::IsTerminal; use std::io::IsTerminal;
use std::{io, time::Duration};
fn print_line_style_reset(string: &str) { fn print_line_style_reset(string: &str) {
println!("{}{}{}", string, STYLE_RESET, move_cursor_to_column(0)); println!("{}{}{}", string, STYLE_RESET, move_cursor_to_column(0));
@@ -38,7 +38,7 @@ fn main() -> io::Result<()> {
COLORS_BG[next(counter)] COLORS_BG[next(counter)]
)); ));
// q to quit // q to quit
if input == Event::Key(KeyEvent::Char('q')) { if input == Event::Key(Key::Char('q'), KeyType::Press, KeyModifiers::none()) {
break; break;
} }
counter = next(counter); counter = next(counter);

View File

@@ -12,18 +12,14 @@
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Event { pub enum Event {
/// An event that happens upon a key being pressed /// An event that happens upon a key being pressed
Key(KeyEvent), Key(Key, KeyType, KeyModifiers),
/// An event that happens upon focus to the terminal window being gained /// An event that happens upon focus to the terminal window being gained
FocusGained, FocusGained,
/// An event that happens upon focus to the terminal window being lost /// An event that happens upon focus to the terminal window being lost
FocusLost, FocusLost,
} }
/// An event that happens upon a key being pressed /// The base key that was pressed
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct KeyEvent(pub Key, pub KeyType, pub KeyModifiers);
/// An event that happens upon a key being pressed
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Key { pub enum Key {
/// The Backspace key /// The Backspace key
@@ -46,8 +42,6 @@ pub enum Key {
PageDown, PageDown,
/// The Tab key /// The Tab key
Tab, Tab,
/// Shift + Tab key
ShiftTab,
/// The delete key /// The delete key
Delete, Delete,
/// The insert key /// The insert key
@@ -119,12 +113,6 @@ pub enum KeyType {
Release, Release,
} }
impl From<KeyEvent> for Event {
fn from(value: KeyEvent) -> Self {
Self::Key(value)
}
}
#[cfg(unix)] #[cfg(unix)]
pub use crate::unix::input::*; pub use crate::unix::input::*;

View File

@@ -11,7 +11,6 @@ unsafe extern "C" {
const STDIN_FILENO: c_int = 0; const STDIN_FILENO: c_int = 0;
const STDOUT_FILENO: c_int = 1; const STDOUT_FILENO: c_int = 1;
const POLLIN: c_short = 1; const POLLIN: c_short = 1;
const ICRNL: c_uint = 0x40;
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
const TIOCGWINSZ: c_ulong = 0x5413; const TIOCGWINSZ: c_ulong = 0x5413;
@@ -146,7 +145,7 @@ pub mod os {
pub mod input { pub mod input {
use super::{POLLIN, STDIN_FILENO}; use super::{POLLIN, STDIN_FILENO};
use crate::input::{Event, Key, KeyEvent, KeyModifiers, KeyType}; use crate::input::{Event, Key, KeyModifiers, KeyType};
use std::ffi::{c_int, c_short, c_ulong, c_void}; use std::ffi::{c_int, c_short, c_ulong, c_void};
use std::io; use std::io;
use std::time::Duration; use std::time::Duration;
@@ -228,44 +227,40 @@ pub mod input {
{ {
match item { match item {
b'\x1b' => try_parse_ansi_sequence(iter), b'\x1b' => try_parse_ansi_sequence(iter),
b'\r' => Ok(Event::Key(KeyEvent( b'\r' => Ok(Event::Key(
Key::Char('\n'), Key::Char('\n'),
KeyType::Press, KeyType::Press,
KeyModifiers::none(), KeyModifiers::none(),
))), )),
b'\n' => Ok(Event::Key(KeyEvent( b'\n' => Ok(Event::Key(
Key::Char('j'), Key::Char('j'),
KeyType::Press, KeyType::Press,
KeyModifiers::none().ctrl(), KeyModifiers::none().ctrl(),
))), )),
b'\t' => Ok(Event::Key(KeyEvent( b'\t' => Ok(Event::Key(
Key::Char('\t'), Key::Char('\t'),
KeyType::Press, KeyType::Press,
KeyModifiers::none(), KeyModifiers::none(),
))), )),
b'\x7f' => Ok(Event::Key(KeyEvent( b'\x7f' => Ok(Event::Key(
Key::Backspace, Key::Backspace,
KeyType::Press, KeyType::Press,
KeyModifiers::none(), KeyModifiers::none(),
))), )),
b'\0' => Ok(Event::Key(KeyEvent( b'\0' => Ok(Event::Key(Key::Null, KeyType::Press, KeyModifiers::none())),
Key::Null, c @ b'\x01'..=b'\x1a' => Ok(Event::Key(
KeyType::Press,
KeyModifiers::none(),
))),
c @ b'\x01'..=b'\x1a' => Ok(Event::Key(KeyEvent(
Key::Char((c + 96) as char), Key::Char((c + 96) as char),
KeyType::Press, KeyType::Press,
KeyModifiers::none().ctrl(), KeyModifiers::none().ctrl(),
))), )),
c @ b'\x1c'..=b'\x1f' => Ok(Event::Key(KeyEvent( c @ b'\x1c'..=b'\x1f' => Ok(Event::Key(
Key::Char((c + 24) as char), Key::Char((c + 24) as char),
KeyType::Press, KeyType::Press,
KeyModifiers::none().ctrl(), KeyModifiers::none().ctrl(),
))), )),
c => { c => {
let character = parse_utf8_char(c, iter)?; let character = parse_utf8_char(c, iter)?;
Ok(Event::Key(KeyEvent( Ok(Event::Key(
Key::Char(parse_utf8_char(c, iter)?), Key::Char(parse_utf8_char(c, iter)?),
KeyType::Press, KeyType::Press,
KeyModifiers { KeyModifiers {
@@ -274,7 +269,7 @@ pub mod input {
ctrl: false, ctrl: false,
meta: false, meta: false,
}, },
))) ))
} }
} }
} }
@@ -302,11 +297,11 @@ pub mod input {
let error = io::Error::other("Could not parse event"); let error = io::Error::other("Could not parse event");
match iter.next() { match iter.next() {
Some(Ok(b'O')) => match iter.next() { Some(Ok(b'O')) => match iter.next() {
Some(Ok(val @ b'P'..=b's')) => Ok(Event::Key(KeyEvent( Some(Ok(val @ b'P'..=b's')) => Ok(Event::Key(
Key::F(1 + val - b'P'), Key::F(1 + val - b'P'),
KeyType::Press, KeyType::Press,
KeyModifiers::none(), KeyModifiers::none(),
))), )),
_ => Err(error), _ => Err(error),
}, },
Some(Ok(b'[')) => try_parse_csi_sequence(iter).ok_or(error), Some(Ok(b'[')) => try_parse_csi_sequence(iter).ok_or(error),
@@ -320,48 +315,24 @@ pub mod input {
{ {
match iter.next() { match iter.next() {
Some(Ok(b'[')) => match iter.next() { Some(Ok(b'[')) => match iter.next() {
Some(Ok(val @ b'A'..=b'E')) => Some(Event::Key(KeyEvent( Some(Ok(val @ b'A'..=b'E')) => Some(Event::Key(
Key::F(1 + val - b'A'), Key::F(1 + val - b'A'),
KeyType::Press, KeyType::Press,
KeyModifiers::none(), KeyModifiers::none(),
))), )),
_ => None, _ => None,
}, },
Some(Ok(b'D')) => Some(Event::Key(KeyEvent( Some(Ok(b'D')) => Some(Event::Key(Key::Left, KeyType::Press, KeyModifiers::none())),
Key::Left, Some(Ok(b'C')) => Some(Event::Key(Key::Right, KeyType::Press, KeyModifiers::none())),
Some(Ok(b'A')) => Some(Event::Key(Key::Up, KeyType::Press, KeyModifiers::none())),
Some(Ok(b'B')) => Some(Event::Key(Key::Down, KeyType::Press, KeyModifiers::none())),
Some(Ok(b'H')) => Some(Event::Key(Key::Home, KeyType::Press, KeyModifiers::none())),
Some(Ok(b'F')) => Some(Event::Key(Key::End, KeyType::Press, KeyModifiers::none())),
Some(Ok(b'Z')) => Some(Event::Key(
Key::Tab,
KeyType::Press, KeyType::Press,
KeyModifiers::none(), KeyModifiers::none().shift(),
))), )),
Some(Ok(b'C')) => Some(Event::Key(KeyEvent(
Key::Right,
KeyType::Press,
KeyModifiers::none(),
))),
Some(Ok(b'A')) => Some(Event::Key(KeyEvent(
Key::Up,
KeyType::Press,
KeyModifiers::none(),
))),
Some(Ok(b'B')) => Some(Event::Key(KeyEvent(
Key::Down,
KeyType::Press,
KeyModifiers::none(),
))),
Some(Ok(b'H')) => Some(Event::Key(KeyEvent(
Key::Home,
KeyType::Press,
KeyModifiers::none(),
))),
Some(Ok(b'F')) => Some(Event::Key(KeyEvent(
Key::End,
KeyType::Press,
KeyModifiers::none(),
))),
Some(Ok(b'Z')) => Some(Event::Key(KeyEvent(
Key::ShiftTab,
KeyType::Press,
KeyModifiers::none(),
))),
_ => None, _ => None,
} }
} }

View File

@@ -148,7 +148,7 @@ pub mod os {
pub mod input { pub mod input {
use super::get_stdin_handle; use super::get_stdin_handle;
use crate::input::{Event, KeyEvent, Key, KeyModifiers, KeyType}; use crate::input::{Event, Key, KeyModifiers, KeyType};
use std::os::windows::raw::HANDLE; use std::os::windows::raw::HANDLE;
use std::{io, mem, time::Duration}; use std::{io, mem, time::Duration};
@@ -241,7 +241,7 @@ pub mod input {
// more this will have to do // more this will have to do
return Err(io::ErrorKind::Other.into()); return Err(io::ErrorKind::Other.into());
} }
Ok(Event::Key(parse_key_event(&key_event))) Ok(parse_key_event(&key_event))
} }
_ => { _ => {
//TODO Make this better //TODO Make this better
@@ -250,41 +250,45 @@ pub mod input {
} }
} }
fn parse_key_event(event: &KeyEventRecord) -> KeyEvent { fn parse_key_event(event: &KeyEventRecord) -> Event {
let ctrl = event.control_key_state & (0x0008 | 0x0004) != 0; // LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED let ctrl = event.control_key_state & (0x0008 | 0x0004) != 0; // LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED
let shift = event.control_key_state & 0x0010 != 0; // SHIFT_PRESSED let shift = event.control_key_state & 0x0010 != 0; // SHIFT_PRESSED
match event.virtual_key_code { match event.virtual_key_code {
0x08 => KeyEvent(Key::Backspace, KeyType::Press, KeyModifiers::none()), 0x08 => Event::Key(Key::Backspace, KeyType::Press, KeyModifiers::none()),
0x09 => { 0x09 => {
if shift { if shift {
KeyEvent(Key::ShiftTab, KeyType::Press, KeyModifiers::none().shift()) Event::Key(Key::Tab, KeyType::Press, KeyModifiers::none().shift())
} else { } else {
KeyEvent(Key::Tab, KeyType::Press, KeyModifiers::none()) Event::Key(Key::Tab, KeyType::Press, KeyModifiers::none())
} }
} }
0x0D => KeyEvent(Key::Char('\n'), KeyType::Press, KeyModifiers::none()), 0x0D => Event::Key(Key::Char('\n'), KeyType::Press, KeyModifiers::none()),
0x1B => KeyEvent(Key::Escape, KeyType::Press, KeyModifiers::none()), 0x1B => Event::Key(Key::Escape, KeyType::Press, KeyModifiers::none()),
0x21 => KeyEvent(Key::PageUp, KeyType::Press, KeyModifiers::none()), 0x21 => Event::Key(Key::PageUp, KeyType::Press, KeyModifiers::none()),
0x22 => KeyEvent(Key::PageDown, KeyType::Press, KeyModifiers::none()), 0x22 => Event::Key(Key::PageDown, KeyType::Press, KeyModifiers::none()),
0x23 => KeyEvent(Key::End, KeyType::Press, KeyModifiers::none()), 0x23 => Event::Key(Key::End, KeyType::Press, KeyModifiers::none()),
0x24 => KeyEvent(Key::Home, KeyType::Press, KeyModifiers::none()), 0x24 => Event::Key(Key::Home, KeyType::Press, KeyModifiers::none()),
0x25 => KeyEvent(Key::Left, KeyType::Press, KeyModifiers::none()), 0x25 => Event::Key(Key::Left, KeyType::Press, KeyModifiers::none()),
0x26 => KeyEvent(Key::Up, KeyType::Press, KeyModifiers::none()), 0x26 => Event::Key(Key::Up, KeyType::Press, KeyModifiers::none()),
0x27 => KeyEvent(Key::Right, KeyType::Press, KeyModifiers::none()), 0x27 => Event::Key(Key::Right, KeyType::Press, KeyModifiers::none()),
0x28 => KeyEvent(Key::Down, KeyType::Press, KeyModifiers::none()), 0x28 => Event::Key(Key::Down, KeyType::Press, KeyModifiers::none()),
0x2D => KeyEvent(Key::Insert, KeyType::Press, KeyModifiers::none()), 0x2D => Event::Key(Key::Insert, KeyType::Press, KeyModifiers::none()),
0x2E => KeyEvent(Key::Delete, KeyType::Press, KeyModifiers::none()), 0x2E => Event::Key(Key::Delete, KeyType::Press, KeyModifiers::none()),
// I don't think anybody is going to try to press F256 clippy // I don't think anybody is going to try to press F256 clippy
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
0x70..=0x87 => KeyEvent(Key::F((event.virtual_key_code - 0x6F) as u8), KeyType::Press, KeyModifiers::none()), // F1-F24 0x70..=0x87 => Event::Key(
Key::F((event.virtual_key_code - 0x6F) as u8),
KeyType::Press,
KeyModifiers::none(),
), // F1-F24
_ => { _ => {
let c = let c =
char::from_u32(u32::from(unsafe { event.u_char.unicode_char })).unwrap_or(' '); char::from_u32(u32::from(unsafe { event.u_char.unicode_char })).unwrap_or(' ');
if ctrl && c.is_ascii_alphabetic() { if ctrl && c.is_ascii_alphabetic() {
KeyEvent(Key::Char(c), KeyType::Press, KeyModifiers::none().ctrl()) Event::Key(Key::Char(c), KeyType::Press, KeyModifiers::none().ctrl())
} else { } else {
KeyEvent(Key::Char(c), KeyType::Press, KeyModifiers::none()) Event::Key(Key::Char(c), KeyType::Press, KeyModifiers::none())
} }
} }
} }