Browse Source

Experimental support for headless mode

pull/45/head
Aditya Kulkarni 5 years ago
parent
commit
4e259d6e6c
  1. 6
      src/connection.cpp
  2. 341
      src/main.cpp
  3. 7
      src/mainwindow.cpp
  4. 4
      src/mainwindow.h
  5. 11
      src/rpc.cpp
  6. 7
      src/settings.h

6
src/connection.cpp

@ -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);
}
/**

341
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>
#else
#include <windows.h>
#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
}
}
}
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
// 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();
#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
// This will be used only during debugging for compatibility reasons
unsigned int seed = std::time(0);
#endif
std::srand(seed);
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);
Settings::init();
QCoreApplication::setOrganizationName("zec-qt-wallet-org");
QCoreApplication::setApplicationName("zec-qt-wallet");
// 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!";
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();
}
if (argc >= 2 && QString::fromStdString(argv[1]) == "--no-embedded") {
Settings::getInstance()->setUseEmbedded(false);
} else {
Settings::getInstance()->setUseEmbedded(true);
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));
}
MainWindow w;
w.setWindowTitle("zec-qt-wallet v" + QString(APP_VERSION));
w.show();
return QApplication::exec();
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);
}

7
src/mainwindow.cpp

@ -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() {

4
src/mainwindow.h

@ -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

@ -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));
}
}
}

7
src/settings.h

@ -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
Loading…
Cancel
Save