diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fcfd9d..a9d3c5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,6 @@ set(VERSION "beta-4") option(FETCH_DEPS "Download dependencies if they are not found" ON) 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") @@ -32,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 3bd5ac4..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 @@ -212,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 @@ -256,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 @@ -383,7 +366,6 @@ 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 Valve's OpenVR library: ${OPENVR}") message(STATUS "This build is for Android: ${ANDROID}") diff --git a/src/appcontext.cpp b/src/appcontext.cpp index 244234e..c0df7c3 100644 --- a/src/appcontext.cpp +++ b/src/appcontext.cpp @@ -193,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() { diff --git a/src/assets.qrc b/src/assets.qrc index 9969331..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 @@ -226,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/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 359d25c..32140ef 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -175,18 +175,22 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) : 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::stopped, m_xmrig, &XMRigWidget::onStopped); + 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::onRigDownloads); connect(m_ctx, &AppContext::WownerodDownloads, m_xmrig, &XMRigWidget::onWownerodDownloads); - connect(m_xmrig, &XMRigWidget::miningStarted, [=]{ m_ctx->setWindowTitle(true); }); - connect(m_xmrig, &XMRigWidget::miningEnded, [=]{ m_ctx->setWindowTitle(false); }); - connect(ui->ccsWidget, &CCSWidget::selected, this, &MainWindow::showSendScreen); connect(m_ctx, &AppContext::ccsUpdated, ui->ccsWidget->model(), &CCSModel::updateEntries); connect(m_ctx, &AppContext::redditUpdated, ui->redditWidget->model(), &RedditModel::updatePosts); @@ -196,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, [=]{ @@ -296,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 @@ -333,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(); @@ -521,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){ @@ -1055,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() { @@ -1083,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" @@ -1093,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) { @@ -1101,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) { 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/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 7068595..77cbe17 100644 --- a/src/utils/config.cpp +++ b/src/utils/config.cpp @@ -47,6 +47,7 @@ static const QHash configStrings = { {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 b4304f4..fa6ff29 100644 --- a/src/utils/config.h +++ b/src/utils/config.h @@ -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 69a9698..93a40d9 100644 --- a/src/utils/xmrig.cpp +++ b/src/utils/xmrig.cpp @@ -15,16 +15,16 @@ XmRig::XmRig(const QString &configDir, QObject *parent) : { m_statusTimer->setInterval(5000); connect(m_statusTimer, &QTimer::timeout, [this]{ - if(mining_started && m_process.state() == QProcess::Running) + 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() { @@ -42,7 +42,9 @@ 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) { + if (state == QProcess::ProcessState::Running || + state == QProcess::ProcessState::Starting || + daemonMiningState != DaemonMiningState::idle) { emit error("Can't start wownerod, already running or starting"); return false; } @@ -72,45 +74,106 @@ bool XmRig::start(const QString &path, int threads) { return true; } -void XmRig::stateChanged(QProcess::ProcessState state) { - if(state == QProcess::ProcessState::Running) +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"); - this->mining_started = false; - emit stopped(); + changeDaemonState(DaemonMiningState::idle); } } -void XmRig::handleProcessOutput() { +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); - if(line.trimmed().isEmpty() || line.startsWith("status")) continue; - if(line.contains("Mining started. Good luck")) - this->mining_started = true; - else if(line.contains("you won a block reward")) + 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(line.contains("mining at")) { - auto rate = line.remove(0, line.indexOf("mining at")); - rate = rate.remove(rate.indexOf(","), rate.length()); - rate = rate.remove(0, 9); - rate = rate.trimmed(); - emit hashrate(rate); + 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); + emit output(line.trimmed()); } } -void XmRig::handleProcessError(QProcess::ProcessError err) { +void XmRig::changeDaemonState(const DaemonMiningState state) { + if(daemonMiningState == state) return; + + daemonMiningState = state; + emit daemonStateChanged(daemonMiningState); +} + +void XmRig::onHandleProcessError(QProcess::ProcessError err) { if (err == QProcess::ProcessError::Crashed) emit error("wownerod crashed or killed"); else if (err == QProcess::ProcessError::FailedToStart) { auto path = config()->get(Config::wownerodPath).toString(); emit error(QString("wownerod binary failed to start: %1").arg(path)); } - this->mining_started = false; + + changeDaemonState(DaemonMiningState::idle); } diff --git a/src/utils/xmrig.h b/src/utils/xmrig.h index 662de3c..0c01b1d 100644 --- a/src/utils/xmrig.h +++ b/src/utils/xmrig.h @@ -14,6 +14,13 @@ #include "utils/childproc.h" +enum DaemonMiningState { + idle = 0, + startup, + syncing, + mining +}; + class AppContext; class XmRig : public QObject { @@ -26,23 +33,30 @@ public: bool start(const QString &path, int threads); void stop(); + DaemonMiningState daemonMiningState = DaemonMiningState::idle; + signals: void error(const QString &msg); void output(const QByteArray &data); - void stopped(); 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; - bool mining_started = false; + 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 6263cd2..f461bcd 100644 --- a/src/widgets/xmrigwidget.cpp +++ b/src/widgets/xmrigwidget.cpp @@ -21,10 +21,10 @@ XMRigWidget::XMRigWidget(AppContext *ctx, QWidget *parent) : m_contextMenuWownerod(new QMenu(this)) { ui->setupUi(this); + this->resetUI(); QPixmap p(":assets/images/fire.png"); ui->lbl_logo->setPixmap(p.scaled(268, 271, Qt::KeepAspectRatio, Qt::SmoothTransformation)); - ui->lbl_reward->hide(); // table XMRig ui->tableRig->setModel(this->m_modelRig); @@ -53,11 +53,10 @@ 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); - ui->check_autoscroll->setChecked(true); - ui->label_status->setTextInteractionFlags(Qt::TextSelectableByMouse); - ui->label_status->hide(); + // 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(); @@ -80,6 +79,65 @@ XMRigWidget::XMRigWidget(AppContext *ctx, QWidget *parent) : } } +void XMRigWidget::resetUI() { + ui->consoleFrame->hide(); + ui->qmlFrame->hide(); + ui->qmlFrameTxt->hide(); + + ui->check_autoscroll->setChecked(true); + ui->label_status->setTextInteractionFlags(Qt::TextSelectableByMouse); + ui->label_status->hide(); + ui->console->clear(); + + this->destroyQml(); +} + +void XMRigWidget::startUI() { + this->resetUI(); + bool simplifiedUI = config()->get(Config::simplifiedMiningInterface).toBool(); + + if(simplifiedUI) { + this->initConsole(); + } else { + this->initQML(); + } +} + +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); + + 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; @@ -111,11 +169,37 @@ void XMRigWidget::onBrowseClicked() { ui->lineEdit_path->setText(fileName); } +void XMRigWidget::onSyncStatus(unsigned int from, unsigned int to, unsigned int pct) { + emit syncStatus(from, to, pct); +} + +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(); + } + + m_daemonMiningState = state; + emit daemonMiningStateChanged(); +} + +void XMRigWidget::onUptimeChanged(const QString &uptime) { + emit uptimeChanged(uptime); +} + void XMRigWidget::onBlockReward() { QDateTime date = QDateTime::currentDateTime(); QString formattedTime = date.toString("yyyy/MM/dd hh:mm"); - ui->lbl_reward->setText(QString("Congrats: new block found at %1").arg(formattedTime)); - ui->lbl_reward->show(); + + 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() { @@ -128,42 +212,35 @@ void XMRigWidget::onStartClicked() { ui->btn_start->setEnabled(false); ui->btn_stop->setEnabled(true); - emit miningStarted(); } void XMRigWidget::onStopClicked() { - m_ctx->XMRig->stop(); + 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(); - - this->appendText(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::onStopped() { - ui->btn_start->setEnabled(true); - ui->btn_stop->setEnabled(false); - ui->label_status->hide(); - emit miningEnded(); -} - void XMRigWidget::onProcessError(const QString &msg) { this->appendText(msg); - ui->btn_start->setEnabled(true); - ui->btn_stop->setEnabled(false); - ui->label_status->hide(); - emit miningEnded(); } -void XMRigWidget::onHashrate(const QString &hashrate) { +void XMRigWidget::onSimplifiedMiningChanged(int idx) { + config()->set(Config::simplifiedMiningInterface, idx == 1); + this->startUI(); +} + +void XMRigWidget::onHashrate(const QString &rate) { ui->label_status->show(); - ui->label_status->setText(QString("Mining at %1").arg(hashrate)); + ui->label_status->setText(QString("Mining at %1").arg(rate)); + emit hashrate(rate); } void XMRigWidget::onWownerodDownloads(const QJsonObject &data) { @@ -216,6 +293,14 @@ void XMRigWidget::onWownerodDownloads(const QJsonObject &data) { ui->tableWownerod->setColumnWidth(2, 100); } +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::onRigDownloads(const QJsonObject &data) { m_modelRig->clear(); m_urlsRig.clear(); diff --git a/src/widgets/xmrigwidget.h b/src/widgets/xmrigwidget.h index 465aaf2..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: @@ -30,7 +39,6 @@ public slots: void onWalletOpened(Wallet *wallet); void onStartClicked(); void onStopClicked(); - void onStopped(); void onClearClicked(); void onBlockReward(); void onRigDownloads(const QJsonObject &data); @@ -39,15 +47,26 @@ public slots: void wownerodLinkClicked(); void onProcessError(const QString &msg); void onProcessOutput(const QByteArray &msg); - void onHashrate(const QString &hashrate); + 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 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 showContextRigMenu(const QPoint &pos); @@ -63,8 +82,19 @@ private: int m_threads; 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 1fdeb50..841e5ec 100644 --- a/src/widgets/xmrigwidget.ui +++ b/src/widgets/xmrigwidget.ui @@ -6,8 +6,8 @@ 0 0 - 959 - 534 + 854 + 431 @@ -35,68 +35,19 @@ Mining - - - - - - - Clear - - - - - - - auto-scroll - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Mining at - - - - - - - Stop - - - - - - - Start mining - - - - - + - + QFrame::NoFrame QFrame::Plain - + + 0 + + 0 @@ -116,187 +67,222 @@ + + + + + + Clear + + + + + + + auto-scroll + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Mining at + + + + + + + Stop + + + + + + + Start mining + + + + + - - - - - - Executable - - - - - - - - - Path to wownerod... - - - - - - - Browse - - - - - - - - - CPU threads - - - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Qt::Horizontal - - - - - - rewardtxt + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QML area here + + + + - How-To + Settings + + + + Qt::Vertical + + + + 20 + 386 + + + + - - 12 - - - - - - - img - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - 16 - - - - How to solo mine - - - - + + - 1. Go to the 'wownerod' tab + Graphics - - - - 2. Download the latest wownerod archive (right-click) - + + + + + Ultra + + + + + Potato + + - - + + - 3. Unpack/extract the archive + Executable - - - - 4. Go to the 'Mining' tab - - + + + + + + Path to wownerod... + + + + + + + Browse + + + + - - + + - 5. Click 'browse' and select the previously extracted wownerod executable + CPU threads - - - - 6. Click 'Start mining' to start solo-mining. Goodluck! - - + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + - + - P.S: Do not start multiple wownerod instances, i.e if you already have one running on your machine, things will (probably) crash and burn. - - - true + img - + Qt::Vertical @@ -310,19 +296,6 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - -