mirror of
https://github.com/Cyrix126/gupaxx.git
synced 2024-11-16 15:27:46 +00:00
helper/main: fix [Arc<Mutex>] deadlocks, add keyboard shortcuts
There was a deadlock happening between the [Helper]'s [gui_api_p2pool] and the GUI's [gui_api_p2pool], since the locking order was different. The watchdog loop locking order was fixed as well. This was a pain to track down, better than a data race... I guess. Oh and keyboard shortcuts were added in this commit too. Comment from code: // The ordering of these locks is _very_ important. They MUST be in sync // with how the main GUI thread locks stuff or a deadlock will occur // given enough time. They will eventually both want to lock the [Arc<Mutex>] // the other thread is already locking. Yes, I figured this out the hard way, // hence the vast amount of debug!() messages. // // Example of different order (BAD!): // // GUI Main -> locks [p2pool] first // Helper -> locks [gui_api_p2pool] first // GUI Status Tab -> trys to lock [gui_api_p2pool] -> CAN'T // Helper -> trys to lock [p2pool] -> CAN'T // // These two threads are now in a deadlock because both // are trying to access locks the other one already has. // // The locking order here must be in the same chronological // order as the main GUI thread (top to bottom).
This commit is contained in:
parent
128fa500bb
commit
79b0361152
6 changed files with 229 additions and 77 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -2796,9 +2796,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.9"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
|
||||
checksum = "cf1c2c742266c2f1041c914ba65355a83ae8747b05f208319784083583494b4b"
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
|
@ -3420,18 +3420,18 @@ checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.149"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055"
|
||||
checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.149"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4"
|
||||
checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
![banner.png](https://github.com/hinto-janaiyo/gupax/blob/main/images/banner.png)
|
||||
Gupax is a (Windows|macOS|Linux) GUI for mining [**Monero**](https://github.com/monero-project/monero) on [**P2Pool**](https://github.com/SChernykh/p2pool), using [**XMRig**](https://github.com/xmrig/xmrig).
|
||||
|
||||
**If you just want to see a short 1-minute video on how to download and run Gupax: [click here.](#Video)**
|
||||
**To see a 1-minute video on how to download and run Gupax: [click here.](#Video)**
|
||||
|
||||
## Contents
|
||||
* [What is Monero/P2Pool/XMRig/Gupax?](#what-is-monero-p2pool-xmrig-and-gupax)
|
||||
|
|
|
@ -40,6 +40,19 @@ pub const HORIZONTAL: &str = "--------------------------------------------";
|
|||
// The text to separate my "process stopped, here's stats" text from the process output in the console.
|
||||
pub const HORI_CONSOLE: &str = "---------------------------------------------------------------------------------------------------------------------------";
|
||||
|
||||
// Keyboard shortcuts
|
||||
pub const KEYBOARD_SHORTCUTS: &str =
|
||||
r#"*--------------------------------------*
|
||||
| Key shortcuts |
|
||||
|--------------------------------------|
|
||||
| F11 | Fullscreen |
|
||||
| Escape | Quit screen |
|
||||
| Left/Right | Switch Tabs |
|
||||
| Up | Start/Restart |
|
||||
| Down | Stop |
|
||||
| S | Save |
|
||||
| R | Reset |
|
||||
*--------------------------------------*"#;
|
||||
// P2Pool & XMRig default API stuff
|
||||
#[cfg(target_os = "windows")]
|
||||
pub const P2POOL_API_PATH: &str = r"local\stats"; // The default relative FS path of P2Pool's local API
|
||||
|
|
|
@ -558,6 +558,12 @@ impl Helper {
|
|||
}
|
||||
drop(lock);
|
||||
|
||||
|
||||
// Check if logs need resetting
|
||||
debug!("P2Pool Watchdog | Attempting log reset check");
|
||||
Self::check_reset_output_full(&output_full, ProcessName::P2pool);
|
||||
Self::check_reset_gui_p2pool_output(&gui_api);
|
||||
|
||||
// Always update from output
|
||||
debug!("P2Pool Watchdog | Starting [update_from_output()]");
|
||||
PubP2poolApi::update_from_output(&pub_api, &output_full, &output_buf, start.elapsed(), ®ex);
|
||||
|
@ -572,11 +578,6 @@ impl Helper {
|
|||
}
|
||||
}
|
||||
|
||||
// Check if logs need resetting
|
||||
debug!("P2Pool Watchdog | Attempting log reset check");
|
||||
Self::check_reset_output_full(&output_full, ProcessName::P2pool);
|
||||
Self::check_reset_gui_p2pool_output(&gui_api);
|
||||
|
||||
// Sleep (only if 900ms hasn't passed)
|
||||
let elapsed = now.elapsed().as_millis();
|
||||
// Since logic goes off if less than 1000, casting should be safe
|
||||
|
@ -910,6 +911,11 @@ impl Helper {
|
|||
}
|
||||
drop(lock);
|
||||
|
||||
// Check if logs need resetting
|
||||
debug!("XMRig Watchdog | Attempting log reset check");
|
||||
Self::check_reset_output_full(&output_full, ProcessName::Xmrig);
|
||||
Self::check_reset_gui_xmrig_output(&gui_api);
|
||||
|
||||
// Always update from output
|
||||
debug!("XMRig Watchdog | Starting [update_from_output()]");
|
||||
PubXmrigApi::update_from_output(&pub_api, &output_buf, start.elapsed());
|
||||
|
@ -923,11 +929,6 @@ impl Helper {
|
|||
warn!("XMRig Watchdog | Could not send HTTP API request to: {}", api_ip_port);
|
||||
}
|
||||
|
||||
// Check if logs need resetting
|
||||
debug!("XMRig Watchdog | Attempting log reset check");
|
||||
Self::check_reset_output_full(&output_full, ProcessName::Xmrig);
|
||||
Self::check_reset_gui_xmrig_output(&gui_api);
|
||||
|
||||
// Sleep (only if 900ms hasn't passed)
|
||||
let elapsed = now.elapsed().as_millis();
|
||||
// Since logic goes off if less than 1000, casting should be safe
|
||||
|
@ -978,8 +979,33 @@ impl Helper {
|
|||
|
||||
// The "helper" thread. Syncs data between threads here and the GUI.
|
||||
pub fn spawn_helper(helper: &Arc<Mutex<Self>>, mut sysinfo: sysinfo::System, pid: sysinfo::Pid, max_threads: usize) {
|
||||
let mut helper = Arc::clone(helper);
|
||||
let mut pub_sys = Arc::clone(&helper.lock().unwrap().pub_sys);
|
||||
// The ordering of these locks is _very_ important. They MUST be in sync with how the main GUI thread locks stuff
|
||||
// or a deadlock will occur given enough time. They will eventually both want to lock the [Arc<Mutex>] the other
|
||||
// thread is already locking. Yes, I figured this out the hard way, hence the vast amount of debug!() messages.
|
||||
// Example of different order (BAD!):
|
||||
//
|
||||
// GUI Main -> locks [p2pool] first
|
||||
// Helper -> locks [gui_api_p2pool] first
|
||||
// GUI Status Tab -> trys to lock [gui_api_p2pool] -> CAN'T
|
||||
// Helper -> trys to lock [p2pool] -> CAN'T
|
||||
//
|
||||
// These two threads are now in a deadlock because both
|
||||
// are trying to access locks the other one already has.
|
||||
//
|
||||
// The locking order here must be in the same chronological
|
||||
// order as the main GUI thread (top to bottom).
|
||||
|
||||
let helper = Arc::clone(helper);
|
||||
let lock = helper.lock().unwrap();
|
||||
let p2pool = Arc::clone(&lock.p2pool);
|
||||
let xmrig = Arc::clone(&lock.xmrig);
|
||||
let pub_sys = Arc::clone(&lock.pub_sys);
|
||||
let gui_api_p2pool = Arc::clone(&lock.gui_api_p2pool);
|
||||
let gui_api_xmrig = Arc::clone(&lock.gui_api_xmrig);
|
||||
let pub_api_p2pool = Arc::clone(&lock.pub_api_p2pool);
|
||||
let pub_api_xmrig = Arc::clone(&lock.pub_api_xmrig);
|
||||
drop(lock);
|
||||
|
||||
let sysinfo_cpu = sysinfo::CpuRefreshKind::everything();
|
||||
let sysinfo_processes = sysinfo::ProcessRefreshKind::new().with_cpu();
|
||||
|
||||
|
@ -991,23 +1017,21 @@ impl Helper {
|
|||
let start = Instant::now();
|
||||
debug!("Helper | ----------- Start of loop -----------");
|
||||
|
||||
//
|
||||
// Ignore the invasive [debug!()] messages on the right side of the code.
|
||||
// The reason why they are there are so that it's extremely easy to track
|
||||
// down the culprit of an [Arc<Mutex>] deadlock. I know, they're ugly.
|
||||
//
|
||||
|
||||
// 2. Lock... EVERYTHING!
|
||||
let mut lock = helper.lock().unwrap(); debug!("Helper | Locking (1/8) ... [helper]");
|
||||
let mut lock = helper.lock().unwrap(); debug!("Helper | Locking (1/8) ... [helper]");
|
||||
let p2pool = p2pool.lock().unwrap(); debug!("Helper | Locking (2/8) ... [p2pool]");
|
||||
let xmrig = xmrig.lock().unwrap(); debug!("Helper | Locking (3/8) ... [xmrig]");
|
||||
let mut lock_pub_sys = pub_sys.lock().unwrap(); debug!("Helper | Locking (4/8) ... [pub_sys]");
|
||||
let mut gui_api_p2pool = gui_api_p2pool.lock().unwrap(); debug!("Helper | Locking (5/8) ... [gui_api_p2pool]");
|
||||
let mut gui_api_xmrig = gui_api_xmrig.lock().unwrap(); debug!("Helper | Locking (6/8) ... [gui_api_xmrig]");
|
||||
let mut pub_api_p2pool = pub_api_p2pool.lock().unwrap(); debug!("Helper | Locking (7/8) ... [pub_api_p2pool]");
|
||||
let mut pub_api_xmrig = pub_api_xmrig.lock().unwrap(); debug!("Helper | Locking (8/8) ... [pub_api_xmrig]");
|
||||
// Calculate Gupax's uptime always.
|
||||
lock.uptime = HumanTime::into_human(lock.instant.elapsed());
|
||||
let mut gui_api_p2pool = lock.gui_api_p2pool.lock().unwrap(); debug!("Helper | Locking (2/8) ... [gui_api_p2pool]");
|
||||
let mut gui_api_xmrig = lock.gui_api_xmrig.lock().unwrap(); debug!("Helper | Locking (3/8) ... [gui_api_xmrig]");
|
||||
let mut pub_api_p2pool = lock.pub_api_p2pool.lock().unwrap(); debug!("Helper | Locking (4/8) ... [pub_api_p2pool]");
|
||||
let mut pub_api_xmrig = lock.pub_api_xmrig.lock().unwrap(); debug!("Helper | Locking (5/8) ... [pub_api_xmrig]");
|
||||
let p2pool = lock.p2pool.lock().unwrap(); debug!("Helper | Locking (6/8) ... [p2pool]");
|
||||
let xmrig = lock.xmrig.lock().unwrap(); debug!("Helper | Locking (7/8) ... [xmrig]");
|
||||
let mut lock_pub_sys = pub_sys.lock().unwrap(); debug!("Helper | Locking (8/8) ... [pub_sys]");
|
||||
// If [P2Pool] is alive...
|
||||
if p2pool.is_alive() {
|
||||
debug!("Helper | P2Pool is alive! Running [combine_gui_pub_api()]");
|
||||
|
@ -1056,7 +1080,7 @@ impl Helper {
|
|||
// 5. End loop
|
||||
}
|
||||
|
||||
// 5. Something has gone terribly wrong if the helper exited this loop.
|
||||
// 6. Something has gone terribly wrong if the helper exited this loop.
|
||||
let text = "HELPER THREAD HAS ESCAPED THE LOOP...!";
|
||||
error!("{}", text);error!("{}", text);error!("{}", text);panic!("{}", text);
|
||||
|
||||
|
|
203
src/main.rs
203
src/main.rs
|
@ -562,9 +562,60 @@ impl Regexes {
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [Pressed] enum
|
||||
// These represent the keys pressed during the frame.
|
||||
// I could use egui's [Key] but there is no option for
|
||||
// a [None] and wrapping [key_pressed] like [Option<egui::Key>]
|
||||
// meant that I had to destructure like this:
|
||||
// if let Some(egui::Key)) = key_pressed { /* do thing */ }
|
||||
//
|
||||
// That's ugly, so these are used instead so a simple compare can be used.
|
||||
#[derive(Debug,Clone,Eq,PartialEq)]
|
||||
enum KeyPressed {
|
||||
F11,
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
Esc,
|
||||
S,
|
||||
R,
|
||||
None,
|
||||
}
|
||||
|
||||
impl KeyPressed {
|
||||
fn is_f11(&self) -> bool {
|
||||
*self == Self::F11
|
||||
}
|
||||
fn is_left(&self) -> bool {
|
||||
*self == Self::Left
|
||||
}
|
||||
fn is_right(&self) -> bool {
|
||||
*self == Self::Right
|
||||
}
|
||||
fn is_up(&self) -> bool {
|
||||
*self == Self::Up
|
||||
}
|
||||
fn is_down(&self) -> bool {
|
||||
*self == Self::Down
|
||||
}
|
||||
fn is_esc(&self) -> bool {
|
||||
*self == Self::Esc
|
||||
}
|
||||
fn is_s(&self) -> bool {
|
||||
*self == Self::S
|
||||
}
|
||||
fn is_r(&self) -> bool {
|
||||
*self == Self::R
|
||||
}
|
||||
fn is_none(&self) -> bool {
|
||||
*self == Self::None
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Init functions
|
||||
fn init_text_styles(ctx: &egui::Context, width: f32) {
|
||||
let scale = width / 26.666;
|
||||
let scale = width / 30.0;
|
||||
let mut style = (*ctx.style()).clone();
|
||||
style.text_styles = [
|
||||
(Small, FontId::new(scale/3.0, Proportional)),
|
||||
|
@ -859,15 +910,74 @@ impl eframe::App for App {
|
|||
debug!("App | ----------- Start of [update()] -----------");
|
||||
|
||||
// If [F11] was pressed, reverse [fullscreen] bool
|
||||
if ctx.input_mut().consume_key(Modifiers::NONE, Key::F11) {
|
||||
let mut input = ctx.input_mut();
|
||||
let mut key: KeyPressed = {
|
||||
if input.consume_key(Modifiers::NONE, Key::F11) {
|
||||
KeyPressed::F11
|
||||
} else if input.consume_key(Modifiers::NONE, Key::ArrowLeft) {
|
||||
KeyPressed::Left
|
||||
} else if input.consume_key(Modifiers::NONE, Key::ArrowRight) {
|
||||
KeyPressed::Right
|
||||
} else if input.consume_key(Modifiers::NONE, Key::ArrowUp) {
|
||||
KeyPressed::Up
|
||||
} else if input.consume_key(Modifiers::NONE, Key::ArrowDown) {
|
||||
KeyPressed::Down
|
||||
} else if input.consume_key(Modifiers::NONE, Key::Escape) {
|
||||
KeyPressed::Esc
|
||||
} else if input.consume_key(Modifiers::NONE, Key::S) {
|
||||
KeyPressed::S
|
||||
} else if input.consume_key(Modifiers::NONE, Key::R) {
|
||||
KeyPressed::R
|
||||
} else {
|
||||
KeyPressed::None
|
||||
}
|
||||
};
|
||||
drop(input);
|
||||
|
||||
if key.is_f11() {
|
||||
let info = frame.info();
|
||||
frame.set_fullscreen(!info.window_info.fullscreen);
|
||||
// Change Tabs LEFT
|
||||
} else if key.is_left() {
|
||||
match self.tab {
|
||||
Tab::About => self.tab = Tab::Xmrig,
|
||||
Tab::Status => self.tab = Tab::About,
|
||||
Tab::Gupax => self.tab = Tab::Status,
|
||||
Tab::P2pool => self.tab = Tab::Gupax,
|
||||
Tab::Xmrig => self.tab = Tab::P2pool,
|
||||
};
|
||||
// Change Tabs RIGHT
|
||||
} else if key.is_right() {
|
||||
match self.tab {
|
||||
Tab::About => self.tab = Tab::Status,
|
||||
Tab::Status => self.tab = Tab::Gupax,
|
||||
Tab::Gupax => self.tab = Tab::P2pool,
|
||||
Tab::P2pool => self.tab = Tab::Xmrig,
|
||||
Tab::Xmrig => self.tab = Tab::About,
|
||||
};
|
||||
}
|
||||
|
||||
// Refresh AT LEAST once a second
|
||||
debug!("App | Refreshing frame once per second");
|
||||
ctx.request_repaint_after(SECOND);
|
||||
|
||||
// Get P2Pool/XMRig process state.
|
||||
// These values are checked multiple times so
|
||||
// might as well check only once here to save
|
||||
// on a bunch of [.lock().unwrap()]s.
|
||||
debug!("App | Locking and collecting P2Pool state...");
|
||||
let p2pool = self.p2pool.lock().unwrap();
|
||||
let p2pool_is_alive = p2pool.is_alive();
|
||||
let p2pool_is_waiting = p2pool.is_waiting();
|
||||
let p2pool_state = p2pool.state;
|
||||
drop(p2pool);
|
||||
debug!("App | Locking and collecting XMRig state...");
|
||||
let xmrig = self.xmrig.lock().unwrap();
|
||||
let xmrig_is_alive = xmrig.is_alive();
|
||||
let xmrig_is_waiting = xmrig.is_waiting();
|
||||
let xmrig_state = xmrig.state;
|
||||
drop(xmrig);
|
||||
|
||||
// This sets the top level Ui dimensions.
|
||||
// Used as a reference for other uis.
|
||||
debug!("App | Setting width/height");
|
||||
|
@ -950,8 +1060,8 @@ impl eframe::App for App {
|
|||
StayQuit => {
|
||||
let mut text = "".to_string();
|
||||
if *self.update.lock().unwrap().updating.lock().unwrap() { text = format!("{}\nUpdate is in progress...! Quitting may cause file corruption!", text); }
|
||||
if self.p2pool.lock().unwrap().is_alive() { text = format!("{}\nP2Pool is online...!", text); }
|
||||
if self.xmrig.lock().unwrap().is_alive() { text = format!("{}\nXMRig is online...!", text); }
|
||||
if p2pool_is_alive { text = format!("{}\nP2Pool is online...!", text); }
|
||||
if xmrig_is_alive { text = format!("{}\nXMRig is online...!", text); }
|
||||
ui.add_sized([width, height], Label::new("--- Are you sure you want to quit? ---"));
|
||||
ui.add_sized([width, height], Label::new(text))
|
||||
},
|
||||
|
@ -985,18 +1095,15 @@ impl eframe::App for App {
|
|||
};
|
||||
let height = ui.available_height();
|
||||
|
||||
// Capture [Esc] key
|
||||
let esc = ctx.input_mut().consume_key(Modifiers::NONE, Key::Escape);
|
||||
|
||||
match self.error_state.buttons {
|
||||
YesNo => {
|
||||
if ui.add_sized([width, height/2.0], Button::new("Yes")).clicked() { self.error_state.reset() }
|
||||
// If [Esc] was pressed, assume [No]
|
||||
if esc || ui.add_sized([width, height/2.0], Button::new("No")).clicked() { exit(0); }
|
||||
if key.is_esc() || ui.add_sized([width, height/2.0], Button::new("No")).clicked() { exit(0); }
|
||||
},
|
||||
StayQuit => {
|
||||
// If [Esc] was pressed, assume [Stay]
|
||||
if esc || ui.add_sized([width, height/2.0], Button::new("Stay")).clicked() {
|
||||
if key.is_esc() || ui.add_sized([width, height/2.0], Button::new("Stay")).clicked() {
|
||||
self.error_state = ErrorState::new();
|
||||
}
|
||||
if ui.add_sized([width, height/2.0], Button::new("Quit")).clicked() { exit(0); }
|
||||
|
@ -1020,7 +1127,7 @@ impl eframe::App for App {
|
|||
Err(e) => self.error_state.set(format!("State reset fail: {}", e), ErrorFerris::Panic, ErrorButtons::Quit),
|
||||
};
|
||||
}
|
||||
if esc || ui.add_sized([width, height/2.0], Button::new("No")).clicked() { self.error_state.reset() }
|
||||
if key.is_esc() || ui.add_sized([width, height/2.0], Button::new("No")).clicked() { self.error_state.reset() }
|
||||
},
|
||||
ResetNode => {
|
||||
if ui.add_sized([width, height/2.0], Button::new("Yes")).clicked() {
|
||||
|
@ -1038,7 +1145,7 @@ impl eframe::App for App {
|
|||
Err(e) => self.error_state.set(format!("Node reset fail: {}", e), ErrorFerris::Panic, ErrorButtons::Quit),
|
||||
};
|
||||
}
|
||||
if esc || ui.add_sized([width, height/2.0], Button::new("No")).clicked() { self.error_state.reset() }
|
||||
if key.is_esc() || ui.add_sized([width, height/2.0], Button::new("No")).clicked() { self.error_state.reset() }
|
||||
},
|
||||
ErrorButtons::Sudo => {
|
||||
let sudo_width = width/10.0;
|
||||
|
@ -1067,13 +1174,13 @@ impl eframe::App for App {
|
|||
let color = if hide { BLACK } else { BRIGHT_YELLOW };
|
||||
if ui.add_sized([box_width, height], Button::new(RichText::new("👁").color(color))).on_hover_text(PASSWORD_HIDE).clicked() { sudo.hide = !sudo.hide; }
|
||||
});
|
||||
if (esc && !sudo.testing) || ui.add_sized([width, height*4.0], Button::new("Leave")).clicked() { self.error_state.reset(); };
|
||||
if (key.is_esc() && !sudo.testing) || ui.add_sized([width, height*4.0], Button::new("Leave")).clicked() { self.error_state.reset(); };
|
||||
// If [test_sudo()] finished, reset error state.
|
||||
if sudo.success {
|
||||
self.error_state.reset();
|
||||
}
|
||||
},
|
||||
Okay|WindowsAdmin => if esc || ui.add_sized([width, height], Button::new("Okay")).clicked() { self.error_state.reset(); },
|
||||
Okay|WindowsAdmin => if key.is_esc() || ui.add_sized([width, height], Button::new("Okay")).clicked() { self.error_state.reset(); },
|
||||
Quit => if ui.add_sized([width, height], Button::new("Quit")).clicked() { exit(1); },
|
||||
}
|
||||
})});
|
||||
|
@ -1148,14 +1255,14 @@ impl eframe::App for App {
|
|||
ui.separator();
|
||||
// [P2Pool/XMRig] Status
|
||||
use ProcessState::*;
|
||||
match self.p2pool.lock().unwrap().state {
|
||||
match p2pool_state {
|
||||
Alive => ui.add_sized([width, height], Label::new(RichText::new("P2Pool ⏺").color(GREEN))).on_hover_text(P2POOL_ALIVE),
|
||||
Dead => ui.add_sized([width, height], Label::new(RichText::new("P2Pool ⏺").color(GRAY))).on_hover_text(P2POOL_DEAD),
|
||||
Failed => ui.add_sized([width, height], Label::new(RichText::new("P2Pool ⏺").color(RED))).on_hover_text(P2POOL_FAILED),
|
||||
Middle|Waiting => ui.add_sized([width, height], Label::new(RichText::new("P2Pool ⏺").color(YELLOW))).on_hover_text(P2POOL_MIDDLE),
|
||||
};
|
||||
ui.separator();
|
||||
match self.xmrig.lock().unwrap().state {
|
||||
match xmrig_state {
|
||||
Alive => ui.add_sized([width, height], Label::new(RichText::new("XMRig ⏺").color(GREEN))).on_hover_text(XMRIG_ALIVE),
|
||||
Dead => ui.add_sized([width, height], Label::new(RichText::new("XMRig ⏺").color(GRAY))).on_hover_text(XMRIG_DEAD),
|
||||
Failed => ui.add_sized([width, height], Label::new(RichText::new("XMRig ⏺").color(RED))).on_hover_text(XMRIG_FAILED),
|
||||
|
@ -1172,7 +1279,7 @@ impl eframe::App for App {
|
|||
ui.group(|ui| {
|
||||
ui.set_enabled(self.diff);
|
||||
let width = width / 2.0;
|
||||
if ui.add_sized([width, height], Button::new("Reset")).on_hover_text("Reset changes").clicked() {
|
||||
if key.is_r() || ui.add_sized([width, height], Button::new("Reset")).on_hover_text("Reset changes").clicked() {
|
||||
let og = self.og.lock().unwrap().clone();
|
||||
self.state.gupax = og.gupax;
|
||||
self.state.p2pool = og.p2pool;
|
||||
|
@ -1180,7 +1287,7 @@ impl eframe::App for App {
|
|||
self.node_vec = self.og_node_vec.clone();
|
||||
self.pool_vec = self.og_pool_vec.clone();
|
||||
}
|
||||
if ui.add_sized([width, height], Button::new("Save")).on_hover_text("Save changes").clicked() {
|
||||
if key.is_s() || ui.add_sized([width, height], Button::new("Save")).on_hover_text("Save changes").clicked() {
|
||||
match State::save(&mut self.state, &self.state_path) {
|
||||
Ok(_) => {
|
||||
let mut og = self.og.lock().unwrap();
|
||||
|
@ -1230,17 +1337,17 @@ impl eframe::App for App {
|
|||
});
|
||||
ui.group(|ui| {
|
||||
let width = (ui.available_width()/3.0)-5.0;
|
||||
if self.p2pool.lock().unwrap().is_waiting() {
|
||||
if p2pool_is_waiting {
|
||||
ui.add_enabled_ui(false, |ui| {
|
||||
ui.add_sized([width, height], Button::new("⟲")).on_disabled_hover_text("Restart P2Pool");
|
||||
ui.add_sized([width, height], Button::new("⏹")).on_disabled_hover_text("Stop P2Pool");
|
||||
ui.add_sized([width, height], Button::new("▶")).on_disabled_hover_text("Start P2Pool");
|
||||
});
|
||||
} else if self.p2pool.lock().unwrap().is_alive() {
|
||||
if ui.add_sized([width, height], Button::new("⟲")).on_hover_text("Restart P2Pool").clicked() {
|
||||
} else if p2pool_is_alive {
|
||||
if key.is_up() || ui.add_sized([width, height], Button::new("⟲")).on_hover_text("Restart P2Pool").clicked() {
|
||||
Helper::restart_p2pool(&self.helper, &self.state.p2pool, &self.state.gupax.absolute_p2pool_path);
|
||||
}
|
||||
if ui.add_sized([width, height], Button::new("⏹")).on_hover_text("Stop P2Pool").clicked() {
|
||||
if key.is_down() || ui.add_sized([width, height], Button::new("⏹")).on_hover_text("Stop P2Pool").clicked() {
|
||||
Helper::stop_p2pool(&self.helper);
|
||||
}
|
||||
ui.add_enabled_ui(false, |ui| {
|
||||
|
@ -1260,7 +1367,7 @@ impl eframe::App for App {
|
|||
ui.set_enabled(false);
|
||||
text = P2POOL_PATH_NOT_EXE.to_string();
|
||||
}
|
||||
if ui.add_sized([width, height], Button::new("▶")).on_hover_text("Start P2Pool").on_disabled_hover_text(text).clicked() {
|
||||
if key.is_up() || ui.add_sized([width, height], Button::new("▶")).on_hover_text("Start P2Pool").on_disabled_hover_text(text).clicked() {
|
||||
Helper::start_p2pool(&self.helper, &self.state.p2pool, &self.state.gupax.absolute_p2pool_path);
|
||||
}
|
||||
}
|
||||
|
@ -1279,14 +1386,14 @@ impl eframe::App for App {
|
|||
});
|
||||
ui.group(|ui| {
|
||||
let width = (ui.available_width()/3.0)-5.0;
|
||||
if self.xmrig.lock().unwrap().is_waiting() {
|
||||
if xmrig_is_waiting {
|
||||
ui.add_enabled_ui(false, |ui| {
|
||||
ui.add_sized([width, height], Button::new("⟲")).on_disabled_hover_text("Restart XMRig");
|
||||
ui.add_sized([width, height], Button::new("⏹")).on_disabled_hover_text("Stop XMRig");
|
||||
ui.add_sized([width, height], Button::new("▶")).on_disabled_hover_text("Start XMRig");
|
||||
});
|
||||
} else if self.xmrig.lock().unwrap().is_alive() {
|
||||
if ui.add_sized([width, height], Button::new("⟲")).on_hover_text("Restart XMRig").clicked() {
|
||||
} else if xmrig_is_alive {
|
||||
if key.is_up() || ui.add_sized([width, height], Button::new("⟲")).on_hover_text("Restart XMRig").clicked() {
|
||||
if cfg!(windows) {
|
||||
Helper::restart_xmrig(&self.helper, &self.state.xmrig, &self.state.gupax.absolute_xmrig_path, Arc::clone(&self.sudo));
|
||||
} else if cfg!(target_os = "macos") {
|
||||
|
@ -1297,7 +1404,7 @@ impl eframe::App for App {
|
|||
self.error_state.ask_sudo(&self.sudo);
|
||||
}
|
||||
}
|
||||
if ui.add_sized([width, height], Button::new("⏹")).on_hover_text("Stop XMRig").clicked() {
|
||||
if key.is_down() || ui.add_sized([width, height], Button::new("⏹")).on_hover_text("Stop XMRig").clicked() {
|
||||
if cfg!(target_os = "macos") {
|
||||
self.sudo.lock().unwrap().signal = ProcessSignal::Stop;
|
||||
self.error_state.ask_sudo(&self.sudo);
|
||||
|
@ -1319,11 +1426,11 @@ impl eframe::App for App {
|
|||
text = XMRIG_PATH_NOT_EXE.to_string();
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
if ui.add_sized([width, height], Button::new("▶")).on_hover_text("Start XMRig").on_disabled_hover_text(text).clicked() {
|
||||
if key.is_up() || ui.add_sized([width, height], Button::new("▶")).on_hover_text("Start XMRig").on_disabled_hover_text(text).clicked() {
|
||||
Helper::start_xmrig(&self.helper, &self.state.xmrig, &self.state.gupax.absolute_xmrig_path, Arc::clone(&self.sudo));
|
||||
}
|
||||
#[cfg(target_family = "unix")]
|
||||
if ui.add_sized([width, height], Button::new("▶")).on_hover_text("Start XMRig").on_disabled_hover_text(text).clicked() {
|
||||
if key.is_up() || ui.add_sized([width, height], Button::new("▶")).on_hover_text("Start XMRig").on_disabled_hover_text(text).clicked() {
|
||||
self.sudo.lock().unwrap().signal = ProcessSignal::Start;
|
||||
self.error_state.ask_sudo(&self.sudo);
|
||||
}
|
||||
|
@ -1346,29 +1453,37 @@ impl eframe::App for App {
|
|||
match self.tab {
|
||||
Tab::About => {
|
||||
debug!("App | Entering [About] Tab");
|
||||
let width = self.width;
|
||||
let height = self.height/30.0;
|
||||
let max_height = self.height;
|
||||
ui.add_space(10.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
// Display [Gupax] banner at max, 1/4 the available length
|
||||
self.img.banner.show_max_size(ui, Vec2::new(self.width, self.height/4.0));
|
||||
ui.label("Gupax is a cross-platform GUI for mining");
|
||||
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");
|
||||
ui.set_max_height(max_height);
|
||||
// Display [Gupax] banner
|
||||
self.img.banner.show_max_size(ui, Vec2::new(width, height*3.0));
|
||||
ui.add_sized([width, height], Label::new("Gupax is a cross-platform GUI for mining"));
|
||||
ui.add_sized([width, height], Hyperlink::from_label_and_url("[Monero]", "https://www.github.com/monero-project/monero"));
|
||||
ui.add_sized([width, height], Label::new("on"));
|
||||
ui.add_sized([width, height], Hyperlink::from_label_and_url("[P2Pool]", "https://www.github.com/SChernykh/p2pool"));
|
||||
ui.add_sized([width, height], Label::new("using"));
|
||||
ui.add_sized([width, height], Hyperlink::from_label_and_url("[XMRig]", "https://www.github.com/xmrig/xmrig"));
|
||||
|
||||
ui.add_space(ui.available_height()/1.8);
|
||||
ui.hyperlink_to("Powered by egui", "https://github.com/emilk/egui");
|
||||
ui.hyperlink_to("Made by hinto-janaiyo".to_string(), "https://gupax.io");
|
||||
ui.label("egui is licensed under MIT & Apache-2.0");
|
||||
ui.label("Gupax, P2Pool, and XMRig are licensed under GPLv3");
|
||||
if cfg!(debug_assertions) { ui.label(format!("{}", self.now.elapsed().as_secs_f64())); }
|
||||
ui.add_space(SPACE*3.0);
|
||||
ui.style_mut().override_text_style = Some(Monospace);
|
||||
ui.add_sized([width, height], Label::new(KEYBOARD_SHORTCUTS));
|
||||
ui.style_mut().override_text_style = Some(Body);
|
||||
ui.add_space(SPACE*3.0);
|
||||
|
||||
ui.add_sized([width, height], Hyperlink::from_label_and_url("Powered by egui", "https://github.com/emilk/egui"));
|
||||
ui.add_sized([width, height], Hyperlink::from_label_and_url("Made by hinto-janaiyo".to_string(), "https://gupax.io"));
|
||||
ui.add_sized([width, height], Label::new("egui is licensed under MIT & Apache-2.0"));
|
||||
ui.add_sized([width, height], Label::new("Gupax, P2Pool, and XMRig are licensed under GPLv3"));
|
||||
if cfg!(debug_assertions) { ui.label(format!("Gupax is running in debug mode - {}", self.now.elapsed().as_secs_f64())); }
|
||||
});
|
||||
}
|
||||
Tab::Status => {
|
||||
debug!("App | Entering [Status] Tab");
|
||||
Status::show(&self.pub_sys, &self.p2pool_api, &self.xmrig_api, &self.p2pool_img, &self.xmrig_img, self.p2pool.lock().unwrap().is_alive(), self.xmrig.lock().unwrap().is_alive(), self.width, self.height, ctx, ui);
|
||||
Status::show(&self.pub_sys, &self.p2pool_api, &self.xmrig_api, &self.p2pool_img, &self.xmrig_img, p2pool_is_alive, xmrig_is_alive, self.width, self.height, ctx, ui);
|
||||
}
|
||||
Tab::Gupax => {
|
||||
debug!("App | Entering [Gupax] Tab");
|
||||
|
|
|
@ -36,7 +36,7 @@ use egui::{
|
|||
pub struct Status {}
|
||||
|
||||
impl Status {
|
||||
pub fn show(sys: &Arc<Mutex<Sys>>, p2pool_api: &Arc<Mutex<PubP2poolApi>>, xmrig_api: &Arc<Mutex<PubXmrigApi>>, p2pool_img: &Arc<Mutex<ImgP2pool>>, xmrig_img: &Arc<Mutex<ImgXmrig>>, p2pool_online: bool, xmrig_online: bool, width: f32, height: f32, ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||
pub fn show(sys: &Arc<Mutex<Sys>>, p2pool_api: &Arc<Mutex<PubP2poolApi>>, xmrig_api: &Arc<Mutex<PubXmrigApi>>, p2pool_img: &Arc<Mutex<ImgP2pool>>, xmrig_img: &Arc<Mutex<ImgXmrig>>, p2pool_alive: bool, xmrig_alive: bool, width: f32, height: f32, ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||
let width = (width/3.0)-(SPACE*1.666);
|
||||
let min_height = height/1.14;
|
||||
let height = height/25.0;
|
||||
|
@ -64,7 +64,7 @@ pub fn show(sys: &Arc<Mutex<Sys>>, p2pool_api: &Arc<Mutex<PubP2poolApi>>, xmrig_
|
|||
// [P2Pool]
|
||||
ui.group(|ui| { ui.vertical(|ui| {
|
||||
debug!("Status Tab | Rendering [P2Pool]");
|
||||
ui.set_enabled(p2pool_online);
|
||||
ui.set_enabled(p2pool_alive);
|
||||
ui.set_min_height(min_height);
|
||||
ui.add_sized([width, height*2.0], Label::new(RichText::new("[P2Pool]").color(LIGHT_GRAY).text_style(TextStyle::Name("MonospaceLarge".into())))).on_hover_text("P2Pool is online").on_disabled_hover_text("P2Pool is offline");
|
||||
let api = p2pool_api.lock().unwrap();
|
||||
|
@ -89,7 +89,7 @@ pub fn show(sys: &Arc<Mutex<Sys>>, p2pool_api: &Arc<Mutex<PubP2poolApi>>, xmrig_
|
|||
// [XMRig]
|
||||
ui.group(|ui| { ui.vertical(|ui| {
|
||||
debug!("Status Tab | Rendering [XMRig]");
|
||||
ui.set_enabled(xmrig_online);
|
||||
ui.set_enabled(xmrig_alive);
|
||||
ui.set_min_height(min_height);
|
||||
ui.add_sized([width, height*2.0], Label::new(RichText::new("[XMRig]").color(LIGHT_GRAY).text_style(TextStyle::Name("MonospaceLarge".into())))).on_hover_text("XMRig is online").on_disabled_hover_text("XMRig is offline");
|
||||
let api = xmrig_api.lock().unwrap();
|
||||
|
|
Loading…
Reference in a new issue