@ -30,6 +30,8 @@
# include <QSettings>
# include <QSettings>
# include <QTimer>
# include <QTimer>
# include <QTranslator>
# include <QTranslator>
# include <QWeakPointer>
# include <QThread>
# if defined(QT_STATICPLUGIN)
# if defined(QT_STATICPLUGIN)
# include <QtPlugin>
# include <QtPlugin>
@ -52,18 +54,18 @@ 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
// Need a global reference for the notifications to find the GUI and splash screen
static BitcoinGUI * guiref ;
static QWeakPointer < BitcoinGUI > guiref ;
static SplashScreen * splashref ;
static QWeakPointer < SplashScreen > splashref ;
static bool ThreadSafeMessageBox ( const std : : string & message , const std : : string & caption , unsigned int style )
static bool ThreadSafeMessageBox ( const std : : string & message , const std : : string & caption , unsigned int style )
{
{
if ( guiref )
if ( ! guiref . isNull ( ) )
{
{
bool modal = ( style & CClientUIInterface : : MODAL ) ;
bool modal = ( style & CClientUIInterface : : MODAL ) ;
bool ret = false ;
bool ret = false ;
// In case of modal message, use blocking connection to wait for user to click a button
// In case of modal message, use blocking connection to wait for user to click a button
QMetaObject : : invokeMethod ( guiref , " message " ,
QMetaObject : : invokeMethod ( guiref . data ( ) , " message " ,
modal ? GUIUtil : : blockingGUIThreadConnection ( ) : Qt : : QueuedConnection ,
modal ? GUIUtil : : blockingGUIThreadConnection ( ) : Qt : : QueuedConnection ,
Q_ARG ( QString , QString : : fromStdString ( caption ) ) ,
Q_ARG ( QString , QString : : fromStdString ( caption ) ) ,
Q_ARG ( QString , QString : : fromStdString ( message ) ) ,
Q_ARG ( QString , QString : : fromStdString ( message ) ) ,
@ -81,10 +83,13 @@ static bool ThreadSafeMessageBox(const std::string& message, const std::string&
static void InitMessage ( const std : : string & message )
static void InitMessage ( const std : : string & message )
{
{
if ( splashref )
if ( ! splashref . isNull ( ) )
{
{
splashref - > showMessage ( QString : : fromStdString ( message ) , Qt : : AlignBottom | Qt : : AlignHCenter , QColor ( 55 , 55 , 55 ) ) ;
QMetaObject : : invokeMethod ( splashref . data ( ) , " showMessage " ,
qApp - > processEvents ( ) ;
Qt : : QueuedConnection ,
Q_ARG ( QString , QString : : fromStdString ( message ) ) ,
Q_ARG ( int , Qt : : AlignBottom | Qt : : AlignHCenter ) ,
Q_ARG ( QColor , QColor ( 55 , 55 , 55 ) ) ) ;
}
}
LogPrintf ( " init message: %s \n " , message . c_str ( ) ) ;
LogPrintf ( " init message: %s \n " , message . c_str ( ) ) ;
}
}
@ -97,15 +102,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 +158,265 @@ 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 ) ;
/// 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 ( ) ;
private :
QThread * coreThread ;
PaymentServer * paymentServer ;
OptionsModel * optionsModel ;
ClientModel * clientModel ;
BitcoinGUI * window ;
WalletModel * walletModel ;
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 ) ,
returnValue ( 0 )
{
setQuitOnLastWindowClosed ( false ) ;
startThread ( ) ;
}
BitcoinApplication : : ~ BitcoinApplication ( )
{
LogPrintf ( " Stopping thread \n " ) ;
emit stopThread ( ) ;
coreThread - > wait ( ) ;
LogPrintf ( " Stopped thread \n " ) ;
}
void BitcoinApplication : : createPaymentServer ( )
{
paymentServer = new PaymentServer ( this ) ;
}
void BitcoinApplication : : createOptionsModel ( )
{
optionsModel = new OptionsModel ( ) ;
}
void BitcoinApplication : : createWindow ( bool isaTestNet )
{
window = new BitcoinGUI ( isaTestNet , 0 ) ;
guiref = window ;
QTimer * pollShutdownTimer = new QTimer ( window ) ;
connect ( pollShutdownTimer , SIGNAL ( timeout ( ) ) , window , SLOT ( detectShutdown ( ) ) ) ;
pollShutdownTimer - > start ( 200 ) ;
}
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 ( ) ;
guiref . clear ( ) ;
delete walletModel ;
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 )
{
optionsModel - > Upgrade ( ) ; // Must be done after AppInit2
PaymentServer : : LoadRootCAs ( ) ;
paymentServer - > setOptionsModel ( optionsModel ) ;
if ( ! splashref . isNull ( ) )
splashref . data ( ) - > finish ( 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 +444,7 @@ int main(int argc, char *argv[])
# endif
# endif
Q_INIT_RESOURCE ( bitcoin ) ;
Q_INIT_RESOURCE ( bitcoin ) ;
Q Application app ( argc , argv ) ;
Bitcoin Application 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 ) ;
@ -254,7 +509,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 +521,29 @@ 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 . ThreadSafeMessageBox . connect ( ThreadSafeMessageBox ) ;
uiInterface . InitMessage . connect ( InitMessage ) ;
uiInterface . InitMessage . connect ( InitMessage ) ;
uiInterface . Translate . connect ( Translate ) ;
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 ) ;
{
SplashScreen * splash = new SplashScreen ( QPixmap ( ) , 0 , isaTestNet ) ;
splash - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
splash - > show ( ) ;
splashref = splash ;
}
try
try
{
{
@ -293,93 +553,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 ;
app . exec ( ) ;
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 ( ) ;
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