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
|
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
|
|
|
//#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
2022-10-15 19:15:27 +00:00
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- Imports
|
|
|
|
// egui/eframe
|
2022-10-17 02:28:41 +00:00
|
|
|
use egui::Ui;
|
2022-10-15 19:15:27 +00:00
|
|
|
use egui::TextStyle::*;
|
2022-10-01 16:58:22 +00:00
|
|
|
use egui::color::Color32;
|
|
|
|
use egui::FontFamily::Proportional;
|
2022-10-15 19:15:27 +00:00
|
|
|
use egui::{FontId,Label,RichText,Stroke,Vec2,Pos2};
|
2022-10-17 00:36:58 +00:00
|
|
|
use egui::special_emojis::GITHUB;
|
2022-10-29 13:13:00 +00:00
|
|
|
use egui::{Key,Modifiers};
|
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-10-15 19:15:27 +00:00
|
|
|
// std
|
2022-10-01 16:58:22 +00:00
|
|
|
use std::io::Write;
|
2022-10-15 19:15:27 +00:00
|
|
|
use std::process::exit;
|
2022-10-14 21:13:38 +00:00
|
|
|
use std::sync::{Arc,Mutex};
|
2022-10-16 21:29:24 +00:00
|
|
|
use std::{thread,env};
|
2022-10-15 19:15:27 +00:00
|
|
|
use std::time::Instant;
|
2022-10-16 21:29:24 +00:00
|
|
|
use std::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 about;
|
|
|
|
mod status;
|
|
|
|
mod gupax;
|
|
|
|
mod p2pool;
|
|
|
|
mod xmrig;
|
2022-10-25 02:58:42 +00:00
|
|
|
mod update;
|
2022-11-14 02:56:25 +00:00
|
|
|
use {ferris::*,constants::*,node::*,disk::*,about::*,status::*,gupax::*,p2pool::*,xmrig::*,update::*};
|
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-16 21:29:24 +00:00
|
|
|
quit: bool, // Was the quit button clicked?
|
|
|
|
quit_confirm: bool, // Was the quit confirmed?
|
2022-11-11 02:20:31 +00:00
|
|
|
ping: Arc<Mutex<Ping>>, // Ping data found in [node.rs]
|
2022-10-17 02:28:41 +00:00
|
|
|
width: f32, // Top-level width
|
|
|
|
height: f32, // Top-level height
|
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-14 02:56:25 +00:00
|
|
|
node_vec: Vec<(String, Node)>, // Manual Node 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-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]
|
|
|
|
startup: bool,
|
|
|
|
reset: 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-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]
|
|
|
|
banner: RetainedImage, // Gupax banner image
|
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-10-16 21:29:24 +00:00
|
|
|
let app = Self {
|
2022-10-15 19:15:27 +00:00
|
|
|
tab: Tab::default(),
|
|
|
|
quit: false,
|
|
|
|
quit_confirm: false,
|
2022-11-11 02:20:31 +00:00
|
|
|
ping: Arc::new(Mutex::new(Ping::new())),
|
2022-10-17 02:28:41 +00:00
|
|
|
width: 1280.0,
|
|
|
|
height: 720.0,
|
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-14 02:56:25 +00:00
|
|
|
node_vec: Node::new_vec(),
|
2022-10-15 19:15:27 +00:00
|
|
|
diff: false,
|
2022-10-16 21:29:24 +00:00
|
|
|
p2pool: false,
|
|
|
|
xmrig: false,
|
|
|
|
startup: true,
|
|
|
|
reset: false,
|
2022-10-15 19:15:27 +00:00
|
|
|
now: Instant::now(),
|
2022-10-19 18:35:32 +00:00
|
|
|
exe: "".to_string(),
|
2022-11-02 22:18:41 +00:00
|
|
|
dir: "".to_string(),
|
2022-10-16 21:29:24 +00:00
|
|
|
resolution: Vec2::new(1280.0, 720.0),
|
2022-10-15 19:15:27 +00:00
|
|
|
os: OS,
|
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-11 02:20:31 +00:00
|
|
|
banner: RetainedImage::from_image_bytes("banner.png", BYTES_BANNER).unwrap(),
|
2022-11-14 02:56:25 +00:00
|
|
|
regex: Regexes::new(),
|
2022-10-16 21:29:24 +00:00
|
|
|
};
|
2022-10-17 00:36:58 +00:00
|
|
|
// Apply arg state
|
|
|
|
let mut app = parse_args(app);
|
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-10-29 13:13:00 +00:00
|
|
|
Err(err) => { panic_main(err.to_string()); exit(1); },
|
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,
|
|
|
|
Err(err) => { panic_main(err.to_string()); exit(1); },
|
|
|
|
};
|
2022-10-17 00:36:58 +00:00
|
|
|
// Read disk state if no [--reset] arg
|
|
|
|
if app.reset == false {
|
|
|
|
app.og = match State::get() {
|
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
|
|
|
Ok(toml) => Arc::new(Mutex::new(toml)),
|
2022-10-29 13:13:00 +00:00
|
|
|
Err(err) => { panic_main(err.to_string()); exit(1); },
|
2022-10-17 00:36:58 +00:00
|
|
|
};
|
|
|
|
}
|
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
|
|
|
app.state = app.og.lock().unwrap().clone();
|
2022-11-14 02:56:25 +00:00
|
|
|
// Get node list
|
|
|
|
app.node_vec = Node::get().unwrap();
|
2022-10-27 03:15:56 +00:00
|
|
|
// Handle 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
|
|
|
app.og.lock().unwrap().xmrig.max_threads = num_cpus::get();
|
|
|
|
let current = app.og.lock().unwrap().xmrig.current_threads;
|
|
|
|
let max = app.og.lock().unwrap().xmrig.max_threads;
|
|
|
|
if current > max {
|
|
|
|
app.og.lock().unwrap().xmrig.current_threads = max;
|
|
|
|
}
|
2022-10-27 03:15:56 +00:00
|
|
|
// Apply TOML values to [Update]
|
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 p2pool_path = app.og.lock().unwrap().gupax.absolute_p2pool_path.clone();
|
|
|
|
let xmrig_path = app.og.lock().unwrap().gupax.absolute_xmrig_path.clone();
|
|
|
|
let tor = app.og.lock().unwrap().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-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-14 02:56:25 +00:00
|
|
|
//---------------------------------------------------------------------------------------------------- [Regexes] struct
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
struct Regexes {
|
|
|
|
name: Regex,
|
|
|
|
address: Regex,
|
|
|
|
ipv4: Regex,
|
|
|
|
domain: Regex,
|
|
|
|
port: Regex,
|
|
|
|
}
|
|
|
|
|
|
|
|
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-10-01 16:58:22 +00:00
|
|
|
].into();
|
2022-11-11 02:20:31 +00:00
|
|
|
// style.visuals.selection.stroke = Stroke { width: 5.0, color: Color32::from_rgb(255, 255, 255) };
|
2022-10-01 16:58:22 +00:00
|
|
|
// style.spacing.slider_width = scale;
|
|
|
|
// style.spacing.text_edit_width = scale;
|
|
|
|
// style.spacing.button_padding = Vec2::new(scale/2.0, scale/2.0);
|
|
|
|
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-11 02:20:31 +00:00
|
|
|
//fn init_color(ctx: &egui::Context) {
|
|
|
|
// let mut style = (*ctx.style()).clone();
|
|
|
|
// style.visuals.widgets.inactive.fg_stroke.color = Color32::from_rgb(100, 100, 100);
|
|
|
|
// style.visuals.selection.bg_fill = Color32::from_rgb(255, 125, 50);
|
|
|
|
// style.visuals.selection.stroke = Stroke { width: 5.0, color: Color32::from_rgb(255, 255, 255) };
|
|
|
|
// ctx.set_style(style);
|
|
|
|
//}
|
|
|
|
|
2022-10-13 12:57:50 +00:00
|
|
|
fn init_logger() {
|
2022-10-27 03:15:56 +00:00
|
|
|
// #[cfg(debug_assertions)]
|
2022-10-16 21:29:24 +00:00
|
|
|
let filter = LevelFilter::Info;
|
2022-10-27 03:15:56 +00:00
|
|
|
// #[cfg(not(debug_assertions))]
|
|
|
|
// let filter = LevelFilter::Warn;
|
2022-10-01 16:58:22 +00:00
|
|
|
use env_logger::fmt::Color;
|
|
|
|
Builder::new().format(|buf, record| {
|
|
|
|
let level;
|
|
|
|
let mut style = buf.style();
|
|
|
|
match record.level() {
|
|
|
|
Level::Error => { style.set_color(Color::Red); level = "ERROR" },
|
2022-10-13 12:57:50 +00:00
|
|
|
Level::Warn => { style.set_color(Color::Yellow); level = "WARN" },
|
|
|
|
Level::Info => { style.set_color(Color::White); level = "INFO" },
|
2022-10-01 16:58:22 +00:00
|
|
|
Level::Debug => { style.set_color(Color::Blue); level = "DEBUG" },
|
|
|
|
Level::Trace => { style.set_color(Color::Magenta); level = "TRACE" },
|
|
|
|
};
|
|
|
|
writeln!(
|
|
|
|
buf,
|
2022-10-13 12:57:50 +00:00
|
|
|
"[{}] [{}] [{}:{}] {}",
|
2022-10-01 16:58:22 +00:00
|
|
|
style.set_bold(true).value(level),
|
|
|
|
buf.style().set_dimmed(true).value(chrono::Local::now().format("%F %T%.3f")),
|
|
|
|
buf.style().set_dimmed(true).value(record.file().unwrap_or("???")),
|
|
|
|
buf.style().set_dimmed(true).value(record.line().unwrap_or(0)),
|
|
|
|
record.args(),
|
|
|
|
)
|
2022-10-16 21:29:24 +00:00
|
|
|
}).filter_level(filter).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-10-13 12:57:50 +00:00
|
|
|
fn init_options() -> NativeOptions {
|
2022-10-01 16:58:22 +00:00
|
|
|
let mut options = eframe::NativeOptions::default();
|
2022-10-27 03:15:56 +00:00
|
|
|
options.min_window_size = Option::from(Vec2::new(854.0, 480.0));
|
2022-10-01 16:58:22 +00:00
|
|
|
options.max_window_size = Option::from(Vec2::new(3180.0, 2160.0));
|
|
|
|
options.initial_window_size = Option::from(Vec2::new(1280.0, 720.0));
|
|
|
|
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-11 04:42:57 +00:00
|
|
|
fn init_auto(app: &App) {
|
|
|
|
info!("Starting init_auto()...");
|
|
|
|
// [Auto-Update]
|
|
|
|
if app.state.gupax.auto_update {
|
|
|
|
let path_p2pool = app.og.lock().unwrap().gupax.absolute_p2pool_path.display().to_string();
|
|
|
|
let path_xmrig = app.og.lock().unwrap().gupax.absolute_xmrig_path.display().to_string();
|
|
|
|
let tor = app.og.lock().unwrap().gupax.update_via_tor;
|
|
|
|
app.update.lock().unwrap().path_p2pool = path_p2pool;
|
|
|
|
app.update.lock().unwrap().path_xmrig = path_xmrig;
|
|
|
|
app.update.lock().unwrap().tor = tor;
|
|
|
|
let og = Arc::clone(&app.og);
|
|
|
|
let og_ver = Arc::clone(&app.og.lock().unwrap().version);
|
|
|
|
let state_ver = Arc::clone(&app.state.version);
|
|
|
|
let update = Arc::clone(&app.update);
|
|
|
|
let update_thread = Arc::clone(&app.update);
|
|
|
|
thread::spawn(move|| {
|
|
|
|
info!("Spawning update thread...");
|
|
|
|
match Update::start(update_thread, og_ver.clone(), state_ver.clone()) {
|
|
|
|
Err(e) => {
|
|
|
|
info!("Update ... FAIL ... {}", e);
|
|
|
|
*update.lock().unwrap().msg.lock().unwrap() = format!("{} | {}", MSG_FAILED, e);
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
info!("Update | Saving state...");
|
|
|
|
match State::save(&mut og.lock().unwrap()) {
|
|
|
|
Ok(_) => info!("Update ... OK"),
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Update | Saving state ... FAIL ... {}", e);
|
|
|
|
*update.lock().unwrap().msg.lock().unwrap() = format!("Saving new versions into state failed");
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
*update.lock().unwrap().updating.lock().unwrap() = false;
|
|
|
|
});
|
|
|
|
} 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-11 04:42:57 +00:00
|
|
|
let ping = Arc::clone(&app.ping);
|
|
|
|
let og = Arc::clone(&app.og);
|
|
|
|
thread::spawn(move|| {
|
|
|
|
info!("Spawning ping thread...");
|
|
|
|
crate::node::ping(ping, og);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
info!("Skipping auto-ping...");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-16 21:29:24 +00:00
|
|
|
//---------------------------------------------------------------------------------------------------- Misc functions
|
|
|
|
fn parse_args(mut app: App) -> App {
|
|
|
|
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() {
|
|
|
|
"-h"|"--help" => { println!("{}", ARG_HELP); exit(0); },
|
|
|
|
"-v"|"--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-10-18 19:26:21 +00:00
|
|
|
"-f"|"--ferris" => { println!("{}", FERRIS); exit(0); },
|
2022-10-16 21:29:24 +00:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Everything else
|
|
|
|
for arg in args {
|
|
|
|
match arg.as_str() {
|
2022-11-14 02:56:25 +00:00
|
|
|
"-l"|"--node-list" => { info!("Printing node list..."); print_disk_file(File::Node); }
|
|
|
|
"-s"|"--state" => { info!("Printing state..."); print_disk_file(File::State); }
|
2022-10-16 21:29:24 +00:00
|
|
|
"-n"|"--no-startup" => { info!("Disabling startup..."); app.startup = false; }
|
2022-11-14 02:56:25 +00:00
|
|
|
"-r"|"--reset" => { info!("Resetting state..."); app.reset = true; }
|
|
|
|
_ => { eprintln!("[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-10-25 02:58:42 +00:00
|
|
|
Ok(mut path) => { Ok(path.display().to_string()) },
|
2022-10-19 18:35:32 +00:00
|
|
|
Err(err) => { error!("Couldn't get exe basepath PATH"); return Err(err) },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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()) },
|
|
|
|
Err(err) => { error!("Couldn't get exe basepath PATH"); return Err(err) },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
// Clean any [gupax_tmp.*] directories
|
|
|
|
pub fn clean_dir() -> Result<(), anyhow::Error> {
|
|
|
|
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 }
|
|
|
|
if entry.file_name().to_str().ok_or(anyhow::Error::msg("Basename failed"))?.starts_with("gupax_update_") {
|
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
|
|
|
|
fn print_disk_file(file: File) {
|
|
|
|
let path = match get_file_path(file) {
|
|
|
|
Ok(path) => path,
|
|
|
|
Err(e) => { error!("{}", e); exit(1); },
|
|
|
|
};
|
|
|
|
match std::fs::read_to_string(&path) {
|
|
|
|
Ok(string) => { println!("{}", string); exit(0); },
|
|
|
|
Err(e) => { error!("{}", e); exit(1); },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-29 13:13:00 +00:00
|
|
|
//---------------------------------------------------------------------------------------------------- [App] frame for [Panic] situations
|
|
|
|
fn panic_main(error: String) {
|
2022-10-19 18:35:32 +00:00
|
|
|
error!("{}", error);
|
|
|
|
let options = Panic::options();
|
2022-10-29 13:13:00 +00:00
|
|
|
let name = format!("Gupax {}", GUPAX_VERSION);
|
|
|
|
eframe::run_native(&name, options, Box::new(|cc| Box::new(Panic::new(cc, error))),);
|
2022-10-19 18:35:32 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2022-10-14 21:13:38 +00:00
|
|
|
struct Panic { error_msg: String, }
|
|
|
|
impl Panic {
|
|
|
|
fn options() -> NativeOptions {
|
|
|
|
let mut options = eframe::NativeOptions::default();
|
|
|
|
let frame = Option::from(Vec2::new(1280.0, 720.0));
|
|
|
|
options.min_window_size = frame;
|
|
|
|
options.max_window_size = frame;
|
|
|
|
options.initial_window_size = frame;
|
|
|
|
options.follow_system_theme = false;
|
|
|
|
options.default_theme = eframe::Theme::Dark;
|
|
|
|
let icon = image::load_from_memory(BYTES_ICON).expect("Failed to read icon bytes").to_rgba8();
|
|
|
|
let (icon_width, icon_height) = icon.dimensions();
|
|
|
|
options.icon_data = Some(eframe::IconData {
|
|
|
|
rgba: icon.into_raw(),
|
|
|
|
width: icon_width,
|
|
|
|
height: icon_height,
|
|
|
|
});
|
|
|
|
info!("Panic::options() ... OK");
|
|
|
|
options
|
|
|
|
}
|
|
|
|
fn new(cc: &eframe::CreationContext<'_>, error_msg: String) -> Self {
|
|
|
|
let resolution = cc.integration_info.window_info.size;
|
|
|
|
init_text_styles(&cc.egui_ctx, resolution[0] as f32);
|
|
|
|
Self { error_msg }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl eframe::App for Panic {
|
|
|
|
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
|
|
|
egui::CentralPanel::default().show(ctx, |ui| {
|
|
|
|
let width = ui.available_width();
|
|
|
|
let height = ui.available_height();
|
|
|
|
init_text_styles(ctx, width);
|
|
|
|
ui.add_sized([width, height/8.0], Label::new("Gupax has encountered a fatal error:"));
|
|
|
|
ui.add_sized([width, height/8.0], Label::new(&self.error_msg));
|
|
|
|
ui.add_sized([width, height/3.0], Label::new("Please report to: https://github.com/hinto-janaiyo/gupax/issues"));
|
|
|
|
ui.add_sized([width, height/3.0], egui::Button::new("Quit")).clicked() && exit(1)
|
|
|
|
});
|
|
|
|
}
|
2022-10-01 16:58:22 +00:00
|
|
|
}
|
|
|
|
|
2022-10-15 19:15:27 +00:00
|
|
|
//---------------------------------------------------------------------------------------------------- Main [App] frame
|
|
|
|
fn main() {
|
|
|
|
init_logger();
|
|
|
|
let options = init_options();
|
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-10-27 03:15:56 +00:00
|
|
|
let app = App::new();
|
2022-10-29 13:13:00 +00:00
|
|
|
let name = app.name_version.clone();
|
2022-11-11 04:42:57 +00:00
|
|
|
init_auto(&app);
|
2022-10-29 13:13:00 +00:00
|
|
|
eframe::run_native(&name, 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-10-15 19:15:27 +00:00
|
|
|
self.quit = true;
|
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 self.og.lock().unwrap().gupax.ask_before_quit {
|
2022-10-17 00:36:58 +00:00
|
|
|
self.quit_confirm
|
|
|
|
} 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-11-11 02:20:31 +00:00
|
|
|
// crate::node::ping();
|
|
|
|
// std::process::exit(0);
|
|
|
|
// init_color(ctx);
|
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.
|
|
|
|
egui::CentralPanel::default().show(ctx, |ui| { self.width = ui.available_width(); self.height = ui.available_height(); });
|
|
|
|
// This sets fonts globally depending on the width.
|
|
|
|
init_text_styles(ctx, self.width);
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
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 no state diff (og == state), compare and enable 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().clone();
|
2022-11-11 02:20:31 +00:00
|
|
|
// The [P2Pool Node] selection needs to be the same
|
|
|
|
// for both [State] and [Og] because of [Auto-select]
|
|
|
|
// Wrapping [node] within an [Arc<Mutex>] is a lot more work
|
|
|
|
// so sending it into the [Ping] thread is not viable.
|
|
|
|
self.state.p2pool.node = self.og.lock().unwrap().p2pool.node;
|
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 og.gupax != self.state.gupax || og.p2pool != self.state.p2pool || og.xmrig != self.state.xmrig {
|
|
|
|
self.diff = true;
|
|
|
|
} else {
|
|
|
|
self.diff = false;
|
|
|
|
}
|
|
|
|
|
2022-10-14 21:13:38 +00:00
|
|
|
// Close confirmation.
|
2022-10-15 19:15:27 +00:00
|
|
|
if self.quit {
|
2022-10-18 19:26:21 +00:00
|
|
|
// If [ask_before_quit == true]
|
2022-11-02 22:18:41 +00:00
|
|
|
if self.og.lock().unwrap().gupax.ask_before_quit {
|
2022-10-18 19:26:21 +00:00
|
|
|
egui::TopBottomPanel::bottom("quit").show(ctx, |ui| {
|
|
|
|
let width = self.width;
|
|
|
|
let height = self.height/8.0;
|
|
|
|
ui.group(|ui| {
|
|
|
|
if ui.add_sized([width, height], egui::Button::new("Yes")).clicked() {
|
2022-11-02 22:18:41 +00:00
|
|
|
if self.og.lock().unwrap().gupax.save_before_quit {
|
2022-10-18 19:26:21 +00:00
|
|
|
if self.diff {
|
|
|
|
info!("Saving before quit...");
|
|
|
|
match self.state.save() {
|
|
|
|
Err(err) => { error!("{}", err); exit(1); },
|
|
|
|
_ => (),
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
info!("No changed detected, not saving...");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
info!("Quit confirmation = yes ... goodbye!");
|
|
|
|
exit(0);
|
|
|
|
} else if ui.add_sized([width, height], egui::Button::new("No")).clicked() {
|
|
|
|
self.quit = false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
egui::CentralPanel::default().show(ctx, |ui| {
|
|
|
|
let width = self.width;
|
|
|
|
let height = ui.available_height();
|
|
|
|
let ten = height/10.0;
|
|
|
|
// Detect processes or update
|
|
|
|
ui.add_space(ten);
|
2022-11-02 22:18:41 +00:00
|
|
|
if *self.update.lock().unwrap().updating.lock().unwrap() || self.p2pool || self.xmrig {
|
2022-10-18 19:26:21 +00:00
|
|
|
ui.add_sized([width, height/4.0], Label::new("Are you sure you want to quit?"));
|
2022-11-02 22:18:41 +00:00
|
|
|
if *self.update.lock().unwrap().updating.lock().unwrap() { ui.add_sized([width, ten], Label::new("Update is in progress...!")); }
|
2022-10-18 19:26:21 +00:00
|
|
|
if self.p2pool { ui.add_sized([width, ten], Label::new("P2Pool is online...!")); }
|
|
|
|
if self.xmrig { ui.add_sized([width, ten], Label::new("XMRig is online...!")); }
|
|
|
|
// Else, just quit
|
|
|
|
} else {
|
2022-11-02 22:18:41 +00:00
|
|
|
if self.og.lock().unwrap().gupax.save_before_quit {
|
2022-10-18 19:26:21 +00:00
|
|
|
if self.diff {
|
|
|
|
info!("Saving before quit...");
|
|
|
|
match self.state.save() {
|
|
|
|
Err(err) => { error!("{}", err); exit(1); },
|
|
|
|
_ => (),
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
info!("No changed detected, not saving...");
|
|
|
|
}
|
2022-10-17 02:28:41 +00:00
|
|
|
}
|
2022-10-18 19:26:21 +00:00
|
|
|
info!("No processes or update in progress ... goodbye!");
|
2022-10-17 02:28:41 +00:00
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
});
|
2022-10-18 19:26:21 +00:00
|
|
|
// Else, quit (save if [save_before_quit == true]
|
|
|
|
} else {
|
2022-11-02 22:18:41 +00:00
|
|
|
if self.og.lock().unwrap().gupax.save_before_quit {
|
2022-10-18 19:26:21 +00:00
|
|
|
if self.diff {
|
2022-10-16 21:29:24 +00:00
|
|
|
info!("Saving before quit...");
|
|
|
|
match self.state.save() {
|
|
|
|
Err(err) => { error!("{}", err); exit(1); },
|
|
|
|
_ => (),
|
|
|
|
};
|
2022-10-18 19:26:21 +00:00
|
|
|
} else {
|
|
|
|
info!("No changed detected, not saving...");
|
2022-10-13 12:57:50 +00:00
|
|
|
}
|
2022-10-16 21:29:24 +00:00
|
|
|
}
|
2022-10-18 19:26:21 +00:00
|
|
|
info!("Quit confirmation = yes ... goodbye!");
|
|
|
|
exit(0);
|
|
|
|
}
|
2022-10-13 12:57:50 +00:00
|
|
|
return
|
|
|
|
}
|
2022-10-14 21:13:38 +00:00
|
|
|
|
2022-10-01 16:58:22 +00:00
|
|
|
// Top: Tabs
|
2022-10-17 02:28:41 +00:00
|
|
|
egui::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| {
|
|
|
|
ui.style_mut().override_text_style = Some(Name("Tab".into()));
|
|
|
|
ui.style_mut().visuals.widgets.inactive.fg_stroke.color = Color32::from_rgb(100, 100, 100);
|
|
|
|
ui.style_mut().visuals.selection.bg_fill = Color32::from_rgb(255, 120, 120);
|
2022-11-11 02:20:31 +00:00
|
|
|
ui.style_mut().visuals.selection.stroke = Stroke { width: 5.0, color: Color32::from_rgb(255, 255, 255) };
|
2022-10-17 02:28:41 +00:00
|
|
|
if ui.add_sized([width, height], egui::SelectableLabel::new(self.tab == Tab::About, "About")).clicked() { self.tab = Tab::About; }
|
|
|
|
ui.separator();
|
|
|
|
if ui.add_sized([width, height], egui::SelectableLabel::new(self.tab == Tab::Status, "Status")).clicked() { self.tab = Tab::Status; }
|
|
|
|
ui.separator();
|
|
|
|
if ui.add_sized([width, height], egui::SelectableLabel::new(self.tab == Tab::Gupax, "Gupax")).clicked() { self.tab = Tab::Gupax; }
|
|
|
|
ui.separator();
|
|
|
|
if ui.add_sized([width, height], egui::SelectableLabel::new(self.tab == Tab::P2pool, "P2Pool")).clicked() { self.tab = Tab::P2pool; }
|
|
|
|
ui.separator();
|
|
|
|
if ui.add_sized([width, height], egui::SelectableLabel::new(self.tab == Tab::Xmrig, "XMRig")).clicked() { self.tab = Tab::Xmrig; }
|
|
|
|
});
|
|
|
|
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
|
|
|
|
egui::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-11 02:20:31 +00:00
|
|
|
ui.add_sized([width, height], Label::new(RichText::new("P2Pool ⏺").color(Color32::from_rgb(100, 230, 100))));
|
2022-10-01 16:58:22 +00:00
|
|
|
} else {
|
2022-11-11 02:20:31 +00:00
|
|
|
ui.add_sized([width, height], Label::new(RichText::new("P2Pool ⏺").color(Color32::from_rgb(230, 50, 50))));
|
2022-10-01 16:58:22 +00:00
|
|
|
}
|
|
|
|
ui.separator();
|
2022-10-17 00:36:58 +00:00
|
|
|
if self.xmrig {
|
2022-11-11 02:20:31 +00:00
|
|
|
ui.add_sized([width, height], Label::new(RichText::new("XMRig ⏺").color(Color32::from_rgb(100, 230, 100))));
|
2022-10-01 16:58:22 +00:00
|
|
|
} else {
|
2022-11-11 02:20:31 +00:00
|
|
|
ui.add_sized([width, height], Label::new(RichText::new("XMRig ⏺").color(Color32::from_rgb(230, 50, 50))));
|
2022-10-01 16:58:22 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::RIGHT), |ui| {
|
2022-11-11 02:20:31 +00:00
|
|
|
// [Start/Stop/Restart] + [Simple/Advanced] + [Save/Reset]
|
|
|
|
let width = (ui.available_width()/3.0)-(SPACE*3.0);
|
|
|
|
ui.group(|ui| {
|
|
|
|
if self.diff == false {
|
|
|
|
ui.set_enabled(false)
|
|
|
|
}
|
|
|
|
let width = width / 2.0;
|
|
|
|
if ui.add_sized([width, height], egui::Button::new("Reset")).on_hover_text("Reset changes").clicked() {
|
|
|
|
self.state.gupax = self.og.lock().unwrap().gupax.clone();
|
|
|
|
self.state.p2pool = self.og.lock().unwrap().p2pool.clone();
|
|
|
|
self.state.xmrig = self.og.lock().unwrap().xmrig.clone();
|
|
|
|
}
|
|
|
|
if ui.add_sized([width, height], egui::Button::new("Save")).on_hover_text("Save changes").clicked() {
|
|
|
|
self.og.lock().unwrap().gupax = self.state.gupax.clone();
|
|
|
|
self.og.lock().unwrap().p2pool = self.state.p2pool.clone();
|
|
|
|
self.og.lock().unwrap().xmrig = self.state.xmrig.clone();
|
|
|
|
self.og.lock().unwrap().save();
|
2022-10-01 16:58:22 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-11-11 02:20:31 +00:00
|
|
|
match self.tab {
|
|
|
|
Tab::P2pool => {
|
|
|
|
ui.group(|ui| {
|
|
|
|
let width = width / 1.5;
|
2022-11-14 02:56:25 +00:00
|
|
|
if ui.add_sized([width, height], egui::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-14 02:56:25 +00:00
|
|
|
if ui.add_sized([width, height], egui::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 {
|
|
|
|
if ui.add_sized([width, height], egui::Button::new("⟲")).on_hover_text("Restart P2Pool").clicked() { self.p2pool = false; }
|
|
|
|
if ui.add_sized([width, height], egui::Button::new("⏹")).on_hover_text("Stop P2Pool").clicked() { self.p2pool = false; }
|
|
|
|
ui.add_enabled_ui(false, |ui| {
|
|
|
|
ui.add_sized([width, height], egui::Button::new("⏺")).on_hover_text("Start P2Pool");
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
ui.add_enabled_ui(false, |ui| {
|
|
|
|
ui.add_sized([width, height], egui::Button::new("⟲")).on_hover_text("Restart P2Pool");
|
|
|
|
ui.add_sized([width, height], egui::Button::new("⏹")).on_hover_text("Stop P2Pool");
|
|
|
|
});
|
|
|
|
if ui.add_sized([width, height], egui::Button::new("⏺")).on_hover_text("Start P2Pool").clicked() { self.p2pool = true; }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
Tab::Xmrig => {
|
|
|
|
ui.group(|ui| {
|
|
|
|
let width = width / 1.5;
|
|
|
|
if ui.add_sized([width, height], egui::SelectableLabel::new(!self.state.xmrig.simple, "Advanced")).clicked() {
|
|
|
|
self.state.xmrig.simple = false;
|
|
|
|
}
|
|
|
|
ui.separator();
|
|
|
|
if ui.add_sized([width, height], egui::SelectableLabel::new(self.state.xmrig.simple, "Simple")).clicked() {
|
|
|
|
self.state.xmrig.simple = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
ui.group(|ui| {
|
|
|
|
let width = (ui.available_width()/3.0)-5.0;
|
|
|
|
if self.xmrig {
|
|
|
|
if ui.add_sized([width, height], egui::Button::new("⟲")).on_hover_text("Restart XMRig").clicked() { self.xmrig = false; }
|
|
|
|
if ui.add_sized([width, height], egui::Button::new("⏹")).on_hover_text("Stop XMRig").clicked() { self.xmrig = false; }
|
|
|
|
ui.add_enabled_ui(false, |ui| {
|
|
|
|
ui.add_sized([width, height], egui::Button::new("⏺")).on_hover_text("Start XMRig");
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
ui.add_enabled_ui(false, |ui| {
|
|
|
|
ui.add_sized([width, height], egui::Button::new("⟲")).on_hover_text("Restart XMRig");
|
|
|
|
ui.add_sized([width, height], egui::Button::new("⏹")).on_hover_text("Stop XMRig");
|
|
|
|
});
|
|
|
|
if ui.add_sized([width, height], egui::Button::new("⏺")).on_hover_text("Start XMRig").clicked() { self.xmrig = true; }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
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-10-17 02:28:41 +00:00
|
|
|
egui::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-10-17 02:28:41 +00:00
|
|
|
ui.style_mut().override_text_style = Some(egui::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
|
|
|
|
self.banner.show_max_size(ui, Vec2::new(self.width, self.height/4.0));
|
|
|
|
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-10-27 03:15:56 +00:00
|
|
|
ui.add_space(ui.available_height()/2.0);
|
2022-10-17 02:28:41 +00:00
|
|
|
ui.hyperlink_to("Powered by egui", "https://github.com/emilk/egui");
|
|
|
|
ui.hyperlink_to(format!("{} {}", GITHUB, "Gupax made by hinto-janaiyo"), "https://www.github.com/hinto-janaiyo/gupax");
|
|
|
|
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 => {
|
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
|
|
|
Gupax::show(&mut self.state.gupax, &self.og, &self.state.version, &self.update, self.width, self.height, 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 => {
|
|
|
|
Xmrig::show(&mut self.state.xmrig, self.width, self.height, ctx, ui);
|
|
|
|
}
|
|
|
|
}
|
2022-10-01 16:58:22 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|