mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
move tui run methods to in/out and relax Sized constraint
This commit is contained in:
parent
44201ebf76
commit
6fd87ce4ed
5 changed files with 169 additions and 211 deletions
|
|
@ -23,12 +23,9 @@ mod tui_border; pub use self::tui_border::*;
|
||||||
mod tui_field; pub use self::tui_field::*;
|
mod tui_field; pub use self::tui_field::*;
|
||||||
mod tui_buffer; pub use self::tui_buffer::*;
|
mod tui_buffer; pub use self::tui_buffer::*;
|
||||||
mod tui_file; pub use self::tui_file::*;
|
mod tui_file; pub use self::tui_file::*;
|
||||||
|
|
||||||
mod tui_edn_keymap; pub use self::tui_edn_keymap::*;
|
mod tui_edn_keymap; pub use self::tui_edn_keymap::*;
|
||||||
|
pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}};
|
||||||
pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicUsize, AtomicBool, Ordering::*}};
|
|
||||||
pub(crate) use std::io::{stdout, Stdout};
|
pub(crate) use std::io::{stdout, Stdout};
|
||||||
pub(crate) use std::error::Error;
|
|
||||||
pub(crate) use std::path::PathBuf;
|
pub(crate) use std::path::PathBuf;
|
||||||
pub(crate) use std::ffi::OsString;
|
pub(crate) use std::ffi::OsString;
|
||||||
|
|
||||||
|
|
@ -49,7 +46,7 @@ pub use ::crossterm;
|
||||||
pub(crate) use crossterm::{
|
pub(crate) use crossterm::{
|
||||||
ExecutableCommand,
|
ExecutableCommand,
|
||||||
terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode},
|
terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode},
|
||||||
event::{KeyCode, KeyModifiers, KeyEvent, KeyEventKind, KeyEventState},
|
event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use ::ratatui;
|
pub use ::ratatui;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
impl EdnInput for TuiIn {
|
impl EdnInput for TuiIn {
|
||||||
fn matches_edn (&self, token: &str) -> bool {
|
fn matches_edn (&self, token: &str) -> bool {
|
||||||
if let Some(event) = KeyMatcher::new(token).build() {
|
if let Some(event) = KeyMatcher::new(token).build() {
|
||||||
|
|
@ -80,7 +79,6 @@ impl KeyMatcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)] #[test] fn test_parse_key () {
|
#[cfg(test)] #[test] fn test_parse_key () {
|
||||||
use KeyModifiers as Mods;
|
use KeyModifiers as Mods;
|
||||||
let test = |x: &str, y|assert_eq!(KeyMatcher::new(x).build(), Some(Event::Key(y)));
|
let test = |x: &str, y|assert_eq!(KeyMatcher::new(x).build(), Some(Event::Key(y)));
|
||||||
|
|
@ -95,41 +93,3 @@ impl KeyMatcher {
|
||||||
test(":ctrl-alt-shift-x",
|
test(":ctrl-alt-shift-x",
|
||||||
KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL | Mods::ALT | Mods::SHIFT ));
|
KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL | Mods::ALT | Mods::SHIFT ));
|
||||||
}
|
}
|
||||||
|
|
||||||
//fn parse_key (spec: std::str::Chars<'_>, mut mods: KeyModifiers) -> Option<Event> {
|
|
||||||
//Some(Event::Key(match spec.chars() {
|
|
||||||
//":enter" | ":return" => KeyEvent::new(KeyCode::Enter, mods),
|
|
||||||
//":delete" | ":del" => KeyEvent::new(KeyCode::Enter, mods),
|
|
||||||
|
|
||||||
//":comma" => KeyEvent::new(KeyCode::Char(','), mods),
|
|
||||||
//":period" => KeyEvent::new(KeyCode::Char('.'), mods),
|
|
||||||
|
|
||||||
//":plus" => KeyEvent::new(KeyCode::Char('+'), mods),
|
|
||||||
//":underscore" => KeyEvent::new(KeyCode::Char('_'), mods),
|
|
||||||
|
|
||||||
//":up" => KeyEvent::new(KeyCode::Up, mods),
|
|
||||||
//":down" => KeyEvent::new(KeyCode::Down, mods),
|
|
||||||
//":left" => KeyEvent::new(KeyCode::Left, mods),
|
|
||||||
//":right" => KeyEvent::new(KeyCode::Right, mods),
|
|
||||||
|
|
||||||
//_ => {
|
|
||||||
//let chars = spec.chars().collect::<Vec<_>>();
|
|
||||||
//match chars.as_slice() {
|
|
||||||
//[':', c] => KeyEvent::new(KeyCode::Char(*c), mods),
|
|
||||||
//[':', c @ ..] => {
|
|
||||||
//let mut splits = c.split(|c|*c=='-');
|
|
||||||
//while let Some(split) = splits.next() {
|
|
||||||
//match split {
|
|
||||||
//['c','t','r','l'] => mods |= KeyModifiers::CONTROL,
|
|
||||||
//['a','l','t'] => mods |= KeyModifiers::ALT,
|
|
||||||
//['s','h','i','f','t'] => mods |= KeyModifiers::SHIFT,
|
|
||||||
//_ => return parse_key(split.iter().collect(), mods)
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//panic!("invalid key event {spec:?}")
|
|
||||||
//}
|
|
||||||
//_ => panic!("invalid key event {spec:?}")
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}))
|
|
||||||
//}
|
|
||||||
|
|
|
||||||
|
|
@ -1,107 +1,135 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
pub use crossterm::event::Event;
|
use std::time::Duration;
|
||||||
use Event as CrosstermEvent;
|
use std::thread::{spawn, JoinHandle};
|
||||||
|
use crossterm::event::{poll, read};
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)] pub struct TuiIn(pub Arc<AtomicBool>, pub Event);
|
||||||
pub struct TuiIn(pub Arc<AtomicBool>, pub CrosstermEvent);
|
|
||||||
|
|
||||||
impl Input for TuiIn {
|
impl Input for TuiIn {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
type Handled = bool;
|
type Handled = bool;
|
||||||
fn event (&self) -> &CrosstermEvent { &self.1 }
|
fn event (&self) -> &Event { &self.1 }
|
||||||
fn is_done (&self) -> bool { self.0.fetch_and(true, Relaxed) }
|
fn is_done (&self) -> bool { self.0.fetch_and(true, Relaxed) }
|
||||||
fn done (&self) { self.0.store(true, Relaxed); }
|
fn done (&self) { self.0.store(true, Relaxed); }
|
||||||
}
|
}
|
||||||
|
impl TuiIn {
|
||||||
|
/// Spawn the input thread.
|
||||||
|
pub fn run_input <T: Handle<TuiIn> + 'static> (
|
||||||
|
engine: &Arc<RwLock<Tui>>,
|
||||||
|
state: &Arc<RwLock<T>>,
|
||||||
|
timer: Duration
|
||||||
|
) -> JoinHandle<()> {
|
||||||
|
let exited = engine.read().unwrap().exited.clone();
|
||||||
|
let state = state.clone();
|
||||||
|
spawn(move || loop {
|
||||||
|
if exited.fetch_and(true, Relaxed) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if poll(timer).is_ok() {
|
||||||
|
let event = read().unwrap();
|
||||||
|
match event {
|
||||||
|
|
||||||
/// Define a key
|
crossterm::event::Event::Key(KeyEvent {
|
||||||
pub const fn key (code: KeyCode) -> Event {
|
code: KeyCode::Char('c'),
|
||||||
let modifiers = KeyModifiers::NONE;
|
modifiers: KeyModifiers::CONTROL,
|
||||||
let kind = KeyEventKind::Press;
|
kind: KeyEventKind::Press,
|
||||||
let state = KeyEventState::NONE;
|
state: KeyEventState::NONE
|
||||||
Event::Key(KeyEvent { code, modifiers, kind, state })
|
}) => {
|
||||||
}
|
exited.store(true, Relaxed);
|
||||||
|
},
|
||||||
/// Add Ctrl modifier to key
|
_ => {
|
||||||
pub const fn ctrl (event: Event) -> Event {
|
let exited = exited.clone();
|
||||||
match event {
|
if let Err(e) = state.write().unwrap().handle(&TuiIn(exited, event)) {
|
||||||
Event::Key(mut event) => {
|
panic!("{e}")
|
||||||
event.modifiers = event.modifiers.union(KeyModifiers::CONTROL)
|
}
|
||||||
},
|
}
|
||||||
_ => {}
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
event
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add Alt modifier to key
|
|
||||||
pub const fn alt (event: Event) -> Event {
|
|
||||||
match event {
|
|
||||||
Event::Key(mut event) => {
|
|
||||||
event.modifiers = event.modifiers.union(KeyModifiers::ALT)
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
event
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add Shift modifier to key
|
|
||||||
pub const fn shift (event: Event) -> Event {
|
|
||||||
match event {
|
|
||||||
Event::Key(mut event) => {
|
|
||||||
event.modifiers = event.modifiers.union(KeyModifiers::SHIFT)
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
event
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export] macro_rules! kpat {
|
|
||||||
(Ctrl-Alt-$code:pat) => { kpat!($code, KeyModifiers::CONTROL | KeyModifiers::ALT) };
|
|
||||||
(Ctrl-$code:pat) => { kpat!($code, KeyModifiers::CONTROL) };
|
|
||||||
(Alt-$code:pat) => { kpat!($code, KeyModifiers::ALT) };
|
|
||||||
(Shift-$code:pat) => { kpat!($code, KeyModifiers::SHIFT) };
|
|
||||||
($code:pat) => {
|
|
||||||
crossterm::event::Event::Key(KeyEvent {
|
|
||||||
code: $code,
|
|
||||||
modifiers: KeyModifiers::NONE,
|
|
||||||
kind: KeyEventKind::Press,
|
|
||||||
state: KeyEventState::NONE
|
|
||||||
})
|
|
||||||
};
|
|
||||||
($code:pat, $modifiers: pat) => {
|
|
||||||
crossterm::event::Event::Key(KeyEvent {
|
|
||||||
code: $code,
|
|
||||||
modifiers: $modifiers,
|
|
||||||
kind: KeyEventKind::Press,
|
|
||||||
state: KeyEventState::NONE
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export] macro_rules! kexp {
|
|
||||||
(Ctrl-Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::from_bits(0b0000_0110).unwrap()) };
|
|
||||||
(Ctrl-$code:ident) => { key_event_expr!($code, KeyModifiers::CONTROL) };
|
|
||||||
(Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::ALT) };
|
|
||||||
(Shift-$code:ident) => { key_event_expr!($code, KeyModifiers::SHIFT) };
|
|
||||||
($code:ident) => { key_event_expr!($code) };
|
|
||||||
($code:expr) => { key_event_expr!($code) };
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export] macro_rules! key_event_expr {
|
|
||||||
($code:expr, $modifiers: expr) => {
|
|
||||||
crossterm::event::Event::Key(KeyEvent {
|
|
||||||
code: $code,
|
|
||||||
modifiers: $modifiers,
|
|
||||||
kind: KeyEventKind::Press,
|
|
||||||
state: KeyEventState::NONE
|
|
||||||
})
|
|
||||||
};
|
|
||||||
($code:expr) => {
|
|
||||||
crossterm::event::Event::Key(KeyEvent {
|
|
||||||
code: $code,
|
|
||||||
modifiers: KeyModifiers::NONE,
|
|
||||||
kind: KeyEventKind::Press,
|
|
||||||
state: KeyEventState::NONE
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
//.
|
||||||
|
///// Define a key
|
||||||
|
//pub const fn key (code: KeyCode) -> Event {
|
||||||
|
//let modifiers = KeyModifiers::NONE;
|
||||||
|
//let kind = KeyEventKind::Press;
|
||||||
|
//let state = KeyEventState::NONE;
|
||||||
|
//Event::Key(KeyEvent { code, modifiers, kind, state })
|
||||||
|
//}
|
||||||
|
///// Add Ctrl modifier to key
|
||||||
|
//pub const fn ctrl (event: Event) -> Event {
|
||||||
|
//match event {
|
||||||
|
//Event::Key(mut event) => {
|
||||||
|
//event.modifiers = event.modifiers.union(KeyModifiers::CONTROL)
|
||||||
|
//},
|
||||||
|
//_ => {}
|
||||||
|
//}
|
||||||
|
//event
|
||||||
|
//}
|
||||||
|
///// Add Alt modifier to key
|
||||||
|
//pub const fn alt (event: Event) -> Event {
|
||||||
|
//match event {
|
||||||
|
//Event::Key(mut event) => {
|
||||||
|
//event.modifiers = event.modifiers.union(KeyModifiers::ALT)
|
||||||
|
//},
|
||||||
|
//_ => {}
|
||||||
|
//}
|
||||||
|
//event
|
||||||
|
//}
|
||||||
|
///// Add Shift modifier to key
|
||||||
|
//pub const fn shift (event: Event) -> Event {
|
||||||
|
//match event {
|
||||||
|
//Event::Key(mut event) => {
|
||||||
|
//event.modifiers = event.modifiers.union(KeyModifiers::SHIFT)
|
||||||
|
//},
|
||||||
|
//_ => {}
|
||||||
|
//}
|
||||||
|
//event
|
||||||
|
//}
|
||||||
|
//#[macro_export] macro_rules! kpat {
|
||||||
|
//(Ctrl-Alt-$code:pat) => { kpat!($code, KeyModifiers::CONTROL | KeyModifiers::ALT) };
|
||||||
|
//(Ctrl-$code:pat) => { kpat!($code, KeyModifiers::CONTROL) };
|
||||||
|
//(Alt-$code:pat) => { kpat!($code, KeyModifiers::ALT) };
|
||||||
|
//(Shift-$code:pat) => { kpat!($code, KeyModifiers::SHIFT) };
|
||||||
|
//($code:pat) => {
|
||||||
|
//crossterm::event::Event::Key(KeyEvent {
|
||||||
|
//code: $code,
|
||||||
|
//modifiers: KeyModifiers::NONE,
|
||||||
|
//kind: KeyEventKind::Press,
|
||||||
|
//state: KeyEventState::NONE
|
||||||
|
//})
|
||||||
|
//};
|
||||||
|
//($code:pat, $modifiers: pat) => {
|
||||||
|
//crossterm::event::Event::Key(KeyEvent {
|
||||||
|
//code: $code,
|
||||||
|
//modifiers: $modifiers,
|
||||||
|
//kind: KeyEventKind::Press,
|
||||||
|
//state: KeyEventState::NONE
|
||||||
|
//})
|
||||||
|
//};
|
||||||
|
//}
|
||||||
|
//#[macro_export] macro_rules! kexp {
|
||||||
|
//(Ctrl-Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::from_bits(0b0000_0110).unwrap()) };
|
||||||
|
//(Ctrl-$code:ident) => { key_event_expr!($code, KeyModifiers::CONTROL) };
|
||||||
|
//(Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::ALT) };
|
||||||
|
//(Shift-$code:ident) => { key_event_expr!($code, KeyModifiers::SHIFT) };
|
||||||
|
//($code:ident) => { key_event_expr!($code) };
|
||||||
|
//($code:expr) => { key_event_expr!($code) };
|
||||||
|
//}
|
||||||
|
//#[macro_export] macro_rules! key_event_expr {
|
||||||
|
//($code:expr, $modifiers: expr) => {
|
||||||
|
//crossterm::event::Event::Key(KeyEvent {
|
||||||
|
//code: $code,
|
||||||
|
//modifiers: $modifiers,
|
||||||
|
//kind: KeyEventKind::Press,
|
||||||
|
//state: KeyEventState::NONE
|
||||||
|
//})
|
||||||
|
//};
|
||||||
|
//($code:expr) => {
|
||||||
|
//crossterm::event::Event::Key(KeyEvent {
|
||||||
|
//code: $code,
|
||||||
|
//modifiers: KeyModifiers::NONE,
|
||||||
|
//kind: KeyEventKind::Press,
|
||||||
|
//state: KeyEventState::NONE
|
||||||
|
//})
|
||||||
|
//};
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::thread::{spawn, JoinHandle};
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct TuiOut {
|
pub struct TuiOut {
|
||||||
pub buffer: Buffer,
|
pub buffer: Buffer,
|
||||||
pub area: [u16;4]
|
pub area: [u16;4]
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Output for TuiOut {
|
impl Output for TuiOut {
|
||||||
type Unit = u16;
|
type Unit = u16;
|
||||||
type Size = [Self::Unit;2];
|
type Size = [Self::Unit;2];
|
||||||
|
|
@ -19,8 +19,42 @@ impl Output for TuiOut {
|
||||||
*self.area_mut() = last;
|
*self.area_mut() = last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TuiOut {
|
impl TuiOut {
|
||||||
|
/// Spawn the output thread.
|
||||||
|
pub fn run_output <T: Render<TuiOut> + 'static> (
|
||||||
|
engine: &Arc<RwLock<Tui>>,
|
||||||
|
state: &Arc<RwLock<T>>,
|
||||||
|
timer: Duration
|
||||||
|
) -> JoinHandle<()> {
|
||||||
|
let exited = engine.read().unwrap().exited.clone();
|
||||||
|
let engine = engine.clone();
|
||||||
|
let state = state.clone();
|
||||||
|
let Size { width, height } = engine.read().unwrap().backend.size().expect("get size failed");
|
||||||
|
let mut buffer = Buffer::empty(Rect { x: 0, y: 0, width, height });
|
||||||
|
spawn(move || loop {
|
||||||
|
if exited.fetch_and(true, Relaxed) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
let t0 = engine.read().unwrap().perf.get_t0();
|
||||||
|
let Size { width, height } = engine.read().unwrap().backend.size()
|
||||||
|
.expect("get size failed");
|
||||||
|
if let Ok(state) = state.try_read() {
|
||||||
|
let size = Rect { x: 0, y: 0, width, height };
|
||||||
|
if buffer.area != size {
|
||||||
|
engine.write().unwrap().backend.clear_region(ClearType::All)
|
||||||
|
.expect("clear failed");
|
||||||
|
buffer.resize(size);
|
||||||
|
buffer.reset();
|
||||||
|
}
|
||||||
|
let mut output = TuiOut { buffer, area: [0, 0, width, height] };
|
||||||
|
state.render(&mut output);
|
||||||
|
buffer = engine.write().unwrap().flip(output.buffer, size);
|
||||||
|
}
|
||||||
|
let t1 = engine.read().unwrap().perf.get_t1(t0).unwrap();
|
||||||
|
buffer.set_string(0, 0, &format!("{:>3}.{:>3}ms", t1.as_millis(), t1.as_micros() % 1000), Style::default());
|
||||||
|
std::thread::sleep(timer);
|
||||||
|
})
|
||||||
|
}
|
||||||
pub fn buffer_update (&mut self, area: [u16;4], callback: &impl Fn(&mut Cell, u16, u16)) {
|
pub fn buffer_update (&mut self, area: [u16;4], callback: &impl Fn(&mut Cell, u16, u16)) {
|
||||||
buffer_update(&mut self.buffer, area, callback);
|
buffer_update(&mut self.buffer, area, callback);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,85 +1,24 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use ratatui::prelude::Size;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::thread::{spawn, JoinHandle};
|
pub trait TuiRun<R: Render<TuiOut> + Handle<TuiIn> + 'static> {
|
||||||
|
|
||||||
pub trait TuiRun<R: Render<TuiOut> + Handle<TuiIn> + Sized + 'static> {
|
|
||||||
/// Run an app in the main loop.
|
/// Run an app in the main loop.
|
||||||
fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>;
|
fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>;
|
||||||
/// Spawn the input thread.
|
|
||||||
fn run_input (&self, state: &Arc<RwLock<R>>, poll: Duration) -> JoinHandle<()>;
|
|
||||||
/// Spawn the output thread.
|
|
||||||
fn run_output (&self, state: &Arc<RwLock<R>>, sleep: Duration) -> JoinHandle<()>;
|
|
||||||
}
|
}
|
||||||
|
impl<T: Render<TuiOut> + Handle<TuiIn> + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
|
||||||
impl<T: Render<TuiOut> + Handle<TuiIn> + Sized + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
|
|
||||||
fn run (&self, state: &Arc<RwLock<T>>) -> Usually<()> {
|
fn run (&self, state: &Arc<RwLock<T>>) -> Usually<()> {
|
||||||
let _input_thread = self.run_input(state, Duration::from_millis(100));
|
let _input_thread = TuiIn::run_input(self, state, Duration::from_millis(100));
|
||||||
self.write().unwrap().setup()?;
|
self.write().unwrap().setup()?;
|
||||||
let render_thread = self.run_output(state, Duration::from_millis(10));
|
let render_thread = TuiOut::run_output(self, state, Duration::from_millis(10));
|
||||||
match render_thread.join() {
|
match render_thread.join() {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
self.write().unwrap().teardown()?;
|
self.write().unwrap().teardown()?;
|
||||||
println!("\n\rRan successfully: {result:?}\n\r");
|
println!("\n\rRan successfully: {result:?}\n\r");
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(error) => {
|
||||||
self.write().unwrap().teardown()?;
|
self.write().unwrap().teardown()?;
|
||||||
panic!("\n\rRender thread failed.\n\r")
|
panic!("\n\rRender thread failed: {error:?}.\n\r")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn run_input (&self, state: &Arc<RwLock<T>>, poll: Duration) -> JoinHandle<()> {
|
|
||||||
let exited = self.read().unwrap().exited.clone();
|
|
||||||
let state = state.clone();
|
|
||||||
spawn(move || loop {
|
|
||||||
if exited.fetch_and(true, Relaxed) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if ::crossterm::event::poll(poll).is_ok() {
|
|
||||||
let event = ::crossterm::event::read().unwrap();
|
|
||||||
match event {
|
|
||||||
kpat!(Ctrl-KeyCode::Char('c')) => {
|
|
||||||
exited.store(true, Relaxed);
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
let exited = exited.clone();
|
|
||||||
if let Err(e) = state.write().unwrap().handle(&TuiIn(exited, event)) {
|
|
||||||
panic!("{e}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn run_output (&self, state: &Arc<RwLock<T>>, sleep: Duration) -> JoinHandle<()> {
|
|
||||||
let exited = self.read().unwrap().exited.clone();
|
|
||||||
let engine = self.clone();
|
|
||||||
let state = state.clone();
|
|
||||||
let Size { width, height } = engine.read().unwrap().backend.size().expect("get size failed");
|
|
||||||
let mut buffer = Buffer::empty(Rect { x: 0, y: 0, width, height });
|
|
||||||
spawn(move || loop {
|
|
||||||
if exited.fetch_and(true, Relaxed) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
let t0 = engine.read().unwrap().perf.get_t0();
|
|
||||||
let Size { width, height } = engine.read().unwrap().backend.size()
|
|
||||||
.expect("get size failed");
|
|
||||||
if let Ok(state) = state.try_read() {
|
|
||||||
let size = Rect { x: 0, y: 0, width, height };
|
|
||||||
if buffer.area != size {
|
|
||||||
engine.write().unwrap().backend.clear_region(ClearType::All)
|
|
||||||
.expect("clear failed");
|
|
||||||
buffer.resize(size);
|
|
||||||
buffer.reset();
|
|
||||||
}
|
|
||||||
let mut output = TuiOut { buffer, area: [0, 0, width, height] };
|
|
||||||
state.render(&mut output);
|
|
||||||
buffer = engine.write().unwrap().flip(output.buffer, size);
|
|
||||||
}
|
|
||||||
let t1 = engine.read().unwrap().perf.get_t1(t0).unwrap();
|
|
||||||
buffer.set_string(0, 0, &format!("{:>03}.{:>03}ms", t1.as_millis(), t1.as_micros() % 1000), Style::default());
|
|
||||||
std::thread::sleep(sleep);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue