2017-09-01 12:24:32 +00:00
/*
2017-09-07 20:00:08 +00:00
* meli - bin . rs
2017-09-01 12:24:32 +00:00
*
2018-07-16 12:26:06 +00:00
* Copyright 2017 - 2018 Manos Pitsidianakis
2017-09-07 20:00:08 +00:00
*
2017-09-01 12:24:32 +00: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 07:42:52 +00:00
2019-07-17 17:35:11 +00:00
//!
//! This crate contains the frontend stuff of the application. The application entry way on
2020-02-04 13:52:12 +00:00
//! `src/bin.rs` creates an event loop and passes input to a thread.
2019-07-17 17:35:11 +00: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 07:42:52 +00:00
2018-08-05 09:14:26 +00:00
use std ::alloc ::System ;
2020-02-04 13:52:12 +00:00
use std ::collections ::VecDeque ;
2020-06-07 15:02:20 +00:00
use std ::path ::PathBuf ;
2020-09-16 16:57:06 +00:00
2020-02-04 13:52:12 +00:00
#[ macro_use ]
extern crate serde_derive ;
extern crate linkify ;
2022-05-02 12:44:39 +00:00
pub ( crate ) use melib ::uuid ;
2020-02-04 13:52:12 +00:00
2020-05-28 18:01:13 +00:00
extern crate bitflags ;
2020-02-04 13:52:12 +00:00
extern crate serde_json ;
2020-07-24 17:13:18 +00:00
#[ macro_use ]
2020-02-04 13:52:12 +00:00
extern crate smallvec ;
2020-06-06 20:22:26 +00:00
extern crate termion ;
2018-08-05 09:14:26 +00:00
2022-08-25 12:17:18 +00:00
use structopt ::StructOpt ;
2018-08-05 09:14:26 +00:00
#[ global_allocator ]
static GLOBAL : System = System ;
2020-02-04 13:52:12 +00:00
#[ macro_use ]
extern crate melib ;
2019-12-01 15:11:13 +00:00
use melib ::* ;
2020-02-04 13:52:12 +00:00
#[ macro_use ]
pub mod types ;
use crate ::types ::* ;
#[ macro_use ]
pub mod terminal ;
use crate ::terminal ::* ;
#[ macro_use ]
2020-07-25 10:08:36 +00:00
pub mod command ;
use crate ::command ::* ;
2020-02-04 13:52:12 +00: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 12:59:09 +00:00
pub mod jobs ;
2020-02-04 13:52:12 +00:00
pub mod mailcap ;
2017-09-01 12:24:32 +00:00
2019-09-09 09:53:39 +00:00
use std ::os ::raw ::c_int ;
2020-01-08 15:07:14 +00:00
2019-09-09 09:53:39 +00:00
fn notify (
signals : & [ c_int ] ,
2019-09-22 08:00:05 +00:00
sender : crossbeam ::channel ::Sender < ThreadEvent > ,
2019-09-09 09:53:39 +00:00
) -> std ::result ::Result < crossbeam ::channel ::Receiver < c_int > , std ::io ::Error > {
2020-03-12 07:47:39 +00:00
use std ::time ::Duration ;
2022-05-02 12:44:39 +00:00
let ( alarm_pipe_r , alarm_pipe_w ) =
nix ::unistd ::pipe ( ) . map_err ( | err | std ::io ::Error ::from_raw_os_error ( err as i32 ) ) ? ;
2020-01-08 15:07:14 +00:00
let alarm_handler = move | info : & nix ::libc ::siginfo_t | {
let value = unsafe { info . si_value ( ) . sival_ptr as u8 } ;
2020-05-18 12:47:19 +00:00
let _ = nix ::unistd ::write ( alarm_pipe_w , & [ value ] ) ;
2020-01-08 15:07:14 +00:00
} ;
unsafe {
2022-05-02 12:44:39 +00:00
signal_hook_registry ::register_sigaction ( signal_hook ::consts ::SIGALRM , alarm_handler ) ? ;
2020-01-08 15:07:14 +00:00
}
2019-09-09 09:53:39 +00:00
let ( s , r ) = crossbeam ::channel ::bounded ( 100 ) ;
2022-05-02 12:44:39 +00:00
let mut signals = signal_hook ::iterator ::Signals ::new ( signals ) ? ;
2020-05-18 12:47:19 +00:00
let _ = nix ::fcntl ::fcntl (
alarm_pipe_r ,
nix ::fcntl ::FcntlArg ::F_SETFL ( nix ::fcntl ::OFlag ::O_NONBLOCK ) ,
) ;
2019-09-09 09:53:39 +00:00
std ::thread ::spawn ( move | | {
2019-09-22 08:00:05 +00:00
let mut ctr = 0 ;
loop {
ctr % = 3 ;
if ctr = = 0 {
2020-05-18 12:47:19 +00:00
let _ = sender
. send_timeout ( ThreadEvent ::Pulse , Duration ::from_millis ( 500 ) )
. ok ( ) ;
2019-09-22 08:00:05 +00:00
}
for signal in signals . pending ( ) {
2020-05-18 12:47:19 +00:00
let _ = s . send_timeout ( signal , Duration ::from_millis ( 500 ) ) . ok ( ) ;
}
2019-09-22 08:00:05 +00:00
std ::thread ::sleep ( std ::time ::Duration ::from_millis ( 100 ) ) ;
ctr + = 1 ;
2019-09-09 09:53:39 +00:00
}
} ) ;
Ok ( r )
}
2022-08-25 12:17:18 +00:00
#[ cfg(feature = " cli-docs " ) ]
2020-06-07 15:02:20 +00: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 08:45:09 +00:00
}
2022-08-25 12:17:18 +00:00
#[ cfg(feature = " cli-docs " ) ]
2020-10-17 17:50:29 +00:00
#[ derive(Copy, Clone, Debug) ]
2020-06-07 15:02:20 +00:00
/// Choose manpage
2020-02-09 00:26:21 +00:00
enum ManPages {
2020-06-07 15:02:20 +00:00
/// meli(1)
2020-02-09 00:26:21 +00:00
Main = 0 ,
2020-06-07 15:02:20 +00:00
/// meli.conf(5)
2020-02-09 00:26:21 +00:00
Conf = 1 ,
2020-06-07 15:02:20 +00:00
/// meli-themes(5)
2020-02-09 00:26:21 +00:00
Themes = 2 ,
}
2020-06-07 15:02:20 +00: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 15:06:28 +00:00
2021-09-08 19:09:32 +00:00
#[ structopt(display_order = 4) ]
/// print compile time feature flags of this binary
CompiledWith ,
2020-06-10 15:06:28 +00:00
/// View mail from input file.
View {
#[ structopt(value_name = " INPUT " , parse(from_os_str)) ]
path : PathBuf ,
} ,
2020-06-07 15:02:20 +00:00
}
#[ derive(Debug, StructOpt) ]
struct ManOpt {
#[ structopt(default_value = " meli " , possible_values=& [ " meli " , " conf " , " themes " ] , value_name= " PAGE " , parse(try_from_str = parse_manpage)) ]
2022-08-25 12:17:18 +00:00
#[ cfg(feature = " cli-docs " ) ]
2020-06-07 15:02:20 +00:00
page : ManPages ,
2020-10-17 17:50:29 +00:00
/// If true, output text in stdout instead of spawning $PAGER.
#[ structopt(long = " no-raw " , alias = " no-raw " , value_name = " bool " ) ]
2022-08-25 12:17:18 +00:00
#[ cfg(feature = " cli-docs " ) ]
2020-10-17 17:50:29 +00:00
no_raw : Option < Option < bool > > ,
2019-07-11 08:45:09 +00:00
}
2018-07-24 10:28:15 +00:00
2019-11-15 22:33:22 +00:00
fn main ( ) {
2020-06-07 15:02:20 +00:00
let opt = Opt ::from_args ( ) ;
::std ::process ::exit ( match run_app ( opt ) {
2019-11-15 22:33:22 +00:00
Ok ( ( ) ) = > 0 ,
Err ( err ) = > {
eprintln! ( " {} " , err ) ;
1
}
} ) ;
}
2020-06-07 15:02:20 +00: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 08:45:09 +00:00
}
2020-06-07 15:02:20 +00:00
match opt . subcommand {
Some ( SubCommand ::TestConfig { path } ) = > {
let config_path = if let Some ( path ) = path {
path
} else {
crate ::conf ::get_config_file ( ) ?
} ;
2022-03-20 14:31:55 +00:00
conf ::FileSettings ::validate ( config_path , true , false ) ? ; // TODO: test for tty/interaction
2020-06-07 15:02:20 +00:00
return Ok ( ( ) ) ;
2020-02-09 00:26:21 +00:00
}
2020-06-07 15:02:20 +00: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 08:45:09 +00:00
}
2020-02-09 00:26:21 +00:00
#[ cfg(feature = " cli-docs " ) ]
2020-06-07 15:02:20 +00:00
Some ( SubCommand ::Man ( manopt ) ) = > {
2020-10-17 17:50:29 +00:00
let ManOpt { page , no_raw } = manopt ;
2022-08-25 12:17:18 +00:00
const MANPAGES : [ & [ u8 ] ; 3 ] = [
2020-10-17 17:50:29 +00:00
include_bytes! ( concat! ( env! ( " OUT_DIR " ) , " /meli.txt.gz " ) ) ,
include_bytes! ( concat! ( env! ( " OUT_DIR " ) , " /meli.conf.txt.gz " ) ) ,
include_bytes! ( concat! ( env! ( " OUT_DIR " ) , " /meli-themes.txt.gz " ) ) ,
2020-02-09 00:26:21 +00:00
] ;
2020-10-17 17:50:29 +00:00
use flate2 ::bufread ::GzDecoder ;
use std ::io ::prelude ::* ;
let mut gz = GzDecoder ::new ( MANPAGES [ page as usize ] ) ;
let mut v = String ::with_capacity (
str ::parse ::< usize > ( unsafe {
std ::str ::from_utf8_unchecked ( gz . header ( ) . unwrap ( ) . comment ( ) . unwrap ( ) )
} )
2022-08-25 12:17:18 +00:00
. unwrap_or_else ( | _ | {
panic! ( " {:?} was not compressed with size comment header " , page )
} ) ,
2020-10-17 17:50:29 +00:00
) ;
gz . read_to_string ( & mut v ) ? ;
if let Some ( no_raw ) = no_raw {
match no_raw {
Some ( true ) = > { }
None if ( unsafe { libc ::isatty ( libc ::STDOUT_FILENO ) = = 1 } ) = > { }
Some ( false ) | None = > {
println! ( " {} " , & v ) ;
return Ok ( ( ) ) ;
}
}
2022-08-25 12:17:18 +00:00
} else if unsafe { libc ::isatty ( libc ::STDOUT_FILENO ) ! = 1 } {
println! ( " {} " , & v ) ;
return Ok ( ( ) ) ;
2020-10-17 17:50:29 +00:00
}
use std ::process ::{ Command , Stdio } ;
2022-08-25 12:17:18 +00:00
let mut handle =
Command ::new ( std ::env ::var ( " PAGER " ) . unwrap_or_else ( | _ | " more " . to_string ( ) ) )
. stdin ( Stdio ::piped ( ) )
. stdout ( Stdio ::inherit ( ) )
. stderr ( Stdio ::inherit ( ) )
. spawn ( ) ? ;
2020-10-17 17:50:29 +00:00
handle . stdin . take ( ) . unwrap ( ) . write_all ( v . as_bytes ( ) ) ? ;
handle . wait ( ) ? ;
2020-02-09 00:26:21 +00:00
return Ok ( ( ) ) ;
}
#[ cfg(not(feature = " cli-docs " )) ]
2020-06-07 15:02:20 +00: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 00:26:21 +00:00
}
2021-09-08 19:09:32 +00:00
Some ( SubCommand ::CompiledWith ) = > {
#[ cfg(feature = " notmuch " ) ]
println! ( " notmuch " ) ;
#[ cfg(feature = " jmap " ) ]
println! ( " jmap " ) ;
#[ cfg(feature = " sqlite3 " ) ]
println! ( " sqlite3 " ) ;
#[ cfg(feature = " smtp " ) ]
println! ( " smtp " ) ;
#[ cfg(feature = " regexp " ) ]
println! ( " regexp " ) ;
#[ cfg(feature = " dbus-notifications " ) ]
println! ( " dbus-notifications " ) ;
#[ cfg(feature = " cli-docs " ) ]
println! ( " cli-docs " ) ;
#[ cfg(feature = " gpgme " ) ]
println! ( " gpgme " ) ;
return Ok ( ( ) ) ;
}
2020-06-07 15:02:20 +00:00
Some ( SubCommand ::PrintLoadedThemes ) = > {
let s = conf ::FileSettings ::new ( ) ? ;
2022-08-25 12:17:18 +00:00
print! ( " {} " , s . terminal . themes ) ;
2020-06-07 15:02:20 +00:00
return Ok ( ( ) ) ;
}
Some ( SubCommand ::PrintDefaultTheme ) = > {
print! ( " {} " , conf ::Themes ::default ( ) . key_to_string ( " dark " , false ) ) ;
return Ok ( ( ) ) ;
}
2020-06-10 15:06:28 +00: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 15:02:20 +00:00
None = > { }
2019-07-11 08:45:09 +00:00
}
2018-07-11 14:07:51 +00:00
2020-01-08 15:07:14 +00: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 12:26:06 +00:00
/* Catch SIGWINCH to handle terminal resizing */
2019-09-09 09:53:39 +00:00
let signals = & [
/* Catch SIGWINCH to handle terminal resizing */
2022-05-02 12:44:39 +00:00
signal_hook ::consts ::SIGWINCH ,
2019-11-05 06:32:27 +00:00
/* Catch SIGCHLD to handle embed applications status change */
2022-05-02 12:44:39 +00:00
signal_hook ::consts ::SIGCHLD ,
2019-09-09 09:53:39 +00:00
] ;
2020-01-08 15:07:14 +00:00
let signal_recvr = notify ( signals , sender . clone ( ) ) ? ;
/* Create the application State. */
2020-06-10 15:06:28 +00: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 19:51:55 +00:00
let wrapper = Mail ::new ( bytes , Some ( Flag ::SEEN ) )
2020-06-10 15:06:28 +00: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-11 22:37:57 +00:00
#[ cfg(feature = " svgscreenshot " ) ]
state . register_component ( Box ::new ( components ::svg ::SVGScreenshotFilter ::new ( ) ) ) ;
2020-06-10 15:06:28 +00:00
let window = Box ::new ( Tabbed ::new (
vec! [
Box ::new ( listing ::Listing ::new ( & mut state . context ) ) ,
Box ::new ( ContactList ::new ( & state . context ) ) ,
] ,
& state . context ,
) ) ;
2020-10-14 09:50:38 +00:00
let status_bar = Box ::new ( StatusBar ::new ( & state . context , window ) ) ;
2020-06-10 15:06:28 +00:00
state . register_component ( status_bar ) ;
2020-10-28 21:28:41 +00:00
#[ cfg(all(target_os = " linux " , feature = " dbus-notifications " )) ]
2020-09-10 17:31:12 +00:00
{
2020-10-29 11:09:31 +00:00
let dbus_notifications = Box ::new ( components ::notifications ::DbusNotifications ::new (
& state . context ,
) ) ;
2020-09-10 17:31:12 +00:00
state . register_component ( dbus_notifications ) ;
}
state . register_component ( Box ::new (
components ::notifications ::NotificationCommand ::new ( ) ,
) ) ;
2020-06-10 15:06:28 +00:00
}
2020-07-25 10:24:42 +00:00
let enter_command_mode : Key = state
. context
. settings
. shortcuts
. general
. enter_command_mode
. clone ( ) ;
2020-11-15 19:02:06 +00:00
let quit_key : Key = state . context . settings . shortcuts . general . quit . clone ( ) ;
2018-07-17 14:16:16 +00:00
2020-02-04 13:52:12 +00:00
/* Keep track of the input mode. See UIMode for details */
2017-09-16 12:05:28 +00:00
' main : loop {
2018-07-14 18:41:38 +00:00
state . render ( ) ;
2017-07-23 11:01:17 +00:00
2017-09-16 12:05:28 +00:00
' inner : loop {
2019-04-10 19:01:02 +00:00
/* Check if any components have sent reply events to State. */
2020-02-04 13:52:12 +00:00
let events : smallvec ::SmallVec < [ UIEvent ; 8 ] > = state . context . replies ( ) ;
2018-07-16 08:08:04 +00:00
for e in events {
state . rcv_event ( e ) ;
}
2018-10-14 16:49:16 +00:00
state . redraw ( ) ;
2018-07-21 08:20:13 +00:00
/* Poll on all channels. Currently we have the input channel for stdin, watching events and the signal watcher. */
2019-09-09 09:53:39 +00:00
crossbeam ::select! {
recv ( receiver ) -> r = > {
2019-09-22 08:00:05 +00:00
match r {
2020-01-27 15:15:29 +00:00
Ok ( ThreadEvent ::Pulse ) | Ok ( ThreadEvent ::UIEvent ( UIEvent ::Timer ( _ ) ) ) = > { } ,
2019-09-22 08:00:05 +00:00
_ = > { debug! ( & r ) ; }
}
match r . unwrap ( ) {
2020-05-29 12:35:29 +00:00
ThreadEvent ::Input ( ( Key ::Ctrl ( 'z' ) , _ ) ) if state . mode ! = UIMode ::Embed = > {
2018-08-07 12:01:15 +00:00
state . switch_to_main_screen ( ) ;
2018-07-24 10:28:15 +00: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 12:01:15 +00:00
state . switch_to_alternate_screen ( ) ;
2018-07-24 10:28:15 +00:00
// BUG: thread sends input event after one received key
state . update_size ( ) ;
state . render ( ) ;
state . redraw ( ) ;
} ,
2020-05-29 12:35:29 +00:00
ThreadEvent ::Input ( raw_input @ ( Key ::Ctrl ( 'l' ) , _ ) ) = > {
2019-11-19 18:39:43 +00:00
/* Manual screen redraw */
state . update_size ( ) ;
state . render ( ) ;
state . redraw ( ) ;
2020-05-29 12:35:29 +00:00
if state . mode = = UIMode ::Embed {
2020-06-11 22:42:06 +00:00
state . rcv_event ( UIEvent ::EmbedInput ( raw_input ) ) ;
state . redraw ( ) ;
2020-05-29 12:35:29 +00:00
}
2019-11-19 18:39:43 +00:00
} ,
2020-05-29 12:35:29 +00:00
ThreadEvent ::Input ( ( k , r ) ) = > {
2018-07-21 08:20:13 +00:00
match state . mode {
2018-07-16 10:36:28 +00:00
UIMode ::Normal = > {
match k {
2020-11-15 19:02:06 +00:00
_ if k = = quit_key = > {
2019-09-27 10:14:16 +00:00
if state . can_quit_cleanly ( ) {
drop ( state ) ;
break 'main ;
} else {
state . redraw ( ) ;
}
2018-07-16 10:36:28 +00:00
} ,
2020-07-25 10:24:42 +00:00
_ if k = = enter_command_mode = > {
2020-07-25 10:08:36 +00:00
state . mode = UIMode ::Command ;
state . rcv_event ( UIEvent ::ChangeMode ( UIMode ::Command ) ) ;
2018-07-16 10:36:28 +00:00
state . redraw ( ) ;
}
key = > {
2019-04-10 20:37:20 +00:00
state . rcv_event ( UIEvent ::Input ( key ) ) ;
2018-07-16 10:36:28 +00:00
state . redraw ( ) ;
} ,
}
2018-07-14 22:27:13 +00:00
} ,
2019-02-25 09:11:56 +00:00
UIMode ::Insert = > {
match k {
2020-10-24 11:32:02 +00:00
Key ::Esc = > {
2019-04-10 20:37:20 +00:00
state . rcv_event ( UIEvent ::ChangeMode ( UIMode ::Normal ) ) ;
2019-02-25 09:11:56 +00:00
state . redraw ( ) ;
} ,
k = > {
2019-04-10 20:37:20 +00:00
state . rcv_event ( UIEvent ::InsertInput ( k ) ) ;
2019-02-25 09:11:56 +00:00
state . redraw ( ) ;
} ,
}
}
2020-07-25 10:08:36 +00:00
UIMode ::Command = > {
2018-07-16 10:36:28 +00:00
match k {
2020-07-25 10:00:23 +00:00
Key ::Char ( '\n' ) = > {
2018-07-21 08:20:13 +00:00
state . mode = UIMode ::Normal ;
2019-04-10 20:37:20 +00:00
state . rcv_event ( UIEvent ::ChangeMode ( UIMode ::Normal ) ) ;
2018-07-16 10:36:28 +00:00
state . redraw ( ) ;
} ,
2018-08-07 13:14:06 +00:00
k = > {
2020-07-25 10:08:36 +00:00
state . rcv_event ( UIEvent ::CmdInput ( k ) ) ;
2018-07-16 10:36:28 +00:00
state . redraw ( ) ;
} ,
}
2018-07-16 08:08:04 +00:00
} ,
2020-05-29 12:35:29 +00:00
UIMode ::Embed = > {
state . rcv_event ( UIEvent ::EmbedInput ( ( k , r ) ) ) ;
state . redraw ( ) ;
} ,
2018-07-21 08:20:13 +00:00
UIMode ::Fork = > {
break 'inner ; // `goto` 'reap loop, and wait on child.
} ,
2018-07-14 22:27:13 +00:00
}
2018-07-13 15:38:57 +00:00
} ,
2018-09-05 13:08:11 +00:00
ThreadEvent ::RefreshMailbox ( event ) = > {
2018-09-12 12:10:19 +00:00
state . refresh_event ( * event ) ;
2018-07-17 14:16:16 +00:00
state . redraw ( ) ;
2018-07-16 10:36:28 +00:00
} ,
2019-04-10 20:37:20 +00:00
ThreadEvent ::UIEvent ( UIEvent ::ChangeMode ( f ) ) = > {
2018-07-21 08:20:13 +00:00
state . mode = f ;
2019-02-25 09:11:56 +00:00
if f = = UIMode ::Fork {
break 'inner ; // `goto` 'reap loop, and wait on child.
}
2018-07-21 08:20:13 +00:00
}
2018-08-06 10:33:10 +00:00
ThreadEvent ::UIEvent ( e ) = > {
2019-04-10 20:37:20 +00:00
state . rcv_event ( e ) ;
2019-12-14 17:56:43 +00:00
state . redraw ( ) ;
2018-07-13 15:38:57 +00:00
} ,
2019-09-11 14:57:55 +00:00
ThreadEvent ::Pulse = > {
2019-11-23 16:00:00 +00:00
state . check_accounts ( ) ;
2019-09-11 14:57:55 +00:00
state . redraw ( ) ;
} ,
2020-06-26 15:31:37 +00:00
ThreadEvent ::JobFinished ( id ) = > {
debug! ( " Job finished {} " , id ) ;
2020-08-17 12:31:30 +00:00
for account in state . context . accounts . values_mut ( ) {
2020-06-27 18:40:46 +00:00
if account . process_event ( & id ) {
break ;
}
}
2020-06-26 15:31:37 +00:00
//state.new_thread(id, name);
} ,
2017-09-28 15:06:35 +00:00
}
2017-09-01 12:24:32 +00:00
} ,
2019-09-09 09:53:39 +00:00
recv ( signal_recvr ) -> sig = > {
match sig . unwrap ( ) {
2022-05-02 12:44:39 +00:00
signal_hook ::consts ::SIGWINCH = > {
2019-09-09 09:53:39 +00:00
if state . mode ! = UIMode ::Fork {
state . update_size ( ) ;
state . render ( ) ;
state . redraw ( ) ;
}
} ,
2022-05-02 12:44:39 +00:00
signal_hook ::consts ::SIGCHLD = > {
2020-02-27 14:46:47 +00:00
state . rcv_event ( UIEvent ::EmbedInput ( ( Key ::Null , vec! [ 0 ] ) ) ) ;
state . redraw ( ) ;
}
2020-01-01 22:11:13 +00:00
other = > {
debug! ( " got other signal: {:?} " , other ) ;
}
2018-07-16 10:36:28 +00:00
}
2018-07-13 15:38:57 +00:00
} ,
2017-07-23 11:01:17 +00:00
}
2018-07-21 08:20:13 +00:00
} // end of 'inner
' reap : loop {
match state . try_wait_on_child ( ) {
Some ( true ) = > {
2018-08-16 13:32:47 +00:00
state . restore_input ( ) ;
2018-09-03 22:49:29 +00:00
state . switch_to_alternate_screen ( ) ;
2018-07-27 18:37:56 +00:00
}
2018-07-21 08:20:13 +00:00
Some ( false ) = > {
use std ::{ thread , time } ;
2018-07-21 14:29:29 +00:00
let ten_millis = time ::Duration ::from_millis ( 1500 ) ;
2018-07-21 08:20:13 +00:00
thread ::sleep ( ten_millis ) ;
2018-07-24 10:28:15 +00:00
2018-07-21 08:20:13 +00:00
continue 'reap ;
2018-07-27 18:37:56 +00:00
}
None = > {
2018-09-03 22:49:29 +00:00
state . mode = UIMode ::Normal ;
state . render ( ) ;
2018-07-27 18:37:56 +00:00
break 'reap ;
}
2018-07-21 08:20:13 +00:00
}
2017-07-23 11:01:17 +00:00
}
}
2019-09-09 09:53:39 +00:00
Ok ( ( ) )
2017-07-23 11:01:17 +00:00
}