2022-10-01 16:58:22 +00:00
// Gupax - GUI Uniting P2Pool And XMRig
//
// Copyright (c) 2022 hinto-janaiyo
//
// This program 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.
//
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
2022-10-31 20:08:25 +00:00
// Hide console in Windows
2022-11-22 02:01:50 +00:00
#![ cfg_attr(not(debug_assertions), windows_subsystem = " windows " ) ]
2022-10-15 19:15:27 +00:00
//---------------------------------------------------------------------------------------------------- Imports
// egui/eframe
2022-11-23 04:21:46 +00:00
use egui ::{
TextStyle ::* ,
color ::Color32 ,
FontFamily ::Proportional ,
2022-12-07 23:02:08 +00:00
TextStyle , Spinner ,
2022-11-23 04:21:46 +00:00
Layout , Align ,
FontId , Label , RichText , Stroke , Vec2 , Button , SelectableLabel ,
2022-12-07 23:02:08 +00:00
Key , Modifiers , TextEdit ,
2022-11-23 04:21:46 +00:00
CentralPanel , TopBottomPanel ,
2022-12-07 23:02:08 +00:00
Hyperlink ,
2022-11-23 04:21:46 +00:00
} ;
2022-10-01 16:58:22 +00:00
use egui_extras ::RetainedImage ;
2022-10-15 19:15:27 +00:00
use eframe ::{ egui , NativeOptions } ;
// Logging
2022-10-01 16:58:22 +00:00
use log ::* ;
2022-10-15 19:15:27 +00:00
use env_logger ::{ Builder , WriteStyle } ;
2022-11-11 02:20:31 +00:00
// Regex
use regex ::Regex ;
2022-12-06 03:33:35 +00:00
// Serde
use serde ::{ Serialize , Deserialize } ;
2022-10-15 19:15:27 +00:00
// std
2022-11-20 18:31:00 +00:00
use std ::{
cargo/tor/p2pool: clean deps, warn macos arti, fix node overflow
Cargo: Cleanup unused dependencies, enable some build optimizations
Tor: Arti doesn't seem to work on macOS
Even a bare Arti+Hyper request doesn't seem to work, so it's
probably not something to do with Gupax. A lot of issues only
seem to popup in a VM (OpenGL, TLS) even though on bare metal
Gupax runs fine, so Tor might work fine on real macOS but I don't
have real macOS to test it. VM macOS can't create a circuit, so,
disable by default and add a warning that it's unstable.
P2Pool: Let selected_index start at 0, and only +1 when printing
to the user, this makes the overflow math when adding/deleting a
lot more simple because selected_index will match the actual index
of the node vector
2022-11-21 22:16:31 +00:00
env ,
2022-11-20 18:31:00 +00:00
io ::Write ,
process ::exit ,
sync ::{ Arc , Mutex } ,
time ::Instant ,
path ::PathBuf ,
} ;
2022-12-14 03:41:05 +00:00
// Sysinfo
use sysinfo ::SystemExt ;
2022-10-15 19:15:27 +00:00
// Modules
2022-10-18 19:26:21 +00:00
mod ferris ;
2022-10-01 16:58:22 +00:00
mod constants ;
2022-10-14 21:13:38 +00:00
mod node ;
2022-11-14 02:56:25 +00:00
mod disk ;
2022-10-01 16:58:22 +00:00
mod status ;
mod gupax ;
mod p2pool ;
mod xmrig ;
2022-10-25 02:58:42 +00:00
mod update ;
2022-11-30 03:38:01 +00:00
mod helper ;
2022-12-26 18:56:37 +00:00
mod human ;
2022-12-27 17:58:46 +00:00
use { ferris ::* , constants ::* , node ::* , disk ::* , update ::* , gupax ::* , helper ::* } ;
2022-10-01 16:58:22 +00:00
2022-12-10 02:00:33 +00:00
// Sudo (dummy values for Windows)
2022-12-07 23:02:08 +00:00
mod sudo ;
2022-12-10 02:00:33 +00:00
use crate ::sudo ::* ;
2022-12-07 23:02:08 +00:00
#[ cfg(target_family = " unix " ) ]
2022-12-10 02:00:33 +00:00
extern crate sudo as sudo_check ;
2022-12-07 23:02:08 +00:00
2022-10-15 19:15:27 +00:00
//---------------------------------------------------------------------------------------------------- Struct + Impl
// The state of the outer main [App].
// See the [State] struct in [state.rs] for the
// actual inner state of the tab settings.
2022-10-01 16:58:22 +00:00
pub struct App {
2022-10-15 19:15:27 +00:00
// Misc state
tab : Tab , // What tab are we on?
2022-10-17 02:28:41 +00:00
width : f32 , // Top-level width
height : f32 , // Top-level height
2022-12-02 18:15:26 +00:00
// Alpha (transparency)
// This value is used to incrementally increase/decrease
// the transparency when resizing. Basically, it fades
// in/out of black to hide jitter when resizing with [init_text_styles()]
alpha : u8 ,
2022-11-25 16:59:48 +00:00
// This is a one time trigger so [init_text_styles()] isn't
// called 60x a second when resizing the window. Instead,
// it only gets called if this bool is true and the user
// is hovering over egui (ctx.is_pointer_over_area()).
2022-12-02 18:15:26 +00:00
must_resize : bool , // Sets the flag so we know to [init_text_styles()]
resizing : bool , // Are we in the process of resizing? (For black fade in/out)
2022-10-25 02:58:42 +00:00
// State
update: save [Version] to state, use runtime [og: State]
[og: State] is now completely wrapped in an [Arc<Mutex>] so that
when the update is done, it can [.lock()] the CURRENT runtime
settings of the user and save to [gupax.toml] instead of using an
old copy that was given to it at the beginning of the thread.
In practice, this means users can change settings around during
an update and the update finishing and saving to disk won't be
using their old settings, but the current ones. Wrapping all of
[og: State] within in [Arc<Mutex>] might be overkill compared to
message channels but [State] really is just a few [bool]'s, [u*],
and small [String]'s, so it's not much data.
To bypass a deadlock when comparing [og == state] every frame,
[og]'s struct fields get cloned every frame into separate
variables, then it gets compared. This is also pretty stupid, but
again, the data being cloned is so tiny that it doesn't seem to
slow anything down.
2022-11-02 17:58:44 +00:00
og : Arc < Mutex < State > > , // og = Old state to compare against
2022-10-25 02:58:42 +00:00
state : State , // state = Working state (current settings)
update: save [Version] to state, use runtime [og: State]
[og: State] is now completely wrapped in an [Arc<Mutex>] so that
when the update is done, it can [.lock()] the CURRENT runtime
settings of the user and save to [gupax.toml] instead of using an
old copy that was given to it at the beginning of the thread.
In practice, this means users can change settings around during
an update and the update finishing and saving to disk won't be
using their old settings, but the current ones. Wrapping all of
[og: State] within in [Arc<Mutex>] might be overkill compared to
message channels but [State] really is just a few [bool]'s, [u*],
and small [String]'s, so it's not much data.
To bypass a deadlock when comparing [og == state] every frame,
[og]'s struct fields get cloned every frame into separate
variables, then it gets compared. This is also pretty stupid, but
again, the data being cloned is so tiny that it doesn't seem to
slow anything down.
2022-11-02 17:58:44 +00:00
update : Arc < Mutex < Update > > , // State for update data [update.rs]
2022-11-17 18:03:45 +00:00
file_window : Arc < Mutex < FileWindow > > , // State for the path selector in [Gupax]
2022-11-16 21:07:49 +00:00
ping : Arc < Mutex < Ping > > , // Ping data found in [node.rs]
2022-11-15 21:29:05 +00:00
og_node_vec : Vec < ( String , Node ) > , // Manual Node database
2022-11-14 02:56:25 +00:00
node_vec : Vec < ( String , Node ) > , // Manual Node database
2022-11-23 04:21:46 +00:00
og_pool_vec : Vec < ( String , Pool ) > , // Manual Pool database
pool_vec : Vec < ( String , Pool ) > , // Manual Pool database
update: save [Version] to state, use runtime [og: State]
[og: State] is now completely wrapped in an [Arc<Mutex>] so that
when the update is done, it can [.lock()] the CURRENT runtime
settings of the user and save to [gupax.toml] instead of using an
old copy that was given to it at the beginning of the thread.
In practice, this means users can change settings around during
an update and the update finishing and saving to disk won't be
using their old settings, but the current ones. Wrapping all of
[og: State] within in [Arc<Mutex>] might be overkill compared to
message channels but [State] really is just a few [bool]'s, [u*],
and small [String]'s, so it's not much data.
To bypass a deadlock when comparing [og == state] every frame,
[og]'s struct fields get cloned every frame into separate
variables, then it gets compared. This is also pretty stupid, but
again, the data being cloned is so tiny that it doesn't seem to
slow anything down.
2022-11-02 17:58:44 +00:00
diff : bool , // This bool indicates state changes
2022-11-27 20:11:00 +00:00
// Restart state:
// If Gupax updated itself, this represents that the
// user should (but isn't required to) restart Gupax.
restart : Arc < Mutex < Restart > > ,
2022-11-30 03:38:01 +00:00
// Error State:
2022-11-15 21:29:05 +00:00
// These values are essentially global variables that
// indicate if an error message needs to be displayed
// (it takes up the whole screen with [error_msg] and buttons for ok/quit/etc)
error_state : ErrorState ,
2022-12-05 19:55:50 +00:00
// Helper/API State:
// This holds everything related to the data processed by the "helper thread".
// This includes the "helper" threads public P2Pool/XMRig's API.
2022-12-06 03:33:35 +00:00
helper : Arc < Mutex < Helper > > , // [Helper] state, mostly for Gupax uptime
2022-12-11 20:49:01 +00:00
pub_sys : Arc < Mutex < Sys > > , // [Sys] state, read by [Status], mutated by [Helper]
2022-12-06 03:33:35 +00:00
p2pool : Arc < Mutex < Process > > , // [P2Pool] process state
xmrig : Arc < Mutex < Process > > , // [XMRig] process state
p2pool_api : Arc < Mutex < PubP2poolApi > > , // Public ready-to-print P2Pool API made by the "helper" thread
xmrig_api : Arc < Mutex < PubXmrigApi > > , // Public ready-to-print XMRig API made by the "helper" thread
2022-12-06 22:48:48 +00:00
p2pool_img : Arc < Mutex < ImgP2pool > > , // A one-time snapshot of what data P2Pool started with
xmrig_img : Arc < Mutex < ImgXmrig > > , // A one-time snapshot of what data XMRig started with
2022-12-23 14:44:20 +00:00
// STDIN Buffer
p2pool_stdin : String , // The buffer between the p2pool console and the [Helper]
xmrig_stdin : String , // The buffer between the xmrig console and the [Helper]
2022-12-07 23:02:08 +00:00
// Sudo State
2022-12-10 02:00:33 +00:00
sudo : Arc < Mutex < SudoState > > , // This is just a dummy struct on [Windows].
2022-10-16 21:29:24 +00:00
// State from [--flags]
2022-11-15 03:11:00 +00:00
no_startup : bool ,
2022-12-26 22:37:45 +00:00
// Gupax-P2Pool API
// Gupax's P2Pool API (e.g: ~/.local/share/gupax/p2pool/)
// This is a file-based API that contains data for permanent stats.
// The below struct holds everything needed for it, the paths, the
// actual stats, and all the functions needed to mutate them.
gupax_p2pool_api : GupaxP2poolApi ,
2022-10-15 19:15:27 +00:00
// Static stuff
2022-12-11 20:49:01 +00:00
pid : sysinfo ::Pid , // Gupax's PID
max_threads : usize , // Max amount of detected system threads
2022-10-16 21:29:24 +00:00
now : Instant , // Internal timer
2022-10-19 18:35:32 +00:00
exe : String , // Path for [Gupax] binary
2022-11-02 22:18:41 +00:00
dir : String , // Directory [Gupax] binary is in
2022-10-16 21:29:24 +00:00
resolution : Vec2 , // Frame resolution
os : & 'static str , // OS
2022-12-10 03:06:42 +00:00
admin : bool , // Are we admin? (for Windows)
2022-12-26 22:37:45 +00:00
os_data_path : PathBuf , // OS data path (e.g: ~/.local/share/gupax/)
gupax_p2pool_api_path : PathBuf , // Gupax-P2Pool API path (e.g: ~/.local/share/gupax/p2pool/)
2022-11-20 18:31:00 +00:00
state_path : PathBuf , // State file path
node_path : PathBuf , // Node file path
2022-11-23 04:21:46 +00:00
pool_path : PathBuf , // Pool file path
2022-11-11 02:20:31 +00:00
version : & 'static str , // Gupax version
2022-10-16 21:29:24 +00:00
name_version : String , // [Gupax vX.X.X]
2022-11-16 19:07:27 +00:00
img : Images , // Custom Struct holding pre-compiled bytes of [Images]
2022-11-14 02:56:25 +00:00
regex : Regexes , // Custom Struct holding pre-made [Regex]'s
2022-10-01 16:58:22 +00:00
}
impl App {
2022-10-16 21:29:24 +00:00
fn cc ( cc : & eframe ::CreationContext < '_ > , app : Self ) -> Self {
let resolution = cc . integration_info . window_info . size ;
2022-12-16 19:33:04 +00:00
init_text_styles ( & cc . egui_ctx , resolution [ 0 ] ) ;
2022-10-01 16:58:22 +00:00
Self {
2022-10-16 21:29:24 +00:00
resolution ,
.. app
}
}
2022-12-17 15:09:50 +00:00
fn save_before_quit ( & mut self ) {
if let Err ( e ) = State ::save ( & mut self . state , & self . state_path ) { error! ( " State file: {} " , e ) ; }
if let Err ( e ) = Node ::save ( & self . node_vec , & self . node_path ) { error! ( " Node list: {} " , e ) ; }
if let Err ( e ) = Pool ::save ( & self . pool_vec , & self . pool_path ) { error! ( " Pool list: {} " , e ) ; }
}
2022-11-30 22:21:55 +00:00
fn new ( now : Instant ) -> Self {
2022-11-17 18:03:45 +00:00
info! ( " Initializing App Struct... " ) ;
2022-12-12 19:34:17 +00:00
debug! ( " App Init | P2Pool & XMRig processes... " ) ;
2022-12-06 03:33:35 +00:00
let p2pool = Arc ::new ( Mutex ::new ( Process ::new ( ProcessName ::P2pool , String ::new ( ) , PathBuf ::new ( ) ) ) ) ;
let xmrig = Arc ::new ( Mutex ::new ( Process ::new ( ProcessName ::Xmrig , String ::new ( ) , PathBuf ::new ( ) ) ) ) ;
2022-12-05 19:55:50 +00:00
let p2pool_api = Arc ::new ( Mutex ::new ( PubP2poolApi ::new ( ) ) ) ;
let xmrig_api = Arc ::new ( Mutex ::new ( PubXmrigApi ::new ( ) ) ) ;
2022-12-06 22:48:48 +00:00
let p2pool_img = Arc ::new ( Mutex ::new ( ImgP2pool ::new ( ) ) ) ;
let xmrig_img = Arc ::new ( Mutex ::new ( ImgXmrig ::new ( ) ) ) ;
2022-12-11 20:49:01 +00:00
2022-12-12 19:34:17 +00:00
debug! ( " App Init | Sysinfo... " ) ;
2022-12-11 20:49:01 +00:00
// We give this to the [Helper] thread.
let mut sysinfo = sysinfo ::System ::new_with_specifics (
sysinfo ::RefreshKind ::new ( )
. with_cpu ( sysinfo ::CpuRefreshKind ::everything ( ) )
. with_processes ( sysinfo ::ProcessRefreshKind ::new ( ) . with_cpu ( ) )
. with_memory ( ) ) ;
sysinfo . refresh_all ( ) ;
2022-12-17 00:16:16 +00:00
let pid = match sysinfo ::get_current_pid ( ) {
Ok ( pid ) = > pid ,
Err ( e ) = > { error! ( " App Init | Failed to get sysinfo PID: {} " , e ) ; exit ( 1 ) }
} ;
2022-12-11 20:49:01 +00:00
let pub_sys = Arc ::new ( Mutex ::new ( Sys ::new ( ) ) ) ;
2022-12-12 19:34:17 +00:00
debug! ( " App Init | The rest of the [App]... " ) ;
2022-11-20 18:31:00 +00:00
let mut app = Self {
2022-10-15 19:15:27 +00:00
tab : Tab ::default ( ) ,
2022-11-11 02:20:31 +00:00
ping : Arc ::new ( Mutex ::new ( Ping ::new ( ) ) ) ,
2022-11-23 04:21:46 +00:00
width : APP_DEFAULT_WIDTH ,
height : APP_DEFAULT_HEIGHT ,
2022-11-25 16:59:48 +00:00
must_resize : false ,
2022-11-14 02:56:25 +00:00
og : Arc ::new ( Mutex ::new ( State ::new ( ) ) ) ,
state : State ::new ( ) ,
2022-11-02 22:18:41 +00:00
update : Arc ::new ( Mutex ::new ( Update ::new ( String ::new ( ) , PathBuf ::new ( ) , PathBuf ::new ( ) , true ) ) ) ,
2022-11-17 18:03:45 +00:00
file_window : FileWindow ::new ( ) ,
2022-11-15 21:29:05 +00:00
og_node_vec : Node ::new_vec ( ) ,
2022-11-14 02:56:25 +00:00
node_vec : Node ::new_vec ( ) ,
2022-11-23 04:21:46 +00:00
og_pool_vec : Pool ::new_vec ( ) ,
pool_vec : Pool ::new_vec ( ) ,
2022-11-27 20:11:00 +00:00
restart : Arc ::new ( Mutex ::new ( Restart ::No ) ) ,
2022-10-15 19:15:27 +00:00
diff : false ,
2022-11-15 21:29:05 +00:00
error_state : ErrorState ::new ( ) ,
2022-12-11 20:49:01 +00:00
helper : Arc ::new ( Mutex ::new ( Helper ::new ( now , pub_sys . clone ( ) , p2pool . clone ( ) , xmrig . clone ( ) , p2pool_api . clone ( ) , xmrig_api . clone ( ) , p2pool_img . clone ( ) , xmrig_img . clone ( ) ) ) ) ,
2022-12-06 03:33:35 +00:00
p2pool ,
xmrig ,
2022-12-05 19:55:50 +00:00
p2pool_api ,
xmrig_api ,
2022-12-06 22:48:48 +00:00
p2pool_img ,
xmrig_img ,
2022-12-23 14:44:20 +00:00
p2pool_stdin : String ::with_capacity ( 10 ) ,
xmrig_stdin : String ::with_capacity ( 10 ) ,
2022-12-07 23:02:08 +00:00
sudo : Arc ::new ( Mutex ::new ( SudoState ::new ( ) ) ) ,
2022-12-02 18:15:26 +00:00
resizing : false ,
alpha : 0 ,
2022-11-15 03:11:00 +00:00
no_startup : false ,
2022-12-26 22:37:45 +00:00
gupax_p2pool_api : GupaxP2poolApi ::new ( ) ,
2022-12-11 20:49:01 +00:00
pub_sys ,
pid ,
max_threads : num_cpus ::get ( ) ,
2022-11-30 22:21:55 +00:00
now ,
2022-12-10 03:06:42 +00:00
admin : false ,
2022-11-20 18:31:00 +00:00
exe : String ::new ( ) ,
dir : String ::new ( ) ,
2022-11-23 04:21:46 +00:00
resolution : Vec2 ::new ( APP_DEFAULT_HEIGHT , APP_DEFAULT_WIDTH ) ,
2022-10-15 19:15:27 +00:00
os : OS ,
2022-11-16 19:07:27 +00:00
os_data_path : PathBuf ::new ( ) ,
2022-12-26 22:37:45 +00:00
gupax_p2pool_api_path : PathBuf ::new ( ) ,
2022-11-20 18:31:00 +00:00
state_path : PathBuf ::new ( ) ,
node_path : PathBuf ::new ( ) ,
2022-11-23 04:21:46 +00:00
pool_path : PathBuf ::new ( ) ,
2022-11-11 02:20:31 +00:00
version : GUPAX_VERSION ,
2022-10-17 00:36:58 +00:00
name_version : format ! ( " Gupax {} " , GUPAX_VERSION ) ,
2022-11-16 19:07:27 +00:00
img : Images ::new ( ) ,
2022-11-14 02:56:25 +00:00
regex : Regexes ::new ( ) ,
2022-10-16 21:29:24 +00:00
} ;
2022-11-20 18:31:00 +00:00
//---------------------------------------------------------------------------------------------------- App init data that *could* panic
2022-12-12 19:34:17 +00:00
debug! ( " App Init | Getting EXE path... " ) ;
2022-11-20 18:31:00 +00:00
let mut panic = String ::new ( ) ;
2022-11-02 22:18:41 +00:00
// Get exe path
app . exe = match get_exe ( ) {
2022-10-19 18:35:32 +00:00
Ok ( exe ) = > exe ,
2022-11-20 18:31:00 +00:00
Err ( e ) = > { panic = format! ( " get_exe(): {} " , e ) ; app . error_state . set ( panic . clone ( ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ; String ::new ( ) } ,
2022-10-19 18:35:32 +00:00
} ;
2022-11-02 22:18:41 +00:00
// Get exe directory path
app . dir = match get_exe_dir ( ) {
Ok ( dir ) = > dir ,
2022-11-20 18:31:00 +00:00
Err ( e ) = > { panic = format! ( " get_exe_dir(): {} " , e ) ; app . error_state . set ( panic . clone ( ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ; String ::new ( ) } ,
2022-11-02 22:18:41 +00:00
} ;
2022-11-16 19:07:27 +00:00
// Get OS data path
2022-11-20 18:31:00 +00:00
app . os_data_path = match get_gupax_data_path ( ) {
2022-11-16 19:07:27 +00:00
Ok ( dir ) = > dir ,
2022-11-20 18:31:00 +00:00
Err ( e ) = > { panic = format! ( " get_os_data_path(): {} " , e ) ; app . error_state . set ( panic . clone ( ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ; PathBuf ::new ( ) } ,
2022-11-16 19:07:27 +00:00
} ;
2022-11-20 18:31:00 +00:00
2022-12-12 19:34:17 +00:00
debug! ( " App Init | Setting TOML path... " ) ;
2022-11-23 04:21:46 +00:00
// Set [*.toml] path
2022-11-20 18:31:00 +00:00
app . state_path = app . os_data_path . clone ( ) ;
2022-12-26 22:37:45 +00:00
app . state_path . push ( STATE_TOML ) ;
2022-11-20 18:31:00 +00:00
app . node_path = app . os_data_path . clone ( ) ;
2022-12-26 22:37:45 +00:00
app . node_path . push ( NODE_TOML ) ;
2022-11-23 04:21:46 +00:00
app . pool_path = app . os_data_path . clone ( ) ;
2022-12-26 22:37:45 +00:00
app . pool_path . push ( POOL_TOML ) ;
2022-11-20 18:31:00 +00:00
// Apply arg state
// It's not safe to [--reset] if any of the previous variables
// are unset (null path), so make sure we just abort if the [panic] String contains something.
2022-12-12 19:34:17 +00:00
debug! ( " App Init | Applying argument state... " ) ;
2022-11-20 18:31:00 +00:00
let mut app = parse_args ( app , panic ) ;
2022-11-20 02:20:28 +00:00
// Read disk state
2022-12-12 19:34:17 +00:00
debug! ( " App Init | Reading disk state... " ) ;
2022-11-20 02:20:28 +00:00
use TomlError ::* ;
2022-11-20 18:31:00 +00:00
app . state = match State ::get ( & app . state_path ) {
2022-11-20 02:20:28 +00:00
Ok ( toml ) = > toml ,
Err ( err ) = > {
error! ( " State ... {} " , err ) ;
match err {
2022-11-20 18:31:00 +00:00
Io ( e ) = > app . error_state . set ( format! ( " State file: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
Path ( e ) = > app . error_state . set ( format! ( " State file: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
Serialize ( e ) = > app . error_state . set ( format! ( " State file: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
Deserialize ( e ) = > app . error_state . set ( format! ( " State file: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
2022-11-20 19:46:43 +00:00
Format ( e ) = > app . error_state . set ( format! ( " State file: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
2022-11-20 18:31:00 +00:00
Merge ( e ) = > app . error_state . set ( format! ( " State file: {} " , e ) , ErrorFerris ::Error , ErrorButtons ::ResetState ) ,
2022-12-17 00:16:16 +00:00
_ = > ( ) ,
2022-11-20 02:20:28 +00:00
} ;
State ::new ( )
} ,
} ;
app . og = Arc ::new ( Mutex ::new ( app . state . clone ( ) ) ) ;
// Read node list
2022-12-12 19:34:17 +00:00
debug! ( " App Init | Reading node list... " ) ;
2022-12-13 17:44:57 +00:00
app . node_vec = match Node ::get ( & app . node_path ) {
2022-11-20 02:20:28 +00:00
Ok ( toml ) = > toml ,
Err ( err ) = > {
error! ( " Node ... {} " , err ) ;
match err {
2022-11-20 18:31:00 +00:00
Io ( e ) = > app . error_state . set ( format! ( " Node list: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
Path ( e ) = > app . error_state . set ( format! ( " Node list: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
Serialize ( e ) = > app . error_state . set ( format! ( " Node list: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
Deserialize ( e ) = > app . error_state . set ( format! ( " Node list: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
2022-12-17 00:16:16 +00:00
Format ( e ) = > app . error_state . set ( format! ( " Node list: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
2022-11-20 18:31:00 +00:00
Merge ( e ) = > app . error_state . set ( format! ( " Node list: {} " , e ) , ErrorFerris ::Error , ErrorButtons ::ResetState ) ,
2022-12-17 00:16:16 +00:00
Parse ( e ) = > app . error_state . set ( format! ( " Node list: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
2022-11-20 02:20:28 +00:00
} ;
Node ::new_vec ( )
} ,
} ;
2022-12-13 17:44:57 +00:00
app . og_node_vec = app . node_vec . clone ( ) ;
debug! ( " Node Vec: " ) ;
debug! ( " {:#?} " , app . node_vec ) ;
2022-11-23 04:21:46 +00:00
// Read pool list
2022-12-12 19:34:17 +00:00
debug! ( " App Init | Reading pool list... " ) ;
2022-12-13 17:44:57 +00:00
app . pool_vec = match Pool ::get ( & app . pool_path ) {
2022-11-23 04:21:46 +00:00
Ok ( toml ) = > toml ,
Err ( err ) = > {
error! ( " Pool ... {} " , err ) ;
match err {
Io ( e ) = > app . error_state . set ( format! ( " Pool list: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
Path ( e ) = > app . error_state . set ( format! ( " Pool list: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
Serialize ( e ) = > app . error_state . set ( format! ( " Pool list: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
Deserialize ( e ) = > app . error_state . set ( format! ( " Pool list: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
2022-12-17 00:16:16 +00:00
Format ( e ) = > app . error_state . set ( format! ( " Pool list: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
2022-11-23 04:21:46 +00:00
Merge ( e ) = > app . error_state . set ( format! ( " Pool list: {} " , e ) , ErrorFerris ::Error , ErrorButtons ::ResetState ) ,
2022-12-17 00:16:16 +00:00
Parse ( e ) = > app . error_state . set ( format! ( " Pool list: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
2022-11-23 04:21:46 +00:00
} ;
Pool ::new_vec ( )
} ,
} ;
2022-12-13 17:44:57 +00:00
app . og_pool_vec = app . pool_vec . clone ( ) ;
debug! ( " Pool Vec: " ) ;
debug! ( " {:#?} " , app . pool_vec ) ;
2022-12-26 22:37:45 +00:00
//----------------------------------------------------------------------------------------------------
// Read [GupaxP2poolApi] disk files
app . gupax_p2pool_api_path = crate ::disk ::get_gupax_p2pool_path ( & app . os_data_path ) ;
app . gupax_p2pool_api . fill_paths ( & app . gupax_p2pool_api_path ) ;
GupaxP2poolApi ::create_all_files ( & app . gupax_p2pool_api_path ) ;
debug! ( " App Init | Reading Gupax-P2Pool API files... " ) ;
match app . gupax_p2pool_api . read_all_files_and_update ( ) {
Ok ( _ ) = > {
info! (
" GupaxP2poolApi ... Payouts: {} | XMR (atomic-units): {} | Blocks: {} " ,
app . gupax_p2pool_api . payout ,
app . gupax_p2pool_api . xmr ,
app . gupax_p2pool_api . block ,
) ;
} ,
Err ( err ) = > {
error! ( " GupaxP2poolApi ... {} " , err ) ;
match err {
Io ( e ) = > app . error_state . set ( format! ( " GupaxP2poolApi: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
Path ( e ) = > app . error_state . set ( format! ( " GupaxP2poolApi: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
Serialize ( e ) = > app . error_state . set ( format! ( " GupaxP2poolApi: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
Deserialize ( e ) = > app . error_state . set ( format! ( " GupaxP2poolApi: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
Format ( e ) = > app . error_state . set ( format! ( " GupaxP2poolApi: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
Merge ( e ) = > app . error_state . set ( format! ( " GupaxP2poolApi: {} " , e ) , ErrorFerris ::Error , ErrorButtons ::ResetState ) ,
Parse ( e ) = > app . error_state . set ( format! ( " GupaxP2poolApi: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
} ;
} ,
} ;
2022-11-20 02:20:28 +00:00
2022-11-20 18:31:00 +00:00
//----------------------------------------------------------------------------------------------------
2022-11-20 02:20:28 +00:00
let mut og = app . og . lock ( ) . unwrap ( ) ; // Lock [og]
2022-10-27 03:15:56 +00:00
// Handle max threads
2022-12-12 19:34:17 +00:00
debug! ( " App Init | Handling max thread overflow... " ) ;
2022-12-11 20:49:01 +00:00
og . xmrig . max_threads = app . max_threads ;
2022-11-16 02:19:30 +00:00
let current = og . xmrig . current_threads ;
let max = og . xmrig . max_threads ;
update: save [Version] to state, use runtime [og: State]
[og: State] is now completely wrapped in an [Arc<Mutex>] so that
when the update is done, it can [.lock()] the CURRENT runtime
settings of the user and save to [gupax.toml] instead of using an
old copy that was given to it at the beginning of the thread.
In practice, this means users can change settings around during
an update and the update finishing and saving to disk won't be
using their old settings, but the current ones. Wrapping all of
[og: State] within in [Arc<Mutex>] might be overkill compared to
message channels but [State] really is just a few [bool]'s, [u*],
and small [String]'s, so it's not much data.
To bypass a deadlock when comparing [og == state] every frame,
[og]'s struct fields get cloned every frame into separate
variables, then it gets compared. This is also pretty stupid, but
again, the data being cloned is so tiny that it doesn't seem to
slow anything down.
2022-11-02 17:58:44 +00:00
if current > max {
2022-11-16 02:19:30 +00:00
og . xmrig . current_threads = max ;
update: save [Version] to state, use runtime [og: State]
[og: State] is now completely wrapped in an [Arc<Mutex>] so that
when the update is done, it can [.lock()] the CURRENT runtime
settings of the user and save to [gupax.toml] instead of using an
old copy that was given to it at the beginning of the thread.
In practice, this means users can change settings around during
an update and the update finishing and saving to disk won't be
using their old settings, but the current ones. Wrapping all of
[og: State] within in [Arc<Mutex>] might be overkill compared to
message channels but [State] really is just a few [bool]'s, [u*],
and small [String]'s, so it's not much data.
To bypass a deadlock when comparing [og == state] every frame,
[og]'s struct fields get cloned every frame into separate
variables, then it gets compared. This is also pretty stupid, but
again, the data being cloned is so tiny that it doesn't seem to
slow anything down.
2022-11-02 17:58:44 +00:00
}
2022-11-19 18:34:46 +00:00
// Handle [node_vec] overflow
2022-12-12 19:34:17 +00:00
debug! ( " App Init | Handling [node_vec] overflow " ) ;
cargo/tor/p2pool: clean deps, warn macos arti, fix node overflow
Cargo: Cleanup unused dependencies, enable some build optimizations
Tor: Arti doesn't seem to work on macOS
Even a bare Arti+Hyper request doesn't seem to work, so it's
probably not something to do with Gupax. A lot of issues only
seem to popup in a VM (OpenGL, TLS) even though on bare metal
Gupax runs fine, so Tor might work fine on real macOS but I don't
have real macOS to test it. VM macOS can't create a circuit, so,
disable by default and add a warning that it's unstable.
P2Pool: Let selected_index start at 0, and only +1 when printing
to the user, this makes the overflow math when adding/deleting a
lot more simple because selected_index will match the actual index
of the node vector
2022-11-21 22:16:31 +00:00
if og . p2pool . selected_index > app . og_node_vec . len ( ) {
2022-11-19 18:34:46 +00:00
warn! ( " App | Overflowing manual node index [{} > {}], resetting to 1 " , og . p2pool . selected_index , app . og_node_vec . len ( ) ) ;
let ( name , node ) = app . og_node_vec [ 0 ] . clone ( ) ;
cargo/tor/p2pool: clean deps, warn macos arti, fix node overflow
Cargo: Cleanup unused dependencies, enable some build optimizations
Tor: Arti doesn't seem to work on macOS
Even a bare Arti+Hyper request doesn't seem to work, so it's
probably not something to do with Gupax. A lot of issues only
seem to popup in a VM (OpenGL, TLS) even though on bare metal
Gupax runs fine, so Tor might work fine on real macOS but I don't
have real macOS to test it. VM macOS can't create a circuit, so,
disable by default and add a warning that it's unstable.
P2Pool: Let selected_index start at 0, and only +1 when printing
to the user, this makes the overflow math when adding/deleting a
lot more simple because selected_index will match the actual index
of the node vector
2022-11-21 22:16:31 +00:00
og . p2pool . selected_index = 0 ;
2022-11-19 18:34:46 +00:00
og . p2pool . selected_name = name . clone ( ) ;
og . p2pool . selected_ip = node . ip . clone ( ) ;
og . p2pool . selected_rpc = node . rpc . clone ( ) ;
cargo/tor/p2pool: clean deps, warn macos arti, fix node overflow
Cargo: Cleanup unused dependencies, enable some build optimizations
Tor: Arti doesn't seem to work on macOS
Even a bare Arti+Hyper request doesn't seem to work, so it's
probably not something to do with Gupax. A lot of issues only
seem to popup in a VM (OpenGL, TLS) even though on bare metal
Gupax runs fine, so Tor might work fine on real macOS but I don't
have real macOS to test it. VM macOS can't create a circuit, so,
disable by default and add a warning that it's unstable.
P2Pool: Let selected_index start at 0, and only +1 when printing
to the user, this makes the overflow math when adding/deleting a
lot more simple because selected_index will match the actual index
of the node vector
2022-11-21 22:16:31 +00:00
og . p2pool . selected_zmq = node . zmq . clone ( ) ;
app . state . p2pool . selected_index = 0 ;
2022-11-19 18:34:46 +00:00
app . state . p2pool . selected_name = name ;
app . state . p2pool . selected_ip = node . ip ;
app . state . p2pool . selected_rpc = node . rpc ;
app . state . p2pool . selected_zmq = node . zmq ;
}
2022-11-23 04:21:46 +00:00
// Handle [pool_vec] overflow
2022-12-12 19:34:17 +00:00
debug! ( " App Init | Handling [pool_vec] overflow... " ) ;
2022-11-23 04:21:46 +00:00
if og . xmrig . selected_index > app . og_pool_vec . len ( ) {
warn! ( " App | Overflowing manual pool index [{} > {}], resetting to 1 " , og . xmrig . selected_index , app . og_pool_vec . len ( ) ) ;
let ( name , pool ) = app . og_pool_vec [ 0 ] . clone ( ) ;
og . xmrig . selected_index = 0 ;
og . xmrig . selected_name = name . clone ( ) ;
og . xmrig . selected_ip = pool . ip . clone ( ) ;
og . xmrig . selected_port = pool . port . clone ( ) ;
app . state . xmrig . selected_index = 0 ;
app . state . xmrig . selected_name = name ;
app . state . xmrig . selected_ip = pool . ip ;
app . state . xmrig . selected_port = pool . port ;
}
2022-12-12 19:34:17 +00:00
2022-10-27 03:15:56 +00:00
// Apply TOML values to [Update]
2022-12-12 19:34:17 +00:00
debug! ( " App Init | Applying TOML values to [Update]... " ) ;
2022-11-16 02:19:30 +00:00
let p2pool_path = og . gupax . absolute_p2pool_path . clone ( ) ;
let xmrig_path = og . gupax . absolute_xmrig_path . clone ( ) ;
let tor = og . gupax . update_via_tor ;
2022-11-02 22:18:41 +00:00
app . update = Arc ::new ( Mutex ::new ( Update ::new ( app . exe . clone ( ) , p2pool_path , xmrig_path , tor ) ) ) ;
2022-12-12 19:34:17 +00:00
debug! ( " App Init | Setting state Gupax version... " ) ;
2022-11-22 02:01:50 +00:00
// Set state version as compiled in version
og . version . lock ( ) . unwrap ( ) . gupax = GUPAX_VERSION . to_string ( ) ;
app . state . version . lock ( ) . unwrap ( ) . gupax = GUPAX_VERSION . to_string ( ) ;
2022-12-12 19:34:17 +00:00
debug! ( " App Init | Setting saved [Tab]... " ) ;
2022-12-06 03:33:35 +00:00
// Set saved [Tab]
app . tab = app . state . gupax . tab ;
2022-12-12 19:34:17 +00:00
2022-11-16 02:19:30 +00:00
drop ( og ) ; // Unlock [og]
2022-11-17 18:03:45 +00:00
info! ( " App ... OK " ) ;
2022-12-07 02:13:37 +00:00
// Spawn the "Helper" thread.
info! ( " Helper | Spawning helper thread... " ) ;
2022-12-11 20:49:01 +00:00
Helper ::spawn_helper ( & app . helper , sysinfo , app . pid , app . max_threads ) ;
2022-12-07 02:13:37 +00:00
info! ( " Helper ... OK " ) ;
2022-12-10 02:00:33 +00:00
// Check for privilege. Should be Admin on [Windows] and NOT root on Unix.
2022-12-12 19:34:17 +00:00
debug! ( " App Init | Checking for privilege level... " ) ;
2022-12-10 02:00:33 +00:00
#[ cfg(target_os = " windows " ) ]
2022-12-10 20:35:20 +00:00
if is_elevated ::is_elevated ( ) {
app . admin = true ;
} else {
2022-12-10 02:00:33 +00:00
error! ( " Windows | Admin user not detected! " ) ;
2022-12-10 03:06:42 +00:00
app . error_state . set ( format! ( " Gupax was not launched as Administrator! \n Be warned, XMRig might have less hashrate! " ) , ErrorFerris ::Sudo , ErrorButtons ::WindowsAdmin ) ;
2022-12-10 02:00:33 +00:00
}
#[ cfg(target_family = " unix " ) ]
if sudo_check ::check ( ) ! = sudo_check ::RunningAs ::User {
let id = sudo_check ::check ( ) ;
error! ( " Unix | Regular user not detected: [{:?}] " , id ) ;
app . error_state . set ( format! ( " Gupax was launched as: [ {:?} ] \n Please launch Gupax with regular user permissions. " , id ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ;
}
2022-12-22 16:03:38 +00:00
// macOS re-locates "dangerous" applications into some read-only "/private" directory.
// It _seems_ to be fixed by moving [Gupax.app] into "/Applications".
// So, detect if we are in in "/private" and warn the user.
#[ cfg(target_os = " macos " ) ]
if app . exe . starts_with ( " /private " ) {
app . error_state . set ( format! ( " macOS thinks Gupax is a virus! \n (macOS has relocated Gupax for security reasons) \n \n The directory: [ {} ] \n Since this is a private read-only directory, it causes issues with updates and correctly locating P2Pool/XMRig. Please move Gupax into the [Applications] directory, this lets macOS relax a little. \n " , app . exe ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ;
}
2022-10-17 00:36:58 +00:00
app
2022-10-01 16:58:22 +00:00
}
}
2022-11-14 02:56:25 +00:00
//---------------------------------------------------------------------------------------------------- [Tab] Enum + Impl
2022-10-01 16:58:22 +00:00
// The tabs inside [App].
2022-12-06 03:33:35 +00:00
#[ derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize) ]
pub enum Tab {
2022-10-01 16:58:22 +00:00
About ,
Status ,
Gupax ,
P2pool ,
Xmrig ,
}
2022-10-15 19:15:27 +00:00
2022-10-01 16:58:22 +00:00
impl Default for Tab {
fn default ( ) -> Self {
Self ::About
}
}
2022-11-27 20:11:00 +00:00
//---------------------------------------------------------------------------------------------------- [Restart] Enum
#[ derive(Clone, Copy, Debug, PartialEq, Eq) ]
pub enum Restart {
No , // We don't need to restart
Yes , // We updated, user should probably (but isn't required to) restart
}
2022-11-15 21:29:05 +00:00
//---------------------------------------------------------------------------------------------------- [ErrorState] struct
2022-11-24 04:03:56 +00:00
#[ derive(Clone, Copy, Debug, PartialEq, Eq) ]
2022-11-21 01:21:47 +00:00
pub enum ErrorButtons {
YesNo ,
StayQuit ,
ResetState ,
ResetNode ,
Okay ,
Quit ,
2022-12-07 23:02:08 +00:00
Sudo ,
2022-12-10 03:06:42 +00:00
WindowsAdmin ,
2022-12-19 04:57:06 +00:00
Debug ,
2022-11-21 01:21:47 +00:00
}
2022-11-24 04:03:56 +00:00
#[ derive(Clone, Copy, Debug, PartialEq, Eq) ]
2022-11-21 01:21:47 +00:00
pub enum ErrorFerris {
Happy ,
2022-12-19 04:57:06 +00:00
Cute ,
2022-11-21 01:21:47 +00:00
Oops ,
Error ,
Panic ,
2022-12-07 23:02:08 +00:00
Sudo ,
2022-11-21 01:21:47 +00:00
}
2022-11-15 21:29:05 +00:00
pub struct ErrorState {
error : bool , // Is there an error?
2022-11-20 02:20:28 +00:00
msg : String , // What message to display?
2022-11-16 19:07:27 +00:00
ferris : ErrorFerris , // Which ferris to display?
2022-11-15 21:29:05 +00:00
buttons : ErrorButtons , // Which buttons to display?
2022-12-17 22:17:26 +00:00
quit_twice : bool , // This indicates the user tried to quit on the [ask_before_quit] screen
2022-11-15 21:29:05 +00:00
}
2022-12-14 03:41:05 +00:00
impl Default for ErrorState {
fn default ( ) -> Self {
Self ::new ( )
}
}
2022-11-15 21:29:05 +00:00
impl ErrorState {
pub fn new ( ) -> Self {
Self {
error : false ,
2022-11-20 02:20:28 +00:00
msg : " Unknown Error " . to_string ( ) ,
2022-11-16 19:07:27 +00:00
ferris : ErrorFerris ::Oops ,
2022-11-15 21:29:05 +00:00
buttons : ErrorButtons ::Okay ,
2022-12-17 22:17:26 +00:00
quit_twice : false ,
2022-11-15 21:29:05 +00:00
}
}
2022-11-20 18:31:00 +00:00
// Convenience function to enable the [App] error state
pub fn set ( & mut self , msg : impl Into < String > , ferris : ErrorFerris , buttons : ErrorButtons ) {
if self . error {
// If a panic error is already set, return
if self . ferris = = ErrorFerris ::Panic { return }
// If we shouldn't be overriding the current error, return
match self . buttons {
ErrorButtons ::YesNo = > ( ) , // Not important
ErrorButtons ::Okay = > ( ) , // Not important
_ = > return , // Overwrite, Quits, etc
}
}
2022-11-16 19:07:27 +00:00
* self = Self {
2022-11-20 18:31:00 +00:00
error : true ,
2022-11-20 02:20:28 +00:00
msg : msg . into ( ) ,
2022-11-16 19:07:27 +00:00
ferris ,
buttons ,
2022-12-17 22:17:26 +00:00
quit_twice : false ,
2022-11-16 19:07:27 +00:00
} ;
2022-11-15 21:29:05 +00:00
}
2022-12-07 23:02:08 +00:00
// Just sets the current state to new, resetting it.
pub fn reset ( & mut self ) {
* self = Self ::new ( ) ;
}
// Instead of creating a whole new screen and system, this (ab)uses ErrorState
// to ask for the [sudo] when starting XMRig. Yes, yes I know, it's called "ErrorState"
// but rewriting the UI code and button stuff might be worse.
2022-12-08 01:34:21 +00:00
// It also resets the current [SudoState]
pub fn ask_sudo ( & mut self , state : & Arc < Mutex < SudoState > > ) {
2022-12-07 23:02:08 +00:00
* self = Self {
error : true ,
msg : String ::new ( ) ,
ferris : ErrorFerris ::Sudo ,
buttons : ErrorButtons ::Sudo ,
2022-12-17 22:17:26 +00:00
quit_twice : false ,
2022-12-08 01:34:21 +00:00
} ;
2022-12-14 03:41:05 +00:00
SudoState ::reset ( state )
2022-12-07 23:02:08 +00:00
}
2022-11-15 21:29:05 +00:00
}
//---------------------------------------------------------------------------------------------------- [Images] struct
struct Images {
banner : RetainedImage ,
2022-11-20 02:20:28 +00:00
happy : RetainedImage ,
2022-12-19 04:57:06 +00:00
cute : RetainedImage ,
2022-11-15 21:29:05 +00:00
oops : RetainedImage ,
error : RetainedImage ,
panic : RetainedImage ,
2022-12-07 23:02:08 +00:00
sudo : RetainedImage ,
2022-11-15 21:29:05 +00:00
}
impl Images {
fn new ( ) -> Self {
Self {
banner : RetainedImage ::from_image_bytes ( " banner.png " , BYTES_BANNER ) . unwrap ( ) ,
2022-11-20 02:20:28 +00:00
happy : RetainedImage ::from_image_bytes ( " happy.png " , FERRIS_HAPPY ) . unwrap ( ) ,
2022-12-19 04:57:06 +00:00
cute : RetainedImage ::from_image_bytes ( " cute.png " , FERRIS_CUTE ) . unwrap ( ) ,
2022-11-16 19:07:27 +00:00
oops : RetainedImage ::from_image_bytes ( " oops.png " , FERRIS_OOPS ) . unwrap ( ) ,
error : RetainedImage ::from_image_bytes ( " error.png " , FERRIS_ERROR ) . unwrap ( ) ,
panic : RetainedImage ::from_image_bytes ( " panic.png " , FERRIS_PANIC ) . unwrap ( ) ,
2022-12-07 23:02:08 +00:00
sudo : RetainedImage ::from_image_bytes ( " panic.png " , FERRIS_SUDO ) . unwrap ( ) ,
2022-11-15 21:29:05 +00:00
}
}
}
2022-11-14 02:56:25 +00:00
//---------------------------------------------------------------------------------------------------- [Regexes] struct
#[ derive(Clone, Debug) ]
2022-11-16 19:40:25 +00:00
pub struct Regexes {
pub name : Regex ,
pub address : Regex ,
pub ipv4 : Regex ,
pub domain : Regex ,
pub port : Regex ,
2022-11-14 02:56:25 +00:00
}
impl Regexes {
fn new ( ) -> Self {
Regexes {
2022-12-15 23:47:17 +00:00
name : Regex ::new ( " ^[A-Za-z0-9-_.]+( [A-Za-z0-9-_.]+)*$ " ) . unwrap ( ) ,
2022-11-14 02:56:25 +00:00
address : Regex ::new ( " ^4[A-Za-z1-9]+$ " ) . unwrap ( ) , // This still needs to check for (l, I, o, 0)
ipv4 : Regex ::new ( r # "^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$"# ) . unwrap ( ) ,
domain : Regex ::new ( r # "^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})$"# ) . unwrap ( ) ,
port : Regex ::new ( r # "^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$"# ) . unwrap ( ) ,
}
}
2022-12-09 01:24:37 +00:00
// Check if a Monero address is correct.
pub fn addr_ok ( & self , address : & str ) -> bool {
2022-12-14 03:41:05 +00:00
address . len ( ) = = 95 & & Regex ::is_match ( & self . address , address ) & & ! address . contains ( '0' ) & & ! address . contains ( 'O' ) & & ! address . contains ( 'l' )
2022-12-09 01:24:37 +00:00
}
2022-11-14 02:56:25 +00:00
}
2022-12-13 14:39:09 +00:00
//---------------------------------------------------------------------------------------------------- [Pressed] enum
// These represent the keys pressed during the frame.
// I could use egui's [Key] but there is no option for
// a [None] and wrapping [key_pressed] like [Option<egui::Key>]
// meant that I had to destructure like this:
// if let Some(egui::Key)) = key_pressed { /* do thing */ }
//
// That's ugly, so these are used instead so a simple compare can be used.
#[ derive(Debug,Clone,Eq,PartialEq) ]
enum KeyPressed {
F11 ,
Up ,
Down ,
Esc ,
2022-12-16 14:47:16 +00:00
Z ,
X ,
2022-12-27 21:29:12 +00:00
C ,
V ,
2022-12-13 14:39:09 +00:00
S ,
R ,
2022-12-19 04:57:06 +00:00
D ,
2022-12-13 14:39:09 +00:00
None ,
}
impl KeyPressed {
fn is_f11 ( & self ) -> bool {
* self = = Self ::F11
}
2022-12-16 14:47:16 +00:00
fn is_z ( & self ) -> bool {
* self = = Self ::Z
2022-12-13 14:39:09 +00:00
}
2022-12-16 14:47:16 +00:00
fn is_x ( & self ) -> bool {
* self = = Self ::X
2022-12-13 14:39:09 +00:00
}
fn is_up ( & self ) -> bool {
* self = = Self ::Up
}
fn is_down ( & self ) -> bool {
* self = = Self ::Down
}
fn is_esc ( & self ) -> bool {
* self = = Self ::Esc
}
fn is_s ( & self ) -> bool {
* self = = Self ::S
}
fn is_r ( & self ) -> bool {
* self = = Self ::R
}
2022-12-19 04:57:06 +00:00
fn is_d ( & self ) -> bool {
* self = = Self ::D
}
2022-12-27 21:29:12 +00:00
fn is_c ( & self ) -> bool {
* self = = Self ::C
}
fn is_v ( & self ) -> bool {
* self = = Self ::V
}
2022-12-13 14:39:09 +00:00
fn is_none ( & self ) -> bool {
* self = = Self ::None
}
}
2022-10-15 19:15:27 +00:00
//---------------------------------------------------------------------------------------------------- Init functions
2022-10-01 16:58:22 +00:00
fn init_text_styles ( ctx : & egui ::Context , width : f32 ) {
2022-12-13 14:39:09 +00:00
let scale = width / 30.0 ;
2022-10-01 16:58:22 +00:00
let mut style = ( * ctx . style ( ) ) . clone ( ) ;
style . text_styles = [
( Small , FontId ::new ( scale / 3.0 , Proportional ) ) ,
( Body , FontId ::new ( scale / 2.0 , Proportional ) ) ,
( Button , FontId ::new ( scale / 2.0 , Proportional ) ) ,
2022-11-11 02:20:31 +00:00
( Monospace , FontId ::new ( scale / 2.0 , egui ::FontFamily ::Monospace ) ) ,
2022-10-01 16:58:22 +00:00
( Heading , FontId ::new ( scale / 1.5 , Proportional ) ) ,
( Name ( " Tab " . into ( ) ) , FontId ::new ( scale * 1.2 , Proportional ) ) ,
( Name ( " Bottom " . into ( ) ) , FontId ::new ( scale / 2.0 , Proportional ) ) ,
2022-11-14 02:56:25 +00:00
( Name ( " MonospaceSmall " . into ( ) ) , FontId ::new ( scale / 2.5 , egui ::FontFamily ::Monospace ) ) ,
2022-11-15 21:29:05 +00:00
( Name ( " MonospaceLarge " . into ( ) ) , FontId ::new ( scale / 1.5 , egui ::FontFamily ::Monospace ) ) ,
2022-10-01 16:58:22 +00:00
] . into ( ) ;
2022-11-25 16:59:48 +00:00
style . spacing . icon_width_inner = width / 35.0 ;
style . spacing . icon_width = width / 25.0 ;
style . spacing . icon_spacing = 20.0 ;
2022-11-28 02:47:50 +00:00
style . spacing . scroll_bar_width = width / 150.0 ;
2022-10-01 16:58:22 +00:00
ctx . set_style ( style ) ;
ctx . set_pixels_per_point ( 1.0 ) ;
2022-10-13 12:57:50 +00:00
ctx . request_repaint ( ) ;
2022-10-01 16:58:22 +00:00
}
2022-11-21 01:21:47 +00:00
fn init_logger ( now : Instant ) {
2022-10-01 16:58:22 +00:00
use env_logger ::fmt ::Color ;
2022-11-21 01:21:47 +00:00
Builder ::new ( ) . format ( move | buf , record | {
2022-10-01 16:58:22 +00:00
let mut style = buf . style ( ) ;
2022-11-24 04:03:56 +00:00
let level = match record . level ( ) {
Level ::Error = > { style . set_color ( Color ::Red ) ; " ERROR " } ,
Level ::Warn = > { style . set_color ( Color ::Yellow ) ; " WARN " } ,
Level ::Info = > { style . set_color ( Color ::White ) ; " INFO " } ,
Level ::Debug = > { style . set_color ( Color ::Blue ) ; " DEBUG " } ,
Level ::Trace = > { style . set_color ( Color ::Magenta ) ; " TRACE " } ,
2022-10-01 16:58:22 +00:00
} ;
writeln! (
buf ,
2022-10-13 12:57:50 +00:00
" [{}] [{}] [{}:{}] {} " ,
2022-10-01 16:58:22 +00:00
style . set_bold ( true ) . value ( level ) ,
2022-11-22 02:01:50 +00:00
buf . style ( ) . set_dimmed ( true ) . value ( format! ( " {:.3} " , now . elapsed ( ) . as_secs_f32 ( ) ) ) ,
2022-10-01 16:58:22 +00:00
buf . style ( ) . set_dimmed ( true ) . value ( record . file ( ) . unwrap_or ( " ??? " ) ) ,
buf . style ( ) . set_dimmed ( true ) . value ( record . line ( ) . unwrap_or ( 0 ) ) ,
record . args ( ) ,
)
2022-11-21 01:21:47 +00:00
} ) . filter_level ( LevelFilter ::Info ) . write_style ( WriteStyle ::Always ) . parse_default_env ( ) . format_timestamp_millis ( ) . init ( ) ;
2022-10-13 12:57:50 +00:00
info! ( " init_logger() ... OK " ) ;
}
2022-10-01 16:58:22 +00:00
2022-11-23 04:21:46 +00:00
fn init_options ( initial_window_size : Option < Vec2 > ) -> NativeOptions {
2022-10-01 16:58:22 +00:00
let mut options = eframe ::NativeOptions ::default ( ) ;
2022-11-23 04:21:46 +00:00
options . min_window_size = Some ( Vec2 ::new ( APP_MIN_WIDTH , APP_MIN_HEIGHT ) ) ;
options . max_window_size = Some ( Vec2 ::new ( APP_MAX_WIDTH , APP_MAX_HEIGHT ) ) ;
options . initial_window_size = initial_window_size ;
2022-10-01 16:58:22 +00:00
options . follow_system_theme = false ;
options . default_theme = eframe ::Theme ::Dark ;
2022-10-13 12:57:50 +00:00
let icon = image ::load_from_memory ( BYTES_ICON ) . expect ( " Failed to read icon bytes " ) . to_rgba8 ( ) ;
2022-10-01 16:58:22 +00:00
let ( icon_width , icon_height ) = icon . dimensions ( ) ;
options . icon_data = Some ( eframe ::IconData {
rgba : icon . into_raw ( ) ,
width : icon_width ,
height : icon_height ,
} ) ;
2022-10-13 12:57:50 +00:00
info! ( " init_options() ... OK " ) ;
options
}
2022-11-25 01:28:13 +00:00
fn init_auto ( app : & mut App ) {
2022-11-20 19:20:25 +00:00
// Return early if [--no-startup] was not passed
2022-11-15 03:11:00 +00:00
if app . no_startup {
info! ( " [--no-startup] flag passed, skipping init_auto()... " ) ;
return
2022-11-20 02:20:28 +00:00
} else if app . error_state . error {
info! ( " App error detected, skipping init_auto()... " ) ;
return
2022-11-15 03:11:00 +00:00
} else {
info! ( " Starting init_auto()... " ) ;
}
2022-11-20 19:20:25 +00:00
2022-11-11 04:42:57 +00:00
// [Auto-Update]
2022-12-18 02:42:30 +00:00
#[ cfg(not(feature = " distro " )) ]
2022-11-11 04:42:57 +00:00
if app . state . gupax . auto_update {
2022-11-27 20:11:00 +00:00
Update ::spawn_thread ( & app . og , & app . state . gupax , & app . state_path , & app . update , & mut app . error_state , & app . restart ) ;
2022-11-11 04:42:57 +00:00
} else {
info! ( " Skipping auto-update... " ) ;
}
// [Auto-Ping]
2022-12-15 16:21:17 +00:00
if app . state . p2pool . auto_ping & & app . state . p2pool . simple {
2022-12-10 02:00:33 +00:00
Ping ::spawn_thread ( & app . ping )
2022-11-11 04:42:57 +00:00
} else {
info! ( " Skipping auto-ping... " ) ;
}
2022-12-11 04:06:24 +00:00
// [Auto-P2Pool]
if app . state . gupax . auto_p2pool {
if ! Regexes ::addr_ok ( & app . regex , & app . state . p2pool . address ) {
warn! ( " Gupax | P2Pool address is not valid! Skipping auto-p2pool... " ) ;
2022-12-16 14:47:16 +00:00
} else if ! Gupax ::path_is_file ( & app . state . gupax . p2pool_path ) {
warn! ( " Gupax | P2Pool path is not a file! Skipping auto-p2pool... " ) ;
} else if ! crate ::update ::check_p2pool_path ( & app . state . gupax . p2pool_path ) {
warn! ( " Gupax | P2Pool path is not valid! Skipping auto-p2pool... " ) ;
2022-12-11 04:06:24 +00:00
} else {
Helper ::start_p2pool ( & app . helper , & app . state . p2pool , & app . state . gupax . absolute_p2pool_path ) ;
}
} else {
2022-12-11 20:49:01 +00:00
info! ( " Skipping auto-p2pool... " ) ;
2022-12-11 04:06:24 +00:00
}
// [Auto-XMRig]
if app . state . gupax . auto_xmrig {
2022-12-16 14:47:16 +00:00
if ! Gupax ::path_is_file ( & app . state . gupax . xmrig_path ) {
2022-12-11 04:06:24 +00:00
warn! ( " Gupax | XMRig path is not an executable! Skipping auto-xmrig... " ) ;
2022-12-16 14:47:16 +00:00
} else if ! crate ::update ::check_xmrig_path ( & app . state . gupax . xmrig_path ) {
warn! ( " Gupax | XMRig path is not valid! Skipping auto-xmrig... " ) ;
2022-12-14 03:41:05 +00:00
} else if cfg! ( windows ) {
Helper ::start_xmrig ( & app . helper , & app . state . xmrig , & app . state . gupax . absolute_xmrig_path , Arc ::clone ( & app . sudo ) ) ;
2022-12-11 04:06:24 +00:00
} else {
2022-12-14 03:41:05 +00:00
app . sudo . lock ( ) . unwrap ( ) . signal = ProcessSignal ::Start ;
app . error_state . ask_sudo ( & app . sudo ) ;
2022-12-11 04:06:24 +00:00
}
} else {
info! ( " Skipping auto-xmrig... " ) ;
}
2022-11-11 04:42:57 +00:00
}
2022-11-25 16:59:48 +00:00
//---------------------------------------------------------------------------------------------------- Reset functions
2022-11-20 18:31:00 +00:00
fn reset_state ( path : & PathBuf ) -> Result < ( ) , TomlError > {
match State ::create_new ( path ) {
2022-11-20 02:20:28 +00:00
Ok ( _ ) = > { info! ( " Resetting [state.toml] ... OK " ) ; Ok ( ( ) ) } ,
Err ( e ) = > { error! ( " Resetting [state.toml] ... FAIL ... {} " , e ) ; Err ( e ) } ,
2022-11-15 21:29:05 +00:00
}
2022-11-20 02:20:28 +00:00
}
2022-11-20 18:31:00 +00:00
fn reset_nodes ( path : & PathBuf ) -> Result < ( ) , TomlError > {
match Node ::create_new ( path ) {
2022-11-20 02:20:28 +00:00
Ok ( _ ) = > { info! ( " Resetting [node.toml] ... OK " ) ; Ok ( ( ) ) } ,
Err ( e ) = > { error! ( " Resetting [node.toml] ... FAIL ... {} " , e ) ; Err ( e ) } ,
}
}
2022-11-25 02:03:42 +00:00
fn reset_pools ( path : & PathBuf ) -> Result < ( ) , TomlError > {
match Pool ::create_new ( path ) {
Ok ( _ ) = > { info! ( " Resetting [pool.toml] ... OK " ) ; Ok ( ( ) ) } ,
Err ( e ) = > { error! ( " Resetting [pool.toml] ... FAIL ... {} " , e ) ; Err ( e ) } ,
}
}
fn reset ( path : & PathBuf , state : & PathBuf , node : & PathBuf , pool : & PathBuf ) {
2022-11-20 02:20:28 +00:00
let mut code = 0 ;
2022-11-20 18:31:00 +00:00
// Attempt to remove directory first
match std ::fs ::remove_dir_all ( path ) {
Ok ( _ ) = > info! ( " Removing OS data path ... OK " ) ,
Err ( e ) = > { error! ( " Removing OS data path ... FAIL ... {} " , e ) ; code = 1 ; } ,
}
2022-11-21 01:21:47 +00:00
// Recreate
match create_gupax_dir ( path ) {
Ok ( _ ) = > ( ) ,
Err ( _ ) = > code = 1 ,
}
2022-11-20 18:31:00 +00:00
match reset_state ( state ) {
2022-11-20 02:20:28 +00:00
Ok ( _ ) = > ( ) ,
Err ( _ ) = > code = 1 ,
}
2022-11-20 18:31:00 +00:00
match reset_nodes ( node ) {
2022-11-20 02:20:28 +00:00
Ok ( _ ) = > ( ) ,
Err ( _ ) = > code = 1 ,
2022-11-15 21:29:05 +00:00
}
2022-11-25 02:03:42 +00:00
match reset_pools ( pool ) {
Ok ( _ ) = > ( ) ,
Err ( _ ) = > code = 1 ,
}
2022-11-15 21:29:05 +00:00
match code {
2022-11-20 18:31:00 +00:00
0 = > println! ( " \n Gupax reset ... OK " ) ,
2022-11-25 02:03:42 +00:00
_ = > eprintln! ( " \n Gupax reset ... FAIL " ) ,
2022-11-15 21:29:05 +00:00
}
exit ( code ) ;
}
2022-10-16 21:29:24 +00:00
//---------------------------------------------------------------------------------------------------- Misc functions
2022-11-20 18:31:00 +00:00
fn parse_args < S : Into < String > > ( mut app : App , panic : S ) -> App {
2022-10-16 21:29:24 +00:00
info! ( " Parsing CLI arguments... " ) ;
let mut args : Vec < String > = env ::args ( ) . collect ( ) ;
if args . len ( ) = = 1 { info! ( " No args ... OK " ) ; return app } else { args . remove ( 0 ) ; info! ( " Args ... {:?} " , args ) ; }
// [help/version], exit early
for arg in & args {
match arg . as_str ( ) {
2022-11-20 02:20:28 +00:00
" --help " = > { println! ( " {} " , ARG_HELP ) ; exit ( 0 ) ; } ,
" --version " = > {
2022-12-12 20:34:49 +00:00
println! ( " Gupax {} [OS: {} , Commit: {} ] \n This Gupax was originally bundled with: \n - P2Pool {} \n - XMRig {} \n \n {} " , GUPAX_VERSION , OS_NAME , & COMMIT [ .. 40 ] , P2POOL_VERSION , XMRIG_VERSION , ARG_COPYRIGHT ) ;
2022-10-16 21:29:24 +00:00
exit ( 0 ) ;
} ,
2022-11-20 02:20:28 +00:00
" --ferris " = > { println! ( " {} " , FERRIS_ANSI ) ; exit ( 0 ) ; } ,
2022-10-16 21:29:24 +00:00
_ = > ( ) ,
}
}
2022-11-20 18:31:00 +00:00
// Abort on panic
let panic = panic . into ( ) ;
if ! panic . is_empty ( ) {
info! ( " [Gupax error] {} " , panic ) ;
exit ( 1 ) ;
}
2022-10-16 21:29:24 +00:00
// Everything else
for arg in args {
match arg . as_str ( ) {
2022-11-20 18:31:00 +00:00
" --state " = > { info! ( " Printing state... " ) ; print_disk_file ( & app . state_path ) ; }
" --nodes " = > { info! ( " Printing node list... " ) ; print_disk_file ( & app . node_path ) ; }
2022-11-25 02:03:42 +00:00
" --reset-state " = > if let Ok ( ( ) ) = reset_state ( & app . state_path ) { println! ( " \n State reset ... OK " ) ; exit ( 0 ) ; } else { eprintln! ( " \n State reset ... FAIL " ) ; exit ( 1 ) } ,
" --reset-nodes " = > if let Ok ( ( ) ) = reset_nodes ( & app . node_path ) { println! ( " \n Node reset ... OK " ) ; exit ( 0 ) } else { eprintln! ( " \n Node reset ... FAIL " ) ; exit ( 1 ) } ,
" --reset-pools " = > if let Ok ( ( ) ) = reset_pools ( & app . pool_path ) { println! ( " \n Pool reset ... OK " ) ; exit ( 0 ) } else { eprintln! ( " \n Pool reset ... FAIL " ) ; exit ( 1 ) } ,
" --reset-all " = > reset ( & app . os_data_path , & app . state_path , & app . node_path , & app . pool_path ) ,
2022-11-20 02:20:28 +00:00
" --no-startup " = > app . no_startup = true ,
2022-11-25 02:03:42 +00:00
_ = > { eprintln! ( " \n [Gupax error] Invalid option: [ {} ] \n For help, use: [--help] " , arg ) ; exit ( 1 ) ; } ,
2022-10-16 21:29:24 +00:00
}
}
app
}
2022-10-25 02:58:42 +00:00
// Get absolute [Gupax] binary path
pub fn get_exe ( ) -> Result < String , std ::io ::Error > {
2022-10-19 18:35:32 +00:00
match std ::env ::current_exe ( ) {
2022-11-16 19:40:25 +00:00
Ok ( path ) = > { Ok ( path . display ( ) . to_string ( ) ) } ,
2022-11-24 04:03:56 +00:00
Err ( err ) = > { error! ( " Couldn't get absolute Gupax PATH " ) ; Err ( err ) } ,
2022-10-19 18:35:32 +00:00
}
}
2022-10-25 02:58:42 +00:00
// Get absolute [Gupax] directory path
pub fn get_exe_dir ( ) -> Result < String , std ::io ::Error > {
match std ::env ::current_exe ( ) {
Ok ( mut path ) = > { path . pop ( ) ; Ok ( path . display ( ) . to_string ( ) ) } ,
2022-11-24 04:03:56 +00:00
Err ( err ) = > { error! ( " Couldn't get exe basepath PATH " ) ; Err ( err ) } ,
2022-10-25 02:58:42 +00:00
}
}
2022-11-15 03:11:00 +00:00
// Clean any [gupax_update_.*] directories
// The trailing random bits must be exactly 10 alphanumeric characters
update: save [Version] to state, use runtime [og: State]
[og: State] is now completely wrapped in an [Arc<Mutex>] so that
when the update is done, it can [.lock()] the CURRENT runtime
settings of the user and save to [gupax.toml] instead of using an
old copy that was given to it at the beginning of the thread.
In practice, this means users can change settings around during
an update and the update finishing and saving to disk won't be
using their old settings, but the current ones. Wrapping all of
[og: State] within in [Arc<Mutex>] might be overkill compared to
message channels but [State] really is just a few [bool]'s, [u*],
and small [String]'s, so it's not much data.
To bypass a deadlock when comparing [og == state] every frame,
[og]'s struct fields get cloned every frame into separate
variables, then it gets compared. This is also pretty stupid, but
again, the data being cloned is so tiny that it doesn't seem to
slow anything down.
2022-11-02 17:58:44 +00:00
pub fn clean_dir ( ) -> Result < ( ) , anyhow ::Error > {
2022-11-15 03:11:00 +00:00
let regex = Regex ::new ( " ^gupax_update_[A-Za-z0-9]{10}$ " ) . unwrap ( ) ;
update: save [Version] to state, use runtime [og: State]
[og: State] is now completely wrapped in an [Arc<Mutex>] so that
when the update is done, it can [.lock()] the CURRENT runtime
settings of the user and save to [gupax.toml] instead of using an
old copy that was given to it at the beginning of the thread.
In practice, this means users can change settings around during
an update and the update finishing and saving to disk won't be
using their old settings, but the current ones. Wrapping all of
[og: State] within in [Arc<Mutex>] might be overkill compared to
message channels but [State] really is just a few [bool]'s, [u*],
and small [String]'s, so it's not much data.
To bypass a deadlock when comparing [og == state] every frame,
[og]'s struct fields get cloned every frame into separate
variables, then it gets compared. This is also pretty stupid, but
again, the data being cloned is so tiny that it doesn't seem to
slow anything down.
2022-11-02 17:58:44 +00:00
for entry in std ::fs ::read_dir ( get_exe_dir ( ) ? ) ? {
let entry = entry ? ;
2022-11-02 22:18:41 +00:00
if ! entry . path ( ) . is_dir ( ) { continue }
2022-11-24 04:03:56 +00:00
if Regex ::is_match ( & regex , entry . file_name ( ) . to_str ( ) . ok_or_else ( | | anyhow ::Error ::msg ( " Basename failed " ) ) ? ) {
update: save [Version] to state, use runtime [og: State]
[og: State] is now completely wrapped in an [Arc<Mutex>] so that
when the update is done, it can [.lock()] the CURRENT runtime
settings of the user and save to [gupax.toml] instead of using an
old copy that was given to it at the beginning of the thread.
In practice, this means users can change settings around during
an update and the update finishing and saving to disk won't be
using their old settings, but the current ones. Wrapping all of
[og: State] within in [Arc<Mutex>] might be overkill compared to
message channels but [State] really is just a few [bool]'s, [u*],
and small [String]'s, so it's not much data.
To bypass a deadlock when comparing [og == state] every frame,
[og]'s struct fields get cloned every frame into separate
variables, then it gets compared. This is also pretty stupid, but
again, the data being cloned is so tiny that it doesn't seem to
slow anything down.
2022-11-02 17:58:44 +00:00
let path = entry . path ( ) ;
match std ::fs ::remove_dir_all ( & path ) {
Ok ( _ ) = > info! ( " Remove [{}] ... OK " , path . display ( ) ) ,
Err ( e ) = > warn! ( " Remove [{}] ... FAIL ... {} " , path . display ( ) , e ) ,
}
}
}
Ok ( ( ) )
}
2022-11-14 02:56:25 +00:00
// Print disk files to console
2022-11-20 18:31:00 +00:00
fn print_disk_file ( path : & PathBuf ) {
2022-11-24 04:03:56 +00:00
match std ::fs ::read_to_string ( path ) {
2022-11-19 18:34:46 +00:00
Ok ( string ) = > { print! ( " {} " , string ) ; exit ( 0 ) ; } ,
2022-11-14 02:56:25 +00:00
Err ( e ) = > { error! ( " {} " , e ) ; exit ( 1 ) ; } ,
}
}
2022-10-15 19:15:27 +00:00
//---------------------------------------------------------------------------------------------------- Main [App] frame
fn main ( ) {
2022-11-16 02:19:30 +00:00
let now = Instant ::now ( ) ;
2022-11-21 01:21:47 +00:00
init_logger ( now ) ;
2022-11-30 22:21:55 +00:00
let mut app = App ::new ( now ) ;
2022-11-25 01:28:13 +00:00
init_auto ( & mut app ) ;
2022-12-07 03:01:36 +00:00
let selected_width = app . state . gupax . selected_width as f32 ;
let selected_height = app . state . gupax . selected_height as f32 ;
let initial_window_size = if selected_width > APP_MAX_WIDTH | | selected_height > APP_MAX_HEIGHT {
warn! ( " App | Set width or height was greater than the maximum! Starting with the default resolution... " ) ;
Some ( Vec2 ::new ( APP_DEFAULT_WIDTH , APP_DEFAULT_HEIGHT ) )
} else {
Some ( Vec2 ::new ( app . state . gupax . selected_width as f32 , app . state . gupax . selected_height as f32 ) )
2022-11-23 04:21:46 +00:00
} ;
let options = init_options ( initial_window_size ) ;
update: save [Version] to state, use runtime [og: State]
[og: State] is now completely wrapped in an [Arc<Mutex>] so that
when the update is done, it can [.lock()] the CURRENT runtime
settings of the user and save to [gupax.toml] instead of using an
old copy that was given to it at the beginning of the thread.
In practice, this means users can change settings around during
an update and the update finishing and saving to disk won't be
using their old settings, but the current ones. Wrapping all of
[og: State] within in [Arc<Mutex>] might be overkill compared to
message channels but [State] really is just a few [bool]'s, [u*],
and small [String]'s, so it's not much data.
To bypass a deadlock when comparing [og == state] every frame,
[og]'s struct fields get cloned every frame into separate
variables, then it gets compared. This is also pretty stupid, but
again, the data being cloned is so tiny that it doesn't seem to
slow anything down.
2022-11-02 17:58:44 +00:00
match clean_dir ( ) {
Ok ( _ ) = > info! ( " Temporary folder cleanup ... OK " ) ,
Err ( e ) = > warn! ( " Could not cleanup [gupax_tmp] folders: {} " , e ) ,
}
2022-12-26 22:37:45 +00:00
info! ( " /*************************************/ Init ... OK /*************************************/ " ) ;
2022-11-16 02:19:30 +00:00
eframe ::run_native ( & app . name_version . clone ( ) , options , Box ::new ( | cc | Box ::new ( App ::cc ( cc , app ) ) ) , ) ;
2022-10-15 19:15:27 +00:00
}
2022-10-01 16:58:22 +00:00
impl eframe ::App for App {
2022-10-13 12:57:50 +00:00
fn on_close_event ( & mut self ) -> bool {
2022-12-17 22:17:26 +00:00
if self . state . gupax . ask_before_quit {
// If we're already on the [ask_before_quit] screen and
// the user tried to exit again, exit.
if self . error_state . quit_twice {
if self . state . gupax . save_before_quit { self . save_before_quit ( ) ; }
return true
}
// Else, set the error
2022-11-20 18:31:00 +00:00
self . error_state . set ( " " , ErrorFerris ::Oops , ErrorButtons ::StayQuit ) ;
2022-12-17 22:17:26 +00:00
self . error_state . quit_twice = true ;
2022-11-16 19:07:27 +00:00
false
2022-12-17 15:09:50 +00:00
// Else, just quit.
2022-10-17 00:36:58 +00:00
} else {
2022-12-17 15:09:50 +00:00
if self . state . gupax . save_before_quit { self . save_before_quit ( ) ; }
2022-10-17 00:36:58 +00:00
true
}
2022-10-13 12:57:50 +00:00
}
2022-10-15 19:15:27 +00:00
2022-10-13 12:57:50 +00:00
fn update ( & mut self , ctx : & egui ::Context , frame : & mut eframe ::Frame ) {
2022-10-25 02:58:42 +00:00
// *-------*
// | DEBUG |
// *-------*
2022-12-12 19:34:17 +00:00
debug! ( " App | ----------- Start of [update()] ----------- " ) ;
2022-10-27 03:15:56 +00:00
2022-12-02 18:15:26 +00:00
// If [F11] was pressed, reverse [fullscreen] bool
2022-12-13 14:39:09 +00:00
let mut input = ctx . input_mut ( ) ;
2022-12-14 03:41:05 +00:00
let key : KeyPressed = {
2022-12-13 14:39:09 +00:00
if input . consume_key ( Modifiers ::NONE , Key ::F11 ) {
KeyPressed ::F11
2022-12-16 14:47:16 +00:00
} else if input . consume_key ( Modifiers ::NONE , Key ::Z ) {
KeyPressed ::Z
} else if input . consume_key ( Modifiers ::NONE , Key ::X ) {
KeyPressed ::X
2022-12-27 21:29:12 +00:00
} else if input . consume_key ( Modifiers ::NONE , Key ::C ) {
KeyPressed ::C
} else if input . consume_key ( Modifiers ::NONE , Key ::V ) {
KeyPressed ::V
2022-12-13 14:39:09 +00:00
} else if input . consume_key ( Modifiers ::NONE , Key ::ArrowUp ) {
KeyPressed ::Up
} else if input . consume_key ( Modifiers ::NONE , Key ::ArrowDown ) {
KeyPressed ::Down
} else if input . consume_key ( Modifiers ::NONE , Key ::Escape ) {
KeyPressed ::Esc
} else if input . consume_key ( Modifiers ::NONE , Key ::S ) {
KeyPressed ::S
} else if input . consume_key ( Modifiers ::NONE , Key ::R ) {
KeyPressed ::R
2022-12-19 04:57:06 +00:00
} else if input . consume_key ( Modifiers ::NONE , Key ::D ) {
KeyPressed ::D
2022-12-13 14:39:09 +00:00
} else {
KeyPressed ::None
}
} ;
drop ( input ) ;
2022-12-16 14:47:16 +00:00
// Check if egui wants keyboard input.
// This prevents keyboard shortcuts from clobbering TextEdits.
// (Typing S in text would always [Save] instead)
let wants_input = ctx . wants_keyboard_input ( ) ;
2022-12-13 14:39:09 +00:00
if key . is_f11 ( ) {
2022-12-02 18:15:26 +00:00
let info = frame . info ( ) ;
frame . set_fullscreen ( ! info . window_info . fullscreen ) ;
2022-12-13 14:39:09 +00:00
// Change Tabs LEFT
2022-12-16 14:47:16 +00:00
} else if key . is_z ( ) & & ! wants_input {
2022-12-13 14:39:09 +00:00
match self . tab {
Tab ::About = > self . tab = Tab ::Xmrig ,
Tab ::Status = > self . tab = Tab ::About ,
Tab ::Gupax = > self . tab = Tab ::Status ,
Tab ::P2pool = > self . tab = Tab ::Gupax ,
Tab ::Xmrig = > self . tab = Tab ::P2pool ,
} ;
// Change Tabs RIGHT
2022-12-16 14:47:16 +00:00
} else if key . is_x ( ) & & ! wants_input {
2022-12-13 14:39:09 +00:00
match self . tab {
Tab ::About = > self . tab = Tab ::Status ,
Tab ::Status = > self . tab = Tab ::Gupax ,
Tab ::Gupax = > self . tab = Tab ::P2pool ,
Tab ::P2pool = > self . tab = Tab ::Xmrig ,
Tab ::Xmrig = > self . tab = Tab ::About ,
} ;
2022-12-27 21:29:12 +00:00
// Change Submenu LEFT
} else if key . is_c ( ) & & ! wants_input {
match self . tab {
Tab ::Status = > {
match self . state . status . submenu {
Submenu ::Processes = > self . state . status . submenu = Submenu ::Monero ,
Submenu ::P2pool = > self . state . status . submenu = Submenu ::Processes ,
Submenu ::Monero = > self . state . status . submenu = Submenu ::P2pool ,
}
} ,
Tab ::Gupax = > self . state . gupax . simple = ! self . state . gupax . simple ,
Tab ::P2pool = > self . state . p2pool . simple = ! self . state . p2pool . simple ,
Tab ::Xmrig = > self . state . xmrig . simple = ! self . state . xmrig . simple ,
_ = > ( ) ,
} ;
// Change Submenu RIGHT
} else if key . is_v ( ) & & ! wants_input {
match self . tab {
Tab ::Status = > {
match self . state . status . submenu {
Submenu ::Processes = > self . state . status . submenu = Submenu ::P2pool ,
Submenu ::P2pool = > self . state . status . submenu = Submenu ::Monero ,
Submenu ::Monero = > self . state . status . submenu = Submenu ::Processes ,
}
} ,
Tab ::Gupax = > self . state . gupax . simple = ! self . state . gupax . simple ,
Tab ::P2pool = > self . state . p2pool . simple = ! self . state . p2pool . simple ,
Tab ::Xmrig = > self . state . xmrig . simple = ! self . state . xmrig . simple ,
_ = > ( ) ,
} ;
2022-12-02 18:15:26 +00:00
}
2022-12-06 03:33:35 +00:00
// Refresh AT LEAST once a second
2022-12-12 19:34:17 +00:00
debug! ( " App | Refreshing frame once per second " ) ;
2022-12-06 03:33:35 +00:00
ctx . request_repaint_after ( SECOND ) ;
2022-12-13 14:39:09 +00:00
// Get P2Pool/XMRig process state.
// These values are checked multiple times so
// might as well check only once here to save
// on a bunch of [.lock().unwrap()]s.
debug! ( " App | Locking and collecting P2Pool state... " ) ;
let p2pool = self . p2pool . lock ( ) . unwrap ( ) ;
let p2pool_is_alive = p2pool . is_alive ( ) ;
let p2pool_is_waiting = p2pool . is_waiting ( ) ;
2022-12-14 22:37:29 +00:00
let p2pool_state = p2pool . state ;
2022-12-13 14:39:09 +00:00
drop ( p2pool ) ;
debug! ( " App | Locking and collecting XMRig state... " ) ;
let xmrig = self . xmrig . lock ( ) . unwrap ( ) ;
let xmrig_is_alive = xmrig . is_alive ( ) ;
let xmrig_is_waiting = xmrig . is_waiting ( ) ;
2022-12-14 22:37:29 +00:00
let xmrig_state = xmrig . state ;
2022-12-13 14:39:09 +00:00
drop ( xmrig ) ;
2022-10-17 02:28:41 +00:00
// This sets the top level Ui dimensions.
// Used as a reference for other uis.
2022-12-12 19:34:17 +00:00
debug! ( " App | Setting width/height " ) ;
2022-11-25 16:59:48 +00:00
CentralPanel ::default ( ) . show ( ctx , | ui | {
let available_width = ui . available_width ( ) ;
if self . width ! = available_width {
self . width = available_width ;
2022-12-02 18:15:26 +00:00
if self . now . elapsed ( ) . as_secs ( ) > 5 {
self . must_resize = true ;
}
2022-11-25 16:59:48 +00:00
} ;
self . height = ui . available_height ( ) ;
} ) ;
// This resizes fonts/buttons/etc globally depending on the width.
// This is separate from the [self.width != available_width] logic above
// because placing [init_text_styles()] above would mean calling it 60x a second
// while the user was readjusting the frame. It's a pretty heavy operation and looks
// buggy when calling it that many times. Looking for a [must_resize] in addtion to
// checking if the user is hovering over the app means that we only have call it once.
2022-12-12 19:34:17 +00:00
debug! ( " App | Checking if we need to resize " ) ;
2022-12-02 18:15:26 +00:00
if self . must_resize & & ctx . is_pointer_over_area ( ) {
self . resizing = true ;
2022-11-25 16:59:48 +00:00
self . must_resize = false ;
}
2022-12-02 18:15:26 +00:00
// This (ab)uses [Area] and [TextEdit] to overlay a full black layer over whatever UI we had before.
// It incrementally becomes more opaque until [self.alpha] >= 250, when we just switch to pure black (no alpha).
// When black, we're safe to [init_text_styles()], and then incrementally go transparent, until we remove the layer.
if self . resizing {
egui ::Area ::new ( " resize_layer " ) . order ( egui ::Order ::Foreground ) . anchor ( egui ::Align2 ::CENTER_CENTER , ( 0.0 , 0.0 ) ) . show ( ctx , | ui | {
if self . alpha < 250 {
egui ::Frame ::none ( ) . fill ( Color32 ::from_rgba_premultiplied ( 0 , 0 , 0 , self . alpha ) ) . show ( ui , | ui | {
ui . add_sized ( [ ui . available_width ( ) + SPACE , ui . available_height ( ) + SPACE ] , egui ::TextEdit ::multiline ( & mut " " ) ) ;
} ) ;
ctx . request_repaint ( ) ;
self . alpha + = 10 ;
} else {
egui ::Frame ::none ( ) . fill ( Color32 ::from_rgb ( 0 , 0 , 0 ) ) . show ( ui , | ui | {
ui . add_sized ( [ ui . available_width ( ) + SPACE , ui . available_height ( ) + SPACE ] , egui ::TextEdit ::multiline ( & mut " " ) ) ;
} ) ;
ctx . request_repaint ( ) ;
info! ( " App | Resizing frame to match new internal resolution: [{}x{}] " , self . width , self . height ) ;
init_text_styles ( ctx , self . width ) ;
self . resizing = false ;
}
} ) ;
} else if self . alpha ! = 0 {
egui ::Area ::new ( " resize_layer " ) . order ( egui ::Order ::Foreground ) . anchor ( egui ::Align2 ::CENTER_CENTER , ( 0.0 , 0.0 ) ) . show ( ctx , | ui | {
egui ::Frame ::none ( ) . fill ( Color32 ::from_rgba_premultiplied ( 0 , 0 , 0 , self . alpha ) ) . show ( ui , | ui | {
ui . add_sized ( [ ui . available_width ( ) + SPACE , ui . available_height ( ) + SPACE ] , egui ::TextEdit ::multiline ( & mut " " ) ) ;
} )
} ) ;
self . alpha - = 10 ;
ctx . request_repaint ( ) ;
}
2022-10-29 13:13:00 +00:00
2022-11-15 21:29:05 +00:00
// If there's an error, display [ErrorState] on the whole screen until user responds
2022-12-12 19:34:17 +00:00
debug! ( " App | Checking if there is an error in [ErrorState] " ) ;
2022-11-15 21:29:05 +00:00
if self . error_state . error {
2022-11-23 04:21:46 +00:00
CentralPanel ::default ( ) . show ( ctx , | ui | {
2022-11-15 21:29:05 +00:00
ui . vertical_centered ( | ui | {
// Set width/height/font
let width = self . width ;
let height = self . height / 4.0 ;
ui . style_mut ( ) . override_text_style = Some ( Name ( " MonospaceLarge " . into ( ) ) ) ;
2022-11-16 19:07:27 +00:00
// Display ferris
use ErrorFerris ::* ;
2022-11-20 02:20:28 +00:00
use ErrorButtons ::* ;
2022-11-16 19:07:27 +00:00
let ferris = match self . error_state . ferris {
2022-11-20 02:20:28 +00:00
Happy = > & self . img . happy ,
2022-12-19 04:57:06 +00:00
Cute = > & self . img . cute ,
2022-11-20 02:20:28 +00:00
Oops = > & self . img . oops ,
2022-11-16 19:07:27 +00:00
Error = > & self . img . error ,
Panic = > & self . img . panic ,
2022-12-07 23:02:08 +00:00
ErrorFerris ::Sudo = > & self . img . sudo ,
2022-11-16 19:07:27 +00:00
} ;
2022-12-19 04:57:06 +00:00
match self . error_state . buttons {
2022-12-23 14:44:20 +00:00
Debug = > ui . add_sized ( [ width , height / 4.0 ] , Label ::new ( " --- Debug Info --- \n \n Press [ESC] to quit " ) ) ,
2022-12-19 04:57:06 +00:00
_ = > ferris . show_max_size ( ui , Vec2 ::new ( width , height ) ) ,
} ;
2022-11-16 19:07:27 +00:00
// Error/Quit screen
match self . error_state . buttons {
StayQuit = > {
2022-11-17 02:14:21 +00:00
let mut text = " " . to_string ( ) ;
2022-12-12 03:01:37 +00:00
if * self . update . lock ( ) . unwrap ( ) . updating . lock ( ) . unwrap ( ) { text = format! ( " {} \n Update is in progress...! Quitting may cause file corruption! " , text ) ; }
2022-12-13 14:39:09 +00:00
if p2pool_is_alive { text = format! ( " {} \n P2Pool is online...! " , text ) ; }
if xmrig_is_alive { text = format! ( " {} \n XMRig is online...! " , text ) ; }
2022-11-17 02:14:21 +00:00
ui . add_sized ( [ width , height ] , Label ::new ( " --- Are you sure you want to quit? --- " ) ) ;
2022-11-16 19:07:27 +00:00
ui . add_sized ( [ width , height ] , Label ::new ( text ) )
} ,
2022-11-20 02:20:28 +00:00
ResetState = > {
ui . add_sized ( [ width , height ] , Label ::new ( format! ( " --- Gupax has encountered an error! --- \n {} " , & self . error_state . msg ) ) ) ;
ui . add_sized ( [ width , height ] , Label ::new ( " Reset Gupax state? (Your settings) " ) )
} ,
ResetNode = > {
ui . add_sized ( [ width , height ] , Label ::new ( format! ( " --- Gupax has encountered an error! --- \n {} " , & self . error_state . msg ) ) ) ;
ui . add_sized ( [ width , height ] , Label ::new ( " Reset the manual node list? " ) )
} ,
2022-12-07 23:02:08 +00:00
ErrorButtons ::Sudo = > {
let text = format! ( " Why does XMRig need admin priviledge? \n {} " , XMRIG_ADMIN_REASON ) ;
let height = height / 4.0 ;
ui . add_sized ( [ width , height ] , Label ::new ( format! ( " --- Gupax needs sudo/admin priviledge for XMRig! --- \n {} " , & self . error_state . msg ) ) ) ;
ui . style_mut ( ) . override_text_style = Some ( Name ( " MonospaceSmall " . into ( ) ) ) ;
ui . add_sized ( [ width / 2.0 , height ] , Label ::new ( text ) ) ;
ui . add_sized ( [ width , height ] , Hyperlink ::from_label_and_url ( " Click here for more info. " , " https://xmrig.com/docs/miner/randomx-optimization-guide " ) )
} ,
2022-12-19 04:57:06 +00:00
Debug = > {
ui . style_mut ( ) . override_text_style = Some ( Monospace ) ;
2022-12-23 14:44:20 +00:00
egui ::Frame ::none ( ) . fill ( DARK_GRAY ) . show ( ui , | ui | {
let width = ui . available_width ( ) ;
let height = ui . available_height ( ) ;
egui ::ScrollArea ::vertical ( ) . max_width ( width ) . max_height ( height ) . auto_shrink ( [ false ; 2 ] ) . show_viewport ( ui , | ui , _ | {
ui . add_sized ( [ width - 20.0 , height ] , TextEdit ::multiline ( & mut self . error_state . msg . as_str ( ) ) ) ;
} ) ;
} ) ;
ui . label ( " " )
2022-12-19 04:57:06 +00:00
} ,
2022-11-17 02:14:21 +00:00
_ = > {
2022-11-20 02:20:28 +00:00
match self . error_state . ferris {
Panic = > ui . add_sized ( [ width , height ] , Label ::new ( " --- Gupax has encountered an un-recoverable error! --- " ) ) ,
Happy = > ui . add_sized ( [ width , height ] , Label ::new ( " --- Success! --- " ) ) ,
_ = > ui . add_sized ( [ width , height ] , Label ::new ( " --- Gupax has encountered an error! --- " ) ) ,
} ;
2022-12-10 03:06:42 +00:00
let height = height / 2.0 ;
// Show GitHub rant link for Windows admin problems.
2022-12-16 03:35:49 +00:00
if cfg! ( windows ) & & self . error_state . buttons = = ErrorButtons ::WindowsAdmin {
ui . add_sized ( [ width , height ] , Hyperlink ::from_label_and_url (
" [Why does Gupax need to be Admin? (on Windows)] " ,
" https://github.com/hinto-janaiyo/gupax/tree/main/src#why-does-gupax-need-to-be-admin-on-windows "
) ) ;
ui . add_sized ( [ width , height ] , Label ::new ( & self . error_state . msg ) )
} else {
ui . add_sized ( [ width , height ] , Label ::new ( & self . error_state . msg ) )
}
2022-11-17 02:14:21 +00:00
} ,
2022-11-16 19:07:27 +00:00
} ;
2022-11-15 21:29:05 +00:00
let height = ui . available_height ( ) ;
2022-11-16 19:07:27 +00:00
2022-11-15 21:29:05 +00:00
match self . error_state . buttons {
YesNo = > {
2022-12-07 23:02:08 +00:00
if ui . add_sized ( [ width , height / 2.0 ] , Button ::new ( " Yes " ) ) . clicked ( ) { self . error_state . reset ( ) }
2022-11-16 19:07:27 +00:00
// If [Esc] was pressed, assume [No]
2022-12-13 14:39:09 +00:00
if key . is_esc ( ) | | ui . add_sized ( [ width , height / 2.0 ] , Button ::new ( " No " ) ) . clicked ( ) { exit ( 0 ) ; }
2022-11-15 21:29:05 +00:00
} ,
2022-11-16 19:07:27 +00:00
StayQuit = > {
// If [Esc] was pressed, assume [Stay]
2022-12-13 14:39:09 +00:00
if key . is_esc ( ) | | ui . add_sized ( [ width , height / 2.0 ] , Button ::new ( " Stay " ) ) . clicked ( ) {
2022-11-16 19:07:27 +00:00
self . error_state = ErrorState ::new ( ) ;
}
2022-12-17 15:09:50 +00:00
if ui . add_sized ( [ width , height / 2.0 ] , Button ::new ( " Quit " ) ) . clicked ( ) {
if self . state . gupax . save_before_quit { self . save_before_quit ( ) ; }
exit ( 0 ) ;
}
2022-11-15 21:29:05 +00:00
} ,
2022-11-20 02:20:28 +00:00
// This code handles the [state.toml/node.toml] resetting, [panic!]'ing if it errors once more
// Another error after this either means an IO error or permission error, which Gupax can't fix.
// [Yes/No] buttons
ResetState = > {
2022-11-23 04:21:46 +00:00
if ui . add_sized ( [ width , height / 2.0 ] , Button ::new ( " Yes " ) ) . clicked ( ) {
2022-11-20 18:31:00 +00:00
match reset_state ( & self . state_path ) {
2022-11-20 02:20:28 +00:00
Ok ( _ ) = > {
2022-11-20 18:31:00 +00:00
match State ::get ( & self . state_path ) {
2022-11-20 02:20:28 +00:00
Ok ( s ) = > {
self . state = s ;
self . og = Arc ::new ( Mutex ::new ( self . state . clone ( ) ) ) ;
2022-11-20 18:31:00 +00:00
self . error_state . set ( " State read OK " , ErrorFerris ::Happy , ErrorButtons ::Okay ) ;
2022-11-20 02:20:28 +00:00
} ,
2022-11-20 18:31:00 +00:00
Err ( e ) = > self . error_state . set ( format! ( " State read fail: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
2022-11-20 02:20:28 +00:00
}
} ,
2022-11-20 18:31:00 +00:00
Err ( e ) = > self . error_state . set ( format! ( " State reset fail: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
2022-11-20 02:20:28 +00:00
} ;
}
2022-12-13 14:39:09 +00:00
if key . is_esc ( ) | | ui . add_sized ( [ width , height / 2.0 ] , Button ::new ( " No " ) ) . clicked ( ) { self . error_state . reset ( ) }
2022-11-20 02:20:28 +00:00
} ,
ResetNode = > {
2022-11-23 04:21:46 +00:00
if ui . add_sized ( [ width , height / 2.0 ] , Button ::new ( " Yes " ) ) . clicked ( ) {
2022-11-20 18:31:00 +00:00
match reset_nodes ( & self . node_path ) {
2022-11-20 02:20:28 +00:00
Ok ( _ ) = > {
2022-11-20 18:31:00 +00:00
match Node ::get ( & self . node_path ) {
2022-11-20 02:20:28 +00:00
Ok ( s ) = > {
self . node_vec = s ;
self . og_node_vec = self . node_vec . clone ( ) ;
2022-11-20 18:31:00 +00:00
self . error_state . set ( " Node read OK " , ErrorFerris ::Happy , ErrorButtons ::Okay ) ;
2022-11-20 02:20:28 +00:00
} ,
2022-11-20 18:31:00 +00:00
Err ( e ) = > self . error_state . set ( format! ( " Node read fail: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
2022-11-20 02:20:28 +00:00
}
} ,
2022-11-20 18:31:00 +00:00
Err ( e ) = > self . error_state . set ( format! ( " Node reset fail: {} " , e ) , ErrorFerris ::Panic , ErrorButtons ::Quit ) ,
2022-11-20 02:20:28 +00:00
} ;
}
2022-12-13 14:39:09 +00:00
if key . is_esc ( ) | | ui . add_sized ( [ width , height / 2.0 ] , Button ::new ( " No " ) ) . clicked ( ) { self . error_state . reset ( ) }
2022-12-07 23:02:08 +00:00
} ,
ErrorButtons ::Sudo = > {
2022-12-08 01:50:14 +00:00
let sudo_width = width / 10.0 ;
2022-12-07 23:02:08 +00:00
let height = ui . available_height ( ) / 4.0 ;
let mut sudo = self . sudo . lock ( ) . unwrap ( ) ;
2022-12-14 03:41:05 +00:00
let hide = sudo . hide ;
2022-12-07 23:02:08 +00:00
ui . style_mut ( ) . override_text_style = Some ( Monospace ) ;
if sudo . testing {
ui . add_sized ( [ width , height ] , Spinner ::new ( ) . size ( height ) ) ;
ui . set_enabled ( false ) ;
} else {
ui . add_sized ( [ width , height ] , Label ::new ( & sudo . msg ) ) ;
}
ui . add_space ( height ) ;
let height = ui . available_height ( ) / 5.0 ;
// Password input box with a hider.
ui . horizontal ( | ui | {
let response = ui . add_sized ( [ sudo_width * 8.0 , height ] , TextEdit ::hint_text ( TextEdit ::singleline ( & mut sudo . pass ) . password ( hide ) , PASSWORD_TEXT ) ) ;
let box_width = ( ui . available_width ( ) / 2.0 ) - 5.0 ;
if ( response . lost_focus ( ) & & ui . input ( ) . key_pressed ( Key ::Enter ) ) | |
ui . add_sized ( [ box_width , height ] , Button ::new ( " Enter " ) ) . on_hover_text ( PASSWORD_ENTER ) . clicked ( ) {
2022-12-13 17:44:57 +00:00
response . request_focus ( ) ;
2022-12-07 23:02:08 +00:00
if ! sudo . testing {
2022-12-08 17:29:38 +00:00
SudoState ::test_sudo ( self . sudo . clone ( ) , & self . helper . clone ( ) , & self . state . xmrig , & self . state . gupax . absolute_xmrig_path ) ;
2022-12-07 23:02:08 +00:00
}
}
let color = if hide { BLACK } else { BRIGHT_YELLOW } ;
if ui . add_sized ( [ box_width , height ] , Button ::new ( RichText ::new ( " 👁 " ) . color ( color ) ) ) . on_hover_text ( PASSWORD_HIDE ) . clicked ( ) { sudo . hide = ! sudo . hide ; }
} ) ;
2022-12-18 01:51:50 +00:00
if ( key . is_esc ( ) & & ! sudo . testing ) | | ui . add_sized ( [ width , height * 4.0 ] , Button ::new ( " Leave " ) ) . on_hover_text ( PASSWORD_LEAVE ) . clicked ( ) { self . error_state . reset ( ) ; } ;
2022-12-08 23:31:20 +00:00
// If [test_sudo()] finished, reset error state.
2022-12-08 17:29:38 +00:00
if sudo . success {
self . error_state . reset ( ) ;
}
2022-11-20 02:20:28 +00:00
} ,
2022-12-23 18:33:40 +00:00
Okay | WindowsAdmin = > if key . is_esc ( ) | | ui . add_sized ( [ width , height ] , Button ::new ( " Okay " ) ) . clicked ( ) { self . error_state . reset ( ) ; } ,
2022-12-23 14:44:20 +00:00
Debug = > if key . is_esc ( ) { self . error_state . reset ( ) ; } ,
2022-11-23 04:21:46 +00:00
Quit = > if ui . add_sized ( [ width , height ] , Button ::new ( " Quit " ) ) . clicked ( ) { exit ( 1 ) ; } ,
2022-11-15 21:29:05 +00:00
}
} ) } ) ;
return
}
2022-12-13 17:44:57 +00:00
// Compare [og == state] & [node_vec/pool_vec] and enable diff if found.
2022-11-15 21:29:05 +00:00
// The struct fields are compared directly because [Version]
// contains Arc<Mutex>'s that cannot be compared easily.
// They don't need to be compared anyway.
2022-12-12 19:34:17 +00:00
debug! ( " App | Checking diff between [og] & [state] " ) ;
2022-11-15 21:29:05 +00:00
let og = self . og . lock ( ) . unwrap ( ) ;
2022-12-27 17:58:46 +00:00
if og . status ! = self . state . status | |
og . gupax ! = self . state . gupax | |
2022-12-13 17:44:57 +00:00
og . p2pool ! = self . state . p2pool | |
og . xmrig ! = self . state . xmrig | |
self . og_node_vec ! = self . node_vec | |
self . og_pool_vec ! = self . pool_vec {
update: save [Version] to state, use runtime [og: State]
[og: State] is now completely wrapped in an [Arc<Mutex>] so that
when the update is done, it can [.lock()] the CURRENT runtime
settings of the user and save to [gupax.toml] instead of using an
old copy that was given to it at the beginning of the thread.
In practice, this means users can change settings around during
an update and the update finishing and saving to disk won't be
using their old settings, but the current ones. Wrapping all of
[og: State] within in [Arc<Mutex>] might be overkill compared to
message channels but [State] really is just a few [bool]'s, [u*],
and small [String]'s, so it's not much data.
To bypass a deadlock when comparing [og == state] every frame,
[og]'s struct fields get cloned every frame into separate
variables, then it gets compared. This is also pretty stupid, but
again, the data being cloned is so tiny that it doesn't seem to
slow anything down.
2022-11-02 17:58:44 +00:00
self . diff = true ;
} else {
self . diff = false ;
}
2022-11-15 21:29:05 +00:00
drop ( og ) ;
update: save [Version] to state, use runtime [og: State]
[og: State] is now completely wrapped in an [Arc<Mutex>] so that
when the update is done, it can [.lock()] the CURRENT runtime
settings of the user and save to [gupax.toml] instead of using an
old copy that was given to it at the beginning of the thread.
In practice, this means users can change settings around during
an update and the update finishing and saving to disk won't be
using their old settings, but the current ones. Wrapping all of
[og: State] within in [Arc<Mutex>] might be overkill compared to
message channels but [State] really is just a few [bool]'s, [u*],
and small [String]'s, so it's not much data.
To bypass a deadlock when comparing [og == state] every frame,
[og]'s struct fields get cloned every frame into separate
variables, then it gets compared. This is also pretty stupid, but
again, the data being cloned is so tiny that it doesn't seem to
slow anything down.
2022-11-02 17:58:44 +00:00
2022-10-01 16:58:22 +00:00
// Top: Tabs
2022-12-12 19:34:17 +00:00
debug! ( " App | Rendering TOP tabs " ) ;
2022-11-23 04:21:46 +00:00
TopBottomPanel ::top ( " top " ) . show ( ctx , | ui | {
2022-10-28 19:45:13 +00:00
let width = ( self . width - ( SPACE * 10.0 ) ) / 5.0 ;
2022-11-14 02:56:25 +00:00
let height = self . height / 12.0 ;
2022-12-11 20:49:01 +00:00
ui . add_space ( 4.0 ) ;
ui . horizontal ( | ui | {
let style = ui . style_mut ( ) ;
style . override_text_style = Some ( Name ( " Tab " . into ( ) ) ) ;
style . visuals . widgets . inactive . fg_stroke . color = Color32 ::from_rgb ( 100 , 100 , 100 ) ;
style . visuals . selection . bg_fill = Color32 ::from_rgb ( 255 , 120 , 120 ) ;
style . visuals . selection . stroke = Stroke { width : 5.0 , color : Color32 ::from_rgb ( 255 , 255 , 255 ) } ;
if ui . add_sized ( [ width , height ] , SelectableLabel ::new ( self . tab = = Tab ::About , " About " ) ) . clicked ( ) { self . tab = Tab ::About ; }
ui . separator ( ) ;
if ui . add_sized ( [ width , height ] , SelectableLabel ::new ( self . tab = = Tab ::Status , " Status " ) ) . clicked ( ) { self . tab = Tab ::Status ; }
ui . separator ( ) ;
if ui . add_sized ( [ width , height ] , SelectableLabel ::new ( self . tab = = Tab ::Gupax , " Gupax " ) ) . clicked ( ) { self . tab = Tab ::Gupax ; }
ui . separator ( ) ;
if ui . add_sized ( [ width , height ] , SelectableLabel ::new ( self . tab = = Tab ::P2pool , " P2Pool " ) ) . clicked ( ) { self . tab = Tab ::P2pool ; }
ui . separator ( ) ;
if ui . add_sized ( [ width , height ] , SelectableLabel ::new ( self . tab = = Tab ::Xmrig , " XMRig " ) ) . clicked ( ) { self . tab = Tab ::Xmrig ; }
2022-10-01 16:58:22 +00:00
} ) ;
2022-12-11 20:49:01 +00:00
ui . add_space ( 4.0 ) ;
2022-10-17 02:28:41 +00:00
} ) ;
2022-10-01 16:58:22 +00:00
// Bottom: app info + state/process buttons
2022-12-12 19:34:17 +00:00
debug! ( " App | Rendering BOTTOM bar " ) ;
2022-11-23 04:21:46 +00:00
TopBottomPanel ::bottom ( " bottom " ) . show ( ctx , | ui | {
2022-11-14 02:56:25 +00:00
let height = self . height / 20.0 ;
2022-10-01 16:58:22 +00:00
ui . style_mut ( ) . override_text_style = Some ( Name ( " Bottom " . into ( ) ) ) ;
ui . horizontal ( | ui | {
ui . group ( | ui | {
2022-11-11 02:20:31 +00:00
let width = ( ( self . width / 2.0 ) / 4.0 ) - ( SPACE * 2.0 ) ;
2022-11-27 20:11:00 +00:00
// [Gupax Version]
// Is yellow if the user updated and should (but isn't required to) restart.
match * self . restart . lock ( ) . unwrap ( ) {
2022-12-07 03:01:36 +00:00
Restart ::Yes = > ui . add_sized ( [ width , height ] , Label ::new ( RichText ::new ( & self . name_version ) . color ( YELLOW ) ) ) . on_hover_text ( GUPAX_SHOULD_RESTART ) ,
_ = > ui . add_sized ( [ width , height ] , Label ::new ( & self . name_version ) ) . on_hover_text ( GUPAX_UP_TO_DATE ) ,
2022-11-27 20:11:00 +00:00
} ;
2022-10-01 16:58:22 +00:00
ui . separator ( ) ;
2022-11-27 20:11:00 +00:00
// [OS]
2022-12-10 03:06:42 +00:00
// Check if admin for windows.
// Unix SHOULDN'T be running as root, and the check is done when
// [App] is initialized, so no reason to check here.
#[ cfg(target_os = " windows " ) ]
if self . admin {
ui . add_sized ( [ width , height ] , Label ::new ( self . os ) ) ;
} else {
ui . add_sized ( [ width , height ] , Label ::new ( RichText ::new ( self . os ) . color ( RED ) ) ) . on_hover_text ( WINDOWS_NOT_ADMIN ) ;
}
#[ cfg(target_family = " unix " ) ]
2022-10-01 16:58:22 +00:00
ui . add_sized ( [ width , height ] , Label ::new ( self . os ) ) ;
ui . separator ( ) ;
2022-11-27 20:11:00 +00:00
// [P2Pool/XMRig] Status
2022-12-06 03:33:35 +00:00
use ProcessState ::* ;
2022-12-13 14:39:09 +00:00
match p2pool_state {
2022-12-06 03:33:35 +00:00
Alive = > ui . add_sized ( [ width , height ] , Label ::new ( RichText ::new ( " P2Pool ⏺ " ) . color ( GREEN ) ) ) . on_hover_text ( P2POOL_ALIVE ) ,
Dead = > ui . add_sized ( [ width , height ] , Label ::new ( RichText ::new ( " P2Pool ⏺ " ) . color ( GRAY ) ) ) . on_hover_text ( P2POOL_DEAD ) ,
Failed = > ui . add_sized ( [ width , height ] , Label ::new ( RichText ::new ( " P2Pool ⏺ " ) . color ( RED ) ) ) . on_hover_text ( P2POOL_FAILED ) ,
Middle | Waiting = > ui . add_sized ( [ width , height ] , Label ::new ( RichText ::new ( " P2Pool ⏺ " ) . color ( YELLOW ) ) ) . on_hover_text ( P2POOL_MIDDLE ) ,
} ;
2022-10-01 16:58:22 +00:00
ui . separator ( ) ;
2022-12-13 14:39:09 +00:00
match xmrig_state {
2022-12-06 03:33:35 +00:00
Alive = > ui . add_sized ( [ width , height ] , Label ::new ( RichText ::new ( " XMRig ⏺ " ) . color ( GREEN ) ) ) . on_hover_text ( XMRIG_ALIVE ) ,
Dead = > ui . add_sized ( [ width , height ] , Label ::new ( RichText ::new ( " XMRig ⏺ " ) . color ( GRAY ) ) ) . on_hover_text ( XMRIG_DEAD ) ,
Failed = > ui . add_sized ( [ width , height ] , Label ::new ( RichText ::new ( " XMRig ⏺ " ) . color ( RED ) ) ) . on_hover_text ( XMRIG_FAILED ) ,
Middle | Waiting = > ui . add_sized ( [ width , height ] , Label ::new ( RichText ::new ( " XMRig ⏺ " ) . color ( YELLOW ) ) ) . on_hover_text ( XMRIG_MIDDLE ) ,
} ;
2022-10-01 16:58:22 +00:00
} ) ;
2022-11-23 04:21:46 +00:00
// [Save/Reset]
ui . with_layout ( Layout ::right_to_left ( Align ::RIGHT ) , | ui | {
2022-12-27 21:29:12 +00:00
let width = ( ui . available_width ( ) / 3.0 ) - ( SPACE * 3.0 ) ;
2022-11-11 02:20:31 +00:00
ui . group ( | ui | {
2022-11-15 21:29:05 +00:00
ui . set_enabled ( self . diff ) ;
2022-11-11 02:20:31 +00:00
let width = width / 2.0 ;
2022-12-17 03:38:46 +00:00
if key . is_r ( ) & & ! wants_input & & self . diff | | ui . add_sized ( [ width , height ] , Button ::new ( " Reset " ) ) . on_hover_text ( " Reset changes " ) . clicked ( ) {
2022-11-15 21:29:05 +00:00
let og = self . og . lock ( ) . unwrap ( ) . clone ( ) ;
2022-12-27 17:58:46 +00:00
self . state . status = og . status ;
2022-11-15 21:29:05 +00:00
self . state . gupax = og . gupax ;
self . state . p2pool = og . p2pool ;
self . state . xmrig = og . xmrig ;
self . node_vec = self . og_node_vec . clone ( ) ;
2022-11-23 04:21:46 +00:00
self . pool_vec = self . og_pool_vec . clone ( ) ;
2022-11-11 02:20:31 +00:00
}
2022-12-17 03:38:46 +00:00
if key . is_s ( ) & & ! wants_input & & self . diff | | ui . add_sized ( [ width , height ] , Button ::new ( " Save " ) ) . on_hover_text ( " Save changes " ) . clicked ( ) {
2022-11-20 18:31:00 +00:00
match State ::save ( & mut self . state , & self . state_path ) {
2022-11-20 02:20:28 +00:00
Ok ( _ ) = > {
let mut og = self . og . lock ( ) . unwrap ( ) ;
2022-12-27 17:58:46 +00:00
og . status = self . state . status . clone ( ) ;
2022-11-20 02:20:28 +00:00
og . gupax = self . state . gupax . clone ( ) ;
og . p2pool = self . state . p2pool . clone ( ) ;
og . xmrig = self . state . xmrig . clone ( ) ;
} ,
Err ( e ) = > {
2022-11-20 18:31:00 +00:00
self . error_state . set ( format! ( " State file: {} " , e ) , ErrorFerris ::Error , ErrorButtons ::Okay ) ;
2022-11-20 02:20:28 +00:00
} ,
} ;
2022-12-13 17:44:57 +00:00
match Node ::save ( & self . node_vec , & self . node_path ) {
2022-11-20 02:20:28 +00:00
Ok ( _ ) = > self . og_node_vec = self . node_vec . clone ( ) ,
2022-11-20 18:31:00 +00:00
Err ( e ) = > self . error_state . set ( format! ( " Node list: {} " , e ) , ErrorFerris ::Error , ErrorButtons ::Okay ) ,
2022-11-20 02:20:28 +00:00
} ;
2022-12-13 17:44:57 +00:00
match Pool ::save ( & self . pool_vec , & self . pool_path ) {
2022-11-23 04:21:46 +00:00
Ok ( _ ) = > self . og_pool_vec = self . pool_vec . clone ( ) ,
Err ( e ) = > self . error_state . set ( format! ( " Pool list: {} " , e ) , ErrorFerris ::Error , ErrorButtons ::Okay ) ,
} ;
2022-10-01 16:58:22 +00:00
}
} ) ;
2022-11-23 04:21:46 +00:00
// [Simple/Advanced] + [Start/Stop/Restart]
2022-11-11 02:20:31 +00:00
match self . tab {
2022-12-27 21:29:12 +00:00
Tab ::Status = > {
ui . group ( | ui | {
let width = ( ui . available_width ( ) / 3.0 ) - 14.25 ;
if ui . add_sized ( [ width , height ] , SelectableLabel ::new ( self . state . status . submenu = = Submenu ::Monero , " Monero " ) ) . on_hover_text ( STATUS_SUBMENU_MONERO ) . clicked ( ) {
self . state . status . submenu = Submenu ::Monero ;
}
ui . separator ( ) ;
if ui . add_sized ( [ width , height ] , SelectableLabel ::new ( self . state . status . submenu = = Submenu ::P2pool , " P2Pool " ) ) . on_hover_text ( STATUS_SUBMENU_P2POOL ) . clicked ( ) {
self . state . status . submenu = Submenu ::P2pool ;
}
ui . separator ( ) ;
if ui . add_sized ( [ width , height ] , SelectableLabel ::new ( self . state . status . submenu = = Submenu ::Processes , " Processes " ) ) . on_hover_text ( STATUS_SUBMENU_PROCESSES ) . clicked ( ) {
self . state . status . submenu = Submenu ::Processes ;
}
} ) ;
} ,
2022-11-23 04:21:46 +00:00
Tab ::Gupax = > {
ui . group ( | ui | {
2022-12-27 21:29:12 +00:00
let width = ( ui . available_width ( ) / 2.0 ) - 10.5 ;
2022-11-23 04:21:46 +00:00
if ui . add_sized ( [ width , height ] , SelectableLabel ::new ( ! self . state . gupax . simple , " Advanced " ) ) . on_hover_text ( GUPAX_ADVANCED ) . clicked ( ) {
self . state . gupax . simple = false ;
}
ui . separator ( ) ;
if ui . add_sized ( [ width , height ] , SelectableLabel ::new ( self . state . gupax . simple , " Simple " ) ) . on_hover_text ( GUPAX_SIMPLE ) . clicked ( ) {
self . state . gupax . simple = true ;
}
} ) ;
} ,
2022-11-11 02:20:31 +00:00
Tab ::P2pool = > {
ui . group ( | ui | {
let width = width / 1.5 ;
2022-11-23 04:21:46 +00:00
if ui . add_sized ( [ width , height ] , SelectableLabel ::new ( ! self . state . p2pool . simple , " Advanced " ) ) . on_hover_text ( P2POOL_ADVANCED ) . clicked ( ) {
2022-11-11 02:20:31 +00:00
self . state . p2pool . simple = false ;
}
ui . separator ( ) ;
2022-11-23 04:21:46 +00:00
if ui . add_sized ( [ width , height ] , SelectableLabel ::new ( self . state . p2pool . simple , " Simple " ) ) . on_hover_text ( P2POOL_SIMPLE ) . clicked ( ) {
2022-11-11 02:20:31 +00:00
self . state . p2pool . simple = true ;
}
} ) ;
ui . group ( | ui | {
let width = ( ui . available_width ( ) / 3.0 ) - 5.0 ;
2022-12-13 14:39:09 +00:00
if p2pool_is_waiting {
2022-12-06 03:33:35 +00:00
ui . add_enabled_ui ( false , | ui | {
2022-12-23 16:41:43 +00:00
ui . add_sized ( [ width , height ] , Button ::new ( " ⟲ " ) ) . on_disabled_hover_text ( P2POOL_MIDDLE ) ;
ui . add_sized ( [ width , height ] , Button ::new ( " ⏹ " ) ) . on_disabled_hover_text ( P2POOL_MIDDLE ) ;
ui . add_sized ( [ width , height ] , Button ::new ( " ▶ " ) ) . on_disabled_hover_text ( P2POOL_MIDDLE ) ;
2022-12-06 03:33:35 +00:00
} ) ;
2022-12-13 14:39:09 +00:00
} else if p2pool_is_alive {
2022-12-16 14:47:16 +00:00
if key . is_up ( ) & & ! wants_input | | ui . add_sized ( [ width , height ] , Button ::new ( " ⟲ " ) ) . on_hover_text ( " Restart P2Pool " ) . clicked ( ) {
2022-12-06 22:48:48 +00:00
Helper ::restart_p2pool ( & self . helper , & self . state . p2pool , & self . state . gupax . absolute_p2pool_path ) ;
2022-12-06 03:33:35 +00:00
}
2022-12-16 14:47:16 +00:00
if key . is_down ( ) & & ! wants_input | | ui . add_sized ( [ width , height ] , Button ::new ( " ⏹ " ) ) . on_hover_text ( " Stop P2Pool " ) . clicked ( ) {
2022-12-06 03:33:35 +00:00
Helper ::stop_p2pool ( & self . helper ) ;
}
ui . add_enabled_ui ( false , | ui | {
2022-12-11 02:48:25 +00:00
ui . add_sized ( [ width , height ] , Button ::new ( " ▶ " ) ) . on_disabled_hover_text ( " Start P2Pool " ) ;
2022-12-06 03:33:35 +00:00
} ) ;
} else {
ui . add_enabled_ui ( false , | ui | {
2022-12-11 02:48:25 +00:00
ui . add_sized ( [ width , height ] , Button ::new ( " ⟲ " ) ) . on_disabled_hover_text ( " Restart P2Pool " ) ;
ui . add_sized ( [ width , height ] , Button ::new ( " ⏹ " ) ) . on_disabled_hover_text ( " Stop P2Pool " ) ;
2022-12-06 03:33:35 +00:00
} ) ;
2022-12-09 01:24:37 +00:00
// Check if address is okay before allowing to start.
let mut text = String ::new ( ) ;
2022-12-13 17:51:32 +00:00
let mut ui_enabled = true ;
2022-12-09 01:24:37 +00:00
if ! Regexes ::addr_ok ( & self . regex , & self . state . p2pool . address ) {
2022-12-13 17:51:32 +00:00
ui_enabled = false ;
2022-12-23 16:41:43 +00:00
text = format! ( " Error: {} " , P2POOL_ADDRESS ) ;
2022-12-16 14:47:16 +00:00
} else if ! Gupax ::path_is_file ( & self . state . gupax . p2pool_path ) {
2022-12-13 17:51:32 +00:00
ui_enabled = false ;
2022-12-23 16:41:43 +00:00
text = format! ( " Error: {} " , P2POOL_PATH_NOT_FILE ) ;
2022-12-16 14:47:16 +00:00
} else if ! crate ::update ::check_p2pool_path ( & self . state . gupax . p2pool_path ) {
ui_enabled = false ;
2022-12-23 16:41:43 +00:00
text = format! ( " Error: {} " , P2POOL_PATH_NOT_VALID ) ;
2022-12-09 01:24:37 +00:00
}
2022-12-13 17:51:32 +00:00
ui . set_enabled ( ui_enabled ) ;
2022-12-23 16:41:43 +00:00
let color = if ui_enabled { GREEN } else { RED } ;
if ( ui_enabled & & key . is_up ( ) & & ! wants_input ) | | ui . add_sized ( [ width , height ] , Button ::new ( RichText ::new ( " ▶ " ) . color ( color ) ) ) . on_hover_text ( " Start P2Pool " ) . on_disabled_hover_text ( text ) . clicked ( ) {
2022-12-06 22:48:48 +00:00
Helper ::start_p2pool ( & self . helper , & self . state . p2pool , & self . state . gupax . absolute_p2pool_path ) ;
2022-12-06 03:33:35 +00:00
}
2022-11-11 02:20:31 +00:00
}
} ) ;
2022-11-23 04:21:46 +00:00
} ,
2022-11-11 02:20:31 +00:00
Tab ::Xmrig = > {
ui . group ( | ui | {
let width = width / 1.5 ;
2022-11-23 04:21:46 +00:00
if ui . add_sized ( [ width , height ] , SelectableLabel ::new ( ! self . state . xmrig . simple , " Advanced " ) ) . on_hover_text ( XMRIG_ADVANCED ) . clicked ( ) {
2022-11-11 02:20:31 +00:00
self . state . xmrig . simple = false ;
}
ui . separator ( ) ;
2022-11-23 04:21:46 +00:00
if ui . add_sized ( [ width , height ] , SelectableLabel ::new ( self . state . xmrig . simple , " Simple " ) ) . on_hover_text ( XMRIG_SIMPLE ) . clicked ( ) {
2022-11-11 02:20:31 +00:00
self . state . xmrig . simple = true ;
}
} ) ;
ui . group ( | ui | {
let width = ( ui . available_width ( ) / 3.0 ) - 5.0 ;
2022-12-13 14:39:09 +00:00
if xmrig_is_waiting {
2022-12-08 17:29:38 +00:00
ui . add_enabled_ui ( false , | ui | {
2022-12-23 16:41:43 +00:00
ui . add_sized ( [ width , height ] , Button ::new ( " ⟲ " ) ) . on_disabled_hover_text ( XMRIG_MIDDLE ) ;
ui . add_sized ( [ width , height ] , Button ::new ( " ⏹ " ) ) . on_disabled_hover_text ( XMRIG_MIDDLE ) ;
ui . add_sized ( [ width , height ] , Button ::new ( " ▶ " ) ) . on_disabled_hover_text ( XMRIG_MIDDLE ) ;
2022-12-08 17:29:38 +00:00
} ) ;
2022-12-13 14:39:09 +00:00
} else if xmrig_is_alive {
2022-12-16 14:47:16 +00:00
if key . is_up ( ) & & ! wants_input | | ui . add_sized ( [ width , height ] , Button ::new ( " ⟲ " ) ) . on_hover_text ( " Restart XMRig " ) . clicked ( ) {
2022-12-11 01:55:44 +00:00
if cfg! ( windows ) {
Helper ::restart_xmrig ( & self . helper , & self . state . xmrig , & self . state . gupax . absolute_xmrig_path , Arc ::clone ( & self . sudo ) ) ;
} else {
self . sudo . lock ( ) . unwrap ( ) . signal = ProcessSignal ::Restart ;
self . error_state . ask_sudo ( & self . sudo ) ;
}
2022-12-06 03:33:35 +00:00
}
2022-12-16 14:47:16 +00:00
if key . is_down ( ) & & ! wants_input | | ui . add_sized ( [ width , height ] , Button ::new ( " ⏹ " ) ) . on_hover_text ( " Stop XMRig " ) . clicked ( ) {
2022-12-11 01:55:44 +00:00
if cfg! ( target_os = " macos " ) {
self . sudo . lock ( ) . unwrap ( ) . signal = ProcessSignal ::Stop ;
self . error_state . ask_sudo ( & self . sudo ) ;
} else {
Helper ::stop_xmrig ( & self . helper ) ;
}
2022-12-06 03:33:35 +00:00
}
2022-11-11 02:20:31 +00:00
ui . add_enabled_ui ( false , | ui | {
2022-12-11 02:48:25 +00:00
ui . add_sized ( [ width , height ] , Button ::new ( " ▶ " ) ) . on_disabled_hover_text ( " Start XMRig " ) ;
2022-11-11 02:20:31 +00:00
} ) ;
} else {
ui . add_enabled_ui ( false , | ui | {
2022-12-11 02:48:25 +00:00
ui . add_sized ( [ width , height ] , Button ::new ( " ⟲ " ) ) . on_disabled_hover_text ( " Restart XMRig " ) ;
ui . add_sized ( [ width , height ] , Button ::new ( " ⏹ " ) ) . on_disabled_hover_text ( " Stop XMRig " ) ;
2022-11-11 02:20:31 +00:00
} ) ;
2022-12-09 01:24:37 +00:00
let mut text = String ::new ( ) ;
2022-12-13 17:51:32 +00:00
let mut ui_enabled = true ;
2022-12-16 14:47:16 +00:00
if ! Gupax ::path_is_file ( & self . state . gupax . xmrig_path ) {
ui_enabled = false ;
2022-12-23 16:41:43 +00:00
text = format! ( " Error: {} " , XMRIG_PATH_NOT_FILE ) ;
2022-12-16 14:47:16 +00:00
} else if ! crate ::update ::check_xmrig_path ( & self . state . gupax . xmrig_path ) {
2022-12-13 17:51:32 +00:00
ui_enabled = false ;
2022-12-23 16:41:43 +00:00
text = format! ( " Error: {} " , XMRIG_PATH_NOT_VALID ) ;
2022-12-09 01:24:37 +00:00
}
2022-12-13 17:51:32 +00:00
ui . set_enabled ( ui_enabled ) ;
2022-12-23 16:41:43 +00:00
let color = if ui_enabled { GREEN } else { RED } ;
2022-12-10 02:00:33 +00:00
#[ cfg(target_os = " windows " ) ]
2022-12-23 16:41:43 +00:00
if ( ui_enabled & & key . is_up ( ) & & ! wants_input ) | | ui . add_sized ( [ width , height ] , Button ::new ( RichText ::new ( " ▶ " ) . color ( color ) ) ) . on_hover_text ( " Start XMRig " ) . on_disabled_hover_text ( text ) . clicked ( ) {
2022-12-10 02:00:33 +00:00
Helper ::start_xmrig ( & self . helper , & self . state . xmrig , & self . state . gupax . absolute_xmrig_path , Arc ::clone ( & self . sudo ) ) ;
}
#[ cfg(target_family = " unix " ) ]
2022-12-23 16:41:43 +00:00
if ( ui_enabled & & key . is_up ( ) & & ! wants_input ) | | ui . add_sized ( [ width , height ] , Button ::new ( RichText ::new ( " ▶ " ) . color ( color ) ) ) . on_hover_text ( " Start XMRig " ) . on_disabled_hover_text ( text ) . clicked ( ) {
2022-12-08 23:31:20 +00:00
self . sudo . lock ( ) . unwrap ( ) . signal = ProcessSignal ::Start ;
2022-12-08 01:34:21 +00:00
self . error_state . ask_sudo ( & self . sudo ) ;
2022-12-06 03:33:35 +00:00
}
2022-11-11 02:20:31 +00:00
}
} ) ;
2022-11-23 04:21:46 +00:00
} ,
2022-11-11 02:20:31 +00:00
_ = > ( ) ,
}
2022-10-17 02:28:41 +00:00
} ) ;
2022-11-11 02:20:31 +00:00
} ) ;
} ) ;
2022-10-17 00:36:58 +00:00
2022-10-27 03:15:56 +00:00
// Middle panel, contents of the [Tab]
2022-12-12 19:34:17 +00:00
debug! ( " App | Rendering CENTRAL_PANEL (tab contents) " ) ;
2022-11-23 04:21:46 +00:00
CentralPanel ::default ( ) . show ( ctx , | ui | {
2022-10-27 03:15:56 +00:00
// This sets the Ui dimensions after Top/Bottom are filled
self . width = ui . available_width ( ) ;
self . height = ui . available_height ( ) ;
2022-11-23 04:21:46 +00:00
ui . style_mut ( ) . override_text_style = Some ( TextStyle ::Body ) ;
2022-10-27 03:15:56 +00:00
match self . tab {
Tab ::About = > {
2022-12-12 19:34:17 +00:00
debug! ( " App | Entering [About] Tab " ) ;
2022-12-19 04:57:06 +00:00
// If [D], show some debug info with [ErrorState]
if key . is_d ( ) {
2022-12-23 14:44:20 +00:00
debug! ( " App | Entering [Debug Info] " ) ;
2022-12-19 04:57:06 +00:00
#[ cfg(feature = " distro " ) ]
let distro = true ;
#[ cfg(not(feature = " distro " )) ]
let distro = false ;
2022-12-27 14:22:46 +00:00
let p2pool_gui_len = self . p2pool_api . lock ( ) . unwrap ( ) . output . len ( ) ;
let xmrig_gui_len = self . xmrig_api . lock ( ) . unwrap ( ) . output . len ( ) ;
2022-12-19 04:57:06 +00:00
let debug_info = format! (
" Gupax version: {} \n
Bundled P2Pool version : { } \ n
Bundled XMRig version : { } \ n
Gupax uptime : { } seconds \ n
2022-12-23 14:44:20 +00:00
Selected resolution : { } x { } \ n
2022-12-19 04:57:06 +00:00
Internal resolution : { } x { } \ n
Operating system : { } \ n
Max detected threads : { } \ n
Gupax PID : { } \ n
State diff : { } \ n
2022-12-23 14:44:20 +00:00
Node list length : { } \ n
Pool list length : { } \ n
2022-12-19 04:57:06 +00:00
Admin privilege : { } \ n
Release build : { } \ n
Debug build : { } \ n
Distro build : { } \ n
Build commit : { } \ n
OS Data PATH : { } \ n
Gupax PATH : { } \ n
P2Pool PATH : { } \ n
2022-12-23 14:44:20 +00:00
XMRig PATH : { } \ n
P2Pool console byte length : { } \ n
2022-12-23 18:33:40 +00:00
XMRig console byte length : { } \ n
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - P2POOL IMAGE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{ :#? } \ n
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - XMRIG IMAGE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{ :#? } \ n
2022-12-27 14:22:46 +00:00
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - P2POOL GUI API - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{ :#? } \ n
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - XMRIG GUI API - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{ :#? } \ n
2022-12-23 18:33:40 +00:00
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - WORKING STATE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{ :#? } \ n
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ORIGINAL STATE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2022-12-23 14:44:20 +00:00
{ :#? } " ,
2022-12-19 04:57:06 +00:00
GUPAX_VERSION ,
P2POOL_VERSION ,
XMRIG_VERSION ,
self . now . elapsed ( ) . as_secs_f32 ( ) ,
2022-12-23 14:44:20 +00:00
self . state . gupax . selected_width as u16 ,
self . state . gupax . selected_height as u16 ,
2022-12-19 04:57:06 +00:00
self . width as u16 ,
self . height as u16 ,
OS_NAME ,
self . max_threads ,
self . pid ,
self . diff ,
2022-12-23 14:44:20 +00:00
self . node_vec . len ( ) ,
self . pool_vec . len ( ) ,
2022-12-19 04:57:06 +00:00
self . admin ,
! cfg! ( debug_assertions ) ,
cfg! ( debug_assertions ) ,
distro ,
COMMIT ,
self . os_data_path . display ( ) ,
self . exe ,
self . state . gupax . absolute_p2pool_path . display ( ) ,
self . state . gupax . absolute_xmrig_path . display ( ) ,
2022-12-27 14:22:46 +00:00
p2pool_gui_len ,
xmrig_gui_len ,
2022-12-23 18:33:40 +00:00
self . p2pool_img . lock ( ) . unwrap ( ) ,
self . xmrig_img . lock ( ) . unwrap ( ) ,
2022-12-27 14:22:46 +00:00
self . p2pool_api . lock ( ) . unwrap ( ) ,
self . xmrig_api . lock ( ) . unwrap ( ) ,
2022-12-23 14:44:20 +00:00
self . state ,
self . og . lock ( ) . unwrap ( ) ,
2022-12-19 04:57:06 +00:00
) ;
self . error_state . set ( debug_info , ErrorFerris ::Cute , ErrorButtons ::Debug ) ;
}
2022-12-13 14:39:09 +00:00
let width = self . width ;
let height = self . height / 30.0 ;
let max_height = self . height ;
2022-10-17 02:28:41 +00:00
ui . add_space ( 10.0 ) ;
ui . vertical_centered ( | ui | {
2022-12-13 14:39:09 +00:00
ui . set_max_height ( max_height ) ;
// Display [Gupax] banner
2022-12-17 18:42:04 +00:00
let link_width = width / 15.0 ;
2022-12-13 14:39:09 +00:00
self . img . banner . show_max_size ( ui , Vec2 ::new ( width , height * 3.0 ) ) ;
ui . add_sized ( [ width , height ] , Label ::new ( " Gupax is a cross-platform GUI for mining " ) ) ;
2022-12-17 18:42:04 +00:00
ui . add_sized ( [ link_width , height ] , Hyperlink ::from_label_and_url ( " [Monero] " , " https://www.github.com/monero-project/monero " ) ) ;
2022-12-13 14:39:09 +00:00
ui . add_sized ( [ width , height ] , Label ::new ( " on " ) ) ;
2022-12-17 18:42:04 +00:00
ui . add_sized ( [ link_width , height ] , Hyperlink ::from_label_and_url ( " [P2Pool] " , " https://www.github.com/SChernykh/p2pool " ) ) ;
2022-12-13 14:39:09 +00:00
ui . add_sized ( [ width , height ] , Label ::new ( " using " ) ) ;
2022-12-17 18:42:04 +00:00
ui . add_sized ( [ link_width , height ] , Hyperlink ::from_label_and_url ( " [XMRig] " , " https://www.github.com/xmrig/xmrig " ) ) ;
2022-12-13 14:39:09 +00:00
ui . add_space ( SPACE * 3.0 ) ;
ui . style_mut ( ) . override_text_style = Some ( Monospace ) ;
ui . add_sized ( [ width , height ] , Label ::new ( KEYBOARD_SHORTCUTS ) ) ;
ui . style_mut ( ) . override_text_style = Some ( Body ) ;
ui . add_space ( SPACE * 3.0 ) ;
ui . add_sized ( [ width , height ] , Label ::new ( " egui is licensed under MIT & Apache-2.0 " ) ) ;
ui . add_sized ( [ width , height ] , Label ::new ( " Gupax, P2Pool, and XMRig are licensed under GPLv3 " ) ) ;
2022-12-14 22:37:29 +00:00
if cfg! ( debug_assertions ) { ui . label ( format! ( " Gupax is running in debug mode - {} " , self . now . elapsed ( ) . as_secs_f64 ( ) ) ) ; }
2022-10-17 02:28:41 +00:00
} ) ;
2022-10-27 03:15:56 +00:00
}
Tab ::Status = > {
2022-12-12 19:34:17 +00:00
debug! ( " App | Entering [Status] Tab " ) ;
2022-12-27 21:29:12 +00:00
crate ::disk ::Status ::show ( & mut self . state . status , & self . pub_sys , & self . p2pool_api , & self . xmrig_api , & self . p2pool_img , & self . xmrig_img , p2pool_is_alive , xmrig_is_alive , self . max_threads , self . width , self . height , ctx , ui ) ;
2022-10-27 03:15:56 +00:00
}
Tab ::Gupax = > {
2022-12-12 19:34:17 +00:00
debug! ( " App | Entering [Gupax] Tab " ) ;
2022-12-27 17:58:46 +00:00
crate ::disk ::Gupax ::show ( & mut self . state . gupax , & self . og , & self . state_path , & self . update , & self . file_window , & mut self . error_state , & self . restart , self . width , self . height , frame , ctx , ui ) ;
2022-10-27 03:15:56 +00:00
}
Tab ::P2pool = > {
2022-12-12 19:34:17 +00:00
debug! ( " App | Entering [P2Pool] Tab " ) ;
2022-12-27 17:58:46 +00:00
crate ::disk ::P2pool ::show ( & mut self . state . p2pool , & mut self . node_vec , & self . og , & self . ping , & self . regex , & self . p2pool , & self . p2pool_api , & mut self . p2pool_stdin , self . width , self . height , ctx , ui ) ;
2022-10-27 03:15:56 +00:00
}
Tab ::Xmrig = > {
2022-12-12 19:34:17 +00:00
debug! ( " App | Entering [XMRig] Tab " ) ;
2022-12-27 17:58:46 +00:00
crate ::disk ::Xmrig ::show ( & mut self . state . xmrig , & mut self . pool_vec , & self . regex , & self . xmrig , & self . xmrig_api , & mut self . xmrig_stdin , self . width , self . height , ctx , ui ) ;
2022-10-27 03:15:56 +00:00
}
}
2022-10-01 16:58:22 +00:00
} ) ;
}
}
2022-12-17 22:17:26 +00:00
//---------------------------------------------------------------------------------------------------- TESTS
#[ cfg(test) ]
mod test {
#[ test ]
fn build_regex ( ) {
2022-12-18 01:51:50 +00:00
use regex ::Regex ;
let r = crate ::Regexes ::new ( ) ;
assert! ( Regex ::is_match ( & r . name , " _this_ is... a n-a-m-e. " ) ) ;
assert! ( Regex ::is_match ( & r . address , " 44hintoFpuo3ugKfcqJvh5BmrsTRpnTasJmetKC4VXCt6QDtbHVuixdTtsm6Ptp7Y8haXnJ6j8Gj2dra8CKy5ewz7Vi9CYW " ) ) ;
assert! ( Regex ::is_match ( & r . ipv4 , " 192.168.1.2 " ) ) ;
assert! ( Regex ::is_match ( & r . ipv4 , " 127.0.0.1 " ) ) ;
assert! ( Regex ::is_match ( & r . domain , " my.node.com " ) ) ;
assert! ( Regex ::is_match ( & r . domain , " my.monero-node123.net " ) ) ;
assert! ( Regex ::is_match ( & r . domain , " www.my-node.org " ) ) ;
assert! ( Regex ::is_match ( & r . domain , " www.my-monero-node123.io " ) ) ;
for i in 1 ..= 65535 {
assert! ( Regex ::is_match ( & r . port , & i . to_string ( ) ) ) ;
}
assert! ( ! Regex ::is_match ( & r . port , " 0 " ) ) ;
assert! ( ! Regex ::is_match ( & r . port , " 65536 " ) ) ;
2022-12-17 22:17:26 +00:00
}
}