2017-09-01 15:24:32 +03:00
/*
2017-09-07 23:00:08 +03:00
* meli - bin . rs
2017-09-01 15:24:32 +03:00
*
2018-07-16 15:26:06 +03:00
* Copyright 2017 - 2018 Manos Pitsidianakis
2017-09-07 23:00:08 +03:00
*
2017-09-01 15:24:32 +03:00
* This file is part of meli .
*
* meli is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* meli is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with meli . If not , see < http ://www.gnu.org/licenses/>.
* /
2018-07-18 10:42:52 +03:00
/*! This crate contains the frontend stuff of the application. The application entry way on `src / bin.rs` creates an event loop and passes input to the `ui` module.
The mail handling stuff is done in the ` melib ` crate which includes all backend needs . The split is done to theoretically be able to create different frontends with the same innards .
* /
2018-08-05 12:14:26 +03:00
use std ::alloc ::System ;
#[ global_allocator ]
static GLOBAL : System = System ;
2017-09-07 23:00:08 +03:00
extern crate melib ;
2018-07-20 12:15:06 +03:00
extern crate ui ;
2018-07-16 11:08:04 +03:00
2018-07-11 18:58:57 +03:00
pub use melib ::* ;
2018-08-06 22:20:34 +03:00
pub use ui ::* ;
2017-09-01 15:24:32 +03:00
2017-09-28 18:06:35 +03:00
use std ::thread ;
2018-07-16 13:36:28 +03:00
#[ macro_use ]
extern crate chan ;
extern crate chan_signal ;
use chan_signal ::Signal ;
2018-07-24 13:28:15 +03:00
extern crate nix ;
2018-07-27 21:37:56 +03:00
fn make_input_thread (
sx : chan ::Sender < ThreadEvent > ,
rx : chan ::Receiver < bool > ,
) -> thread ::JoinHandle < ( ) > {
let stdin = std ::io ::stdin ( ) ;
thread ::Builder ::new ( )
. name ( " input-thread " . to_string ( ) )
. spawn ( move | | {
get_events (
stdin ,
| k | {
sx . send ( ThreadEvent ::Input ( k ) ) ;
} ,
| | {
2018-08-11 18:00:21 +03:00
sx . send ( ThreadEvent ::UIEvent ( UIEventType ::ChangeMode ( UIMode ::Fork ) ) ) ;
2018-07-27 21:37:56 +03:00
} ,
2018-08-07 15:01:15 +03:00
& rx ,
2018-07-27 21:37:56 +03:00
)
} )
. unwrap ( )
2018-07-21 11:20:13 +03:00
}
2018-08-03 13:46:08 +03:00
2017-07-23 14:01:17 +03:00
fn main ( ) {
2018-07-21 11:20:13 +03:00
/* Lock all stdio outs */
2018-07-24 13:28:15 +03:00
//let _stdout = stdout();
//let mut _stdout = _stdout.lock();
2018-07-11 17:07:51 +03:00
/*
2018-07-13 18:38:57 +03:00
let _stderr = stderr ( ) ;
let mut _stderr = _stderr . lock ( ) ;
* /
2018-07-11 17:07:51 +03:00
2018-07-16 15:26:06 +03:00
/* Catch SIGWINCH to handle terminal resizing */
2018-07-16 13:36:28 +03:00
let signal = chan_signal ::notify ( & [ Signal ::WINCH ] ) ;
2018-07-16 15:26:06 +03:00
/* Create a channel to communicate with other threads. The main process is the sole receiver.
* * /
2018-07-16 13:36:28 +03:00
let ( sender , receiver ) = chan ::sync ( ::std ::mem ::size_of ::< ThreadEvent > ( ) ) ;
2018-07-21 11:20:13 +03:00
/*
* Create async channel to block the input - thread if we need to fork and stop it from reading
* stdin , see get_events ( ) for details
* * /
let ( tx , rx ) = chan ::async ( ) ;
2018-07-24 13:28:15 +03:00
/* Get input thread handle to join it if we need to */
let mut _thread_handler = make_input_thread ( sender . clone ( ) , rx . clone ( ) ) ;
2017-09-28 18:06:35 +03:00
2018-07-16 15:26:06 +03:00
/* Create the application State. This is the 'System' part of an ECS architecture */
2018-07-27 21:37:56 +03:00
let mut state = State ::new ( sender . clone ( ) , tx ) ;
2018-07-11 17:07:51 +03:00
2018-07-16 15:26:06 +03:00
/* Register some reasonably useful interfaces */
2018-07-27 21:37:56 +03:00
let menu = Entity {
component : Box ::new ( AccountMenu ::new ( & state . context . accounts ) ) ,
} ;
2018-07-16 15:26:06 +03:00
let listing = MailListing ::new ( ) ;
2018-07-27 21:37:56 +03:00
let b = Entity {
component : Box ::new ( listing ) ,
} ;
2018-08-11 18:00:21 +03:00
let mut tabs = Box ::new ( Tabbed ::new ( vec! [ Box ::new ( VSplit ::new ( menu , b , 90 , true ) ) ] ) ) ;
tabs . add_component ( Box ::new ( Composer { } ) ) ;
let window = Entity { component : tabs } ;
2018-07-27 21:37:56 +03:00
let status_bar = Entity {
component : Box ::new ( StatusBar ::new ( window ) ) ,
} ;
2018-07-14 15:04:42 +03:00
state . register_entity ( status_bar ) ;
2018-07-13 18:38:57 +03:00
2018-07-27 21:37:56 +03:00
let xdg_notifications = Entity {
component : Box ::new ( ui ::components ::notifications ::XDGNotifications { } ) ,
} ;
2018-07-17 17:16:16 +03:00
state . register_entity ( xdg_notifications ) ;
2018-07-16 15:26:06 +03:00
/* Keep track of the input mode. See ui::UIMode for details */
2017-09-16 15:05:28 +03:00
' main : loop {
2018-07-14 21:41:38 +03:00
state . render ( ) ;
2017-07-23 14:01:17 +03:00
2017-09-16 15:05:28 +03:00
' inner : loop {
2018-07-16 15:26:06 +03:00
/* Check if any entities have sent reply events to State. */
2018-07-20 12:44:04 +03:00
let events : Vec < UIEvent > = state . context . replies ( ) ;
2018-07-16 11:08:04 +03:00
for e in events {
state . rcv_event ( e ) ;
}
2018-07-21 11:20:13 +03:00
/* Poll on all channels. Currently we have the input channel for stdin, watching events and the signal watcher. */
2018-07-16 13:36:28 +03:00
chan_select! {
receiver . recv ( ) -> r = > {
match r . unwrap ( ) {
2018-07-24 13:28:15 +03:00
ThreadEvent ::Input ( Key ::Ctrl ( 'z' ) ) = > {
2018-08-07 15:01:15 +03:00
state . switch_to_main_screen ( ) ;
2018-07-24 13:28:15 +03:00
//_thread_handler.join().expect("Couldn't join on the associated thread");
let self_pid = nix ::unistd ::Pid ::this ( ) ;
nix ::sys ::signal ::kill ( self_pid , nix ::sys ::signal ::Signal ::SIGSTOP ) . unwrap ( ) ;
2018-08-07 15:01:15 +03:00
state . switch_to_alternate_screen ( ) ;
2018-07-24 13:28:15 +03:00
_thread_handler = make_input_thread ( sender . clone ( ) , rx . clone ( ) ) ;
// BUG: thread sends input event after one received key
state . update_size ( ) ;
state . render ( ) ;
state . redraw ( ) ;
} ,
2018-07-16 13:36:28 +03:00
ThreadEvent ::Input ( k ) = > {
2018-07-21 11:20:13 +03:00
match state . mode {
2018-07-16 13:36:28 +03:00
UIMode ::Normal = > {
match k {
Key ::Char ( 'q' ) | Key ::Char ( 'Q' ) = > {
2018-07-17 17:16:16 +03:00
drop ( state ) ;
2018-07-16 13:36:28 +03:00
break 'main ;
} ,
Key ::Char ( ';' ) = > {
2018-07-21 11:20:13 +03:00
state . mode = UIMode ::Execute ;
state . rcv_event ( UIEvent { id : 0 , event_type : UIEventType ::ChangeMode ( UIMode ::Execute ) } ) ;
2018-07-16 13:36:28 +03:00
state . redraw ( ) ;
}
key = > {
state . rcv_event ( UIEvent { id : 0 , event_type : UIEventType ::Input ( key ) } ) ;
state . redraw ( ) ;
} ,
}
2018-07-15 01:27:13 +03:00
} ,
2018-07-16 13:36:28 +03:00
UIMode ::Execute = > {
match k {
Key ::Char ( '\n' ) | Key ::Esc = > {
2018-07-21 11:20:13 +03:00
state . mode = UIMode ::Normal ;
state . rcv_event ( UIEvent { id : 0 , event_type : UIEventType ::ChangeMode ( UIMode ::Normal ) } ) ;
2018-07-16 13:36:28 +03:00
state . redraw ( ) ;
} ,
2018-08-07 16:14:06 +03:00
k = > {
2018-07-16 13:36:28 +03:00
state . rcv_event ( UIEvent { id : 0 , event_type : UIEventType ::ExInput ( k ) } ) ;
state . redraw ( ) ;
} ,
}
2018-07-16 11:08:04 +03:00
} ,
2018-07-21 11:20:13 +03:00
UIMode ::Fork = > {
break 'inner ; // `goto` 'reap loop, and wait on child.
} ,
2018-07-15 01:27:13 +03:00
}
2018-07-13 18:38:57 +03:00
} ,
2018-08-03 13:46:08 +03:00
ThreadEvent ::RefreshMailbox { hash : h } = > {
2018-08-08 10:41:25 +03:00
state . hash_to_folder ( h ) ;
2018-08-12 16:55:45 +03:00
state . rcv_event ( UIEvent { id : 0 , event_type : UIEventType ::Notification ( String ::from ( " Update in mailbox " ) ) } ) ;
2018-07-17 17:16:16 +03:00
state . redraw ( ) ;
2018-07-16 13:36:28 +03:00
} ,
2018-08-06 13:33:10 +03:00
ThreadEvent ::UIEvent ( UIEventType ::ChangeMode ( f ) ) = > {
2018-07-21 11:20:13 +03:00
state . mode = f ;
break 'inner ; // `goto` 'reap loop, and wait on child.
}
2018-08-06 13:33:10 +03:00
ThreadEvent ::UIEvent ( UIEventType ::StartupCheck ) = > {
let mut flag = false ;
2018-08-15 15:32:30 +03:00
let mut render_flag = false ;
2018-08-06 13:33:10 +03:00
for account in & mut state . context . accounts {
let len = account . len ( ) ;
for i in 0 .. len {
match account . status ( i ) {
2018-08-15 15:32:30 +03:00
Ok ( true ) = > {
render_flag = true ;
} ,
Ok ( false ) = > { } ,
2018-08-06 13:33:10 +03:00
Err ( _ ) = > {
flag | = true ;
}
}
}
}
2018-08-06 22:20:34 +03:00
if ! flag {
state . finish_startup ( ) ;
}
2018-08-15 15:32:30 +03:00
if render_flag {
state . render ( ) ;
}
2018-08-06 13:33:10 +03:00
}
ThreadEvent ::UIEvent ( e ) = > {
2018-07-16 13:36:28 +03:00
state . rcv_event ( UIEvent { id : 0 , event_type : e } ) ;
state . render ( ) ;
2018-07-13 18:38:57 +03:00
} ,
2018-08-06 14:58:54 +03:00
ThreadEvent ::ThreadJoin ( id ) = > {
state . join ( id ) ;
} ,
2017-09-28 18:06:35 +03:00
}
2017-09-01 15:24:32 +03:00
} ,
2018-07-16 13:36:28 +03:00
signal . recv ( ) -> signal = > {
2018-07-21 11:20:13 +03:00
if state . mode ! = UIMode ::Fork {
if let Some ( Signal ::WINCH ) = signal {
state . update_size ( ) ;
state . render ( ) ;
state . redraw ( ) ;
}
2018-07-16 13:36:28 +03:00
}
2018-07-13 18:38:57 +03:00
} ,
2017-07-23 14:01:17 +03:00
}
2018-07-21 11:20:13 +03:00
} // end of 'inner
' reap : loop {
match state . try_wait_on_child ( ) {
Some ( true ) = > {
make_input_thread ( sender . clone ( ) , rx . clone ( ) ) ;
state . mode = UIMode ::Normal ;
state . render ( ) ;
2018-07-27 21:37:56 +03:00
}
2018-07-21 11:20:13 +03:00
Some ( false ) = > {
use std ::{ thread , time } ;
2018-07-21 17:29:29 +03:00
let ten_millis = time ::Duration ::from_millis ( 1500 ) ;
2018-07-21 11:20:13 +03:00
thread ::sleep ( ten_millis ) ;
2018-07-24 13:28:15 +03:00
2018-07-21 11:20:13 +03:00
continue 'reap ;
2018-07-27 21:37:56 +03:00
}
None = > {
break 'reap ;
}
2018-07-21 11:20:13 +03:00
}
2017-07-23 14:01:17 +03:00
}
}
}