lib-nested/lib-nested-tty/src/terminal.rs

235 lines
7.8 KiB
Rust
Raw Normal View History

2020-12-09 12:56:38 +01:00
use {
2023-02-13 18:39:45 +01:00
r3vi::{
view::{
2021-11-19 12:19:52 +01:00
channel::{queue_channel, set_channel, ChannelReceiver, ChannelSender},
Observer, OuterViewPort,
2023-02-13 18:39:45 +01:00
grid::*,
index::*,
}
2020-12-14 19:35:35 +01:00
},
2023-11-28 20:52:25 +01:00
crate::atom::{TerminalStyle},
crate::{TerminalView},
2021-11-19 12:19:52 +01:00
async_std::{stream::StreamExt, task},
cgmath::{Point2, Vector2},
2020-12-14 19:35:35 +01:00
signal_hook,
signal_hook_async_std::Signals,
2021-11-19 12:19:52 +01:00
std::sync::RwLock,
std::{
collections::HashSet,
io::{stdin, stdout, Write},
sync::Arc,
},
2020-12-09 12:56:38 +01:00
termion::{
2021-11-19 12:19:52 +01:00
input::{MouseTerminal, TermRead},
2020-12-09 12:56:38 +01:00
raw::IntoRawMode,
2021-01-06 21:35:46 +01:00
},
2020-12-09 12:56:38 +01:00
};
2023-11-28 17:16:51 +01:00
#[derive(PartialEq, Eq, Clone, Debug)]
2020-12-09 12:56:38 +01:00
pub enum TerminalEvent {
2020-12-14 19:35:35 +01:00
Resize(Vector2<i16>),
2021-11-19 12:19:52 +01:00
Input(termion::event::Event),
2020-12-09 12:56:38 +01:00
}
pub struct Terminal {
2021-01-06 21:35:46 +01:00
writer: Arc<TermOutWriter>,
_observer: Arc<RwLock<TermOutObserver>>,
2021-01-06 21:35:46 +01:00
2020-12-14 19:35:35 +01:00
events: ChannelReceiver<Vec<TerminalEvent>>,
2021-11-19 12:19:52 +01:00
_signal_handle: signal_hook_async_std::Handle,
2020-12-09 12:56:38 +01:00
}
impl Terminal {
2021-11-19 12:19:52 +01:00
pub fn new(port: OuterViewPort<dyn TerminalView>) -> Self {
2021-01-06 21:35:46 +01:00
let (dirty_pos_tx, dirty_pos_rx) = set_channel();
let writer = Arc::new(TermOutWriter {
out: RwLock::new(MouseTerminal::from(stdout().into_raw_mode().unwrap())),
dirty_pos_rx,
2021-11-19 12:19:52 +01:00
view: port.get_view_arc(),
2021-01-06 21:35:46 +01:00
});
let observer = Arc::new(RwLock::new(TermOutObserver {
2021-01-06 21:35:46 +01:00
dirty_pos_tx,
2021-11-19 12:19:52 +01:00
writer: writer.clone(),
}));
2021-01-06 21:35:46 +01:00
port.add_observer(observer.clone());
let (event_tx, event_rx) = queue_channel();
2020-12-09 12:56:38 +01:00
let input_tx = event_tx.clone();
std::thread::spawn(move || {
for event in stdin().events() {
2021-01-06 21:35:46 +01:00
input_tx.send(TerminalEvent::Input(event.unwrap()));
2020-12-09 12:56:38 +01:00
}
});
2020-12-14 19:35:35 +01:00
// send initial teriminal size
2021-01-06 21:35:46 +01:00
let (w, h) = termion::terminal_size().unwrap();
event_tx.send(TerminalEvent::Resize(Vector2::new(w as i16, h as i16)));
2020-12-14 19:35:35 +01:00
// and again on SIGWINCH
2021-11-19 12:19:52 +01:00
let signals = Signals::new(&[signal_hook::consts::signal::SIGWINCH]).unwrap();
2020-12-14 19:35:35 +01:00
let handle = signals.handle();
task::spawn(async move {
let mut signals = signals.fuse();
while let Some(signal) = signals.next().await {
match signal {
2021-01-06 21:35:46 +01:00
signal_hook::consts::signal::SIGWINCH => {
2021-11-19 12:19:52 +01:00
let (w, h) = termion::terminal_size().unwrap();
2021-01-06 21:35:46 +01:00
event_tx.send(TerminalEvent::Resize(Vector2::new(w as i16, h as i16)));
2021-11-19 12:19:52 +01:00
}
2020-12-14 19:35:35 +01:00
_ => unreachable!(),
}
2020-12-09 12:56:38 +01:00
}
});
2020-12-14 19:35:35 +01:00
2020-12-09 12:56:38 +01:00
Terminal {
2021-01-06 21:35:46 +01:00
writer,
_observer: observer,
2020-12-14 19:35:35 +01:00
events: event_rx,
2021-11-19 12:19:52 +01:00
_signal_handle: handle,
2020-12-09 12:56:38 +01:00
}
}
2021-01-06 21:35:46 +01:00
pub fn get_writer(&self) -> Arc<TermOutWriter> {
self.writer.clone()
}
2020-12-14 19:35:35 +01:00
pub async fn next_event(&mut self) -> TerminalEvent {
self.events.next().await.unwrap()
}
2021-01-06 21:35:46 +01:00
}
struct TermOutObserver {
dirty_pos_tx: ChannelSender<HashSet<Point2<i16>>>,
2021-11-19 12:19:52 +01:00
writer: Arc<TermOutWriter>,
2021-01-06 21:35:46 +01:00
}
impl TermOutObserver {
fn send_area(&mut self, area: IndexArea<Point2<i16>>) {
match area {
2021-11-19 12:19:52 +01:00
IndexArea::Empty => {}
IndexArea::Full => {
let (w, h) = termion::terminal_size().unwrap();
2021-11-19 12:19:52 +01:00
for pos in
GridWindowIterator::from(Point2::new(0, 0)..Point2::new(w as i16, h as i16))
{
self.dirty_pos_tx.send(pos);
}
}
IndexArea::Range(r) => {
for pos in GridWindowIterator::from(r) {
self.dirty_pos_tx.send(pos);
}
}
IndexArea::Set(v) => {
for pos in v {
self.dirty_pos_tx.send(pos);
}
}
2021-11-19 12:19:52 +01:00
}
}
}
2021-01-06 21:35:46 +01:00
impl Observer<dyn TerminalView> for TermOutObserver {
fn reset(&mut self, view: Option<Arc<dyn TerminalView>>) {
2021-01-06 21:35:46 +01:00
self.writer.reset();
if let Some(view) = view {
self.send_area(view.area());
2021-01-06 21:35:46 +01:00
}
}
2020-12-14 19:35:35 +01:00
fn notify(&mut self, area: &IndexArea<Point2<i16>>) {
self.send_area(area.clone());
2021-01-06 21:35:46 +01:00
}
}
2020-12-09 12:56:38 +01:00
2021-01-06 21:35:46 +01:00
pub struct TermOutWriter {
out: RwLock<MouseTerminal<termion::raw::RawTerminal<std::io::Stdout>>>,
dirty_pos_rx: ChannelReceiver<HashSet<Point2<i16>>>,
2021-11-19 12:19:52 +01:00
view: Arc<RwLock<Option<Arc<dyn TerminalView>>>>,
2021-01-06 21:35:46 +01:00
}
2020-12-09 12:56:38 +01:00
2021-01-06 21:35:46 +01:00
impl TermOutWriter {
fn reset(&self) {
let mut out = self.out.write().unwrap();
2021-05-13 16:22:30 +02:00
write!(out, "{}", termion::clear::All).ok();
2020-12-09 12:56:38 +01:00
}
2021-01-06 21:35:46 +01:00
}
2020-12-09 12:56:38 +01:00
2021-01-06 21:35:46 +01:00
impl TermOutWriter {
pub async fn show(&self) -> std::io::Result<()> {
// init
2021-11-19 12:19:52 +01:00
write!(
self.out.write().unwrap(),
"{}{}{}",
termion::cursor::Hide,
termion::cursor::Goto(1, 1),
termion::style::Reset
)?;
2021-01-06 21:35:46 +01:00
let mut cur_pos = Point2::<i16>::new(0, 0);
2020-12-09 12:56:38 +01:00
let mut cur_style = TerminalStyle::default();
2021-01-06 21:35:46 +01:00
// draw atoms until view port is destroyed
while let Some(dirty_pos) = self.dirty_pos_rx.recv().await {
2021-11-19 12:19:52 +01:00
let (w, _h) = termion::terminal_size().unwrap();
2021-01-06 21:35:46 +01:00
if let Some(view) = self.view.read().unwrap().as_ref() {
let mut out = self.out.write().unwrap();
2021-11-19 12:19:52 +01:00
let d = dirty_pos
.into_iter()
.filter(|p| p.x >= 0 && p.y >= 0 && p.x < w as i16 && p.y < w as i16); //.collect::<Vec<_>>();
/*
d.sort_by(|a,b| {
if a.y < b.y {
std::cmp::Ordering::Less
} else if a.y == b.y {
a.x.cmp(&b.x)
} else {
std::cmp::Ordering::Greater
}
});
*/
for pos in d {
2021-01-06 21:35:46 +01:00
if pos != cur_pos {
2021-11-19 12:19:52 +01:00
write!(
out,
"{}",
termion::cursor::Goto(pos.x as u16 + 1, pos.y as u16 + 1)
)?;
2021-01-06 21:35:46 +01:00
}
if let Some(atom) = view.get(&pos) {
if cur_style != atom.style {
cur_style = atom.style;
2023-03-02 02:41:22 +01:00
write!(out, "{}", termion::style::Reset)?;
2021-01-06 21:35:46 +01:00
write!(out, "{}", atom.style)?;
}
write!(out, "{}", atom.c.unwrap_or(' '))?;
} else {
write!(out, "{} ", termion::style::Reset)?;
cur_style = TerminalStyle::default();
2020-12-09 12:56:38 +01:00
}
2021-01-06 21:35:46 +01:00
cur_pos = pos + Vector2::new(1, 0);
2020-12-09 12:56:38 +01:00
}
2021-01-06 21:35:46 +01:00
out.flush()?;
}
2020-12-09 12:56:38 +01:00
}
2021-01-06 21:35:46 +01:00
// restore conventional terminal settings
let mut out = self.out.write().unwrap();
2020-12-09 12:56:38 +01:00
write!(out, "{}", termion::cursor::Show)?;
out.flush()?;
2021-01-06 21:35:46 +01:00
std::io::Result::Ok(())
2020-12-09 12:56:38 +01:00
}
}