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,
|
|
|
|
TextStyle,
|
|
|
|
Layout,Align,
|
|
|
|
FontId,Label,RichText,Stroke,Vec2,Button,SelectableLabel,
|
|
|
|
Key,Modifiers,
|
|
|
|
CentralPanel,TopBottomPanel,
|
|
|
|
};
|
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-11-16 21:07:49 +00:00
|
|
|
|
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-10-01 16:58:22 +00:00
|
|
|
|
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-17 18:03:45 +00:00
|
|
|
use {ferris::*,constants::*,node::*,disk::*,status::*,update::*,gupax::*};
|
2022-10-01 16:58:22 +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-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()).
|
|
|
|
must_resize: bool,
|
|
|
|
first_frame: bool, // Is this the very first frame?
|
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-15 21:29:05 +00:00
|
|
|
// Error State
|
|
|
|
// 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-10-16 21:29:24 +00:00
|
|
|
// Process/update state:
|
|
|
|
// Doesn't make sense to save this on disk
|
|
|
|
// so it's represented as a bool here.
|
|
|
|
p2pool: bool, // Is p2pool online?
|
|
|
|
xmrig: bool, // Is xmrig online?
|
|
|
|
// State from [--flags]
|
2022-11-15 03:11:00 +00:00
|
|
|
no_startup: bool,
|
2022-10-15 19:15:27 +00:00
|
|
|
// Static stuff
|
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-11-16 19:07:27 +00:00
|
|
|
os_data_path: PathBuf, // OS data path (e.g: ~/.local/share/gupax)
|
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;
|
|
|
|
init_text_styles(&cc.egui_ctx, resolution[0] as f32);
|
2022-10-01 16:58:22 +00:00
|
|
|
Self {
|
2022-10-16 21:29:24 +00:00
|
|
|
resolution,
|
|
|
|
..app
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-17 00:36:58 +00:00
|
|
|
fn new() -> Self {
|
2022-11-17 18:03:45 +00:00
|
|
|
info!("Initializing App Struct...");
|
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,
|
|
|
|
first_frame: true,
|
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-10-15 19:15:27 +00:00
|
|
|
diff: false,
|
2022-11-15 21:29:05 +00:00
|
|
|
error_state: ErrorState::new(),
|
2022-10-16 21:29:24 +00:00
|
|
|
p2pool: false,
|
|
|
|
xmrig: false,
|
2022-11-15 03:11:00 +00:00
|
|
|
no_startup: false,
|
2022-10-15 19:15:27 +00:00
|
|
|
now: Instant::now(),
|
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-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
|
|
|
|
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-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();
|
|
|
|
app.state_path.push("state.toml");
|
|
|
|
app.node_path = app.os_data_path.clone();
|
|
|
|
app.node_path.push("node.toml");
|
2022-11-23 04:21:46 +00:00
|
|
|
app.pool_path = app.os_data_path.clone();
|
|
|
|
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.
|
|
|
|
let mut app = parse_args(app, panic);
|
|
|
|
|
2022-11-20 02:20:28 +00:00
|
|
|
// Read disk state
|
|
|
|
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-11-20 02:20:28 +00:00
|
|
|
};
|
|
|
|
State::new()
|
|
|
|
},
|
|
|
|
};
|
|
|
|
app.og = Arc::new(Mutex::new(app.state.clone()));
|
|
|
|
// Read node list
|
2022-11-20 18:31:00 +00:00
|
|
|
app.og_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-11-23 04:21:46 +00:00
|
|
|
Format(e) => app.error_state.set(format!("Node file: {}", 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-11-20 02:20:28 +00:00
|
|
|
};
|
|
|
|
Node::new_vec()
|
|
|
|
},
|
|
|
|
};
|
2022-11-23 04:21:46 +00:00
|
|
|
// Read pool list
|
|
|
|
app.og_pool_vec = match Pool::get(&app.pool_path) {
|
|
|
|
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),
|
|
|
|
Format(e) => app.error_state.set(format!("Pool file: {}", e), ErrorFerris::Panic, ErrorButtons::Quit),
|
|
|
|
Merge(e) => app.error_state.set(format!("Pool list: {}", e), ErrorFerris::Error, ErrorButtons::ResetState),
|
|
|
|
};
|
|
|
|
Pool::new_vec()
|
|
|
|
},
|
|
|
|
};
|
|
|
|
app.pool_vec = app.og_pool_vec.clone();
|
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-11-16 02:19:30 +00:00
|
|
|
og.xmrig.max_threads = num_cpus::get();
|
|
|
|
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
|
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
|
|
|
|
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-10-27 03:15:56 +00:00
|
|
|
// Apply 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-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-11-16 02:19:30 +00:00
|
|
|
drop(og); // Unlock [og]
|
2022-11-17 18:03:45 +00:00
|
|
|
info!("App ... OK");
|
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].
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
|
|
enum Tab {
|
|
|
|
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-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-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,
|
|
|
|
Oops,
|
|
|
|
Error,
|
|
|
|
Panic,
|
|
|
|
}
|
|
|
|
|
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?
|
|
|
|
}
|
|
|
|
|
|
|
|
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-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-11-15 21:29:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- [Images] struct
|
|
|
|
struct Images {
|
|
|
|
banner: RetainedImage,
|
2022-11-20 02:20:28 +00:00
|
|
|
happy: RetainedImage,
|
2022-11-15 21:29:05 +00:00
|
|
|
oops: RetainedImage,
|
|
|
|
error: RetainedImage,
|
|
|
|
panic: RetainedImage,
|
|
|
|
}
|
|
|
|
|
|
|
|
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-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-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 {
|
|
|
|
name: Regex::new("^[A-Za-z0-9-_]+( [A-Za-z0-9-_]+)*$").unwrap(),
|
|
|
|
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-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) {
|
|
|
|
let scale = width / 26.666;
|
|
|
|
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-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]
|
|
|
|
if app.state.gupax.auto_update {
|
2022-11-25 01:28:13 +00:00
|
|
|
Update::spawn_thread(&app.og, &app.state.gupax, &app.state_path, &app.update, &mut app.error_state);
|
2022-11-11 04:42:57 +00:00
|
|
|
} else {
|
|
|
|
info!("Skipping auto-update...");
|
|
|
|
}
|
|
|
|
|
|
|
|
// [Auto-Ping]
|
2022-11-14 02:56:25 +00:00
|
|
|
let auto_node = app.og.lock().unwrap().p2pool.auto_node;
|
|
|
|
let simple = app.og.lock().unwrap().p2pool.simple;
|
|
|
|
if auto_node && simple {
|
2022-11-20 19:20:25 +00:00
|
|
|
Ping::spawn_thread(&app.ping, &app.og)
|
2022-11-11 04:42:57 +00:00
|
|
|
} else {
|
|
|
|
info!("Skipping auto-ping...");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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!("\nGupax reset ... OK"),
|
2022-11-25 02:03:42 +00:00
|
|
|
_ => eprintln!("\nGupax 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-11-14 02:56:25 +00:00
|
|
|
println!("Gupax {} [OS: {}, Commit: {}]\n\n{}", GUPAX_VERSION, OS_NAME, &COMMIT[..40], 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!("\nState reset ... OK"); exit(0); } else { eprintln!("\nState reset ... FAIL"); exit(1) },
|
|
|
|
"--reset-nodes" => if let Ok(()) = reset_nodes(&app.node_path) { println!("\nNode reset ... OK"); exit(0) } else { eprintln!("\nNode reset ... FAIL"); exit(1) },
|
|
|
|
"--reset-pools" => if let Ok(()) = reset_pools(&app.pool_path) { println!("\nPool reset ... OK"); exit(0) } else { eprintln!("\nPool 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: [{}]\nFor 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(®ex, 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-23 04:21:46 +00:00
|
|
|
let mut app = App::new();
|
|
|
|
app.now = now;
|
2022-11-25 01:28:13 +00:00
|
|
|
init_auto(&mut app);
|
2022-11-23 04:21:46 +00:00
|
|
|
let initial_window_size = match app.state.gupax.simple {
|
|
|
|
true => Some(Vec2::new(app.state.gupax.selected_width as f32, app.state.gupax.selected_height as f32)),
|
|
|
|
false => Some(Vec2::new(APP_DEFAULT_WIDTH, APP_DEFAULT_HEIGHT)),
|
|
|
|
};
|
|
|
|
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-11-21 01:21:47 +00:00
|
|
|
info!("Init ... DONE");
|
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-11-16 19:07:27 +00:00
|
|
|
if self.state.gupax.ask_before_quit {
|
2022-11-20 18:31:00 +00:00
|
|
|
self.error_state.set("", ErrorFerris::Oops, ErrorButtons::StayQuit);
|
2022-11-16 19:07:27 +00:00
|
|
|
false
|
2022-10-17 00:36:58 +00:00
|
|
|
} else {
|
|
|
|
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-10-27 03:15:56 +00:00
|
|
|
|
2022-10-17 02:28:41 +00:00
|
|
|
// This sets the top level Ui dimensions.
|
|
|
|
// Used as a reference for other uis.
|
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;
|
|
|
|
self.must_resize = true;
|
|
|
|
};
|
|
|
|
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.
|
|
|
|
if self.must_resize && ctx.is_pointer_over_area(){
|
|
|
|
info!("App | Resizing frame to match new internal resolution: [{}x{}]", self.width, self.height);
|
|
|
|
init_text_styles(ctx, self.width);
|
|
|
|
self.must_resize = false;
|
|
|
|
}
|
2022-10-17 02:28:41 +00:00
|
|
|
|
2022-10-29 13:13:00 +00:00
|
|
|
// If [F11] was pressed, reverse [fullscreen] bool
|
|
|
|
if ctx.input_mut().consume_key(Modifiers::NONE, Key::F11) {
|
|
|
|
let info = frame.info();
|
|
|
|
frame.set_fullscreen(!info.window_info.fullscreen);
|
|
|
|
}
|
|
|
|
|
2022-11-15 21:29:05 +00:00
|
|
|
// If there's an error, display [ErrorState] on the whole screen until user responds
|
|
|
|
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,
|
|
|
|
Oops => &self.img.oops,
|
2022-11-16 19:07:27 +00:00
|
|
|
Error => &self.img.error,
|
|
|
|
Panic => &self.img.panic,
|
|
|
|
};
|
|
|
|
ferris.show_max_size(ui, Vec2::new(width, height));
|
|
|
|
|
|
|
|
// Error/Quit screen
|
|
|
|
match self.error_state.buttons {
|
|
|
|
StayQuit => {
|
2022-11-17 02:14:21 +00:00
|
|
|
let mut text = "".to_string();
|
2022-11-16 19:07:27 +00:00
|
|
|
if *self.update.lock().unwrap().updating.lock().unwrap() { text = format!("{}\nUpdate is in progress...!", text); }
|
|
|
|
if self.p2pool { text = format!("{}\nP2Pool is online...!", text); }
|
|
|
|
if self.xmrig { text = format!("{}\nXMRig 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-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! ---")),
|
|
|
|
};
|
|
|
|
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
|
|
|
|
|
|
|
// Capture [Esc] key
|
|
|
|
let esc = ctx.input_mut().consume_key(Modifiers::NONE, Key::Escape);
|
|
|
|
|
2022-11-15 21:29:05 +00:00
|
|
|
match self.error_state.buttons {
|
|
|
|
YesNo => {
|
2022-11-23 04:21:46 +00:00
|
|
|
if ui.add_sized([width, height/2.0], Button::new("Yes")).clicked() { self.error_state = ErrorState::new(); }
|
2022-11-16 19:07:27 +00:00
|
|
|
// If [Esc] was pressed, assume [No]
|
2022-11-23 04:21:46 +00:00
|
|
|
if 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-11-23 04:21:46 +00:00
|
|
|
if 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-11-23 04:21:46 +00:00
|
|
|
if ui.add_sized([width, height/2.0], Button::new("Quit")).clicked() { 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-11-23 04:21:46 +00:00
|
|
|
if esc || ui.add_sized([width, height/2.0], Button::new("No")).clicked() { self.error_state = ErrorState::new() }
|
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-11-23 04:21:46 +00:00
|
|
|
if esc || ui.add_sized([width, height/2.0], Button::new("No")).clicked() { self.error_state = ErrorState::new() }
|
2022-11-20 02:20:28 +00:00
|
|
|
},
|
2022-11-23 04:21:46 +00:00
|
|
|
Okay => if esc || ui.add_sized([width, height], Button::new("Okay")).clicked() { self.error_state = ErrorState::new(); },
|
|
|
|
Quit => if ui.add_sized([width, height], Button::new("Quit")).clicked() { exit(1); },
|
2022-11-15 21:29:05 +00:00
|
|
|
}
|
|
|
|
})});
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compare [og == state] and the [node_vec] and enable diff if found.
|
|
|
|
// 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.
|
|
|
|
let og = self.og.lock().unwrap();
|
|
|
|
if og.gupax != self.state.gupax || og.p2pool != self.state.p2pool || og.xmrig != self.state.xmrig || self.og_node_vec != self.node_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-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-10-17 02:28:41 +00:00
|
|
|
ui.group(|ui| {
|
|
|
|
ui.add_space(4.0);
|
|
|
|
ui.horizontal(|ui| {
|
2022-11-16 19:07:27 +00:00
|
|
|
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) };
|
2022-11-23 04:21:46 +00:00
|
|
|
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::About, "About")).clicked() { self.tab = Tab::About; }
|
2022-10-17 02:28:41 +00:00
|
|
|
ui.separator();
|
2022-11-23 04:21:46 +00:00
|
|
|
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::Status, "Status")).clicked() { self.tab = Tab::Status; }
|
2022-10-17 02:28:41 +00:00
|
|
|
ui.separator();
|
2022-11-23 04:21:46 +00:00
|
|
|
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::Gupax, "Gupax")).clicked() { self.tab = Tab::Gupax; }
|
2022-10-17 02:28:41 +00:00
|
|
|
ui.separator();
|
2022-11-23 04:21:46 +00:00
|
|
|
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::P2pool, "P2Pool")).clicked() { self.tab = Tab::P2pool; }
|
2022-10-17 02:28:41 +00:00
|
|
|
ui.separator();
|
2022-11-23 04:21:46 +00:00
|
|
|
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::Xmrig, "XMRig")).clicked() { self.tab = Tab::Xmrig; }
|
2022-10-17 02:28:41 +00:00
|
|
|
});
|
|
|
|
ui.add_space(4.0);
|
2022-10-01 16:58:22 +00:00
|
|
|
});
|
2022-10-17 02:28:41 +00:00
|
|
|
});
|
2022-10-01 16:58:22 +00:00
|
|
|
|
|
|
|
// Bottom: app info + state/process buttons
|
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
|
|
|
// [Gupax Version] + [OS] + [P2Pool on/off] + [XMRig on/off]
|
|
|
|
let width = ((self.width/2.0)/4.0)-(SPACE*2.0);
|
2022-10-17 00:36:58 +00:00
|
|
|
ui.add_sized([width, height], Label::new(&*self.name_version));
|
2022-10-01 16:58:22 +00:00
|
|
|
ui.separator();
|
|
|
|
ui.add_sized([width, height], Label::new(self.os));
|
|
|
|
ui.separator();
|
2022-10-17 00:36:58 +00:00
|
|
|
if self.p2pool {
|
2022-11-23 04:21:46 +00:00
|
|
|
ui.add_sized([width, height], Label::new(RichText::new("P2Pool ⏺").color(GREEN)));
|
2022-10-01 16:58:22 +00:00
|
|
|
} else {
|
2022-11-23 04:21:46 +00:00
|
|
|
ui.add_sized([width, height], Label::new(RichText::new("P2Pool ⏺").color(RED)));
|
2022-10-01 16:58:22 +00:00
|
|
|
}
|
|
|
|
ui.separator();
|
2022-10-17 00:36:58 +00:00
|
|
|
if self.xmrig {
|
2022-11-23 04:21:46 +00:00
|
|
|
ui.add_sized([width, height], Label::new(RichText::new("XMRig ⏺").color(GREEN)));
|
2022-10-01 16:58:22 +00:00
|
|
|
} else {
|
2022-11-23 04:21:46 +00:00
|
|
|
ui.add_sized([width, height], Label::new(RichText::new("XMRig ⏺").color(RED)));
|
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| {
|
|
|
|
let width = match self.tab {
|
|
|
|
Tab::Gupax => (ui.available_width()/2.0)-(SPACE*3.0),
|
|
|
|
_ => (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-11-23 04:21:46 +00:00
|
|
|
if 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();
|
|
|
|
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-11-23 04:21:46 +00:00
|
|
|
if 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();
|
|
|
|
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-11-20 18:31:00 +00:00
|
|
|
match Node::save(&self.og_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-11-23 04:21:46 +00:00
|
|
|
match Pool::save(&self.og_pool_vec, &self.pool_path) {
|
|
|
|
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-11-23 04:21:46 +00:00
|
|
|
Tab::Gupax => {
|
|
|
|
ui.group(|ui| {
|
|
|
|
let width = width / 2.0;
|
|
|
|
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;
|
|
|
|
if self.p2pool {
|
2022-11-23 04:21:46 +00:00
|
|
|
if ui.add_sized([width, height], Button::new("⟲")).on_hover_text("Restart P2Pool").clicked() { self.p2pool = false; }
|
|
|
|
if ui.add_sized([width, height], Button::new("⏹")).on_hover_text("Stop P2Pool").clicked() { self.p2pool = false; }
|
2022-11-11 02:20:31 +00:00
|
|
|
ui.add_enabled_ui(false, |ui| {
|
2022-11-23 04:21:46 +00:00
|
|
|
ui.add_sized([width, height], Button::new("⏺")).on_hover_text("Start P2Pool");
|
2022-11-11 02:20:31 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
ui.add_enabled_ui(false, |ui| {
|
2022-11-23 04:21:46 +00:00
|
|
|
ui.add_sized([width, height], Button::new("⟲")).on_hover_text("Restart P2Pool");
|
|
|
|
ui.add_sized([width, height], Button::new("⏹")).on_hover_text("Stop P2Pool");
|
2022-11-11 02:20:31 +00:00
|
|
|
});
|
2022-11-23 04:21:46 +00:00
|
|
|
if ui.add_sized([width, height], Button::new("⏺")).on_hover_text("Start P2Pool").clicked() { self.p2pool = true; }
|
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;
|
|
|
|
if self.xmrig {
|
2022-11-23 04:21:46 +00:00
|
|
|
if ui.add_sized([width, height], Button::new("⟲")).on_hover_text("Restart XMRig").clicked() { self.xmrig = false; }
|
|
|
|
if ui.add_sized([width, height], Button::new("⏹")).on_hover_text("Stop XMRig").clicked() { self.xmrig = false; }
|
2022-11-11 02:20:31 +00:00
|
|
|
ui.add_enabled_ui(false, |ui| {
|
2022-11-23 04:21:46 +00:00
|
|
|
ui.add_sized([width, height], Button::new("⏺")).on_hover_text("Start XMRig");
|
2022-11-11 02:20:31 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
ui.add_enabled_ui(false, |ui| {
|
2022-11-23 04:21:46 +00:00
|
|
|
ui.add_sized([width, height], Button::new("⟲")).on_hover_text("Restart XMRig");
|
|
|
|
ui.add_sized([width, height], Button::new("⏹")).on_hover_text("Stop XMRig");
|
2022-11-11 02:20:31 +00:00
|
|
|
});
|
2022-11-23 04:21:46 +00:00
|
|
|
if ui.add_sized([width, height], Button::new("⏺")).on_hover_text("Start XMRig").clicked() { self.xmrig = true; }
|
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-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-10-17 02:28:41 +00:00
|
|
|
ui.add_space(10.0);
|
|
|
|
ui.vertical_centered(|ui| {
|
2022-10-27 03:15:56 +00:00
|
|
|
// Display [Gupax] banner at max, 1/4 the available length
|
2022-11-16 19:07:27 +00:00
|
|
|
self.img.banner.show_max_size(ui, Vec2::new(self.width, self.height/4.0));
|
2022-10-27 03:15:56 +00:00
|
|
|
ui.label("Gupax is a cross-platform GUI for mining");
|
2022-10-17 02:28:41 +00:00
|
|
|
ui.hyperlink_to("[Monero]", "https://www.github.com/monero-project/monero");
|
|
|
|
ui.label("on the decentralized");
|
|
|
|
ui.hyperlink_to("[P2Pool]", "https://www.github.com/SChernykh/p2pool");
|
|
|
|
ui.label("using the dedicated");
|
|
|
|
ui.hyperlink_to("[XMRig]", "https://www.github.com/xmrig/xmrig");
|
|
|
|
ui.label("miner for max hashrate");
|
2022-10-17 00:36:58 +00:00
|
|
|
|
2022-11-23 04:21:46 +00:00
|
|
|
ui.add_space(ui.available_height()/1.8);
|
2022-10-17 02:28:41 +00:00
|
|
|
ui.hyperlink_to("Powered by egui", "https://github.com/emilk/egui");
|
2022-11-24 04:03:56 +00:00
|
|
|
ui.hyperlink_to("Made by hinto-janaiyo".to_string(), "https://gupax.io");
|
2022-10-17 02:28:41 +00:00
|
|
|
ui.label("egui is licensed under MIT & Apache-2.0");
|
|
|
|
ui.label("Gupax, P2Pool, and XMRig are licensed under GPLv3");
|
|
|
|
});
|
2022-10-27 03:15:56 +00:00
|
|
|
}
|
|
|
|
Tab::Status => {
|
|
|
|
Status::show(self, self.width, self.height, ctx, ui);
|
|
|
|
}
|
|
|
|
Tab::Gupax => {
|
2022-11-25 01:28:13 +00:00
|
|
|
Gupax::show(&mut self.state.gupax, &self.og, &self.state_path, &self.update, &self.file_window, &mut self.error_state, self.width, self.height, frame, ctx, ui);
|
2022-10-27 03:15:56 +00:00
|
|
|
}
|
|
|
|
Tab::P2pool => {
|
2022-11-14 02:56:25 +00:00
|
|
|
P2pool::show(&mut self.state.p2pool, &mut self.node_vec, &self.og, self.p2pool, &self.ping, &self.regex, self.width, self.height, ctx, ui);
|
2022-10-27 03:15:56 +00:00
|
|
|
}
|
|
|
|
Tab::Xmrig => {
|
2022-11-23 04:21:46 +00:00
|
|
|
Xmrig::show(&mut self.state.xmrig, &mut self.pool_vec, &self.regex, self.width, self.height, ctx, ui);
|
2022-10-27 03:15:56 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-01 16:58:22 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|