From f25a79763b9ae493598230a109eb0788def17199 Mon Sep 17 00:00:00 2001 From: Irene Knapp Date: Tue, 12 Mar 2024 22:06:24 -0700 Subject: separate the main from the library, move it to an example Change-Id: I3478f222ee4b24d9d1796f0f58986186119bc2f7 --- src/terminal.rs | 271 -------------------------------------------------------- 1 file changed, 271 deletions(-) delete mode 100644 src/terminal.rs (limited to 'src/terminal.rs') diff --git a/src/terminal.rs b/src/terminal.rs deleted file mode 100644 index 405a719..0000000 --- a/src/terminal.rs +++ /dev/null @@ -1,271 +0,0 @@ -#![forbid(unsafe_code)] -use crate::terminal::prelude::*; - -use crate::terminal::decoding::CharBufReader; - -use nix::sys::termios::{self, Termios}; -use std::os::unix::io::{AsRawFd, RawFd}; -use std::sync::Arc; -use tokio::io::{self, AsyncRead, AsyncWriteExt}; -use tokio::signal::unix::{signal, SignalKind}; -use tokio::sync::{mpsc, Mutex}; -use tokio::task; - -pub mod decoding; -pub mod error; -pub mod prelude; - - -#[derive(Clone,Debug,Eq,Hash,Ord,PartialEq,PartialOrd)] -struct Point { - x: usize, - y: usize, -} - -impl Point { - pub fn new(x: usize, y: usize) -> Point { - Point { - x: x, - y: y, - } - } -} - - -#[derive(Debug,Eq,PartialEq)] -struct LineBuffer { - lines: Vec, - cursor: Point, -} - -impl LineBuffer { - pub fn new() -> LineBuffer { - LineBuffer { - lines: vec![String::new()], - cursor: Point::new(0, 0), - } - } - - pub fn insert(&mut self, input: &str) { - let line = &mut self.lines[self.cursor.y]; - line.replace_range(self.cursor.x .. self.cursor.x, input); - self.cursor.x += input.len(); - } - - pub fn backspace(&mut self) { - if self.cursor.x > 0 { - let line = &mut self.lines[self.cursor.y]; - line.replace_range(self.cursor.x - 1 .. self.cursor.x, ""); - self.cursor.x -= 1; - } else if self.cursor.y > 0 { - let second_line = self.lines.remove(self.cursor.y); - let first_line = &mut self.lines[self.cursor.y - 1]; - - self.cursor.x = first_line.len(); - self.cursor.y -= 1; - - first_line.push_str(&second_line); - } - } - - pub fn as_string(&self) -> String { - self.lines.join("\n") - } - - pub fn cursor_to_end_of_line(&self) -> &str { - &self.lines[self.cursor.y][self.cursor.x ..] - } -} - - -pub enum Input { - String(String), - End, -} - - -#[derive(Clone,Debug,Eq,Hash,Ord,PartialEq,PartialOrd)] -enum InputAction { - Backspace, - Execute, -} - - -pub struct Terminal { - reader: Arc>>, - line_buffer: LineBuffer, - file_descriptor: RawFd, - initial_termios: Termios, - interrupt_receiver: mpsc::Receiver<()>, -} - - -async fn handle_signals(interrupt_sender: mpsc::Sender<()>) -> Result<()> -{ - let mut stream = signal(SignalKind::interrupt()).map_err(error::internal)?; - /* TODO make it work on other signals: - SignalKind::hangup(); - SignalKind::interrupt(); - SignalKind::terminate(); - SignalKind::quit(); - */ - - loop { - stream.recv().await; - interrupt_sender.send(()).await.map_err(error::internal)?; - } -} - - -impl Terminal { - pub fn init(input_stream: InputStream) -> Result> { - let (interrupt_sender, interrupt_receiver) = mpsc::channel(1); - - let _ = task::spawn(handle_signals(interrupt_sender)); - - let fd = input_stream.as_raw_fd(); - let termios = termios::tcgetattr(fd).map_err(error::mode_setting)?; - let reader = Arc::new(Mutex::new(CharBufReader::new(input_stream))); - let line_buffer = LineBuffer::new(); - - let terminal = Terminal { - reader: reader, - line_buffer: line_buffer, - file_descriptor: fd, - initial_termios: termios, - interrupt_receiver: interrupt_receiver, - }; - - terminal.init_modes()?; - - Ok(terminal) - } - - - pub fn cleanup(self) -> Result<()> { - self.cleanup_modes()?; - - Ok(()) - } - - - fn init_modes(&self) -> Result<()> { - let mut termios = self.initial_termios.clone(); - - termios.local_flags.remove(termios::LocalFlags::ECHO); - termios.local_flags.remove(termios::LocalFlags::ECHONL); - termios.local_flags.remove(termios::LocalFlags::ECHOCTL); - termios.local_flags.remove(termios::LocalFlags::ICANON); - - termios.local_flags.insert(termios::LocalFlags::ISIG); - - termios.control_chars[termios::SpecialCharacterIndices::VMIN as usize] = 1; - termios.control_chars[termios::SpecialCharacterIndices::VTIME as usize] = 0; - - termios::tcsetattr(self.file_descriptor, - termios::SetArg::TCSANOW, - &termios) - .map_err(error::mode_setting)?; - - Ok(()) - } - - - pub fn cleanup_modes(&self) -> Result<()> { - let termios = self.initial_termios.clone(); - - termios::tcsetattr(self.file_descriptor, - termios::SetArg::TCSANOW, - &termios) - .map_err(error::mode_setting)?; - - Ok(()) - } - - - pub async fn handle_input(&mut self) -> Result - { - let mut stdout = io::stdout(); - - loop { - let mut action: Option = None; - - let string = tokio::select! { - result = CharBufReader::fill_buf(Arc::clone(&self.reader)) => { - let string: String = result.map_err(error::input)?; - string - } - _ = self.interrupt_receiver.recv() => { - return Ok(Input::End); - } - }; - - let mut chars = string.char_indices(); - - for (_, c) in &mut chars { - match c { - '\n' => { - action = Some(InputAction::Execute); - } - '\u{7f}' => { - action = Some(InputAction::Backspace); - } - _ => { - self.line_buffer.insert(&c.to_string()); - - stdout.write_all(format!("{}", c).as_bytes()).await - .map_err(error::internal)?; - stdout.flush().await.map_err(error::internal)?; - } - } - - if action.is_some() { - break; - } - } - - let n_to_consume = match chars.next() { - Some((offset, _)) => offset, - None => string.len(), - }; - - Arc::get_mut(&mut self.reader).unwrap().lock().await.consume(n_to_consume); - - match action { - Some(InputAction::Execute) => { - break; - } - Some(InputAction::Backspace) => { - self.line_buffer.backspace(); - - stdout.write_all(format!("\u{08}\u{1b}[1X{}", - self.line_buffer.cursor_to_end_of_line()) - .as_bytes()).await.map_err(error::internal)?; - stdout.flush().await.map_err(error::internal)?; - } - None => { } - } - } - - println!("line buffer {:?}", self.line_buffer); - let input = Input::String(self.line_buffer.as_string()); - Ok(input) - } -} - - -/* -#[cfg(test)] -mod tests { - use super::*; - use std::io::Cursor; - - #[test] - fn test_empty_input() { - let buffer = Cursor::new(vec![]); - let terminal = Terminal::init(buffer).unwrap(); - let result = terminal.handle_input(); - assert!(result.is_ok()); - } -} -*/ -- cgit 1.4.1