Experimental support for headless mode
This commit is contained in:
@@ -27,7 +27,8 @@ ConnectionLoader::~ConnectionLoader() {
|
||||
|
||||
void ConnectionLoader::loadConnection() {
|
||||
QTimer::singleShot(1, [=]() { this->doAutoConnect(); });
|
||||
d->exec();
|
||||
if (!Settings::getInstance()->isHeadless())
|
||||
d->exec();
|
||||
}
|
||||
|
||||
void ConnectionLoader::doAutoConnect(bool tryEzcashdStart) {
|
||||
@@ -442,6 +443,7 @@ void ConnectionLoader::refreshZcashdState(Connection* connection, std::function<
|
||||
[=] (auto) {
|
||||
// Success, hide the dialog if it was shown.
|
||||
d->hide();
|
||||
main->logger->write("zcashd is online.");
|
||||
this->doRPCSetConnection(connection);
|
||||
},
|
||||
[=] (auto reply, auto res) {
|
||||
@@ -481,6 +483,8 @@ void ConnectionLoader::refreshZcashdState(Connection* connection, std::function<
|
||||
void ConnectionLoader::showInformation(QString info, QString detail) {
|
||||
connD->status->setText(info);
|
||||
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 "rpc.h"
|
||||
#include "settings.h"
|
||||
#include "turnstile.h"
|
||||
|
||||
#include "version.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
class SignalHandler
|
||||
{
|
||||
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
public:
|
||||
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");
|
||||
QCoreApplication::setApplicationName("zec-qt-wallet");
|
||||
virtual bool handleSignal(int signal) = 0;
|
||||
|
||||
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);
|
||||
private:
|
||||
int _mask;
|
||||
};
|
||||
|
||||
QIcon icon(":/icons/res/icon.ico");
|
||||
QApplication::setWindowIcon(icon);
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
QFontDatabase::addApplicationFont(":/fonts/res/Ubuntu-R.ttf");
|
||||
qApp->setFont(QFont("Ubuntu", 11, QFont::Normal, false));
|
||||
#endif
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
// 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();
|
||||
#include <windows.h>
|
||||
|
||||
// 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!";
|
||||
#endif //!_WIN32
|
||||
|
||||
// There can be only ONE SignalHandler per process
|
||||
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());
|
||||
}
|
||||
|
||||
void MainWindow::doClose() {
|
||||
closeEvent(nullptr);
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent* event) {
|
||||
QSettings s;
|
||||
|
||||
@@ -167,7 +171,8 @@ void MainWindow::closeEvent(QCloseEvent* event) {
|
||||
rpc->shutdownZcashd();
|
||||
|
||||
// Bubble up
|
||||
QMainWindow::closeEvent(event);
|
||||
if (event)
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
|
||||
void MainWindow::turnstileProgress() {
|
||||
|
||||
@@ -54,7 +54,6 @@ public:
|
||||
void updateTAddrCombo(bool checked);
|
||||
void updateFromCombo();
|
||||
|
||||
|
||||
Ui::MainWindow* ui;
|
||||
|
||||
QLabel* statusLabel;
|
||||
@@ -63,6 +62,9 @@ public:
|
||||
QWidget* zcashdtab;
|
||||
|
||||
Logger* logger;
|
||||
|
||||
void doClose();
|
||||
|
||||
private:
|
||||
void closeEvent(QCloseEvent* event);
|
||||
|
||||
|
||||
11
src/rpc.cpp
11
src/rpc.cpp
@@ -1116,8 +1116,15 @@ void RPC::shutdownZcashd() {
|
||||
});
|
||||
waiter.start(1000);
|
||||
|
||||
// Wait for the zcash process to exit.
|
||||
d.exec();
|
||||
// Wait for the zcash process to exit.
|
||||
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; }
|
||||
bool useEmbedded() { return _useEmbedded; }
|
||||
|
||||
void setHeadless(bool h) { _headless = h; }
|
||||
bool isHeadless() { return _headless; }
|
||||
|
||||
int getBlockNumber();
|
||||
void setBlockNumber(int number);
|
||||
|
||||
@@ -103,8 +106,10 @@ private:
|
||||
bool _isSyncing = false;
|
||||
int _blockNumber = 0;
|
||||
bool _useEmbedded = false;
|
||||
bool _headless = false;
|
||||
int _peerConnections = 0;
|
||||
double zecPrice = 0.0;
|
||||
|
||||
double zecPrice = 0.0;
|
||||
};
|
||||
|
||||
#endif // SETTINGS_H
|
||||
Reference in New Issue
Block a user