separated drawing to screen and internals

This commit is contained in:
Raphael Jacobs 2021-09-17 18:22:53 +02:00
parent cfc3b64f53
commit 1822cf4237
8 changed files with 526 additions and 295 deletions

View File

@ -1,3 +1,7 @@
# v1.6 - 17 September 2021
Massive redesign, completely separated drawing to screen and internals
# v1.5 - 24 August 2021
Proper display of utf8 character width.

View File

@ -27,7 +27,7 @@ it takes less than a minute on my machine!
- [ ] Help menu
- [ ] Config
- [ ] Autosave feature
- [ ] Better utf-8 support
- [x] Better utf-8 support
- [ ] Mouse support
- [ ] Copy-paste support with xclip or similar
- [ ] Search-and-replace

View File

@ -1,17 +1,18 @@
use crate::Colorscheme;
use crate::Config;
use crate::Document;
use crate::Events;
use crate::Row;
use crate::SharedInfo;
use crate::Statusbar;
use crate::Terminal;
use std::cmp;
use std::env;
use std::error::Error;
use std::sync::Arc;
use termion::event::Key;
use tokio::select;
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
use tokio::sync::RwLock;
#[derive(Default)]
pub struct Position {
@ -20,21 +21,24 @@ pub struct Position {
}
pub struct Editor {
terminal: Terminal,
document: Document,
statusbar: Statusbar,
// terminal: Terminal,
// pub document: Arc<RwLock<Document>>,
// pub statusbar: Arc<RwLock<Statusbar>>,
// pub cursor_position: Arc<RwLock<Position>>,
pub shared: Arc<RwLock<SharedInfo>>,
cursor_position: Position,
offset: Position,
inputreceiver: UnboundedReceiver<Key>,
eventsender: UnboundedSender<Events>,
// offset: Position,
rememberx: usize,
quitting: bool,
colorscheme: Colorscheme,
config: Config,
// colorscheme: Colorscheme,
// config: Config,
}
impl Editor {
pub fn default() -> Self {
pub fn default(irt: UnboundedReceiver<Key>, st: UnboundedSender<Events>) -> Self {
let args: Vec<String> = env::args().collect();
let document = if args.len() > 1 {
let filename = &args[1];
@ -45,178 +49,85 @@ impl Editor {
Document::default()
};
let terminal = Terminal::default().expect("Failed to initialize terminal");
let tsd = terminal.sender.clone();
// let terminal = Terminal::default().expect("Failed to initialize terminal");
let shared = SharedInfo {
cpos: Position::default(),
stbar: Statusbar::new(st.clone()),
doc: document,
};
Self {
terminal,
document,
statusbar: Statusbar::new(tsd),
shared: Arc::new(RwLock::new(shared)),
inputreceiver: irt,
eventsender: st,
cursor_position: Position::default(),
offset: Position::default(),
// offset: Position::default(),
rememberx: 0,
quitting: false,
colorscheme: Colorscheme::default(),
config: Config::default(),
// colorscheme: Colorscheme::default(),
// config: Config::default(),
}
}
pub async fn run(&mut self) -> Result<(), Box<dyn Error>> {
fn refresh_screen(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
self.eventsender.send(Events::Refresh)?;
Ok(())
}
fn refresh_status(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
self.eventsender.send(Events::RefreshStatus)?;
Ok(())
}
fn quit(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
self.eventsender.send(Events::Quit)?;
Ok(())
}
fn scroll(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
self.eventsender.send(Events::Scroll)?;
Ok(())
}
pub async fn run(&mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
// let ticker = tick(Duration::from_millis(500));
self.refresh_screen()?;
loop {
select! {
event = self.terminal.receiver.recv() => {match event.unwrap() {
Events::Quit => {
self.terminal.reset_screen();
break;
},
Events::Refresh => {self.refresh_screen()?;}
Events::RefreshStatus => {self.refresh_status()?;}
}}
k = self.terminal.inputreceiver.recv() => {self.process_keypress(k.unwrap()).await?; self.refresh_screen()?;}
// recv(ticker) -> _ => {self.refresh_screen();}
match self.inputreceiver.recv().await {
Some(k) => {
self.process_keypress(k).await?;
self.refresh_screen()?;
}
None => {}
}
}
Ok(())
}
fn calculate_pos(& self) -> Position {
async fn process_keypress(&mut self, k: Key) -> Result<(), Box<dyn Error + Send + Sync>> {
let sharedarc = self.shared.clone();
let mut sharedlock = sharedarc.write().await;
let shared = &mut *sharedlock;
let Position { x, y } = self.cursor_position;
let Position { x: ox, y: oy } = self.offset;
let mut doc = &mut shared.doc;
let cpos = &mut shared.cpos;
let stbar = &mut shared.stbar;
let w = self.terminal.size().width as usize;
let maxw = self.config.maxwidth;
let wpad = (w.saturating_sub(maxw)) / 2;
let ypad = self.config.vertical_padding;
let mut linewidth = 0;
if let Some(r) = self.document.row(y) {
linewidth = r.width(ox, x);
}
Position {
x: linewidth + wpad,
y: (y + ypad).saturating_sub(oy),
}
}
fn refresh_screen(&mut self) -> Result<(), Box<dyn Error>> {
Terminal::cursor_hide();
self.draw_rows();
self.draw_info();
self.terminal.cursor_position(&self.calculate_pos());
Terminal::cursor_show();
self.terminal.flush()?;
Ok(())
}
fn refresh_status(&mut self) -> Result<(), Box<dyn Error>> {
Terminal::cursor_hide();
let s = self.terminal.size();
let w = s.width as usize;
let h = s.height as usize - 2;
self.terminal.cursor_position(&Position { x: 0, y: h });
self.draw_info();
self.terminal.cursor_position(&self.calculate_pos());
Terminal::cursor_show();
self.terminal.flush()?;
Ok(())
}
fn draw_row(&self, row: &Row) {
let w = self.terminal.size().width as usize;
let maxw = self.config.maxwidth;
let pad = (w.saturating_sub(maxw)) / 2;
let start = self.offset.x;
let end = self.offset.x + w - pad * 2;
let r = row.render(start, end);
let space = &" ".repeat(pad);
println!("{}{}\r", space, r)
}
fn draw_rows(&self) {
Terminal::cursor_reset();
Terminal::set_text_color(self.colorscheme.text);
Terminal::set_bg_color(self.colorscheme.background);
let h = self.terminal.size().height as usize;
// let w = self.terminal.size().width as usize;
let vertical_pad = self.config.vertical_padding.saturating_sub(self.offset.y);
let trueh = h.saturating_sub(vertical_pad);
for _ in 0..vertical_pad {
Terminal::clear_current_line();
println!("\r")
}
for terminal_row in 0..trueh - 2 {
Terminal::clear_current_line();
if let Some(row) = self.document.row(
terminal_row as usize + self.offset.y.saturating_sub(self.config.vertical_padding),
) {
self.draw_row(row);
} else {
println!("\r");
}
}
Terminal::reset_text_color();
Terminal::reset_bg_color();
}
fn draw_info(&self) {
Terminal::set_bg_color(self.colorscheme.status_background);
Terminal::set_text_color(self.colorscheme.status_text);
let width = self.terminal.size().width as usize;
let status = self.statusbar.status();
let status2 = self
.statusbar
.status2(&self.document, &self.cursor_position, width);
Terminal::clear_current_line();
println!("{}\r", status);
Terminal::clear_current_line();
print!("{}\r", status2);
Terminal::reset_text_color();
Terminal::reset_bg_color();
}
async fn process_keypress(&mut self, k: Key) -> Result<(), Box<dyn Error>> {
match k {
Key::Ctrl('q') => {
if self.document.dirty && !self.quitting {
self.statusbar.change_status(
if doc.dirty && !self.quitting {
stbar.change_status(
&"Unsaved changes. Are you sure you want to quit? Press ctrl+q again.",
)?;
self.quitting = true;
} else {
self.terminal.quit()?;
self.quit()?;
}
return Ok(()); // Returing instantly, so that the self.quitting variable won't be reset to false
@ -224,23 +135,29 @@ impl Editor {
}
Key::Ctrl('s') => {
if self.document.dirty {
match self.document.save() {
if doc.dirty {
match doc.save() {
Ok(true) => {
self.statusbar
.change_status("Document saved successfully.")?;
stbar.change_status("Document saved successfully.")?;
}
Ok(false) => {
drop(sharedlock); // DROP THE LOCK BEFORE PROMPTING
let filename = self
.prompt(&String::from("Input filename"), &String::from(""))
.await?;
let sharedarc = self.shared.clone();
let shared = &mut *(*sharedarc).write().await;
let mut doc = &mut shared.doc;
let stbar = &mut shared.stbar;
self.document.filename = filename;
if let Ok(true) = self.document.save() {
self.statusbar
.change_status("Document saved successfully.")?;
doc.filename = filename;
if let Ok(true) = doc.save() {
stbar.change_status("Document saved successfully.")?;
} else {
self.statusbar.change_status("Operation canceled.")?;
stbar.change_status("Operation canceled.")?;
}
}
Err(e) => {
@ -248,60 +165,74 @@ impl Editor {
} // TODO edit stuff
};
} else {
self.statusbar.change_status("No need to save, pal!")?;
stbar.change_status("No need to save, pal!")?;
}
}
Key::Ctrl('o') => {
drop(sharedlock); // DROP THE LOCK BEFORE PROMPTING
let filename = self
.prompt(&String::from("Input filename"), &String::from(""))
.await?;
let sharedarc = self.shared.clone();
let shared = &mut *(*sharedarc).write().await;
let mut doc = &mut shared.doc;
let cpos = &mut shared.cpos;
let stbar = &mut shared.stbar;
match filename {
Some(fl) if !fl.is_empty() => {
self.document = Document::open(&fl).unwrap_or_default();
self.document.filename = Some(fl);
self.statusbar.change_status("Opening file")?;
self.cursor_position = Position { x: 0, y: 0 };
*doc = Document::open(&fl).unwrap_or_default();
doc.filename = Some(fl);
stbar.change_status("Opening file")?;
*cpos = Position { x: 0, y: 0 };
}
_ => {
self.statusbar.change_status("Operation canceled.")?;
stbar.change_status("Operation canceled.")?;
}
}
}
Key::Char(c) => {
self.document.dirty = true;
self.document.insert(&self.cursor_position, c);
self.move_cursor(Key::Right);
doc.dirty = true;
doc.insert(&cpos, c);
self.move_cursor(Key::Right, doc, cpos).await?;
}
Key::Delete => {
self.document.dirty = true;
self.document.delete(&self.cursor_position)
doc.dirty = true;
doc.delete(&cpos)
}
Key::Backspace => {
if self.cursor_position.x > 0 || self.cursor_position.y > 0 {
self.document.dirty = true;
self.move_cursor(Key::Left);
self.document.delete(&self.cursor_position);
if cpos.x > 0 || cpos.y > 0 {
doc.dirty = true;
self.move_cursor(Key::Left, doc, cpos).await?;
doc.delete(&cpos);
}
}
Key::Up | Key::Down | Key::Left | Key::Right => self.move_cursor(k),
Key::Up | Key::Down | Key::Left | Key::Right => {
self.move_cursor(k, doc, cpos).await?;
}
_ => (),
}
self.quitting = false;
Ok(())
}
fn savefile(&self) {
self.document.filecheck();
}
// fn savefile(&self) {
// doc.filecheck();
// }
fn move_cursor(&mut self, key: Key) {
let Position { mut y, mut x } = self.cursor_position;
async fn move_cursor(&mut self, key: Key, doc: &Document, cpos: &mut Position) -> Result<(), Box<dyn Error + Sync + Send>> {
// let doc = (*docarc).read().await;
// let mut cpos = (*curarc).write().await;
let w = self.document.row(y).map_or(0, Row::len);
let h = self.document.len();
let Position { mut y, mut x } = *cpos;
let w = doc.row(y).map_or(0, Row::len);
let h = doc.len();
match key {
Key::Up => y = y.saturating_sub(1),
@ -315,7 +246,7 @@ impl Editor {
x -= 1;
} else if y > 0 {
y -= 1;
x = self.document.row(y).map_or(0, Row::len);
x = doc.row(y).map_or(0, Row::len);
}
self.rememberx = x;
}
@ -330,7 +261,7 @@ impl Editor {
}
_ => (),
}
let neww = self.document.row(y).map_or(0, Row::len);
let neww = doc.row(y).map_or(0, Row::len);
if neww <= self.rememberx {
x = neww;
@ -338,103 +269,91 @@ impl Editor {
x = self.rememberx
};
self.cursor_position = Position { x, y };
self.scroll();
}
fn scroll(&mut self) {
let Position { x, y } = self.cursor_position;
let s = self.terminal.size();
let w = s.width as usize;
let h = s.height as usize - 2;
// let yscroll = self.config.vertical_scrollspace;
// let xscroll = self.config.horizontal_scrollspace;
let ypad = self.config.vertical_padding;
let maxw = self.config.maxwidth;
let mut offset = &mut self.offset;
let true_w = cmp::min(w, maxw);
if y - offset.y.saturating_sub(ypad) + 1 > h - ypad.saturating_sub(offset.y) {
offset.y = y.saturating_sub(h - ypad).saturating_add(1);
} else if y < offset.y {
offset.y = y;
}
if x + 1 > offset.x.saturating_add(true_w) {
offset.x = x.saturating_sub(true_w).saturating_add(1);
} else if x < offset.x {
offset.x = x;
}
*cpos = Position { x, y };
self.scroll()
}
async fn prompt(
&mut self,
prompt_message: &String,
original: &String,
) -> Result<Option<String>, Box<dyn Error>> {
self.statusbar.prompting = true;
self.statusbar.status_stat = prompt_message.clone();
) -> Result<Option<String>, Box<dyn Error + Send + Sync>> {
let sharedarc = self.shared.clone();
let mut sharedlock = sharedarc.write().await;
let shared = &mut *sharedlock;
let stbar = &mut shared.stbar;
stbar.prompting = true;
stbar.status_stat = prompt_message.clone();
let mut pos = original.len();
let mut r = Row::from(&original[..]);
stbar.status2_stat = r.render(0, r.len());
drop(sharedlock); // DROP BEFORE REFRESHING
self.refresh_status()?;
loop {
self.statusbar.status2_stat = r.render(0, r.len());
self.refresh_status()?;
select!(
k = self.terminal.inputreceiver.recv() => {
match k.unwrap() {
k = self.inputreceiver.recv() => {
{
let mut sharedlock = sharedarc.write().await;
let shared = &mut *sharedlock;
let stbar = &mut shared.stbar;
Key::Left => {
if pos > 0 {
pos = pos - 1
match k.unwrap() {
Key::Left => {
if pos > 0 {
pos = pos - 1
}
}
Key::Right => {
if pos < r.len() {
pos = pos + 1
}
}
Key::Char('\n')=> {
stbar.prompting = false;
if r.is_empty() {
return Ok(None)
} else {
return Ok(
Some(r.render(0, r.len()))
)
}
}
Key::Esc => {
stbar.prompting = false;
return Ok(None)
}
Key::Backspace => {
if pos > 0 {
r.delete(pos-1);
pos = pos - 1
}
}
Key::Char(c) => {
r.insert(pos, c);
pos = pos + 1
}
_ => {}
}
stbar.status2_stat = r.render(0, r.len());
}
Key::Right => {
if pos < r.len() {
pos = pos + 1
}
}
Key::Char('\n')=> {
self.statusbar.prompting = false;
if r.is_empty() {
return Ok(None)
} else {
return Ok(
Some(r.render(0, r.len()))
)
}
}
Key::Esc => {
self.statusbar.prompting = false;
return Ok(None)
}
Key::Backspace => {
if pos > 0 {
r.delete(pos-1);
pos = pos - 1
}
}
Key::Char(c) => {
r.insert(pos, c);
pos = pos + 1
}
_ => {}
}
self.refresh_status()?; // everything is dropped automatically, refresh again after each character
})
}
}

View File

@ -3,4 +3,5 @@ pub enum Events {
Refresh,
RefreshStatus,
Quit,
Scroll,
}

View File

@ -7,22 +7,79 @@ mod events;
mod row;
mod statusbar;
mod terminal;
mod helpmenu;
mod renderer;
use renderer::Renderer;
use editor::Editor;
pub use colorscheme::Colorscheme;
pub use config::Config;
pub use document::Document;
use editor::Editor;
pub use editor::Position;
pub use events::Events;
pub use row::Row;
pub use statusbar::Statusbar;
pub use terminal::Terminal;
use std::error::Error;
use tokio::sync::mpsc::{unbounded_channel};
use tokio::select;
pub struct SharedInfo {
cpos : Position,
doc : Document,
stbar : Statusbar
}
#[tokio::main]
async fn main() {
let mut edit = Editor::default();
match edit.run().await {
Err(e) => println!("FUUUUUUUUUUCK!!"),
Ok(()) => (),
}
let (st, rt) = unbounded_channel();
let (ist, irt) = unbounded_channel();
let mut edit = Editor::default(irt, st.clone());
let shared = edit.shared.clone();
let mut render = Renderer::default(st, ist, rt, shared);
// TODO: ERROR CARCHING INSIDE TOKIO THREADS
// tokio::task::JoinHandle<std::result::Result<(), Box<dyn Error + Send + Sync>>>
let t1 : tokio::task::JoinHandle<std::result::Result<(), Box<dyn Error + Send + Sync>>> = tokio::spawn(async move {
render.run().await?;
Ok(())
});
let t2 : tokio::task::JoinHandle<std::result::Result<(), Box<dyn Error + Send + Sync>>> = tokio::spawn(async move {
edit.run().await?;
Ok(())
});
// sleep(Duration::from_secs(10)).await;
select!(
k = t1 => {
match k {
Err(e) => println!("Catastrophic error: \n{:?}", e), // TODO: crash handling
Ok(_) => return
}
}
k = t2 => {
match k {
Err(e) => println!("Catastrophic error: \n{:?}", e), // TODO: crash handling
Ok(_) => return
}
}
)
}

249
src/renderer.rs Normal file
View File

@ -0,0 +1,249 @@
use crate::Colorscheme;
use crate::Config;
use crate::Events;
use crate::Position;
use crate::Row;
use crate::SharedInfo;
use crate::Terminal;
use std::cmp;
use std::error::Error;
use std::sync::Arc;
use termion::event::Key;
use tokio::select;
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
use tokio::sync::RwLock;
pub struct Renderer {
shared: Arc<RwLock<SharedInfo>>,
terminal: Terminal,
// render_position: Position,
offset: Position,
colorscheme: Colorscheme,
pub receiver: UnboundedReceiver<Events>,
config: Config,
}
impl Renderer {
pub fn default(
st: UnboundedSender<Events>,
ist: UnboundedSender<Key>,
rt: UnboundedReceiver<Events>,
shared: Arc<RwLock<SharedInfo>>,
) -> Self {
// let (st, rt) = unbounded_channel();
// let (ist, irt) = unbounded_channel();
let terminal = Terminal::default(st.clone(), ist).expect("Failed to initialize terminal");
// let editor = Editor::default(irt, st);
Self {
shared,
terminal,
receiver: rt,
// render_position: Position::default(),
offset: Position::default(),
colorscheme: Colorscheme::default(),
config: Config::default(),
}
}
pub async fn run(&mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
self.refresh_screen().await?;
loop {
select! {
event = self.receiver.recv() => {match event.unwrap() {
Events::Quit => {
self.terminal.reset_screen();
break;
},
Events::Refresh => {self.refresh_screen().await?;}
Events::RefreshStatus => {self.refresh_status().await?;}
Events::Scroll => {self.scroll().await;}
}}
}
}
Ok(())
}
async fn calculate_pos(&self) -> Position {
let sharedarc = self.shared.clone();
let shared = (*sharedarc).read().await;
let doc = &shared.doc;
let cpos = &shared.cpos;
let Position { x, y } = *cpos;
let Position { x: ox, y: oy } = self.offset;
let w = self.terminal.size().width as usize;
let maxw = self.config.maxwidth;
let wpad = (w.saturating_sub(maxw)) / 2;
let ypad = self.config.vertical_padding;
let mut linewidth = 0;
if let Some(r) = doc.row(y) {
linewidth = r.width(ox, x);
}
Position {
x: linewidth + wpad,
y: (y + ypad).saturating_sub(oy),
}
}
async fn refresh_screen(&mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
Terminal::cursor_hide();
self.draw_rows().await;
self.draw_info().await;
self.terminal.cursor_position(&self.calculate_pos().await);
Terminal::cursor_show();
self.terminal.flush()?;
Ok(())
}
async fn refresh_status(&mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
Terminal::cursor_hide();
let s = self.terminal.size();
// let w = s.width as usize;
let h = s.height as usize - 2;
self.terminal.cursor_position(&Position { x: 0, y: h });
self.draw_info().await;
self.terminal.cursor_position(&self.calculate_pos().await);
Terminal::cursor_show();
self.terminal.flush()?;
Ok(())
}
fn draw_row(&self, row: &Row) {
let w = self.terminal.size().width as usize;
let maxw = self.config.maxwidth;
let pad = (w.saturating_sub(maxw)) / 2;
let start = self.offset.x;
let end = self.offset.x + w - pad * 2;
let r = row.render(start, end);
let space = &" ".repeat(pad);
println!("{}{}\r", space, r)
}
async fn draw_rows(&self) {
let sharedarc = self.shared.clone();
let shared = (*sharedarc).read().await;
let doc = &shared.doc;
Terminal::cursor_reset();
Terminal::set_text_color(self.colorscheme.text);
Terminal::set_bg_color(self.colorscheme.background);
let h = self.terminal.size().height as usize;
// let w = self.terminal.size().width as usize;
let vertical_pad = self.config.vertical_padding.saturating_sub(self.offset.y);
let trueh = h.saturating_sub(vertical_pad);
for _ in 0..vertical_pad {
Terminal::clear_current_line();
println!("\r")
}
for terminal_row in 0..trueh - 2 {
Terminal::clear_current_line();
if let Some(row) = doc.row(
terminal_row as usize + self.offset.y.saturating_sub(self.config.vertical_padding),
) {
self.draw_row(row);
} else {
println!("\r");
}
}
Terminal::reset_text_color();
Terminal::reset_bg_color();
}
async fn draw_info(&self) {
let sharedarc = self.shared.clone();
let shared = (*sharedarc).read().await;
let doc = &shared.doc;
let cpos = &shared.cpos;
let stbar = &shared.stbar;
Terminal::set_bg_color(self.colorscheme.status_background);
Terminal::set_text_color(self.colorscheme.status_text);
let width = self.terminal.size().width as usize;
let status = stbar.status();
let status2 = stbar.status2(&doc, &cpos, width);
Terminal::clear_current_line();
println!("{}\r", status);
Terminal::clear_current_line();
print!("{}\r", status2);
Terminal::reset_text_color();
Terminal::reset_bg_color();
}
async fn scroll(&mut self) {
let sharedarc = self.shared.clone();
let shared = (*sharedarc).read().await;
let cpos = &shared.cpos;
let Position { x, y } = *cpos;
let s = self.terminal.size();
let w = s.width as usize;
let h = s.height as usize - 2;
// let yscroll = self.config.vertical_scrollspace;
// let xscroll = self.config.horizontal_scrollspace;
let ypad = self.config.vertical_padding;
let maxw = self.config.maxwidth;
let mut offset = &mut self.offset;
let true_w = cmp::min(w, maxw);
if y - offset.y.saturating_sub(ypad) + 1 > h - ypad.saturating_sub(offset.y) {
offset.y = y.saturating_sub(h - ypad).saturating_add(1);
} else if y < offset.y {
offset.y = y;
}
if x + 1 > offset.x.saturating_add(true_w) {
offset.x = x.saturating_sub(true_w).saturating_add(1);
} else if x < offset.x {
offset.x = x;
}
}
}

View File

@ -64,7 +64,7 @@ impl Statusbar {
}
}
pub fn change_status(&mut self, message: &str) -> Result<(), Box<dyn Error>> {
pub fn change_status(&mut self, message: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
let mut s = self.sharedstatus.write().unwrap();
*s = String::from(message);

View File

@ -14,7 +14,7 @@ use termion::raw::{IntoRawMode, RawTerminal};
use termion::screen::{AlternateScreen, ToMainScreen};
use tokio::signal::unix::{signal, SignalKind};
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use tokio::sync::mpsc::{UnboundedSender};
#[derive(Clone, Copy)]
pub struct Size {
@ -28,23 +28,23 @@ pub struct Terminal {
screen: AlternateScreen<RawTerminal<Stdout>>,
pub sender: UnboundedSender<Events>,
pub receiver: UnboundedReceiver<Events>,
// pub receiver: UnboundedReceiver<Events>,
inputsender: UnboundedSender<Key>,
pub inputreceiver: UnboundedReceiver<Key>,
// pub inputreceiver: UnboundedReceiver<Key>,
sigthread: Option<tokio::task::JoinHandle<()>>,
keythread: Option<std::thread::JoinHandle<()>>,
}
impl Terminal {
pub fn default() -> Result<Self, Box<dyn Error>> {
pub fn default(st: UnboundedSender<Events>, ist: UnboundedSender<Key>) -> Result<Self, Box<dyn Error + Send + Sync>> {
let size = termion::terminal_size()?;
let out = stdout().into_raw_mode()?;
let screen = AlternateScreen::from(out);
let (st, rt) = unbounded_channel();
let (ist, irt) = unbounded_channel();
// let (st, rt) = unbounded_channel();
// let (ist, irt) = unbounded_channel();
let mut res = Self {
size: Arc::new(RwLock::new(Size {
@ -53,9 +53,9 @@ impl Terminal {
})),
screen,
sender: st,
receiver: rt,
// receiver: rt,
inputsender: ist,
inputreceiver: irt,
// inputreceiver: irt,
sigthread: None,
keythread: None,
};
@ -70,7 +70,7 @@ impl Terminal {
println!("{}", ToMainScreen);
}
fn start_update_thread(&mut self) -> Result<(), Box<dyn Error>> {
fn start_update_thread(&mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
let mut signals = signal(SignalKind::window_change())?; // TODO: Support for other signals
let arcsize = self.size.clone();
@ -79,6 +79,7 @@ impl Terminal {
let resizethread = tokio::spawn(async move {
loop {
match signals.recv().await {
Some(_) => {
let size = termion::terminal_size().unwrap_or_default();
@ -98,7 +99,7 @@ impl Terminal {
Ok(())
}
fn start_reading_keys(&mut self) -> Result<(), Box<dyn Error>> {
fn start_reading_keys(&mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
let mut reader = stdin().keys();
let sender = self.inputsender.clone();
@ -133,7 +134,7 @@ impl Terminal {
print!("{}", termion::cursor::Goto(x, y))
}
pub fn flush(&mut self) -> Result<(), Box<dyn Error>> {
pub fn flush(&mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
self.screen.flush()?;
Ok(())
}