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-11-16 19:40:25 +00:00
|
|
|
|
use crate::State;
|
2022-11-14 02:56:25 +00:00
|
|
|
|
use egui::{
|
|
|
|
|
TextStyle::Monospace,
|
|
|
|
|
RichText,
|
|
|
|
|
Label,
|
|
|
|
|
Color32,
|
|
|
|
|
};
|
2022-10-01 16:58:22 +00:00
|
|
|
|
use crate::constants::*;
|
2022-11-14 02:56:25 +00:00
|
|
|
|
use crate::disk::{Gupax,Version};
|
2022-10-25 02:58:42 +00:00
|
|
|
|
use crate::update::*;
|
2022-11-20 18:31:00 +00:00
|
|
|
|
use std::{
|
|
|
|
|
thread,
|
|
|
|
|
sync::{Arc,Mutex},
|
|
|
|
|
path::PathBuf,
|
|
|
|
|
};
|
2022-10-27 03:15:56 +00:00
|
|
|
|
use log::*;
|
2022-10-01 16:58:22 +00:00
|
|
|
|
|
2022-11-17 18:03:45 +00:00
|
|
|
|
//---------------------------------------------------------------------------------------------------- FileWindow
|
|
|
|
|
// Struct for writing/reading the path state.
|
|
|
|
|
// The opened file picker is started in a new
|
|
|
|
|
// thread so main() needs to be in sync.
|
|
|
|
|
pub struct FileWindow {
|
|
|
|
|
thread: bool, // Is there already a FileWindow thread?
|
|
|
|
|
picked_p2pool: bool, // Did the user pick a path for p2pool?
|
|
|
|
|
picked_xmrig: bool, // Did the user pick a path for xmrig?
|
|
|
|
|
p2pool_path: String, // The picked p2pool path
|
|
|
|
|
xmrig_path: String, // The picked p2pool path
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FileWindow {
|
|
|
|
|
pub fn new() -> Arc<Mutex<Self>> {
|
|
|
|
|
Arc::new(Mutex::new(Self {
|
|
|
|
|
thread: false,
|
|
|
|
|
picked_p2pool: false,
|
|
|
|
|
picked_xmrig: false,
|
|
|
|
|
p2pool_path: String::new(),
|
|
|
|
|
xmrig_path: String::new(),
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------------- Gupax
|
2022-10-01 16:58:22 +00:00
|
|
|
|
impl Gupax {
|
2022-11-20 18:31:00 +00:00
|
|
|
|
pub fn show(&mut self, og: &Arc<Mutex<State>>, state_ver: &Arc<Mutex<Version>>, update: &Arc<Mutex<Update>>, file_window: &Arc<Mutex<FileWindow>>, state_path: &PathBuf, width: f32, height: f32, ctx: &egui::Context, ui: &mut egui::Ui) {
|
2022-10-27 03:15:56 +00:00
|
|
|
|
// Update button + Progress bar
|
|
|
|
|
ui.group(|ui| {
|
|
|
|
|
// These are in unnecessary [ui.vertical()]'s
|
|
|
|
|
// because I need to use [ui.set_enabled]s, but I can't
|
|
|
|
|
// find a way to use a [ui.xxx()] with [ui.add_sized()].
|
|
|
|
|
// I have to pick one. This one seperates them though.
|
2022-11-14 02:56:25 +00:00
|
|
|
|
let height = height/8.0;
|
2022-10-27 03:15:56 +00:00
|
|
|
|
let width = width - SPACE;
|
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 updating = *update.lock().unwrap().updating.lock().unwrap();
|
2022-10-27 03:15:56 +00:00
|
|
|
|
ui.vertical(|ui| {
|
2022-10-27 15:52:18 +00:00
|
|
|
|
ui.set_enabled(!updating);
|
2022-10-27 03:15:56 +00:00
|
|
|
|
if ui.add_sized([width, height], egui::Button::new("Check for updates")).on_hover_text(GUPAX_UPDATE).clicked() {
|
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.lock().unwrap().path_p2pool = og.lock().unwrap().gupax.absolute_p2pool_path.display().to_string();
|
|
|
|
|
update.lock().unwrap().path_xmrig = og.lock().unwrap().gupax.absolute_xmrig_path.display().to_string();
|
|
|
|
|
update.lock().unwrap().tor = og.lock().unwrap().gupax.update_via_tor;
|
|
|
|
|
let og = Arc::clone(&og);
|
|
|
|
|
let state_ver = Arc::clone(&state_ver);
|
|
|
|
|
let update = Arc::clone(&update);
|
|
|
|
|
let update_thread = Arc::clone(&update);
|
2022-11-20 18:31:00 +00:00
|
|
|
|
let state_path = state_path.clone();
|
2022-10-27 03:15:56 +00:00
|
|
|
|
thread::spawn(move|| {
|
|
|
|
|
info!("Spawning update thread...");
|
2022-11-19 14:39:26 +00:00
|
|
|
|
match Update::start(update_thread, og.clone(), state_ver.clone()) {
|
2022-10-27 15:52:18 +00:00
|
|
|
|
Err(e) => {
|
2022-11-02 22:18:41 +00:00
|
|
|
|
info!("Update ... FAIL ... {}", e);
|
2022-10-28 20:14:43 +00:00
|
|
|
|
*update.lock().unwrap().msg.lock().unwrap() = format!("{} | {}", MSG_FAILED, e);
|
2022-10-27 15:52:18 +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
|
|
|
|
_ => {
|
|
|
|
|
info!("Update | Saving state...");
|
2022-11-20 18:31:00 +00:00
|
|
|
|
match State::save(&mut og.lock().unwrap(), &state_path) {
|
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(_) => 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;
|
2022-10-27 03:15:56 +00:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
ui.vertical(|ui| {
|
2022-10-27 15:52:18 +00:00
|
|
|
|
ui.set_enabled(updating);
|
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 prog = *update.lock().unwrap().prog.lock().unwrap();
|
|
|
|
|
let msg = format!("{}\n{}{}", *update.lock().unwrap().msg.lock().unwrap(), prog, "%");
|
2022-11-11 02:20:31 +00:00
|
|
|
|
ui.add_sized([width, height*1.4], egui::Label::new(RichText::text_style(RichText::new(msg), Monospace)));
|
2022-10-27 03:15:56 +00:00
|
|
|
|
let height = height/2.0;
|
2022-10-28 19:45:13 +00:00
|
|
|
|
if updating {
|
|
|
|
|
ui.add_sized([width, height], egui::Spinner::new().size(height));
|
|
|
|
|
} else {
|
|
|
|
|
ui.add_sized([width, height], egui::Label::new("..."));
|
|
|
|
|
}
|
2022-11-16 19:40:25 +00:00
|
|
|
|
ui.add_sized([width, height], egui::ProgressBar::new(update.lock().unwrap().prog.lock().unwrap().round() / 100.0));
|
2022-10-27 03:15:56 +00:00
|
|
|
|
});
|
|
|
|
|
});
|
2022-10-01 16:58:22 +00:00
|
|
|
|
|
|
|
|
|
ui.horizontal(|ui| {
|
|
|
|
|
ui.group(|ui| {
|
2022-11-14 02:56:25 +00:00
|
|
|
|
let width = (width - SPACE*7.5)/4.0;
|
|
|
|
|
let height = height/8.0;
|
2022-10-17 00:36:58 +00:00
|
|
|
|
let mut style = (*ctx.style()).clone();
|
2022-11-14 02:56:25 +00:00
|
|
|
|
style.spacing.icon_width_inner = width / 8.0;
|
|
|
|
|
style.spacing.icon_width = width / 6.0;
|
2022-10-28 19:45:13 +00:00
|
|
|
|
style.spacing.icon_spacing = 20.0;
|
2022-10-17 00:36:58 +00:00
|
|
|
|
ctx.set_style(style);
|
2022-11-14 02:56:25 +00:00
|
|
|
|
ui.add_sized([width, height], egui::Checkbox::new(&mut self.auto_update, "Auto-update")).on_hover_text(GUPAX_AUTO_UPDATE);
|
2022-10-27 03:15:56 +00:00
|
|
|
|
ui.separator();
|
2022-11-14 02:56:25 +00:00
|
|
|
|
ui.add_sized([width, height], egui::Checkbox::new(&mut self.update_via_tor, "Update via Tor")).on_hover_text(GUPAX_UPDATE_VIA_TOR);
|
2022-10-27 03:15:56 +00:00
|
|
|
|
ui.separator();
|
2022-11-14 02:56:25 +00:00
|
|
|
|
ui.add_sized([width, height], egui::Checkbox::new(&mut self.ask_before_quit, "Ask before quit")).on_hover_text(GUPAX_ASK_BEFORE_QUIT);
|
2022-10-27 03:15:56 +00:00
|
|
|
|
ui.separator();
|
2022-11-14 02:56:25 +00:00
|
|
|
|
ui.add_sized([width, height], egui::Checkbox::new(&mut self.save_before_quit, "Save before quit")).on_hover_text(GUPAX_SAVE_BEFORE_QUIT);
|
2022-10-01 16:58:22 +00:00
|
|
|
|
});
|
|
|
|
|
});
|
2022-10-27 03:15:56 +00:00
|
|
|
|
ui.add_space(SPACE);
|
2022-10-01 16:58:22 +00:00
|
|
|
|
|
2022-11-14 02:56:25 +00:00
|
|
|
|
ui.style_mut().override_text_style = Some(Monospace);
|
|
|
|
|
let height = height/20.0;
|
|
|
|
|
let text_edit = (ui.available_width()/10.0)-SPACE;
|
2022-10-01 16:58:22 +00:00
|
|
|
|
ui.horizontal(|ui| {
|
2022-11-14 02:56:25 +00:00
|
|
|
|
if self.p2pool_path.is_empty() {
|
|
|
|
|
ui.add_sized([text_edit, height], Label::new(RichText::new("P2Pool Binary Path ➖").color(Color32::LIGHT_GRAY)));
|
|
|
|
|
} else {
|
|
|
|
|
match crate::disk::into_absolute_path(self.p2pool_path.clone()) {
|
|
|
|
|
Ok(path) => {
|
|
|
|
|
if path.is_file() {
|
|
|
|
|
ui.add_sized([text_edit, height], Label::new(RichText::new("P2Pool Binary Path ✔").color(Color32::from_rgb(100, 230, 100))))
|
|
|
|
|
} else {
|
|
|
|
|
ui.add_sized([text_edit, height], Label::new(RichText::new("P2Pool Binary Path ❌").color(Color32::from_rgb(230, 50, 50))))
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
_ => ui.add_sized([text_edit, height], Label::new(RichText::new("P2Pool Binary Path ❌").color(Color32::from_rgb(230, 50, 50)))),
|
|
|
|
|
};
|
|
|
|
|
}
|
2022-10-28 19:45:13 +00:00
|
|
|
|
ui.spacing_mut().text_edit_width = ui.available_width() - SPACE;
|
2022-11-17 21:53:55 +00:00
|
|
|
|
ui.set_enabled(!file_window.lock().unwrap().thread);
|
2022-11-18 03:45:57 +00:00
|
|
|
|
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
|
2022-11-17 21:53:55 +00:00
|
|
|
|
file_window.lock().unwrap().thread = true;
|
|
|
|
|
let file_window = Arc::clone(file_window);
|
|
|
|
|
thread::spawn(move|| {
|
|
|
|
|
match rfd::FileDialog::new().set_title("Select P2Pool Binary for Gupax").pick_file() {
|
|
|
|
|
Some(path) => {
|
|
|
|
|
info!("Gupax | [{}] path selected for P2Pool", path.display());
|
|
|
|
|
file_window.lock().unwrap().p2pool_path = path.display().to_string();
|
|
|
|
|
file_window.lock().unwrap().picked_p2pool = true;
|
|
|
|
|
},
|
|
|
|
|
None => info!("Gupax | No path selected for P2Pool"),
|
|
|
|
|
};
|
|
|
|
|
file_window.lock().unwrap().thread = false;
|
|
|
|
|
});
|
2022-11-17 18:03:45 +00:00
|
|
|
|
}
|
2022-11-14 02:56:25 +00:00
|
|
|
|
ui.text_edit_singleline(&mut self.p2pool_path).on_hover_text(GUPAX_PATH_P2POOL);
|
2022-10-01 16:58:22 +00:00
|
|
|
|
});
|
|
|
|
|
ui.horizontal(|ui| {
|
2022-11-14 02:56:25 +00:00
|
|
|
|
if self.xmrig_path.is_empty() {
|
|
|
|
|
ui.add_sized([text_edit, height], Label::new(RichText::new(" XMRig Binary Path ➖").color(Color32::LIGHT_GRAY)));
|
|
|
|
|
} else {
|
|
|
|
|
match crate::disk::into_absolute_path(self.xmrig_path.clone()) {
|
|
|
|
|
Ok(path) => {
|
|
|
|
|
if path.is_file() {
|
|
|
|
|
ui.add_sized([text_edit, height], Label::new(RichText::new(" XMRig Binary Path ✔").color(Color32::from_rgb(100, 230, 100))))
|
|
|
|
|
} else {
|
|
|
|
|
ui.add_sized([text_edit, height], Label::new(RichText::new(" XMRig Binary Path ❌").color(Color32::from_rgb(230, 50, 50))))
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
_ => ui.add_sized([text_edit, height], Label::new(RichText::new(" XMRig Binary Path ❌").color(Color32::from_rgb(230, 50, 50)))),
|
|
|
|
|
};
|
|
|
|
|
}
|
2022-10-28 19:45:13 +00:00
|
|
|
|
ui.spacing_mut().text_edit_width = ui.available_width() - SPACE;
|
2022-11-17 21:53:55 +00:00
|
|
|
|
ui.set_enabled(!file_window.lock().unwrap().thread);
|
2022-11-18 19:25:13 +00:00
|
|
|
|
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
|
2022-11-17 21:53:55 +00:00
|
|
|
|
file_window.lock().unwrap().thread = true;
|
|
|
|
|
let file_window = Arc::clone(file_window);
|
|
|
|
|
thread::spawn(move|| {
|
|
|
|
|
match rfd::FileDialog::new().set_title("Select XMRig Binary for Gupax").pick_file() {
|
|
|
|
|
Some(path) => {
|
|
|
|
|
info!("Gupax | [{}] path selected for XMRig", path.display());
|
|
|
|
|
file_window.lock().unwrap().xmrig_path = path.display().to_string();
|
|
|
|
|
file_window.lock().unwrap().picked_xmrig = true;
|
|
|
|
|
},
|
|
|
|
|
None => info!("Gupax | No path selected for XMRig"),
|
|
|
|
|
};
|
|
|
|
|
file_window.lock().unwrap().thread = false;
|
|
|
|
|
});
|
2022-11-17 18:03:45 +00:00
|
|
|
|
}
|
2022-11-14 02:56:25 +00:00
|
|
|
|
ui.text_edit_singleline(&mut self.xmrig_path).on_hover_text(GUPAX_PATH_XMRIG);
|
2022-10-01 16:58:22 +00:00
|
|
|
|
});
|
2022-11-17 18:03:45 +00:00
|
|
|
|
let mut guard = file_window.lock().unwrap();
|
|
|
|
|
if guard.picked_p2pool { self.p2pool_path = guard.p2pool_path.clone(); guard.picked_p2pool = false; }
|
|
|
|
|
if guard.picked_xmrig { self.xmrig_path = guard.xmrig_path.clone(); guard.picked_xmrig = false; }
|
|
|
|
|
drop(guard);
|
2022-10-01 16:58:22 +00:00
|
|
|
|
}
|
|
|
|
|
}
|