mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-11-03 21:40:52 -05:00 
			
		
		
		
	Load a base language translation file if found. E.g. if locale language is es-AR then wsjtx_es.qm will be loaded before wsjtx_es_AR.qm. this allows partial translations for languages where variants or dialects only require some translated strings to be different from the base language translations. the aim is to greatly simplify the work of translators, particularly when doing updates to translations.
		
			
				
	
	
		
			529 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			529 lines
		
	
	
		
			22 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 <QTranslator>
 | 
						|
#include <QRegularExpression>
 | 
						|
#include <QObject>
 | 
						|
#include <QSettings>
 | 
						|
#include <QLibraryInfo>
 | 
						|
#include <QSysInfo>
 | 
						|
#include <QDir>
 | 
						|
#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
 | 
						|
    {
 | 
						|
      QLocale locale;           // get the current system locale
 | 
						|
      qDebug () << "locale: language:" << locale.language () << "script:" << locale.script ()
 | 
						|
                << "country:" << locale.country () << "ui-languages:" << locale.uiLanguages ();
 | 
						|
      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
 | 
						|
      //
 | 
						|
      QTranslator translator_from_resources;
 | 
						|
      QTranslator base_translator_from_resources;
 | 
						|
      // 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
 | 
						|
      for (QString locale_name : locale.uiLanguages ())
 | 
						|
        {
 | 
						|
          auto language = locale_name.left (2);
 | 
						|
          if (base_translator_from_resources.load ("wsjtx_" + language, ":/Translations"))
 | 
						|
            {
 | 
						|
              qDebug () << QString {"Loaded base translation file from :/Translations based on language %1"}.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)
 | 
						|
      if (translator_from_resources.load (locale, "wsjtx", "_", ":/Translations"))
 | 
						|
        {
 | 
						|
          qDebug () << "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
 | 
						|
      // 
 | 
						|
 | 
						|
      QTranslator translation_override_from_resources;
 | 
						|
      QTranslator base_translation_override_from_resources;
 | 
						|
      // 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.
 | 
						|
      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"))
 | 
						|
            {
 | 
						|
              qDebug () << QString {"Loaded base translation file from :/Translations based on language %1"}.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")) 
 | 
						|
            {
 | 
						|
              qDebug () << QString {"Loaded translation file from :/Translations based on language %1"}.arg (language);
 | 
						|
              a.installTranslator (&translation_override_from_resources);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
      QTranslator translator_from_cwd;
 | 
						|
      QTranslator base_translator_from_cwd;
 | 
						|
      // 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
 | 
						|
      for (QString locale_name : locale.uiLanguages ())
 | 
						|
        {
 | 
						|
          auto language = locale_name.left (2);
 | 
						|
          if (base_translator_from_cwd.load ("wsjtx_" + language))
 | 
						|
            {
 | 
						|
              qDebug () << 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)
 | 
						|
      if (translator_from_cwd.load (locale, "wsjtx", "_"))
 | 
						|
        {
 | 
						|
          qDebug () << "loaded translations for current locale from a file";
 | 
						|
          a.installTranslator (&translator_from_cwd);
 | 
						|
        }
 | 
						|
 | 
						|
      QTranslator translation_override_from_cwd;
 | 
						|
      QTranslator base_translation_override_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.
 | 
						|
      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))
 | 
						|
            {
 | 
						|
              qDebug () << 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))
 | 
						|
            {
 | 
						|
              qDebug () << 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
 | 
						|
 | 
						|
      // 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;
 | 
						|
}
 |