Add watcher and input threads
parent
5ed4c37e52
commit
577889f7da
|
@ -24,6 +24,7 @@ crossbeam = "^0.3.0"
|
|||
fnv = "1.0.3"
|
||||
encoding = "0.2.33"
|
||||
bitflags = "1.0"
|
||||
notify = "4.0.1"
|
||||
|
||||
[dependencies.ncurses]
|
||||
features = ["wide"]
|
||||
|
|
8
README
8
README
|
@ -1,10 +1,10 @@
|
|||
__
|
||||
__/ \__
|
||||
/ \__/ \__ .
|
||||
\__/ \__/ \ , _ , _ ___ | '
|
||||
/ \__ \__/ |' `|' `. .' ` | |
|
||||
\__/ \__/ \ | | | |----' | |
|
||||
\__/ \__/ / ' / `.___, /\__ /
|
||||
\__/ \__/ \ , _ , _ ___ │ '
|
||||
/ \__ \__/ │' `│ `┒ .' ` │ │
|
||||
\__/ \__/ \ │ │ │ |────' │ │
|
||||
\__/ \__/ │ / `.___, /\__ /
|
||||
\__/
|
||||
|
||||
terminal mail user agent
|
||||
|
|
96
src/bin.rs
96
src/bin.rs
|
@ -21,22 +21,47 @@
|
|||
|
||||
mod ui;
|
||||
use ui::index::*;
|
||||
use ui::ThreadEvent;
|
||||
|
||||
extern crate melib;
|
||||
use melib::*;
|
||||
use mailbox::*;
|
||||
use conf::*;
|
||||
|
||||
extern crate ncurses;
|
||||
|
||||
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
|
||||
use std::thread;
|
||||
|
||||
fn main() {
|
||||
let locale_conf = ncurses::LcCategory::all;
|
||||
ncurses::setlocale(locale_conf, "en_US.UTF-8");
|
||||
let set = Settings::new();
|
||||
let ui = ui::TUI::initialize();
|
||||
|
||||
let (sender, receiver): (SyncSender<ThreadEvent>, Receiver<ThreadEvent>) =
|
||||
sync_channel(::std::mem::size_of::<ThreadEvent>());
|
||||
{
|
||||
let sender = sender.clone();
|
||||
let mut ch = None;
|
||||
thread::Builder::new()
|
||||
.name("input-thread".to_string())
|
||||
.spawn(move || loop {
|
||||
ch = ncurses::get_wch();
|
||||
if let Some(k) = ch {
|
||||
sender.send(ThreadEvent::Input(k)).unwrap();
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let mut j = 0;
|
||||
let folder_length = set.accounts["norn"].folders.len();
|
||||
let mut account = Account::new("norn".to_string(), set.accounts["norn"].clone());
|
||||
{
|
||||
let sender = sender.clone();
|
||||
account.watch(RefreshEventConsumer::new(Box::new(move |r| {
|
||||
sender.send(ThreadEvent::from(r)).unwrap();
|
||||
})));
|
||||
}
|
||||
'main: loop {
|
||||
ncurses::touchwin(ncurses::stdscr());
|
||||
ncurses::mv(0, 0);
|
||||
|
@ -50,37 +75,48 @@ fn main() {
|
|||
|
||||
index.draw();
|
||||
|
||||
let mut ch;
|
||||
'inner: loop {
|
||||
ch = ncurses::get_wch();
|
||||
match ch {
|
||||
Some(ncurses::WchResult::KeyCode(k @ ncurses::KEY_UP)) |
|
||||
Some(ncurses::WchResult::KeyCode(k @ ncurses::KEY_DOWN)) => {
|
||||
index.handle_input(k);
|
||||
continue;
|
||||
}
|
||||
Some(ncurses::WchResult::Char(k @ 10)) => {
|
||||
index.handle_input(k as i32);
|
||||
continue;
|
||||
}
|
||||
Some(ncurses::WchResult::KeyCode(ncurses::KEY_F1)) |
|
||||
Some(ncurses::WchResult::Char(113)) => {
|
||||
break 'main;
|
||||
}
|
||||
Some(ncurses::WchResult::Char(74)) => if j < folder_length - 1 {
|
||||
j += 1;
|
||||
break 'inner;
|
||||
match receiver.recv().unwrap() {
|
||||
ThreadEvent::Input(k) => match k {
|
||||
ncurses::WchResult::KeyCode(k @ ncurses::KEY_UP)
|
||||
| ncurses::WchResult::KeyCode(k @ ncurses::KEY_DOWN) => {
|
||||
index.handle_input(k);
|
||||
continue;
|
||||
}
|
||||
ncurses::WchResult::Char(k @ 10) => {
|
||||
index.handle_input(k as i32);
|
||||
continue;
|
||||
}
|
||||
ncurses::WchResult::KeyCode(k @ ncurses::KEY_F1) => {
|
||||
if !index.handle_input(k) {
|
||||
break 'main;
|
||||
}
|
||||
}
|
||||
ncurses::WchResult::Char(113) => {
|
||||
break 'main;
|
||||
}
|
||||
ncurses::WchResult::Char(74) => {
|
||||
if j < folder_length - 1 {
|
||||
j += 1;
|
||||
break 'inner;
|
||||
}
|
||||
}
|
||||
ncurses::WchResult::Char(75) => {
|
||||
if j > 0 {
|
||||
j -= 1;
|
||||
break 'inner;
|
||||
}
|
||||
}
|
||||
ncurses::WchResult::KeyCode(ncurses::KEY_RESIZE) => {
|
||||
eprintln!("key_resize");
|
||||
index.redraw();
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Some(ncurses::WchResult::Char(75)) => if j > 0 {
|
||||
j -= 1;
|
||||
break 'inner;
|
||||
},
|
||||
Some(ncurses::WchResult::KeyCode(ncurses::KEY_RESIZE)) => {
|
||||
eprintln!("key_resize");
|
||||
index.redraw();
|
||||
continue;
|
||||
ThreadEvent::RefreshMailbox { name: n } => {
|
||||
eprintln!("Refresh mailbox {}", n);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,3 +35,8 @@ extern crate encoding;
|
|||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
pub use mailbox::*;
|
||||
pub use conf::*;
|
||||
|
||||
pub use mailbox::backends::RefreshEventConsumer;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*/
|
||||
|
||||
use mailbox::*;
|
||||
use mailbox::backends::RefreshEventConsumer;
|
||||
use conf::AccountSettings;
|
||||
use std::ops::{Index, IndexMut};
|
||||
#[derive(Debug)]
|
||||
|
@ -30,9 +31,11 @@ pub struct Account {
|
|||
sent_folder: Option<usize>,
|
||||
|
||||
settings: AccountSettings,
|
||||
pub backend: Box<MailBackend>,
|
||||
}
|
||||
|
||||
|
||||
use mailbox::backends::maildir::MaildirType;
|
||||
impl Account {
|
||||
pub fn new(name: String, settings: AccountSettings) -> Self {
|
||||
eprintln!("new acc");
|
||||
|
@ -44,6 +47,7 @@ impl Account {
|
|||
for _ in 0..settings.folders.len() {
|
||||
folders.push(None);
|
||||
}
|
||||
let backend = Box::new(MaildirType::new(""));
|
||||
Account {
|
||||
name: name,
|
||||
folders: folders,
|
||||
|
@ -51,8 +55,12 @@ impl Account {
|
|||
sent_folder: sent_folder,
|
||||
|
||||
settings: settings,
|
||||
backend: backend,
|
||||
}
|
||||
}
|
||||
pub fn watch(&self, r: RefreshEventConsumer) -> () {
|
||||
self.backend.watch(r, &self.settings.folders);
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for Account {
|
||||
|
|
|
@ -21,18 +21,23 @@
|
|||
|
||||
use mailbox::email::{Envelope, Flag};
|
||||
use error::{MeliError, Result};
|
||||
use mailbox::backends::{BackendOp, BackendOpGenerator, MailBackend};
|
||||
use mailbox::backends::{BackendOp, BackendOpGenerator, MailBackend, RefreshEvent, RefreshEventConsumer};
|
||||
use mailbox::email::parser;
|
||||
|
||||
extern crate notify;
|
||||
|
||||
use self::notify::{Watcher, RecursiveMode, watcher};
|
||||
use std::time::Duration;
|
||||
|
||||
use std::sync::mpsc::channel;
|
||||
//use std::sync::mpsc::sync_channel;
|
||||
//use std::sync::mpsc::SyncSender;
|
||||
//use std::time::Duration;
|
||||
use std::thread;
|
||||
extern crate crossbeam;
|
||||
use std::path::PathBuf;
|
||||
use memmap::{Mmap, Protection};
|
||||
|
||||
|
||||
pub struct MaildirType {
|
||||
path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct MaildirOp {
|
||||
path: String,
|
||||
|
@ -64,9 +69,10 @@ impl BackendOp for MaildirOp {
|
|||
fn as_bytes(&mut self) -> Result<&[u8]> {
|
||||
if self.slice.is_none() {
|
||||
self.slice = Some(
|
||||
Mmap::open_path(self.path.to_string(), Protection::Read).unwrap(),
|
||||
Mmap::open_path(self.path.to_string(), Protection::Read)?,
|
||||
);
|
||||
}
|
||||
/* Unwrap is safe since we use ? above. */
|
||||
Ok(unsafe { self.slice.as_ref().unwrap().as_slice() })
|
||||
}
|
||||
fn fetch_headers(&mut self) -> Result<&[u8]> {
|
||||
|
@ -107,6 +113,12 @@ impl BackendOp for MaildirOp {
|
|||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MaildirType {
|
||||
path: String,
|
||||
}
|
||||
|
||||
|
||||
impl MailBackend for MaildirType {
|
||||
fn get(&self) -> Result<Vec<Envelope>> {
|
||||
self.get_multicore(4)
|
||||
|
@ -132,6 +144,34 @@ impl MailBackend for MaildirType {
|
|||
Ok(r)
|
||||
*/
|
||||
}
|
||||
fn watch(&self, sender: RefreshEventConsumer, folders: &[String]) -> () {
|
||||
let folders = folders.to_vec();
|
||||
|
||||
thread::Builder::new().name("folder watch".to_string()).spawn(move || {
|
||||
let (tx, rx) = channel();
|
||||
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
|
||||
for f in folders {
|
||||
if MaildirType::is_valid(&f).is_err() {
|
||||
continue;
|
||||
}
|
||||
let mut p = PathBuf::from(&f);
|
||||
p.push("cur");
|
||||
watcher.watch(&p, RecursiveMode::NonRecursive).unwrap();
|
||||
p.pop();
|
||||
p.push("new");
|
||||
watcher.watch(&p, RecursiveMode::NonRecursive).unwrap();
|
||||
eprintln!("watching {:?}", f);
|
||||
}
|
||||
loop {
|
||||
match rx.recv() {
|
||||
Ok(event) => {
|
||||
sender.send(RefreshEvent { folder: format!("{:?}", event) });
|
||||
}
|
||||
Err(e) => eprintln!("watch error: {:?}", e),
|
||||
}
|
||||
}
|
||||
}).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl MaildirType {
|
||||
|
@ -198,9 +238,10 @@ panic!("didn't parse"); },
|
|||
let mut local_r: Vec<Envelope> = Vec::with_capacity(chunk.len());
|
||||
for e in chunk {
|
||||
let e_copy = e.to_string();
|
||||
if let Some(e) = Envelope::from(Box::new(BackendOpGenerator::new(
|
||||
if let Some(mut e) = Envelope::from(Box::new(BackendOpGenerator::new(
|
||||
Box::new(move || Box::new(MaildirOp::new(e_copy.clone()))),
|
||||
))) {
|
||||
e.populate_headers();
|
||||
local_r.push(e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,10 +25,26 @@ use error::Result;
|
|||
|
||||
use std::fmt;
|
||||
|
||||
pub trait MailBackend {
|
||||
fn get(&self) -> Result<Vec<Envelope>>;
|
||||
pub struct RefreshEvent {
|
||||
pub folder: String,
|
||||
}
|
||||
|
||||
pub struct RefreshEventConsumer(Box<Fn(RefreshEvent) -> ()>);
|
||||
unsafe impl Send for RefreshEventConsumer {}
|
||||
unsafe impl Sync for RefreshEventConsumer {}
|
||||
impl RefreshEventConsumer {
|
||||
pub fn new(b: Box<Fn(RefreshEvent) -> ()>) -> Self {
|
||||
RefreshEventConsumer(b)
|
||||
}
|
||||
pub fn send(&self, r: RefreshEvent) -> () {
|
||||
self.0(r);
|
||||
}
|
||||
}
|
||||
pub trait MailBackend: ::std::fmt::Debug {
|
||||
fn get(&self) -> Result<Vec<Envelope>>;
|
||||
fn watch(&self, sender:RefreshEventConsumer, folders: &[String]) -> ();
|
||||
//fn new(folders: &Vec<String>) -> Box<Self>;
|
||||
}
|
||||
|
||||
/// A `BackendOp` manages common operations for the various mail backends. They only live for the
|
||||
/// duration of the operation. They are generated by `BackendOpGenerator` on demand.
|
||||
|
|
|
@ -156,6 +156,7 @@ impl AttachmentBuilder {
|
|||
self
|
||||
}
|
||||
fn decode(&self) -> String {
|
||||
// TODO: Use charset for decoding
|
||||
match self.content_transfer_encoding {
|
||||
ContentTransferEncoding::Base64 => {
|
||||
match ::base64::decode(&::std::str::from_utf8(&self.raw)
|
||||
|
@ -178,8 +179,8 @@ impl AttachmentBuilder {
|
|||
}
|
||||
}
|
||||
ContentTransferEncoding::QuotedPrintable => parser::quoted_printable_text(&self.raw)
|
||||
.to_full_result()
|
||||
.unwrap(),
|
||||
.to_full_result()
|
||||
.unwrap(),
|
||||
ContentTransferEncoding::_7Bit |
|
||||
ContentTransferEncoding::_8Bit |
|
||||
ContentTransferEncoding::Other { .. } => {
|
||||
|
|
|
@ -41,31 +41,25 @@ pub struct Mailbox {
|
|||
pub collection: Vec<Envelope>,
|
||||
pub threaded_collection: Vec<usize>,
|
||||
threads: Vec<Container>,
|
||||
length: usize,
|
||||
}
|
||||
|
||||
|
||||
impl Mailbox {
|
||||
pub fn new(path: &str, sent_folder: &Option<Result<Mailbox>>) -> Result<Mailbox> {
|
||||
let mut collection: Vec<Envelope> = maildir::MaildirType::new(path).get()?;
|
||||
for e in &mut collection {
|
||||
e.populate_headers();
|
||||
}
|
||||
collection.sort_by(|a, b| a.get_date().cmp(&b.get_date()));
|
||||
let (threads, threaded_collection) = build_threads(&mut collection, sent_folder);
|
||||
|
||||
let length = collection.len();
|
||||
|
||||
Ok(Mailbox {
|
||||
path: path.to_string(),
|
||||
collection: collection,
|
||||
threads: threads,
|
||||
length: length,
|
||||
threaded_collection: threaded_collection,
|
||||
})
|
||||
}
|
||||
pub fn get_length(&self) -> usize {
|
||||
self.length
|
||||
self.collection.len()
|
||||
}
|
||||
pub fn get_threaded_mail(&self, i: usize) -> usize {
|
||||
let thread = self.threads[self.threaded_collection[i]];
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
use mailbox::email::Envelope;
|
||||
use mailbox::*;
|
||||
use error::MeliError;
|
||||
|
||||
use super::pager::Pager;
|
||||
use std::error::Error;
|
||||
|
||||
extern crate ncurses;
|
||||
|
@ -28,7 +30,7 @@ extern crate ncurses;
|
|||
pub trait Window {
|
||||
fn draw(&mut self) -> ();
|
||||
fn redraw(&mut self) -> ();
|
||||
fn handle_input(&mut self, input: i32) -> ();
|
||||
fn handle_input(&mut self, input: i32) -> bool;
|
||||
}
|
||||
|
||||
pub struct ErrorWindow {
|
||||
|
@ -46,7 +48,7 @@ impl Window for ErrorWindow {
|
|||
ncurses::waddstr(self.win, &self.description);
|
||||
ncurses::wrefresh(self.win);
|
||||
}
|
||||
fn handle_input(&mut self, _: i32) -> () {}
|
||||
fn handle_input(&mut self, _: i32) -> bool { false }
|
||||
}
|
||||
impl ErrorWindow {
|
||||
pub fn new(err: MeliError) -> Self {
|
||||
|
@ -78,12 +80,14 @@ pub struct Index {
|
|||
threaded: bool,
|
||||
|
||||
cursor_idx: usize,
|
||||
|
||||
pager: Option<Pager>,
|
||||
}
|
||||
|
||||
|
||||
impl Window for Index {
|
||||
fn draw(&mut self) {
|
||||
if self.mailbox.get_length() == 0 {
|
||||
if self.get_length() == 0 {
|
||||
return;
|
||||
}
|
||||
let mut x = 0;
|
||||
|
@ -199,7 +203,7 @@ impl Window for Index {
|
|||
fn redraw(&mut self) -> () {
|
||||
ncurses::wnoutrefresh(self.win);
|
||||
ncurses::doupdate();
|
||||
if self.mailbox.get_length() == 0 {
|
||||
if self.get_length() == 0 {
|
||||
return;
|
||||
}
|
||||
/* Draw newly highlighted entry */
|
||||
|
@ -224,13 +228,31 @@ impl Window for Index {
|
|||
);
|
||||
ncurses::wrefresh(self.win);
|
||||
}
|
||||
fn handle_input(&mut self, motion: i32) {
|
||||
if self.mailbox.get_length() == 0 {
|
||||
return;
|
||||
fn handle_input(&mut self, motion: i32) -> bool {
|
||||
if self.get_length() == 0 {
|
||||
return false;
|
||||
}
|
||||
ncurses::getmaxyx(self.win, &mut self.screen_height, &mut self.screen_width);
|
||||
if self.screen_height == 0 {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if self.pager.is_some() {
|
||||
match motion {
|
||||
m @ ncurses::KEY_UP |
|
||||
m @ ncurses::KEY_DOWN |
|
||||
m @ ncurses::KEY_NPAGE |
|
||||
m @ ncurses::KEY_PPAGE => {
|
||||
self.pager.as_mut().unwrap().scroll(m);
|
||||
return true;
|
||||
},
|
||||
ncurses::KEY_F1 => {
|
||||
self.pager = None;
|
||||
self.redraw();
|
||||
return true;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
let mut x = 0;
|
||||
let mut y = 0;
|
||||
|
@ -240,19 +262,19 @@ impl Window for Index {
|
|||
ncurses::KEY_UP => if self.cursor_idx > 0 {
|
||||
self.cursor_idx -= 1;
|
||||
} else {
|
||||
return;
|
||||
return false;
|
||||
},
|
||||
ncurses::KEY_DOWN => if self.cursor_idx < self.mailbox.get_length() - 1 {
|
||||
ncurses::KEY_DOWN => if self.cursor_idx < self.get_length() - 1 {
|
||||
self.cursor_idx += 1;
|
||||
} else {
|
||||
return;
|
||||
return false;
|
||||
},
|
||||
10 => {
|
||||
self.show_pager();
|
||||
self.redraw();
|
||||
return true;
|
||||
}
|
||||
_ => {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,10 +340,10 @@ impl Window for Index {
|
|||
* └ i-1 ┘
|
||||
*/
|
||||
if pminrow != pminrow_prev &&
|
||||
pminrow + self.screen_height > self.mailbox.get_length() as i32
|
||||
pminrow + self.screen_height > self.get_length() as i32
|
||||
{
|
||||
/* touch dead entries in index (tell ncurses to redraw the empty lines next refresh) */
|
||||
let live_entries = self.mailbox.get_length() as i32 - pminrow;
|
||||
let live_entries = self.get_length() as i32 - pminrow;
|
||||
ncurses::wredrawln(self.win, live_entries, self.screen_height);
|
||||
ncurses::wrefresh(self.win);
|
||||
}
|
||||
|
@ -334,6 +356,7 @@ impl Window for Index {
|
|||
self.screen_height - 1,
|
||||
self.screen_width - 1,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
impl Index {
|
||||
|
@ -365,15 +388,6 @@ impl Index {
|
|||
ncurses::printw(&format!("Mailbox {} is empty.\n", mailbox.path));
|
||||
ncurses::refresh();
|
||||
}
|
||||
let mut color = true;
|
||||
let mut thread_color = Vec::with_capacity(mailbox_length);
|
||||
for i in &mailbox.threaded_collection {
|
||||
let container = mailbox.get_thread(*i);
|
||||
if !container.has_parent() {
|
||||
color = !color;
|
||||
}
|
||||
thread_color.push(color);
|
||||
}
|
||||
Index {
|
||||
mailbox: mailbox,
|
||||
win: win,
|
||||
|
@ -382,9 +396,16 @@ impl Index {
|
|||
screen_height: 0,
|
||||
threaded: true,
|
||||
cursor_idx: 0,
|
||||
pager: None,
|
||||
}
|
||||
}
|
||||
fn get_length(&self) -> usize {
|
||||
if self.threaded {
|
||||
self.mailbox.threaded_collection.len()
|
||||
} else {
|
||||
self.mailbox.get_length()
|
||||
}
|
||||
}
|
||||
|
||||
/* draw_entry() doesn't take &mut self because borrow checker complains if it's called from
|
||||
* another method. */
|
||||
fn draw_entry(
|
||||
|
@ -465,7 +486,7 @@ impl Index {
|
|||
ncurses::wattroff(win, attr);
|
||||
}
|
||||
fn show_pager(&mut self) {
|
||||
if self.mailbox.get_length() == 0 {
|
||||
if self.get_length() == 0 {
|
||||
return;
|
||||
}
|
||||
ncurses::getmaxyx(self.win, &mut self.screen_height, &mut self.screen_width);
|
||||
|
@ -475,22 +496,11 @@ impl Index {
|
|||
} else {
|
||||
&mut self.mailbox.collection[self.cursor_idx]
|
||||
};
|
||||
let mut pager = super::pager::Pager::new(self.win, x);
|
||||
let mut pager = Pager::new(self.win, x);
|
||||
/* TODO: Fix this: */
|
||||
pager.scroll(ncurses::KEY_DOWN);
|
||||
pager.scroll(ncurses::KEY_UP);
|
||||
let mut ch = ncurses::getch();
|
||||
while ch != ncurses::KEY_F(1) {
|
||||
match ch {
|
||||
m @ ncurses::KEY_UP |
|
||||
m @ ncurses::KEY_DOWN |
|
||||
m @ ncurses::KEY_NPAGE |
|
||||
m @ ncurses::KEY_PPAGE => {
|
||||
pager.scroll(m);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ch = ncurses::getch();
|
||||
}
|
||||
self.pager = Some(pager);
|
||||
}
|
||||
}
|
||||
impl Drop for Index {
|
||||
|
|
|
@ -23,6 +23,8 @@ pub mod index;
|
|||
pub mod pager;
|
||||
|
||||
extern crate ncurses;
|
||||
extern crate melib;
|
||||
use melib::mailbox::backends::RefreshEvent;
|
||||
|
||||
/* Color pairs; foreground && background. */
|
||||
pub static COLOR_PAIR_DEFAULT: i16 = 1;
|
||||
|
@ -64,3 +66,15 @@ impl Drop for TUI {
|
|||
ncurses::endwin();
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ThreadEvent {
|
||||
Input(ncurses::WchResult),
|
||||
RefreshMailbox{ name: String },
|
||||
//Decode { _ }, // For gpg2 signature check
|
||||
}
|
||||
|
||||
impl From<RefreshEvent> for ThreadEvent {
|
||||
fn from(event: RefreshEvent) -> Self {
|
||||
ThreadEvent::RefreshMailbox { name: event.folder }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue