mirror of
https://github.com/JS8Call-improved/JS8Call-improved
synced 2026-08-13 17:47:36 -04:00
Rename all header files with .h extension Fix function declaration in HamlibTransceiver.h that overrode member function but was not marked override Remove unused variable in Modulator.cpp Remove unused files from source tree Remove duplicate LazyFillComboBox files Reformat codebase to LLVM C++ standard
290 lines
10 KiB
C++
290 lines
10 KiB
C++
#include <exception>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
#include <fftw3.h>
|
|
#include <locale.h>
|
|
|
|
#include <QApplication>
|
|
#include <QDateTime>
|
|
#include <QDir>
|
|
#include <QLibraryInfo>
|
|
#include <QLockFile>
|
|
#include <QLoggingCategory>
|
|
#include <QObject>
|
|
#include <QRegularExpression>
|
|
#include <QSettings>
|
|
#include <QStack>
|
|
#include <QStandardPaths>
|
|
#include <QStringList>
|
|
#include <QSysInfo>
|
|
|
|
#if QT_VERSION >= 0x050200
|
|
#include <QCommandLineOption>
|
|
#include <QCommandLineParser>
|
|
#endif
|
|
|
|
#include "FrequencyList.h"
|
|
#include "JS8MessageBox.h"
|
|
#include "JS8_Include/SettingsGroup.h"
|
|
#include "JS8_Include/commons.h"
|
|
#include "JS8_UI/mainwindow.h"
|
|
#include "MetaDataRegistry.h"
|
|
#include "MultiSettings.h"
|
|
#include "Radio.h"
|
|
#include "TraceFile.h"
|
|
#include "revision_utils.h"
|
|
|
|
#include "DriftingDateTime.h"
|
|
|
|
Q_DECLARE_LOGGING_CATEGORY(main_js8)
|
|
|
|
namespace {
|
|
class MessageTimestamper {
|
|
public:
|
|
MessageTimestamper() {
|
|
prior_handlers_.push(qInstallMessageHandler(message_handler));
|
|
}
|
|
~MessageTimestamper() {
|
|
if (prior_handlers_.size())
|
|
qInstallMessageHandler(prior_handlers_.pop());
|
|
}
|
|
|
|
private:
|
|
static void message_handler(QtMsgType type,
|
|
QMessageLogContext const &context,
|
|
QString const &msg) {
|
|
QtMessageHandler handler{prior_handlers_.top()};
|
|
if (handler) {
|
|
handler(type, context,
|
|
DriftingDateTime::currentDateTimeUtc().toString(
|
|
"yy-MM-ddTHH:mm:ss.zzzZ: ") +
|
|
msg);
|
|
}
|
|
}
|
|
static QStack<QtMessageHandler> prior_handlers_;
|
|
};
|
|
QStack<QtMessageHandler> MessageTimestamper::prior_handlers_;
|
|
} // namespace
|
|
|
|
int main(int argc, char *argv[]) {
|
|
// Add timestamps to all debug messages
|
|
MessageTimestamper message_timestamper;
|
|
|
|
// make the Qt type magic happen
|
|
Radio::register_types();
|
|
register_types();
|
|
|
|
QApplication a(argc, argv);
|
|
try {
|
|
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("JS8Call");
|
|
a.setApplicationVersion(version());
|
|
|
|
#if QT_VERSION >= 0x050200
|
|
QCommandLineParser parser;
|
|
parser.setApplicationDescription("\n" PROJECT_SUMMARY_DESCRIPTION);
|
|
auto help_option = parser.addHelpOption();
|
|
auto version_option = parser.addVersionOption();
|
|
|
|
QCommandLineOption output_option(QStringList() << "o" << "output",
|
|
"Write debug statements into <file>.",
|
|
"file");
|
|
parser.addOption(output_option);
|
|
|
|
// 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);
|
|
|
|
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())) {
|
|
std::cerr << parser.errorText().toLocal8Bit().data() << std::endl;
|
|
return -1;
|
|
} else {
|
|
if (parser.isSet(help_option)) {
|
|
parser.showHelp(-1);
|
|
return 0;
|
|
} else if (parser.isSet(version_option)) {
|
|
parser.showVersion();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (parser.isSet(output_option)) {
|
|
new TraceFile(parser.value(output_option));
|
|
}
|
|
|
|
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 << "Invalid rig name - \\ & / not allowed"
|
|
<< 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);
|
|
while (!instance_lock.tryLock()) {
|
|
if (QLockFile::LockFailedError == instance_lock.error()) {
|
|
switch (JS8MessageBox::query_message(
|
|
nullptr,
|
|
a.translate("main", "Another instance may be running"),
|
|
a.translate("main", "try to remove stale lock file?"),
|
|
QString{},
|
|
JS8MessageBox::Yes | JS8MessageBox::Retry |
|
|
JS8MessageBox::No,
|
|
JS8MessageBox::Yes)) {
|
|
case JS8MessageBox::Yes:
|
|
instance_lock.removeStaleLockFile();
|
|
break;
|
|
|
|
case JS8MessageBox::Retry:
|
|
break;
|
|
|
|
default:
|
|
throw std::runtime_error{
|
|
"Multiple instances must have unique rig names"};
|
|
}
|
|
} else {
|
|
throw std::runtime_error{"Failed to access lock file"};
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if WSJT_QDEBUG_TO_FILE
|
|
// Open a trace file
|
|
TraceFile trace_file{
|
|
temp_dir.absoluteFilePath(a.applicationName() + "_trace.log")};
|
|
qCDebug(main_js8) << program_title() + " - 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)) {
|
|
JS8MessageBox::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 = JS8MessageBox::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()),
|
|
JS8MessageBox::Retry | JS8MessageBox::Cancel);
|
|
if (JS8MessageBox::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);
|
|
|
|
int result;
|
|
do {
|
|
#if WSJT_QDEBUG_TO_FILE
|
|
// announce to trace file and dump settings
|
|
qCDebug(main_js8) << "++++++++++++++++++++++++++++ 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>();
|
|
qCDebug(main_js8).nospace() << key << ": ";
|
|
for (auto const &item : sequence) {
|
|
qCDebug(main_js8).nospace() << '\t' << item;
|
|
}
|
|
} else {
|
|
qCDebug(main_js8).nospace() << key << ": " << value;
|
|
}
|
|
}
|
|
qCDebug(main_js8) << "---------------------------- Settings "
|
|
"----------------------------";
|
|
#endif
|
|
|
|
// run the application UI
|
|
MainWindow w(program_version(), temp_dir, multiple,
|
|
&multi_settings);
|
|
w.show();
|
|
result = a.exec();
|
|
} while (!result && !multi_settings.exit());
|
|
|
|
fftwf_forget_wisdom();
|
|
fftwf_cleanup();
|
|
|
|
temp_dir.removeRecursively(); // clean up temp files
|
|
return result;
|
|
} catch (std::exception const &e) {
|
|
JS8MessageBox::critical_message(nullptr, "Fatal error", e.what());
|
|
std::cerr << "Error: " << e.what() << '\n';
|
|
} catch (...) {
|
|
JS8MessageBox::critical_message(nullptr, "Unexpected fatal error");
|
|
std::cerr << "Unexpected fatal error\n";
|
|
throw; // hoping the runtime might tell us more about the exception
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
Q_LOGGING_CATEGORY(main_js8, "main.js8", QtWarningMsg)
|