Aditya Kulkarni
5 years ago
6 changed files with 330 additions and 46 deletions
@ -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 |
|
||||
|
#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.
|
#ifdef _WIN32 |
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) |
BOOL WINAPI WIN32_handleFunc(DWORD signal) |
||||
unsigned int seed = QRandomGenerator::securelySeeded().generate(); |
{ |
||||
|
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 |
#else |
||||
// This will be used only during debugging for compatibility reasons
|
void POSIX_handleFunc(int signal) |
||||
unsigned int seed = std::time(0); |
{ |
||||
#endif |
if (g_handler) |
||||
std::srand(seed); |
{ |
||||
|
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
|
QString locale = QLocale::system().name(); |
||||
if (sodium_init() < 0) { |
locale.truncate(locale.lastIndexOf('_')); // Get the language code
|
||||
/* panic! the library couldn't be initialized, it is not safe to use */ |
qDebug() << "Loading locale " << locale; |
||||
qDebug() << "libsodium is not initialized!"; |
|
||||
|
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") { |
void DispatchToMainThread(std::function<void()> callback) |
||||
Settings::getInstance()->setUseEmbedded(false); |
{ |
||||
} else { |
// any thread
|
||||
Settings::getInstance()->setUseEmbedded(true); |
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; |
bool handleSignal(int signal) |
||||
w.setWindowTitle("zec-qt-wallet v" + QString(APP_VERSION)); |
{ |
||||
w.show(); |
std::cout << std::endl << "Interrupted with signal " << signal << std::endl; |
||||
|
|
||||
return QApplication::exec(); |
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); |
||||
} |
} |
||||
|
|
||||
|
Loading…
Reference in new issue