diff --git a/CMakeLists.txt b/CMakeLists.txt index d1ceda3..a9d3c5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,9 +10,7 @@ set(VERSION_REVISION "0") set(VERSION "beta-4") option(FETCH_DEPS "Download dependencies if they are not found" ON) -option(XMRIG "Include XMRig module") option(OPENVR "Include OpenVR support") -option(QML "Include QtQuick (QML)") option(ANDROID "Android deployment") option(ANDROID_DEBUG "View the Android app on desktop") option(TOR_BIN "Path to Tor binary to embed inside WOWlet") @@ -33,9 +31,6 @@ set(BUILD_GUI_DEPS ON) set(BUILD_64 ON CACHE BOOL "Build 64-bit binaries") set(INSTALL_VENDORED_LIBUNBOUND ${STATIC}) set(USE_SINGLE_BUILDDIR ON) -if(OPENVR OR ANDROID_DEBUG) - set(QML ON) -endif() # Are we in debug mode? set(_CMAKE_BUILD_TYPE "") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e899ba7..d96ade5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,11 +39,7 @@ file(GLOB SOURCE_FILES "dialog/*.cpp" ) -if(QML) - find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets Quick Qml QuickControls2 QmlImportScanner Multimedia) -else() - find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets Multimedia) -endif() +find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets Quick QuickWidgets Qml QuickControls2 QuickCompiler QmlImportScanner Multimedia) if(OPENVR) # include some extra files @@ -172,10 +168,6 @@ if(TOR_BIN) target_compile_definitions(wowlet PRIVATE HAS_TOR_BIN=1) endif() -if(XMRIG) - target_compile_definitions(wowlet PRIVATE HAS_XMRIG=1) -endif() - if(ANDROID) target_compile_definitions(wowlet PRIVATE HAS_ANDROID=1) endif() @@ -216,10 +208,7 @@ endif() target_compile_definitions(wowlet PUBLIC VR_API_PUBLIC) -if(QML) - qt5_import_qml_plugins(${PROJECT_NAME}) - target_compile_definitions(wowlet PRIVATE HAS_QML=1) -endif() +qt5_import_qml_plugins(${PROJECT_NAME}) target_compile_definitions(wowlet PUBLIC @@ -260,30 +249,20 @@ target_link_libraries(wowlet PUBLIC ${EXTRA_LIBRARIES}) # Link Qt libraries -if(QML) - target_link_libraries(wowlet PUBLIC - Qt5::Core - Qt5::Widgets - Qt5::Gui - Qt5::Network - Qt5::Svg - Qt5::QSvgPlugin - Qt5::QSvgIconPlugin - Qt5::Xml - Qt5::WebSockets - Qt5::Quick - Qt5::Qml - Qt5::QuickControls2) -else() - target_link_libraries(wowlet PUBLIC - Qt5::Core - Qt5::Widgets - Qt5::Gui - Qt5::Network - Qt5::Svg - Qt5::Xml - Qt5::WebSockets) -endif() +target_link_libraries(wowlet PUBLIC + Qt5::Core + Qt5::Widgets + Qt5::Gui + Qt5::Network + Qt5::Svg + Qt5::QSvgPlugin + Qt5::QSvgIconPlugin + Qt5::Xml + Qt5::WebSockets + Qt5::Quick + Qt5::Qml + Qt5::QuickControls2 + Qt5::QuickWidgets) if(ANDROID) # yolo some hardcoded paths @@ -387,9 +366,7 @@ message(STATUS "VERSION_MAJOR: ${VERSION_MAJOR}") message(STATUS "VERSION_MINOR: ${VERSION_MINOR}") message(STATUS "VERSION_REVISION: ${VERSION_REVISION}") message(STATUS "STATIC: ${STATIC}") -message(STATUS "Include QtQuick (QML): ${QML}") message(STATUS "VERSION: ${VERSION}") -message(STATUS "Include the XMRIG tab: ${XMRIG}") message(STATUS "Include Valve's OpenVR library: ${OPENVR}") message(STATUS "This build is for Android: ${ANDROID}") message(STATUS "This build is for testing the Android app on desktop: ${ANDROID_DEBUG}") diff --git a/src/appcontext.cpp b/src/appcontext.cpp index 4b23c0a..c0df7c3 100644 --- a/src/appcontext.cpp +++ b/src/appcontext.cpp @@ -163,10 +163,8 @@ AppContext::AppContext(QCommandLineParser *cmdargs) { AppContext::prices = new Prices(); // XMRig -#ifdef HAS_XMRIG this->XMRig = new XmRig(this->configDirectory, this); this->XMRig->prepare(); -#endif this->walletManager = WalletManager::instance(); QString logPath = QString("%1/daemon.log").arg(configDirectory); @@ -195,12 +193,14 @@ void AppContext::initTor() { this->tor = new Tor(this, this); this->tor->start(); - if (!isWhonix && backendHost.contains(".onion")) { + if (!isWhonix && !backendHost.contains(".onion")) { qDebug() << "'backend-host' did not contain '.onion' - running without Tor proxy."; - this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort); - this->network->setProxy(*networkProxy); - this->ws->webSocket.setProxy(*networkProxy); + return; } + + this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort); + this->network->setProxy(*networkProxy); + this->ws->webSocket.setProxy(*networkProxy); } void AppContext::initWS() { @@ -447,11 +447,12 @@ void AppContext::onWSMessage(const QJsonObject &msg) { else if(cmd == "rpc_nodes") { this->onWSNodes(msg.value("data").toArray()); } -#if defined(HAS_XMRIG) else if(cmd == "xmrig") { this->XMRigDownloads(msg.value("data").toObject()); } -#endif + else if(cmd == "wownerod_releases") { + emit WownerodDownloads(msg.value("data").toObject()); + } else if(cmd == "crypto_rates") { QJsonArray crypto_rates = msg.value("data").toArray(); AppContext::prices->cryptoPricesReceived(crypto_rates); diff --git a/src/appcontext.h b/src/appcontext.h index 9e9f99c..6d12de8 100644 --- a/src/appcontext.h +++ b/src/appcontext.h @@ -215,6 +215,7 @@ signals: void yellowUpdated(); void nodeSourceChanged(NodeSource nodeSource); void XMRigDownloads(const QJsonObject &data); + void WownerodDownloads(const QJsonObject &data); void pinLookupReceived(QString address, QString pin); void pinLookupErrorReceived(); void pinReceived(QString pin); diff --git a/src/assets.qrc b/src/assets.qrc index da545e3..efebcca 100644 --- a/src/assets.qrc +++ b/src/assets.qrc @@ -28,6 +28,8 @@ assets/images/confirmed.svg assets/images/connect.svg assets/images/copy.png + assets/images/dog_running.gif + assets/images/dog_sitting.gif assets/images/edit.png assets/images/exchange.png assets/images/exchange_white.png @@ -37,6 +39,7 @@ assets/images/eye_blind.png assets/images/wowlet.png assets/images/file.png + assets/images/fire.png assets/images/gnome-calc.png assets/images/history.png assets/images/info.png @@ -225,5 +228,31 @@ assets/images/zoom.png assets/mnemonic_25_english.txt assets/restore_heights_wownero_mainnet.txt + + assets/images/mining/bottom_center_console.png + assets/images/mining/intel.png + assets/images/mining/amd.png + assets/images/mining/lowerleft_circle.png + assets/images/mining/lowerleft.png + assets/images/mining/lower_repeat.png + assets/images/mining/lowerright.png + assets/images/mining/r_bottom.png + assets/images/mining/r_left.png + assets/images/mining/r_right.png + assets/images/mining/topleft.png + assets/images/mining/topright_bar.png + assets/images/mining/topright_left.png + assets/images/mining/topright_middle.png + assets/images/mining/topright_right.png + assets/images/mining/warning.png + assets/images/mining/axe.png + assets/images/mining/lowerleft_btn.png + assets/images/mining/elmo.gif + assets/images/mining/bubble.png + assets/images/mining/mining.webp + + assets/fonts/ComicMono.ttf + assets/fonts/ComicMono-Bold.ttf + ui/qml/mining.qml diff --git a/src/assets/fonts/ComicMono-Bold.ttf b/src/assets/fonts/ComicMono-Bold.ttf new file mode 100644 index 0000000..e03f41e Binary files /dev/null and b/src/assets/fonts/ComicMono-Bold.ttf differ diff --git a/src/assets/fonts/ComicMono.ttf b/src/assets/fonts/ComicMono.ttf new file mode 100644 index 0000000..9bc7354 Binary files /dev/null and b/src/assets/fonts/ComicMono.ttf differ diff --git a/src/assets/images/dog_running.gif b/src/assets/images/dog_running.gif new file mode 100644 index 0000000..433ee58 Binary files /dev/null and b/src/assets/images/dog_running.gif differ diff --git a/src/assets/images/dog_sitting.gif b/src/assets/images/dog_sitting.gif new file mode 100644 index 0000000..f773aee Binary files /dev/null and b/src/assets/images/dog_sitting.gif differ diff --git a/src/assets/images/fire.png b/src/assets/images/fire.png new file mode 100644 index 0000000..6f5d037 Binary files /dev/null and b/src/assets/images/fire.png differ diff --git a/src/assets/images/mining/amd.png b/src/assets/images/mining/amd.png new file mode 100644 index 0000000..a75f597 Binary files /dev/null and b/src/assets/images/mining/amd.png differ diff --git a/src/assets/images/mining/axe.png b/src/assets/images/mining/axe.png new file mode 100644 index 0000000..7321bfd Binary files /dev/null and b/src/assets/images/mining/axe.png differ diff --git a/src/assets/images/mining/bottom_center_console.png b/src/assets/images/mining/bottom_center_console.png new file mode 100644 index 0000000..cf492e0 Binary files /dev/null and b/src/assets/images/mining/bottom_center_console.png differ diff --git a/src/assets/images/mining/bubble.png b/src/assets/images/mining/bubble.png new file mode 100644 index 0000000..6681c18 Binary files /dev/null and b/src/assets/images/mining/bubble.png differ diff --git a/src/assets/images/mining/elmo.gif b/src/assets/images/mining/elmo.gif new file mode 100644 index 0000000..bbecfad Binary files /dev/null and b/src/assets/images/mining/elmo.gif differ diff --git a/src/assets/images/mining/intel.png b/src/assets/images/mining/intel.png new file mode 100644 index 0000000..909df59 Binary files /dev/null and b/src/assets/images/mining/intel.png differ diff --git a/src/assets/images/mining/lower_repeat.png b/src/assets/images/mining/lower_repeat.png new file mode 100644 index 0000000..9b8cf16 Binary files /dev/null and b/src/assets/images/mining/lower_repeat.png differ diff --git a/src/assets/images/mining/lowerleft.png b/src/assets/images/mining/lowerleft.png new file mode 100644 index 0000000..97db4c3 Binary files /dev/null and b/src/assets/images/mining/lowerleft.png differ diff --git a/src/assets/images/mining/lowerleft_btn.png b/src/assets/images/mining/lowerleft_btn.png new file mode 100644 index 0000000..2e2f275 Binary files /dev/null and b/src/assets/images/mining/lowerleft_btn.png differ diff --git a/src/assets/images/mining/lowerleft_circle.png b/src/assets/images/mining/lowerleft_circle.png new file mode 100644 index 0000000..7baa63f Binary files /dev/null and b/src/assets/images/mining/lowerleft_circle.png differ diff --git a/src/assets/images/mining/lowerright.png b/src/assets/images/mining/lowerright.png new file mode 100644 index 0000000..c9f2faf Binary files /dev/null and b/src/assets/images/mining/lowerright.png differ diff --git a/src/assets/images/mining/mining.webp b/src/assets/images/mining/mining.webp new file mode 100644 index 0000000..b11fe4a Binary files /dev/null and b/src/assets/images/mining/mining.webp differ diff --git a/src/assets/images/mining/r_bottom.png b/src/assets/images/mining/r_bottom.png new file mode 100644 index 0000000..8fa382f Binary files /dev/null and b/src/assets/images/mining/r_bottom.png differ diff --git a/src/assets/images/mining/r_left.png b/src/assets/images/mining/r_left.png new file mode 100644 index 0000000..b498e85 Binary files /dev/null and b/src/assets/images/mining/r_left.png differ diff --git a/src/assets/images/mining/r_right.png b/src/assets/images/mining/r_right.png new file mode 100644 index 0000000..8f56bfe Binary files /dev/null and b/src/assets/images/mining/r_right.png differ diff --git a/src/assets/images/mining/topleft.png b/src/assets/images/mining/topleft.png new file mode 100644 index 0000000..c4ddea1 Binary files /dev/null and b/src/assets/images/mining/topleft.png differ diff --git a/src/assets/images/mining/topright_bar.png b/src/assets/images/mining/topright_bar.png new file mode 100644 index 0000000..988f19c Binary files /dev/null and b/src/assets/images/mining/topright_bar.png differ diff --git a/src/assets/images/mining/topright_left.png b/src/assets/images/mining/topright_left.png new file mode 100644 index 0000000..3251de8 Binary files /dev/null and b/src/assets/images/mining/topright_left.png differ diff --git a/src/assets/images/mining/topright_middle.png b/src/assets/images/mining/topright_middle.png new file mode 100644 index 0000000..c590986 Binary files /dev/null and b/src/assets/images/mining/topright_middle.png differ diff --git a/src/assets/images/mining/topright_right.png b/src/assets/images/mining/topright_right.png new file mode 100644 index 0000000..3602e7c Binary files /dev/null and b/src/assets/images/mining/topright_right.png differ diff --git a/src/assets/images/mining/warning.png b/src/assets/images/mining/warning.png new file mode 100644 index 0000000..29745cb Binary files /dev/null and b/src/assets/images/mining/warning.png differ diff --git a/src/globals.h b/src/globals.h index 909f838..90578f6 100644 --- a/src/globals.h +++ b/src/globals.h @@ -9,6 +9,23 @@ namespace globals { const qreal cdiv = 1e11; + + enum Tabs { + HOME = 0, + HISTORY, + SEND, + RECEIVE, + COINS, + CALC, + XMRIG + }; + + enum TabsHome { + FORUM, + REDDIT, + SUCHWOW, + WFS + }; } #endif //WOWLET_GLOBALS_H diff --git a/src/main.cpp b/src/main.cpp index 02a5047..c0443b3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,6 +31,7 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin) int main(int argc, char *argv[]) { Q_INIT_RESOURCE(assets); + qputenv("QML_DISABLE_DISK_CACHE", "1"); #if defined(Q_OS_MAC) && defined(HAS_TOR_BIN) Q_INIT_RESOURCE(assets_tor_macos); @@ -140,16 +141,8 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) { #endif qRegisterMetaType>(); -#ifdef HAS_QML - qputenv("QML_DISABLE_DISK_CACHE", "1"); -#endif - #ifdef __ANDROID__ if(android || androidDebug) { -#ifndef HAS_QML - qCritical() << "Wowlet compiled without QML support. Try -DQML=ON"; - return 1; -#endif QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication mobile_app(argc, argv); auto *ctx = new AppContext(&parser); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 1b5050b..32140ef 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -171,22 +171,25 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) : connect(m_ctx->nodes, &Nodes::WSNodeExhausted, this, &MainWindow::showWSNodeExhaustedMessage); // XMRig -#ifdef HAS_XMRIG m_xmrig = new XMRigWidget(m_ctx, this); ui->xmrRigLayout->addWidget(m_xmrig); connect(m_ctx->XMRig, &XmRig::output, m_xmrig, &XMRigWidget::onProcessOutput); connect(m_ctx->XMRig, &XmRig::error, m_xmrig, &XMRigWidget::onProcessError); + connect(m_ctx->XMRig, &XmRig::output, m_xmrig, &XMRigWidget::daemonOutput); + connect(m_ctx->XMRig, &XmRig::error, m_xmrig, &XMRigWidget::daemonOutput); + connect(m_ctx->XMRig, &XmRig::blockReward, m_xmrig, &XMRigWidget::onBlockReward); connect(m_ctx->XMRig, &XmRig::hashrate, m_xmrig, &XMRigWidget::onHashrate); + connect(m_ctx->XMRig, &XmRig::daemonStateChanged, m_xmrig, &XMRigWidget::onDaemonStateChanged); + connect(m_ctx->XMRig, &XmRig::syncStatus, m_xmrig, &XMRigWidget::onSyncStatus); + connect(m_ctx->XMRig, &XmRig::uptimeChanged, m_xmrig, &XMRigWidget::onUptimeChanged); + connect(m_ctx->XMRig, &XmRig::daemonStateChanged, [=](DaemonMiningState state) { + m_ctx->setWindowTitle(state >= DaemonMiningState::mining); + }); connect(m_ctx, &AppContext::walletClosed, m_xmrig, &XMRigWidget::onWalletClosed); connect(m_ctx, &AppContext::walletOpened, m_xmrig, &XMRigWidget::onWalletOpened); - connect(m_ctx, &AppContext::XMRigDownloads, m_xmrig, &XMRigWidget::onDownloads); - - connect(m_xmrig, &XMRigWidget::miningStarted, [=]{ m_ctx->setWindowTitle(true); }); - connect(m_xmrig, &XMRigWidget::miningEnded, [=]{ m_ctx->setWindowTitle(false); }); -#else - ui->tabWidget->setTabVisible(Tabs::XMRIG, false); -#endif + connect(m_ctx, &AppContext::XMRigDownloads, m_xmrig, &XMRigWidget::onRigDownloads); + connect(m_ctx, &AppContext::WownerodDownloads, m_xmrig, &XMRigWidget::onWownerodDownloads); connect(ui->ccsWidget, &CCSWidget::selected, this, &MainWindow::showSendScreen); connect(m_ctx, &AppContext::ccsUpdated, ui->ccsWidget->model(), &CCSModel::updateEntries); @@ -197,8 +200,9 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) : connect(ui->redditWidget, &RedditWidget::setStatusText, this, &MainWindow::setStatusText); + connect(ui->tabWidget, &QTabWidget::currentChanged, m_xmrig, &XMRigWidget::onMenuTabChanged); connect(ui->tabHomeWidget, &QTabWidget::currentChanged, [](int index){ - config()->set(Config::homeWidget, TabsHome(index)); + config()->set(Config::homeWidget, globals::TabsHome(index)); }); connect(m_ctx, &AppContext::donationNag, [=]{ @@ -297,7 +301,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) : }); connect(ui->receiveWidget, &ReceiveWidget::showTransactions, [this](const QString &text) { ui->historyWidget->setSearchText(text); - ui->tabWidget->setCurrentIndex(Tabs::HISTORY); + ui->tabWidget->setCurrentIndex(globals::Tabs::HISTORY); }); // History @@ -334,9 +338,9 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) : connect(m_ctx, &AppContext::walletAboutToClose, [=]{ if (!config()->get(Config::showTabHome).toBool()) - ui->tabWidget->setCurrentIndex(Tabs::HISTORY); + ui->tabWidget->setCurrentIndex(globals::Tabs::HISTORY); else - ui->tabWidget->setCurrentIndex(Tabs::HOME); + ui->tabWidget->setCurrentIndex(globals::Tabs::HOME); // Clear all tables when wallet is closed ui->historyWidget->resetModel(); @@ -421,13 +425,9 @@ void MainWindow::initMenu() { m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc); m_tabShowHideSignalMapper->setMapping(ui->actionShow_calc, "Calc"); -#if defined(HAS_XMRIG) connect(ui->actionShow_XMRig, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map)); m_tabShowHideMapper["Mining"] = new ToggleTab(ui->tabXmrRig, "Mining", "Mining", ui->actionShow_XMRig, Config::showTabXMRig); m_tabShowHideSignalMapper->setMapping(ui->actionShow_XMRig, "Mining"); -#else - ui->actionShow_XMRig->setVisible(false); -#endif for (const auto &key: m_tabShowHideMapper.keys()) { const auto toggleTab = m_tabShowHideMapper.value(key); @@ -526,7 +526,7 @@ void MainWindow::menuToggleTabVisible(const QString &key){ void MainWindow::initWidgets() { int homeWidget = config()->get(Config::homeWidget).toInt(); - ui->tabHomeWidget->setCurrentIndex(TabsHome(homeWidget)); + ui->tabHomeWidget->setCurrentIndex(globals::TabsHome(homeWidget)); } WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){ @@ -1060,25 +1060,25 @@ void MainWindow::donateButtonClicked() { donation = 0.1337; ui->sendWidget->fill(m_ctx->donationAddress, "Donation to the WOWlet development team", donation); - ui->tabWidget->setCurrentIndex(Tabs::SEND); + ui->tabWidget->setCurrentIndex(globals::Tabs::SEND); } void MainWindow::showHistoryTab() { this->raise(); this->show(); - ui->tabWidget->setCurrentIndex(Tabs::HISTORY); + ui->tabWidget->setCurrentIndex(globals::Tabs::HISTORY); } void MainWindow::showSendTab() { this->raise(); this->show(); - ui->tabWidget->setCurrentIndex(Tabs::SEND); + ui->tabWidget->setCurrentIndex(globals::Tabs::SEND); } void MainWindow::showHomeWindow() { this->raise(); this->show(); - ui->tabWidget->setCurrentIndex(Tabs::HOME); + ui->tabWidget->setCurrentIndex(globals::Tabs::HOME); } void MainWindow::showCalcWindow() { @@ -1088,7 +1088,7 @@ void MainWindow::showCalcWindow() { } void MainWindow::payToMany() { - ui->tabWidget->setCurrentIndex(Tabs::SEND); + ui->tabWidget->setCurrentIndex(globals::Tabs::SEND); ui->sendWidget->payToMany(); QMessageBox::information(this, "Pay to many", "Enter a list of outputs in the 'Pay to' field.\n" "One output per line.\n" @@ -1098,7 +1098,7 @@ void MainWindow::payToMany() { void MainWindow::showSendScreen(const CCSEntry &entry) { ui->sendWidget->fill(entry); - ui->tabWidget->setCurrentIndex(Tabs::SEND); + ui->tabWidget->setCurrentIndex(globals::Tabs::SEND); } void MainWindow::suchDonate(const QString address) { @@ -1106,7 +1106,7 @@ void MainWindow::suchDonate(const QString address) { QString preferredCurrency = config()->get(Config::preferredFiatCurrency).toString(); double donation = AppContext::prices->convert(preferredCurrency, "WOW", tipAmount); ui->sendWidget->fill(address, "SuchWow contribution :-)", donation); - ui->tabWidget->setCurrentIndex(Tabs::SEND); + ui->tabWidget->setCurrentIndex(globals::Tabs::SEND); } void MainWindow::onViewOnBlockExplorer(const QString &txid) { @@ -1283,6 +1283,7 @@ void MainWindow::importOutputs() { void MainWindow::cleanupBeforeClose() { m_ctx->closeWallet(false, true); m_ctx->tor->stop(); + m_ctx->XMRig->stop(); this->saveGeo(); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 39d5df0..0160f4f 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -33,6 +33,7 @@ #include "utils/config.h" #include "wizard/walletwizard.h" #include "settings.h" +#include "globals.h" #include "dialog/aboutdialog.h" #include "dialog/signverifydialog.h" #include "dialog/verifyproofdialog.h" @@ -74,23 +75,6 @@ public: qreal screenDpiPhysical; qreal screenRatio; - enum Tabs { - HOME = 0, - HISTORY, - SEND, - RECEIVE, - COINS, - CALC, - XMRIG - }; - - enum TabsHome { - FORUM, - REDDIT, - SUCHWOW, - WFS - }; - public slots: void initWidgets(); void initMenu(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 574c282..d3263f3 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -316,31 +316,31 @@ - + - :/assets/images/gnome-calc.png:/assets/images/gnome-calc.png + :/assets/images/mining.png:/assets/images/mining.png - Calc + Mining - + - + - + - :/assets/images/mining.png:/assets/images/mining.png + :/assets/images/gnome-calc.png:/assets/images/gnome-calc.png - Mining + Calc - + - + diff --git a/src/ui/qml/mining.qml b/src/ui/qml/mining.qml new file mode 100644 index 0000000..d76fe23 --- /dev/null +++ b/src/ui/qml/mining.qml @@ -0,0 +1,678 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.15 +import QtQuick.Controls 2.3 + +Rectangle { + id: root + color: "#181725" + anchors.fill: parent + property variant buffer: [] + property int bufferMaxLength: 12 + // state: 0:idle 1:startup 2:syncing 3:mining + signal startMining(); + signal stopMining(); + + Component.onCompleted: { + calcAvailableHeightConsoleLines(); + } + + onHeightChanged: { + calcAvailableHeightConsoleLines(); + } + + function calcAvailableHeightConsoleLines() { + var max_lines = parseInt(textContainer.height / 20); + if(root.bufferMaxLength != max_lines && max_lines >= 4) + root.bufferMaxLength = max_lines; + } + +// width: 980 +// height: 480 + + Column { + FontLoader { id: comicMono; source: "qrc:/fonts/ComicMono.ttf" } + FontLoader { id: comicMonoBold; source: "qrc:/fonts/ComicMono-Bold.ttf" } + } + + ColumnLayout { + width: parent.width + height: parent.height + spacing: 0 + + RowLayout { + spacing: 0 + Layout.fillWidth: true + Layout.preferredHeight: 128 + + Image { + source: "qrc:/mining/topleft.png" + Layout.preferredWidth: 435 + Layout.preferredHeight: 128 + smooth: false + + // top-left monitors + ColumnLayout { + width: 102 + height: 74 + + anchors.left: parent.left + anchors.leftMargin: 14 + anchors.top: parent.top + anchors.topMargin: 42 + + Rectangle { + color: "transparent" + Layout.fillWidth: true + Layout.fillHeight: true + + Text { + text: "Hashrate" + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 4 + font.pointSize: 14 + font.family: comicMonoBold.name; + antialiasing: false + color: "#41FF00" + } + } + + Rectangle { + color: "transparent" + Layout.fillWidth: true + Layout.fillHeight: true + + Text { + id: hashRateText + text: "-" + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 4 + font.pointSize: 16 + font.family: comicMono.name; + antialiasing: false + color: "#41FF00" + } + } + } + + ColumnLayout { + width: 102 + height: 74 + + anchors.left: parent.left + anchors.leftMargin: 142 + anchors.top: parent.top + anchors.topMargin: 42 + + Rectangle { + color: "transparent" + Layout.fillWidth: true + Layout.fillHeight: true + + Text { + text: "uptime" + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 4 + font.pointSize: 14 + font.family: comicMonoBold.name; + antialiasing: false + color: "#41FF00" + } + } + + Rectangle { + color: "transparent" + Layout.fillWidth: true + Layout.fillHeight: true + + Text { + id: miningUptime + text: "-" + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 4 + font.pointSize: 12 + font.family: comicMono.name; + antialiasing: false + color: "#41FF00" + } + } + } + + AnimatedImage { + visible: mining.daemonMiningState !== 0 + source: "qrc:/mining/mining.webp" + fillMode: Image.PreserveAspectCrop + width: 115 + height: 86 + + anchors.left: parent.left + anchors.leftMargin: 263 + anchors.top: parent.top + anchors.topMargin: 34 + } + } + + ColumnLayout { + Layout.fillWidth: true + Layout.preferredHeight: 128 + spacing: 0 + + Image { + source: "qrc:/mining/warning.png" + Layout.fillWidth: true + Layout.preferredHeight: 15 + fillMode: Image.TileHorizontally + smooth: false + } + + Image { + source: "qrc:/mining/topright_bar.png" + Layout.fillWidth: true + Layout.preferredHeight: 4 + fillMode: Image.TileHorizontally + smooth: false + } + + RowLayout { + spacing: 0 + Layout.fillHeight: true + Layout.preferredHeight: 102 + + Image { + Layout.preferredWidth: 5 + Layout.preferredHeight: parent.height + source: "qrc:/mining/topright_left.png" + smooth: false + } + + Image { + Layout.fillWidth: true + Layout.preferredHeight: parent.height + source: "qrc:/mining/topright_middle.png" + fillMode: Image.TileHorizontally + smooth: false + + RowLayout { + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: 6 + anchors.right: parent.right + anchors.rightMargin: 8 + anchors.topMargin: 12 + + height: 78 + spacing: 16 + + ColumnLayout { + Layout.minimumWidth: 200 + Layout.maximumWidth: 260 + Layout.fillHeight: true + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: "transparent" + + Text { + text: "Block Height" + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 8 + font.pointSize: 20 + font.family: comicMonoBold.name; + color: "#41FF00" + antialiasing: false + } + } + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: "transparent" + + Text { + id: heightText + text: "-" + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 8 + font.pointSize: 18 + font.family: comicMonoBold.name; + color: "#41FF00" + antialiasing: false + } + } + } + + ColumnLayout { + Layout.minimumWidth: 200 + Layout.maximumWidth: 260 + Layout.fillHeight: true + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: "transparent" + + Text { + text: "Sync Progress" + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 8 + font.pointSize: 20 + font.family: comicMonoBold.name; + color: "#41FF00" + antialiasing: false + } + } + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: "transparent" + + Text { + id: syncPctText + text: "-" + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + + font.pointSize: 18 + font.family: comicMonoBold.name; + color: "#41FF00" + antialiasing: false + } + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + } + + Image { + Layout.preferredWidth: 5 + Layout.preferredHeight: parent.height + source: "qrc:/mining/topright_right.png" + smooth: false + } + } + + Item { + Layout.preferredHeight: 7 // 15 + 4 + 102 + 7 = 128 + Layout.fillWidth: true + } + } + } + + RowLayout { + spacing: 0 + //Layout.preferredHeight: 128 + Layout.fillHeight: true + Layout.fillWidth: true + + Image { + Layout.preferredWidth: 6 + Layout.fillHeight: true + source: "qrc:/mining/r_left.png" + fillMode: Image.TileVertically + smooth: false + } + + Item { + // text container + Layout.fillWidth: true + Layout.fillHeight: true + + Rectangle { + id: textContainer + color: "transparent" + height: parent.height - 20 + width: parent.width - 32 + clip: true + + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + + Text { + id: cons + anchors.margins: 4 + anchors.fill: parent + text: "Miner is idle." + font.pointSize: 12 + font.family: comicMono.name; + wrapMode: Text.WordWrap + color: "white" + } + } + } + + Image { + Layout.preferredWidth: 6 + Layout.fillHeight: true + source: "qrc:/mining/r_right.png" + fillMode: Image.TileVertically + smooth: false + } + } + + RowLayout { + spacing: 0 + Layout.preferredHeight: 140 + Layout.fillWidth: true + + Image { + Layout.preferredWidth: 306 + Layout.preferredHeight: 140 + source: "qrc:/mining/lowerleft.png" + smooth: false + + AnimatedImage { + source: mining.daemonMiningState === 0 ? "qrc:/assets/images/dog_sitting.gif" : "qrc:/assets/images/dog_running.gif" + width: 80 + height: 60 + anchors.bottom: parent.bottom + anchors.bottomMargin: 22 + anchors.left: parent.left + anchors.leftMargin: 22 + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onEntered: { + showBubble(); + } + onExited: { + hideBubble(); + } + } + } + } + + Image { + Layout.fillWidth: true + Layout.preferredHeight: 140 + source: "qrc:/mining/lower_repeat.png" + fillMode: Image.TileHorizontally + smooth: false + } + + Image { + Layout.preferredWidth: 236 + Layout.preferredHeight: 140 + source: "qrc:/mining/bottom_center_console.png" + smooth: false + + Rectangle { + // middle console clock + anchors.left: parent.left + anchors.leftMargin: 100 + anchors.top: parent.top + anchors.topMargin: 8 + color: "transparent" + + width: 54 + height: 16 + + Text { + id: clock + text: "" + antialiasing: false + font.pointSize: 9 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.family: comicMonoBold.name; + color: "#41FF00"; + + Component.onCompleted: { + root.setClock(); + } + } + } + + Image { + source: { + var imgs = ["qrc:/mining/amd.png", "qrc:/mining/intel.png"]; + return imgs[Math.floor(Math.random()*imgs.length)]; + } + width: 100 + height: 100 + fillMode: Image.Pad + smooth: false + + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + + } + } + + Image { + Layout.fillWidth: true + Layout.preferredHeight: 140 + source: "qrc:/mining/lower_repeat.png" + fillMode: Image.TileHorizontally + smooth: false + } + + Image { + Layout.preferredWidth: 308 + Layout.preferredHeight: 140 + source: "qrc:/mining/lowerright.png" + smooth: false + + Rectangle { + // lower-right button container + color: "transparent" + width: 106 + height: 100 + anchors.right: parent.right + anchors.rightMargin: 11 + anchors.top: parent.top + anchors.topMargin: 34 + + Image { + id: imgAxe + visible: mining.daemonMiningState === 0 + source: "qrc:/mining/axe.png" + width: 73 + height: 75 + smooth: false + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + + AnimatedImage { + visible: mining.daemonMiningState !== 0 + source: "qrc:/mining/elmo.gif" + width: 106 + height: 100 + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + + Text { + id: stopMiningBtn + visible: true + text: "Stop Mining" + font.pointSize: 10 + z: parent.z + 1 + color: "black" + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 28 + font.family: comicMonoBold.name; + antialiasing: false + } + } + + MouseArea { + anchors.fill: parent + z: parent.z + 1 + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + + onClicked: { + if(mining.daemonMiningState === 0) { + root.startMining(); + root.calcAvailableHeightConsoleLines(); + } + else + root.stopMining(); + } + onEntered: { + imgAxe.height = 64 + imgAxe.width = 64 + } + onExited: { + imgAxe.height = 75 + imgAxe.width = 73 + } + } + } + } + } + } + + Image { + id: bubble + visible: false + source: "qrc:/mining/bubble.png" + width: 200 + height: 60 + anchors.bottom: parent.bottom + anchors.bottomMargin: 64 + anchors.left: parent.left + anchors.leftMargin: 48 + + Rectangle { + anchors.top: parent.top + anchors.topMargin: 6 + anchors.left: parent.left + anchors.leftMargin: 10 + height: 26 + color: "transparent" + width: 183 + z: parent.z + 1 + + Text { + id: bubbleText + text: "" + color: "black" + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + //font.family: ComicMonoBold.name; + } + } + } + + Timer { + id: setClockTimer + interval: 1000*60 + running: true + repeat: true + onTriggered: setClock() + } + + Timer { + id: dogBubbleTimer + interval: 1000*30 + running: true + repeat: true + onTriggered: { + if(Math.random() >= 0.5) return; + if(bubble.visible) return; + root.dogBubbleRemoval.stop(); + root.dogBubbleRemoval.start(); + + var msg = root.bubbleMessage(); + + bubbleText.text = msg; + bubble.visible = true; + } + } + + Timer { + id: dogBubbleRemoval + interval: 2500 + running: false + repeat: false + onTriggered: bubble.visible = false; + } + + function setClock() { + var now = new Date(); + var hours = now.getHours(); + var minutes = ('0'+now.getMinutes()).slice(-2); + clock.text = hours + ":" + minutes; + } + + function resetComponents() { + hashRateText.text = "-"; + miningUptime.text = "-"; + syncPctText.text = "-"; + heightText.text = "-"; + } + + function consoleAppend(line) { + if(root.buffer.length >= root.bufferMaxLength) + root.buffer.shift() + root.buffer.push(line); + + cons.text = ""; + for(var i = 0; i != root.bufferMaxLength; i++) { + if(root.buffer[i]) + cons.text += root.buffer[i] + "\n"; + } + } + + Connections { + target: mining + function onDaemonOutput(line) { + root.consoleAppend(line); + } + + function onSyncStatus(from, to, pct) { + syncPctText.text = pct + "%"; + heightText.text = from + "/" + to; + } + + function onUptimeChanged(uptime) { + miningUptime.text = uptime; + } + + function onHashrate(hashrate) { + hashRateText.text = hashrate; + } + } + + function showBubble() { + if(bubble.visible) return; + var msg = root.bubbleMessage(); + + bubbleText.text = msg; + bubble.visible = true; + } + + function hideBubble() { + bubble.visible = false; + } + + function bubbleMessage() { + var active = ["such work!", "mining WOW!", "woof woof!", "I am tired!", "mining :@", "weeeee", "blocks everywhere!", "wooohoo", "working, twerkin'", "looking4blocks", "mining blocks"]; + var inactive = ["doing nothing!", "ZzZZzzZ", "wen mining?!", "ETA mining?!", "zZZzzZZz", "omg so bored", "so bored!!", "much idle, many zZz", "lets go!", "i'm ready!"]; + var syncing = ["wen 1gbit", "syncin'", "zZzz", "ETA sync ready?!", "downloading blocks", "fetching blocks"]; + var msg = ""; + + if(mining.daemonMiningState === 0) { + return inactive[Math.floor(Math.random()*inactive.length)]; + } else if (mining.daemonMiningState === 2) { + return syncing[Math.floor(Math.random()*syncing.length)]; + } else { + return active[Math.floor(Math.random()*active.length)]; + } + } +} diff --git a/src/utils/config.cpp b/src/utils/config.cpp index 6cd65ba..77cbe17 100644 --- a/src/utils/config.cpp +++ b/src/utils/config.cpp @@ -33,20 +33,21 @@ static const QHash configStrings = { {Config::autoOpenWalletPath,{QS("autoOpenWalletPath"), ""}}, {Config::walletPath,{QS("walletPath"), ""}}, {Config::xmrigPath,{QS("xmrigPath"), ""}}, - {Config::xmrigPool,{QS("xmrigPool"), "cryptonote.social:2223"}}, + {Config::wownerodPath, {QS("wownerodPath"), ""}}, {Config::nodes,{QS("nodes"), "{}"}}, {Config::websocketEnabled,{QS("websocketEnabled"), true}}, {Config::nodeSource,{QS("nodeSource"), 0}}, {Config::useOnionNodes,{QS("useOnionNodes"), false}}, {Config::showTabHome,{QS("showTabHome"), true}}, {Config::showTabCoins,{QS("showTabCoins"), false}}, - {Config::showTabXMRig,{QS("showTabXMRig"), false}}, + {Config::showTabXMRig,{QS("showTabXMRig"), true}}, {Config::showTabCalc,{QS("showTabCalc"), false}}, {Config::geometry, {QS("geometry"), {}}}, {Config::windowState, {QS("windowState"), {}}}, {Config::firstRun,{QS("firstRun"), false}}, {Config::hideBalance, {QS("hideBalance"), false}}, {Config::hideOnClose, {QS("hideOnClose"), false}}, + {Config::simplifiedMiningInterface, {QS("simplifiedMiningInterface"), false}}, {Config::hideFiatBalance, {QS("hideFiatBalance"), false}}, {Config::redditFrontend, {QS("redditFrontend"), "old.reddit.com"}}, {Config::showHistorySyncNotice, {QS("showHistorySyncNotice"), true}}, diff --git a/src/utils/config.h b/src/utils/config.h index 98732fb..fa6ff29 100644 --- a/src/utils/config.h +++ b/src/utils/config.h @@ -35,7 +35,7 @@ public: walletDirectory, walletPath, xmrigPath, - xmrigPool, + wownerodPath, nodes, websocketEnabled, nodeSource, @@ -50,6 +50,7 @@ public: hideBalance, hideFiatBalance, hideOnClose, + simplifiedMiningInterface, redditFrontend, showHistorySyncNotice, ignoreUpdateWarning, diff --git a/src/utils/xmrig.cpp b/src/utils/xmrig.cpp index 4cdb709..93a40d9 100644 --- a/src/utils/xmrig.cpp +++ b/src/utils/xmrig.cpp @@ -7,17 +7,24 @@ #include "utils/utils.h" #include "utils/xmrig.h" -#include "appcontext.h" +#include "mainwindow.h" -XmRig::XmRig(const QString &configDir, QObject *parent) : QObject(parent) { - this->rigDir = QDir(configDir).filePath("xmrig"); +XmRig::XmRig(const QString &configDir, QObject *parent) : + QObject(parent), + m_statusTimer(new QTimer(this)) +{ + m_statusTimer->setInterval(5000); + connect(m_statusTimer, &QTimer::timeout, [this]{ + if(daemonMiningState == DaemonMiningState::mining && m_process.state() == QProcess::Running) + m_process.write("status\n"); + }); } void XmRig::prepare() { m_process.setProcessChannelMode(QProcess::MergedChannels); - connect(&m_process, &QProcess::readyReadStandardOutput, this, &XmRig::handleProcessOutput); - connect(&m_process, &QProcess::errorOccurred, this, &XmRig::handleProcessError); - connect(&m_process, &QProcess::stateChanged, this, &XmRig::stateChanged); + connect(&m_process, &QProcess::readyReadStandardOutput, this, &XmRig::onHandleProcessOutput); + connect(&m_process, &QProcess::errorOccurred, this, &XmRig::onHandleProcessError); + connect(&m_process, &QProcess::stateChanged, this, &XmRig::onProcessStateChanged); } void XmRig::stop() { @@ -28,74 +35,145 @@ void XmRig::stop() { m_process.terminate(); #endif } + m_statusTimer->stop(); } -void XmRig::start(const QString &path, - int threads, - const QString &address, - const QString &username, - const QString &password, - bool tor, bool tls) { +bool XmRig::start(const QString &path, int threads) { + m_ctx = MainWindow::getContext(); + auto state = m_process.state(); - if (state == QProcess::ProcessState::Running || state == QProcess::ProcessState::Starting) { - emit error("Can't start XMRig, already running or starting"); - return; + if (state == QProcess::ProcessState::Running || + state == QProcess::ProcessState::Starting || + daemonMiningState != DaemonMiningState::idle) { + emit error("Can't start wownerod, already running or starting"); + return false; } if(path.isEmpty()) { - emit error("XmRig->Start path parameter missing."); - return; + emit error("wownerod path seems to be empty."); + return false; } if(!Utils::fileExists(path)) { - emit error(QString("Path to XMRig binary invalid; file does not exist: %1").arg(path)); - return; + emit error(QString("Path to wownerod binary is invalid; file does not exist: %1").arg(path)); + return false; } + auto privateSpendKey = m_ctx->currentWallet->getSecretSpendKey(); QStringList arguments; - arguments << "-o" << address; - arguments << "-a" << "rx/wow"; - arguments << "-u" << username; - if(!password.isEmpty()) - arguments << "-p" << password; - arguments << "--no-color"; - arguments << "-t" << QString::number(threads); - if(tor) - arguments << "-x" << QString("%1:%2").arg(Tor::torHost).arg(Tor::torPort); - if(tls) - arguments << "--tls"; - arguments << "--donate-level" << "1"; + arguments << "--mining-threads" << QString::number(threads); + arguments << "--start-mining" << m_ctx->currentWallet->address(0, 0); + arguments << "--spendkey" << privateSpendKey; + QString cmd = QString("%1 %2").arg(path, arguments.join(" ")); + cmd = cmd.replace(privateSpendKey, "[redacted]"); emit output(cmd.toUtf8()); + m_process.start(path, arguments); + m_statusTimer->start(); + return true; } -void XmRig::stateChanged(QProcess::ProcessState state) { - if(state == QProcess::ProcessState::Running) - emit output("XMRig started"); - else if (state == QProcess::ProcessState::NotRunning) - emit output("XMRig stopped"); +void XmRig::onProcessStateChanged(QProcess::ProcessState state) { + if(state == QProcess::ProcessState::Running) { + emit output("wownerod started"); + changeDaemonState(DaemonMiningState::startup); + } + else if (state == QProcess::ProcessState::NotRunning) { + emit output("wownerod stopped"); + changeDaemonState(DaemonMiningState::idle); + } } -void XmRig::handleProcessOutput() { - QByteArray _output = m_process.readAllStandardOutput(); - if(_output.contains("miner") && _output.contains("speed")) { - // detect hashrate - auto str = Utils::barrayToString(_output); - auto spl = str.mid(str.indexOf("speed")).split(" "); - auto rate = spl.at(2) + "H/s"; - qDebug() << "mining hashrate: " << rate; - emit hashrate(rate); +void XmRig::onHandleProcessOutput() { + QByteArray data = m_process.readAllStandardOutput(); + + for(auto &line: data.split('\n')) { + // remove timestamp + if(line.indexOf("\tI") >= 20) + line = line.remove(0, line.indexOf("\tI") + 2); + line = line.trimmed(); + + // sad attempt at removing ANSI color codes + // yes this is stupid, no i dont care + // works remarkably well so far lmao + auto ansi_start = QByteArray("\x1b\x5b"); + line = line.replace(ansi_start, ""); + line = line.replace("0;36m", ""); + line = line.replace("0;35m", ""); + line = line.replace("0;34m", ""); + line = line.replace("0;33m", ""); + line = line.replace("0;32m", ""); + line = line.replace("1;32m", ""); + line = line.replace("1;33m", ""); + line = line.replace("1;34m", ""); + line = line.replace("1;35m", ""); + line = line.replace("1;36m", ""); + if(line.startsWith("0m")) continue; + + auto lower = line.toLower(); + if(lower.isEmpty() || lower.startsWith("status")) continue; + + if(lower.startsWith("the daemon will start synchronizing")) { + changeDaemonState(DaemonMiningState::startup); + } else if(lower.startsWith("synchronization started")) { + changeDaemonState(DaemonMiningState::syncing); + } else if(lower.startsWith("synced") && lower.contains("left")) { + if(daemonMiningState < DaemonMiningState::syncing) changeDaemonState(DaemonMiningState::syncing); + QRegularExpression re("synced (\\d+)\\/(\\d+) \\((\\d+)%, (\\d+) left"); + + QRegularExpressionMatch match = re.match(lower); + if (match.hasMatch()) { + auto from = match.captured(1); + auto to = match.captured(2); + auto pct = match.captured(3); + m_from = from.toUInt(); + m_to = to.toUInt(); + emit syncStatus(m_from, m_to, pct.toInt()); + } + } else if(lower.contains("mining started. good luck")) { + emit syncStatus(m_to, m_to, 100); + changeDaemonState(DaemonMiningState::mining); + } + else if(lower.contains("you won a block reward")) + emit blockReward(); + else if(lower.contains("mining at")) { + QRegularExpression re("Height\\: (\\d+)\\/(\\d+) \\((.*)\\) on mainnet, mining at (.*), net hash .*, uptime (.*)"); + + QRegularExpressionMatch match = re.match(line); + if (match.hasMatch()) { + m_from = match.captured(1).toUInt(); + m_to = match.captured(2).toUInt(); + unsigned int pct = match.captured(3).replace("%", "").toDouble(); + auto rate = match.captured(4); + auto uptime = match.captured(5).replace(" ", ""); + + emit uptimeChanged(uptime); + emit syncStatus(m_to, m_to, pct); + emit hashrate(rate); + + line = line.remove(0, line.indexOf("mining at")); + } + } + + emit output(line.trimmed()); } +} - emit output(_output); +void XmRig::changeDaemonState(const DaemonMiningState state) { + if(daemonMiningState == state) return; + + daemonMiningState = state; + emit daemonStateChanged(daemonMiningState); } -void XmRig::handleProcessError(QProcess::ProcessError err) { +void XmRig::onHandleProcessError(QProcess::ProcessError err) { if (err == QProcess::ProcessError::Crashed) - emit error("XMRig crashed or killed"); + emit error("wownerod crashed or killed"); else if (err == QProcess::ProcessError::FailedToStart) { - auto path = config()->get(Config::xmrigPath).toString(); - emit error(QString("XMRig binary failed to start: %1").arg(path)); + auto path = config()->get(Config::wownerodPath).toString(); + emit error(QString("wownerod binary failed to start: %1").arg(path)); } + + changeDaemonState(DaemonMiningState::idle); } diff --git a/src/utils/xmrig.h b/src/utils/xmrig.h index c25c52a..0c01b1d 100644 --- a/src/utils/xmrig.h +++ b/src/utils/xmrig.h @@ -14,6 +14,14 @@ #include "utils/childproc.h" +enum DaemonMiningState { + idle = 0, + startup, + syncing, + mining +}; + +class AppContext; class XmRig : public QObject { Q_OBJECT @@ -22,24 +30,33 @@ public: explicit XmRig(const QString &configDir, QObject *parent = nullptr); void prepare(); - void start(const QString &path, int threads, const QString &address, const QString &username, const QString &password, bool tor = false, bool tls = true); + bool start(const QString &path, int threads); void stop(); - QString rigDir; - QString rigPath; + DaemonMiningState daemonMiningState = DaemonMiningState::idle; signals: void error(const QString &msg); void output(const QByteArray &data); + void blockReward(); + void syncStatus(unsigned int from, unsigned int to, unsigned int pct); void hashrate(const QString &rate); + void daemonStateChanged(DaemonMiningState state); + void uptimeChanged(QString &uptime); private slots: - void stateChanged(QProcess::ProcessState); - void handleProcessOutput(); - void handleProcessError(QProcess::ProcessError error); + void onProcessStateChanged(QProcess::ProcessState); + void onHandleProcessOutput(); + void onHandleProcessError(QProcess::ProcessError error); private: + void changeDaemonState(DaemonMiningState state); + ChildProcess m_process; + AppContext *m_ctx; + QTimer *m_statusTimer; + unsigned int m_to; + unsigned int m_from; }; #endif //WOWLET_XMRIG_H diff --git a/src/widgets/xmrigwidget.cpp b/src/widgets/xmrigwidget.cpp index c683c9e..f461bcd 100644 --- a/src/widgets/xmrigwidget.cpp +++ b/src/widgets/xmrigwidget.cpp @@ -15,19 +15,28 @@ XMRigWidget::XMRigWidget(AppContext *ctx, QWidget *parent) : QWidget(parent), ui(new Ui::XMRigWidget), m_ctx(ctx), - m_model(new QStandardItemModel(this)), - m_contextMenu(new QMenu(this)) + m_modelRig(new QStandardItemModel(this)), + m_modelWownerod(new QStandardItemModel(this)), + m_contextMenuRig(new QMenu(this)), + m_contextMenuWownerod(new QMenu(this)) { ui->setupUi(this); + this->resetUI(); - QPixmap p(":assets/images/mining.png"); - ui->lbl_logo->setPixmap(p.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + QPixmap p(":assets/images/fire.png"); + ui->lbl_logo->setPixmap(p.scaled(268, 271, Qt::KeepAspectRatio, Qt::SmoothTransformation)); - // table - ui->tableView->setModel(this->m_model); - m_contextMenu->addAction(QIcon(":/assets/images/network.png"), "Download file", this, &XMRigWidget::linkClicked); - connect(ui->tableView, &QHeaderView::customContextMenuRequested, this, &XMRigWidget::showContextMenu); - connect(ui->tableView, &QTableView::doubleClicked, this, &XMRigWidget::linkClicked); + // table XMRig + ui->tableRig->setModel(this->m_modelRig); + m_contextMenuRig->addAction(QIcon(":/assets/images/network.png"), "Download file", this, &XMRigWidget::rigLinkClicked); + connect(ui->tableRig, &QHeaderView::customContextMenuRequested, this, &XMRigWidget::showContextRigMenu); + connect(ui->tableRig, &QTableView::doubleClicked, this, &XMRigWidget::rigLinkClicked); + + // table wownerod + ui->tableWownerod->setModel(this->m_modelWownerod); + m_contextMenuWownerod->addAction(QIcon(":/assets/images/network.png"), "Download file", this, &XMRigWidget::wownerodLinkClicked); + connect(ui->tableWownerod, &QHeaderView::customContextMenuRequested, this, &XMRigWidget::showContextWownerodMenu); + connect(ui->tableWownerod, &QTableView::doubleClicked, this, &XMRigWidget::wownerodLinkClicked); // threads ui->threadSlider->setMinimum(1); @@ -44,78 +53,107 @@ XMRigWidget::XMRigWidget(AppContext *ctx, QWidget *parent) : connect(ui->btn_browse, &QPushButton::clicked, this, &XMRigWidget::onBrowseClicked); connect(ui->btn_clear, &QPushButton::clicked, this, &XMRigWidget::onClearClicked); - // defaults - ui->btn_stop->setEnabled(false); + // graphics + bool simplifiedUI = config()->get(Config::simplifiedMiningInterface).toBool(); + ui->comboBox_gfx->setCurrentIndex(simplifiedUI ? 1 : 0); + connect(ui->comboBox_gfx, QOverload::of(&QComboBox::currentIndexChanged), this, &XMRigWidget::onSimplifiedMiningChanged); + + // wownerod binary + auto path = config()->get(Config::wownerodPath).toString(); + if(!path.isEmpty()) + ui->lineEdit_path->setText(path); + + connect(ui->lineEdit_path, &QLineEdit::textChanged, [=] { + config()->set(Config::wownerodPath, ui->lineEdit_path->text().trimmed()); + }); + + // info + this->appendText(QString("Detected %1 CPU threads.").arg(threads)); + if(path.isEmpty()) + this->appendText(QString("wownerod path is empty - please point towards the wownerod executable.").arg(path)); + else if(!Utils::fileExists(path)) + this->appendText("Invalid path to the wownerod executable detected. Please set the correct path."); + else { + this->appendText(QString("wownerod path set to '%1'").arg(path)); + this->appendText("Ready to mine."); + } +} + +void XMRigWidget::resetUI() { + ui->consoleFrame->hide(); + ui->qmlFrame->hide(); + ui->qmlFrameTxt->hide(); + ui->check_autoscroll->setChecked(true); - ui->relayTor->setChecked(false); - ui->check_tls->setChecked(true); ui->label_status->setTextInteractionFlags(Qt::TextSelectableByMouse); ui->label_status->hide(); - ui->soloFrame->hide(); - ui->poolFrame->hide(); + ui->console->clear(); - // XMRig binary - auto path = config()->get(Config::xmrigPath).toString(); - if(!path.isEmpty()) { - ui->lineEdit_path->setText(path); - } + this->destroyQml(); +} - // pools - ui->poolFrame->show(); - ui->combo_pools->insertItems(0, m_pools); - auto preferredPool = config()->get(Config::xmrigPool).toString(); - if (m_pools.contains(preferredPool)) - ui->combo_pools->setCurrentIndex(m_pools.indexOf(preferredPool)); - else { - preferredPool = m_pools.at(0); - config()->set(Config::xmrigPool, preferredPool); +void XMRigWidget::startUI() { + this->resetUI(); + bool simplifiedUI = config()->get(Config::simplifiedMiningInterface).toBool(); + + if(simplifiedUI) { + this->initConsole(); + } else { + this->initQML(); } - connect(ui->combo_pools, QOverload::of(&QComboBox::currentIndexChanged), this, &XMRigWidget::onPoolChanged); +} - // info - ui->console->appendPlainText(QString("Detected %1 CPU threads.").arg(threads)); - if(!path.isEmpty() && !Utils::fileExists(path)) - ui->console->appendPlainText("Invalid path to XMRig binary detected. Please reconfigure on the Settings tab."); - else - ui->console->appendPlainText(QString("XMRig path set to %1").arg(path)); - - ui->console->appendPlainText("Ready to mine."); - - // username/password - connect(ui->lineEdit_password, &QLineEdit::editingFinished, [=]() { - m_ctx->currentWallet->setCacheAttribute("wowlet.xmrig_password", ui->lineEdit_password->text()); - m_ctx->storeWallet(); - }); - connect(ui->lineEdit_address, &QLineEdit::editingFinished, [=]() { - m_ctx->currentWallet->setCacheAttribute("wowlet.xmrig_username", ui->lineEdit_address->text()); - m_ctx->storeWallet(); - }); +void XMRigWidget::initConsole() { + ui->consoleFrame->show(); +} + +void XMRigWidget::initQML() { + if(m_quickWidget != nullptr) return; + m_quickWidget = new QQuickWidget(this); + + auto *qctx = m_quickWidget->rootContext(); + qctx->setContextProperty("cfg", config()); + qctx->setContextProperty("ctx", m_ctx); + qctx->setContextProperty("mining", this); - // checkbox connects - connect(ui->check_solo, &QCheckBox::stateChanged, this, &XMRigWidget::onSoloChecked); + m_quickWidget->setSource(QUrl("qrc:/mining.qml")); + m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + + connect((QObject*)m_quickWidget->rootObject(), SIGNAL(startMining()), + this, SLOT(onStartClicked())); + + connect((QObject*)m_quickWidget->rootObject(), SIGNAL(stopMining()), + this, SLOT(onStopClicked())); + + ui->qmlFrame->layout()->addWidget(m_quickWidget); + ui->qmlFrame->show(); + qDebug() << "created QML mining widget"; +} + +void XMRigWidget::destroyQml() { + if(m_quickWidget == nullptr) return; + m_quickWidget->disconnect(); + m_quickWidget->deleteLater(); + m_quickWidget = nullptr; + qDebug() << "destroyed QML mining widget"; +} + +void XMRigWidget::appendText(const QString &line) { + ui->console->appendPlainText(line); + m_consoleBuffer += 1; + if(m_consoleBuffer >= m_consoleBufferMax) { + ui->console->clear(); + m_consoleBuffer = 0; + } } void XMRigWidget::onWalletClosed() { this->onStopClicked(); this->onClearClicked(); - ui->lineEdit_password->setText(""); - ui->lineEdit_address->setText(""); } void XMRigWidget::onWalletOpened(Wallet *wallet){ - // Xmrig username - auto username = m_ctx->currentWallet->getCacheAttribute("wowlet.xmrig_username"); - if(!username.isEmpty()) - ui->lineEdit_address->setText(username); - - // Xmrig passwd - auto password = m_ctx->currentWallet->getCacheAttribute("wowlet.xmrig_password"); - if(!password.isEmpty()) { - ui->lineEdit_password->setText(password); - } else { - ui->lineEdit_password->setText("wowlet"); - m_ctx->currentWallet->setCacheAttribute("wowlet.xmrig_password", ui->lineEdit_password->text()); - } + int egiwoge = 1; } void XMRigWidget::onThreadsValueChanged(int threads) { @@ -123,99 +161,94 @@ void XMRigWidget::onThreadsValueChanged(int threads) { ui->label_threads->setText(QString("CPU threads: %1").arg(m_threads)); } -void XMRigWidget::onPoolChanged(int pos) { - config()->set(Config::xmrigPool, m_pools.at(pos)); -} - void XMRigWidget::onBrowseClicked() { QString fileName = QFileDialog::getOpenFileName( - this, "Path to XMRig executable", QDir::homePath()); + this, "Path to wownerod executable", QDir::homePath()); if (fileName.isEmpty()) return; - config()->set(Config::xmrigPath, fileName); + config()->set(Config::wownerodPath, fileName); ui->lineEdit_path->setText(fileName); } -void XMRigWidget::onClearClicked() { - ui->console->clear(); +void XMRigWidget::onSyncStatus(unsigned int from, unsigned int to, unsigned int pct) { + emit syncStatus(from, to, pct); } -void XMRigWidget::onStartClicked() { - QString xmrigPath; - bool solo = ui->check_solo->isChecked(); - xmrigPath = config()->get(Config::xmrigPath).toString(); - - // username is receiving address usually - auto username = m_ctx->currentWallet->getCacheAttribute("wowlet.xmrig_username"); - auto password = m_ctx->currentWallet->getCacheAttribute("wowlet.xmrig_password"); - - if(username.isEmpty()) { - QString err = "Please specify a receiving address on the Settings screen"; - ui->console->appendPlainText(err); - QMessageBox::warning(this, "Error", err); - return; +void XMRigWidget::onDaemonStateChanged(DaemonMiningState state) { + if(state == DaemonMiningState::idle) { + ui->btn_stop->setEnabled(false); + ui->btn_start->setEnabled(true); + ui->label_status->hide(); + } else { + ui->btn_stop->setEnabled(true); + ui->btn_start->setEnabled(false); + ui->label_status->show(); } - QString address; - if(solo) - address = ui->lineEdit_solo->text().trimmed(); - else - address = config()->get(Config::xmrigPool).toString(); + m_daemonMiningState = state; + emit daemonMiningStateChanged(); +} - if(address.contains("cryptonote.social") && !username.contains(".")) { - // cryptonote social requires ., we'll just grab a few chars from primary addy - username = QString("%1.%2").arg(username, m_ctx->currentWallet->address(0, 0).mid(0, 6)); - } +void XMRigWidget::onUptimeChanged(const QString &uptime) { + emit uptimeChanged(uptime); +} - m_ctx->XMRig->start(xmrigPath, m_threads, address, username, password, ui->relayTor->isChecked(), ui->check_tls->isChecked()); +void XMRigWidget::onBlockReward() { + QDateTime date = QDateTime::currentDateTime(); + QString formattedTime = date.toString("yyyy/MM/dd hh:mm"); + + auto reward = QString("Congrats: new block found at %1").arg(formattedTime); + + // @TODO: this might be blocking, what if multiple rewards happen? + QMessageBox::information(this, "Reward found", reward); +} + +void XMRigWidget::onClearClicked() { + ui->console->clear(); +} + +void XMRigWidget::onStartClicked() { + auto binPath = config()->get(Config::wownerodPath).toString(); + if(!m_ctx->XMRig->start(binPath, m_threads)) return; ui->btn_start->setEnabled(false); ui->btn_stop->setEnabled(true); - emit miningStarted(); } void XMRigWidget::onStopClicked() { - m_ctx->XMRig->stop(); - ui->btn_start->setEnabled(true); - ui->btn_stop->setEnabled(false); - ui->label_status->hide(); - emit miningEnded(); + if(m_ctx->XMRig->daemonMiningState != DaemonMiningState::idle) + m_ctx->XMRig->stop(); } void XMRigWidget::onProcessOutput(const QByteArray &data) { - auto output = Utils::barrayToString(data); - if(output.endsWith("\n")) - output = output.trimmed(); - - ui->console->appendPlainText(output); + auto line = Utils::barrayToString(data); + line = line.trimmed(); + this->appendText(line); if(ui->check_autoscroll->isChecked()) ui->console->verticalScrollBar()->setValue(ui->console->verticalScrollBar()->maximum()); } void XMRigWidget::onProcessError(const QString &msg) { - ui->console->appendPlainText("\n" + msg); - ui->btn_start->setEnabled(true); - ui->btn_stop->setEnabled(false); - ui->label_status->hide(); - emit miningEnded(); + this->appendText(msg); } -void XMRigWidget::onHashrate(const QString &hashrate) { - ui->label_status->show(); - ui->label_status->setText(QString("Mining at %1").arg(hashrate)); +void XMRigWidget::onSimplifiedMiningChanged(int idx) { + config()->set(Config::simplifiedMiningInterface, idx == 1); + this->startUI(); } -void XMRigWidget::onDownloads(const QJsonObject &data) { - // For the downloads table we'll manually update the table - // with items once, as opposed to creating a class in - // src/models/. Saves effort; full-blown model - // is unnecessary in this case. +void XMRigWidget::onHashrate(const QString &rate) { + ui->label_status->show(); + ui->label_status->setText(QString("Mining at %1").arg(rate)); + emit hashrate(rate); +} - m_model->clear(); - m_urls.clear(); +void XMRigWidget::onWownerodDownloads(const QJsonObject &data) { + m_modelWownerod->clear(); + m_urlsWownerod.clear(); auto version = data.value("version").toString(); - ui->label_latest_version->setText(QString("Latest version: %1").arg(version)); + ui->label_latest_version_wownerod->setText(QString("Latest version: %1").arg(version)); QJsonObject assets = data.value("assets").toObject(); const auto _linux = assets.value("linux").toArray(); @@ -240,54 +273,112 @@ void XMRigWidget::onDownloads(const QJsonObject &data) { auto _url = _obj.value("url").toString(); auto _created_at = _obj.value("created_at").toString(); - m_urls.append(_url); + m_urlsWownerod.append(_url); auto download_count = _obj.value("download_count").toInt(); - m_model->setItem(i, 0, Utils::qStandardItem(_name)); - m_model->setItem(i, 1, Utils::qStandardItem(_created_at)); - m_model->setItem(i, 2, Utils::qStandardItem(QString::number(download_count))); + m_modelWownerod->setItem(i, 0, Utils::qStandardItem(_name)); + m_modelWownerod->setItem(i, 1, Utils::qStandardItem(_created_at)); + m_modelWownerod->setItem(i, 2, Utils::qStandardItem(QString::number(download_count))); i++; } - m_model->setHeaderData(0, Qt::Horizontal, tr("Filename"), Qt::DisplayRole); - m_model->setHeaderData(1, Qt::Horizontal, tr("Date"), Qt::DisplayRole); - m_model->setHeaderData(2, Qt::Horizontal, tr("Downloads"), Qt::DisplayRole); + m_modelWownerod->setHeaderData(0, Qt::Horizontal, tr("Filename"), Qt::DisplayRole); + m_modelWownerod->setHeaderData(1, Qt::Horizontal, tr("Date"), Qt::DisplayRole); + m_modelWownerod->setHeaderData(2, Qt::Horizontal, tr("Downloads"), Qt::DisplayRole); - ui->tableView->verticalHeader()->setVisible(false); - ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows); - ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - ui->tableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); - ui->tableView->setColumnWidth(2, 100); + ui->tableWownerod->verticalHeader()->setVisible(false); + ui->tableWownerod->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tableWownerod->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui->tableWownerod->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->tableWownerod->setColumnWidth(2, 100); } -void XMRigWidget::showContextMenu(const QPoint &pos) { - QModelIndex index = ui->tableView->indexAt(pos); - if (!index.isValid()) { - return; - } - m_contextMenu->exec(ui->tableView->viewport()->mapToGlobal(pos)); +void XMRigWidget::onMenuTabChanged(int index) { + if(m_tabIndex == globals::Tabs::XMRIG && index != m_tabIndex) + this->resetUI(); + else if(globals::Tabs(index + 1) == globals::Tabs::XMRIG) + this->startUI(); + m_tabIndex = index + 1; } -void XMRigWidget::onSoloChecked(int state) { - if(state == 2) { - ui->poolFrame->hide(); - ui->soloFrame->show(); - ui->check_tls->setChecked(false); +void XMRigWidget::onRigDownloads(const QJsonObject &data) { + m_modelRig->clear(); + m_urlsRig.clear(); + + auto version = data.value("version").toString(); + ui->label_latest_version_rig->setText(QString("Latest version: %1").arg(version)); + QJsonObject assets = data.value("assets").toObject(); + + const auto _linux = assets.value("linux").toArray(); + const auto macos = assets.value("macos").toArray(); + const auto windows = assets.value("windows").toArray(); + + auto info = QSysInfo::productType(); + QJsonArray *os_assets; + if(info == "osx") { + os_assets = const_cast(&macos); + } else if (info == "windows") { + os_assets = const_cast(&windows); + } else { + // assume linux + os_assets = const_cast(&_linux); } - else { - ui->poolFrame->show(); - ui->soloFrame->hide(); + + int i = 0; + for(const auto &entry: *os_assets) { + auto _obj = entry.toObject(); + auto _name = _obj.value("name").toString(); + auto _url = _obj.value("url").toString(); + auto _created_at = _obj.value("created_at").toString(); + + m_urlsRig.append(_url); + auto download_count = _obj.value("download_count").toInt(); + + m_modelRig->setItem(i, 0, Utils::qStandardItem(_name)); + m_modelRig->setItem(i, 1, Utils::qStandardItem(_created_at)); + m_modelRig->setItem(i, 2, Utils::qStandardItem(QString::number(download_count))); + i++; } + + m_modelRig->setHeaderData(0, Qt::Horizontal, tr("Filename"), Qt::DisplayRole); + m_modelRig->setHeaderData(1, Qt::Horizontal, tr("Date"), Qt::DisplayRole); + m_modelRig->setHeaderData(2, Qt::Horizontal, tr("Downloads"), Qt::DisplayRole); + + ui->tableRig->verticalHeader()->setVisible(false); + ui->tableRig->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tableRig->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui->tableRig->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->tableRig->setColumnWidth(2, 100); +} + +void XMRigWidget::showContextRigMenu(const QPoint &pos) { + QModelIndex index = ui->tableRig->indexAt(pos); + if (!index.isValid()) + return; + m_contextMenuRig->exec(ui->tableRig->viewport()->mapToGlobal(pos)); +} + +void XMRigWidget::showContextWownerodMenu(const QPoint &pos) { + QModelIndex index = ui->tableWownerod->indexAt(pos); + if (!index.isValid()) + return; + m_contextMenuRig->exec(ui->tableWownerod->viewport()->mapToGlobal(pos)); +} + +void XMRigWidget::wownerodLinkClicked() { + QModelIndex index = ui->tableRig->currentIndex(); + auto download_link = m_urlsRig.at(index.row()); + Utils::externalLinkWarning(this, download_link); } -void XMRigWidget::linkClicked() { - QModelIndex index = ui->tableView->currentIndex(); - auto download_link = m_urls.at(index.row()); +void XMRigWidget::rigLinkClicked() { + QModelIndex index = ui->tableRig->currentIndex(); + auto download_link = m_urlsRig.at(index.row()); Utils::externalLinkWarning(this, download_link); } QStandardItemModel *XMRigWidget::model() { - return m_model; + return m_modelRig; } XMRigWidget::~XMRigWidget() { diff --git a/src/widgets/xmrigwidget.h b/src/widgets/xmrigwidget.h index d146204..9de5b3e 100644 --- a/src/widgets/xmrigwidget.h +++ b/src/widgets/xmrigwidget.h @@ -4,6 +4,10 @@ #ifndef XMRIGWIDGET_H #define XMRIGWIDGET_H +#include +#include +#include +#include #include #include #include @@ -11,6 +15,7 @@ #include "utils/xmrig.h" #include "utils/config.h" #include "appcontext.h" +#include "globals.h" namespace Ui { class XMRigWidget; @@ -23,6 +28,10 @@ class XMRigWidget : public QWidget public: explicit XMRigWidget(AppContext *ctx, QWidget *parent = nullptr); ~XMRigWidget() override; + + Q_PROPERTY(int daemonMiningState READ daemonMiningState NOTIFY daemonMiningStateChanged); + int daemonMiningState() const { return m_daemonMiningState; } + QStandardItemModel *model(); public slots: @@ -31,32 +40,61 @@ public slots: void onStartClicked(); void onStopClicked(); void onClearClicked(); - void onDownloads(const QJsonObject &data); - void linkClicked(); + void onBlockReward(); + void onRigDownloads(const QJsonObject &data); + void onWownerodDownloads(const QJsonObject &data); + void rigLinkClicked(); + void wownerodLinkClicked(); void onProcessError(const QString &msg); void onProcessOutput(const QByteArray &msg); - void onHashrate(const QString &hashrate); - void onSoloChecked(int state); + void onHashrate(const QString &rate); + void onDaemonStateChanged(DaemonMiningState state); + void onSyncStatus(unsigned int from, unsigned int to, unsigned int pct); + void onUptimeChanged(const QString &uptime); + void onMenuTabChanged(int index); private slots: void onBrowseClicked(); void onThreadsValueChanged(int date); - void onPoolChanged(int pos); + void onSimplifiedMiningChanged(int idx); signals: - void miningStarted(); - void miningEnded(); + void daemonOutput(const QString &line); + void syncStatus(unsigned int from, unsigned int to, unsigned int pct); + void hashrate(const QString &rate); + void daemonMiningStateChanged(); + void uptimeChanged(const QString &uptime); + +//protected: +// void resizeEvent(QResizeEvent *event) override; private: - void showContextMenu(const QPoint &pos); + void showContextRigMenu(const QPoint &pos); + void showContextWownerodMenu(const QPoint &pos); + void appendText(const QString &line); AppContext *m_ctx; Ui::XMRigWidget *ui; - QStandardItemModel *m_model; - QMenu *m_contextMenu; + QStandardItemModel *m_modelRig; + QStandardItemModel *m_modelWownerod; + QMenu *m_contextMenuRig; + QMenu *m_contextMenuWownerod; int m_threads; - QStringList m_urls; - QStringList m_pools{"cryptonote.social:2223", "pool.hashvault.pro:8888"}; + QStringList m_urlsRig; + QStringList m_urlsWownerod; + unsigned int m_tabIndex = 0; + unsigned int m_consoleBuffer = 0; + unsigned int m_consoleBufferMax = 2000; + int m_daemonMiningState = 0; + + QQuickWidget *m_quickWidget = nullptr; + + void resetUI(); + void startUI(); + + void initConsole(); + void initQML(); + void destroyQml(); }; -#endif // REDDITWIDGET_H +#endif diff --git a/src/widgets/xmrigwidget.ui b/src/widgets/xmrigwidget.ui index 5fc6db9..841e5ec 100644 --- a/src/widgets/xmrigwidget.ui +++ b/src/widgets/xmrigwidget.ui @@ -6,8 +6,8 @@ 0 0 - 1329 - 540 + 854 + 431 @@ -29,22 +29,25 @@ - 1 + 0 Mining - - - + + + QFrame::NoFrame QFrame::Plain - + + 0 + + 0 @@ -57,14 +60,14 @@ 0 - + true - + @@ -119,105 +122,128 @@ + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QML area here + + + + + + Settings - - - + + + + + Qt::Vertical + + + + 20 + 386 + + + + + + - - - + + + + + Graphics + + + + + - - - - - Threads: - - - - - - - - - Qt::Horizontal - - - - - - + + Ultra + - - - Qt::Horizontal - - - - 0 - 20 - - - + + Potato + - + - - - - QFrame::NoFrame - - - QFrame::Plain + + + + Executable - - - 0 - - - 0 - - - 0 - - - 0 - - - + - - - TLS + + + Path to wownerod... - + - Tor + Browse + + + + + + CPU threads + + + + + - - - Solo mine + + + Qt::Horizontal - + Qt::Horizontal @@ -231,209 +257,32 @@ - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - Pool - - - - - - - - 0 - 0 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - Node address - - - - - - - 127.0.0.1:18081 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - - - Receiving address - - - - - - - - - - Password (optional) - - - - - - - - - - XMRig executable - - - - - - - - - /path/to/xmrig - - - - - - - Browse - - - - - - - - + Qt::Horizontal - - QSizePolicy::Maximum - - 24 + 40 20 - + - logoimg + img - + Qt::Vertical @@ -449,30 +298,70 @@ - - - - Qt::Vertical + + + + + wownerod + + + + + + + + Latest version: + + + + + + + false + + + (right-click to download) + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::CustomContextMenu - - - 20 - 0 - + + QAbstractItemView::NoEditTriggers - + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + false + + - Downloads + XMRig - + Latest version: @@ -494,7 +383,7 @@ - + Qt::CustomContextMenu