Files
ObsidianDragon/src/ui/screens/mining_screen.h
DanS 3aee55b49c ObsidianDragon - DragonX ImGui Wallet
Full-node GUI wallet for DragonX cryptocurrency.
Built with Dear ImGui, SDL3, and OpenGL3/DX11.

Features:
- Send/receive shielded and transparent transactions
- Autoshield with merged transaction display
- Built-in CPU mining (xmrig)
- Peer management and network monitoring
- Wallet encryption with PIN lock
- QR code generation for receive addresses
- Transaction history with pagination
- Console for direct RPC commands
- Cross-platform (Linux, Windows)
2026-02-27 00:26:01 -06:00

372 lines
13 KiB
C++

// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#pragma once
#include "../material/material.h"
#include "../../embedded/IconsMaterialDesign.h"
#include "imgui.h"
#include <string>
#include <vector>
#include <functional>
namespace dragonx {
namespace ui {
namespace screens {
using namespace material;
// ============================================================================
// Mining Screen
// ============================================================================
// Mining controls and statistics display
/**
* @brief Mining statistics
*/
struct MiningStats {
bool isMining = false;
int threads = 0;
int maxThreads = 8;
double hashrate = 0.0; // Hashes per second
double networkHashrate = 0.0; // Network hashrate
int blocksFound = 0;
double totalMined = 0.0;
std::string miningAddress;
int currentHeight = 0;
double networkDifficulty = 0.0;
std::string estimatedTimeToBlock;
};
/**
* @brief Recent mined block
*/
struct MinedBlock {
int height;
double reward;
std::string time;
std::string txid;
};
/**
* @brief Mining screen with controls and stats
*/
class MiningScreen {
public:
void setStats(const MiningStats& stats) { m_stats = stats; }
void setMinedBlocks(const std::vector<MinedBlock>& blocks) { m_minedBlocks = blocks; }
void setOnStartMining(std::function<void(int threads)> callback) {
m_onStartMining = callback;
}
void setOnStopMining(std::function<void()> callback) {
m_onStopMining = callback;
}
void setOnSetAddress(std::function<void(const std::string&)> callback) {
m_onSetAddress = callback;
}
void render();
private:
void renderMiningControls();
void renderStatsCards();
void renderMinedBlocksList();
MiningStats m_stats;
std::vector<MinedBlock> m_minedBlocks;
std::function<void(int)> m_onStartMining;
std::function<void()> m_onStopMining;
std::function<void(const std::string&)> m_onSetAddress;
int m_selectedThreads = 4;
};
// ============================================================================
// Implementation
// ============================================================================
inline void MiningScreen::render() {
// Title
ImGui::BeginGroup();
{
Typography::instance().text(TypeStyle::H5, "Mining");
ImGui::SameLine();
// Mining status indicator
if (m_stats.isMining) {
ChipSpec chipSpec;
chipSpec.variant = ChipVariant::Filled;
chipSpec.color = colors::Green500;
Chip(ICON_MD_HARDWARE " Mining Active", chipSpec);
}
}
ImGui::EndGroup();
ImGui::Dummy(ImVec2(0, spacing::dp(2)));
// Mining controls card
renderMiningControls();
ImGui::Dummy(ImVec2(0, spacing::dp(2)));
// Stats cards row
renderStatsCards();
ImGui::Dummy(ImVec2(0, spacing::dp(2)));
// Mined blocks history
renderMinedBlocksList();
}
inline void MiningScreen::renderMiningControls() {
CardSpec cardSpec;
cardSpec.elevation = 2;
cardSpec.padding = spacing::dp(3);
ImGui::PushID("mining_controls");
if (BeginCard(cardSpec)) {
Typography::instance().textColored(TypeStyle::Overline, OnSurfaceMedium(), "MINING CONTROLS");
ImGui::Dummy(ImVec2(0, spacing::dp(2)));
// Thread selector
ImGui::BeginGroup();
{
Typography::instance().text(TypeStyle::Body1, "Mining Threads:");
ImGui::SameLine();
ImGui::SetNextItemWidth(150.0f);
ImGui::SliderInt("##threads", &m_selectedThreads, 1, m_stats.maxThreads);
ImGui::SameLine();
char threadInfo[64];
snprintf(threadInfo, sizeof(threadInfo), "(%d available)", m_stats.maxThreads);
Typography::instance().textColored(TypeStyle::Caption, OnSurfaceMedium(), threadInfo);
}
ImGui::EndGroup();
ImGui::Dummy(ImVec2(0, spacing::dp(2)));
// Mining address display
if (!m_stats.miningAddress.empty()) {
Typography::instance().textColored(TypeStyle::Caption, OnSurfaceMedium(), "Mining to:");
std::string displayAddr = m_stats.miningAddress;
if (displayAddr.length() > 50) {
displayAddr = displayAddr.substr(0, 25) + "..." +
displayAddr.substr(displayAddr.length() - 20);
}
Typography::instance().text(TypeStyle::Body2, displayAddr.c_str());
}
ImGui::Dummy(ImVec2(0, spacing::dp(2)));
// Start/Stop buttons
if (m_stats.isMining) {
// Stop button (red)
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(colors::Red700.x, colors::Red700.y, colors::Red700.z, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(colors::Red500.x, colors::Red500.y, colors::Red500.z, 1.0f));
if (ImGui::Button("⏹ STOP MINING", ImVec2(150, 40))) {
if (m_onStopMining) m_onStopMining();
}
ImGui::PopStyleColor(2);
ImGui::SameLine();
// Live hashrate display
char hashrateStr[64];
if (m_stats.hashrate > 1000000) {
snprintf(hashrateStr, sizeof(hashrateStr), "%.2f MH/s", m_stats.hashrate / 1000000.0);
} else if (m_stats.hashrate > 1000) {
snprintf(hashrateStr, sizeof(hashrateStr), "%.2f KH/s", m_stats.hashrate / 1000.0);
} else {
snprintf(hashrateStr, sizeof(hashrateStr), "%.0f H/s", m_stats.hashrate);
}
Typography::instance().textColored(TypeStyle::H6, Primary(), hashrateStr);
} else {
// Start button (green)
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(colors::Green700.x, colors::Green700.y, colors::Green700.z, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(colors::Green500.x, colors::Green500.y, colors::Green500.z, 1.0f));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1, 1, 1, 1));
if (ImGui::Button(ICON_MD_PLAY_ARROW " START MINING", ImVec2(150, 40))) {
if (m_onStartMining) m_onStartMining(m_selectedThreads);
}
ImGui::PopStyleColor(3);
}
EndCard();
}
ImGui::PopID();
}
inline void MiningScreen::renderStatsCards() {
float availWidth = ImGui::GetContentRegionAvail().x;
float cardWidth = (availWidth - spacing::dp(2) * 2) / 3.0f;
// Hashrate card
ImGui::BeginGroup();
{
CardSpec cardSpec;
cardSpec.elevation = 1;
cardSpec.padding = spacing::dp(2);
cardSpec.width = cardWidth;
ImGui::PushID("hashrate_card");
if (BeginCard(cardSpec)) {
Typography::instance().textColored(TypeStyle::Overline, OnSurfaceMedium(), "YOUR HASHRATE");
ImGui::Dummy(ImVec2(0, spacing::dp(1)));
char hashrateStr[64];
if (m_stats.hashrate > 1000000) {
snprintf(hashrateStr, sizeof(hashrateStr), "%.2f", m_stats.hashrate / 1000000.0);
Typography::instance().text(TypeStyle::H4, hashrateStr);
ImGui::SameLine();
Typography::instance().textColored(TypeStyle::Body1, OnSurfaceMedium(), "MH/s");
} else if (m_stats.hashrate > 1000) {
snprintf(hashrateStr, sizeof(hashrateStr), "%.2f", m_stats.hashrate / 1000.0);
Typography::instance().text(TypeStyle::H4, hashrateStr);
ImGui::SameLine();
Typography::instance().textColored(TypeStyle::Body1, OnSurfaceMedium(), "KH/s");
} else {
snprintf(hashrateStr, sizeof(hashrateStr), "%.0f", m_stats.hashrate);
Typography::instance().text(TypeStyle::H4, hashrateStr);
ImGui::SameLine();
Typography::instance().textColored(TypeStyle::Body1, OnSurfaceMedium(), "H/s");
}
EndCard();
}
ImGui::PopID();
}
ImGui::EndGroup();
ImGui::SameLine(0, spacing::dp(2));
// Network hashrate card
ImGui::BeginGroup();
{
CardSpec cardSpec;
cardSpec.elevation = 1;
cardSpec.padding = spacing::dp(2);
cardSpec.width = cardWidth;
ImGui::PushID("network_card");
if (BeginCard(cardSpec)) {
Typography::instance().textColored(TypeStyle::Overline, OnSurfaceMedium(), "NETWORK HASHRATE");
ImGui::Dummy(ImVec2(0, spacing::dp(1)));
char hashrateStr[64];
if (m_stats.networkHashrate > 1000000000) {
snprintf(hashrateStr, sizeof(hashrateStr), "%.2f", m_stats.networkHashrate / 1000000000.0);
Typography::instance().text(TypeStyle::H4, hashrateStr);
ImGui::SameLine();
Typography::instance().textColored(TypeStyle::Body1, OnSurfaceMedium(), "GH/s");
} else if (m_stats.networkHashrate > 1000000) {
snprintf(hashrateStr, sizeof(hashrateStr), "%.2f", m_stats.networkHashrate / 1000000.0);
Typography::instance().text(TypeStyle::H4, hashrateStr);
ImGui::SameLine();
Typography::instance().textColored(TypeStyle::Body1, OnSurfaceMedium(), "MH/s");
} else {
snprintf(hashrateStr, sizeof(hashrateStr), "%.2f", m_stats.networkHashrate / 1000.0);
Typography::instance().text(TypeStyle::H4, hashrateStr);
ImGui::SameLine();
Typography::instance().textColored(TypeStyle::Body1, OnSurfaceMedium(), "KH/s");
}
EndCard();
}
ImGui::PopID();
}
ImGui::EndGroup();
ImGui::SameLine(0, spacing::dp(2));
// Total mined card
ImGui::BeginGroup();
{
CardSpec cardSpec;
cardSpec.elevation = 1;
cardSpec.padding = spacing::dp(2);
cardSpec.width = cardWidth;
ImGui::PushID("mined_card");
if (BeginCard(cardSpec)) {
Typography::instance().textColored(TypeStyle::Overline, OnSurfaceMedium(), "TOTAL MINED");
ImGui::Dummy(ImVec2(0, spacing::dp(1)));
char minedStr[64];
snprintf(minedStr, sizeof(minedStr), "%.4f", m_stats.totalMined);
Typography::instance().text(TypeStyle::H4, minedStr);
ImGui::SameLine();
Typography::instance().textColored(TypeStyle::Body1, OnSurfaceMedium(), "DRGX");
ImGui::Dummy(ImVec2(0, spacing::dp(0.5f)));
char blocksStr[32];
snprintf(blocksStr, sizeof(blocksStr), "%d blocks found", m_stats.blocksFound);
Typography::instance().textColored(TypeStyle::Caption, OnSurfaceMedium(), blocksStr);
EndCard();
}
ImGui::PopID();
}
ImGui::EndGroup();
}
inline void MiningScreen::renderMinedBlocksList() {
CardSpec cardSpec;
cardSpec.elevation = 1;
cardSpec.padding = spacing::dp(2);
ImGui::PushID("mined_blocks");
if (BeginCard(cardSpec)) {
Typography::instance().textColored(TypeStyle::Overline, OnSurfaceMedium(), "BLOCKS YOU MINED");
ImGui::Dummy(ImVec2(0, spacing::dp(1)));
if (m_minedBlocks.empty()) {
Typography::instance().textColored(TypeStyle::Body2, OnSurfaceMedium(),
"No blocks mined yet. Keep mining!");
} else {
BeginList("mined_list", false);
for (size_t i = 0; i < m_minedBlocks.size() && i < 10; i++) {
const auto& block = m_minedBlocks[i];
ListItemSpec itemSpec;
itemSpec.leadingIcon = "🏆";
char primaryStr[64];
snprintf(primaryStr, sizeof(primaryStr), "Block #%d", block.height);
itemSpec.primaryText = primaryStr;
char secondaryStr[64];
snprintf(secondaryStr, sizeof(secondaryStr), "+%.4f DRGX • %s",
block.reward, block.time.c_str());
itemSpec.secondaryText = secondaryStr;
itemSpec.dividerBelow = (i < m_minedBlocks.size() - 1 && i < 9);
ListItem(itemSpec);
}
EndList();
}
EndCard();
}
ImGui::PopID();
}
} // namespace screens
} // namespace ui
} // namespace dragonx