feat(mining): "Update miner" button + dialog wiring the xmrig updater
Wires util::XmrigUpdater into the GUI: - ui/windows/xmrig_download_dialog.h: a modal (mirrors BootstrapDownloadDialog) that drives the updater — Checking -> Up-to-date/Update-available -> Downloading/Verifying/Extracting -> Done/Failed, with a progress bar and a "verified against its published checksum" note. On success it persists the installed release tag to settings. Rendered each frame from App::render. - mining_tab: an "Update miner…" button in the pool section, disabled (with a tooltip) while xmrig is running so a live binary is never replaced. - settings: persist the installed DRG-XMRig tag (xmrig_version) for update detection. Both variants build; suite passes; GUI smoke-launched without crashing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -46,6 +46,7 @@
|
||||
#include "ui/windows/address_label_dialog.h"
|
||||
#include "ui/windows/address_transfer_dialog.h"
|
||||
#include "ui/windows/bootstrap_download_dialog.h"
|
||||
#include "ui/windows/xmrig_download_dialog.h"
|
||||
#include "ui/windows/console_tab.h"
|
||||
#include "ui/pages/settings_page.h"
|
||||
#include "ui/theme.h"
|
||||
@@ -1529,6 +1530,7 @@ void App::render()
|
||||
|
||||
// Bootstrap download from settings
|
||||
ui::BootstrapDownloadDialog::render();
|
||||
ui::XmrigDownloadDialog::render();
|
||||
|
||||
// Windows Defender antivirus help dialog
|
||||
renderAntivirusHelpDialog();
|
||||
|
||||
@@ -251,6 +251,7 @@ bool Settings::load(const std::string& path)
|
||||
if (j.contains("pool_hugepages")) pool_hugepages_ = j["pool_hugepages"].get<bool>();
|
||||
if (j.contains("pool_mode")) pool_mode_ = j["pool_mode"].get<bool>();
|
||||
if (j.contains("mine_when_idle")) mine_when_idle_ = j["mine_when_idle"].get<bool>();
|
||||
if (j.contains("xmrig_version")) xmrig_version_ = j["xmrig_version"].get<std::string>();
|
||||
if (j.contains("mine_idle_delay")) mine_idle_delay_= std::max(30, j["mine_idle_delay"].get<int>());
|
||||
if (j.contains("idle_thread_scaling")) idle_thread_scaling_ = j["idle_thread_scaling"].get<bool>();
|
||||
if (j.contains("idle_threads_active")) idle_threads_active_ = j["idle_threads_active"].get<int>();
|
||||
@@ -383,6 +384,7 @@ bool Settings::save(const std::string& path)
|
||||
j["pool_hugepages"] = pool_hugepages_;
|
||||
j["pool_mode"] = pool_mode_;
|
||||
j["mine_when_idle"] = mine_when_idle_;
|
||||
j["xmrig_version"] = xmrig_version_;
|
||||
j["mine_idle_delay"]= mine_idle_delay_;
|
||||
j["idle_thread_scaling"] = idle_thread_scaling_;
|
||||
j["idle_threads_active"] = idle_threads_active_;
|
||||
|
||||
@@ -300,6 +300,10 @@ public:
|
||||
bool getPoolMode() const { return pool_mode_; }
|
||||
void setPoolMode(bool v) { pool_mode_ = v; }
|
||||
|
||||
// Installed DRG-XMRig release tag (for in-app miner update detection); empty if unknown/bundled.
|
||||
std::string getXmrigVersion() const { return xmrig_version_; }
|
||||
void setXmrigVersion(const std::string& v) { xmrig_version_ = v; }
|
||||
|
||||
// Mine when idle (auto-start mining when system is idle)
|
||||
bool getMineWhenIdle() const { return mine_when_idle_; }
|
||||
void setMineWhenIdle(bool v) { mine_when_idle_ = v; }
|
||||
@@ -428,6 +432,7 @@ private:
|
||||
bool pool_tls_ = false;
|
||||
bool pool_hugepages_ = true;
|
||||
bool pool_mode_ = false; // false=solo, true=pool
|
||||
std::string xmrig_version_; // installed DRG-XMRig release tag (update detection)
|
||||
bool mine_when_idle_ = false; // auto-start mining when system idle
|
||||
int mine_idle_delay_= 120; // seconds of idle before mining starts
|
||||
bool idle_thread_scaling_ = false; // scale threads instead of start/stop
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "mining_benchmark.h"
|
||||
#include "mining_tab_helpers.h"
|
||||
#include "mining_pool_panel.h"
|
||||
#include "xmrig_download_dialog.h"
|
||||
#include "../../app.h"
|
||||
#include "../../util/i18n.h"
|
||||
#include "../../config/version.h"
|
||||
@@ -1574,6 +1575,21 @@ static void RenderMiningTabContent(App* app)
|
||||
ImGui::Dummy(ImVec2(0, gap));
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// POOL MINER — download / update the bundled DRG-XMRig miner from the project release server.
|
||||
// (Pool mode uses xmrig; disabled while it's running so we never replace a live binary.)
|
||||
// ================================================================
|
||||
if (s_pool_mode) {
|
||||
const bool minerBusy = state.pool_mining.xmrig_running;
|
||||
ImGui::BeginDisabled(minerBusy);
|
||||
if (TactileButton("Update miner…", ImVec2(availWidth, 0)))
|
||||
XmrigDownloadDialog::show(app);
|
||||
ImGui::EndDisabled();
|
||||
if (minerBusy && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||
ImGui::SetTooltip("Stop mining before updating the miner.");
|
||||
ImGui::Dummy(ImVec2(0, gap));
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// HASHRATE + STATS — Combined glass card: stat values on top, chart below
|
||||
// (Or full-card log view when toggled in pool mode)
|
||||
|
||||
160
src/ui/windows/xmrig_download_dialog.h
Normal file
160
src/ui/windows/xmrig_download_dialog.h
Normal file
@@ -0,0 +1,160 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
//
|
||||
// Modal dialog to download / update the DRG-XMRig miner from the project Gitea, driven by
|
||||
// util::XmrigUpdater. States follow the updater: Checking -> UpToDate/UpdateAvailable ->
|
||||
// Downloading/Verifying/Extracting -> Done / Failed. Header-only (static inline state), mirroring
|
||||
// BootstrapDownloadDialog. The installed release tag is persisted to settings on success.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "../../app.h"
|
||||
#include "../../config/settings.h"
|
||||
#include "../../resources/embedded_resources.h" // resources::getDaemonDirectory()
|
||||
#include "../../util/xmrig_updater.h"
|
||||
#include "../material/draw_helpers.h"
|
||||
#include "../material/type.h"
|
||||
#include "../material/colors.h"
|
||||
#include "../theme.h"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace dragonx {
|
||||
namespace ui {
|
||||
|
||||
class XmrigDownloadDialog {
|
||||
public:
|
||||
static void show(App* app) {
|
||||
if (!app) return;
|
||||
s_app = app;
|
||||
s_open = true;
|
||||
s_persisted = false;
|
||||
s_updater = std::make_unique<util::XmrigUpdater>();
|
||||
s_updater->startCheck(app->settings() ? app->settings()->getXmrigVersion() : std::string());
|
||||
}
|
||||
|
||||
static bool isOpen() { return s_open; }
|
||||
|
||||
static void render() {
|
||||
if (!s_open || !s_app || !s_updater) {
|
||||
if (!s_open) s_updater.reset(); // closed: drop the updater (dtor joins the worker)
|
||||
return;
|
||||
}
|
||||
using namespace material;
|
||||
const float dp = Layout::dpiScale();
|
||||
if (BeginOverlayDialog("Update Miner", &s_open, 480.0f, 0.94f)) {
|
||||
const auto p = s_updater->getProgress();
|
||||
using St = util::XmrigUpdater::State;
|
||||
switch (p.state) {
|
||||
case St::Checking: renderChecking(dp, p); break;
|
||||
case St::UpToDate:
|
||||
case St::UpdateAvailable: renderPrompt(dp, p); break;
|
||||
case St::Downloading:
|
||||
case St::Verifying:
|
||||
case St::Extracting: renderProgress(dp, p); break;
|
||||
case St::Done: renderDone(dp, p); break;
|
||||
case St::Failed: renderFailed(dp, p); break;
|
||||
default: break;
|
||||
}
|
||||
EndOverlayDialog();
|
||||
}
|
||||
if (!s_open) s_updater.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
using Progress = util::XmrigUpdater::Progress;
|
||||
using St = util::XmrigUpdater::State;
|
||||
|
||||
static float fullW() { return ImGui::GetContentRegionAvail().x; }
|
||||
|
||||
static void renderChecking(float, const Progress&) {
|
||||
using namespace material;
|
||||
Type().text(TypeStyle::Body2, "Checking for the latest miner…");
|
||||
}
|
||||
|
||||
static void renderPrompt(float, const Progress& p) {
|
||||
using namespace material;
|
||||
const std::string installed = p.installed_tag.empty() ? "none" : p.installed_tag;
|
||||
if (p.state == St::UpdateAvailable) {
|
||||
Type().text(TypeStyle::Subtitle2, "A new miner is available");
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("Latest: %s", p.latest_tag.c_str());
|
||||
ImGui::Text("Installed: %s", installed.c_str());
|
||||
ImGui::Spacing();
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(),
|
||||
"The download is verified against its published SHA-256 checksum before install.");
|
||||
ImGui::Spacing();
|
||||
if (TactileButton("Download & install", ImVec2(fullW(), 0)))
|
||||
s_updater->startInstall(resources::getDaemonDirectory());
|
||||
} else {
|
||||
Type().text(TypeStyle::Subtitle2, "The miner is up to date");
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("Installed: %s", p.latest_tag.c_str());
|
||||
ImGui::Spacing();
|
||||
if (TactileButton("Reinstall", ImVec2(fullW(), 0)))
|
||||
s_updater->startInstall(resources::getDaemonDirectory());
|
||||
}
|
||||
ImGui::Spacing();
|
||||
if (TactileButton("Close", ImVec2(fullW(), 0))) s_open = false;
|
||||
}
|
||||
|
||||
static void renderProgress(float dp, const Progress& p) {
|
||||
using namespace material;
|
||||
const char* title = p.state == St::Downloading ? "Downloading…"
|
||||
: p.state == St::Verifying ? "Verifying…"
|
||||
: "Installing…";
|
||||
Type().text(TypeStyle::Subtitle2, title);
|
||||
ImGui::Spacing();
|
||||
const float barH = 8.0f * dp;
|
||||
const float barW = fullW();
|
||||
const ImVec2 bMin = ImGui::GetCursorScreenPos();
|
||||
const ImVec2 bMax(bMin.x + barW, bMin.y + barH);
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
dl->AddRectFilled(bMin, bMax, IM_COL32(255, 255, 255, 30), 4.0f * dp);
|
||||
const float fillW = barW * (p.percent / 100.0f);
|
||||
if (fillW > 0)
|
||||
dl->AddRectFilled(bMin, ImVec2(bMin.x + fillW, bMax.y), Primary(), 4.0f * dp);
|
||||
ImGui::Dummy(ImVec2(0, barH));
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("%s", p.status_text.c_str());
|
||||
}
|
||||
|
||||
static void renderDone(float, const Progress& p) {
|
||||
using namespace material;
|
||||
if (!s_persisted) {
|
||||
if (s_app->settings() && !p.latest_tag.empty()) {
|
||||
s_app->settings()->setXmrigVersion(p.latest_tag);
|
||||
s_app->settings()->save();
|
||||
}
|
||||
s_persisted = true;
|
||||
}
|
||||
Type().textColored(TypeStyle::Subtitle2, Success(), "Miner installed");
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("Version: %s", p.latest_tag.c_str());
|
||||
ImGui::Spacing();
|
||||
if (TactileButton("Close", ImVec2(fullW(), 0))) s_open = false;
|
||||
}
|
||||
|
||||
static void renderFailed(float, const Progress& p) {
|
||||
using namespace material;
|
||||
Type().textColored(TypeStyle::Subtitle2, Error(), "Update failed");
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("%s", p.error.empty() ? "Unknown error." : p.error.c_str());
|
||||
ImGui::Spacing();
|
||||
if (TactileButton("Retry", ImVec2(fullW(), 0)))
|
||||
s_updater->startInstall(resources::getDaemonDirectory());
|
||||
ImGui::Spacing();
|
||||
if (TactileButton("Close", ImVec2(fullW(), 0))) s_open = false;
|
||||
}
|
||||
|
||||
static inline bool s_open = false;
|
||||
static inline bool s_persisted = false;
|
||||
static inline App* s_app = nullptr;
|
||||
static inline std::unique_ptr<util::XmrigUpdater> s_updater;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace dragonx
|
||||
Reference in New Issue
Block a user