// Copyright 2019-2021 The Hush developers // Released under the GPLv3 #include #include "precompiled.h" #include "mainwindow.h" #include "rpc.h" #include "settings.h" #include "version.h" class SignalHandler { public: SignalHandler(int mask = DEFAULT_SIGNALS); virtual ~SignalHandler(); 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; virtual bool handleSignal(int signal) = 0; private: int _mask; }; #include #ifndef _WIN32 #include #else #endif //!_WIN32 // There can be only ONE SignalHandler per process SignalHandler* g_handler(NULL); #ifndef _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; for (int i=0;ihandleSignal(signo); } } #endif //_WIN32 class Application : public SignalHandler { public: Application() : SignalHandler(SignalHandler::SIG_INT), w(nullptr) {} ~Application() {} int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); SingleApplication a(argc, argv, true); // Command line parser QCommandLineParser parser; parser.setApplicationDescription("Shielded desktop wallet and embedded full node for Hush"); parser.addHelpOption(); // A boolean option for running it headless QCommandLineOption headlessOption(QStringList() << "headless", "Running it via GUI."); parser.addOption(headlessOption); // No embedded will disable the embedded hushd node QCommandLineOption noembeddedOption(QStringList() << "no-embedded", "Disable embedded hushd"); parser.addOption(noembeddedOption); // Positional argument will specify a Hush payment URI parser.addPositionalArgument("hushURI", "An optional HUSH URI to pay"); parser.process(a); // Check for a positional argument indicating a Hush payment URI if (a.isSecondary()) { if (parser.positionalArguments().length() > 0) { a.sendMessage(parser.positionalArguments()[0].toUtf8()); } a.exit( 0 ); return 0; } QCoreApplication::setOrganizationName("Hush"); QCoreApplication::setApplicationName("SilentDragon"); QString locale = QLocale::system().name(); locale.truncate(locale.lastIndexOf('_')); // Get the language code qDebug() << "Loading locale " << locale; QTranslator translator; translator.load(QString(":/translations/res/silentdragon_") + locale); a.installTranslator(&translator); QIcon icon(":/icons/res/icon.ico"); QApplication::setWindowIcon(icon); // TODO: update for SD #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); } // Check for embedded option if (parser.isSet(noembeddedOption)) { Settings::getInstance()->setUseEmbedded(false); } else { Settings::getInstance()->setUseEmbedded(true); } w = new MainWindow(); w->setWindowTitle("SilentDragon v" + QString(APP_VERSION)); // If there was a payment URI on the command line, pay it if (parser.positionalArguments().length() > 0) { w->payZcashURI(parser.positionalArguments()[0]); } // Listen for any secondary instances telling us about a Hush payment URI QObject::connect(&a, &SingleApplication::receivedMessage, [=] (quint32, QByteArray msg) { QString uri(msg); // We need to execute this async, otherwise the app seems to crash for some reason. QTimer::singleShot(1, [=]() { w->payZcashURI(uri); }); }); // For MacOS, we have an event filter a.installEventFilter(w); // Check if starting headless 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 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); }