Browse Source

Merge pull request #3493

9a2305a qt: Stop shutdown detection timer during shutdown (Wladimir J. van der Laan)
35ecf85 qt: Remove global references in bitcoin.cpp (Wladimir J. van der Laan)
55fe4de qt: Show window while shutting down (Wladimir J. van der Laan)
202d853 qt: Move initialization/shutdown to a thread (Wladimir J. van der Laan)
0.10
Wladimir J. van der Laan 11 years ago
parent
commit
054be736d4
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 2
      src/qt/Makefile.am
  2. 456
      src/qt/bitcoin.cpp
  3. 38
      src/qt/bitcoingui.cpp
  4. 5
      src/qt/bitcoingui.h
  5. 36
      src/qt/splashscreen.cpp
  6. 11
      src/qt/splashscreen.h

2
src/qt/Makefile.am

@ -108,7 +108,7 @@ QT_MOC_CPP = moc_aboutdialog.cpp moc_addressbookpage.cpp \
BITCOIN_MM = macdockiconhandler.mm macnotificationhandler.mm BITCOIN_MM = macdockiconhandler.mm macnotificationhandler.mm
QT_MOC = intro.moc overviewpage.moc rpcconsole.moc QT_MOC = intro.moc overviewpage.moc rpcconsole.moc bitcoin.moc
QT_QRC_CPP = qrc_bitcoin.cpp QT_QRC_CPP = qrc_bitcoin.cpp
QT_QRC = bitcoin.qrc QT_QRC = bitcoin.qrc

456
src/qt/bitcoin.cpp

@ -30,6 +30,9 @@
#include <QSettings> #include <QSettings>
#include <QTimer> #include <QTimer>
#include <QTranslator> #include <QTranslator>
#include <QThread>
#include <QVBoxLayout>
#include <QLabel>
#if defined(QT_STATICPLUGIN) #if defined(QT_STATICPLUGIN)
#include <QtPlugin> #include <QtPlugin>
@ -52,40 +55,8 @@ Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
// Declare meta types used for QMetaObject::invokeMethod // Declare meta types used for QMetaObject::invokeMethod
Q_DECLARE_METATYPE(bool*) Q_DECLARE_METATYPE(bool*)
// Need a global reference for the notifications to find the GUI
static BitcoinGUI *guiref;
static SplashScreen *splashref;
static bool ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style)
{
if(guiref)
{
bool modal = (style & CClientUIInterface::MODAL);
bool ret = false;
// In case of modal message, use blocking connection to wait for user to click a button
QMetaObject::invokeMethod(guiref, "message",
modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(caption)),
Q_ARG(QString, QString::fromStdString(message)),
Q_ARG(unsigned int, style),
Q_ARG(bool*, &ret));
return ret;
}
else
{
LogPrintf("%s: %s\n", caption.c_str(), message.c_str());
fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str());
return false;
}
}
static void InitMessage(const std::string &message) static void InitMessage(const std::string &message)
{ {
if(splashref)
{
splashref->showMessage(QString::fromStdString(message), Qt::AlignBottom|Qt::AlignHCenter, QColor(55,55,55));
qApp->processEvents();
}
LogPrintf("init message: %s\n", message.c_str()); LogPrintf("init message: %s\n", message.c_str());
} }
@ -97,15 +68,6 @@ static std::string Translate(const char* psz)
return QCoreApplication::translate("bitcoin-core", psz).toStdString(); return QCoreApplication::translate("bitcoin-core", psz).toStdString();
} }
/* Handle runaway exceptions. Shows a message box with the problem and quits the program.
*/
static void handleRunawayException(std::exception *e)
{
PrintExceptionContinue(e, "Runaway exception");
QMessageBox::critical(0, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. Bitcoin can no longer continue safely and will quit.") + QString("\n\n") + QString::fromStdString(strMiscWarning));
exit(1);
}
/** Set up translations */ /** Set up translations */
static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator) static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator)
{ {
@ -162,6 +124,300 @@ void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons
} }
#endif #endif
/** Class encapsulating Bitcoin Core startup and shutdown.
* Allows running startup and shutdown in a different thread from the UI thread.
*/
class BitcoinCore: public QObject
{
Q_OBJECT
public:
explicit BitcoinCore();
public slots:
void initialize();
void shutdown();
signals:
void initializeResult(int retval);
void shutdownResult(int retval);
void runawayException(const QString &message);
private:
boost::thread_group threadGroup;
/// Pass fatal exception message to UI thread
void handleRunawayException(std::exception *e);
};
/** Main Bitcoin application object */
class BitcoinApplication: public QApplication
{
Q_OBJECT
public:
explicit BitcoinApplication(int &argc, char **argv);
~BitcoinApplication();
/// Create payment server
void createPaymentServer();
/// Create options model
void createOptionsModel();
/// Create main window
void createWindow(bool isaTestNet);
/// Create splash screen
void createSplashScreen(bool isaTestNet);
/// Request core initialization
void requestInitialize();
/// Request core shutdown
void requestShutdown();
/// Get process return value
int getReturnValue() { return returnValue; }
public slots:
void initializeResult(int retval);
void shutdownResult(int retval);
/// Handle runaway exceptions. Shows a message box with the problem and quits the program.
void handleRunawayException(const QString &message);
signals:
void requestedInitialize();
void requestedShutdown();
void stopThread();
void splashFinished(QWidget *window);
private:
QThread *coreThread;
PaymentServer* paymentServer;
OptionsModel *optionsModel;
ClientModel *clientModel;
BitcoinGUI *window;
WalletModel *walletModel;
QTimer *pollShutdownTimer;
int returnValue;
void startThread();
};
#include "bitcoin.moc"
BitcoinCore::BitcoinCore():
QObject()
{
}
void BitcoinCore::handleRunawayException(std::exception *e)
{
PrintExceptionContinue(e, "Runaway exception");
emit runawayException(QString::fromStdString(strMiscWarning));
}
void BitcoinCore::initialize()
{
try
{
LogPrintf("Running AppInit2 in thread\n");
int rv = AppInit2(threadGroup);
emit initializeResult(rv);
} catch (std::exception& e) {
handleRunawayException(&e);
} catch (...) {
handleRunawayException(NULL);
}
}
void BitcoinCore::shutdown()
{
try
{
LogPrintf("Running Shutdown in thread\n");
threadGroup.interrupt_all();
threadGroup.join_all();
Shutdown();
LogPrintf("Shutdown finished\n");
emit shutdownResult(1);
} catch (std::exception& e) {
handleRunawayException(&e);
} catch (...) {
handleRunawayException(NULL);
}
}
BitcoinApplication::BitcoinApplication(int &argc, char **argv):
QApplication(argc, argv),
coreThread(0),
paymentServer(0),
optionsModel(0),
clientModel(0),
window(0),
walletModel(0),
pollShutdownTimer(0),
returnValue(0)
{
setQuitOnLastWindowClosed(false);
startThread();
}
BitcoinApplication::~BitcoinApplication()
{
LogPrintf("Stopping thread\n");
emit stopThread();
coreThread->wait();
LogPrintf("Stopped thread\n");
delete window;
delete paymentServer;
delete optionsModel;
}
void BitcoinApplication::createPaymentServer()
{
paymentServer = new PaymentServer(this);
}
void BitcoinApplication::createOptionsModel()
{
optionsModel = new OptionsModel();
}
void BitcoinApplication::createWindow(bool isaTestNet)
{
window = new BitcoinGUI(isaTestNet, 0);
pollShutdownTimer = new QTimer(window);
connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown()));
pollShutdownTimer->start(200);
}
void BitcoinApplication::createSplashScreen(bool isaTestNet)
{
SplashScreen *splash = new SplashScreen(QPixmap(), 0, isaTestNet);
splash->setAttribute(Qt::WA_DeleteOnClose);
splash->show();
connect(this, SIGNAL(splashFinished(QWidget*)), splash, SLOT(slotFinish(QWidget*)));
}
void BitcoinApplication::startThread()
{
coreThread = new QThread(this);
BitcoinCore *executor = new BitcoinCore();
executor->moveToThread(coreThread);
/* communication to and from thread */
connect(executor, SIGNAL(initializeResult(int)), this, SLOT(initializeResult(int)));
connect(executor, SIGNAL(shutdownResult(int)), this, SLOT(shutdownResult(int)));
connect(executor, SIGNAL(runawayException(QString)), this, SLOT(handleRunawayException(QString)));
connect(this, SIGNAL(requestedInitialize()), executor, SLOT(initialize()));
connect(this, SIGNAL(requestedShutdown()), executor, SLOT(shutdown()));
/* make sure executor object is deleted in its own thread */
connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater()));
connect(this, SIGNAL(stopThread()), coreThread, SLOT(quit()));
coreThread->start();
}
void BitcoinApplication::requestInitialize()
{
LogPrintf("Requesting initialize\n");
emit requestedInitialize();
}
void BitcoinApplication::requestShutdown()
{
LogPrintf("Requesting shutdown\n");
window->hide();
window->setClientModel(0);
window->removeAllWallets();
pollShutdownTimer->stop();
delete walletModel;
walletModel = 0;
delete clientModel;
clientModel = 0;
// Show a simple window indicating shutdown status
QWidget *shutdownWindow = new QWidget();
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(new QLabel(
tr("Bitcoin Core is shutting down...\n") +
tr("Do not shut down the computer until this window disappears.")));
shutdownWindow->setLayout(layout);
// Center shutdown window at where main window was
const QPoint global = window->mapToGlobal(window->rect().center());
shutdownWindow->move(global.x() - shutdownWindow->width() / 2, global.y() - shutdownWindow->height() / 2);
shutdownWindow->show();
// Request shutdown from core thread
emit requestedShutdown();
}
void BitcoinApplication::initializeResult(int retval)
{
LogPrintf("Initialization result: %i\n", retval);
// Set exit result: 0 if successful, 1 if failure
returnValue = retval ? 0 : 1;
if(retval)
{
// Miscellaneous initialization after core is initialized
optionsModel->Upgrade(); // Must be done after AppInit2
PaymentServer::LoadRootCAs();
paymentServer->setOptionsModel(optionsModel);
emit splashFinished(window);
clientModel = new ClientModel(optionsModel);
window->setClientModel(clientModel);
if(pwalletMain)
{
walletModel = new WalletModel(pwalletMain, optionsModel);
window->addWallet("~Default", walletModel);
window->setCurrentWallet("~Default");
connect(walletModel, SIGNAL(coinsSent(CWallet*,SendCoinsRecipient,QByteArray)),
paymentServer, SLOT(fetchPaymentACK(CWallet*,const SendCoinsRecipient&,QByteArray)));
}
// If -min option passed, start window minimized.
if(GetBoolArg("-min", false))
{
window->showMinimized();
}
else
{
window->show();
}
// Now that initialization/startup is done, process any command-line
// bitcoin: URIs or payment requests:
connect(paymentServer, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)),
window, SLOT(handlePaymentRequest(SendCoinsRecipient)));
connect(window, SIGNAL(receivedURI(QString)),
paymentServer, SLOT(handleURIOrFile(QString)));
connect(paymentServer, SIGNAL(message(QString,QString,unsigned int)),
window, SLOT(message(QString,QString,unsigned int)));
QTimer::singleShot(100, paymentServer, SLOT(uiReady()));
} else {
quit(); // Exit main loop
}
}
void BitcoinApplication::shutdownResult(int retval)
{
LogPrintf("Shutdown result: %i\n", retval);
quit(); // Exit main loop after shutdown finished
}
void BitcoinApplication::handleRunawayException(const QString &message)
{
QMessageBox::critical(0, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. Bitcoin can no longer continue safely and will quit.") + QString("\n\n") + message);
::exit(1);
}
#ifndef BITCOIN_QT_TEST #ifndef BITCOIN_QT_TEST
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@ -189,7 +445,7 @@ int main(int argc, char *argv[])
#endif #endif
Q_INIT_RESOURCE(bitcoin); Q_INIT_RESOURCE(bitcoin);
QApplication app(argc, argv); BitcoinApplication app(argc, argv);
#if QT_VERSION > 0x050100 #if QT_VERSION > 0x050100
// Generate high-dpi pixmaps // Generate high-dpi pixmaps
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
@ -215,6 +471,7 @@ int main(int argc, char *argv[])
// Now that QSettings are accessible, initialize translations // Now that QSettings are accessible, initialize translations
QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator; QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator;
initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator);
uiInterface.Translate.connect(Translate);
// Show help message immediately after parsing command-line options (for "-lang") and setting locale, // Show help message immediately after parsing command-line options (for "-lang") and setting locale,
// but before showing splash screen. // but before showing splash screen.
@ -254,7 +511,7 @@ int main(int argc, char *argv[])
// Start up the payment server early, too, so impatient users that click on // Start up the payment server early, too, so impatient users that click on
// bitcoin: links repeatedly have their payment requests routed to this process: // bitcoin: links repeatedly have their payment requests routed to this process:
PaymentServer* paymentServer = new PaymentServer(&app); app.createPaymentServer();
/// 8. Main GUI initialization /// 8. Main GUI initialization
// Install global event filter that makes sure that long tooltips can be word-wrapped // Install global event filter that makes sure that long tooltips can be word-wrapped
@ -266,24 +523,22 @@ int main(int argc, char *argv[])
qInstallMessageHandler(DebugMessageHandler); qInstallMessageHandler(DebugMessageHandler);
#endif #endif
// Load GUI settings from QSettings // Load GUI settings from QSettings
OptionsModel optionsModel; app.createOptionsModel();
// Subscribe to global signals from core // Subscribe to global signals from core
uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox);
uiInterface.InitMessage.connect(InitMessage); uiInterface.InitMessage.connect(InitMessage);
uiInterface.Translate.connect(Translate);
// Show splash screen if appropriate // Show help message immediately after parsing command-line options (for "-lang") and setting locale,
SplashScreen splash(QPixmap(), 0, isaTestNet); // but before showing splash screen.
if (GetBoolArg("-splash", true) && !GetBoolArg("-min", false)) if (mapArgs.count("-?") || mapArgs.count("--help"))
{ {
splash.show(); GUIUtil::HelpMessageBox help;
splash.setAutoFillBackground(true); help.showOrPrint();
splashref = &splash; return 1;
} }
app.processEvents(); if (GetBoolArg("-splash", true) && !GetBoolArg("-min", false))
app.setQuitOnLastWindowClosed(false); app.createSplashScreen(isaTestNet);
try try
{ {
@ -293,93 +548,18 @@ int main(int argc, char *argv[])
if (GUIUtil::GetStartOnSystemStartup()) if (GUIUtil::GetStartOnSystemStartup())
GUIUtil::SetStartOnSystemStartup(true); GUIUtil::SetStartOnSystemStartup(true);
#endif #endif
app.createWindow(isaTestNet);
boost::thread_group threadGroup; app.requestInitialize();
app.exec();
BitcoinGUI window(isaTestNet, 0); app.requestShutdown();
guiref = &window;
QTimer* pollShutdownTimer = new QTimer(guiref);
QObject::connect(pollShutdownTimer, SIGNAL(timeout()), guiref, SLOT(detectShutdown()));
pollShutdownTimer->start(200);
if(AppInit2(threadGroup))
{
{
// Put this in a block, so that the Model objects are cleaned up before
// calling Shutdown().
optionsModel.Upgrade(); // Must be done after AppInit2
PaymentServer::LoadRootCAs();
paymentServer->setOptionsModel(&optionsModel);
if (splashref)
splash.finish(&window);
ClientModel clientModel(&optionsModel);
window.setClientModel(&clientModel);
WalletModel *walletModel = 0;
if(pwalletMain)
walletModel = new WalletModel(pwalletMain, &optionsModel);
if(walletModel)
{
window.addWallet("~Default", walletModel);
window.setCurrentWallet("~Default");
}
// If -min option passed, start window minimized.
if(GetBoolArg("-min", false))
{
window.showMinimized();
}
else
{
window.show();
}
// Now that initialization/startup is done, process any command-line
// bitcoin: URIs or payment requests:
QObject::connect(paymentServer, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)),
&window, SLOT(handlePaymentRequest(SendCoinsRecipient)));
QObject::connect(&window, SIGNAL(receivedURI(QString)),
paymentServer, SLOT(handleURIOrFile(QString)));
if(walletModel)
{
QObject::connect(walletModel, SIGNAL(coinsSent(CWallet*,SendCoinsRecipient,QByteArray)),
paymentServer, SLOT(fetchPaymentACK(CWallet*,const SendCoinsRecipient&,QByteArray)));
}
QObject::connect(paymentServer, SIGNAL(message(QString,QString,unsigned int)),
guiref, SLOT(message(QString,QString,unsigned int)));
QTimer::singleShot(100, paymentServer, SLOT(uiReady()));
app.exec(); app.exec();
window.hide();
window.setClientModel(0);
window.removeAllWallets();
guiref = 0;
delete walletModel;
}
// Shutdown the core and its threads, but don't exit the GUI here
threadGroup.interrupt_all();
threadGroup.join_all();
Shutdown();
}
else
{
threadGroup.interrupt_all();
threadGroup.join_all();
Shutdown();
return 1;
}
} catch (std::exception& e) { } catch (std::exception& e) {
handleRunawayException(&e); PrintExceptionContinue(&e, "Runaway exception");
app.handleRunawayException(QString::fromStdString(strMiscWarning));
} catch (...) { } catch (...) {
handleRunawayException(NULL); PrintExceptionContinue(NULL, "Runaway exception");
app.handleRunawayException(QString::fromStdString(strMiscWarning));
} }
return 0; return app.getReturnValue();
} }
#endif // BITCOIN_QT_TEST #endif // BITCOIN_QT_TEST

38
src/qt/bitcoingui.cpp

@ -170,10 +170,16 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) :
// Initially wallet actions should be disabled // Initially wallet actions should be disabled
setWalletActionsEnabled(false); setWalletActionsEnabled(false);
// Subscribe to notifications from core
subscribeToCoreSignals();
} }
BitcoinGUI::~BitcoinGUI() BitcoinGUI::~BitcoinGUI()
{ {
// Unsubscribe from notifications from core
unsubscribeFromCoreSignals();
GUIUtil::saveWindowGeometry("nWindow", this); GUIUtil::saveWindowGeometry("nWindow", this);
if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu) if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu)
trayIcon->hide(); trayIcon->hide();
@ -849,5 +855,35 @@ void BitcoinGUI::toggleHidden()
void BitcoinGUI::detectShutdown() void BitcoinGUI::detectShutdown()
{ {
if (ShutdownRequested()) if (ShutdownRequested())
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection); {
if(rpcConsole)
rpcConsole->hide();
qApp->quit();
}
}
static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string& message, const std::string& caption, unsigned int style)
{
bool modal = (style & CClientUIInterface::MODAL);
bool ret = false;
// In case of modal message, use blocking connection to wait for user to click a button
QMetaObject::invokeMethod(gui, "message",
modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(caption)),
Q_ARG(QString, QString::fromStdString(message)),
Q_ARG(unsigned int, style),
Q_ARG(bool*, &ret));
return ret;
}
void BitcoinGUI::subscribeToCoreSignals()
{
// Connect signals to client
uiInterface.ThreadSafeMessageBox.connect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3));
}
void BitcoinGUI::unsubscribeFromCoreSignals()
{
// Disconnect signals from client
uiInterface.ThreadSafeMessageBox.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3));
} }

5
src/qt/bitcoingui.h

@ -111,6 +111,11 @@ private:
/** Enable or disable all wallet-related actions */ /** Enable or disable all wallet-related actions */
void setWalletActionsEnabled(bool enabled); void setWalletActionsEnabled(bool enabled);
/** Connect core signals to GUI client */
void subscribeToCoreSignals();
/** Disconnect core signals from GUI client */
void unsubscribeFromCoreSignals();
signals: signals:
/** Signal raised when a URI was entered or dragged to the GUI */ /** Signal raised when a URI was entered or dragged to the GUI */
void receivedURI(const QString &uri); void receivedURI(const QString &uri);

36
src/qt/splashscreen.cpp

@ -6,6 +6,7 @@
#include "clientversion.h" #include "clientversion.h"
#include "util.h" #include "util.h"
#include "ui_interface.h"
#include <QApplication> #include <QApplication>
#include <QPainter> #include <QPainter>
@ -13,6 +14,8 @@
SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f, bool isTestNet) : SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f, bool isTestNet) :
QSplashScreen(pixmap, f) QSplashScreen(pixmap, f)
{ {
setAutoFillBackground(true);
// set reference point, paddings // set reference point, paddings
int paddingRight = 50; int paddingRight = 50;
int paddingTop = 50; int paddingTop = 50;
@ -83,4 +86,37 @@ SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f, bool isTest
pixPaint.end(); pixPaint.end();
this->setPixmap(newPixmap); this->setPixmap(newPixmap);
subscribeToCoreSignals();
}
SplashScreen::~SplashScreen()
{
unsubscribeFromCoreSignals();
}
void SplashScreen::slotFinish(QWidget *mainWin)
{
finish(mainWin);
}
static void InitMessage(SplashScreen *splash, const std::string &message)
{
QMetaObject::invokeMethod(splash, "showMessage",
Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(message)),
Q_ARG(int, Qt::AlignBottom|Qt::AlignHCenter),
Q_ARG(QColor, QColor(55,55,55)));
}
void SplashScreen::subscribeToCoreSignals()
{
// Connect signals to client
uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1));
}
void SplashScreen::unsubscribeFromCoreSignals()
{
// Disconnect signals from client
uiInterface.InitMessage.disconnect(boost::bind(InitMessage, this, _1));
} }

11
src/qt/splashscreen.h

@ -15,6 +15,17 @@ class SplashScreen : public QSplashScreen
public: public:
explicit SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f, bool isTestNet); explicit SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f, bool isTestNet);
~SplashScreen();
public slots:
/** Slot to call finish() method as it's not defined as slot */
void slotFinish(QWidget *mainWin);
private:
/** Connect core signals to splash screen */
void subscribeToCoreSignals();
/** Disconnect core signals to splash screen */
void unsubscribeFromCoreSignals();
}; };
#endif // SPLASHSCREEN_H #endif // SPLASHSCREEN_H

Loading…
Cancel
Save