diff options
Diffstat (limited to 'gui')
| -rw-r--r-- | gui/init.cpp | 222 | ||||
| -rw-r--r-- | gui/init.hpp | 14 | ||||
| -rw-r--r-- | gui/main-window.cpp | 142 | ||||
| -rw-r--r-- | gui/main-window.hpp | 22 | 
4 files changed, 328 insertions, 72 deletions
| diff --git a/gui/init.cpp b/gui/init.cpp new file mode 100644 index 00000000..7ccc67fe --- /dev/null +++ b/gui/init.cpp @@ -0,0 +1,222 @@ +#include "init.hpp" + +#if defined(Q_CREATOR_RUN) +#   pragma clang diagnostic ignored "-Wmain" +#endif + +#include "migration/migration.hpp" +#include "gui/main-window.hpp" +#include "options/options.hpp" +using namespace options; +#include "opentrack-library-path.h" + +#include <memory> +#include <cstring> +#include <cstdio> +#include <cstdlib> + +#include <QApplication> +#include <QStyleFactory> +#include <QLocale> +#include <QTranslator> +#include <QApplication> +#include <QDir> +#include <QFile> +#include <QFileDialog> +#include <QString> +#include <QSysInfo> + +#include <QDebug> + +#if /* denormal control */ \ +    /* GNU */   defined __x86_64__  || defined __SSE2__ || \ +    /* MSVC */  defined _M_AMD64    || (defined _M_IX86_FP && _M_IX86_FP >= 2) +#   include <xmmintrin.h> +#   include <pmmintrin.h> +#   include <cfloat> + +#define OTR_HAS_DENORM_CONTROL +void set_fp_mask() +{ +    _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); +    _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); +    _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); +    _MM_SET_EXCEPTION_MASK(_MM_MASK_MASK); +} +#endif + +void set_qt_style() +{ +#if defined _WIN32 +    if (QSysInfo::WindowsVersion == QSysInfo::WV_XP) +        return; +#endif + +#if defined _WIN32 || defined __APPLE__ +    // our layouts on OSX make some control wrongly sized -sh 20160908 +    { +        const char* preferred[] { "fusion", "windowsvista", "macintosh" }; +        for (const char* style_name : preferred) +        { +            QStyle* s = QStyleFactory::create(style_name); +            if (s) +            { +                QApplication::setStyle(s); +                break; +            } +        } +    } +#endif +} + +#ifdef _WIN32 + +void qdebug_to_console(QtMsgType, const QMessageLogContext& ctx, const QString &msg) +{ +    const unsigned short* const str_ = msg.utf16(); +    auto str = reinterpret_cast<const wchar_t* const>(str_); +    static_assert(sizeof(*str_) == sizeof(*str), ""); + +    std::fflush(stderr); +    if (ctx.function) +        std::fprintf(stderr, "[%s:%d%s]: %ls\n", ctx.file, ctx.line, ctx.function, str); +    else if (ctx.file) +        std::fprintf(stderr, "[%s:%d]: %ls\n", ctx.file, ctx.line, str); +    else +        std::fprintf(stderr, "%ls\n", str); +    std::fflush(stderr); +} + +void attach_parent_console() +{ +    if (AttachConsole(ATTACH_PARENT_PROCESS)) +    { +        // XXX c++ iostreams aren't reopened + +        _wfreopen(L"CON", L"w", stdout); +        _wfreopen(L"CON", L"w", stderr); +        _wfreopen(L"CON", L"r", stdin); +    } +    (void)qInstallMessageHandler(qdebug_to_console); +} + +void add_win32_path() +{ +    // see https://software.intel.com/en-us/articles/limitation-to-the-length-of-the-system-path-variable +    static char env_path[4096] { '\0', }; +    { +        QString lib_path = OPENTRACK_BASE_PATH; +        lib_path.replace("/", "\\"); +        const QByteArray lib_path_ = QFile::encodeName(lib_path); + +        QString mod_path = OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH; +        mod_path.replace("/", "\\"); +        const QByteArray mod_path_ = QFile::encodeName(mod_path); + +        const char* contents[] { +            "PATH=", +            lib_path_.constData(), +            ";", +            mod_path_.constData(), +            ";", +            getenv("PATH"), +        }; + +        bool ok = true; + +        for (const char* ptr : contents) +        { +            if (ptr) +                strcat_s(env_path, sizeof(env_path), ptr); + +            if (!ptr || ptr[0] == '\0' || env_path[0] == '\0') +            { +                qDebug() << "bad path element" +                         << (ptr == nullptr ? "<null>" : ptr); +                ok = false; +                break; +            } +        } + +        if (ok) +        { +            const int error = _putenv(env_path); + +            if (error) +                qDebug() << "can't _putenv win32 path"; +        } +        else +            qDebug() << "can't set win32 path"; +    } +} + +#endif + +int run_window(QApplication& app, std::unique_ptr<QWidget> main_window) +{ +    if (!main_window->isEnabled()) +    { +        qDebug() << "exit before window created"; +        return 2; +    } + +    app.setQuitOnLastWindowClosed(false); + +    int ret = app.exec(); +    qDebug() << "exit" << ret; + +    return ret; +} + +int otr_main(int argc, char** argv, std::function<QWidget*()> make_main_window) +{ +#ifdef _WIN32 +    attach_parent_console(); +#endif + +#if defined OTR_HAS_DENORM_CONTROL +    set_fp_mask(); +#endif + +    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +    QCoreApplication::setAttribute(Qt::AA_X11InitThreads, true); + +    QApplication app(argc, argv); + +#ifdef _WIN32 +    add_win32_path(); +#endif + +    QDir::setCurrent(OPENTRACK_BASE_PATH); + +#if 0 +#if !defined(__linux) && !defined _WIN32 +    // workaround QTBUG-38598 +    QCoreApplication::addLibraryPath("."); +#endif +#endif + +    set_qt_style(); +    QTranslator t; + +    // QLocale::setDefault(QLocale("ru_RU")); // force i18n for testing + +    if (group::with_global_settings_object([&](QSettings& s) { +        return !s.value("disable-translation", false).toBool(); +    })) +    { +        (void) t.load(QLocale(), "", "", OPENTRACK_BASE_PATH + "/" OPENTRACK_I18N_PATH, ".qm"); +        (void) QCoreApplication::installTranslator(&t); +    } + +    int ret = run_window(app, std::unique_ptr<QWidget>(make_main_window())); + +    // msvc crashes in Qt plugin system's dtor +    // Note: QLibrary::PreventUnloadHint seems to workaround it +#if defined(_MSC_VER) && 0 +    qDebug() << "exit: terminating"; +    TerminateProcess(GetCurrentProcess(), 0); +#endif + +    return ret; +} diff --git a/gui/init.hpp b/gui/init.hpp new file mode 100644 index 00000000..d437b084 --- /dev/null +++ b/gui/init.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "export.hpp" + +#include <functional> +#include <QWidget> + +int OTR_GUI_EXPORT otr_main(int argc, char** argv, std::function<QWidget*()> make_main_window); + +template<typename F> +auto run_application(int argc, char** argv, F&& fun) +{ +    return otr_main(argc, argv, fun); +} diff --git a/gui/main-window.cpp b/gui/main-window.cpp index 1f84ea71..bc20cf59 100644 --- a/gui/main-window.cpp +++ b/gui/main-window.cpp @@ -15,73 +15,71 @@  #include "compat/check-visible.hpp"  #include "compat/sleep.hpp" -#include <QFile> -#include <QFileDialog> +#include <QMessageBox>  #include <QDesktopServices> -#include <QCoreApplication> -#include <QApplication> -#include <QIcon> -#include <QString> -#include <QChar> -#include <QSignalBlocker> - -#ifdef _WIN32 -#   include <windows.h> -#endif +#include <QDir>  extern "C" const char* const opentrack_version; +#if !defined EXIT_SUCCESS +#   define EXIT_SUCCESS 0 +#endif + +#if !defined EXIT_FAILURE +#   define EXIT_FAILURE 1 +#endif + +// + +/* FreeBSD sysexits(3) + * + * The input data was incorrect	in some	way.  This + * should only be used for user's data and not system + * files. + */ + +#if !defined EX_DATAERR +#   define EX_DATAERR 65 +#endif +  #if !defined _WIN32 && !defined __APPLE__  #   include <unistd.h>  void MainWindow::annoy_if_root()  {      if (geteuid() == 0)      { -        for (unsigned k = 0; k < 2; k++) -        { -            portable::sleep(1 * 1000); -            QMessageBox::critical(this, -                                  tr("Running as root is bad"), -                                  tr("Do not run as root. Set correct device node permissions."), -                                  QMessageBox::Ok); -            portable::sleep(1 * 1000); -            QMessageBox::critical(this, -                                  tr("Running as root is bad, seriously"), -                                  tr("Do not run as root. I'll keep whining at every startup."), -                                  QMessageBox::Ok); -            portable::sleep(3 * 1000); -            QMessageBox::critical(this, -                                  tr("Running as root is really seriously bad"), -                                  tr("Do not run as root. Be annoyed, comprehensively."), -                                  QMessageBox::Ok); -        } +        portable::sleep(4000); +        QMessageBox::critical(this, +                              tr("Running as root is bad"), +                              tr("Do not run as root. Set correct device node permissions."), +                              QMessageBox::Ok); +        portable::sleep(4000); +        QMessageBox::critical(this, +                              tr("Running as root is bad, seriously"), +                              tr("Do not run as root. I'll keep whining at every startup."), +                              QMessageBox::Ok); +        portable::sleep(4000); +        QMessageBox::critical(this, +                              tr("Running as root is really seriously bad"), +                              tr("Do not run as root. Be annoyed, comprehensively."), +                              QMessageBox::Ok);      }  }  #endif  main_window::main_window() : -    State(OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH), -    pose_update_timer(this), -    kbd_quit(QKeySequence("Ctrl+Q"), this), -    menu_action_header(&tray_menu), -    menu_action_show(&tray_menu), -    menu_action_exit(&tray_menu), -    menu_action_tracker(&tray_menu), -    menu_action_filter(&tray_menu), -    menu_action_proto(&tray_menu), -    menu_action_options(&tray_menu), -    menu_action_mappings(&tray_menu) +    State(OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH)  {      ui.setupUi(this); +    setAttribute(Qt::WA_QuitOnClose, true); +  #if !defined _WIN32 && !defined __APPLE__      annoy_if_root();  #endif -    { -        setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); -        setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | windowFlags()); -    } +    setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); +    setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | windowFlags());      update_button_state(false, false); @@ -92,7 +90,10 @@ main_window::main_window() :      }      if (!refresh_config_list()) +    { +        exit(EX_DATAERR);          return; +    }      connect(ui.btnEditCurves, SIGNAL(clicked()), this, SLOT(show_mapping_window()));      connect(ui.btnShortcuts, SIGNAL(clicked()), this, SLOT(show_options_dialog())); @@ -133,14 +134,15 @@ main_window::main_window() :          ui.profile_button->setMenu(&profile_menu);      } -    if (!progn( +    {          const QString cur = group::ini_filename(); -        if (is_config_listed(cur)) -            return set_profile(cur); -        else -            return set_profile(OPENTRACK_DEFAULT_CONFIG); -    )) -        return; +        bool ok = is_config_listed(cur) ? set_profile(cur) : set_profile(OPENTRACK_DEFAULT_CONFIG); +        if (!ok) +        { +            exit(EX_DATAERR); +            return; +        } +    }      // only tie and connect main screen options after migrations are done      // below is fine, set_profile() is called already @@ -162,9 +164,9 @@ main_window::main_window() :                  this,                  [&](const QString&) { if (pFilterDialog) pFilterDialog = nullptr; }); -        connect(&m.tracker_dll, base_value::value_changed<QString>(), this, &main_window::save_modules, Qt::QueuedConnection); -        connect(&m.protocol_dll, base_value::value_changed<QString>(), this, &main_window::save_modules, Qt::QueuedConnection); -        connect(&m.filter_dll, base_value::value_changed<QString>(), this, &main_window::save_modules, Qt::QueuedConnection); +        connect(&m.tracker_dll, base_value::value_changed<QString>(), this, &main_window::save_modules, Qt::DirectConnection); +        connect(&m.protocol_dll, base_value::value_changed<QString>(), this, &main_window::save_modules, Qt::DirectConnection); +        connect(&m.filter_dll, base_value::value_changed<QString>(), this, &main_window::save_modules, Qt::DirectConnection);          tie_setting(m.tracker_dll, ui.iconcomboTrackerSource);          tie_setting(m.protocol_dll, ui.iconcomboProtocol); @@ -202,6 +204,17 @@ main_window::main_window() :      det_timer.start(1000);      config_list_timer.start(1000 * 5);      kbd_quit.setEnabled(true); + +    adjustSize(); +    setFixedSize(size()); + +    if (!start_in_tray()) +    { +        setVisible(true); +        show(); +    } +    else +        setVisible(false);  }  void main_window::init_tray_menu() @@ -301,15 +314,15 @@ void main_window::die_on_config_not_writable()                            QMessageBox::Close, QMessageBox::NoButton);      // signals main() to short-circuit -    if (!isVisible()) -        setEnabled(false); +    //if (!isVisible()) +    //    setEnabled(false); -    setVisible(false); +    //setVisible(false);      // tray related -    qApp->setQuitOnLastWindowClosed(true); +    //qApp->setQuitOnLastWindowClosed(true); -    close(); +    exit(EX_DATAERR);  }  bool main_window::maybe_die_on_config_not_writable(const QString& current, QStringList* ini_list_) @@ -701,9 +714,11 @@ void main_window::show_mapping_window()      mk_window(mapping_widget, pose);  } -void main_window::exit() +void main_window::exit(int status)  { -    QCoreApplication::exit(0); +    setEnabled(false); +    close(); +    QCoreApplication::exit(status);  }  bool main_window::set_profile(const QString& new_name_) @@ -885,8 +900,9 @@ void main_window::changeEvent(QEvent* e)      }  } -void main_window::closeEvent(QCloseEvent*) +void main_window::closeEvent(QCloseEvent* ev)  { +    ev->accept();      exit();  } diff --git a/gui/main-window.hpp b/gui/main-window.hpp index 253c1194..402202d6 100644 --- a/gui/main-window.hpp +++ b/gui/main-window.hpp @@ -21,8 +21,7 @@  #include "logic/state.hpp"  #include "options/options.hpp" -#include <QObject> -#include <QWidget> +#include <QApplication>  #include <QMainWindow>  #include <QKeySequence>  #include <QShortcut> @@ -53,12 +52,12 @@ class OTR_GUI_EXPORT main_window : public QMainWindow, private State      module_settings m;      std::unique_ptr<QSystemTrayIcon> tray;      QMenu tray_menu; -    QTimer pose_update_timer; +    QTimer pose_update_timer { this };      QTimer det_timer;      QTimer config_list_timer;      std::unique_ptr<options_dialog> options_widget;      std::unique_ptr<mapping_dialog> mapping_widget; -    QShortcut kbd_quit; +    QShortcut kbd_quit { QKeySequence("Ctrl+Q"), this };      std::unique_ptr<IFilterDialog> pFilterDialog;      std::unique_ptr<IProtocolDialog> pProtocolDialog;      std::unique_ptr<ITrackerDialog> pTrackerDialog; @@ -66,9 +65,14 @@ class OTR_GUI_EXPORT main_window : public QMainWindow, private State      process_detector_worker det;      QMenu profile_menu; -    QAction menu_action_header, menu_action_show, menu_action_exit, -            menu_action_tracker, menu_action_filter, menu_action_proto, -            menu_action_options, menu_action_mappings; +    QAction menu_action_header   { &tray_menu }, +            menu_action_show     { &tray_menu }, +            menu_action_exit     { &tray_menu }, +            menu_action_tracker  { &tray_menu }, +            menu_action_filter   { &tray_menu }, +            menu_action_proto    { &tray_menu }, +            menu_action_options  { &tray_menu }, +            menu_action_mappings { &tray_menu };      std::shared_ptr<dylib> current_tracker()      { @@ -96,7 +100,7 @@ class OTR_GUI_EXPORT main_window : public QMainWindow, private State      void init_tray_menu();      void changeEvent(QEvent* e) override; -    void closeEvent(QCloseEvent*) override; +    void closeEvent(QCloseEvent* ev) override;      bool event(QEvent *event) override;      bool maybe_hide_to_tray(QEvent* e);  #if !defined _WIN32 && !defined __APPLE__ @@ -117,7 +121,7 @@ class OTR_GUI_EXPORT main_window : public QMainWindow, private State  private slots:      void save_modules(); -    void exit(); +    void exit(int status = 0);      bool set_profile(const QString& new_name);      void show_tracker_settings(); | 
