mirror of
https://github.com/Xyverle/neutuino.git
synced 2026-06-26 22:23:14 -04:00
clean up & reorganize repo + fix clippy warnings
This commit is contained in:
@@ -181,6 +181,9 @@ pub fn exit_alternate_screen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_window_title(title: &str) {
|
pub fn set_window_title(title: &str) {
|
||||||
assert!(title.len() <= 255, "Title length longer than maximum of 255");
|
assert!(
|
||||||
|
title.len() <= 255,
|
||||||
|
"Title length longer than maximum of 255"
|
||||||
|
);
|
||||||
println!("\x1b]0;{title}\x1b\x5c");
|
println!("\x1b]0;{title}\x1b\x5c");
|
||||||
}
|
}
|
||||||
|
|||||||
211
src/input.rs
211
src/input.rs
@@ -1,191 +1,54 @@
|
|||||||
use std::mem;
|
use std::io::{self, Read};
|
||||||
use std::os::raw::c_int;
|
use std::sync::mpsc;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
#[derive(Debug)]
|
/// An asynchronous reader.
|
||||||
pub enum Key {
|
///
|
||||||
String(String),
|
/// This acts as any other stream, with the exception that reading from it won't block. Instead,
|
||||||
Char(char),
|
/// the buffer will only be partially updated based on how much the internal buffer holds.
|
||||||
Enter,
|
pub struct AsyncReader {
|
||||||
Escape,
|
recv: mpsc::Receiver<io::Result<u8>>,
|
||||||
Backspace,
|
|
||||||
Tab,
|
|
||||||
ShiftTab,
|
|
||||||
Delete,
|
|
||||||
Home,
|
|
||||||
End,
|
|
||||||
PageUp,
|
|
||||||
PageDown,
|
|
||||||
ArrowUp,
|
|
||||||
ArrowDown,
|
|
||||||
ArrowLeft,
|
|
||||||
ArrowRight,
|
|
||||||
Ctrl(char),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
impl Read for AsyncReader {
|
||||||
pub mod platform {
|
/// Read from the byte stream.
|
||||||
use super::*;
|
///
|
||||||
use std::fs::File;
|
/// This will never block, but try to drain the event queue until empty. If the total number of
|
||||||
use std::io::Read;
|
/// bytes written is lower than the buffer's length, the event queue is empty or that the event
|
||||||
use std::os::fd::AsRawFd;
|
/// stream halted.
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
unsafe extern "C" {
|
let mut total = 0;
|
||||||
fn tcgetattr(fd: c_int, termios_p: *mut Termios) -> c_int;
|
loop {
|
||||||
fn tcsetattr(fd: c_int, optional_actions: c_int, termios_p: *const Termios) -> c_int;
|
if total >= buf.len() {
|
||||||
fn fcntl(fd: c_int, cmd: c_int, arg: c_int) -> c_int;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
const TCSAFLUSH: c_int = 2;
|
|
||||||
const ICANON: c_int = 0o0002;
|
|
||||||
const ECHO: c_int = 0o0010;
|
|
||||||
const O_NONBLOCK: c_int = 0x800;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct Termios {
|
|
||||||
c_iflag: u32,
|
|
||||||
c_oflag: u32,
|
|
||||||
c_cflag: u32,
|
|
||||||
c_lflag: u32,
|
|
||||||
c_cc: [u8; 32],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RawInput {
|
|
||||||
stdin: File,
|
|
||||||
original_termios: Termios,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RawInput {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let stdin = File::open("/dev/tty").unwrap();
|
|
||||||
let fd = stdin.as_raw_fd();
|
|
||||||
let mut termios: Termios = unsafe { mem::zeroed() };
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
tcgetattr(fd, &mut termios);
|
|
||||||
let mut new_termios = termios.clone();
|
|
||||||
new_termios.c_lflag &= !(ICANON | ECHO) as u32;
|
|
||||||
tcsetattr(fd, TCSAFLUSH, &new_termios);
|
|
||||||
fcntl(fd, 4, O_NONBLOCK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
match self.recv.try_recv() {
|
||||||
stdin,
|
Ok(Ok(b)) => {
|
||||||
original_termios: termios,
|
buf[total] = b;
|
||||||
|
total += 1;
|
||||||
|
}
|
||||||
|
Ok(Err(e)) => return Err(e),
|
||||||
|
Err(_) => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_key(&mut self) -> [u8; 6] {
|
Ok(total)
|
||||||
let mut buf = [0; 6];
|
|
||||||
_ = self.stdin.read(&mut buf);
|
|
||||||
buf
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for RawInput {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { tcsetattr(self.stdin.as_raw_fd(), TCSAFLUSH, &self.original_termios) };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
impl AsyncReader {
|
||||||
pub mod platform {
|
pub fn new<R: Read + Send + 'static>(reader: R) -> Self {
|
||||||
use super::*;
|
let (send, recv) = mpsc::channel();
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
extern "system" {
|
thread::spawn(move || {
|
||||||
fn GetStdHandle(nStdHandle: c_int) -> isize;
|
for i in reader.bytes() {
|
||||||
fn SetConsoleMode(hConsoleHandle: isize, dwMode: u32) -> i32;
|
if send.send(i).is_err() {
|
||||||
fn GetConsoleMode(hConsoleHandle: isize, lpMode: *mut u32) -> i32;
|
return;
|
||||||
fn ReadConsoleInputW(hConsoleInput: isize, lpBuffer: *mut InputRecord, nLength: u32, lpNumberOfEventsRead: *mut u32) -> i32;
|
|
||||||
fn PeekConsoleInputW(hConsoleInput: isize, lpBuffer: *mut InputRecord, nLength: u32, lpNumberOfEventsRead: *mut u32) -> i32;
|
|
||||||
}
|
|
||||||
|
|
||||||
const STD_INPUT_HANDLE: c_int = -10;
|
|
||||||
const ENABLE_PROCESSED_INPUT: u32 = 0x0001;
|
|
||||||
const ENABLE_LINE_INPUT: u32 = 0x0002;
|
|
||||||
const ENABLE_ECHO_INPUT: u32 = 0x0004;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
struct InputRecord {
|
|
||||||
event_type: c_ushort,
|
|
||||||
event: KeyEventRecord,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
struct KeyEventRecord {
|
|
||||||
key_down: i32,
|
|
||||||
repeat_count: c_ushort,
|
|
||||||
virtual_key_code: c_ushort,
|
|
||||||
virtual_scan_code: c_ushort,
|
|
||||||
unicode_char: u16,
|
|
||||||
control_key_state: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RawInput {
|
|
||||||
handle: isize,
|
|
||||||
original_mode: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RawInput {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
unsafe {
|
|
||||||
let handle = GetStdHandle(STD_INPUT_HANDLE);
|
|
||||||
let mut mode = 0;
|
|
||||||
GetConsoleMode(handle, &mut mode);
|
|
||||||
SetConsoleMode(handle, mode & !(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));
|
|
||||||
|
|
||||||
Self {
|
|
||||||
handle,
|
|
||||||
original_mode: mode,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
pub fn read_key(&mut self) -> Option<Key> {
|
Self { recv }
|
||||||
let mut records: [InputRecord; 1] = unsafe { mem::zeroed() };
|
|
||||||
let mut num_read = 0;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
PeekConsoleInputW(self.handle, records.as_mut_ptr(), 1, &mut num_read);
|
|
||||||
if num_read == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadConsoleInputW(self.handle, records.as_mut_ptr(), 1, &mut num_read);
|
|
||||||
|
|
||||||
if records[0].event.key_down == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match records[0].event.virtual_key_code {
|
|
||||||
0x1B => Some(Key::Escape),
|
|
||||||
0x0D => Some(Key::Enter),
|
|
||||||
0x08 => Some(Key::Backspace),
|
|
||||||
0x09 => Some(Key::Tab),
|
|
||||||
0x2F => Some(Key::ShiftTab),
|
|
||||||
0x2E => Some(Key::Delete),
|
|
||||||
0x24 => Some(Key::Home),
|
|
||||||
0x23 => Some(Key::End),
|
|
||||||
0x21 => Some(Key::PageUp),
|
|
||||||
0x22 => Some(Key::PageDown),
|
|
||||||
0x26 => Some(Key::ArrowUp),
|
|
||||||
0x28 => Some(Key::ArrowDown),
|
|
||||||
0x25 => Some(Key::ArrowLeft),
|
|
||||||
0x27 => Some(Key::ArrowRight),
|
|
||||||
c if c >= 1 && c <= 26 => Some(Key::Ctrl((b'a' + (c - 1) as u8) as char)),
|
|
||||||
c if records[0].event.unicode_char != 0 => Some(Key::Char(records[0].event.unicode_char as u8 as char)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for RawInput {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { SetConsoleMode(self.handle, self.original_mode) };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
|
|
||||||
/// An asynchronous reader.
|
|
||||||
|
|
||||||
///
|
|
||||||
|
|
||||||
/// This acts as any other stream, with the exception that reading from it won't block. Instead,
|
|
||||||
|
|
||||||
/// the buffer will only be partially updated based on how much the internal buffer holds.
|
|
||||||
pub struct AsyncReader {
|
|
||||||
recv: mpsc::Receiver<io::Result<u8>>,
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Read for AsyncReader {
|
|
||||||
|
|
||||||
/// Read from the byte stream.
|
|
||||||
|
|
||||||
///
|
|
||||||
|
|
||||||
/// This will never block, but try to drain the event queue until empty. If the total number of
|
|
||||||
|
|
||||||
/// bytes written is lower than the buffer's length, the event queue is empty or that the event
|
|
||||||
|
|
||||||
/// stream halted.
|
|
||||||
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
|
|
||||||
let mut total = 0;
|
|
||||||
|
|
||||||
|
|
||||||
loop {
|
|
||||||
|
|
||||||
if total >= buf.len() {
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
match self.recv.try_recv() {
|
|
||||||
|
|
||||||
Ok(Ok(b)) => {
|
|
||||||
|
|
||||||
buf[total] = b;
|
|
||||||
|
|
||||||
total += 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Err(e)) => return Err(e),
|
|
||||||
|
|
||||||
Err(_) => break,
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Ok(total)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsyncReader {
|
|
||||||
pub fn<R: Read> new(reader: R) -> Self {
|
|
||||||
let (send, recv) = mpsc::channel();
|
|
||||||
|
|
||||||
thread::spawn(move || {
|
|
||||||
for i in reader.bytes() {
|
|
||||||
if send.send(i).is_err() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self { recv: recv }
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#![warn(clippy::all, clippy::pedantic)]
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
pub mod ansi;
|
pub mod ansi;
|
||||||
|
pub mod input;
|
||||||
pub mod os;
|
pub mod os;
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
#![allow(unused_imports)]
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
mod unix;
|
#[path = "unix.rs"] mod unix;
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
mod windows;
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use unix as os;
|
use unix as os;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[path = "windows.rs"] mod windows;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use windows as os;
|
use windows as os;
|
||||||
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
use std::ffi::{c_int, c_uint, c_ulong, c_ushort};
|
|
||||||
use std::io;
|
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
unsafe extern "C" {
|
|
||||||
fn isatty(fd: c_int) -> c_int;
|
|
||||||
fn ioctl(fd: c_int, request: c_ulong, argp: *mut u8) -> c_int;
|
|
||||||
fn tcgetattr(fd: c_int, termios_p: *mut Termios) -> c_int;
|
|
||||||
fn tcsetattr(fd: c_int, optional_actions: c_int, termios: *mut Termios) -> c_int;
|
|
||||||
fn cfmakeraw(termios: *mut Termios);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
const TIOCGWINSZ: c_ulong = 0x5413;
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
|
||||||
const TIOCGWINSZ: c_ulong = 0x40087468;
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
const NCCS: usize = 32;
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
|
||||||
const NCCS: usize = 20;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
struct Winsize {
|
|
||||||
row: c_ushort,
|
|
||||||
col: c_ushort,
|
|
||||||
xpixel: c_ushort,
|
|
||||||
ypixel: c_ushort,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Termios {
|
|
||||||
iflag: c_uint,
|
|
||||||
oflag: c_uint,
|
|
||||||
cflag: c_uint,
|
|
||||||
lflag: c_uint,
|
|
||||||
cc: [u8; NCCS],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct RawTerminal {
|
|
||||||
input: Termios,
|
|
||||||
output: Termios,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RawTerminal {
|
|
||||||
pub fn new() -> io::Result<RawTerminal> {
|
|
||||||
let mut term: RawTerminal = unsafe { mem::zeroed() };
|
|
||||||
if unsafe { tcgetattr(0, &raw mut term.input) } != 0 {
|
|
||||||
return Err(io::Error::last_os_error())
|
|
||||||
}
|
|
||||||
if unsafe { tcgetattr(1, &raw mut term.output) } != 0 {
|
|
||||||
return Err(io::Error::last_os_error())
|
|
||||||
}
|
|
||||||
let orig_term = term.clone();
|
|
||||||
unsafe { cfmakeraw(&raw mut term.input) };
|
|
||||||
unsafe { cfmakeraw(&raw mut term.output) };
|
|
||||||
if unsafe { tcsetattr(0, 0, &raw mut term.input) } != 0 {
|
|
||||||
return Err(io::Error::last_os_error())
|
|
||||||
}
|
|
||||||
if unsafe { tcsetattr(1, 0, &raw mut term.output) } != 0 {
|
|
||||||
return Err(io::Error::last_os_error())
|
|
||||||
}
|
|
||||||
Ok(orig_term)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for RawTerminal {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { tcsetattr(0, 0, &raw mut self.input) };
|
|
||||||
unsafe { tcsetattr(1, 0, &raw mut self.input) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn istty() -> bool {
|
|
||||||
unsafe { isatty(1) != 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_terminal_size() -> io::Result<(c_ushort, c_ushort)> {
|
|
||||||
let mut winsize = unsafe { std::mem::zeroed::<Winsize>() };
|
|
||||||
let ioctl_result = unsafe { ioctl(1, TIOCGWINSZ, &raw mut winsize as *mut u8) };
|
|
||||||
|
|
||||||
if ioctl_result == 0 {
|
|
||||||
Ok((winsize.col, winsize.row))
|
|
||||||
} else {
|
|
||||||
Err(io::Error::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enable_ansi() -> io::Result<()> {
|
|
||||||
// ANSI is on by default on unix platforms
|
|
||||||
// This is here for compatibility with the windows version of this API
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use std::ffi::{c_int, c_uint, c_ulong, c_ushort};
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
const STD_INPUT_HANDLE: u32 = 0xFFFFFFF6;
|
const STD_INPUT_HANDLE: u32 = 0xFFFF_FFF6;
|
||||||
const STD_OUTPUT_HANDLE: u32 = 0xFFFFFFF5;
|
const STD_OUTPUT_HANDLE: u32 = 0xFFFF_FFF5;
|
||||||
const ENABLE_VIRTUAL_TERMINAL_PROCESSING: u32 = 4;
|
const ENABLE_VIRTUAL_TERMINAL_PROCESSING: u32 = 4;
|
||||||
const ENABLE_ECHO_INPUT: u32 = 4;
|
const ENABLE_ECHO_INPUT: u32 = 4;
|
||||||
const ENABLE_LINE_INPUT: u32 = 2;
|
const ENABLE_LINE_INPUT: u32 = 2;
|
||||||
@@ -37,12 +36,12 @@ struct ConsoleScreenBufferInfo {
|
|||||||
dwMaximumWindowSizeY: u16,
|
dwMaximumWindowSizeY: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_terminal() -> bool {
|
#[must_use] pub fn is_terminal() -> bool {
|
||||||
let handle = get_std_handle(STD_OUTPUT_HANDLE);
|
let handle = get_std_handle(STD_OUTPUT_HANDLE);
|
||||||
match handle {
|
match handle {
|
||||||
Ok(handle) => {
|
Ok(handle) => {
|
||||||
let mut dwMode = 0;
|
let mut dwMode = 0;
|
||||||
return unsafe { GetConsoleMode(handle, &mut dwMode) != 0 };
|
unsafe { GetConsoleMode(handle, &mut dwMode) != 0 }
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
@@ -52,8 +51,8 @@ pub fn get_terminal_size() -> io::Result<(u16, u16)> {
|
|||||||
let handle = get_std_handle(STD_OUTPUT_HANDLE)?;
|
let handle = get_std_handle(STD_OUTPUT_HANDLE)?;
|
||||||
let mut csbi = ConsoleScreenBufferInfo::default();
|
let mut csbi = ConsoleScreenBufferInfo::default();
|
||||||
if unsafe { GetConsoleScreenBufferInfo(handle, &mut csbi) != 0 } {
|
if unsafe { GetConsoleScreenBufferInfo(handle, &mut csbi) != 0 } {
|
||||||
let width = csbi.dwSizeX as u16;
|
let width = csbi.dwSizeX;
|
||||||
let height = csbi.dwSizeY as u16;
|
let height = csbi.dwSizeY;
|
||||||
return Ok((width, height));
|
return Ok((width, height));
|
||||||
}
|
}
|
||||||
Err(io::Error::last_os_error())
|
Err(io::Error::last_os_error())
|
||||||
@@ -88,10 +87,10 @@ pub fn disable_raw_mode() -> io::Result<()> {
|
|||||||
|
|
||||||
fn get_std_handle(handle: u32) -> io::Result<usize> {
|
fn get_std_handle(handle: u32) -> io::Result<usize> {
|
||||||
let handle = unsafe { GetStdHandle(handle) };
|
let handle = unsafe { GetStdHandle(handle) };
|
||||||
if handle != INVALID_HANDLE_VALUE {
|
if handle == INVALID_HANDLE_VALUE {
|
||||||
return Ok(handle);
|
Err(io::Error::last_os_error())
|
||||||
} else {
|
} else {
|
||||||
return Err(io::Error::last_os_error());
|
Ok(handle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user