summaryrefslogtreecommitdiffhomepage
path: root/gui
diff options
context:
space:
mode:
Diffstat (limited to 'gui')
-rw-r--r--gui/init.cpp222
-rw-r--r--gui/init.hpp14
-rw-r--r--gui/main-window.cpp142
-rw-r--r--gui/main-window.hpp22
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();