M src/app.rs => src/app.rs +39 -42
@@ 1,9 1,9 @@
use crossterm::{
- cursor,
- event::{self, KeyEvent, KeyModifiers},
- style::Print,
- terminal::{self, Clear, ClearType},
- QueueableCommand,
+ cursor,
+ event::{self, KeyEvent, KeyModifiers},
+ style::Print,
+ terminal::{self, Clear, ClearType},
+ QueueableCommand,
};
use std::cmp;
@@ 139,6 139,7 @@ impl InputBuffer {
self.span_len(),
self.buffer.len()
);
+
stdout
.queue(cursor::MoveTo(0, 1))?
.queue(Clear(ClearType::CurrentLine))?
@@ 168,52 169,64 @@ pub struct Shell {
term_rows: u16,
buffer: InputBuffer,
- core: Core,
+ lines: Vec<String>,
is_running: bool,
}
impl Shell {
- pub fn new() -> anyhow::Result<Self> {
+ pub fn new() -> anyhow::Result<Self> {
let (cols, rows) = terminal::size()?;
- Ok(Self {
+ Ok(Self {
term_cols: cols,
term_rows: rows,
buffer: InputBuffer::new(),
- core: Core::new(),
+ lines: vec![],
is_running: true,
- })
+ })
}
- pub fn present(&mut self) -> anyhow::Result<()> {
- let mut stdout = io::stdout();
+ pub fn present(&mut self) -> anyhow::Result<()> {
+ let mut stdout = io::stdout();
+ stdout.queue(cursor::Hide)?;
- // save the input cursor position
- stdout
- //.queue(cursor::SavePosition)?
- .queue(cursor::Hide)?;
-
// draw title line
stdout
.queue(cursor::MoveTo(0, 0))?
.queue(Clear(ClearType::CurrentLine))?
.queue(Print(self.status_line()))?;
-
- // redraw current buffer lines except for prompt
- for i in 2..(self.term_rows - 1) {
- stdout
- .queue(cursor::MoveTo(0, i))?
- .queue(Clear(ClearType::CurrentLine))?;
- }
self.buffer.render(&mut stdout, 0, self.term_rows, self.term_cols)?;
+ stdout.queue(cursor::SavePosition)?;
+
+ // redraw current buffer lines except for prompt
+ let mut last_n_lines = self.lines
+ .iter()
+ .rev() // sort from newest to oldest
+ .take(self.term_rows as usize - 3) // take last n lines, minus room for header & footer
+ .rev(); // reverse again to sort them back into order they were received
+
+ for i in 2..(self.term_rows - 1) {
+ stdout
+ .queue(cursor::MoveTo(0, i))?
+ .queue(Clear(ClearType::CurrentLine))?;
+
+ if let Some(line) = last_n_lines.next() {
+ stdout.queue(Print(&line))?;
+ }
+ }
// restore cursor position
stdout
- //.queue(cursor::RestorePosition)?
+ .queue(cursor::RestorePosition)?
.queue(cursor::Show)?;
- Ok(stdout.flush()?)
+
+ Ok(stdout.flush()?)
+ }
+
+ pub fn event_message(&mut self, line: String) {
+ self.lines.push(line);
}
pub fn event_keydown(&mut self, event: event::KeyEvent) {
@@ 261,23 274,7 @@ impl Shell {
pub fn is_running(&self) -> bool { self.is_running }
-
-
fn status_line(&self) -> String {
format!("linetest - rows [{}] cols [{}]", self.term_rows, self.term_cols)
}
-}
-
-pub struct Core {
-
-}
-
-impl Core {
- pub fn new() -> Self {
- Self {
-
- }
- }
-
-
}=
\ No newline at end of file
M src/main.rs => src/main.rs +12 -56
@@ 1,67 1,23 @@
-use crossterm::{
- cursor,
- event::{self, Event},
- terminal::{EnterAlternateScreen, LeaveAlternateScreen},
- ExecutableCommand,
-};
-
-use std::io;
-use std::time::Duration;
-
+use std::thread;
mod app;
-
+mod net;
+mod ui;
fn main() -> anyhow::Result<()> {
- // take over user's terminal
- crossterm::terminal::enable_raw_mode()?;
- io::stdout()
- .execute(EnterAlternateScreen)?
- .execute(cursor::Hide)?;
-
- // block on UI thread
- ui_thread()?;
-
-
- // return to user's previous terminal
- io::stdout()
- .execute(LeaveAlternateScreen)?
- .execute(cursor::Show)?;
-
- crossterm::terminal::disable_raw_mode()?;
-
- Ok(())
-}
-
-fn ui_thread() -> anyhow::Result<()> {
-
- let mut shell = app::Shell::new()?;
- let poll_hz = Duration::from_millis(1000 / 120);
+ // create channels just to keep task thread alive
+ let (chan_tx, chan_rx) = async_channel::bounded(1024);
-
-
- while shell.is_running() {
- shell.present()?;
- if !event::poll(poll_hz)? { continue }
-
- match event::read()? {
- Event::Key(key_evt) => {
- shell.event_keydown(key_evt);
- },
-
- Event::Resize(cols, rows) => {
- shell.event_resize(cols, rows);
- },
-
- _ => {},
- }
+ // run shell on its own dedicated thread
+ let ui_handle = thread::spawn(|| { ui::start_task(chan_rx) });
+ // TODO: spawn up multiple async workers
+ smol::run(net::start_task(chan_tx))?;
+ // probably having a bad day if we get here ...
+ if let Err(msg) = ui_handle.join() {
+ panic!("err: {:?}", msg);
}
-
-
-
-
Ok(())
}=
\ No newline at end of file
A src/net.rs => src/net.rs +15 -0
@@ 0,0 1,15 @@
+use async_channel::{bounded, Sender, Receiver};
+use std::time::Duration;
+
+pub async fn start_task(shell_tx: Sender<String>) -> anyhow::Result<()> {
+ let mut counter = 0;
+
+ loop {
+ smol::Timer::after(Duration::from_secs(1)).await;
+ shell_tx.send(format!("async task count #{}", counter)).await?;
+ counter += 1;
+ }
+
+
+ Ok(())
+}<
\ No newline at end of file
A src/ui.rs => src/ui.rs +59 -0
@@ 0,0 1,59 @@
+use crate::app;
+
+use async_channel::{Receiver, TryRecvError};
+use crossterm::{
+ cursor,
+ event::{self, Event},
+ terminal::{EnterAlternateScreen, LeaveAlternateScreen},
+ ExecutableCommand,
+};
+
+use std::io;
+use std::time::Duration;
+
+pub fn start_task(rx: Receiver<String>) -> anyhow::Result<()> {
+ let mut shell = app::Shell::new()?;
+ let poll_hz = Duration::from_millis(1000 / 120);
+
+ // take over user's terminal
+ crossterm::terminal::enable_raw_mode()?;
+
+ io::stdout()
+ .execute(EnterAlternateScreen)?
+ .execute(cursor::Hide)?;
+
+ // loop over crossterm events every 120Hz
+ while shell.is_running() {
+ match rx.try_recv() {
+ Ok(line) => shell.event_message(line),
+ Err(TryRecvError::Empty) => {},
+ Err(TryRecvError::Closed) => panic!("channel closed?"),
+ }
+
+ shell.present()?;
+ if !event::poll(poll_hz)? { continue }
+
+ match event::read()? {
+ Event::Key(key_evt) => {
+ shell.event_keydown(key_evt);
+ },
+
+ Event::Resize(cols, rows) => {
+ shell.event_resize(cols, rows);
+ },
+
+ _ => {},
+ }
+
+
+ }
+
+ // return to user's previous terminal
+ io::stdout()
+ .execute(LeaveAlternateScreen)?
+ .execute(cursor::Show)?;
+
+ crossterm::terminal::disable_raw_mode()?;
+
+ Ok(())
+}<
\ No newline at end of file