From d0260ce018ff99831616a7bf20332acb35aa2d91 Mon Sep 17 00:00:00 2001 From: dsc Date: Wed, 14 Oct 2020 22:12:32 +0200 Subject: [PATCH] XMRig tab - Simple mining GUI to download and run XMRig releases. --- CMakeLists.txt | 1 + src/CMakeLists.txt | 4 + src/appcontext.cpp | 16 +- src/appcontext.h | 3 + src/assets.qrc | 2 + src/assets/images/xmrig.ico | Bin 0 -> 5430 bytes src/assets/images/xmrig.svg | 1 + src/mainwindow.cpp | 16 ++ src/mainwindow.ui | 26 +++ src/utils/config.cpp | 3 + src/utils/config.h | 3 + src/utils/xmrig.cpp | 98 +++++++++ src/utils/xmrig.h | 43 ++++ src/widgets/xmrigwidget.cpp | 226 +++++++++++++++++++++ src/widgets/xmrigwidget.h | 58 ++++++ src/widgets/xmrigwidget.ui | 387 ++++++++++++++++++++++++++++++++++++ 16 files changed, 884 insertions(+), 3 deletions(-) create mode 100644 src/assets/images/xmrig.ico create mode 100644 src/assets/images/xmrig.svg create mode 100644 src/utils/xmrig.cpp create mode 100644 src/utils/xmrig.h create mode 100644 src/widgets/xmrigwidget.cpp create mode 100644 src/widgets/xmrigwidget.h create mode 100644 src/widgets/xmrigwidget.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 47a5251..3afe305 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}") option(FETCH_DEPS "Download dependencies if they are not found" ON) option(XMRTO "Include Xmr.To module" ON) +option(XMRig "Include XMRig module" ON) option(BUILD_TOR "Build Tor" OFF) option(STATIC "Link libraries statically, requires static Qt") option(USE_DEVICE_TREZOR "Trezor support compilation" OFF) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 020ba8d..14688dc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -130,6 +130,10 @@ if(XMRTO) target_compile_definitions(feather PRIVATE XMRTO=1) endif() +if(XMRig) + target_compile_definitions(feather PRIVATE MINING=1) +endif() + if(HAVE_SYS_PRCTL_H) target_compile_definitions(feather PRIVATE HAVE_SYS_PRCTL_H=1) endif() diff --git a/src/appcontext.cpp b/src/appcontext.cpp index 3d5aa0a..e1a8202 100644 --- a/src/appcontext.cpp +++ b/src/appcontext.cpp @@ -317,6 +317,7 @@ void AppContext::onWalletOpened(Wallet *wallet) { this->currentWallet = wallet; this->walletPath = this->currentWallet->path() + ".keys"; + this->walletViewOnly = this->currentWallet->viewOnly(); config()->set(Config::walletPath, this->walletPath); connect(this->currentWallet, &Wallet::moneySpent, this, &AppContext::onMoneySpent); @@ -341,12 +342,17 @@ void AppContext::onWalletOpened(Wallet *wallet) { // force trigger preferredFiat signal for history model this->onPreferredFiatCurrencyChanged(config()->get(Config::preferredFiatCurrency).toString()); + this->setWindowTitle(); +} - // (window) title +void AppContext::setWindowTitle(bool mining) { QFileInfo fileInfo(this->walletPath); auto title = QString("Feather - [%1]").arg(fileInfo.fileName()); - if(this->currentWallet->viewOnly()) + if(this->walletViewOnly) title += " [view-only]"; + if(mining) + title += " [mining]"; + emit setTitle(title); } @@ -387,7 +393,11 @@ void AppContext::onWSMessage(const QJsonObject &msg) { else if(cmd == "nodes") { this->onWSNodes(msg.value("data").toArray()); } - +#if defined(MINING) + else if(cmd == "xmrig") { + this->XMRigDownloads(msg.value("data").toObject()); + } +#endif 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 577738a..8f9bdda 100644 --- a/src/appcontext.h +++ b/src/appcontext.h @@ -56,6 +56,7 @@ public: QString walletPath; QString walletPassword = ""; + bool walletViewOnly = false; NetworkType::Type networkType; QString applicationPath; @@ -101,6 +102,7 @@ public: void walletClose(bool emitClosedSignal = true); void storeWallet(); void refreshModels(); + void setWindowTitle(bool mining = false); public slots: void onOpenWallet(const QString& path, const QString &password); @@ -152,6 +154,7 @@ signals: void nodesUpdated(QList> &nodes); void ccsUpdated(QList> &entries); void nodeSourceChanged(NodeSource nodeSource); + void XMRigDownloads(const QJsonObject &data); void setCustomNodes(QList nodes); void ccsEmpty(); void openAliasResolveError(const QString &msg); diff --git a/src/assets.qrc b/src/assets.qrc index 57e1677..e699e14 100644 --- a/src/assets.qrc +++ b/src/assets.qrc @@ -96,6 +96,8 @@ assets/images/warning.png assets/images/xmrto_big.png assets/images/xmrto.png + assets/images/xmrig.ico + assets/images/xmrig.svg assets/images/zoom.png assets/mnemonic_25_english.txt assets/restore_heights_monero_mainnet.txt diff --git a/src/assets/images/xmrig.ico b/src/assets/images/xmrig.ico new file mode 100644 index 0000000000000000000000000000000000000000..ab79afd35efcc5f7a19f4469d294066185fbf98d GIT binary patch literal 5430 zcmb`LJxo+V5XToWv4mC|FM*(iu@oyMekArLQpy!X6Cp^#hiIY)Y9mat(6aaOtNpqv$!~rqgKW|&Q~}>>miDwd0(r_tJ&G$y9WM1HXChkZ&%r*5X|ef z;Pt!XY)Yk4nI|-#<>lo#vo!M4;)7RSN-|hmTdRseZhAV&B+auW_^;0tF5G^-7umYF zdQgF(^I+q?xBoZFzWH{Hp$#pr9>aa>WpUxmy`9L`a}Rf;&7H$zD4hoz_os`c3j1Rt z-?6xQ4EL31g)J@9eSFo0JlMFee*IO&p=~st?I&@I?%R()G+afWxL=yeH_R^_Nv+L; zcEHA6aI_9F6nUeq%>($MxC_4KkMmJ<)3l)&j4XVQotNe(M>n1q%euTHY51&8Xbpgi zUsji~F6ik}Z#+V=7rYOjf7aw@?%D_Xdd()i12*p1!l_FrrmPLA!~;HJh^@{W+;RI4 zb~f(BF62{O9zwa%GPrvm=Xh~Vu#jj$j5r}0Ot|=(XXsS8(STB?IV4wn>G({X>R%#$MRi0BfE!| zqt#&|MaD=!;X4itXZ)R+BE}XW-$5!S7tXlK7K9D0>lg-Q-jJSMT z&d`{c+5I7nKiH&=cZk=@*sE%NuQ6BIQ>}g6I9h3sBIlw%&Rsol?rhPyjt%EdtvJ_C z&H29%sIJZ;b}RpJHasTSaKHs8-26|$-x3*L<$sDk{~t!J!d)_SKS!2y&*biv=KlnA z>^-gCJEFboh3-&kWW>Us`4x9G?f_!T-K0k8*6-5X`~9*y-}2mLxLfFbo`-skYg>_j z+5BC;d#*vZ&*i&{b4X)j&wQ9?91dp{_S~_kS`SXMRigXr_icon \ No newline at end of file diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 0c9c9bb..5f682b6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -12,6 +12,7 @@ #include "mainwindow.h" #include "widgets/ccswidget.h" #include "widgets/redditwidget.h" +#include "widgets/xmrigwidget.h" #include "dialog/txconfdialog.h" #include "dialog/debuginfodialog.h" #include "dialog/walletinfodialog.h" @@ -166,6 +167,13 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) : connect(m_ctx->nodes, &Nodes::nodeExhausted, this, &MainWindow::showNodeExhaustedMessage); connect(m_ctx->nodes, &Nodes::WSNodeExhausted, this, &MainWindow::showWSNodeExhaustedMessage); + // XMRig + connect(m_ctx, &AppContext::XMRigDownloads, ui->xmrigWidget, &XMRigWidget::onDownloads); + connect(m_ctx, &AppContext::walletClosed, ui->xmrigWidget, &XMRigWidget::onStopClicked); + connect(m_ctx, &AppContext::walletClosed, ui->xmrigWidget, &XMRigWidget::onClearClicked); + connect(ui->xmrigWidget, &XMRigWidget::miningStarted, [=]{ m_ctx->setWindowTitle(true); }); + connect(ui->xmrigWidget, &XMRigWidget::miningEnded, [=]{ m_ctx->setWindowTitle(false); }); + // CCS/Reddit widget m_ccsWidget = new CCSWidget(this); m_redditWidget = new RedditWidget(this); @@ -378,6 +386,14 @@ void MainWindow::initMenu() { #else ui->actionShow_xmr_to->setVisible(false); #endif + +#if defined(MINING) + connect(ui->actionShow_XMRig, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map)); + m_tabShowHideMapper["XMRig"] = new ToggleTab(ui->tabXmrRig, "XMRig", "XMRig", ui->actionShow_XMRig, Config::showTabXMRig); + m_tabShowHideSignalMapper->setMapping(ui->actionShow_XMRig, "XMRig"); +#else + ui->actionShow_XMRig->setVisible(false); +#endif connect(ui->actionShow_calc, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map)); m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc); m_tabShowHideSignalMapper->setMapping(ui->actionShow_calc, "Calc"); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 3a58cb5..481cafc 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -280,6 +280,20 @@ + + + + :/assets/images/xmrig.ico:/assets/images/xmrig.ico + + + XmrRig + + + + + + + @@ -386,6 +400,7 @@ + @@ -591,6 +606,11 @@ Outputs + + + Show XMRig + + @@ -635,6 +655,12 @@
calcwidget.h
1 + + XMRigWidget + QWidget +
widgets/xmrigwidget.h
+ 1 +
diff --git a/src/utils/config.cpp b/src/utils/config.cpp index 362df96..c3640dd 100644 --- a/src/utils/config.cpp +++ b/src/utils/config.cpp @@ -34,12 +34,15 @@ static const QHash configStrings = { {Config::walletDirectory,{QS("walletDirectory"), ""}}, {Config::autoOpenWalletPath,{QS("autoOpenWalletPath"), ""}}, {Config::walletPath,{QS("walletPath"), ""}}, + {Config::xmrigPath,{QS("xmrigPath"), ""}}, + {Config::xmrigPool,{QS("xmrigPool"), "pool.xmr.pt:5555"}}, {Config::nodes,{QS("nodes"), "{}"}}, {Config::websocketEnabled,{QS("websocketEnabled"), true}}, {Config::nodeSource,{QS("nodeSource"), 0}}, {Config::useOnionNodes,{QS("useOnionNodes"), false}}, {Config::showTabCoins,{QS("showTabCoins"), false}}, {Config::showTabXMRto,{QS("showTabXMRto"), true}}, + {Config::showTabXMRig,{QS("showTabXMRig"), false}}, {Config::showTabCalc,{QS("showTabCalc"), true}}, {Config::geometry, {QS("geometry"), {}}}, {Config::windowState, {QS("windowState"), {}}}, diff --git a/src/utils/config.h b/src/utils/config.h index 2995062..7aaa4ec 100644 --- a/src/utils/config.h +++ b/src/utils/config.h @@ -32,6 +32,8 @@ public: blockExplorer, walletDirectory, walletPath, + xmrigPath, + xmrigPool, nodes, websocketEnabled, nodeSource, @@ -39,6 +41,7 @@ public: showTabCoins, showTabXMRto, showTabCalc, + showTabXMRig, geometry, windowState, firstRun diff --git a/src/utils/xmrig.cpp b/src/utils/xmrig.cpp new file mode 100644 index 0000000..ffbce2d --- /dev/null +++ b/src/utils/xmrig.cpp @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020, The Monero Project. + +#include +#include +#include +#include +#include + +#include "utils/utils.h" +#include "utils/xmrig.h" + +#include "appcontext.h" + + +XMRig::XMRig(QObject *parent) : QObject(parent) +{ + qDebug() << "Using embedded tor instance"; + 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); +} + +void XMRig::stop() { + if(m_process.state() == QProcess::Running) + m_process.kill(); +} + +void XMRig::terminate() { + if(m_process.state() == QProcess::Running) + m_process.terminate(); +} + +void XMRig::start(unsigned int threads, const QString &pool_name, const QString &receiving_address, bool tor) { + 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; + } + + auto path = config()->get(Config::xmrigPath).toString(); + if(path.isEmpty()) { + emit error("Please set path to XMRig binary before starting."); + return; + } + + if(!Utils::fileExists(path)) { + emit error("Path to XMRig binary invalid; file does not exist."); + return; + } + + QStringList arguments; + arguments << "-o" << pool_name; + arguments << "-a" << "rx/0"; + arguments << "-u" << receiving_address; + arguments << "-p" << "featherwallet"; + arguments << "--no-color"; + arguments << "-t" << QString::number(threads); + if(tor) + arguments << "-x" << QString("%1:%2").arg(Tor::torHost).arg(Tor::torPort); + + QString cmd = QString("%1 %2").arg(path, arguments.join(" ")); + emit output(cmd.toUtf8()); + m_process.start(path, arguments); +} + +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::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); + } + + emit output(_output); +} + +void XMRig::handleProcessError(QProcess::ProcessError err) { + if (err == QProcess::ProcessError::Crashed) + emit error("XMRig 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)); + } +} + diff --git a/src/utils/xmrig.h b/src/utils/xmrig.h new file mode 100644 index 0000000..67350c8 --- /dev/null +++ b/src/utils/xmrig.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020, The Monero Project. + +#ifndef FEATHER_XMRIG_H +#define FEATHER_XMRIG_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils/childproc.h" + +class XMRig : public QObject +{ +Q_OBJECT + +public: + explicit XMRig(QObject *parent = nullptr); + + void start(unsigned int threads, const QString &pool_name, const QString &receiving_address, bool tor = false); + void stop(); + void terminate(); + +signals: + void error(const QString &msg); + void output(const QByteArray &data); + void hashrate(const QString &rate); + +private slots: + void stateChanged(QProcess::ProcessState); + void handleProcessOutput(); + void handleProcessError(QProcess::ProcessError error); + +private: + ChildProcess m_process; +}; + +#endif //FEATHER_XMRIG_H diff --git a/src/widgets/xmrigwidget.cpp b/src/widgets/xmrigwidget.cpp new file mode 100644 index 0000000..96c8064 --- /dev/null +++ b/src/widgets/xmrigwidget.cpp @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020, The Monero Project. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xmrigwidget.h" +#include "ui_xmrigwidget.h" +#include "utils/utils.h" + +XMRigWidget::XMRigWidget(QWidget *parent) : + QWidget(parent), + ui(new Ui::XMRigWidget), + m_model(new QStandardItemModel(this)), + m_contextMenu(new QMenu(this)) +{ + ui->setupUi(this); + + QPixmap p(":assets/images/xmrig.svg"); + ui->lbl_logo->setPixmap(p.scaled(64, 64, 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); + + // XMRig core + m_rig = new XMRig(this); + connect(m_rig, &XMRig::output, this, &XMRigWidget::onProcessOutput); + connect(m_rig, &XMRig::error, this, &XMRigWidget::onProcessError); + connect(m_rig, &XMRig::hashrate, this, &XMRigWidget::onHashrate); + + // threads + ui->threadSlider->setMinimum(1); + int threads = QThread::idealThreadCount(); + m_threads = (unsigned int) threads / 2; + ui->threadSlider->setMaximum(threads); + ui->threadSlider->setValue(m_threads); + ui->label_threads->setText(QString("CPU threads: %1").arg(m_threads)); + connect(ui->threadSlider, &QSlider::valueChanged, this, &XMRigWidget::onThreadsValueChanged); + + // buttons + connect(ui->btn_start, &QPushButton::clicked, this, &XMRigWidget::onStartClicked); + connect(ui->btn_stop, &QPushButton::clicked, this, &XMRigWidget::onStopClicked); + 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->relayTor->setChecked(true); + ui->label_status->setTextInteractionFlags(Qt::TextSelectableByMouse); + // XMRig binary + auto path = config()->get(Config::xmrigPath).toString(); + ui->lineEdit_path->setText(path); + ui->label_status->hide(); + if(path.isEmpty()) + ui->tabWidget->setCurrentIndex(1); + + // pools + 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)); + 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."); +} + +void XMRigWidget::onThreadsValueChanged(int threads) { + m_threads = (unsigned 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()); + if (fileName.isEmpty()) return; + config()->set(Config::xmrigPath, fileName); + ui->lineEdit_path->setText(fileName); +} + +void XMRigWidget::onClearClicked() { + ui->console->clear(); +} + +void XMRigWidget::onStartClicked() { + auto pool_name = config()->get(Config::xmrigPool).toString(); + auto addy = ui->lineEdit_address->text(); + if(addy.isEmpty()) { + Utils::showMessageBox("Error", "Please specify a receiving address on the Settings screen", true); + return; + } + + m_rig->start(m_threads, pool_name, addy, ui->relayTor->isChecked()); + ui->btn_start->setEnabled(false); + ui->btn_stop->setEnabled(true); + emit miningStarted(); +} + +void XMRigWidget::onStopClicked() { + m_rig->terminate(); + ui->btn_start->setEnabled(true); + ui->btn_stop->setEnabled(false); + ui->label_status->hide(); + emit miningEnded(); +} + +void XMRigWidget::onProcessOutput(const QByteArray &data) { + auto output = Utils::barrayToString(data); + if(output.endsWith("\n")) + output = output.trimmed(); + + ui->console->appendPlainText(output); + + 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(); +} + +void XMRigWidget::onHashrate(const QString &hashrate) { + ui->label_status->show(); + ui->label_status->setText(QString("Mining at %1").arg(hashrate)); +} + +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. + + m_model->clear(); + m_urls.clear(); + + auto version = data.value("version").toString(); + ui->label_latest_version->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); + } + + unsigned 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_urls.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))); + 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); + + 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); +} + +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::linkClicked() { + QModelIndex index = ui->tableView->currentIndex(); + auto download_link = m_urls.at(index.row()); + Utils::externalLinkWarning(download_link); +} + +QStandardItemModel *XMRigWidget::model() { + return m_model; +} + +XMRigWidget::~XMRigWidget() { + delete ui; +} diff --git a/src/widgets/xmrigwidget.h b/src/widgets/xmrigwidget.h new file mode 100644 index 0000000..8602577 --- /dev/null +++ b/src/widgets/xmrigwidget.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020, The Monero Project. + +#ifndef XMRIGWIDGET_H +#define XMRIGWIDGET_H + +#include +#include +#include + +#include "utils/xmrig.h" +#include "utils/config.h" + +namespace Ui { + class XMRigWidget; +} + +class XMRigWidget : public QWidget +{ + Q_OBJECT + +public: + explicit XMRigWidget(QWidget *parent = nullptr); + ~XMRigWidget(); + QStandardItemModel *model(); + +public slots: + void onStartClicked(); + void onStopClicked(); + void onClearClicked(); + void onDownloads(const QJsonObject &data); + void linkClicked(); + +private slots: + void onBrowseClicked(); + void onProcessError(const QString &msg); + void onProcessOutput(const QByteArray &msg); + void onThreadsValueChanged(int date); + void onPoolChanged(int pos); + void onHashrate(const QString &hashrate); + +signals: + void miningStarted(); + void miningEnded(); + +private: + void showContextMenu(const QPoint &pos); + + Ui::XMRigWidget *ui; + QStandardItemModel *m_model; + QMenu *m_contextMenu; + unsigned int m_threads; + QStringList m_urls; + QStringList m_pools{"pool.xmr.pt:5555", "pool.supportxmr.com:3333", "mine.xmrpool.net:3333", "xmrpool.eu:5555", "xmr-eu1.nanopool.org:14444", "pool.minexmr.com:4444", "monerohash.com:2222"}; + XMRig *m_rig; +}; + +#endif // REDDITWIDGET_H diff --git a/src/widgets/xmrigwidget.ui b/src/widgets/xmrigwidget.ui new file mode 100644 index 0000000..6ae843a --- /dev/null +++ b/src/widgets/xmrigwidget.ui @@ -0,0 +1,387 @@ + + + XMRigWidget + + + + 0 + 0 + 800 + 556 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + Mining + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + + + + + + Clear + + + + + + + auto-scroll + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Mining at + + + + + + + Stop + + + + + + + Start mining + + + + + + + + + + + + + Settings + + + + + + + + + + Path to XMRig executable + + + + + + + + + + + + Browse + + + + + + + + + Receiving address + + + + + + + + + + Threads: + + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Pool + + + + + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Relay over Tor + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Maximum + + + + 24 + 20 + + + + + + + + + + logoimg + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 2000 + + + + + + + + + Downloads + + + + + + + + Latest version: + + + + + + + false + + + (right-click to download) + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::CustomContextMenu + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + false + + + + + + + + + false + + + Powered by Github API v3 + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + linkClicked() + +