summaryrefslogtreecommitdiffhomepage
path: root/gui
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2015-10-30 08:34:40 +0100
committerStanislaw Halik <sthalik@misaki.pl>2015-10-30 08:39:33 +0100
commit2160e39a4f3d5198eafbb483671583a5a0b51eaa (patch)
tree2370dd4e9a7b43c204d175af3ab2a0ff5b33c41d /gui
parentaa066bdd4622d4f6824fee864f6be6806813f04d (diff)
rename gui directory
Diffstat (limited to 'gui')
-rw-r--r--gui/CMakeLists.txt62
-rw-r--r--gui/curve-config.cpp81
-rw-r--r--gui/curve-config.h18
-rw-r--r--gui/facetracknoir.icobin0 -> 67134 bytes
-rw-r--r--gui/facetracknoir.rc2
-rw-r--r--gui/images/curves.pngbin0 -> 3457 bytes
-rw-r--r--gui/images/facetracknoir.pngbin0 -> 29485 bytes
-rw-r--r--gui/images/filter-16.pngbin0 -> 642 bytes
-rw-r--r--gui/images/no-feed.pngbin0 -> 4471 bytes
-rw-r--r--gui/images/settings16.pngbin0 -> 711 bytes
-rw-r--r--gui/images/tools.pngbin0 -> 3053 bytes
-rw-r--r--gui/keyboard.h50
-rw-r--r--gui/keyboard_listener.ui37
-rw-r--r--gui/main.cpp71
-rw-r--r--gui/main.ui1255
-rw-r--r--gui/mapping.ui391
-rw-r--r--gui/new_config.ui45
-rw-r--r--gui/new_file_dialog.h50
-rw-r--r--gui/options-dialog.cpp96
-rw-r--r--gui/options-dialog.hpp23
-rw-r--r--gui/process_detector.cpp211
-rw-r--r--gui/process_detector.h86
-rw-r--r--gui/process_widget.ui121
-rw-r--r--gui/settings.ui1109
-rw-r--r--gui/ui-res.qrc10
-rw-r--r--gui/ui.cpp567
-rw-r--r--gui/ui.h113
27 files changed, 4398 insertions, 0 deletions
diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt
new file mode 100644
index 00000000..51d0cea3
--- /dev/null
+++ b/gui/CMakeLists.txt
@@ -0,0 +1,62 @@
+opentrack_boilerplate(opentrack NO-LIBRARY)
+
+if(UNIX OR APPLE)
+ target_include_directories(opentrack "${CMAKE_SOURCE_DIR}/qxt-mini")
+ if(APPLE)
+ set(qxt-plat mac)
+ else()
+ set(qxt-plat x11)
+ endif()
+ file(GLOB qxt-mini-c
+ ${CMAKE_SOURCE_DIR}/qxt-mini/*.h
+ ${CMAKE_SOURCE_DIR}/qxt-mini/qxtglobalshortcut.cpp
+ ${CMAKE_SOURCE_DIR}/qxt-mini/plat/qxtglobalshortcut_${qxt-plat}.cpp
+ )
+ opentrack_qt(qxt-mini)
+ add_library(opentrack-qxt-mini STATIC ${qxt-mini-all})
+ target_link_libraries(opentrack-qxt-mini ${MY_QT_LIBS})
+ if(NOT APPLE)
+ target_link_libraries(opentrack-qxt-mini X11)
+ endif()
+endif()
+
+if(WIN32)
+ SET(SDK_CONSOLE_DEBUG FALSE CACHE BOOL "Console window visible at runtime")
+endif()
+
+if(WIN32 AND NOT SDK_CONSOLE_DEBUG)
+ set(opentrack-win32-executable WIN32)
+else()
+ set(opentrack-win32-executable "")
+endif()
+
+if(UNIX OR APPLE)
+ list(APPEND opentrack-c ${CMAKE_SOURCE_DIR}/qxt-mini/qxtglobalshortcut.h)
+endif()
+opentrack_qt(opentrack)
+add_executable(opentrack ${opentrack-win32-executable} ${opentrack-all})
+opentrack_compat(opentrack)
+if(NOT WIN32)
+ set_target_properties(opentrack PROPERTIES SUFFIX ".bin")
+endif()
+target_link_libraries(opentrack opentrack-api opentrack-version opentrack-pose-widget opentrack-spline-widget)
+if(APPLE)
+ SET_TARGET_PROPERTIES(opentrack-qxt-mini PROPERTIES LINK_FLAGS "-framework Carbon -framework CoreFoundation")
+endif()
+if(UNIX OR APPLE)
+ target_link_libraries(opentrack opentrack-qxt-mini)
+endif()
+link_with_dinput8(opentrack)
+target_link_libraries(opentrack ${MY_QT_LIBS})
+
+if(APPLE)
+ # for process detector
+ target_link_libraries(opentrack proc)
+endif()
+
+if(LINUX)
+ # for process detector
+ target_link_libraries(opentrack procps)
+endif()
+
+install(TARGETS opentrack DESTINATION .)
diff --git a/gui/curve-config.cpp b/gui/curve-config.cpp
new file mode 100644
index 00000000..2e9065b4
--- /dev/null
+++ b/gui/curve-config.cpp
@@ -0,0 +1,81 @@
+/* Copyright (c) 2014-2015, Stanislaw Halik <sthalik@misaki.pl>
+
+ * Permission to use, copy, modify, and/or distribute this
+ * software for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice and this permission
+ * notice appear in all copies.
+ */
+
+#include "curve-config.h"
+#include "opentrack/main-settings.hpp"
+MapWidget::MapWidget(Mappings& m, main_settings& s) :
+ m(m)
+{
+ ui.setupUi( this );
+
+ // rest of mapping settings taken care of by options::value<t>
+ m.load_mappings();
+
+ {
+ struct {
+ QFunctionConfigurator* qfc;
+ Axis axis;
+ QCheckBox* checkbox;
+ bool altp;
+ } qfcs[] =
+ {
+ { ui.rxconfig, Yaw, nullptr, false },
+ { ui.ryconfig, Pitch, nullptr, false },
+ { ui.rzconfig, Roll, nullptr, false },
+ { ui.txconfig, TX, nullptr, false },
+ { ui.tyconfig, TY, nullptr, false },
+ { ui.tzconfig, TZ, nullptr, false },
+
+ { ui.rxconfig_alt, Yaw, ui.rx_altp, true },
+ { ui.ryconfig_alt, Pitch, ui.ry_altp, true },
+ { ui.rzconfig_alt, Roll, ui.rz_altp, true },
+ { ui.txconfig_alt, TX, ui.tx_altp, true },
+ { ui.tyconfig_alt, TY, ui.ty_altp, true },
+ { ui.tzconfig_alt, TZ, ui.tz_altp, true },
+ { nullptr, Yaw, nullptr, false }
+ };
+
+ for (int i = 0; qfcs[i].qfc; i++)
+ {
+ const bool altp = qfcs[i].altp;
+ Mapping& axis = m(qfcs[i].axis);
+ Map* conf = altp ? &axis.curveAlt : &axis.curve;
+ const auto& name = qfcs[i].altp ? axis.name2 : axis.name1;
+ if (altp)
+ {
+ QFunctionConfigurator& qfc = *qfcs[i].qfc;
+ connect(qfcs[i].checkbox, &QCheckBox::toggled,
+ [&](bool f) -> void {qfc.setEnabled(f); qfc.force_redraw();});
+ qfc.setEnabled(qfcs[i].checkbox->isChecked());
+ qfc.force_redraw();
+ }
+ qfcs[i].qfc->setConfig(conf, name);
+ }
+ }
+
+ setFont(qApp->font());
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+
+ tie_setting(s.a_x.altp, ui.tx_altp);
+ tie_setting(s.a_y.altp, ui.ty_altp);
+ tie_setting(s.a_z.altp, ui.tz_altp);
+ tie_setting(s.a_yaw.altp, ui.rx_altp);
+ tie_setting(s.a_pitch.altp, ui.ry_altp);
+ tie_setting(s.a_roll.altp, ui.rz_altp);
+}
+
+void MapWidget::doOK() {
+ m.save_mappings();
+ this->close();
+}
+
+void MapWidget::doCancel() {
+ m.invalidate_unsaved();
+ this->close();
+}
diff --git a/gui/curve-config.h b/gui/curve-config.h
new file mode 100644
index 00000000..0cbc7055
--- /dev/null
+++ b/gui/curve-config.h
@@ -0,0 +1,18 @@
+#pragma once
+#include <QWidget>
+#include "opentrack/mappings.hpp"
+#include "ui_mapping.h"
+
+class MapWidget: public QWidget
+{
+ Q_OBJECT
+public:
+ MapWidget(Mappings &m, main_settings &s);
+private:
+ Ui::UICCurveConfigurationDialog ui;
+ Mappings& m;
+ void closeEvent(QCloseEvent *) override { doCancel(); }
+private slots:
+ void doOK();
+ void doCancel();
+};
diff --git a/gui/facetracknoir.ico b/gui/facetracknoir.ico
new file mode 100644
index 00000000..5cac8da1
--- /dev/null
+++ b/gui/facetracknoir.ico
Binary files differ
diff --git a/gui/facetracknoir.rc b/gui/facetracknoir.rc
new file mode 100644
index 00000000..020ffe97
--- /dev/null
+++ b/gui/facetracknoir.rc
@@ -0,0 +1,2 @@
+#include <windows.h>
+IDI_ICON1 ICON "facetracknoir.ico"
diff --git a/gui/images/curves.png b/gui/images/curves.png
new file mode 100644
index 00000000..3f953a0a
--- /dev/null
+++ b/gui/images/curves.png
Binary files differ
diff --git a/gui/images/facetracknoir.png b/gui/images/facetracknoir.png
new file mode 100644
index 00000000..85c06df6
--- /dev/null
+++ b/gui/images/facetracknoir.png
Binary files differ
diff --git a/gui/images/filter-16.png b/gui/images/filter-16.png
new file mode 100644
index 00000000..ecde6a10
--- /dev/null
+++ b/gui/images/filter-16.png
Binary files differ
diff --git a/gui/images/no-feed.png b/gui/images/no-feed.png
new file mode 100644
index 00000000..02aa227a
--- /dev/null
+++ b/gui/images/no-feed.png
Binary files differ
diff --git a/gui/images/settings16.png b/gui/images/settings16.png
new file mode 100644
index 00000000..3b31623b
--- /dev/null
+++ b/gui/images/settings16.png
Binary files differ
diff --git a/gui/images/tools.png b/gui/images/tools.png
new file mode 100644
index 00000000..2da8f9f5
--- /dev/null
+++ b/gui/images/tools.png
Binary files differ
diff --git a/gui/keyboard.h b/gui/keyboard.h
new file mode 100644
index 00000000..62a9ce20
--- /dev/null
+++ b/gui/keyboard.h
@@ -0,0 +1,50 @@
+#pragma once
+#include "ui_keyboard_listener.h"
+#ifdef _WIN32
+#include "opentrack/win32-shortcuts.h"
+#include "opentrack/shortcuts.h"
+#endif
+#include <QLabel>
+#include <QKeyEvent>
+#include <QDebug>
+
+class KeyboardListener : public QLabel
+{
+ Q_OBJECT
+ Ui_keyboard_listener ui;
+#ifdef _WIN32
+ KeybindingWorker w;
+#endif
+public:
+ KeyboardListener(QWidget* parent = nullptr) : QLabel(parent)
+#ifdef _WIN32
+ , w([&](Key& k)
+ {
+ Qt::KeyboardModifiers m;
+ QKeySequence k_;
+ if (win_key::to_qt(k, k_, m))
+ key_pressed(static_cast<QVariant>(k_).toInt() | m);
+ }, this->winId())
+#endif
+ {
+ ui.setupUi(this);
+ setFocusPolicy(Qt::StrongFocus);
+#ifdef _WIN32
+ w.start();
+#endif
+ }
+#ifndef _WIN32
+ void keyPressEvent(QKeyEvent* event) override
+ {
+ //qDebug() << "k" << (event->key() | event->modifiers());
+ switch (event->key() | event->modifiers())
+ {
+ default:
+ emit key_pressed(QKeySequence(event->key() | event->modifiers()));
+ break;
+ }
+ }
+#endif
+signals:
+ void key_pressed(QKeySequence k);
+};
diff --git a/gui/keyboard_listener.ui b/gui/keyboard_listener.ui
new file mode 100644
index 00000000..b6977df0
--- /dev/null
+++ b/gui/keyboard_listener.ui
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>keyboard_listener</class>
+ <widget class="QLabel" name="keyboard_listener">
+ <property name="windowModality">
+ <enum>Qt::ApplicationModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>224</width>
+ <height>33</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Bind a shortcut</string>
+ </property>
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Press a key or close this window to remove the keybinding.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="margin">
+ <number>10</number>
+ </property>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/main.cpp b/gui/main.cpp
new file mode 100644
index 00000000..a63fe54a
--- /dev/null
+++ b/gui/main.cpp
@@ -0,0 +1,71 @@
+#ifdef _WIN32
+# include <stdlib.h>
+#endif
+
+#include "ui.h"
+#include "opentrack/options.hpp"
+using namespace options;
+#include <QApplication>
+#include <QCommandLineParser>
+#include <QStyleFactory>
+#include <QStringList>
+#include <QMessageBox>
+#include <memory>
+#include <cstring>
+
+#ifdef _WIN32
+// workaround QTBUG-38598, allow for launching from another directory
+static void add_program_library_path()
+{
+ char* p = _pgmptr;
+ char path[MAX_PATH+1];
+ strcpy(path, p);
+ char* ptr = strrchr(path, '\\');
+ if (ptr)
+ {
+ *ptr = '\0';
+ QCoreApplication::addLibraryPath(path);
+ }
+}
+#endif
+
+int main(int argc, char** argv)
+{
+#ifdef _WIN32
+ add_program_library_path();
+#elif !defined(__linux)
+ // workaround QTBUG-38598
+ QCoreApplication::addLibraryPath(".");
+#endif
+
+#if defined(_WIN32) || defined(__APPLE__)
+ // qt5 designer-made controls look like shit on 'doze -sh 20140921
+ // also our OSX look leaves a lot to be desired -sh 20150726
+ {
+ const QStringList preferred { "fusion", "windowsvista", "macintosh", "windowsxp" };
+ for (const auto& style_name : preferred)
+ {
+ QStyle* s = QStyleFactory::create(style_name);
+ if (s)
+ {
+ QApplication::setStyle(s);
+ break;
+ }
+ }
+ }
+#endif
+
+ QApplication::setAttribute(Qt::AA_X11InitThreads, true);
+ QApplication app(argc, argv);
+
+ auto w = std::make_shared<MainWindow>();
+
+ w->show();
+ app.exec();
+
+ // on MSVC crashes in atexit
+#ifdef _MSC_VER
+ TerminateProcess(GetCurrentProcess(), 0);
+#endif
+ return 0;
+}
diff --git a/gui/main.ui b/gui/main.ui
new file mode 100644
index 00000000..ab63e832
--- /dev/null
+++ b/gui/main.ui
@@ -0,0 +1,1255 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <author>Lovecraftian Octopus</author>
+ <class>OpentrackUI</class>
+ <widget class="QMainWindow" name="OpentrackUI">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>707</width>
+ <height>494</height>
+ </rect>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="ui-res.qrc">
+ <normaloff>:/images/facetracknoir.png</normaloff>:/images/facetracknoir.png</iconset>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">#video_feed { border: 0; }
+</string>
+ </property>
+ <widget class="QWidget" name="content">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="frame">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QFrame" name="top">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="video_feed">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>480</width>
+ <height>360</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>480</width>
+ <height>360</height>
+ </size>
+ </property>
+ <widget class="QFrame" name="video_frame">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>480</width>
+ <height>360</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <widget class="QLabel" name="video_frame_label">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>480</width>
+ <height>360</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>480</width>
+ <height>360</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>480</width>
+ <height>360</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFrame" name="top_display">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>12</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>12</number>
+ </property>
+ <property name="rightMargin">
+ <number>8</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
+ <widget class="GLWidget" name="pose_display" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>80</width>
+ <height>90</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="box_raw_headpose">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string notr="true">Raw tracker data</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_12">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>3</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>2</number>
+ </property>
+ <item row="0" column="1">
+ <widget class="QLCDNumber" name="raw_x">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Outline</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="lblRotX_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>yaw</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="lblRotZ_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>roll</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="lblZ_4">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>TZ</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="lblRotY_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>pitch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLCDNumber" name="raw_y">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Outline</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLCDNumber" name="raw_yaw">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Outline</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QLCDNumber" name="raw_roll">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Outline</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLCDNumber" name="raw_z">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Outline</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblX_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>TX</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLCDNumber" name="raw_pitch">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Outline</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="lblY_4">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>TY</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="box_mapped_headpose">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string notr="true">Game data</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_14">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>3</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>2</number>
+ </property>
+ <item row="1" column="0">
+ <widget class="QLabel" name="lblY_2">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>TY</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="lblRotY_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>pitch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLCDNumber" name="pose_x">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="lblRotX_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>yaw</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="lblRotZ_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>roll</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="lblZ_2">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>TZ</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLCDNumber" name="pose_y">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QLCDNumber" name="pose_roll">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLCDNumber" name="pose_yaw">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLCDNumber" name="pose_z">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblx">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string>TX</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLCDNumber" name="pose_pitch">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="smallDecimalPoint">
+ <bool>true</bool>
+ </property>
+ <property name="digitCount">
+ <number>4</number>
+ </property>
+ <property name="segmentStyle">
+ <enum>QLCDNumber::Flat</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QFrame" name="bottom_controls">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="topMargin">
+ <number>8</number>
+ </property>
+ <property name="rightMargin">
+ <number>10</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>4</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="1" column="2">
+ <widget class="QGroupBox" name="groupWindows">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>4</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Settings</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="groupProfile">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>2</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>2</number>
+ </property>
+ <property name="bottomMargin">
+ <number>2</number>
+ </property>
+ <item>
+ <widget class="QToolButton" name="profile_button">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Profile</string>
+ </property>
+ <property name="popupMode">
+ <enum>QToolButton::InstantPopup</enum>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextBesideIcon</enum>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ <property name="arrowType">
+ <enum>Qt::DownArrow</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="iconcomboProfile">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxVisibleItems">
+ <number>20</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnEditCurves">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>3</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Mapping</string>
+ </property>
+ <property name="icon">
+ <iconset resource="ui-res.qrc">
+ <normaloff>:/images/curves.png</normaloff>:/images/curves.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>80</width>
+ <height>24</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnShortcuts">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>3</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Options</string>
+ </property>
+ <property name="icon">
+ <iconset resource="ui-res.qrc">
+ <normaloff>:/images/tools.png</normaloff>:/images/tools.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>80</width>
+ <height>24</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QGroupBox" name="groupStartStop">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>3</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string notr="true">Controls</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <property name="spacing">
+ <number>8</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>8</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QToolButton" name="btnStartTracker">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Start</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="btnStopTracker">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Stop</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QFrame" name="frame_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>4</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>8</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupTrackerSource">
+ <property name="title">
+ <string>Tracker</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>3</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QComboBox" name="iconcomboTrackerSource"/>
+ </item>
+ <item row="0" column="1">
+ <widget class="QToolButton" name="btnShowEngineControls">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="flat" stdset="0">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupGameProtocol">
+ <property name="title">
+ <string>Protocol</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>3</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QComboBox" name="iconcomboProtocol"/>
+ </item>
+ <item row="0" column="1">
+ <widget class="QToolButton" name="btnShowServerControls">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="flat" stdset="0">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupFilter">
+ <property name="title">
+ <string>Filter</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>3</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QComboBox" name="iconcomboFilter"/>
+ </item>
+ <item row="0" column="1">
+ <widget class="QToolButton" name="btnShowFilterControls">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="flat" stdset="0">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>GLWidget</class>
+ <extends>QWidget</extends>
+ <header>pose-widget/glwidget.h</header>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>btnStartTracker</tabstop>
+ <tabstop>btnStopTracker</tabstop>
+ <tabstop>iconcomboTrackerSource</tabstop>
+ <tabstop>btnShowEngineControls</tabstop>
+ <tabstop>iconcomboProtocol</tabstop>
+ <tabstop>btnShowServerControls</tabstop>
+ <tabstop>iconcomboFilter</tabstop>
+ <tabstop>btnShowFilterControls</tabstop>
+ <tabstop>profile_button</tabstop>
+ <tabstop>iconcomboProfile</tabstop>
+ <tabstop>btnEditCurves</tabstop>
+ <tabstop>btnShortcuts</tabstop>
+ </tabstops>
+ <resources>
+ <include location="ui-res.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/gui/mapping.ui b/gui/mapping.ui
new file mode 100644
index 00000000..75c32e27
--- /dev/null
+++ b/gui/mapping.ui
@@ -0,0 +1,391 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICCurveConfigurationDialog</class>
+ <widget class="QWidget" name="UICCurveConfigurationDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>970</width>
+ <height>664</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>970</width>
+ <height>664</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>970</width>
+ <height>664</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Mapping properties</string>
+ </property>
+ <property name="windowIcon">
+ <iconset>
+ <normaloff>images/facetracknoir.png</normaloff>images/facetracknoir.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background-color: #ccc;</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="tabPosition">
+ <enum>QTabWidget::North</enum>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tabWidgetPage1">
+ <attribute name="title">
+ <string>Yaw</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QFunctionConfigurator" name="rxconfig" native="true">
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>255</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="rx_altp">
+ <property name="text">
+ <string>Asymmetric mapping below</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFunctionConfigurator" name="rxconfig_alt" native="true">
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>255</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage2">
+ <attribute name="title">
+ <string>Pitch</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QFunctionConfigurator" name="ryconfig" native="true">
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>0</red>
+ <green>255</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="ry_altp">
+ <property name="text">
+ <string>Asymmetric mapping below</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFunctionConfigurator" name="ryconfig_alt" native="true">
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>0</red>
+ <green>255</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage3">
+ <attribute name="title">
+ <string>Roll</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QFunctionConfigurator" name="rzconfig" native="true">
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="rz_altp">
+ <property name="text">
+ <string>Asymmetric mapping below</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFunctionConfigurator" name="rzconfig_alt" native="true">
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage4">
+ <attribute name="title">
+ <string>X</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <widget class="QFunctionConfigurator" name="txconfig" native="true">
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>255</red>
+ <green>0</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="tx_altp">
+ <property name="text">
+ <string>Asymmetric mapping below</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFunctionConfigurator" name="txconfig_alt" native="true">
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>255</red>
+ <green>0</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage5">
+ <attribute name="title">
+ <string>Y</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
+ <widget class="QFunctionConfigurator" name="tyconfig" native="true">
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="ty_altp">
+ <property name="text">
+ <string>Asymmetric mapping below</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFunctionConfigurator" name="tyconfig_alt" native="true">
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage6">
+ <attribute name="title">
+ <string>Z</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_7">
+ <item>
+ <widget class="QFunctionConfigurator" name="tzconfig" native="true">
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>0</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="tz_altp">
+ <property name="text">
+ <string>Asymmetric mapping below</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFunctionConfigurator" name="tzconfig_alt" native="true">
+ <property name="colorBezier" stdset="0">
+ <color>
+ <red>0</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="colorBackground" stdset="0">
+ <color>
+ <red>240</red>
+ <green>240</green>
+ <blue>240</blue>
+ </color>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QFunctionConfigurator</class>
+ <extends>QWidget</extends>
+ <header>spline-widget/qfunctionconfigurator.h</header>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>ry_altp</tabstop>
+ <tabstop>rz_altp</tabstop>
+ <tabstop>tx_altp</tabstop>
+ <tabstop>ty_altp</tabstop>
+ <tabstop>tz_altp</tabstop>
+ <tabstop>tabWidget</tabstop>
+ <tabstop>buttonBox</tabstop>
+ <tabstop>rx_altp</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/gui/new_config.ui b/gui/new_config.ui
new file mode 100644
index 00000000..27dce0f8
--- /dev/null
+++ b/gui/new_config.ui
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UI_new_config</class>
+ <widget class="QDialog" name="UI_new_config">
+ <property name="windowModality">
+ <enum>Qt::ApplicationModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>269</width>
+ <height>67</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Config filename</string>
+ </property>
+ <property name="windowIcon">
+ <iconset>
+ <normaloff>images/facetracknoir.png</normaloff>images/facetracknoir.png</iconset>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>New file name:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="lineEdit"/>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/new_file_dialog.h b/gui/new_file_dialog.h
new file mode 100644
index 00000000..3a35cf71
--- /dev/null
+++ b/gui/new_file_dialog.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "ui_new_config.h"
+#include "opentrack/options.hpp"
+#include <QFile>
+#include <QRegExp>
+#include <QString>
+#include <QMessageBox>
+
+class new_file_dialog : public QDialog
+{
+ Q_OBJECT
+public:
+ new_file_dialog(QWidget* parent = 0) : QDialog(parent), ok(false)
+ {
+ ui.setupUi(this);
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(ok_clicked()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(cancel_clicked()));
+ setFixedSize(size());
+ }
+ bool is_ok(QString& name_)
+ {
+ name_ = name;
+ return ok;
+ }
+private:
+ Ui::UI_new_config ui;
+ bool ok;
+ QString name;
+private slots:
+ void cancel_clicked() { close(); }
+ void ok_clicked()
+ {
+ QString text = ui.lineEdit->text();
+ text = text.replace('/', "");
+ text = text.replace('\\', "");
+ if (text != "" && !text.endsWith(".ini"))
+ text += ".ini";
+ if (text == "" || text == ".ini" || QFile(options::group::ini_directory() + "/" + text).exists())
+ {
+ QMessageBox::warning(this,
+ "File exists", "This file already exists. Pick another name.",
+ QMessageBox::Ok, QMessageBox::NoButton);
+ return;
+ }
+ ok = true;
+ close();
+ name = text;
+ }
+};
diff --git a/gui/options-dialog.cpp b/gui/options-dialog.cpp
new file mode 100644
index 00000000..fef2b0ca
--- /dev/null
+++ b/gui/options-dialog.cpp
@@ -0,0 +1,96 @@
+/* Copyright (c) 2015, Stanislaw Halik <sthalik@misaki.pl>
+
+ * Permission to use, copy, modify, and/or distribute this
+ * software for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice and this permission
+ * notice appear in all copies.
+ */
+
+#include "options-dialog.hpp"
+#include "keyboard.h"
+#include <QPushButton>
+#include <QLayout>
+
+OptionsDialog::OptionsDialog()
+{
+ ui.setupUi( this );
+
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+
+ tie_setting(s.s_main.tray_enabled, ui.trayp);
+
+ tie_setting(s.s_main.center_at_startup, ui.center_at_startup);
+
+ tie_setting(s.s_main.tcomp_p, ui.tcomp_enable);
+ tie_setting(s.s_main.tcomp_tz, ui.tcomp_rz);
+
+ tie_setting(s.s_main.a_x.zero, ui.pos_tx);
+ tie_setting(s.s_main.a_y.zero, ui.pos_ty);
+ tie_setting(s.s_main.a_z.zero, ui.pos_tz);
+ tie_setting(s.s_main.a_yaw.zero, ui.pos_rx);
+ tie_setting(s.s_main.a_pitch.zero, ui.pos_ry);
+ tie_setting(s.s_main.a_roll.zero, ui.pos_rz);
+
+ tie_setting(s.s_main.a_yaw.invert, ui.invert_yaw);
+ tie_setting(s.s_main.a_pitch.invert, ui.invert_pitch);
+ tie_setting(s.s_main.a_roll.invert, ui.invert_roll);
+ tie_setting(s.s_main.a_x.invert, ui.invert_x);
+ tie_setting(s.s_main.a_y.invert, ui.invert_y);
+ tie_setting(s.s_main.a_z.invert, ui.invert_z);
+
+ tie_setting(s.s_main.a_yaw.src, ui.src_yaw);
+ tie_setting(s.s_main.a_pitch.src, ui.src_pitch);
+ tie_setting(s.s_main.a_roll.src, ui.src_roll);
+ tie_setting(s.s_main.a_x.src, ui.src_x);
+ tie_setting(s.s_main.a_y.src, ui.src_y);
+ tie_setting(s.s_main.a_z.src, ui.src_z);
+
+ tie_setting(s.s_main.camera_yaw, ui.camera_yaw);
+ tie_setting(s.s_main.camera_pitch, ui.camera_pitch);
+ tie_setting(s.s_main.camera_roll, ui.camera_roll);
+
+ tie_setting(s.s_main.center_method, ui.center_method);
+
+ connect(ui.bind_center, &QPushButton::pressed, [&]() -> void { bind_key(s.center.keycode, ui.center_text); });
+ connect(ui.bind_zero, &QPushButton::pressed, [&]() -> void { bind_key(s.zero.keycode, ui.zero_text); });
+ connect(ui.bind_toggle, &QPushButton::pressed, [&]() -> void { bind_key(s.toggle.keycode, ui.toggle_text); });
+
+ ui.center_text->setText(s.center.keycode == "" ? "None" : static_cast<QString>(s.center.keycode));
+ ui.toggle_text->setText(s.toggle.keycode == "" ? "None" : static_cast<QString>(s.toggle.keycode));
+ ui.zero_text->setText(s.zero.keycode == "" ? "None" : static_cast<QString>(s.zero.keycode));
+}
+
+void OptionsDialog::bind_key(value<QString>& ret, QLabel* label)
+{
+ ret = "";
+ QDialog d;
+ auto l = new QHBoxLayout;
+ l->setMargin(0);
+ auto k = new KeyboardListener;
+ l->addWidget(k);
+ d.setLayout(l);
+ d.setFixedSize(QSize(500, 300));
+ d.setWindowFlags(Qt::Dialog);
+ connect(k, &KeyboardListener::key_pressed, [&] (QKeySequence s) -> void { ret = s.toString(QKeySequence::PortableText); d.close(); });
+ d.exec();
+ label->setText(ret == "" ? "None" : static_cast<QString>(ret));
+ delete k;
+ delete l;
+}
+
+void OptionsDialog::doOK() {
+ s.b->save();
+ s.s_main.b->save();
+ ui.game_detector->save();
+ this->close();
+ emit reload();
+}
+
+void OptionsDialog::doCancel() {
+ s.b->reload();
+ s.s_main.b->reload();
+ ui.game_detector->revert();
+ close();
+}
+
diff --git a/gui/options-dialog.hpp b/gui/options-dialog.hpp
new file mode 100644
index 00000000..3ef99d06
--- /dev/null
+++ b/gui/options-dialog.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <QObject>
+#include <QWidget>
+#include "ui_settings.h"
+#include "opentrack/shortcuts.h"
+
+class OptionsDialog: public QWidget
+{
+ Q_OBJECT
+signals:
+ void reload();
+public:
+ OptionsDialog();
+private:
+ Ui::UI_Settings ui;
+ Shortcuts::settings s;
+ void closeEvent(QCloseEvent *) override { doCancel(); }
+private slots:
+ void doOK();
+ void doCancel();
+ void bind_key(value<QString>& ret, QLabel* label);
+};
diff --git a/gui/process_detector.cpp b/gui/process_detector.cpp
new file mode 100644
index 00000000..f9fa4b59
--- /dev/null
+++ b/gui/process_detector.cpp
@@ -0,0 +1,211 @@
+/* Copyright (c) 2015, Stanislaw Halik <sthalik@misaki.pl>
+
+ * Permission to use, copy, modify, and/or distribute this
+ * software for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice and this permission
+ * notice appear in all copies.
+ */
+
+#include "process_detector.h"
+#include "ui.h"
+#include "opentrack-compat/process-list.hpp"
+#include <QList>
+#include <QFileDialog>
+#include <QComboBox>
+#include <QString>
+#include <QHash>
+#include <QPushButton>
+
+void settings::set_game_list(const QString &game_list)
+{
+ QSettings settings(OPENTRACK_ORG);
+ settings.setValue("executable-list", game_list);
+}
+
+QString settings::get_game_list()
+{
+ QSettings settings(OPENTRACK_ORG);
+ return settings.value("executable-list").toString();
+}
+
+bool settings::is_enabled()
+{
+ QSettings settings(OPENTRACK_ORG);
+ return settings.value("executable-detector-enabled", false).toBool();
+}
+
+void settings::set_is_enabled(bool enabled)
+{
+ QSettings settings(OPENTRACK_ORG);
+ settings.setValue("executable-detector-enabled", enabled);
+}
+
+QHash<QString, QString> settings::split_process_names()
+{
+ QHash<QString, QString> ret;
+ QString str = get_game_list();
+ QStringList pairs = str.split('|');
+ for (auto pair : pairs)
+ {
+ QList<QString> tmp = pair.split(':');
+ if (tmp.count() != 2)
+ continue;
+ ret[tmp[0]] = tmp[1];
+ }
+ return ret;
+}
+
+void BrowseButton::browse()
+{
+ QFileDialog dialog(this);
+ dialog.setFileMode(QFileDialog::ExistingFile);
+ QString dir_path = QFileInfo(group::ini_pathname()).absolutePath();
+ QString filename = dialog.getOpenFileName(
+ this,
+ tr("Set executable name"),
+ dir_path,
+ tr("Executable (*.exe);;All Files (*)"));
+ MainWindow::set_working_directory();
+ filename = QFileInfo(filename).fileName();
+ if (!filename.isNull())
+ twi->setText(filename);
+}
+
+int process_detector::add_row(QString exe_name, QString profile)
+{
+ int i = ui.tableWidget->rowCount();
+ ui.tableWidget->insertRow(i);
+
+ QComboBox* cb = new QComboBox();
+ cb->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum);
+ cb->addItem("");
+ cb->addItems(group::ini_list());
+ ui.tableWidget->setCellWidget(i, 1, cb);
+
+ QTableWidgetItem* twi = new QTableWidgetItem(exe_name);
+ ui.tableWidget->setItem(i, 0, twi);
+
+ {
+ BrowseButton* b = new BrowseButton(twi);
+ b->setText("...");
+ QObject::connect(b, SIGNAL(pressed()), b, SLOT(browse()));
+ ui.tableWidget->setCellWidget(i, 2, b);
+ }
+
+ cb->setCurrentText(profile);
+
+ return i;
+}
+
+void process_detector::add_items()
+{
+ auto names = s.split_process_names();
+ ui.tableWidget->clearContents();
+ auto keys = names.keys();
+ qSort(keys);
+ for (auto n : keys)
+ add_row(n, names[n]);
+}
+
+process_detector::process_detector(QWidget* parent) : QWidget(parent)
+{
+ ui.setupUi(this);
+ setFixedSize(size());
+ connect(ui.add, SIGNAL(pressed()), this, SLOT(add()));
+ connect(ui.remove, SIGNAL(pressed()), this, SLOT(remove()));
+
+ add_items();
+
+ QResizeEvent e(ui.tableWidget->size(), ui.tableWidget->size());
+ ui.tableWidget->resizeEvent(&e);
+
+ settings s;
+
+ ui.enabled->setChecked(s.is_enabled());
+}
+
+void process_detector::save()
+{
+ QString str;
+
+ for (int i = 0; i < ui.tableWidget->rowCount(); i++)
+ {
+ auto exe = ui.tableWidget->item(i, 0)->text();
+ auto profile = reinterpret_cast<QComboBox*>(ui.tableWidget->cellWidget(i, 1))->currentText();
+ str += "|" + exe + ":" + profile;
+ }
+
+ s.set_game_list(str);
+ s.set_is_enabled(ui.enabled->isChecked());
+}
+
+void process_detector::revert()
+{
+}
+
+void process_detector::add()
+{
+ add_row();
+}
+
+void process_detector::remove()
+{
+ int r = ui.tableWidget->currentRow();
+ if (r != -1)
+ ui.tableWidget->removeRow(r);
+}
+
+bool process_detector_worker::should_stop()
+{
+ if (last_exe_name == "")
+ return false;
+
+ settings s;
+
+ if (!s.is_enabled())
+ {
+ last_exe_name = "";
+ return false;
+ }
+
+ QStringList exe_list = get_all_executable_names();
+
+ if (exe_list.contains(last_exe_name))
+ return false;
+
+ last_exe_name = "";
+
+ return true;
+}
+
+bool process_detector_worker::config_to_start(QString& str)
+{
+ settings s;
+ if (!s.is_enabled())
+ {
+ last_exe_name = "";
+ return false;
+ }
+
+ auto filenames = s.split_process_names();
+ QStringList exe_list = get_all_executable_names();
+
+ // assuming manual stop by user button click.
+ // don't automatically start again while the same process is running.
+ if (last_exe_name != "" && exe_list.contains(last_exe_name))
+ return false;
+ // it's gone, we can start automatically again
+ last_exe_name = "";
+
+ for (auto& name : exe_list)
+ {
+ if (filenames.contains(name))
+ {
+ last_exe_name = name;
+ str = filenames[name];
+ return str != "";
+ }
+ }
+
+ return false;
+}
diff --git a/gui/process_detector.h b/gui/process_detector.h
new file mode 100644
index 00000000..f6497c90
--- /dev/null
+++ b/gui/process_detector.h
@@ -0,0 +1,86 @@
+/* Copyright (c) 2015, Stanislaw Halik <sthalik@misaki.pl>
+
+ * Permission to use, copy, modify, and/or distribute this
+ * software for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice and this permission
+ * notice appear in all copies.
+ */
+
+#pragma once
+
+#include <QObject>
+#include <QWidget>
+#include <QTableWidget>
+#include <QResizeEvent>
+
+#include "opentrack/options.hpp"
+using namespace options;
+
+class FancyTable : public QTableWidget
+{
+ Q_OBJECT
+public:
+ void resizeEvent(QResizeEvent* e) override
+ {
+ QTableView::resizeEvent(e);
+ int w = width();
+ setColumnWidth(2, 32);
+ w -= 48;
+ setColumnWidth(0, w / 2);
+ setColumnWidth(1, w / 2);
+ }
+public:
+ FancyTable(QWidget* parent = nullptr) : QTableWidget(parent) {}
+};
+
+struct settings
+{
+ QHash<QString, QString> split_process_names();
+ QString get_game_list();
+ void set_game_list(const QString& game_list);
+ bool is_enabled();
+ void set_is_enabled(bool enabled);
+};
+
+#include "ui_process_widget.h"
+
+class process_detector : public QWidget
+{
+ Q_OBJECT
+
+ Ui_Dialog ui;
+ settings s;
+
+ int add_row(QString exe_name = "...", QString profile = "");
+ void add_items();
+public:
+ process_detector(QWidget* parent = nullptr);
+public slots:
+ void save();
+ void revert();
+private slots:
+ void add();
+ void remove();
+};
+
+class BrowseButton : public QPushButton
+{
+ Q_OBJECT
+ QTableWidgetItem* twi;
+public:
+ BrowseButton(QTableWidgetItem* twi) : twi(twi)
+ {}
+public slots:
+ void browse();
+};
+
+class process_detector_worker : QObject
+{
+ Q_OBJECT
+ settings s;
+ QString last_exe_name;
+public:
+ bool config_to_start(QString& s);
+ bool should_stop();
+};
+
diff --git a/gui/process_widget.ui b/gui/process_widget.ui
new file mode 100644
index 00000000..63ad9472
--- /dev/null
+++ b/gui/process_widget.ui
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Dialog</class>
+ <widget class="QWidget" name="Dialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>302</width>
+ <height>325</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Game detector</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="FancyTable" name="tableWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <column>
+ <property name="text">
+ <string>Executable</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Profile</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string/>
+ </property>
+ </column>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">border: 0;</string>
+ </property>
+ <property name="title">
+ <string/>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="topMargin">
+ <number>5</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="add">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>14</pointsize>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>+</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="remove">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>14</pointsize>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="enabled">
+ <property name="text">
+ <string>Start profiles from game executable names in this list</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>FancyTable</class>
+ <extends>QTableWidget</extends>
+ <header>process_detector.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/settings.ui b/gui/settings.ui
new file mode 100644
index 00000000..e277b5d8
--- /dev/null
+++ b/gui/settings.ui
@@ -0,0 +1,1109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UI_Settings</class>
+ <widget class="QWidget" name="UI_Settings">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>348</width>
+ <height>548</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Options</string>
+ </property>
+ <property name="windowIcon">
+ <iconset>
+ <normaloff>images/facetracknoir.png</normaloff>images/facetracknoir.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>Shortcuts</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox_8">
+ <property name="title">
+ <string>Global shortcuts</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_8">
+ <item>
+ <widget class="QLabel" name="label_23">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Center&lt;/span&gt; - use current pose as looking perfectly forward.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Toggle&lt;/span&gt; - keep looking at same spot until toggle keypress.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Zero&lt;/span&gt; - keep looking forward while the key is pressed.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="styleSheet">
+ <string notr="true">QGroupBox { border: 0; }</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_8">
+ <item row="0" column="1">
+ <widget class="QLabel" name="center_text">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="textLabel2_3">
+ <property name="text">
+ <string>Center</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QLabel" name="zero_text">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLabel" name="toggle_text">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="textLabel2_5">
+ <property name="text">
+ <string>Toggle</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="textLabel2_6">
+ <property name="text">
+ <string>Zero</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QPushButton" name="bind_center">
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="2">
+ <widget class="QPushButton" name="bind_toggle">
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="2">
+ <widget class="QPushButton" name="bind_zero">
+ <property name="text">
+ <string>Bind</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_9">
+ <property name="title">
+ <string>Centering method</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_26">
+ <property name="text">
+ <string>Method</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="center_method">
+ <item>
+ <property name="text">
+ <string>Relative (inertial device)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Absolute (camera device)</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_25">
+ <property name="text">
+ <string>Try changing this if centering doesn't perform correctly for your input device.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="center_at_startup">
+ <property name="text">
+ <string>Center at startup</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="trayp">
+ <property name="text">
+ <string>Minimize to tray</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_3">
+ <attribute name="title">
+ <string>Camera</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QGroupBox" name="groupBox_5">
+ <property name="title">
+ <string>Camera offset</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_6">
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_17">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Specify an angle for off-center camera as a basis for which direction is which, avoiding axis interconnect. Also see &lt;a href=&quot;https://github.com/opentrack/opentrack/wiki/choosing-camera-offset&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;description on wiki&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignJustify|Qt::AlignVCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="margin">
+ <number>2</number>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" alignment="Qt::AlignLeft">
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="styleSheet">
+ <string notr="true">QGroupBox
+{
+ border: 0;
+}</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_7">
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_21">
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="camera_roll">
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="minimum">
+ <number>-180</number>
+ </property>
+ <property name="maximum">
+ <number>180</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="camera_yaw">
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="minimum">
+ <number>-180</number>
+ </property>
+ <property name="maximum">
+ <number>180</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_19">
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="camera_pitch">
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="minimum">
+ <number>-180</number>
+ </property>
+ <property name="maximum">
+ <number>180</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_18">
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_6">
+ <property name="title">
+ <string>Center pose offset</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
+ <widget class="QLabel" name="label_22">
+ <property name="text">
+ <string>Alter the centered position sent to games by a fixed amount.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="margin">
+ <number>2</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_7">
+ <property name="styleSheet">
+ <string notr="true">QGroupBox {
+ border: 0;
+}</string>
+ </property>
+ <property name="title">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="2" column="1">
+ <widget class="QDoubleSpinBox" name="pos_rz">
+ <property name="suffix">
+ <string> deg.</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-180.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>180.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QDoubleSpinBox" name="pos_tz">
+ <property name="suffix">
+ <string> cm</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-100.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>100.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>TX</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QDoubleSpinBox" name="pos_tx">
+ <property name="suffix">
+ <string> cm</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-100.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>100.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>RY</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>TY</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDoubleSpinBox" name="pos_ry">
+ <property name="suffix">
+ <string> deg.</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-180.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>180.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>TZ</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>RZ</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QDoubleSpinBox" name="pos_ty">
+ <property name="suffix">
+ <string> cm</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-100.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>100.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>RX</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QDoubleSpinBox" name="pos_rx">
+ <property name="suffix">
+ <string> deg.</string>
+ </property>
+ <property name="decimals">
+ <number>3</number>
+ </property>
+ <property name="minimum">
+ <double>-180.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>180.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_4">
+ <attribute name="title">
+ <string>Output</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QGroupBox" name="groupBox_">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="title">
+ <string>Translation compensation</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_7">
+ <item>
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>With compensation on, translation is applied after rotation. For example, rotating +180 degrees yaw and moving backwards results in moving forward as a result of that rotation.</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignJustify|Qt::AlignVCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="margin">
+ <number>2</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="tcomp_enable">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Enable</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="tcomp_rz">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Disable Z axis compensation</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignTop">
+ <widget class="QGroupBox" name="groupBox_4">
+ <property name="maximumSize">
+ <size>
+ <width>65536</width>
+ <height>65536</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="title">
+ <string>Output remap</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4" rowstretch="0,0" columnstretch="0">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinAndMaxSize</enum>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_20">
+ <property name="text">
+ <string>Assign input axis to output axis.</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignJustify|Qt::AlignVCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="styleSheet">
+ <string notr="true">QGroupBox
+{
+ border: 0;
+}</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <item row="7" column="2">
+ <widget class="QCheckBox" name="invert_y">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>X</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Invert</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QComboBox" name="src_x">
+ <item>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QComboBox" name="src_y">
+ <item>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QComboBox" name="src_yaw">
+ <item>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="5" column="2">
+ <widget class="QCheckBox" name="invert_roll">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>Destination</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QCheckBox" name="invert_pitch">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="2">
+ <widget class="QCheckBox" name="invert_x">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_13">
+ <property name="text">
+ <string>Source</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QComboBox" name="src_pitch">
+ <item>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QCheckBox" name="invert_yaw">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QComboBox" name="src_roll">
+ <item>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="8" column="0">
+ <widget class="QLabel" name="label_12">
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="1">
+ <widget class="QComboBox" name="src_z">
+ <item>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="8" column="2">
+ <widget class="QCheckBox" name="invert_z">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ <zorder>label_15</zorder>
+ <zorder>label_13</zorder>
+ <zorder>label_14</zorder>
+ <zorder>src_yaw</zorder>
+ <zorder>invert_yaw</zorder>
+ <zorder>label_7</zorder>
+ <zorder>src_pitch</zorder>
+ <zorder>label_8</zorder>
+ <zorder>invert_pitch</zorder>
+ <zorder>label_9</zorder>
+ <zorder>src_roll</zorder>
+ <zorder>invert_roll</zorder>
+ <zorder>label_10</zorder>
+ <zorder>src_x</zorder>
+ <zorder>invert_x</zorder>
+ <zorder>label_11</zorder>
+ <zorder>src_y</zorder>
+ <zorder>invert_y</zorder>
+ <zorder>label_12</zorder>
+ <zorder>src_z</zorder>
+ <zorder>invert_z</zorder>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_5">
+ <attribute name="title">
+ <string>Game detection</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_9">
+ <item>
+ <widget class="QGroupBox" name="groupBox_1">
+ <property name="title">
+ <string>Game detection</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="0">
+ <widget class="process_detector" name="game_detector" native="true"/>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_24">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Start tracking automatically when a game starts with selected profile, and stop when the game exits.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>process_detector</class>
+ <extends>QWidget</extends>
+ <header>process_detector.h</header>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>tabWidget</tabstop>
+ <tabstop>center_at_startup</tabstop>
+ <tabstop>trayp</tabstop>
+ <tabstop>camera_yaw</tabstop>
+ <tabstop>camera_pitch</tabstop>
+ <tabstop>camera_roll</tabstop>
+ <tabstop>pos_rx</tabstop>
+ <tabstop>pos_ry</tabstop>
+ <tabstop>pos_rz</tabstop>
+ <tabstop>pos_tx</tabstop>
+ <tabstop>pos_ty</tabstop>
+ <tabstop>pos_tz</tabstop>
+ <tabstop>tcomp_enable</tabstop>
+ <tabstop>tcomp_rz</tabstop>
+ <tabstop>src_yaw</tabstop>
+ <tabstop>invert_yaw</tabstop>
+ <tabstop>src_pitch</tabstop>
+ <tabstop>invert_pitch</tabstop>
+ <tabstop>src_roll</tabstop>
+ <tabstop>invert_roll</tabstop>
+ <tabstop>src_x</tabstop>
+ <tabstop>invert_x</tabstop>
+ <tabstop>src_y</tabstop>
+ <tabstop>invert_y</tabstop>
+ <tabstop>src_z</tabstop>
+ <tabstop>invert_z</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/gui/ui-res.qrc b/gui/ui-res.qrc
new file mode 100644
index 00000000..030a6153
--- /dev/null
+++ b/gui/ui-res.qrc
@@ -0,0 +1,10 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/tools.png</file>
+ <file>images/settings16.png</file>
+ <file>images/curves.png</file>
+ <file>images/facetracknoir.png</file>
+ <file>images/no-feed.png</file>
+ <file>images/filter-16.png</file>
+ </qresource>
+</RCC>
diff --git a/gui/ui.cpp b/gui/ui.cpp
new file mode 100644
index 00000000..5ea5dbfa
--- /dev/null
+++ b/gui/ui.cpp
@@ -0,0 +1,567 @@
+/* Copyright (c) 2013-2015, Stanislaw Halik <sthalik@misaki.pl>
+
+ * Permission to use, copy, modify, and/or distribute this
+ * software for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice and this permission
+ * notice appear in all copies.
+ */
+
+#include "ui.h"
+#include "opentrack/tracker.h"
+#include "opentrack/options.hpp"
+#include "new_file_dialog.h"
+#include <QFileDialog>
+#include <QDesktopServices>
+
+#ifndef _WIN32
+# include <unistd.h>
+#else
+# include <windows.h>
+#endif
+
+MainWindow::MainWindow() :
+ pose_update_timer(this),
+ kbd_quit(QKeySequence("Ctrl+Q"), this),
+ no_feed_pixmap(":/images/no-feed.png"),
+ is_refreshing_profiles(false)
+{
+ ui.setupUi(this);
+
+ setFixedSize(size());
+
+ updateButtonState(false, false);
+ ui.video_frame_label->setPixmap(no_feed_pixmap);
+
+ connect(ui.btnEditCurves, SIGNAL(clicked()), this, SLOT(showCurveConfiguration()));
+ connect(ui.btnShortcuts, SIGNAL(clicked()), this, SLOT(show_options_dialog()));
+ connect(ui.btnShowEngineControls, SIGNAL(clicked()), this, SLOT(showTrackerSettings()));
+ connect(ui.btnShowServerControls, SIGNAL(clicked()), this, SLOT(showProtocolSettings()));
+ connect(ui.btnShowFilterControls, SIGNAL(clicked()), this, SLOT(showFilterSettings()));
+
+ modules.filters().push_front(std::make_shared<dylib>("", dylib::Filter));
+
+ for (auto x : modules.trackers())
+ ui.iconcomboTrackerSource->addItem(x->icon, x->name);
+
+ for (auto x : modules.protocols())
+ ui.iconcomboProtocol->addItem(x->icon, x->name);
+
+ for (auto x : modules.filters())
+ ui.iconcomboFilter->addItem(x->icon, x->name);
+
+ refresh_config_list();
+ connect(&config_list_timer, SIGNAL(timeout()), this, SLOT(refresh_config_list()));
+ config_list_timer.start(1000 * 3);
+
+ tie_setting(s.tracker_dll, ui.iconcomboTrackerSource);
+ tie_setting(s.protocol_dll, ui.iconcomboProtocol);
+ tie_setting(s.filter_dll, ui.iconcomboFilter);
+
+ connect(ui.iconcomboTrackerSource,
+ &QComboBox::currentTextChanged,
+ [&](QString) -> void { if (pTrackerDialog) pTrackerDialog = nullptr; save(); });
+
+ connect(ui.iconcomboProtocol,
+ &QComboBox::currentTextChanged,
+ [&](QString) -> void { if (pProtocolDialog) pProtocolDialog = nullptr; save(); });
+
+ connect(ui.iconcomboFilter,
+ &QComboBox::currentTextChanged,
+ [&](QString) -> void { if (pFilterDialog) pFilterDialog = nullptr; save(); });
+
+ connect(ui.btnStartTracker, SIGNAL(clicked()), this, SLOT(startTracker()));
+ connect(ui.btnStopTracker, SIGNAL(clicked()), this, SLOT(stopTracker()));
+ connect(ui.iconcomboProfile, SIGNAL(currentTextChanged(QString)), this, SLOT(profileSelected(QString)));
+
+ connect(&pose_update_timer, SIGNAL(timeout()), this, SLOT(showHeadPose()));
+ connect(&kbd_quit, SIGNAL(activated()), this, SLOT(exit()));
+
+ save_timer.setSingleShot(true);
+ connect(&save_timer, SIGNAL(timeout()), this, SLOT(_save()));
+
+ profile_menu.addAction("Create new empty config", this, SLOT(make_empty_config()));
+ profile_menu.addAction("Create new copied config", this, SLOT(make_copied_config()));
+ profile_menu.addAction("Open configuration directory", this, SLOT(open_config_directory()));
+ ui.profile_button->setMenu(&profile_menu);
+
+ kbd_quit.setEnabled(true);
+
+ connect(&det_timer, SIGNAL(timeout()), this, SLOT(maybe_start_profile_from_executable()));
+ det_timer.start(1000);
+
+ ensure_tray();
+ set_working_directory();
+
+ if (!QFile(group::ini_pathname()).exists())
+ {
+ set_profile(OPENTRACK_DEFAULT_CONFIG);
+ const auto pathname = group::ini_pathname();
+ if (!QFile(pathname).exists())
+ {
+ QFile file(pathname);
+ (void) file.open(QFile::ReadWrite);
+ }
+ }
+
+ if (group::ini_directory() == "")
+ QMessageBox::warning(this,
+ "Configuration not saved.",
+ "Can't create configuration directory! Expect major malfunction.",
+ QMessageBox::Ok, QMessageBox::NoButton);
+
+ ui.btnStartTracker->setFocus();
+}
+
+bool MainWindow::get_new_config_name_from_dialog(QString& ret)
+{
+ new_file_dialog dlg;
+ dlg.exec();
+ return dlg.is_ok(ret);
+}
+
+MainWindow::~MainWindow()
+{
+ maybe_save();
+
+ if (tray)
+ tray->hide();
+ stopTracker();
+}
+
+void MainWindow::set_working_directory()
+{
+ QDir::setCurrent(QCoreApplication::applicationDirPath());
+}
+
+void MainWindow::save_mappings() {
+ pose.save_mappings();
+}
+
+void MainWindow::save()
+{
+ save_timer.stop();
+ save_timer.start(5000);
+}
+
+void MainWindow::maybe_save()
+{
+ if (save_timer.isActive())
+ {
+ save_timer.stop();
+ _save();
+ }
+}
+
+void MainWindow::_save() {
+ s.b->save();
+ save_mappings();
+ mem<QSettings> settings = group::ini_file();
+ settings->sync();
+
+#if defined(__unix) || defined(__linux)
+ QString currentFile = group::ini_pathname();
+ QByteArray bytes = QFile::encodeName(currentFile);
+ const char* filename_as_asciiz = bytes.constData();
+
+ if (access(filename_as_asciiz, R_OK | W_OK))
+ {
+ QMessageBox::warning(this, "Something went wrong", "Check permissions and ownership for your .ini file!", QMessageBox::Ok, QMessageBox::NoButton);
+ }
+#endif
+}
+
+void MainWindow::load_mappings() {
+ pose.load_mappings();
+}
+
+void MainWindow::load_settings() {
+ s.b->reload();
+ load_mappings();
+}
+
+void MainWindow::make_empty_config()
+{
+ QString name;
+ const QString dir = group::ini_directory();
+ if (dir != "" && get_new_config_name_from_dialog(name))
+ {
+ QFile filename(dir + "/" + name);
+ (void) filename.open(QFile::ReadWrite);
+ refresh_config_list();
+ ui.iconcomboProfile->setCurrentText(name);
+ }
+}
+
+void MainWindow::make_copied_config()
+{
+ const QString dir = group::ini_directory();
+ const QString cur = group::ini_pathname();
+ QString name;
+ if (cur != "" && dir != "" && get_new_config_name_from_dialog(name))
+ {
+ const QString new_name = dir + "/" + name;
+ (void) QFile::remove(new_name);
+ (void) QFile::copy(cur, new_name);
+ refresh_config_list();
+ ui.iconcomboProfile->setCurrentText(name);
+ }
+}
+
+void MainWindow::open_config_directory()
+{
+ const QString path = group::ini_directory();
+ if (path != "")
+ {
+ QDesktopServices::openUrl("file:///" + QDir::toNativeSeparators(path));
+ }
+}
+
+extern "C" const char* opentrack_version;
+
+void MainWindow::refresh_config_list()
+{
+ if (work)
+ return;
+
+ if (group::ini_list().size() == 0)
+ {
+ QFile filename(group::ini_directory() + "/" OPENTRACK_DEFAULT_CONFIG);
+ (void) filename.open(QFile::ReadWrite);
+ }
+
+ QStringList ini_list = group::ini_list();
+ set_title();
+ QString current = group::ini_filename();
+ is_refreshing_profiles = true;
+ ui.iconcomboProfile->clear();
+ for (auto x : ini_list)
+ ui.iconcomboProfile->addItem(QIcon(":/images/settings16.png"), x);
+ is_refreshing_profiles = false;
+ ui.iconcomboProfile->setCurrentText(current);
+}
+
+void MainWindow::updateButtonState(bool running, bool inertialp)
+{
+ bool not_running = !running;
+ ui.iconcomboProfile->setEnabled ( not_running );
+ ui.btnStartTracker->setEnabled ( not_running );
+ ui.btnStopTracker->setEnabled ( running );
+ ui.iconcomboProtocol->setEnabled ( not_running );
+ ui.iconcomboFilter->setEnabled ( not_running );
+ ui.iconcomboTrackerSource->setEnabled(not_running);
+ ui.video_frame_label->setVisible(not_running || inertialp);
+ ui.profile_button->setEnabled(not_running);
+}
+
+void MainWindow::reload_options()
+{
+ if (work)
+ work->reload_shortcuts();
+ ensure_tray();
+}
+
+void MainWindow::startTracker() {
+ // tracker dtor needs run first
+ work = nullptr;
+
+ libs = SelectedLibraries(ui.video_frame, current_tracker(), current_protocol(), current_filter());
+
+ {
+ double p[6] = {0,0,0, 0,0,0};
+ display_pose(p, p);
+ }
+
+ if (!libs.correct)
+ {
+ QMessageBox::warning(this, "Library load error",
+ "One of libraries failed to load. Check installation.",
+ QMessageBox::Ok,
+ QMessageBox::NoButton);
+ libs = SelectedLibraries();
+ return;
+ }
+
+ work = std::make_shared<Work>(s, pose, libs, this, winId());
+
+ reload_options();
+
+ if (pTrackerDialog)
+ pTrackerDialog->register_tracker(libs.pTracker.get());
+
+ if (pFilterDialog)
+ pFilterDialog->register_filter(libs.pFilter.get());
+
+ if (pProtocolDialog)
+ pProtocolDialog->register_protocol(libs.pProtocol.get());
+
+ pose_update_timer.start(50);
+
+ // NB check valid since SelectedLibraries ctor called
+ // trackers take care of layout state updates
+ const bool is_inertial = ui.video_frame->layout() == nullptr;
+ updateButtonState(true, is_inertial);
+
+ maybe_save();
+
+ ui.btnStopTracker->setFocus();
+}
+
+void MainWindow::stopTracker( ) {
+ //ui.game_name->setText("Not connected");
+
+ pose_update_timer.stop();
+ ui.pose_display->rotateBy(0, 0, 0, 0, 0, 0);
+
+ if (pTrackerDialog)
+ pTrackerDialog->unregister_tracker();
+
+ if (pProtocolDialog)
+ pProtocolDialog->unregister_protocol();
+
+ if (pFilterDialog)
+ pFilterDialog->unregister_filter();
+
+ maybe_save();
+
+ work = nullptr;
+ libs = SelectedLibraries();
+
+ {
+ double p[6] = {0,0,0, 0,0,0};
+ display_pose(p, p);
+ }
+ updateButtonState(false, false);
+
+ set_title();
+
+ ui.btnStartTracker->setFocus();
+}
+
+void MainWindow::display_pose(const double *mapped, const double *raw)
+{
+ ui.pose_display->rotateBy(mapped[Yaw], mapped[Pitch], mapped[Roll],
+ mapped[TX], mapped[TY], mapped[TZ]);
+
+ if (mapping_widget)
+ mapping_widget->update();
+
+ double mapped_[6], raw_[6];
+
+ for (int i = 0; i < 6; i++)
+ {
+ mapped_[i] = (int) mapped[i];
+ raw_[i] = (int) raw[i];
+ }
+
+ ui.raw_x->display(raw_[TX]);
+ ui.raw_y->display(raw_[TY]);
+ ui.raw_z->display(raw_[TZ]);
+ ui.raw_yaw->display(raw_[Yaw]);
+ ui.raw_pitch->display(raw_[Pitch]);
+ ui.raw_roll->display(raw_[Roll]);
+
+ ui.pose_x->display(mapped_[TX]);
+ ui.pose_y->display(mapped_[TY]);
+ ui.pose_z->display(mapped_[TZ]);
+ ui.pose_yaw->display(mapped_[Yaw]);
+ ui.pose_pitch->display(mapped_[Pitch]);
+ ui.pose_roll->display(mapped_[Roll]);
+
+ QString game_title;
+ if (libs.pProtocol)
+ game_title = libs.pProtocol->game_name();
+ set_title(game_title);
+}
+
+void MainWindow::set_title(const QString& game_title_)
+{
+ QString game_title;
+ if (game_title_ != "")
+ game_title = " :: " + game_title_;
+ QString current = group::ini_filename();
+ setWindowTitle(opentrack_version + QStringLiteral(" :: ") + current + game_title);
+}
+
+void MainWindow::showHeadPose()
+{
+ double mapped[6], raw[6];
+
+ work->tracker->get_raw_and_mapped_poses(mapped, raw);
+
+ display_pose(mapped, raw);
+}
+
+template<typename t>
+bool mk_dialog(mem<dylib> lib, mem<t>& orig)
+{
+ if (orig && orig->isVisible())
+ {
+ orig->show();
+ orig->raise();
+ return false;
+ }
+
+ if (lib && lib->Dialog)
+ {
+ auto dialog = mem<t>(reinterpret_cast<t*>(lib->Dialog()));
+ dialog->setWindowFlags(Qt::Dialog);
+ dialog->setFixedSize(dialog->size());
+
+ orig = dialog;
+ dialog->show();
+ dialog->raise();
+
+ QObject::connect(dialog.get(), &BaseDialog::closing, [&]() -> void { orig = nullptr; });
+
+ return true;
+ }
+
+ return false;
+}
+
+void MainWindow::showTrackerSettings()
+{
+ if (mk_dialog(current_tracker(), pTrackerDialog) && libs.pTracker)
+ pTrackerDialog->register_tracker(libs.pTracker.get());
+}
+
+void MainWindow::showProtocolSettings() {
+ if (mk_dialog(current_protocol(), pProtocolDialog) && libs.pProtocol)
+ pProtocolDialog->register_protocol(libs.pProtocol.get());
+}
+
+void MainWindow::showFilterSettings() {
+ if (mk_dialog(current_filter(), pFilterDialog) && libs.pFilter)
+ pFilterDialog->register_filter(libs.pFilter.get());
+}
+
+template<typename t, typename... Args>
+bool mk_window(mem<t>* place, Args... params)
+{
+ if (*place && (*place)->isVisible())
+ {
+ (*place)->show();
+ (*place)->raise();
+ return false;
+ }
+ else
+ {
+ *place = std::make_shared<t>(params...);
+ (*place)->setWindowFlags(Qt::Dialog);
+ (*place)->show();
+ (*place)->raise();
+ return true;
+ }
+}
+
+void MainWindow::show_options_dialog() {
+ if (mk_window(&options_widget))
+ connect(options_widget.get(), SIGNAL(reload()), this, SLOT(reload_options()));
+}
+
+void MainWindow::showCurveConfiguration() {
+ mk_window<MapWidget, Mappings&, main_settings&>(&mapping_widget, pose, s);
+}
+
+void MainWindow::exit() {
+ QCoreApplication::exit(0);
+}
+
+void MainWindow::profileSelected(QString name)
+{
+ if (name == "" || is_refreshing_profiles)
+ return;
+
+ const auto old_name = group::ini_filename();
+ const auto new_name = name;
+
+ if (old_name != new_name)
+ {
+ save();
+
+ {
+ QSettings settings(OPENTRACK_ORG);
+ settings.setValue (OPENTRACK_CONFIG_FILENAME_KEY, new_name);
+ }
+
+ set_title();
+ load_settings();
+ }
+}
+
+void MainWindow::shortcutRecentered()
+{
+ qDebug() << "Center";
+ if (work)
+ work->tracker->center();
+}
+
+void MainWindow::shortcutToggled()
+{
+ qDebug() << "Toggle";
+ if (work)
+ work->tracker->toggle_enabled();
+}
+
+void MainWindow::shortcutZeroed()
+{
+ qDebug() << "Zero";
+ if (work)
+ work->tracker->zero();
+}
+
+void MainWindow::ensure_tray()
+{
+ if (tray)
+ tray->hide();
+ tray = nullptr;
+ if (s.tray_enabled)
+ {
+ tray = std::make_shared<QSystemTrayIcon>(this);
+ tray->setIcon(QIcon(":/images/facetracknoir.png"));
+ tray->show();
+ connect(tray.get(), SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
+ this, SLOT(restore_from_tray(QSystemTrayIcon::ActivationReason)));
+ }
+}
+
+void MainWindow::restore_from_tray(QSystemTrayIcon::ActivationReason)
+{
+ show();
+ setWindowState(Qt::WindowNoState);
+}
+
+void MainWindow::changeEvent(QEvent* e)
+{
+ if (s.tray_enabled && e->type() == QEvent::WindowStateChange && (windowState() & Qt::WindowMinimized))
+ {
+ if (!tray)
+ ensure_tray();
+ hide();
+ }
+ QMainWindow::changeEvent(e);
+}
+
+void MainWindow::maybe_start_profile_from_executable()
+{
+ if (!work)
+ {
+ QString prof;
+ if (det.config_to_start(prof))
+ {
+ ui.iconcomboProfile->setCurrentText(prof);
+ startTracker();
+ }
+ }
+ else
+ {
+ if (det.should_stop())
+ stopTracker();
+ }
+}
+
+void MainWindow::set_profile(const QString &profile)
+{
+ QSettings settings(OPENTRACK_ORG);
+ settings.setValue(OPENTRACK_CONFIG_FILENAME_KEY, profile);
+}
diff --git a/gui/ui.h b/gui/ui.h
new file mode 100644
index 00000000..91e4ebbf
--- /dev/null
+++ b/gui/ui.h
@@ -0,0 +1,113 @@
+/* Copyright (c) 2014-2015, Stanislaw Halik <sthalik@misaki.pl>
+
+ * Permission to use, copy, modify, and/or distribute this
+ * software for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice and this permission
+ * notice appear in all copies.
+ */
+
+#pragma once
+
+#include <QMainWindow>
+#include <QKeySequence>
+#include <QShortcut>
+#include <QPixmap>
+#include <QTimer>
+#include <QSystemTrayIcon>
+#include <QString>
+#include <QMenu>
+
+#include "ui_main.h"
+
+#include "opentrack/options.hpp"
+#include "opentrack/main-settings.hpp"
+#include "opentrack/plugin-support.hpp"
+#include "opentrack/tracker.h"
+#include "opentrack/shortcuts.h"
+#include "opentrack/work.hpp"
+#include "opentrack/state.hpp"
+#include "curve-config.h"
+#include "options-dialog.hpp"
+#include "process_detector.h"
+
+using namespace options;
+
+class MainWindow : public QMainWindow, private State
+{
+ Q_OBJECT
+
+ Ui::OpentrackUI ui;
+ mem<QSystemTrayIcon> tray;
+ QTimer pose_update_timer;
+ QTimer det_timer;
+ QTimer config_list_timer;
+ mem<OptionsDialog> options_widget;
+ mem<MapWidget> mapping_widget;
+ QShortcut kbd_quit;
+ QPixmap no_feed_pixmap;
+ mem<IFilterDialog> pFilterDialog;
+ mem<IProtocolDialog> pProtocolDialog;
+ mem<ITrackerDialog> pTrackerDialog;
+ process_detector_worker det;
+ QMenu profile_menu;
+ bool is_refreshing_profiles;
+ QTimer save_timer;
+
+ mem<dylib> current_tracker()
+ {
+ return modules.trackers().value(ui.iconcomboTrackerSource->currentIndex(), nullptr);
+ }
+ mem<dylib> current_protocol()
+ {
+ return modules.protocols().value(ui.iconcomboProtocol->currentIndex(), nullptr);
+ }
+ mem<dylib> current_filter()
+ {
+ return modules.filters().value(ui.iconcomboFilter->currentIndex(), nullptr);
+ }
+
+ void changeEvent(QEvent* e) override;
+
+ void load_settings();
+ void updateButtonState(bool running, bool inertialp);
+ void display_pose(const double* mapped, const double* raw);
+ void ensure_tray();
+ void set_title(const QString& game_title = QStringLiteral(""));
+ static bool get_new_config_name_from_dialog(QString &ret);
+ void set_profile(const QString& profile);
+ void maybe_save();
+private slots:
+ void _save();
+ void save();
+ void exit();
+ void profileSelected(QString name);
+
+ void showTrackerSettings();
+ void showProtocolSettings();
+ void showFilterSettings();
+ void show_options_dialog();
+ void showCurveConfiguration();
+ void showHeadPose();
+
+ void restore_from_tray(QSystemTrayIcon::ActivationReason);
+ void maybe_start_profile_from_executable();
+
+ void make_empty_config();
+ void make_copied_config();
+ void open_config_directory();
+ void refresh_config_list();
+
+ void startTracker();
+ void stopTracker();
+ void reload_options();
+public slots:
+ void shortcutRecentered();
+ void shortcutToggled();
+ void shortcutZeroed();
+public:
+ MainWindow();
+ ~MainWindow();
+ void save_mappings();
+ void load_mappings();
+ static void set_working_directory();
+};