]> sigrok.org Git - pulseview.git/blobdiff - main.cpp
Fix #1292/1294 by snapping to any edge when not hovering over a signal
[pulseview.git] / main.cpp
index 1e214e03f97df04c1f694b971ca25df0a36cc3eb..8054e49be3315810bcd355d994c689f30471cd61 100644 (file)
--- a/main.cpp
+++ b/main.cpp
 #endif
 
 #include <cstdint>
-#include <libsigrokcxx/libsigrokcxx.hpp>
-
+#include <fstream>
 #include <getopt.h>
+#include <vector>
 
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+#include <QCheckBox>
 #include <QDebug>
+#include <QFile>
+#include <QFileInfo>
+#include <QMessageBox>
 #include <QSettings>
+#include <QTextStream>
+
+#include "config.h"
 
 #ifdef ENABLE_SIGNALS
 #include "signalhandler.hpp"
 #endif
 
+#ifdef ENABLE_STACKTRACE
+#include <signal.h>
+#include <boost/stacktrace.hpp>
+#include <QStandardPaths>
+#endif
+
 #include "pv/application.hpp"
 #include "pv/devicemanager.hpp"
+#include "pv/globalsettings.hpp"
+#include "pv/logging.hpp"
 #include "pv/mainwindow.hpp"
 #include "pv/session.hpp"
+
 #ifdef ANDROID
 #include <libsigrokandroidutils/libsigrokandroidutils.h>
 #include "android/assetreader.hpp"
 #include "android/loghandler.hpp"
 #endif
 
-#include "config.h"
-
 #ifdef _WIN32
 #include <QtPlugin>
 Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
@@ -52,9 +68,76 @@ Q_IMPORT_PLUGIN(QSvgPlugin)
 #endif
 
 using std::exception;
+using std::ifstream;
+using std::ofstream;
 using std::shared_ptr;
 using std::string;
 
+#if ENABLE_STACKTRACE
+QString stacktrace_filename;
+
+void signal_handler(int signum)
+{
+       ::signal(signum, SIG_DFL);
+       boost::stacktrace::safe_dump_to(stacktrace_filename.toLocal8Bit().data());
+       ::raise(SIGABRT);
+}
+
+void process_stacktrace(QString temp_path)
+{
+       const QString stacktrace_outfile = temp_path + "/pv_stacktrace.txt";
+
+       ifstream ifs(stacktrace_filename.toLocal8Bit().data());
+       ofstream ofs(stacktrace_outfile.toLocal8Bit().data(),
+               ofstream::out | ofstream::trunc);
+
+       boost::stacktrace::stacktrace st =
+               boost::stacktrace::stacktrace::from_dump(ifs);
+       ofs << st;
+
+       ofs.close();
+       ifs.close();
+
+       QFile f(stacktrace_outfile);
+       f.open(QFile::ReadOnly | QFile::Text);
+       QTextStream fs(&f);
+       QString stacktrace = fs.readAll();
+       stacktrace = stacktrace.trimmed().replace('\n', "<br />");
+
+       qDebug() << QObject::tr("Stack trace of previous crash:");
+       qDebug() << "---------------------------------------------------------";
+       // Note: qDebug() prints quotation marks for QString output, so we feed it char*
+       qDebug() << stacktrace.toLocal8Bit().data();
+       qDebug() << "---------------------------------------------------------";
+
+       f.close();
+
+       // Remove stack trace so we don't process it again the next time we run
+       QFile::remove(stacktrace_filename.toLocal8Bit().data());
+
+       // Show notification dialog if permitted
+       pv::GlobalSettings settings;
+       if (settings.value(pv::GlobalSettings::Key_Log_NotifyOfStacktrace).toBool()) {
+               QCheckBox *cb = new QCheckBox(QObject::tr("Don't show this message again"));
+
+               QMessageBox msgbox;
+               msgbox.setText(QObject::tr("When %1 last crashed, it created a stack trace.\n" \
+                       "A human-readable form has been saved to disk and was written to " \
+                       "the log. You may access it from the settings dialog.").arg(PV_TITLE));
+               msgbox.setIcon(QMessageBox::Icon::Information);
+               msgbox.addButton(QMessageBox::Ok);
+               msgbox.setCheckBox(cb);
+
+               QObject::connect(cb, &QCheckBox::stateChanged, [](int state){
+                       pv::GlobalSettings settings;
+                       settings.setValue(pv::GlobalSettings::Key_Log_NotifyOfStacktrace,
+                               !state); });
+
+               msgbox.exec();
+       }
+}
+#endif
+
 void usage()
 {
        fprintf(stdout,
@@ -79,9 +162,11 @@ int main(int argc, char *argv[])
 {
        int ret = 0;
        shared_ptr<sigrok::Context> context;
-       string open_file, open_file_format, driver;
+       string open_file_format, driver;
+       vector<string> open_files;
        bool restore_sessions = true;
        bool do_scan = true;
+       bool show_version = false;
 
        Application a(argc, argv);
 
@@ -98,14 +183,16 @@ int main(int argc, char *argv[])
                        {"version", no_argument, nullptr, 'V'},
                        {"loglevel", required_argument, nullptr, 'l'},
                        {"driver", required_argument, nullptr, 'd'},
+                       {"no-scan", no_argument, nullptr, 'D'},
                        {"input-file", required_argument, nullptr, 'i'},
                        {"input-format", required_argument, nullptr, 'I'},
                        {"clean", no_argument, nullptr, 'c'},
+                       {"log-to-stdout", no_argument, nullptr, 's'},
                        {nullptr, 0, nullptr, 0}
                };
 
                const int c = getopt_long(argc, argv,
-                       "l:Vhc?d:Di:I:", long_options, nullptr);
+                       "h?VDcl:d:i:I:", long_options, nullptr);
                if (c == -1)
                        break;
 
@@ -116,9 +203,8 @@ int main(int argc, char *argv[])
                        return 0;
 
                case 'V':
-                       // Print version info
-                       fprintf(stdout, "%s %s\n", PV_TITLE, PV_VERSION_STRING);
-                       return 0;
+                       show_version = true;
+                       break;
 
                case 'l':
                {
@@ -150,7 +236,7 @@ int main(int argc, char *argv[])
                        break;
 
                case 'i':
-                       open_file = optarg;
+                       open_files.emplace_back(optarg);
                        break;
 
                case 'I':
@@ -162,19 +248,35 @@ int main(int argc, char *argv[])
                        break;
                }
        }
+       argc -= optind;
+       argv += optind;
 
-       if (argc - optind > 1) {
-               fprintf(stderr, "Only one file can be opened.\n");
-               return 1;
-       }
+       for (int i = 0; i < argc; i++)
+               open_files.emplace_back(argv[i]);
 
-       if (argc - optind == 1)
-               open_file = argv[argc - 1];
+       // Prepare the global settings since logging needs them early on
+       pv::GlobalSettings settings;
+       settings.set_defaults_where_needed();
+
+       pv::logging.init();
 
        // Initialise libsigrok
        context = sigrok::Context::create();
        pv::Session::sr_context = context;
 
+#if ENABLE_STACKTRACE
+       QString temp_path = QStandardPaths::standardLocations(
+               QStandardPaths::TempLocation).at(0);
+       stacktrace_filename = temp_path + "/pv_stacktrace.dmp";
+       qDebug() << "Stack trace file is" << stacktrace_filename;
+
+       ::signal(SIGSEGV, &signal_handler);
+       ::signal(SIGABRT, &signal_handler);
+
+       if (QFileInfo::exists(stacktrace_filename))
+               process_stacktrace(temp_path);
+#endif
+
 #ifdef ANDROID
        context->set_resource_reader(&asset_reader);
 #endif
@@ -191,10 +293,17 @@ int main(int argc, char *argv[])
                srd_decoder_load_all();
 #endif
 
+#ifndef ENABLE_STACKTRACE
                try {
-                       // Create the device manager, initialise the drivers
-                       pv::DeviceManager device_manager(context, driver, do_scan);
+#endif
 
+               // Create the device manager, initialise the drivers
+               pv::DeviceManager device_manager(context, driver, do_scan);
+
+               a.collect_version_info(context);
+               if (show_version) {
+                       a.print_version_info();
+               } else {
                        // Initialise the main window
                        pv::MainWindow w(device_manager);
                        w.show();
@@ -202,33 +311,32 @@ int main(int argc, char *argv[])
                        if (restore_sessions)
                                w.restore_sessions();
 
-                       if (!open_file.empty())
-                               w.add_session_with_file(open_file, open_file_format);
-                       else
+                       if (open_files.empty())
                                w.add_default_session();
+                       else
+                               for (string open_file : open_files)
+                                       w.add_session_with_file(open_file, open_file_format);
 
 #ifdef ENABLE_SIGNALS
                        if (SignalHandler::prepare_signals()) {
-                               SignalHandler *const handler =
-                                       new SignalHandler(&w);
-                               QObject::connect(handler,
-                                       SIGNAL(int_received()),
+                               SignalHandler *const handler = new SignalHandler(&w);
+                               QObject::connect(handler, SIGNAL(int_received()),
                                        &w, SLOT(close()));
-                               QObject::connect(handler,
-                                       SIGNAL(term_received()),
+                               QObject::connect(handler, SIGNAL(term_received()),
                                        &w, SLOT(close()));
-                       } else {
-                               qWarning() <<
-                                       "Could not prepare signal handler.";
-                       }
+                       } else
+                               qWarning() << "Could not prepare signal handler.";
 #endif
 
                        // Run the application
                        ret = a.exec();
+               }
 
+#ifndef ENABLE_STACKTRACE
                } catch (exception& e) {
-                       qDebug() << e.what();
+                       qDebug() << "Exception:" << e.what();
                }
+#endif
 
 #ifdef ENABLE_DECODE
                // Destroy libsigrokdecode