Experimental support for headless mode
This commit is contained in:
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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user