diff --git a/src/global.h b/src/global.h index 22ae0c52bf..edc65f51f9 100644 --- a/src/global.h +++ b/src/global.h @@ -319,11 +319,41 @@ typedef unsigned char uint8_t; // definition for custom event #define MS_PACKET_RECEIVED 0 -/* Classes ********************************************************************/ +/* Exception Classes **********************************************************/ + +class CInfoExit +{ +public: + CInfoExit ( QString strMsg ) : strInfoMsg ( strMsg ) {} + + QString GetInfoMessage() const { return strInfoMsg; } + +protected: + QString strInfoMsg; +}; + +class CErrorExit +{ +public: + CErrorExit ( QString strNewErrorMsg, int iNewExitCode = 1 ) : strErrorMsg ( strNewErrorMsg ), iExitCode ( iNewExitCode ) {} + + QString GetErrorMessage() const { return strErrorMsg; } + + int GetExitCode() const { return iExitCode; } + +protected: + QString strErrorMsg; + int iExitCode; +}; + class CGenErr { public: - CGenErr ( QString strNewErrorMsg, QString strNewErrorType = "" ) : strErrorMsg ( strNewErrorMsg ), strErrorType ( strNewErrorType ) {} + CGenErr ( QString strNewErrorMsg, QString strNewErrorType = "", int iNewExitCode = 1 ) : + strErrorMsg ( strNewErrorMsg ), + strErrorType ( strNewErrorType ), + iExitCode ( iNewExitCode ) + {} QString GetErrorText() const { @@ -338,11 +368,16 @@ class CGenErr } } + int GetExitCode() const { return iExitCode; } + protected: QString strErrorMsg; QString strErrorType; + int iExitCode; }; +/* Event Classes **************************************************************/ + class CCustomEvent : public QEvent { public: @@ -359,6 +394,9 @@ class CCustomEvent : public QEvent }; /* Prototypes for global functions ********************************************/ + +extern const QString GetAppName(); + // command line parsing, TODO do not declare functions globally but in a class QString UsageArguments ( char** argv ); diff --git a/src/main.cpp b/src/main.cpp index 86a37e40e3..b16111c759 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,10 +22,23 @@ * \******************************************************************************/ +#ifdef HEADLESS +# if defined( Q_OS_IOS ) +# error HEADLESS mode is not supported on iOS +# endif +# if defined( ANDROID ) +# error HEADLESS mode is not supported on Android +# endif +#endif + #include #include #include #include "global.h" +#include "settings.h" +#include "util.h" +#include + #ifndef HEADLESS # include # include @@ -34,29 +47,50 @@ # include "clientdlg.h" # endif #endif -#include "settings.h" + +#include "rpcserver.h" +#include "serverrpc.h" #ifndef SERVER_ONLY +# include "clientrpc.h" # include "testbench.h" #endif -#include "util.h" + #ifdef ANDROID # include #endif + #if defined( Q_OS_MACX ) # include "mac/activity.h" extern void qt_set_sequence_auto_mnemonic ( bool bEnable ); #endif -#include -#include "rpcserver.h" -#include "serverrpc.h" -#ifndef SERVER_ONLY -# include "clientrpc.h" -#endif + +// Global App Name ************************************************************* + +static QString strAppName = APP_NAME; // Will be appended by " - ClientName" or "Server - ServerName" if those names are defined + +const QString GetAppName() { return strAppName; } + +// Helpers ********************************************************************* + +const QString getServerNameFromInfo ( const QString strServerInfo ) +{ + if ( !strServerInfo.isEmpty() ) + { + QStringList servInfoParams = strServerInfo.split ( ";" ); + if ( servInfoParams.count() > 0 ) + { + return servInfoParams[0]; + } + } + + return QString(); +} // Implementation ************************************************************** int main ( int argc, char** argv ) { + int exit_code = 0; #if defined( Q_OS_MACX ) // Mnemonic keys are default disabled in Qt for MacOS. The following function enables them. @@ -64,6 +98,23 @@ int main ( int argc, char** argv ) qt_set_sequence_auto_mnemonic ( true ); #endif +#ifdef HEADLESS + // note: pApplication will never be used in headless mode, + // but if we define it here we can use the same source code + // with far less ifdef's +# define QApplication QCoreApplication +# define pApplication pCoreApplication +#else + QApplication* pApplication = NULL; +#endif + QCoreApplication* pCoreApplication = NULL; + + CRpcServer* pRpcServer = NULL; + CServerRpc* pServerRpc = NULL; +#ifndef SERVER_ONLY + CClientRpc* pClientRpc = NULL; +#endif + QString strArgument; double rDbleArgument; QList CommandLineOptions; @@ -72,13 +123,7 @@ int main ( int argc, char** argv ) // initialize all flags and string which might be changed by command line // arguments -#if ( defined( SERVER_BUNDLE ) && defined( Q_OS_MACX ) ) || defined( SERVER_ONLY ) - // if we are on MacOS and we are building a server bundle or requested build with serveronly, start Jamulus in server mode - bool bIsClient = false; - qInfo() << "- Starting in server mode by default (due to compile time option)"; -#else - bool bIsClient = true; -#endif + bool bIsClient = true; bool bUseGUI = true; bool bStartMinimized = false; bool bShowComplRegConnList = false; @@ -115,748 +160,765 @@ int main ( int argc, char** argv ) QString strClientName = ""; QString strJsonRpcSecretFileName = ""; -#if !defined( HEADLESS ) && defined( _WIN32 ) - if ( AttachConsole ( ATTACH_PARENT_PROCESS ) ) - { - freopen ( "CONOUT$", "w", stdout ); - freopen ( "CONOUT$", "w", stderr ); - } -#endif - - // When adding new options, follow the same order as --help output - - // QT docu: argv()[0] is the program name, argv()[1] is the first - // argument and argv()[argc()-1] is the last argument. - // Start with first argument, therefore "i = 1" - for ( int i = 1; i < argc; i++ ) + try { - // Help (usage) flag --------------------------------------------------- - if ( ( !strcmp ( argv[i], "--help" ) ) || ( !strcmp ( argv[i], "-h" ) ) || ( !strcmp ( argv[i], "-?" ) ) ) - { - const QString strHelp = UsageArguments ( argv ); - std::cout << qUtf8Printable ( strHelp ); - exit ( 0 ); - } - - // Version number ------------------------------------------------------ - if ( ( !strcmp ( argv[i], "--version" ) ) || ( !strcmp ( argv[i], "-v" ) ) ) +#if !defined( HEADLESS ) && defined( _WIN32 ) + if ( AttachConsole ( ATTACH_PARENT_PROCESS ) ) { - std::cout << qUtf8Printable ( GetVersionAndNameStr ( false ) ); - exit ( 0 ); + (void) freopen ( "CONOUT$", "w", stdout ); + (void) freopen ( "CONOUT$", "w", stderr ); } +#endif - // Common options: - - // Initialization file ------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-i", "--inifile", strArgument ) ) - { - strIniFileName = strArgument; - qInfo() << qUtf8Printable ( QString ( "- initialization file name: %1" ).arg ( strIniFileName ) ); - CommandLineOptions << "--inifile"; - continue; - } + // When adding new options, follow the same order as --help output - // Disable GUI flag ---------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-n", "--nogui" ) ) + // QT docu: argv()[0] is the program name, argv()[1] is the first + // argument and argv()[argc()-1] is the last argument. + // Start with first argument, therefore "i = 1" + for ( int i = 1; i < argc; i++ ) { - bUseGUI = false; - qInfo() << "- no GUI mode chosen"; - CommandLineOptions << "--nogui"; - continue; - } - // Port number --------------------------------------------------------- - if ( GetNumericArgument ( argc, argv, i, "-p", "--port", 0, 65535, rDbleArgument ) ) - { - iPortNumber = static_cast ( rDbleArgument ); - bCustomPortNumberGiven = true; - qInfo() << qUtf8Printable ( QString ( "- selected port number: %1" ).arg ( iPortNumber ) ); - CommandLineOptions << "--port"; - continue; - } + // Help (usage) flag --------------------------------------------------- + if ( ( !strcmp ( argv[i], "--help" ) ) || ( !strcmp ( argv[i], "-h" ) ) || ( !strcmp ( argv[i], "-?" ) ) ) + { + throw CInfoExit ( UsageArguments ( argv ) ); + } - // JSON-RPC port number ------------------------------------------------ - if ( GetNumericArgument ( argc, argv, i, "--jsonrpcport", "--jsonrpcport", 0, 65535, rDbleArgument ) ) - { - iJsonRpcPortNumber = static_cast ( rDbleArgument ); - qInfo() << qUtf8Printable ( QString ( "- JSON-RPC port number: %1" ).arg ( iJsonRpcPortNumber ) ); - CommandLineOptions << "--jsonrpcport"; - continue; - } + // Version number ------------------------------------------------------ + if ( ( !strcmp ( argv[i], "--version" ) ) || ( !strcmp ( argv[i], "-v" ) ) ) + { + throw CInfoExit ( GetVersionAndNameStr ( false ) ); + } - // JSON-RPC secret file name ------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "--jsonrpcsecretfile", "--jsonrpcsecretfile", strArgument ) ) - { - strJsonRpcSecretFileName = strArgument; - qInfo() << qUtf8Printable ( QString ( "- JSON-RPC secret file: %1" ).arg ( strJsonRpcSecretFileName ) ); - CommandLineOptions << "--jsonrpcsecretfile"; - continue; - } + // Common options: - // Quality of Service -------------------------------------------------- - if ( GetNumericArgument ( argc, argv, i, "-Q", "--qos", 0, 255, rDbleArgument ) ) - { - iQosNumber = static_cast ( rDbleArgument ); - qInfo() << qUtf8Printable ( QString ( "- selected QoS value: %1" ).arg ( iQosNumber ) ); - CommandLineOptions << "--qos"; - continue; - } + // Initialization file ------------------------------------------------- + if ( GetStringArgument ( argc, argv, i, "-i", "--inifile", strArgument ) ) + { + strIniFileName = strArgument; + qInfo() << qUtf8Printable ( QString ( "- initialization file name: %1" ).arg ( strIniFileName ) ); + CommandLineOptions << "--inifile"; + continue; + } - // Disable translations ------------------------------------------------ - if ( GetFlagArgument ( argv, i, "-t", "--notranslation" ) ) - { - bUseTranslation = false; - qInfo() << "- translations disabled"; - CommandLineOptions << "--notranslation"; - continue; - } + // Disable GUI flag ---------------------------------------------------- + if ( GetFlagArgument ( argv, i, "-n", "--nogui" ) ) + { + bUseGUI = false; + qInfo() << "- no GUI mode chosen"; + CommandLineOptions << "--nogui"; + continue; + } - // Enable IPv6 --------------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-6", "--enableipv6" ) ) - { - bEnableIPv6 = true; - qInfo() << "- IPv6 enabled"; - CommandLineOptions << "--enableipv6"; - continue; - } + // Port number --------------------------------------------------------- + if ( GetNumericArgument ( argc, argv, i, "-p", "--port", 0, 65535, rDbleArgument ) ) + { + iPortNumber = static_cast ( rDbleArgument ); + bCustomPortNumberGiven = true; + qInfo() << qUtf8Printable ( QString ( "- selected port number: %1" ).arg ( iPortNumber ) ); + CommandLineOptions << "--port"; + continue; + } - // Server only: + // JSON-RPC port number ------------------------------------------------ + if ( GetNumericArgument ( argc, argv, i, "--jsonrpcport", "--jsonrpcport", 0, 65535, rDbleArgument ) ) + { + iJsonRpcPortNumber = static_cast ( rDbleArgument ); + qInfo() << qUtf8Printable ( QString ( "- JSON-RPC port number: %1" ).arg ( iJsonRpcPortNumber ) ); + CommandLineOptions << "--jsonrpcport"; + continue; + } - // Disconnect all clients on quit -------------------------------------- - if ( GetFlagArgument ( argv, i, "-d", "--discononquit" ) ) - { - bDisconnectAllClientsOnQuit = true; - qInfo() << "- disconnect all clients on quit"; - CommandLineOptions << "--discononquit"; - ServerOnlyOptions << "--discononquit"; - continue; - } + // JSON-RPC secret file name ------------------------------------------- + if ( GetStringArgument ( argc, argv, i, "--jsonrpcsecretfile", "--jsonrpcsecretfile", strArgument ) ) + { + strJsonRpcSecretFileName = strArgument; + qInfo() << qUtf8Printable ( QString ( "- JSON-RPC secret file: %1" ).arg ( strJsonRpcSecretFileName ) ); + CommandLineOptions << "--jsonrpcsecretfile"; + continue; + } - // Directory server ---------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-e", "--directoryserver", strArgument ) ) - { - strDirectoryServer = strArgument; - qInfo() << qUtf8Printable ( QString ( "- directory server: %1" ).arg ( strDirectoryServer ) ); - CommandLineOptions << "--directoryserver"; - ServerOnlyOptions << "--directoryserver"; - continue; - } + // Quality of Service -------------------------------------------------- + if ( GetNumericArgument ( argc, argv, i, "-Q", "--qos", 0, 255, rDbleArgument ) ) + { + iQosNumber = static_cast ( rDbleArgument ); + qInfo() << qUtf8Printable ( QString ( "- selected QoS value: %1" ).arg ( iQosNumber ) ); + CommandLineOptions << "--qos"; + continue; + } - // Central server ** D E P R E C A T E D ** ---------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--centralserver", // no short form - "--centralserver", - strArgument ) ) - { - strDirectoryServer = strArgument; - qInfo() << qUtf8Printable ( QString ( "- directory server: %1" ).arg ( strDirectoryServer ) ); - CommandLineOptions << "--directoryserver"; - ServerOnlyOptions << "--directoryserver"; - continue; - } + // Disable translations ------------------------------------------------ + if ( GetFlagArgument ( argv, i, "-t", "--notranslation" ) ) + { + bUseTranslation = false; + qInfo() << "- translations disabled"; + CommandLineOptions << "--notranslation"; + continue; + } - // Directory file ------------------------------------------------------ - if ( GetStringArgument ( argc, - argv, - i, - "--directoryfile", // no short form - "--directoryfile", - strArgument ) ) - { - strServerListFileName = strArgument; - qInfo() << qUtf8Printable ( QString ( "- directory server persistence file: %1" ).arg ( strServerListFileName ) ); - CommandLineOptions << "--directoryfile"; - ServerOnlyOptions << "--directoryfile"; - continue; - } + // Enable IPv6 --------------------------------------------------------- + if ( GetFlagArgument ( argv, i, "-6", "--enableipv6" ) ) + { + bEnableIPv6 = true; + qInfo() << "- IPv6 enabled"; + CommandLineOptions << "--enableipv6"; + continue; + } - // Server list filter -------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-f", "--listfilter", strArgument ) ) - { - strServerListFilter = strArgument; - qInfo() << qUtf8Printable ( QString ( "- server list filter: %1" ).arg ( strServerListFilter ) ); - CommandLineOptions << "--listfilter"; - ServerOnlyOptions << "--listfilter"; - continue; - } + // Server only: - // Use 64 samples frame size mode -------------------------------------- - if ( GetFlagArgument ( argv, i, "-F", "--fastupdate" ) ) - { - bUseDoubleSystemFrameSize = false; // 64 samples frame size - qInfo() << qUtf8Printable ( QString ( "- using %1 samples frame size mode" ).arg ( SYSTEM_FRAME_SIZE_SAMPLES ) ); - CommandLineOptions << "--fastupdate"; - ServerOnlyOptions << "--fastupdate"; - continue; - } + // Disconnect all clients on quit -------------------------------------- + if ( GetFlagArgument ( argv, i, "-d", "--discononquit" ) ) + { + bDisconnectAllClientsOnQuit = true; + qInfo() << "- disconnect all clients on quit"; + CommandLineOptions << "--discononquit"; + ServerOnlyOptions << "--discononquit"; + continue; + } - // Use logging --------------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-l", "--log", strArgument ) ) - { - strLoggingFileName = strArgument; - qInfo() << qUtf8Printable ( QString ( "- logging file name: %1" ).arg ( strLoggingFileName ) ); - CommandLineOptions << "--log"; - ServerOnlyOptions << "--log"; - continue; - } + // Directory server ---------------------------------------------------- + if ( GetStringArgument ( argc, argv, i, "-e", "--directoryserver", strArgument ) ) + { + strDirectoryServer = strArgument; + qInfo() << qUtf8Printable ( QString ( "- directory server: %1" ).arg ( strDirectoryServer ) ); + CommandLineOptions << "--directoryserver"; + ServerOnlyOptions << "--directoryserver"; + continue; + } - // Use licence flag ---------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-L", "--licence" ) ) - { - // LT_CREATIVECOMMONS is now used just to enable the pop up - eLicenceType = LT_CREATIVECOMMONS; - qInfo() << "- licence required"; - CommandLineOptions << "--licence"; - ServerOnlyOptions << "--licence"; - continue; - } + // Central server ** D E P R E C A T E D ** ---------------------------- + if ( GetStringArgument ( argc, + argv, + i, + "--centralserver", // no short form + "--centralserver", + strArgument ) ) + { + strDirectoryServer = strArgument; + qInfo() << qUtf8Printable ( QString ( "- directory server: %1" ).arg ( strDirectoryServer ) ); + CommandLineOptions << "--directoryserver"; + ServerOnlyOptions << "--directoryserver"; + continue; + } - // HTML status file ---------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-m", "--htmlstatus", strArgument ) ) - { - strHTMLStatusFileName = strArgument; - qInfo() << qUtf8Printable ( QString ( "- HTML status file name: %1" ).arg ( strHTMLStatusFileName ) ); - CommandLineOptions << "--htmlstatus"; - ServerOnlyOptions << "--htmlstatus"; - continue; - } + // Directory file ------------------------------------------------------ + if ( GetStringArgument ( argc, + argv, + i, + "--directoryfile", // no short form + "--directoryfile", + strArgument ) ) + { + strServerListFileName = strArgument; + qInfo() << qUtf8Printable ( QString ( "- directory server persistence file: %1" ).arg ( strServerListFileName ) ); + CommandLineOptions << "--directoryfile"; + ServerOnlyOptions << "--directoryfile"; + continue; + } - // Server info --------------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-o", "--serverinfo", strArgument ) ) - { - strServerInfo = strArgument; - qInfo() << qUtf8Printable ( QString ( "- server info: %1" ).arg ( strServerInfo ) ); - CommandLineOptions << "--serverinfo"; - ServerOnlyOptions << "--serverinfo"; - continue; - } + // Server list filter -------------------------------------------------- + if ( GetStringArgument ( argc, argv, i, "-f", "--listfilter", strArgument ) ) + { + strServerListFilter = strArgument; + qInfo() << qUtf8Printable ( QString ( "- server list filter: %1" ).arg ( strServerListFilter ) ); + CommandLineOptions << "--listfilter"; + ServerOnlyOptions << "--listfilter"; + continue; + } - // Server Public IP ---------------------------------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--serverpublicip", // no short form - "--serverpublicip", - strArgument ) ) - { - strServerPublicIP = strArgument; - qInfo() << qUtf8Printable ( QString ( "- server public IP: %1" ).arg ( strServerPublicIP ) ); - CommandLineOptions << "--serverpublicip"; - ServerOnlyOptions << "--serverpublicip"; - continue; - } + // Use 64 samples frame size mode -------------------------------------- + if ( GetFlagArgument ( argv, i, "-F", "--fastupdate" ) ) + { + bUseDoubleSystemFrameSize = false; // 64 samples frame size + qInfo() << qUtf8Printable ( QString ( "- using %1 samples frame size mode" ).arg ( SYSTEM_FRAME_SIZE_SAMPLES ) ); + CommandLineOptions << "--fastupdate"; + ServerOnlyOptions << "--fastupdate"; + continue; + } - // Enable delay panning on startup ------------------------------------- - if ( GetFlagArgument ( argv, i, "-P", "--delaypan" ) ) - { - bDelayPan = true; - qInfo() << "- starting with delay panning"; - CommandLineOptions << "--delaypan"; - ServerOnlyOptions << "--delaypan"; - continue; - } + // Use logging --------------------------------------------------------- + if ( GetStringArgument ( argc, argv, i, "-l", "--log", strArgument ) ) + { + strLoggingFileName = strArgument; + qInfo() << qUtf8Printable ( QString ( "- logging file name: %1" ).arg ( strLoggingFileName ) ); + CommandLineOptions << "--log"; + ServerOnlyOptions << "--log"; + continue; + } - // Recording directory ------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-R", "--recording", strArgument ) ) - { - strRecordingDirName = strArgument; - qInfo() << qUtf8Printable ( QString ( "- recording directory name: %1" ).arg ( strRecordingDirName ) ); - CommandLineOptions << "--recording"; - ServerOnlyOptions << "--recording"; - continue; - } + // Use licence flag ---------------------------------------------------- + if ( GetFlagArgument ( argv, i, "-L", "--licence" ) ) + { + // LT_CREATIVECOMMONS is now used just to enable the pop up + eLicenceType = LT_CREATIVECOMMONS; + qInfo() << "- licence required"; + CommandLineOptions << "--licence"; + ServerOnlyOptions << "--licence"; + continue; + } - // Disable recording on startup ---------------------------------------- - if ( GetFlagArgument ( argv, - i, - "--norecord", // no short form - "--norecord" ) ) - { - bDisableRecording = true; - qInfo() << "- recording will not take place until enabled"; - CommandLineOptions << "--norecord"; - ServerOnlyOptions << "--norecord"; - continue; - } + // HTML status file ---------------------------------------------------- + if ( GetStringArgument ( argc, argv, i, "-m", "--htmlstatus", strArgument ) ) + { + strHTMLStatusFileName = strArgument; + qInfo() << qUtf8Printable ( QString ( "- HTML status file name: %1" ).arg ( strHTMLStatusFileName ) ); + CommandLineOptions << "--htmlstatus"; + ServerOnlyOptions << "--htmlstatus"; + continue; + } - // Server mode flag ---------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-s", "--server" ) ) - { - bIsClient = false; - qInfo() << "- server mode chosen"; - CommandLineOptions << "--server"; - ServerOnlyOptions << "--server"; - continue; - } + // Server info --------------------------------------------------------- + if ( GetStringArgument ( argc, argv, i, "-o", "--serverinfo", strArgument ) ) + { + strServerInfo = strArgument; + qInfo() << qUtf8Printable ( QString ( "- server info: %1" ).arg ( strServerInfo ) ); + CommandLineOptions << "--serverinfo"; + ServerOnlyOptions << "--serverinfo"; + continue; + } - // Server Bind IP -------------------------------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--serverbindip", // no short form - "--serverbindip", - strArgument ) ) - { - strServerBindIP = strArgument; - qInfo() << qUtf8Printable ( QString ( "- server bind IP: %1" ).arg ( strServerBindIP ) ); - CommandLineOptions << "--serverbindip"; - ServerOnlyOptions << "--serverbindip"; - continue; - } + // Server Public IP ---------------------------------------------------- + if ( GetStringArgument ( argc, + argv, + i, + "--serverpublicip", // no short form + "--serverpublicip", + strArgument ) ) + { + strServerPublicIP = strArgument; + qInfo() << qUtf8Printable ( QString ( "- server public IP: %1" ).arg ( strServerPublicIP ) ); + CommandLineOptions << "--serverpublicip"; + ServerOnlyOptions << "--serverpublicip"; + continue; + } - // Use multithreading -------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-T", "--multithreading" ) ) - { - bUseMultithreading = true; - qInfo() << "- using multithreading"; - CommandLineOptions << "--multithreading"; - ServerOnlyOptions << "--multithreading"; - continue; - } + // Enable delay panning on startup ------------------------------------- + if ( GetFlagArgument ( argv, i, "-P", "--delaypan" ) ) + { + bDelayPan = true; + qInfo() << "- starting with delay panning"; + CommandLineOptions << "--delaypan"; + ServerOnlyOptions << "--delaypan"; + continue; + } - // Maximum number of channels ------------------------------------------ - if ( GetNumericArgument ( argc, argv, i, "-u", "--numchannels", 1, MAX_NUM_CHANNELS, rDbleArgument ) ) - { - iNumServerChannels = static_cast ( rDbleArgument ); + // Recording directory ------------------------------------------------- + if ( GetStringArgument ( argc, argv, i, "-R", "--recording", strArgument ) ) + { + strRecordingDirName = strArgument; + qInfo() << qUtf8Printable ( QString ( "- recording directory name: %1" ).arg ( strRecordingDirName ) ); + CommandLineOptions << "--recording"; + ServerOnlyOptions << "--recording"; + continue; + } - qInfo() << qUtf8Printable ( QString ( "- maximum number of channels: %1" ).arg ( iNumServerChannels ) ); + // Disable recording on startup ---------------------------------------- + if ( GetFlagArgument ( argv, + i, + "--norecord", // no short form + "--norecord" ) ) + { + bDisableRecording = true; + qInfo() << "- recording will not take place until enabled"; + CommandLineOptions << "--norecord"; + ServerOnlyOptions << "--norecord"; + continue; + } - CommandLineOptions << "--numchannels"; - ServerOnlyOptions << "--numchannels"; - continue; - } + // Server mode flag ---------------------------------------------------- + if ( GetFlagArgument ( argv, i, "-s", "--server" ) ) + { + bIsClient = false; + qInfo() << "- server mode chosen"; + CommandLineOptions << "--server"; + ServerOnlyOptions << "--server"; + continue; + } - // Server welcome message ---------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-w", "--welcomemessage", strArgument ) ) - { - strWelcomeMessage = strArgument; - qInfo() << qUtf8Printable ( QString ( "- welcome message: %1" ).arg ( strWelcomeMessage ) ); - CommandLineOptions << "--welcomemessage"; - ServerOnlyOptions << "--welcomemessage"; - continue; - } + // Server Bind IP -------------------------------------------------- + if ( GetStringArgument ( argc, + argv, + i, + "--serverbindip", // no short form + "--serverbindip", + strArgument ) ) + { + strServerBindIP = strArgument; + qInfo() << qUtf8Printable ( QString ( "- server bind IP: %1" ).arg ( strServerBindIP ) ); + CommandLineOptions << "--serverbindip"; + ServerOnlyOptions << "--serverbindip"; + continue; + } - // Start minimized ----------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-z", "--startminimized" ) ) - { - bStartMinimized = true; - qInfo() << "- start minimized enabled"; - CommandLineOptions << "--startminimized"; - ServerOnlyOptions << "--startminimized"; - continue; - } + // Use multithreading -------------------------------------------------- + if ( GetFlagArgument ( argv, i, "-T", "--multithreading" ) ) + { + bUseMultithreading = true; + qInfo() << "- using multithreading"; + CommandLineOptions << "--multithreading"; + ServerOnlyOptions << "--multithreading"; + continue; + } - // Client only: + // Maximum number of channels ------------------------------------------ + if ( GetNumericArgument ( argc, argv, i, "-u", "--numchannels", 1, MAX_NUM_CHANNELS, rDbleArgument ) ) + { + iNumServerChannels = static_cast ( rDbleArgument ); - // Connect on startup -------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-c", "--connect", strArgument ) ) - { - strConnOnStartupAddress = NetworkUtil::FixAddress ( strArgument ); - qInfo() << qUtf8Printable ( QString ( "- connect on startup to address: %1" ).arg ( strConnOnStartupAddress ) ); - CommandLineOptions << "--connect"; - ClientOnlyOptions << "--connect"; - continue; - } + qInfo() << qUtf8Printable ( QString ( "- maximum number of channels: %1" ).arg ( iNumServerChannels ) ); - // Disabling auto Jack connections ------------------------------------- - if ( GetFlagArgument ( argv, i, "-j", "--nojackconnect" ) ) - { - bNoAutoJackConnect = true; - qInfo() << "- disable auto Jack connections"; - CommandLineOptions << "--nojackconnect"; - ClientOnlyOptions << "--nojackconnect"; - continue; - } + CommandLineOptions << "--numchannels"; + ServerOnlyOptions << "--numchannels"; + continue; + } - // Mute stream on startup ---------------------------------------------- - if ( GetFlagArgument ( argv, i, "-M", "--mutestream" ) ) - { - bMuteStream = true; - qInfo() << "- mute stream activated"; - CommandLineOptions << "--mutestream"; - ClientOnlyOptions << "--mutestream"; - continue; - } + // Server welcome message ---------------------------------------------- + if ( GetStringArgument ( argc, argv, i, "-w", "--welcomemessage", strArgument ) ) + { + strWelcomeMessage = strArgument; + qInfo() << qUtf8Printable ( QString ( "- welcome message: %1" ).arg ( strWelcomeMessage ) ); + CommandLineOptions << "--welcomemessage"; + ServerOnlyOptions << "--welcomemessage"; + continue; + } - // For headless client mute my own signal in personal mix -------------- - if ( GetFlagArgument ( argv, - i, - "--mutemyown", // no short form - "--mutemyown" ) ) - { - bMuteMeInPersonalMix = true; - qInfo() << "- mute me in my personal mix"; - CommandLineOptions << "--mutemyown"; - ClientOnlyOptions << "--mutemyown"; - continue; - } + // Start minimized ----------------------------------------------------- + if ( GetFlagArgument ( argv, i, "-z", "--startminimized" ) ) + { + bStartMinimized = true; + qInfo() << "- start minimized enabled"; + CommandLineOptions << "--startminimized"; + ServerOnlyOptions << "--startminimized"; + continue; + } - // Client Name --------------------------------------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--clientname", // no short form - "--clientname", - strArgument ) ) - { - strClientName = strArgument; - qInfo() << qUtf8Printable ( QString ( "- client name: %1" ).arg ( strClientName ) ); - CommandLineOptions << "--clientname"; - ClientOnlyOptions << "--clientname"; - continue; - } + // Client only: - // Controller MIDI channel --------------------------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--ctrlmidich", // no short form - "--ctrlmidich", - strArgument ) ) - { - strMIDISetup = strArgument; - qInfo() << qUtf8Printable ( QString ( "- MIDI controller settings: %1" ).arg ( strMIDISetup ) ); - CommandLineOptions << "--ctrlmidich"; - ClientOnlyOptions << "--ctrlmidich"; - continue; - } + // Connect on startup -------------------------------------------------- + if ( GetStringArgument ( argc, argv, i, "-c", "--connect", strArgument ) ) + { + strConnOnStartupAddress = NetworkUtil::FixAddress ( strArgument ); + qInfo() << qUtf8Printable ( QString ( "- connect on startup to address: %1" ).arg ( strConnOnStartupAddress ) ); + CommandLineOptions << "--connect"; + ClientOnlyOptions << "--connect"; + continue; + } - // Undocumented: + // Disabling auto Jack connections ------------------------------------- + if ( GetFlagArgument ( argv, i, "-j", "--nojackconnect" ) ) + { + bNoAutoJackConnect = true; + qInfo() << "- disable auto Jack connections"; + CommandLineOptions << "--nojackconnect"; + ClientOnlyOptions << "--nojackconnect"; + continue; + } - // Show all registered servers in the server list ---------------------- - // Undocumented debugging command line argument: Show all registered - // servers in the server list regardless if a ping to the server is - // possible or not. - if ( GetFlagArgument ( argv, - i, - "--showallservers", // no short form - "--showallservers" ) ) - { - bShowComplRegConnList = true; - qInfo() << "- show all registered servers in server list"; - CommandLineOptions << "--showallservers"; - ClientOnlyOptions << "--showallservers"; - continue; - } + // Mute stream on startup ---------------------------------------------- + if ( GetFlagArgument ( argv, i, "-M", "--mutestream" ) ) + { + bMuteStream = true; + qInfo() << "- mute stream activated"; + CommandLineOptions << "--mutestream"; + ClientOnlyOptions << "--mutestream"; + continue; + } - // Show analyzer console ----------------------------------------------- - // Undocumented debugging command line argument: Show the analyzer - // console to debug network buffer properties. - if ( GetFlagArgument ( argv, - i, - "--showanalyzerconsole", // no short form - "--showanalyzerconsole" ) ) - { - bShowAnalyzerConsole = true; - qInfo() << "- show analyzer console"; - CommandLineOptions << "--showanalyzerconsole"; - ClientOnlyOptions << "--showanalyzerconsole"; - continue; - } + // For headless client mute my own signal in personal mix -------------- + if ( GetFlagArgument ( argv, + i, + "--mutemyown", // no short form + "--mutemyown" ) ) + { + bMuteMeInPersonalMix = true; + qInfo() << "- mute me in my personal mix"; + CommandLineOptions << "--mutemyown"; + ClientOnlyOptions << "--mutemyown"; + continue; + } - // Unknown option ------------------------------------------------------ - qCritical() << qUtf8Printable ( QString ( "%1: Unknown option '%2' -- use '--help' for help" ).arg ( argv[0] ).arg ( argv[i] ) ); + // Client Name --------------------------------------------------------- + if ( GetStringArgument ( argc, + argv, + i, + "--clientname", // no short form + "--clientname", + strArgument ) ) + { + strClientName = strArgument; + qInfo() << qUtf8Printable ( QString ( "- client name: %1" ).arg ( strClientName ) ); + CommandLineOptions << "--clientname"; + ClientOnlyOptions << "--clientname"; + continue; + } -// clicking on the Mac application bundle, the actual application -// is called with weird command line args -> do not exit on these -#if !( defined( Q_OS_MACX ) ) - exit ( 1 ); -#endif - } + // Controller MIDI channel --------------------------------------------- + if ( GetStringArgument ( argc, + argv, + i, + "--ctrlmidich", // no short form + "--ctrlmidich", + strArgument ) ) + { + strMIDISetup = strArgument; + qInfo() << qUtf8Printable ( QString ( "- MIDI controller settings: %1" ).arg ( strMIDISetup ) ); + CommandLineOptions << "--ctrlmidich"; + ClientOnlyOptions << "--ctrlmidich"; + continue; + } - // Dependencies ------------------------------------------------------------ -#ifdef HEADLESS - if ( bUseGUI ) - { - bUseGUI = false; - qWarning() << "No GUI support compiled. Running in headless mode."; - } - Q_UNUSED ( bStartMinimized ) // avoid compiler warnings - Q_UNUSED ( bShowComplRegConnList ) // avoid compiler warnings - Q_UNUSED ( bShowAnalyzerConsole ) // avoid compiler warnings - Q_UNUSED ( bMuteStream ) // avoid compiler warnings -#endif + // Undocumented: -#ifdef SERVER_ONLY - if ( bIsClient ) - { - qCritical() << "Only --server mode is supported in this build."; - exit ( 1 ); - } -#endif + // Show all registered servers in the server list ---------------------- + // Undocumented debugging command line argument: Show all registered + // servers in the server list regardless if a ping to the server is + // possible or not. + if ( GetFlagArgument ( argv, + i, + "--showallservers", // no short form + "--showallservers" ) ) + { + bShowComplRegConnList = true; + qInfo() << "- show all registered servers in server list"; + CommandLineOptions << "--showallservers"; + ClientOnlyOptions << "--showallservers"; + continue; + } - // TODO create settings in default state, if loading from file do that next, then come back here to - // override from command line options, then create client or server, letting them do the validation + // Show analyzer console ----------------------------------------------- + // Undocumented debugging command line argument: Show the analyzer + // console to debug network buffer properties. + if ( GetFlagArgument ( argv, + i, + "--showanalyzerconsole", // no short form + "--showanalyzerconsole" ) ) + { + bShowAnalyzerConsole = true; + qInfo() << "- show analyzer console"; + CommandLineOptions << "--showanalyzerconsole"; + ClientOnlyOptions << "--showanalyzerconsole"; + continue; + } - if ( bIsClient ) - { - if ( ServerOnlyOptions.size() != 0 ) - { - qCritical() << qUtf8Printable ( QString ( "%1: Server only option(s) '%2' used. Did you omit '--server'?" ) - .arg ( argv[0] ) - .arg ( ServerOnlyOptions.join ( ", " ) ) ); - exit ( 1 ); + // Unknown option ------------------------------------------------------ + throw CErrorExit ( qUtf8Printable ( QString ( "%1: Unknown option '%2' -- use '--help' for help" ).arg ( argv[0] ).arg ( argv[i] ) ) ); } - // mute my own signal in personal mix is only supported for headless mode - if ( bUseGUI && bMuteMeInPersonalMix ) - { - bMuteMeInPersonalMix = false; - qWarning() << "Mute my own signal in my personal mix is only supported in headless mode."; - } + // Dependencies ------------------------------------------------------------ - // adjust default port number for client: use different default port than the server since - // if the client is started before the server, the server would get a socket bind error - if ( !bCustomPortNumberGiven ) +#if ( defined( SERVER_BUNDLE ) && defined( Q_OS_MACX ) ) || defined( SERVER_ONLY ) + // if we are on MacOS and we are building a server bundle or requested build with serveronly, start Jamulus in server mode + if ( bIsClient ) { - iPortNumber += 10; // increment by 10 - qInfo() << qUtf8Printable ( QString ( "- allocated port number: %1" ).arg ( iPortNumber ) ); + bIsClient = false; + qInfo() << "- Starting in server mode by default (due to compile time option)"; } - } - else - { - if ( ClientOnlyOptions.size() != 0 ) +#endif + +#ifdef HEADLESS + Q_UNUSED ( bStartMinimized ) // avoid compiler warnings + Q_UNUSED ( bShowComplRegConnList ) // avoid compiler warnings + Q_UNUSED ( bShowAnalyzerConsole ) // avoid compiler warnings + Q_UNUSED ( bMuteStream ) // avoid compiler warnings + + if ( bUseGUI ) { - qCritical() << qUtf8Printable ( - QString ( "%1: Client only option(s) '%2' used. See '--help' for help" ).arg ( argv[0] ).arg ( ClientOnlyOptions.join ( ", " ) ) ); - exit ( 1 ); + bUseGUI = false; + qWarning() << "No GUI support compiled. Running in headless mode."; } +#endif - if ( bUseGUI ) +#if defined( Q_OS_IOS ) + bIsClient = true; // Client only - TODO: maybe a switch in interface to change to server? + bUseGUI = true; + // bUseMultithreading = true; +#endif + + // TODO create settings in default state, if loading from file do that next, then come back here to + // override from command line options, then create client or server, letting them do the validation + // + // See https://github.com/pgScorpio/jamulus/tree/first-big-thing-for-settings for a possible solution on that + + if ( bIsClient ) { - // by definition, when running with the GUI we always default to registering somewhere but - // until the settings are loaded we do not know where, so we cannot be prescriptive here + if ( !strClientName.isEmpty() ) + { + strAppName += " - " + strClientName; + } + + if ( ServerOnlyOptions.size() != 0 ) + { + throw CErrorExit ( qUtf8Printable ( QString ( "%1: Server only option(s) '%2' used. Did you omit '--server'?" ) + .arg ( argv[0] ) + .arg ( ServerOnlyOptions.join ( ", " ) ) ) ); + } - if ( !strServerListFileName.isEmpty() ) + // mute my own signal in personal mix is only supported for headless mode + if ( bUseGUI && bMuteMeInPersonalMix ) { - qInfo() << "Note:" - << "Server list persistence file will only take effect when running as a directory server."; + bMuteMeInPersonalMix = false; + qWarning() << "Mute my own signal in my personal mix is only supported in headless mode."; } - if ( !strServerListFilter.isEmpty() ) + // adjust default port number for client: use different default port than the server since + // if the client is started before the server, the server would get a socket bind error + if ( !bCustomPortNumberGiven ) { - qInfo() << "Note:" - << "Server list filter will only take effect when running as a directory server."; + iPortNumber += 10; // increment by 10 + qInfo() << qUtf8Printable ( QString ( "- allocated port number: %1" ).arg ( iPortNumber ) ); } } else { - // the inifile is not supported for the headless server mode - if ( !strIniFileName.isEmpty() ) + strAppName += "Server"; + if ( !strServerInfo.isEmpty() ) + { + QString strServerName = getServerNameFromInfo ( strServerInfo ); + if ( !strServerName.isEmpty() ) + { + strAppName += " - " + strServerName; + } + } + + if ( ClientOnlyOptions.size() != 0 ) { - qWarning() << "No initialization file support in headless server mode."; - strIniFileName = ""; + throw CErrorExit ( qUtf8Printable ( QString ( "%1: Client only option(s) '%2' used. See '--help' for help" ) + .arg ( argv[0] ) + .arg ( ClientOnlyOptions.join ( ", " ) ) ) ); } - // therefore we know everything based on command line options - if ( strDirectoryServer.compare ( "localhost", Qt::CaseInsensitive ) == 0 || strDirectoryServer.compare ( "127.0.0.1" ) == 0 ) + if ( bUseGUI ) { + // by definition, when running with the GUI we always default to registering somewhere but + // until the settings are loaded we do not know where, so we cannot be prescriptive here + if ( !strServerListFileName.isEmpty() ) { - QFileInfo serverListFileInfo ( strServerListFileName ); - if ( !serverListFileInfo.exists() ) - { - QFile strServerListFile ( strServerListFileName ); - if ( !strServerListFile.open ( QFile::OpenModeFlag::ReadWrite ) ) - { - qWarning() << qUtf8Printable ( - QString ( "Cannot create %1 for reading and writing. Please check permissions." ).arg ( strServerListFileName ) ); - strServerListFileName = ""; - } - } - else if ( !serverListFileInfo.isFile() ) - { - qWarning() << qUtf8Printable ( - QString ( "Server list file %1 must be a plain file. Please check the name." ).arg ( strServerListFileName ) ); - strServerListFileName = ""; - } - else if ( !serverListFileInfo.isReadable() || !serverListFileInfo.isWritable() ) - { - qWarning() << qUtf8Printable ( - QString ( "Server list file %1 must be readable and writeable. Please check the permissions." ) - .arg ( strServerListFileName ) ); - strServerListFileName = ""; - } + qInfo() << "Note:" + << "Server list persistence file will only take effect when running as a directory server."; } if ( !strServerListFilter.isEmpty() ) { - QStringList slWhitelistAddresses = strServerListFilter.split ( ";" ); - for ( int iIdx = 0; iIdx < slWhitelistAddresses.size(); iIdx++ ) + qInfo() << "Note:" + << "Server list filter will only take effect when running as a directory server."; + } + } + else + { + // the inifile is not supported for the headless server mode + if ( !strIniFileName.isEmpty() ) + { + qWarning() << "No initialization file support in headless server mode."; + strIniFileName = ""; + } + // therefore we know everything based on command line options + + if ( strDirectoryServer.compare ( "localhost", Qt::CaseInsensitive ) == 0 || strDirectoryServer.compare ( "127.0.0.1" ) == 0 ) + { + if ( !strServerListFileName.isEmpty() ) { - // check for special case: [version] - if ( ( slWhitelistAddresses.at ( iIdx ).length() > 2 ) && ( slWhitelistAddresses.at ( iIdx ).left ( 1 ) == "[" ) && - ( slWhitelistAddresses.at ( iIdx ).right ( 1 ) == "]" ) ) + QFileInfo serverListFileInfo ( strServerListFileName ); + if ( !serverListFileInfo.exists() ) { - // Good case - it seems QVersionNumber isn't fussy + QFile strServerListFile ( strServerListFileName ); + if ( !strServerListFile.open ( QFile::OpenModeFlag::ReadWrite ) ) + { + qWarning() << qUtf8Printable ( QString ( "Cannot create %1 for reading and writing. Please check permissions." ) + .arg ( strServerListFileName ) ); + strServerListFileName = ""; + } + } + else if ( !serverListFileInfo.isFile() ) + { + qWarning() << qUtf8Printable ( + QString ( "Server list file %1 must be a plain file. Please check the name." ).arg ( strServerListFileName ) ); + strServerListFileName = ""; } - else if ( slWhitelistAddresses.at ( iIdx ).isEmpty() ) + else if ( !serverListFileInfo.isReadable() || !serverListFileInfo.isWritable() ) { - qWarning() << "There is empty entry in the server list filter that will be ignored"; + qWarning() << qUtf8Printable ( + QString ( "Server list file %1 must be readable and writeable. Please check the permissions." ) + .arg ( strServerListFileName ) ); + strServerListFileName = ""; } - else + } + + if ( !strServerListFilter.isEmpty() ) + { + QStringList slWhitelistAddresses = strServerListFilter.split ( ";" ); + for ( int iIdx = 0; iIdx < slWhitelistAddresses.size(); iIdx++ ) { - QHostAddress InetAddr; - if ( !InetAddr.setAddress ( slWhitelistAddresses.at ( iIdx ) ) ) + // check for special case: [version] + if ( ( slWhitelistAddresses.at ( iIdx ).length() > 2 ) && ( slWhitelistAddresses.at ( iIdx ).left ( 1 ) == "[" ) && + ( slWhitelistAddresses.at ( iIdx ).right ( 1 ) == "]" ) ) + { + // Good case - it seems QVersionNumber isn't fussy + } + else if ( slWhitelistAddresses.at ( iIdx ).isEmpty() ) + { + qWarning() << "There is empty entry in the server list filter that will be ignored"; + } + else { - qWarning() << qUtf8Printable ( - QString ( "%1 is not a valid server list filter entry. Only plain IP addresses are supported" ) - .arg ( slWhitelistAddresses.at ( iIdx ) ) ); + QHostAddress InetAddr; + if ( !InetAddr.setAddress ( slWhitelistAddresses.at ( iIdx ) ) ) + { + qWarning() << qUtf8Printable ( + QString ( "%1 is not a valid server list filter entry. Only plain IP addresses are supported" ) + .arg ( slWhitelistAddresses.at ( iIdx ) ) ); + } } } } } - } - else - { - if ( !strServerListFileName.isEmpty() ) + else { - qWarning() << "Server list persistence file will only take effect when running as a directory server."; - strServerListFileName = ""; - } + if ( !strServerListFileName.isEmpty() ) + { + qWarning() << "Server list persistence file will only take effect when running as a directory server."; + strServerListFileName = ""; + } - if ( !strServerListFilter.isEmpty() ) - { - qWarning() << "Server list filter will only take effect when running as a directory server."; - strServerListFileName = ""; + if ( !strServerListFilter.isEmpty() ) + { + qWarning() << "Server list filter will only take effect when running as a directory server."; + strServerListFileName = ""; + } } - } - if ( strDirectoryServer.isEmpty() ) - { - if ( !strServerPublicIP.isEmpty() ) + if ( strDirectoryServer.isEmpty() ) { - qWarning() << "Server Public IP will only take effect when registering a server with a directory server."; - strServerPublicIP = ""; + if ( !strServerPublicIP.isEmpty() ) + { + qWarning() << "Server Public IP will only take effect when registering a server with a directory server."; + strServerPublicIP = ""; + } } - } - else - { - if ( !strServerPublicIP.isEmpty() ) + else { - QHostAddress InetAddr; - if ( !InetAddr.setAddress ( strServerPublicIP ) ) + if ( !strServerPublicIP.isEmpty() ) { - qWarning() << "Server Public IP is invalid. Only plain IP addresses are supported."; - strServerPublicIP = ""; + QHostAddress InetAddr; + if ( !InetAddr.setAddress ( strServerPublicIP ) ) + { + qWarning() << "Server Public IP is invalid. Only plain IP addresses are supported."; + strServerPublicIP = ""; + } } } } - } - if ( !strServerBindIP.isEmpty() ) - { - QHostAddress InetAddr; - if ( !InetAddr.setAddress ( strServerBindIP ) ) + if ( !strServerBindIP.isEmpty() ) { - qWarning() << "Server Bind IP is invalid. Only plain IP addresses are supported."; - strServerBindIP = ""; + QHostAddress InetAddr; + if ( !InetAddr.setAddress ( strServerBindIP ) ) + { + qWarning() << "Server Bind IP is invalid. Only plain IP addresses are supported."; + strServerBindIP = ""; + } } } - } - // Application/GUI setup --------------------------------------------------- - // Application object -#ifdef HEADLESS - QCoreApplication* pApp = new QCoreApplication ( argc, argv ); -#else -# if defined( Q_OS_IOS ) - bUseGUI = true; - bIsClient = true; // Client only - TODO: maybe a switch in interface to change to server? + // Application/GUI setup --------------------------------------------------- + // Application object - // bUseMultithreading = true; - QApplication* pApp = new QApplication ( argc, argv ); -# else - QCoreApplication* pApp = bUseGUI ? new QApplication ( argc, argv ) : new QCoreApplication ( argc, argv ); -# endif -#endif + if ( bUseGUI ) + { + pApplication = new QApplication ( argc, argv ); + } + else + { + pCoreApplication = new QCoreApplication ( argc, argv ); + } #ifdef ANDROID - // special Android coded needed for record audio permission handling - auto result = QtAndroid::checkPermission ( QString ( "android.permission.RECORD_AUDIO" ) ); + // special Android coded needed for record audio permission handling + auto result = QtAndroid::checkPermission ( QString ( "android.permission.RECORD_AUDIO" ) ); - if ( result == QtAndroid::PermissionResult::Denied ) - { - QtAndroid::PermissionResultMap resultHash = QtAndroid::requestPermissionsSync ( QStringList ( { "android.permission.RECORD_AUDIO" } ) ); - - if ( resultHash["android.permission.RECORD_AUDIO"] == QtAndroid::PermissionResult::Denied ) + if ( result == QtAndroid::PermissionResult::Denied ) { - return 0; + QtAndroid::PermissionResultMap resultHash = QtAndroid::requestPermissionsSync ( QStringList ( { "android.permission.RECORD_AUDIO" } ) ); + + if ( resultHash["android.permission.RECORD_AUDIO"] == QtAndroid::PermissionResult::Denied ) + { + return 0; + } } - } #endif #ifdef _WIN32 - // set application priority class -> high priority - SetPriorityClass ( GetCurrentProcess(), HIGH_PRIORITY_CLASS ); - - // For accessible support we need to add a plugin to qt. The plugin has to - // be located in the install directory of the software by the installer. - // Here, we set the path to our application path. - QDir ApplDir ( QApplication::applicationDirPath() ); - pApp->addLibraryPath ( QString ( ApplDir.absolutePath() ) ); + // set application priority class -> high priority + SetPriorityClass ( GetCurrentProcess(), HIGH_PRIORITY_CLASS ); + + // For accessible support we need to add a plugin to qt. The plugin has to + // be located in the install directory of the software by the installer. + // Here, we set the path to our application path. + if ( bUseGUI ) + { + QDir ApplDir ( QApplication::applicationDirPath() ); + pApplication->addLibraryPath ( QString ( ApplDir.absolutePath() ) ); + } + else + { + QDir ApplDir ( QCoreApplication::applicationDirPath() ); + pCoreApplication->addLibraryPath ( QString ( ApplDir.absolutePath() ) ); + } #endif #if defined( Q_OS_MACX ) - // On OSX we need to declare an activity to ensure the process doesn't get - // throttled by OS level Nap, Sleep, and Thread Priority systems. - CActivity activity; + // On OSX we need to declare an activity to ensure the process doesn't get + // throttled by OS level Nap, Sleep, and Thread Priority systems. + CActivity activity; - activity.BeginActivity(); + activity.BeginActivity(); #endif - // init resources - Q_INIT_RESOURCE ( resources ); + // init resources + Q_INIT_RESOURCE ( resources ); #ifndef SERVER_ONLY - // clang-format off + // clang-format off // TEST -> activate the following line to activate the test bench, //CTestbench Testbench ( "127.0.0.1", DEFAULT_PORT_NUMBER ); // clang-format on #endif - CRpcServer* pRpcServer = nullptr; - - if ( iJsonRpcPortNumber != INVALID_PORT ) - { - if ( strJsonRpcSecretFileName.isEmpty() ) + if ( iJsonRpcPortNumber != INVALID_PORT ) { - qCritical() << qUtf8Printable ( QString ( "- JSON-RPC: --jsonrpcsecretfile is required. Exiting." ) ); - exit ( 1 ); - } + if ( strJsonRpcSecretFileName.isEmpty() ) + { + throw CErrorExit ( qUtf8Printable ( QString ( "- JSON-RPC: --jsonrpcsecretfile is required. Exiting." ) ) ); + } - QFile qfJsonRpcSecretFile ( strJsonRpcSecretFileName ); - if ( !qfJsonRpcSecretFile.open ( QFile::OpenModeFlag::ReadOnly ) ) - { - qCritical() << qUtf8Printable ( QString ( "- JSON-RPC: Unable to open secret file %1. Exiting." ).arg ( strJsonRpcSecretFileName ) ); - exit ( 1 ); - } - QTextStream qtsJsonRpcSecretStream ( &qfJsonRpcSecretFile ); - QString strJsonRpcSecret = qtsJsonRpcSecretStream.readLine(); - if ( strJsonRpcSecret.length() < JSON_RPC_MINIMUM_SECRET_LENGTH ) - { - qCritical() << qUtf8Printable ( QString ( "JSON-RPC: Refusing to run with secret of length %1 (required: %2). Exiting." ) - .arg ( strJsonRpcSecret.length() ) - .arg ( JSON_RPC_MINIMUM_SECRET_LENGTH ) ); - exit ( 1 ); - } + QFile qfJsonRpcSecretFile ( strJsonRpcSecretFileName ); + if ( !qfJsonRpcSecretFile.open ( QFile::OpenModeFlag::ReadOnly ) ) + { + throw CErrorExit ( + qUtf8Printable ( QString ( "- JSON-RPC: Unable to open secret file %1. Exiting." ).arg ( strJsonRpcSecretFileName ) ) ); + } + QTextStream qtsJsonRpcSecretStream ( &qfJsonRpcSecretFile ); + QString strJsonRpcSecret = qtsJsonRpcSecretStream.readLine(); + if ( strJsonRpcSecret.length() < JSON_RPC_MINIMUM_SECRET_LENGTH ) + { + throw CErrorExit ( qUtf8Printable ( QString ( "JSON-RPC: Refusing to run with secret of length %1 (required: %2). Exiting." ) + .arg ( strJsonRpcSecret.length() ) + .arg ( JSON_RPC_MINIMUM_SECRET_LENGTH ) ) ); + } - qWarning() << "- JSON-RPC: This interface is experimental and is subject to breaking changes even on patch versions " - "(not subject to semantic versioning) during the initial phase."; + qWarning() << "- JSON-RPC: This interface is experimental and is subject to breaking changes even on patch versions " + "(not subject to semantic versioning) during the initial phase."; - pRpcServer = new CRpcServer ( pApp, iJsonRpcPortNumber, strJsonRpcSecret ); - if ( !pRpcServer->Start() ) - { - qCritical() << qUtf8Printable ( QString ( "- JSON-RPC: Server failed to start. Exiting." ) ); - exit ( 1 ); + pRpcServer = new CRpcServer ( bUseGUI ? pApplication : pCoreApplication, iJsonRpcPortNumber, strJsonRpcSecret ); + + if ( !pRpcServer->Start() ) + { + throw CErrorExit ( qUtf8Printable ( QString ( "- JSON-RPC: Server failed to start. Exiting." ) ) ); + } } - } - try - { #ifndef SERVER_ONLY if ( bIsClient ) { @@ -878,13 +940,13 @@ int main ( int argc, char** argv ) // load translation if ( bUseGUI && bUseTranslation ) { - CLocale::LoadTranslation ( Settings.strLanguage, pApp ); + CLocale::LoadTranslation ( Settings.strLanguage, pApplication ); CInstPictures::UpdateTableOnLanguageChange(); } if ( pRpcServer ) { - new CClientRpc ( &Client, pRpcServer, pRpcServer ); + pClientRpc = new CClientRpc ( &Client, pRpcServer, pRpcServer ); } # ifndef HEADLESS @@ -903,7 +965,7 @@ int main ( int argc, char** argv ) // show dialog ClientDlg.show(); - pApp->exec(); + exit_code = pApplication->exec(); } else # endif @@ -911,7 +973,7 @@ int main ( int argc, char** argv ) // only start application without using the GUI qInfo() << qUtf8Printable ( GetVersionAndNameStr ( false ) ); - pApp->exec(); + exit_code = pCoreApplication->exec(); } } else @@ -942,7 +1004,7 @@ int main ( int argc, char** argv ) if ( pRpcServer ) { - new CServerRpc ( &Server, pRpcServer, pRpcServer ); + pServerRpc = new CServerRpc ( &Server, pRpcServer, pRpcServer ); } #ifndef HEADLESS @@ -953,9 +1015,9 @@ int main ( int argc, char** argv ) Settings.Load ( CommandLineOptions ); // load translation - if ( bUseGUI && bUseTranslation ) + if ( bUseTranslation ) { - CLocale::LoadTranslation ( Settings.strLanguage, pApp ); + CLocale::LoadTranslation ( Settings.strLanguage, pApplication ); } // GUI object for the server @@ -967,7 +1029,7 @@ int main ( int argc, char** argv ) ServerDlg.show(); } - pApp->exec(); + exit_code = pApplication->exec(); } else #endif @@ -982,32 +1044,71 @@ int main ( int argc, char** argv ) Server.SetDirectoryType ( AT_CUSTOM ); } - pApp->exec(); + exit_code = pCoreApplication->exec(); } } } + catch ( const CInfoExit& info ) + { + // show info and exit normally + qInfo() << qUtf8Printable ( QString ( "%1: %2" ).arg ( strAppName, info.GetInfoMessage() ) ); + exit_code = 0; + } + + catch ( const CErrorExit& fatalerr ) + { + // show deliberate exit message + qCritical() << qUtf8Printable ( QString ( "%1: %2" ).arg ( strAppName, fatalerr.GetErrorMessage() ) ); + exit_code = fatalerr.GetExitCode(); + } + catch ( const CGenErr& generr ) { - // show generic error -#ifndef HEADLESS - if ( bUseGUI ) - { - QMessageBox::critical ( nullptr, APP_NAME, generr.GetErrorText(), "Quit", nullptr ); - } - else + // show unhandled generic error message + qCritical() << qUtf8Printable ( QString ( "%1: %2" ).arg ( strAppName, generr.GetErrorText() ) ); + exit_code = generr.GetExitCode(); + } + + catch ( ... ) + { + // show all other unhandled (standard) exceptions + qCritical() << qUtf8Printable ( QString ( "%1: Unknown Exception, Exiting" ).arg ( strAppName ) ); + exit_code = -1; + } + + if ( pServerRpc ) + { + delete pServerRpc; + } + +#ifndef SERVER_ONLY + if ( pClientRpc ) + { + delete pClientRpc; + } #endif - { - qCritical() << qUtf8Printable ( QString ( "%1: %2" ).arg ( APP_NAME ).arg ( generr.GetErrorText() ) ); - exit ( 1 ); - } + + if ( pRpcServer ) + { + delete pRpcServer; } -#if defined( Q_OS_MACX ) - activity.EndActivity(); + if ( pCoreApplication ) + { + delete pCoreApplication; + } + +#ifndef HEADLESS + // note: in headless mode pApplication is the same pointer as pCoreApplication! + // so pApplication is already deleted! + if ( pApplication ) + { + delete pApplication; + } #endif - return 0; + return exit_code; } /******************************************************************************\ @@ -1101,8 +1202,7 @@ bool GetStringArgument ( int argc, char** argv, int& i, QString strShortOpt, QSt { if ( ++i >= argc ) { - qCritical() << qUtf8Printable ( QString ( "%1: '%2' needs a string argument." ).arg ( argv[0] ).arg ( argv[i - 1] ) ); - exit ( 1 ); + throw CErrorExit ( qUtf8Printable ( QString ( "%1: '%2' needs a string argument." ).arg ( argv[0] ).arg ( argv[i - 1] ) ) ); } strArg = argv[i]; @@ -1129,16 +1229,14 @@ bool GetNumericArgument ( int argc, QString errmsg = "%1: '%2' needs a numeric argument from '%3' to '%4'."; if ( ++i >= argc ) { - qCritical() << qUtf8Printable ( errmsg.arg ( argv[0] ).arg ( argv[i - 1] ).arg ( rRangeStart ).arg ( rRangeStop ) ); - exit ( 1 ); + throw CErrorExit ( qUtf8Printable ( errmsg.arg ( argv[0] ).arg ( argv[i - 1] ).arg ( rRangeStart ).arg ( rRangeStop ) ) ); } char* p; rValue = strtod ( argv[i], &p ); if ( *p || ( rValue < rRangeStart ) || ( rValue > rRangeStop ) ) { - qCritical() << qUtf8Printable ( errmsg.arg ( argv[0] ).arg ( argv[i - 1] ).arg ( rRangeStart ).arg ( rRangeStop ) ); - exit ( 1 ); + throw CErrorExit ( qUtf8Printable ( errmsg.arg ( argv[0] ).arg ( argv[i - 1] ).arg ( rRangeStart ).arg ( rRangeStop ) ) ); } return true;