Experimental support for headless mode
This commit is contained in:
@@ -27,7 +27,8 @@ ConnectionLoader::~ConnectionLoader() {
|
|||||||
|
|
||||||
void ConnectionLoader::loadConnection() {
|
void ConnectionLoader::loadConnection() {
|
||||||
QTimer::singleShot(1, [=]() { this->doAutoConnect(); });
|
QTimer::singleShot(1, [=]() { this->doAutoConnect(); });
|
||||||
d->exec();
|
if (!Settings::getInstance()->isHeadless())
|
||||||
|
d->exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionLoader::doAutoConnect(bool tryEzcashdStart) {
|
void ConnectionLoader::doAutoConnect(bool tryEzcashdStart) {
|
||||||
@@ -442,6 +443,7 @@ void ConnectionLoader::refreshZcashdState(Connection* connection, std::function<
|
|||||||
[=] (auto) {
|
[=] (auto) {
|
||||||
// Success, hide the dialog if it was shown.
|
// Success, hide the dialog if it was shown.
|
||||||
d->hide();
|
d->hide();
|
||||||
|
main->logger->write("zcashd is online.");
|
||||||
this->doRPCSetConnection(connection);
|
this->doRPCSetConnection(connection);
|
||||||
},
|
},
|
||||||
[=] (auto reply, auto res) {
|
[=] (auto reply, auto res) {
|
||||||
@@ -481,6 +483,8 @@ void ConnectionLoader::refreshZcashdState(Connection* connection, std::function<
|
|||||||
void ConnectionLoader::showInformation(QString info, QString detail) {
|
void ConnectionLoader::showInformation(QString info, QString detail) {
|
||||||
connD->status->setText(info);
|
connD->status->setText(info);
|
||||||
connD->statusDetail->setText(detail);
|
connD->statusDetail->setText(detail);
|
||||||
|
|
||||||
|
main->logger->write(info + ":" + detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
345
src/main.cpp
345
src/main.cpp
@@ -1,61 +1,322 @@
|
|||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
#include "rpc.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "turnstile.h"
|
#include "turnstile.h"
|
||||||
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
class SignalHandler
|
||||||
{
|
{
|
||||||
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
public:
|
||||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
SignalHandler(int mask = DEFAULT_SIGNALS);
|
||||||
|
virtual ~SignalHandler();
|
||||||
|
|
||||||
QApplication a(argc, argv);
|
enum SIGNALS
|
||||||
|
{
|
||||||
|
SIG_UNHANDLED = 0, // Physical signal not supported by this class
|
||||||
|
SIG_NOOP = 1, // The application is requested to do a no-op (only a target that platform-specific signals map to when they can't be raised anyway)
|
||||||
|
SIG_INT = 2, // Control+C (should terminate but consider that it's a normal way to do so; can delay a bit)
|
||||||
|
SIG_TERM = 4, // Control+Break (should terminate now without regarding the consquences)
|
||||||
|
SIG_CLOSE = 8, // Container window closed (should perform normal termination, like Ctrl^C) [Windows only; on Linux it maps to SIG_TERM]
|
||||||
|
SIG_RELOAD = 16, // Reload the configuration [Linux only, physical signal is SIGHUP; on Windows it maps to SIG_NOOP]
|
||||||
|
DEFAULT_SIGNALS = SIG_INT | SIG_TERM | SIG_CLOSE,
|
||||||
|
};
|
||||||
|
static const int numSignals = 6;
|
||||||
|
|
||||||
QCoreApplication::setOrganizationName("zec-qt-wallet-org");
|
virtual bool handleSignal(int signal) = 0;
|
||||||
QCoreApplication::setApplicationName("zec-qt-wallet");
|
|
||||||
|
|
||||||
QString locale = QLocale::system().name();
|
private:
|
||||||
locale.truncate(locale.lastIndexOf('_')); // Get the language code
|
int _mask;
|
||||||
qDebug() << "Loading locale " << locale;
|
};
|
||||||
|
|
||||||
QTranslator translator;
|
|
||||||
translator.load(QString(":/translations/res/zec_qt_wallet_") + locale);
|
|
||||||
a.installTranslator(&translator);
|
|
||||||
|
|
||||||
QIcon icon(":/icons/res/icon.ico");
|
#include <assert.h>
|
||||||
QApplication::setWindowIcon(icon);
|
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifndef _WIN32
|
||||||
QFontDatabase::addApplicationFont(":/fonts/res/Ubuntu-R.ttf");
|
|
||||||
qApp->setFont(QFont("Ubuntu", 11, QFont::Normal, false));
|
#include <signal.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
// QRandomGenerator generates a secure random number, which we use to seed.
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
|
|
||||||
unsigned int seed = QRandomGenerator::securelySeeded().generate();
|
|
||||||
#else
|
#else
|
||||||
// This will be used only during debugging for compatibility reasons
|
|
||||||
unsigned int seed = std::time(0);
|
|
||||||
#endif
|
|
||||||
std::srand(seed);
|
|
||||||
|
|
||||||
Settings::init();
|
#include <windows.h>
|
||||||
|
|
||||||
// Set up libsodium
|
#endif //!_WIN32
|
||||||
if (sodium_init() < 0) {
|
|
||||||
/* panic! the library couldn't be initialized, it is not safe to use */
|
// There can be only ONE SignalHandler per process
|
||||||
qDebug() << "libsodium is not initialized!";
|
SignalHandler* g_handler(NULL);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
BOOL WINAPI WIN32_handleFunc(DWORD);
|
||||||
|
int WIN32_physicalToLogical(DWORD);
|
||||||
|
DWORD WIN32_logicalToPhysical(int);
|
||||||
|
std::set<int> g_registry;
|
||||||
|
|
||||||
|
#else //_WIN32
|
||||||
|
|
||||||
|
void POSIX_handleFunc(int);
|
||||||
|
int POSIX_physicalToLogical(int);
|
||||||
|
int POSIX_logicalToPhysical(int);
|
||||||
|
|
||||||
|
#endif //_WIN32
|
||||||
|
|
||||||
|
SignalHandler::SignalHandler(int mask) : _mask(mask)
|
||||||
|
{
|
||||||
|
assert(g_handler == NULL);
|
||||||
|
g_handler = this;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetConsoleCtrlHandler(WIN32_handleFunc, TRUE);
|
||||||
|
#endif //_WIN32
|
||||||
|
|
||||||
|
for (int i=0;i<numSignals;i++)
|
||||||
|
{
|
||||||
|
int logical = 0x1 << i;
|
||||||
|
if (_mask & logical)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
g_registry.insert(logical);
|
||||||
|
#else
|
||||||
|
int sig = POSIX_logicalToPhysical(logical);
|
||||||
|
bool failed = signal(sig, POSIX_handleFunc) == SIG_ERR;
|
||||||
|
assert(!failed);
|
||||||
|
(void)failed; // Silence the warning in non _DEBUG; TODO: something better
|
||||||
|
|
||||||
|
#endif //_WIN32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc >= 2 && QString::fromStdString(argv[1]) == "--no-embedded") {
|
|
||||||
Settings::getInstance()->setUseEmbedded(false);
|
|
||||||
} else {
|
|
||||||
Settings::getInstance()->setUseEmbedded(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
MainWindow w;
|
|
||||||
w.setWindowTitle("zec-qt-wallet v" + QString(APP_VERSION));
|
|
||||||
w.show();
|
|
||||||
|
|
||||||
return QApplication::exec();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SignalHandler::~SignalHandler()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetConsoleCtrlHandler(WIN32_handleFunc, FALSE);
|
||||||
|
#else
|
||||||
|
for (int i=0;i<numSignals;i++)
|
||||||
|
{
|
||||||
|
int logical = 0x1 << i;
|
||||||
|
if (_mask & logical)
|
||||||
|
{
|
||||||
|
signal(POSIX_logicalToPhysical(logical), SIG_DFL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //_WIN32
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD WIN32_logicalToPhysical(int signal)
|
||||||
|
{
|
||||||
|
switch (signal)
|
||||||
|
{
|
||||||
|
case SignalHandler::SIG_INT: return CTRL_C_EVENT;
|
||||||
|
case SignalHandler::SIG_TERM: return CTRL_BREAK_EVENT;
|
||||||
|
case SignalHandler::SIG_CLOSE: return CTRL_CLOSE_EVENT;
|
||||||
|
default:
|
||||||
|
return ~(unsigned int)0; // SIG_ERR = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int POSIX_logicalToPhysical(int signal)
|
||||||
|
{
|
||||||
|
switch (signal)
|
||||||
|
{
|
||||||
|
case SignalHandler::SIG_INT: return SIGINT;
|
||||||
|
case SignalHandler::SIG_TERM: return SIGTERM;
|
||||||
|
// In case the client asks for a SIG_CLOSE handler, accept and
|
||||||
|
// bind it to a SIGTERM. Anyway the signal will never be raised
|
||||||
|
case SignalHandler::SIG_CLOSE: return SIGTERM;
|
||||||
|
case SignalHandler::SIG_RELOAD: return SIGHUP;
|
||||||
|
default:
|
||||||
|
return -1; // SIG_ERR = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //_WIN32
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
int WIN32_physicalToLogical(DWORD signal)
|
||||||
|
{
|
||||||
|
switch (signal)
|
||||||
|
{
|
||||||
|
case CTRL_C_EVENT: return SignalHandler::SIG_INT;
|
||||||
|
case CTRL_BREAK_EVENT: return SignalHandler::SIG_TERM;
|
||||||
|
case CTRL_CLOSE_EVENT: return SignalHandler::SIG_CLOSE;
|
||||||
|
default:
|
||||||
|
return SignalHandler::SIG_UNHANDLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int POSIX_physicalToLogical(int signal)
|
||||||
|
{
|
||||||
|
switch (signal)
|
||||||
|
{
|
||||||
|
case SIGINT: return SignalHandler::SIG_INT;
|
||||||
|
case SIGTERM: return SignalHandler::SIG_TERM;
|
||||||
|
case SIGHUP: return SignalHandler::SIG_RELOAD;
|
||||||
|
default:
|
||||||
|
return SignalHandler::SIG_UNHANDLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //_WIN32
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
BOOL WINAPI WIN32_handleFunc(DWORD signal)
|
||||||
|
{
|
||||||
|
if (g_handler)
|
||||||
|
{
|
||||||
|
int signo = WIN32_physicalToLogical(signal);
|
||||||
|
// The std::set is thread-safe in const reading access and we never
|
||||||
|
// write to it after the program has started so we don't need to
|
||||||
|
// protect this search by a mutex
|
||||||
|
std::set<int>::const_iterator found = g_registry.find(signo);
|
||||||
|
if (signo != -1 && found != g_registry.end())
|
||||||
|
{
|
||||||
|
return g_handler->handleSignal(signo) ? TRUE : FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void POSIX_handleFunc(int signal)
|
||||||
|
{
|
||||||
|
if (g_handler)
|
||||||
|
{
|
||||||
|
int signo = POSIX_physicalToLogical(signal);
|
||||||
|
g_handler->handleSignal(signo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //_WIN32
|
||||||
|
|
||||||
|
class Application : public SignalHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Application() : SignalHandler(SignalHandler::SIG_INT), w(nullptr) {}
|
||||||
|
|
||||||
|
~Application() { delete w; }
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||||
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
|
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
|
QCoreApplication::setOrganizationName("zec-qt-wallet-org");
|
||||||
|
QCoreApplication::setApplicationName("zec-qt-wallet");
|
||||||
|
|
||||||
|
QString locale = QLocale::system().name();
|
||||||
|
locale.truncate(locale.lastIndexOf('_')); // Get the language code
|
||||||
|
qDebug() << "Loading locale " << locale;
|
||||||
|
|
||||||
|
QTranslator translator;
|
||||||
|
translator.load(QString(":/translations/res/zec_qt_wallet_") + locale);
|
||||||
|
a.installTranslator(&translator);
|
||||||
|
|
||||||
|
QIcon icon(":/icons/res/icon.ico");
|
||||||
|
QApplication::setWindowIcon(icon);
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/res/Ubuntu-R.ttf");
|
||||||
|
qApp->setFont(QFont("Ubuntu", 11, QFont::Normal, false));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// QRandomGenerator generates a secure random number, which we use to seed.
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
|
||||||
|
unsigned int seed = QRandomGenerator::securelySeeded().generate();
|
||||||
|
#else
|
||||||
|
// This will be used only during debugging for compatibility reasons
|
||||||
|
unsigned int seed = std::time(0);
|
||||||
|
#endif
|
||||||
|
std::srand(seed);
|
||||||
|
|
||||||
|
Settings::init();
|
||||||
|
|
||||||
|
// Set up libsodium
|
||||||
|
if (sodium_init() < 0) {
|
||||||
|
/* panic! the library couldn't be initialized, it is not safe to use */
|
||||||
|
qDebug() << "libsodium is not initialized!";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command line parser
|
||||||
|
QCommandLineParser parser;
|
||||||
|
parser.setApplicationDescription("Shielded desktop wallet and embedded full node for Zcash");
|
||||||
|
parser.addHelpOption();
|
||||||
|
|
||||||
|
// A boolean option for running it headless
|
||||||
|
QCommandLineOption headlessOption(QStringList() << "headless", "Running it via GUI.");
|
||||||
|
parser.addOption(headlessOption);
|
||||||
|
|
||||||
|
QCommandLineOption noembeddedOption(QStringList() << "no-embedded", "Disable embedded zcashd");
|
||||||
|
parser.addOption(noembeddedOption);
|
||||||
|
|
||||||
|
parser.process(a);
|
||||||
|
if (parser.isSet(noembeddedOption)) {
|
||||||
|
Settings::getInstance()->setUseEmbedded(false);
|
||||||
|
} else {
|
||||||
|
Settings::getInstance()->setUseEmbedded(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
w = new MainWindow();
|
||||||
|
w->setWindowTitle("zec-qt-wallet v" + QString(APP_VERSION));
|
||||||
|
|
||||||
|
if (parser.isSet(headlessOption)) {
|
||||||
|
Settings::getInstance()->setHeadless(true);
|
||||||
|
a.setQuitOnLastWindowClosed(false);
|
||||||
|
} else {
|
||||||
|
Settings::getInstance()->setHeadless(false);
|
||||||
|
w->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
return QApplication::exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DispatchToMainThread(std::function<void()> callback)
|
||||||
|
{
|
||||||
|
// any thread
|
||||||
|
QTimer* timer = new QTimer();
|
||||||
|
timer->moveToThread(qApp->thread());
|
||||||
|
timer->setSingleShot(true);
|
||||||
|
QObject::connect(timer, &QTimer::timeout, [=]()
|
||||||
|
{
|
||||||
|
// main thread
|
||||||
|
callback();
|
||||||
|
timer->deleteLater();
|
||||||
|
});
|
||||||
|
QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handleSignal(int signal)
|
||||||
|
{
|
||||||
|
std::cout << std::endl << "Interrupted with signal " << signal << std::endl;
|
||||||
|
|
||||||
|
if (w && w->getRPC()) {
|
||||||
|
// Blocking call to closeEvent on the UI thread.
|
||||||
|
DispatchToMainThread([=] {
|
||||||
|
w->doClose();
|
||||||
|
QApplication::quit();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
QApplication::quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
MainWindow* w;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
Application app;
|
||||||
|
return app.main(argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -154,6 +154,10 @@ void MainWindow::restoreSavedStates() {
|
|||||||
ui->transactionsTable->horizontalHeader()->restoreState(s.value("tratablegeometry").toByteArray());
|
ui->transactionsTable->horizontalHeader()->restoreState(s.value("tratablegeometry").toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::doClose() {
|
||||||
|
closeEvent(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::closeEvent(QCloseEvent* event) {
|
void MainWindow::closeEvent(QCloseEvent* event) {
|
||||||
QSettings s;
|
QSettings s;
|
||||||
|
|
||||||
@@ -167,7 +171,8 @@ void MainWindow::closeEvent(QCloseEvent* event) {
|
|||||||
rpc->shutdownZcashd();
|
rpc->shutdownZcashd();
|
||||||
|
|
||||||
// Bubble up
|
// Bubble up
|
||||||
QMainWindow::closeEvent(event);
|
if (event)
|
||||||
|
QMainWindow::closeEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::turnstileProgress() {
|
void MainWindow::turnstileProgress() {
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ public:
|
|||||||
void updateTAddrCombo(bool checked);
|
void updateTAddrCombo(bool checked);
|
||||||
void updateFromCombo();
|
void updateFromCombo();
|
||||||
|
|
||||||
|
|
||||||
Ui::MainWindow* ui;
|
Ui::MainWindow* ui;
|
||||||
|
|
||||||
QLabel* statusLabel;
|
QLabel* statusLabel;
|
||||||
@@ -63,6 +62,9 @@ public:
|
|||||||
QWidget* zcashdtab;
|
QWidget* zcashdtab;
|
||||||
|
|
||||||
Logger* logger;
|
Logger* logger;
|
||||||
|
|
||||||
|
void doClose();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void closeEvent(QCloseEvent* event);
|
void closeEvent(QCloseEvent* event);
|
||||||
|
|
||||||
|
|||||||
11
src/rpc.cpp
11
src/rpc.cpp
@@ -1116,8 +1116,15 @@ void RPC::shutdownZcashd() {
|
|||||||
});
|
});
|
||||||
waiter.start(1000);
|
waiter.start(1000);
|
||||||
|
|
||||||
// Wait for the zcash process to exit.
|
// Wait for the zcash process to exit.
|
||||||
d.exec();
|
if (!Settings::getInstance()->isHeadless()) {
|
||||||
|
d.exec();
|
||||||
|
} else {
|
||||||
|
while (waiter.isActive()) {
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ public:
|
|||||||
void setUseEmbedded(bool r) { _useEmbedded = r; }
|
void setUseEmbedded(bool r) { _useEmbedded = r; }
|
||||||
bool useEmbedded() { return _useEmbedded; }
|
bool useEmbedded() { return _useEmbedded; }
|
||||||
|
|
||||||
|
void setHeadless(bool h) { _headless = h; }
|
||||||
|
bool isHeadless() { return _headless; }
|
||||||
|
|
||||||
int getBlockNumber();
|
int getBlockNumber();
|
||||||
void setBlockNumber(int number);
|
void setBlockNumber(int number);
|
||||||
|
|
||||||
@@ -103,8 +106,10 @@ private:
|
|||||||
bool _isSyncing = false;
|
bool _isSyncing = false;
|
||||||
int _blockNumber = 0;
|
int _blockNumber = 0;
|
||||||
bool _useEmbedded = false;
|
bool _useEmbedded = false;
|
||||||
|
bool _headless = false;
|
||||||
int _peerConnections = 0;
|
int _peerConnections = 0;
|
||||||
double zecPrice = 0.0;
|
|
||||||
|
double zecPrice = 0.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SETTINGS_H
|
#endif // SETTINGS_H
|
||||||
Reference in New Issue
Block a user