mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-11-03 21:40:52 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			574 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			574 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include <iostream>
 | 
						|
#include <exception>
 | 
						|
#include <stdexcept>
 | 
						|
#include <string>
 | 
						|
 | 
						|
#include <locale.h>
 | 
						|
#include <fftw3.h>
 | 
						|
 | 
						|
#include <QSharedMemory>
 | 
						|
#include <QTemporaryFile>
 | 
						|
#include <QDateTime>
 | 
						|
#include <QApplication>
 | 
						|
#include <QLocale>
 | 
						|
#include <QTranslator>
 | 
						|
#include <QRegularExpression>
 | 
						|
#include <QObject>
 | 
						|
#include <QSettings>
 | 
						|
#include <QLibraryInfo>
 | 
						|
#include <QSysInfo>
 | 
						|
#include <QDir>
 | 
						|
#include <QDirIterator>
 | 
						|
#include <QStandardPaths>
 | 
						|
#include <QStringList>
 | 
						|
#include <QLockFile>
 | 
						|
#include <QSplashScreen>
 | 
						|
#include <QCommandLineParser>
 | 
						|
#include <QCommandLineOption>
 | 
						|
#include <QSqlDatabase>
 | 
						|
#include <QSqlQuery>
 | 
						|
#include <QSqlError>
 | 
						|
 | 
						|
#include "revision_utils.hpp"
 | 
						|
#include "MetaDataRegistry.hpp"
 | 
						|
#include "SettingsGroup.hpp"
 | 
						|
#include "TraceFile.hpp"
 | 
						|
#include "MultiSettings.hpp"
 | 
						|
#include "widgets/mainwindow.h"
 | 
						|
#include "commons.h"
 | 
						|
#include "lib/init_random_seed.h"
 | 
						|
#include "Radio.hpp"
 | 
						|
#include "models/FrequencyList.hpp"
 | 
						|
#include "widgets/SplashScreen.hpp"
 | 
						|
#include "widgets/MessageBox.hpp"       // last to avoid nasty MS macro definitions
 | 
						|
 | 
						|
extern "C" {
 | 
						|
  // Fortran procedures we need
 | 
						|
  void four2a_(_Complex float *, int * nfft, int * ndim, int * isign, int * iform, int len);
 | 
						|
}
 | 
						|
 | 
						|
namespace
 | 
						|
{
 | 
						|
  struct RNGSetup
 | 
						|
  {
 | 
						|
    RNGSetup ()
 | 
						|
    {
 | 
						|
      // one time seed of pseudo RNGs from current time
 | 
						|
      auto seed = QDateTime::currentMSecsSinceEpoch ();
 | 
						|
      qsrand (seed);            // this is good for rand() as well
 | 
						|
    }
 | 
						|
  } seeding;
 | 
						|
 | 
						|
  // We  can't use  the GUI  after QApplication::exit()  is called  so
 | 
						|
  // uncaught exceptions can  get lost on Windows  systems where there
 | 
						|
  // is    no    console    terminal,     so    here    we    override
 | 
						|
  // QApplication::notify() and  wrap the base  class call with  a try
 | 
						|
  // block to catch and display exceptions in a message box.
 | 
						|
  class ExceptionCatchingApplication final
 | 
						|
    : public QApplication
 | 
						|
  {
 | 
						|
  public:
 | 
						|
    explicit ExceptionCatchingApplication (int& argc, char * * argv)
 | 
						|
      : QApplication {argc, argv}
 | 
						|
    {
 | 
						|
    }
 | 
						|
    bool notify (QObject * receiver, QEvent * e) override
 | 
						|
    {
 | 
						|
      try
 | 
						|
        {
 | 
						|
          return QApplication::notify (receiver, e);
 | 
						|
        }
 | 
						|
      catch (std::exception const& e)
 | 
						|
        {
 | 
						|
          MessageBox::critical_message (nullptr, translate ("main", "Fatal error"), e.what ());
 | 
						|
          throw;
 | 
						|
        }
 | 
						|
      catch (...)
 | 
						|
        {
 | 
						|
          MessageBox::critical_message (nullptr, translate ("main", "Unexpected fatal error"));
 | 
						|
          throw;
 | 
						|
        }
 | 
						|
    }
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, char *argv[])
 | 
						|
{
 | 
						|
  // ### Add timestamps to all debug messages
 | 
						|
//  qSetMessagePattern ("[%{time yyyyMMdd HH:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{message}");
 | 
						|
 | 
						|
  init_random_seed ();
 | 
						|
 | 
						|
  // make the Qt type magic happen
 | 
						|
  Radio::register_types ();
 | 
						|
  register_types ();
 | 
						|
 | 
						|
  // Multiple instances communicate with jt9 via this
 | 
						|
  QSharedMemory mem_jt9;
 | 
						|
 | 
						|
  ExceptionCatchingApplication a(argc, argv);
 | 
						|
  try
 | 
						|
    {
 | 
						|
      std::unique_ptr<QStringList> early_messages {new QStringList};
 | 
						|
 | 
						|
      QLocale locale;              // get the current system locale
 | 
						|
      *early_messages << QString {"locale: language: %1 script: %2 country: %3 ui-languages: %4"}
 | 
						|
      .arg (QLocale::languageToString (locale.language ()))
 | 
						|
         .arg (QLocale::scriptToString (locale.script ()))
 | 
						|
         .arg (QLocale::countryToString (locale.country ()))
 | 
						|
         .arg (locale.uiLanguages ().join (", "));
 | 
						|
      // qDebug () << "+++++++++++++++++++++++++++ Resources ++++++++++++++++++++++++++++";
 | 
						|
      // {
 | 
						|
      //   QDirIterator resources_iter {":/", QDirIterator::Subdirectories};
 | 
						|
      //   while (resources_iter.hasNext ())
 | 
						|
      //     {
 | 
						|
      //       qDebug () << resources_iter.next ();
 | 
						|
      //     }
 | 
						|
      // }
 | 
						|
      // qDebug () << "--------------------------- Resources ----------------------------";
 | 
						|
      setlocale (LC_NUMERIC, "C"); // ensure number forms are in
 | 
						|
                                   // consistent format, do this after
 | 
						|
                                   // instantiating QApplication so
 | 
						|
                                   // that GUI has correct l18n
 | 
						|
 | 
						|
      // Override programs executable basename as application name.
 | 
						|
      a.setApplicationName ("WSJT-X");
 | 
						|
      a.setApplicationVersion (version ());
 | 
						|
 | 
						|
      //
 | 
						|
      // Enable base i18n
 | 
						|
      //
 | 
						|
      QString translations_dir {":/Translations"};
 | 
						|
      QTranslator qt_translator;
 | 
						|
      if (qt_translator.load (locale, "qt", "_", translations_dir))
 | 
						|
        {
 | 
						|
          *early_messages << "Loaded Qt translations for current locale from resources";
 | 
						|
          a.installTranslator (&qt_translator);
 | 
						|
        }
 | 
						|
 | 
						|
      // Default translations for releases  use translations stored in
 | 
						|
      // the   resources   file    system   under   the   Translations
 | 
						|
      // directory. These are built by the CMake build system from .ts
 | 
						|
      // files in the translations source directory. New languages are
 | 
						|
      // added by  enabling the  UPDATE_TRANSLATIONS CMake  option and
 | 
						|
      // building with the  new language added to  the LANGUAGES CMake
 | 
						|
      // list  variable.  UPDATE_TRANSLATIONS  will preserve  existing
 | 
						|
      // translations  but   should  only  be  set   when  adding  new
 | 
						|
      // languages.  The  resulting .ts  files should be  checked info
 | 
						|
      // source control for translators to access and update.
 | 
						|
 | 
						|
      // try and load the base translation
 | 
						|
      QTranslator base_translator_from_resources;
 | 
						|
      for (QString locale_name : locale.uiLanguages ())
 | 
						|
        {
 | 
						|
          auto language = locale_name.left (2);
 | 
						|
          if (locale.uiLanguages ().front ().left (2) == language)
 | 
						|
            {
 | 
						|
              *early_messages << QString {"Trying %1"}.arg (language);
 | 
						|
              if (base_translator_from_resources.load ("wsjtx_" + language, translations_dir))
 | 
						|
                {
 | 
						|
                  *early_messages << QString {"Loaded base translation file from %1 based on language %2"}
 | 
						|
                     .arg (translations_dir)
 | 
						|
                     .arg (language);
 | 
						|
                  a.installTranslator (&base_translator_from_resources);
 | 
						|
                  break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
      // now try and load the most specific translations (may be a
 | 
						|
      // duplicate but we shouldn't care)
 | 
						|
      QTranslator translator_from_resources;
 | 
						|
      if (translator_from_resources.load (locale, "wsjtx", "_", translations_dir))
 | 
						|
        {
 | 
						|
          *early_messages << "Loaded translations for current locale from resources";
 | 
						|
          a.installTranslator (&translator_from_resources);
 | 
						|
        }
 | 
						|
 | 
						|
      QCommandLineParser parser;
 | 
						|
      parser.setApplicationDescription ("\n" PROJECT_SUMMARY_DESCRIPTION);
 | 
						|
      auto help_option = parser.addHelpOption ();
 | 
						|
      auto version_option = parser.addVersionOption ();
 | 
						|
 | 
						|
      // support for multiple instances running from a single installation
 | 
						|
      QCommandLineOption rig_option (QStringList {} << "r" << "rig-name"
 | 
						|
                                     , a.translate ("main", "Where <rig-name> is for multi-instance support.")
 | 
						|
                                     , a.translate ("main", "rig-name"));
 | 
						|
      parser.addOption (rig_option);
 | 
						|
 | 
						|
      // support for start up configuration
 | 
						|
      QCommandLineOption cfg_option (QStringList {} << "c" << "config"
 | 
						|
                                     , a.translate ("main", "Where <configuration> is an existing one.")
 | 
						|
                                     , a.translate ("main", "configuration"));
 | 
						|
      parser.addOption (cfg_option);
 | 
						|
 | 
						|
      // support for UI language override (useful on Windows)
 | 
						|
      QCommandLineOption lang_option (QStringList {} << "l" << "language"
 | 
						|
                                     , a.translate ("main", "Where <language> is <lang-code>[-<country-code>].")
 | 
						|
                                     , a.translate ("main", "language"));
 | 
						|
      parser.addOption (lang_option);
 | 
						|
 | 
						|
      QCommandLineOption test_option (QStringList {} << "test-mode"
 | 
						|
                                      , a.translate ("main", "Writable files in test location.  Use with caution, for testing only."));
 | 
						|
      parser.addOption (test_option);
 | 
						|
 | 
						|
      if (!parser.parse (a.arguments ()))
 | 
						|
        {
 | 
						|
          MessageBox::critical_message (nullptr, a.translate ("main", "Command line error"), parser.errorText ());
 | 
						|
          return -1;
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          if (parser.isSet (help_option))
 | 
						|
            {
 | 
						|
              MessageBox::information_message (nullptr, a.translate ("main", "Command line help"), parser.helpText ());
 | 
						|
              return 0;
 | 
						|
            }
 | 
						|
          else if (parser.isSet (version_option))
 | 
						|
            {
 | 
						|
              MessageBox::information_message (nullptr, a.translate ("main", "Application version"), a.applicationVersion ());
 | 
						|
              return 0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
      //
 | 
						|
      // Complete i18n
 | 
						|
      // 
 | 
						|
 | 
						|
      // Load  any matching  translation  from  the current  directory
 | 
						|
      // using the command line  option language override. This allows
 | 
						|
      // translators to  easily test  their translations  by releasing
 | 
						|
      // (lrelease)  a .qm  file  into the  current  directory with  a
 | 
						|
      // suitable name  (e.g.  wsjtx_en_GB.qm), then running  wsjtx to
 | 
						|
      // view the  results.
 | 
						|
      QTranslator base_translation_override_from_resources;
 | 
						|
      QTranslator translation_override_from_resources;
 | 
						|
      if (parser.isSet (lang_option))
 | 
						|
        {
 | 
						|
          auto language = parser.value (lang_option).replace ('-', '_');
 | 
						|
          // try and load the base translation
 | 
						|
          auto base_language = language.left (2);
 | 
						|
          if (base_translation_override_from_resources.load ("wsjtx_" + base_language, translations_dir))
 | 
						|
            {
 | 
						|
              *early_messages << QString {"Loaded base translation file from %1 based on language %2"}
 | 
						|
                 .arg (translations_dir)
 | 
						|
                 .arg (base_language);
 | 
						|
              a.installTranslator (&base_translation_override_from_resources);
 | 
						|
            }
 | 
						|
          // now load the requested translations (may be a duplicate
 | 
						|
          // but we shouldn't care)
 | 
						|
          if (translation_override_from_resources.load ("wsjtx_" + language, translations_dir))
 | 
						|
            {
 | 
						|
              *early_messages << QString {"Loaded translation file from %1 based on language %2"}
 | 
						|
                 .arg (translations_dir)
 | 
						|
                 .arg (language);
 | 
						|
              a.installTranslator (&translation_override_from_resources);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
      // Load  any matching  translation  from  the current  directory
 | 
						|
      // using the  current locale. This allows  translators to easily
 | 
						|
      // test their  translations by  releasing (lrelease) a  .qm file
 | 
						|
      // into  the  current  directory  with  a  suitable  name  (e.g.
 | 
						|
      // wsjtx_en_GB.qm), then running wsjtx  to view the results. The
 | 
						|
      // system locale setting will be  used to select the translation
 | 
						|
      // file which can be overridden by the LANG environment variable
 | 
						|
      // on non-Windows system.
 | 
						|
 | 
						|
      // try and load the base translation
 | 
						|
      QTranslator base_translator_from_cwd;
 | 
						|
      for (QString locale_name : locale.uiLanguages ())
 | 
						|
        {
 | 
						|
          auto language = locale_name.left (2);
 | 
						|
          if (locale.uiLanguages ().front ().left (2) == language)
 | 
						|
            {
 | 
						|
              *early_messages << QString {"Trying %1"}.arg (language);
 | 
						|
              if (base_translator_from_cwd.load ("wsjtx_" + language))
 | 
						|
                {
 | 
						|
                  *early_messages << QString {"Loaded base translation file from $cwd based on language %1"}.arg (language);
 | 
						|
                  a.installTranslator (&base_translator_from_cwd);
 | 
						|
                  break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
      // now try and load the most specific translations (may be a
 | 
						|
      // duplicate but we shouldn't care)
 | 
						|
      QTranslator translator_from_cwd;
 | 
						|
      if (translator_from_cwd.load (locale, "wsjtx", "_"))
 | 
						|
        {
 | 
						|
          *early_messages << "loaded translations for current locale from a file";
 | 
						|
          a.installTranslator (&translator_from_cwd);
 | 
						|
        }
 | 
						|
 | 
						|
      // Load  any matching  translation  from  the current  directory
 | 
						|
      // using the command line  option language override. This allows
 | 
						|
      // translators to  easily test their translations  on Windows by
 | 
						|
      // releasing (lrelease)  a .qm  file into the  current directory
 | 
						|
      // with  a suitable  name (e.g.   wsjtx_en_GB.qm), then  running
 | 
						|
      // wsjtx to view the results.
 | 
						|
      QTranslator base_translation_override_from_cwd;
 | 
						|
      QTranslator translation_override_from_cwd;
 | 
						|
      if (parser.isSet (lang_option))
 | 
						|
        {
 | 
						|
          auto language = parser.value (lang_option).replace ('-', '_');
 | 
						|
          // try and load the base translation
 | 
						|
          auto base_language = language.left (2);
 | 
						|
          if (base_translation_override_from_cwd.load ("wsjtx_" + base_language))
 | 
						|
            {
 | 
						|
              *early_messages << QString {"Loaded base translation file from $cwd based on language %1"}.arg (base_language);
 | 
						|
              a.installTranslator (&base_translation_override_from_cwd);
 | 
						|
            }
 | 
						|
          // now load the requested translations (may be a duplicate
 | 
						|
          // but we shouldn't care)
 | 
						|
          if (translation_override_from_cwd.load ("wsjtx_" + language))
 | 
						|
            {
 | 
						|
              *early_messages << QString {"loaded translation file from $cwd based on language %1"}.arg (language);
 | 
						|
              a.installTranslator (&translation_override_from_cwd);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
      QStandardPaths::setTestModeEnabled (parser.isSet (test_option));
 | 
						|
 | 
						|
      // support for multiple instances running from a single installation
 | 
						|
      bool multiple {false};
 | 
						|
      if (parser.isSet (rig_option) || parser.isSet (test_option))
 | 
						|
        {
 | 
						|
          auto temp_name = parser.value (rig_option);
 | 
						|
          if (!temp_name.isEmpty ())
 | 
						|
            {
 | 
						|
              if (temp_name.contains (QRegularExpression {R"([\\/,])"}))
 | 
						|
                {
 | 
						|
                  std::cerr << QObject::tr ("Invalid rig name - \\ & / not allowed").toLocal8Bit ().data () << std::endl;
 | 
						|
                  parser.showHelp (-1);
 | 
						|
                }
 | 
						|
                
 | 
						|
              a.setApplicationName (a.applicationName () + " - " + temp_name);
 | 
						|
            }
 | 
						|
 | 
						|
          if (parser.isSet (test_option))
 | 
						|
            {
 | 
						|
              a.setApplicationName (a.applicationName () + " - test");
 | 
						|
            }
 | 
						|
 | 
						|
          multiple = true;
 | 
						|
        }
 | 
						|
 | 
						|
      // now we have the application name we can open the settings
 | 
						|
      MultiSettings multi_settings {parser.value (cfg_option)};
 | 
						|
 | 
						|
      // find the temporary files path
 | 
						|
      QDir temp_dir {QStandardPaths::writableLocation (QStandardPaths::TempLocation)};
 | 
						|
      Q_ASSERT (temp_dir.exists ()); // sanity check
 | 
						|
 | 
						|
      // disallow multiple instances with same instance key
 | 
						|
      QLockFile instance_lock {temp_dir.absoluteFilePath (a.applicationName () + ".lock")};
 | 
						|
      instance_lock.setStaleLockTime (0);
 | 
						|
      bool lock_ok {false};
 | 
						|
      while (!(lock_ok = instance_lock.tryLock ()))
 | 
						|
        {
 | 
						|
          if (QLockFile::LockFailedError == instance_lock.error ())
 | 
						|
            {
 | 
						|
              auto button = MessageBox::query_message (nullptr
 | 
						|
                                                       , a.translate ("main", "Another instance may be running")
 | 
						|
                                                       , a.translate ("main", "try to remove stale lock file?")
 | 
						|
                                                       , QString {}
 | 
						|
                                                       , MessageBox::Yes | MessageBox::Retry | MessageBox::No
 | 
						|
                                                       , MessageBox::Yes);
 | 
						|
              switch (button)
 | 
						|
                {
 | 
						|
                case MessageBox::Yes:
 | 
						|
                  instance_lock.removeStaleLockFile ();
 | 
						|
                  break;
 | 
						|
 | 
						|
                case MessageBox::Retry:
 | 
						|
                  break;
 | 
						|
 | 
						|
                default:
 | 
						|
                  throw std::runtime_error {"Multiple instances must have unique rig names"};
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
#if WSJT_QDEBUG_TO_FILE
 | 
						|
      // Open a trace file
 | 
						|
      TraceFile trace_file {temp_dir.absoluteFilePath (a.applicationName () + "_trace.log")};
 | 
						|
      qSetMessagePattern ("[%{time yyyyMMdd HH:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{file}:%{line} - %{message}");
 | 
						|
      qDebug () << program_title (revision ()) + " - Program startup";
 | 
						|
#endif
 | 
						|
 | 
						|
      // trace early messages now we have a trace file
 | 
						|
      for (auto const& message : *early_messages)
 | 
						|
        {
 | 
						|
          qDebug () << message;
 | 
						|
        }
 | 
						|
      early_messages.reset ();  // free memory
 | 
						|
 | 
						|
      // Create a unique writeable temporary directory in a suitable location
 | 
						|
      bool temp_ok {false};
 | 
						|
      QString unique_directory {QApplication::applicationName ()};
 | 
						|
      do
 | 
						|
        {
 | 
						|
          if (!temp_dir.mkpath (unique_directory)
 | 
						|
              || !temp_dir.cd (unique_directory))
 | 
						|
            {
 | 
						|
              MessageBox::critical_message (nullptr,
 | 
						|
                                            a.translate ("main", "Failed to create a temporary directory"),
 | 
						|
                                            a.translate ("main", "Path: \"%1\"").arg (temp_dir.absolutePath ()));
 | 
						|
              throw std::runtime_error {"Failed to create a temporary directory"};
 | 
						|
            }
 | 
						|
          if (!temp_dir.isReadable () || !(temp_ok = QTemporaryFile {temp_dir.absoluteFilePath ("test")}.open ()))
 | 
						|
            {
 | 
						|
              auto button =  MessageBox::critical_message (nullptr,
 | 
						|
                                                           a.translate ("main", "Failed to create a usable temporary directory"),
 | 
						|
                                                           a.translate ("main", "Another application may be locking the directory"),
 | 
						|
                                                           a.translate ("main", "Path: \"%1\"").arg (temp_dir.absolutePath ()),
 | 
						|
                                                           MessageBox::Retry | MessageBox::Cancel);
 | 
						|
              if (MessageBox::Cancel == button)
 | 
						|
                {
 | 
						|
                  throw std::runtime_error {"Failed to create a usable temporary directory"};
 | 
						|
                }
 | 
						|
              temp_dir.cdUp ();  // revert to parent as this one is no good
 | 
						|
            }
 | 
						|
        }
 | 
						|
      while (!temp_ok);
 | 
						|
 | 
						|
      SplashScreen splash;
 | 
						|
      {
 | 
						|
        // change this key if you want to force a new splash screen
 | 
						|
        // for a new version, the user will be able to re-disable it
 | 
						|
        // if they wish
 | 
						|
        QString splash_flag_name {"Splash_v1.7"};
 | 
						|
        if (multi_settings.common_value (splash_flag_name, true).toBool ())
 | 
						|
          {
 | 
						|
            QObject::connect (&splash, &SplashScreen::disabled, [&, splash_flag_name] {
 | 
						|
                multi_settings.set_common_value (splash_flag_name, false);
 | 
						|
                splash.close ();
 | 
						|
              });
 | 
						|
            splash.show ();
 | 
						|
            a.processEvents ();
 | 
						|
          }
 | 
						|
      }
 | 
						|
 | 
						|
      // create writeable data directory if not already there
 | 
						|
      auto writeable_data_dir = QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)};
 | 
						|
      if (!writeable_data_dir.mkpath ("."))
 | 
						|
        {
 | 
						|
          MessageBox::critical_message (nullptr, a.translate ("main", "Failed to create data directory"),
 | 
						|
                                        a.translate ("main", "path: \"%1\"").arg (writeable_data_dir.absolutePath ()));
 | 
						|
          throw std::runtime_error {"Failed to create data directory"};
 | 
						|
        }
 | 
						|
 | 
						|
      // set up SQLite database
 | 
						|
      if (!QSqlDatabase::drivers ().contains ("QSQLITE"))
 | 
						|
        {
 | 
						|
          throw std::runtime_error {"Failed to find SQLite Qt driver"};
 | 
						|
        }
 | 
						|
      auto db = QSqlDatabase::addDatabase ("QSQLITE");
 | 
						|
      db.setDatabaseName (writeable_data_dir.absoluteFilePath ("db.sqlite"));
 | 
						|
      if (!db.open ())
 | 
						|
        {
 | 
						|
          throw std::runtime_error {("Database Error: " + db.lastError ().text ()).toStdString ()};
 | 
						|
        }
 | 
						|
 | 
						|
      // better performance traded for a risk of d/b corruption
 | 
						|
      // on system crash or application crash
 | 
						|
      // db.exec ("PRAGMA synchronous=OFF"); // system crash risk
 | 
						|
      // db.exec ("PRAGMA journal_mode=MEMORY"); // application crash risk
 | 
						|
      db.exec ("PRAGMA locking_mode=EXCLUSIVE");
 | 
						|
 | 
						|
      int result;
 | 
						|
      do
 | 
						|
        {
 | 
						|
#if WSJT_QDEBUG_TO_FILE
 | 
						|
          // announce to trace file and dump settings
 | 
						|
          qDebug () << "++++++++++++++++++++++++++++ Settings ++++++++++++++++++++++++++++";
 | 
						|
          for (auto const& key: multi_settings.settings ()->allKeys ())
 | 
						|
            {
 | 
						|
              auto const& value = multi_settings.settings ()->value (key);
 | 
						|
              if (value.canConvert<QVariantList> ())
 | 
						|
                {
 | 
						|
                  auto const sequence = value.value<QSequentialIterable> ();
 | 
						|
                  qDebug ().nospace () << key << ": ";
 | 
						|
                  for (auto const& item: sequence)
 | 
						|
                    {
 | 
						|
                      qDebug ().nospace () << '\t' << item;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
              else
 | 
						|
                {
 | 
						|
                  qDebug ().nospace () << key << ": " << value;
 | 
						|
                }
 | 
						|
            }
 | 
						|
          qDebug () << "---------------------------- Settings ----------------------------";
 | 
						|
#endif
 | 
						|
 | 
						|
          // Create and initialize shared memory segment
 | 
						|
          // Multiple instances: use rig_name as shared memory key
 | 
						|
          mem_jt9.setKey(a.applicationName ());
 | 
						|
 | 
						|
          if(!mem_jt9.attach()) {
 | 
						|
            if (!mem_jt9.create(sizeof(struct dec_data))) {
 | 
						|
              splash.hide ();
 | 
						|
              MessageBox::critical_message (nullptr, a.translate ("main", "Shared memory error"),
 | 
						|
                                            a.translate ("main", "Unable to create shared memory segment"));
 | 
						|
              throw std::runtime_error {"Shared memory error"};
 | 
						|
            }
 | 
						|
          }
 | 
						|
          mem_jt9.lock ();
 | 
						|
          memset(mem_jt9.data(),0,sizeof(struct dec_data)); //Zero all decoding params in shared memory
 | 
						|
          mem_jt9.unlock ();
 | 
						|
 | 
						|
          unsigned downSampleFactor;
 | 
						|
          {
 | 
						|
            SettingsGroup {multi_settings.settings (), "Tune"};
 | 
						|
 | 
						|
            // deal with Windows Vista and earlier input audio rate
 | 
						|
            // converter problems
 | 
						|
            downSampleFactor = multi_settings.settings ()->value ("Audio/DisableInputResampling",
 | 
						|
#if defined (Q_OS_WIN)
 | 
						|
                                                                  // default to true for
 | 
						|
                                                                  // Windows Vista and older
 | 
						|
                                                                  QSysInfo::WV_VISTA >= QSysInfo::WindowsVersion ? true : false
 | 
						|
#else
 | 
						|
                                                                  false
 | 
						|
#endif
 | 
						|
                                                                  ).toBool () ? 1u : 4u;
 | 
						|
          }
 | 
						|
 | 
						|
          // run the application UI
 | 
						|
          MainWindow w(temp_dir, multiple, &multi_settings, &mem_jt9, downSampleFactor, &splash);
 | 
						|
          w.show();
 | 
						|
          splash.raise ();
 | 
						|
          QObject::connect (&a, SIGNAL (lastWindowClosed()), &a, SLOT (quit()));
 | 
						|
          result = a.exec();
 | 
						|
        }
 | 
						|
      while (!result && !multi_settings.exit ());
 | 
						|
 | 
						|
      // clean up lazily initialized resources
 | 
						|
      {
 | 
						|
        int nfft {-1};
 | 
						|
        int ndim {1};
 | 
						|
        int isign {1};
 | 
						|
        int iform {1};
 | 
						|
        // free FFT plan resources
 | 
						|
        four2a_ (nullptr, &nfft, &ndim, &isign, &iform, 0);
 | 
						|
      }
 | 
						|
      fftwf_forget_wisdom ();
 | 
						|
      fftwf_cleanup ();
 | 
						|
 | 
						|
      temp_dir.removeRecursively (); // clean up temp files
 | 
						|
      return result;
 | 
						|
    }
 | 
						|
  catch (std::exception const& e)
 | 
						|
    {
 | 
						|
      MessageBox::critical_message (nullptr, QApplication::translate ("main", "Fatal error"), e.what ());
 | 
						|
      std::cerr << "Error: " << e.what () << '\n';
 | 
						|
    }
 | 
						|
  catch (...)
 | 
						|
    {
 | 
						|
      MessageBox::critical_message (nullptr, QApplication::translate ("main", "Unexpected fatal error"));
 | 
						|
      std::cerr << "Unexpected fatal error\n";
 | 
						|
      throw;			// hoping the runtime might tell us more about the exception
 | 
						|
    }
 | 
						|
  return -1;
 | 
						|
}
 |