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
2019-07-17 20:35:11 +03:00
//!
//! This crate contains the frontend stuff of the application. The application entry way on
2020-02-04 15:52:12 +02:00
//! `src/bin.rs` creates an event loop and passes input to a thread.
2019-07-17 20:35:11 +03:00
//!
//! 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-07-18 10:42:52 +03:00
2018-08-05 12:14:26 +03:00
use std ::alloc ::System ;
2020-02-04 15:52:12 +02:00
use std ::collections ::VecDeque ;
2020-06-07 18:02:20 +03:00
use std ::path ::PathBuf ;
2020-02-04 15:52:12 +02:00
extern crate xdg_utils ;
#[ macro_use ]
extern crate serde_derive ;
extern crate linkify ;
extern crate uuid ;
2020-05-28 21:01:13 +03:00
extern crate bitflags ;
2020-02-04 15:52:12 +02:00
extern crate serde_json ;
2020-07-24 20:13:18 +03:00
#[ macro_use ]
2020-02-04 15:52:12 +02:00
extern crate smallvec ;
2020-06-06 23:22:26 +03:00
extern crate termion ;
2018-08-05 12:14:26 +03:00
#[ global_allocator ]
static GLOBAL : System = System ;
2020-02-04 15:52:12 +02:00
#[ macro_use ]
extern crate melib ;
2019-12-01 17:11:13 +02:00
use melib ::* ;
2020-02-04 15:52:12 +02:00
mod unix ;
use unix ::* ;
#[ macro_use ]
pub mod types ;
use crate ::types ::* ;
#[ macro_use ]
pub mod terminal ;
use crate ::terminal ::* ;
#[ macro_use ]
2020-07-25 13:08:36 +03:00
pub mod command ;
use crate ::command ::* ;
2020-02-04 15:52:12 +02:00
pub mod state ;
use crate ::state ::* ;
pub mod components ;
use crate ::components ::* ;
#[ macro_use ]
pub mod conf ;
use crate ::conf ::* ;
#[ cfg(feature = " sqlite3 " ) ]
pub mod sqlite3 ;
2020-07-04 15:59:09 +03:00
pub mod jobs ;
2020-02-04 15:52:12 +02:00
pub mod mailcap ;
pub mod plugins ;
2017-09-01 15:24:32 +03:00
2019-09-09 12:53:39 +03:00
use std ::os ::raw ::c_int ;
2020-01-08 17:07:14 +02:00
2019-09-09 12:53:39 +03:00
fn notify (
signals : & [ c_int ] ,
2019-09-22 11:00:05 +03:00
sender : crossbeam ::channel ::Sender < ThreadEvent > ,
2019-09-09 12:53:39 +03:00
) -> std ::result ::Result < crossbeam ::channel ::Receiver < c_int > , std ::io ::Error > {
2020-03-12 09:47:39 +02:00
use std ::time ::Duration ;
2020-05-18 15:47:19 +03:00
let ( alarm_pipe_r , alarm_pipe_w ) = nix ::unistd ::pipe ( ) . map_err ( | err | {
std ::io ::Error ::from_raw_os_error ( err . as_errno ( ) . map ( | n | n as i32 ) . unwrap_or ( 0 ) )
} ) ? ;
2020-01-08 17:07:14 +02:00
let alarm_handler = move | info : & nix ::libc ::siginfo_t | {
let value = unsafe { info . si_value ( ) . sival_ptr as u8 } ;
2020-05-18 15:47:19 +03:00
let _ = nix ::unistd ::write ( alarm_pipe_w , & [ value ] ) ;
2020-01-08 17:07:14 +02:00
} ;
unsafe {
signal_hook_registry ::register_sigaction ( signal_hook ::SIGALRM , alarm_handler ) ? ;
}
2019-09-09 12:53:39 +03:00
let ( s , r ) = crossbeam ::channel ::bounded ( 100 ) ;
let signals = signal_hook ::iterator ::Signals ::new ( signals ) ? ;
2020-05-18 15:47:19 +03:00
let _ = nix ::fcntl ::fcntl (
alarm_pipe_r ,
nix ::fcntl ::FcntlArg ::F_SETFL ( nix ::fcntl ::OFlag ::O_NONBLOCK ) ,
) ;
2019-09-09 12:53:39 +03:00
std ::thread ::spawn ( move | | {
2020-05-18 15:47:19 +03:00
let mut buf = [ 0 ; 1 ] ;
2019-09-22 11:00:05 +03:00
let mut ctr = 0 ;
loop {
ctr % = 3 ;
if ctr = = 0 {
2020-05-18 15:47:19 +03:00
let _ = sender
. send_timeout ( ThreadEvent ::Pulse , Duration ::from_millis ( 500 ) )
. ok ( ) ;
2019-09-22 11:00:05 +03:00
}
for signal in signals . pending ( ) {
2020-05-18 15:47:19 +03:00
let _ = s . send_timeout ( signal , Duration ::from_millis ( 500 ) ) . ok ( ) ;
}
while nix ::unistd ::read ( alarm_pipe_r , buf . as_mut ( ) )
. map ( | s | s > 0 )
. unwrap_or ( false )
{
let value = buf [ 0 ] ;
2020-06-22 17:29:47 +03:00
let _ = sender . send_timeout (
ThreadEvent ::UIEvent ( UIEvent ::Timer ( value ) ) ,
Duration ::from_millis ( 2000 ) ,
) ;
2019-09-22 11:00:05 +03:00
}
std ::thread ::sleep ( std ::time ::Duration ::from_millis ( 100 ) ) ;
ctr + = 1 ;
2019-09-09 12:53:39 +03:00
}
} ) ;
Ok ( r )
}
2020-06-07 18:02:20 +03:00
fn parse_manpage ( src : & str ) -> Result < ManPages > {
match src {
" " | " meli " | " main " = > Ok ( ManPages ::Main ) ,
" meli.conf " | " conf " | " config " | " configuration " = > Ok ( ManPages ::Conf ) ,
" meli-themes " | " themes " | " theming " | " theme " = > Ok ( ManPages ::Themes ) ,
_ = > Err ( MeliError ::new ( format! (
" Invalid documentation page: {} " ,
src
) ) ) ,
}
2019-07-11 11:45:09 +03:00
}
2020-06-07 18:02:20 +03:00
use structopt ::StructOpt ;
#[ derive(Debug) ]
/// Choose manpage
2020-02-09 02:26:21 +02:00
enum ManPages {
2020-06-07 18:02:20 +03:00
/// meli(1)
2020-02-09 02:26:21 +02:00
Main = 0 ,
2020-06-07 18:02:20 +03:00
/// meli.conf(5)
2020-02-09 02:26:21 +02:00
Conf = 1 ,
2020-06-07 18:02:20 +03:00
/// meli-themes(5)
2020-02-09 02:26:21 +02:00
Themes = 2 ,
}
2020-06-07 18:02:20 +03:00
#[ derive(Debug, StructOpt) ]
#[ structopt(name = " meli " , about = " terminal mail client " , version_short = " v " ) ]
struct Opt {
/// use specified configuration file
#[ structopt(short, long, parse(from_os_str)) ]
config : Option < PathBuf > ,
#[ structopt(subcommand) ]
subcommand : Option < SubCommand > ,
}
#[ derive(Debug, StructOpt) ]
enum SubCommand {
/// print default theme in full to stdout and exit.
PrintDefaultTheme ,
/// print loaded themes in full to stdout and exit.
PrintLoadedThemes ,
/// create a sample configuration file with available configuration options. If PATH is not specified, meli will try to create it in $XDG_CONFIG_HOME/meli/config.toml
#[ structopt(display_order = 1) ]
CreateConfig {
#[ structopt(value_name = " NEW_CONFIG_PATH " , parse(from_os_str)) ]
path : Option < PathBuf > ,
} ,
/// test a configuration file for syntax issues or missing options.
#[ structopt(display_order = 2) ]
TestConfig {
#[ structopt(value_name = " CONFIG_PATH " , parse(from_os_str)) ]
path : Option < PathBuf > ,
} ,
#[ structopt(visible_alias= " docs " , aliases=& [ " docs " , " manpage " , " manpages " ] ) ]
#[ structopt(display_order = 3) ]
/// print documentation page and exit (Piping to a pager is recommended.).
Man ( ManOpt ) ,
2020-06-10 18:06:28 +03:00
/// View mail from input file.
View {
#[ structopt(value_name = " INPUT " , parse(from_os_str)) ]
path : PathBuf ,
} ,
2020-06-07 18:02:20 +03:00
}
#[ derive(Debug, StructOpt) ]
struct ManOpt {
#[ structopt(default_value = " meli " , possible_values=& [ " meli " , " conf " , " themes " ] , value_name= " PAGE " , parse(try_from_str = parse_manpage)) ]
page : ManPages ,
2019-07-11 11:45:09 +03:00
}
2018-07-24 13:28:15 +03:00
2019-11-16 00:33:22 +02:00
fn main ( ) {
2020-06-07 18:02:20 +03:00
let opt = Opt ::from_args ( ) ;
::std ::process ::exit ( match run_app ( opt ) {
2019-11-16 00:33:22 +02:00
Ok ( ( ) ) = > 0 ,
Err ( err ) = > {
eprintln! ( " {} " , err ) ;
1
}
} ) ;
}
2020-06-07 18:02:20 +03:00
fn run_app ( opt : Opt ) -> Result < ( ) > {
if let Some ( config_location ) = opt . config . as_ref ( ) {
std ::env ::set_var ( " MELI_CONFIG " , config_location ) ;
2019-07-11 11:45:09 +03:00
}
2020-06-07 18:02:20 +03:00
match opt . subcommand {
Some ( SubCommand ::TestConfig { path } ) = > {
let config_path = if let Some ( path ) = path {
path
} else {
crate ::conf ::get_config_file ( ) ?
} ;
conf ::FileSettings ::validate ( config_path ) ? ;
return Ok ( ( ) ) ;
2020-02-09 02:26:21 +02:00
}
2020-06-07 18:02:20 +03:00
Some ( SubCommand ::CreateConfig { path } ) = > {
let config_path = if let Some ( path ) = path {
path
} else {
crate ::conf ::get_config_file ( ) ?
} ;
if config_path . exists ( ) {
return Err ( MeliError ::new ( format! (
" File `{}` already exists. \n Maybe you meant to specify another path? " ,
config_path . display ( )
) ) ) ;
}
conf ::create_config_file ( & config_path ) ? ;
return Ok ( ( ) ) ;
2019-07-11 11:45:09 +03:00
}
2020-02-09 02:26:21 +02:00
#[ cfg(feature = " cli-docs " ) ]
2020-06-07 18:02:20 +03:00
Some ( SubCommand ::Man ( manopt ) ) = > {
let _page = manopt . page ;
2020-02-09 02:26:21 +02:00
const MANPAGES : [ & 'static str ; 3 ] = [
include_str! ( concat! ( env! ( " OUT_DIR " ) , " /meli.txt " ) ) ,
include_str! ( concat! ( env! ( " OUT_DIR " ) , " /meli.conf.txt " ) ) ,
include_str! ( concat! ( env! ( " OUT_DIR " ) , " /meli-themes.txt " ) ) ,
] ;
println! ( " {} " , MANPAGES [ _page as usize ] ) ;
return Ok ( ( ) ) ;
}
#[ cfg(not(feature = " cli-docs " )) ]
2020-06-07 18:02:20 +03:00
Some ( SubCommand ::Man ( _manopt ) ) = > {
return Err ( MeliError ::new ( " error: this version of meli was not build with embedded documentation. You might have it installed as manpages (eg `man meli`), otherwise check https://meli.delivery " ) ) ;
2020-02-09 02:26:21 +02:00
}
2020-06-07 18:02:20 +03:00
Some ( SubCommand ::PrintLoadedThemes ) = > {
let s = conf ::FileSettings ::new ( ) ? ;
print! ( " {} " , s . terminal . themes . to_string ( ) ) ;
return Ok ( ( ) ) ;
}
Some ( SubCommand ::PrintDefaultTheme ) = > {
print! ( " {} " , conf ::Themes ::default ( ) . key_to_string ( " dark " , false ) ) ;
return Ok ( ( ) ) ;
}
2020-06-10 18:06:28 +03:00
Some ( SubCommand ::View { ref path } ) = > {
if ! path . exists ( ) {
return Err ( MeliError ::new ( format! (
" `{}` is not a valid path " ,
path . display ( )
) ) ) ;
} else if ! path . is_file ( ) {
return Err ( MeliError ::new ( format! (
" `{}` is a directory " ,
path . display ( )
) ) ) ;
}
}
2020-06-07 18:02:20 +03:00
None = > { }
2019-07-11 11:45:09 +03:00
}
2018-07-11 17:07:51 +03:00
2020-01-08 17:07:14 +02:00
/* Create a channel to communicate with other threads. The main process is the sole receiver.
* * /
let ( sender , receiver ) = crossbeam ::channel ::bounded ( 32 * ::std ::mem ::size_of ::< ThreadEvent > ( ) ) ;
2018-07-16 15:26:06 +03:00
/* Catch SIGWINCH to handle terminal resizing */
2019-09-09 12:53:39 +03:00
let signals = & [
/* Catch SIGWINCH to handle terminal resizing */
signal_hook ::SIGWINCH ,
2019-11-05 08:32:27 +02:00
/* Catch SIGCHLD to handle embed applications status change */
signal_hook ::SIGCHLD ,
2019-09-09 12:53:39 +03:00
] ;
2020-01-08 17:07:14 +02:00
let signal_recvr = notify ( signals , sender . clone ( ) ) ? ;
/* Create the application State. */
2020-06-10 18:06:28 +03:00
let mut state ;
if let Some ( SubCommand ::View { path } ) = opt . subcommand {
let bytes = std ::fs ::read ( & path )
. chain_err_summary ( | | format! ( " Could not read from ` {} ` " , path . display ( ) ) ) ? ;
2020-09-09 22:51:55 +03:00
let wrapper = Mail ::new ( bytes , Some ( Flag ::SEEN ) )
2020-06-10 18:06:28 +03:00
. chain_err_summary ( | | format! ( " Could not parse ` {} ` " , path . display ( ) ) ) ? ;
state = State ::new (
Some ( Settings ::without_accounts ( ) . unwrap_or_default ( ) ) ,
sender ,
receiver . clone ( ) ,
) ? ;
state . register_component ( Box ::new ( EnvelopeView ::new ( wrapper , None , None , 0 ) ) ) ;
} else {
state = State ::new ( None , sender , receiver . clone ( ) ) ? ;
2020-06-12 01:37:57 +03:00
#[ cfg(feature = " svgscreenshot " ) ]
state . register_component ( Box ::new ( components ::svg ::SVGScreenshotFilter ::new ( ) ) ) ;
2020-06-10 18:06:28 +03:00
let window = Box ::new ( Tabbed ::new (
vec! [
Box ::new ( listing ::Listing ::new ( & mut state . context ) ) ,
Box ::new ( ContactList ::new ( & state . context ) ) ,
Box ::new ( StatusPanel ::new ( crate ::conf ::value (
& state . context ,
" theme_default " ,
) ) ) ,
] ,
& state . context ,
) ) ;
let status_bar = Box ::new ( StatusBar ::new ( window ) ) ;
state . register_component ( status_bar ) ;
2020-09-10 20:31:12 +03:00
#[ cfg(feature = " dbus-notifications " ) ]
{
let dbus_notifications = Box ::new ( components ::notifications ::DbusNotifications ::new ( ) ) ;
state . register_component ( dbus_notifications ) ;
}
state . register_component ( Box ::new (
components ::notifications ::NotificationCommand ::new ( ) ,
) ) ;
2020-06-10 18:06:28 +03:00
}
2020-07-25 13:24:42 +03:00
let enter_command_mode : Key = state
. context
. settings
. shortcuts
. general
. enter_command_mode
. clone ( ) ;
2018-07-17 17:16:16 +03:00
2020-02-04 15:52:12 +02:00
/* Keep track of the input mode. See 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 {
2019-04-10 22:01:02 +03:00
/* Check if any components have sent reply events to State. */
2020-02-04 15:52:12 +02:00
let events : smallvec ::SmallVec < [ UIEvent ; 8 ] > = state . context . replies ( ) ;
2018-07-16 11:08:04 +03:00
for e in events {
state . rcv_event ( e ) ;
}
2018-10-14 19:49:16 +03:00
state . redraw ( ) ;
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. */
2019-09-09 12:53:39 +03:00
crossbeam ::select! {
recv ( receiver ) -> r = > {
2019-09-22 11:00:05 +03:00
match r {
2020-01-27 17:15:29 +02:00
Ok ( ThreadEvent ::Pulse ) | Ok ( ThreadEvent ::UIEvent ( UIEvent ::Timer ( _ ) ) ) = > { } ,
2019-09-22 11:00:05 +03:00
_ = > { debug! ( & r ) ; }
}
match r . unwrap ( ) {
2020-05-29 15:35:29 +03:00
ThreadEvent ::Input ( ( Key ::Ctrl ( 'z' ) , _ ) ) if state . mode ! = UIMode ::Embed = > {
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
// BUG: thread sends input event after one received key
state . update_size ( ) ;
state . render ( ) ;
state . redraw ( ) ;
} ,
2020-05-29 15:35:29 +03:00
ThreadEvent ::Input ( raw_input @ ( Key ::Ctrl ( 'l' ) , _ ) ) = > {
2019-11-19 20:39:43 +02:00
/* Manual screen redraw */
state . update_size ( ) ;
state . render ( ) ;
state . redraw ( ) ;
2020-05-29 15:35:29 +03:00
if state . mode = = UIMode ::Embed {
2020-06-12 01:42:06 +03:00
state . rcv_event ( UIEvent ::EmbedInput ( raw_input ) ) ;
state . redraw ( ) ;
2020-05-29 15:35:29 +03:00
}
2019-11-19 20:39:43 +02:00
} ,
2020-05-29 15:35:29 +03:00
ThreadEvent ::Input ( ( k , r ) ) = > {
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' ) = > {
2019-09-27 13:14:16 +03:00
if state . can_quit_cleanly ( ) {
drop ( state ) ;
break 'main ;
} else {
state . redraw ( ) ;
}
2018-07-16 13:36:28 +03:00
} ,
2020-07-25 13:24:42 +03:00
_ if k = = enter_command_mode = > {
2020-07-25 13:08:36 +03:00
state . mode = UIMode ::Command ;
state . rcv_event ( UIEvent ::ChangeMode ( UIMode ::Command ) ) ;
2018-07-16 13:36:28 +03:00
state . redraw ( ) ;
}
key = > {
2019-04-10 23:37:20 +03:00
state . rcv_event ( UIEvent ::Input ( key ) ) ;
2018-07-16 13:36:28 +03:00
state . redraw ( ) ;
} ,
}
2018-07-15 01:27:13 +03:00
} ,
2019-02-25 11:11:56 +02:00
UIMode ::Insert = > {
match k {
Key ::Char ( '\n' ) | Key ::Esc = > {
state . mode = UIMode ::Normal ;
2019-04-10 23:37:20 +03:00
state . rcv_event ( UIEvent ::ChangeMode ( UIMode ::Normal ) ) ;
2019-02-25 11:11:56 +02:00
state . redraw ( ) ;
} ,
k = > {
2019-04-10 23:37:20 +03:00
state . rcv_event ( UIEvent ::InsertInput ( k ) ) ;
2019-02-25 11:11:56 +02:00
state . redraw ( ) ;
} ,
}
}
2020-07-25 13:08:36 +03:00
UIMode ::Command = > {
2018-07-16 13:36:28 +03:00
match k {
2020-07-25 13:00:23 +03:00
Key ::Char ( '\n' ) = > {
2018-07-21 11:20:13 +03:00
state . mode = UIMode ::Normal ;
2019-04-10 23:37:20 +03:00
state . rcv_event ( UIEvent ::ChangeMode ( UIMode ::Normal ) ) ;
2018-07-16 13:36:28 +03:00
state . redraw ( ) ;
} ,
2018-08-07 16:14:06 +03:00
k = > {
2020-07-25 13:08:36 +03:00
state . rcv_event ( UIEvent ::CmdInput ( k ) ) ;
2018-07-16 13:36:28 +03:00
state . redraw ( ) ;
} ,
}
2018-07-16 11:08:04 +03:00
} ,
2020-05-29 15:35:29 +03:00
UIMode ::Embed = > {
state . rcv_event ( UIEvent ::EmbedInput ( ( k , r ) ) ) ;
state . redraw ( ) ;
} ,
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-09-05 16:08:11 +03:00
ThreadEvent ::RefreshMailbox ( event ) = > {
2018-09-12 15:10:19 +03:00
state . refresh_event ( * event ) ;
2018-07-17 17:16:16 +03:00
state . redraw ( ) ;
2018-07-16 13:36:28 +03:00
} ,
2019-04-10 23:37:20 +03:00
ThreadEvent ::UIEvent ( UIEvent ::ChangeMode ( f ) ) = > {
2018-07-21 11:20:13 +03:00
state . mode = f ;
2019-02-25 11:11:56 +02:00
if f = = UIMode ::Fork {
break 'inner ; // `goto` 'reap loop, and wait on child.
}
2018-07-21 11:20:13 +03:00
}
2018-08-06 13:33:10 +03:00
ThreadEvent ::UIEvent ( e ) = > {
2019-04-10 23:37:20 +03:00
state . rcv_event ( e ) ;
2019-12-14 19:56:43 +02:00
state . redraw ( ) ;
2018-07-13 18:38:57 +03:00
} ,
2019-09-11 17:57:55 +03:00
ThreadEvent ::Pulse = > {
2019-11-23 18:00:00 +02:00
state . check_accounts ( ) ;
2019-09-11 17:57:55 +03:00
state . redraw ( ) ;
} ,
2020-06-26 18:31:37 +03:00
ThreadEvent ::JobFinished ( id ) = > {
debug! ( " Job finished {} " , id ) ;
2020-08-17 15:31:30 +03:00
for account in state . context . accounts . values_mut ( ) {
2020-06-27 21:40:46 +03:00
if account . process_event ( & id ) {
break ;
}
}
2020-06-26 18:31:37 +03:00
//state.new_thread(id, name);
} ,
2017-09-28 18:06:35 +03:00
}
2017-09-01 15:24:32 +03:00
} ,
2019-09-09 12:53:39 +03:00
recv ( signal_recvr ) -> sig = > {
match sig . unwrap ( ) {
signal_hook ::SIGWINCH = > {
if state . mode ! = UIMode ::Fork {
state . update_size ( ) ;
state . render ( ) ;
state . redraw ( ) ;
}
} ,
2020-02-27 16:46:47 +02:00
signal_hook ::SIGCHLD = > {
state . rcv_event ( UIEvent ::EmbedInput ( ( Key ::Null , vec! [ 0 ] ) ) ) ;
state . redraw ( ) ;
}
2020-01-02 00:11:13 +02:00
other = > {
debug! ( " got other signal: {:?} " , other ) ;
}
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 ) = > {
2018-08-16 16:32:47 +03:00
state . restore_input ( ) ;
2018-09-04 01:49:29 +03:00
state . switch_to_alternate_screen ( ) ;
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 = > {
2018-09-04 01:49:29 +03:00
state . mode = UIMode ::Normal ;
state . render ( ) ;
2018-07-27 21:37:56 +03:00
break 'reap ;
}
2018-07-21 11:20:13 +03:00
}
2017-07-23 14:01:17 +03:00
}
}
2019-09-09 12:53:39 +03:00
Ok ( ( ) )
2017-07-23 14:01:17 +03:00
}