summaryrefslogtreecommitdiffhomepage
path: root/logic
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2016-08-12 18:00:49 +0200
committerStanislaw Halik <sthalik@misaki.pl>2016-08-12 18:00:49 +0200
commit9040b187a1c4fa380f8a12207b9dd6d04b3a10ac (patch)
tree115e1351571d690c1261a9d512e6d44e717f3051 /logic
parent13a18b149764509a3f460be86590250cdcf690fb (diff)
all: rename modules s#^opentrack-##. and opentrack -> api
Adjust usages.
Diffstat (limited to 'logic')
-rw-r--r--logic/CMakeLists.txt7
-rw-r--r--logic/export.hpp28
-rw-r--r--logic/main-settings.hpp109
-rw-r--r--logic/mappings.hpp89
-rw-r--r--logic/selected-libraries.cpp37
-rw-r--r--logic/selected-libraries.hpp25
-rw-r--r--logic/shortcuts.cpp130
-rw-r--r--logic/shortcuts.h68
-rw-r--r--logic/simple-mat.cpp97
-rw-r--r--logic/simple-mat.hpp279
-rw-r--r--logic/state.hpp32
-rw-r--r--logic/tracker.cpp385
-rw-r--r--logic/tracker.h85
-rw-r--r--logic/tracklogger.cpp47
-rw-r--r--logic/tracklogger.hpp65
-rw-r--r--logic/win32-shortcuts.cpp196
-rw-r--r--logic/win32-shortcuts.h24
-rw-r--r--logic/work.cpp73
-rw-r--r--logic/work.hpp43
19 files changed, 1819 insertions, 0 deletions
diff --git a/logic/CMakeLists.txt b/logic/CMakeLists.txt
new file mode 100644
index 00000000..88b0240f
--- /dev/null
+++ b/logic/CMakeLists.txt
@@ -0,0 +1,7 @@
+opentrack_boilerplate(opentrack-logic BIN)
+target_link_libraries(opentrack-logic opentrack-spline-widget)
+if(NOT WIN32)
+ target_link_libraries(opentrack-logic opentrack-qxt-mini)
+else()
+ target_link_libraries(opentrack-logic opentrack-dinput winmm)
+endif()
diff --git a/logic/export.hpp b/logic/export.hpp
new file mode 100644
index 00000000..2503f3a6
--- /dev/null
+++ b/logic/export.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#ifdef BUILD_logic
+# ifdef _WIN32
+# define OPENTRACK_LOGIC_LINKAGE __declspec(dllexport)
+# else
+# define OPENTRACK_LOGIC_LINKAGE
+# endif
+
+# ifndef _MSC_VER
+# define OPENTRACK_LOGIC_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_LOGIC_LINKAGE
+# else
+# define OPENTRACK_LOGIC_EXPORT OPENTRACK_LOGIC_LINKAGE
+# endif
+
+#else
+#ifdef _WIN32
+# define OPENTRACK_LOGIC_LINKAGE __declspec(dllimport)
+#else
+# define OPENTRACK_LOGIC_LINKAGE
+#endif
+
+#ifndef _MSC_VER
+# define OPENTRACK_LOGIC_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_LOGIC_LINKAGE
+#else
+# define OPENTRACK_LOGIC_EXPORT OPENTRACK_LOGIC_LINKAGE
+#endif
+#endif
diff --git a/logic/main-settings.hpp b/logic/main-settings.hpp
new file mode 100644
index 00000000..26313a26
--- /dev/null
+++ b/logic/main-settings.hpp
@@ -0,0 +1,109 @@
+/* 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 <QString>
+#include "compat/options.hpp"
+#include "api/plugin-api.hpp"
+
+using namespace options;
+
+#include "export.hpp"
+
+struct axis_opts
+{
+ pbundle b;
+ value<double> zero;
+ value<bool> invert, altp;
+ value<int> src;
+ axis_opts(pbundle b, QString pfx, int idx) :
+ b(b),
+ zero(b, n(pfx, "zero-pos"), 0),
+ invert(b, n(pfx, "invert-sign"), false),
+ altp(b, n(pfx, "alt-axis-sign"), false),
+ src(b, n(pfx, "source-index"), idx)
+ {}
+private:
+ static inline QString n(QString pfx, QString name) {
+ return QString("%1-%2").arg(pfx, name);
+ }
+};
+
+struct key_opts
+{
+ value<QString> keycode, guid;
+ value<int> button;
+
+ key_opts(pbundle b, const QString& name) :
+ keycode(b, QString("keycode-%1").arg(name), ""),
+ guid(b, QString("guid-%1").arg(name), ""),
+ button(b, QString("button-%1").arg(name), -1)
+ {}
+};
+
+struct module_settings
+{
+ pbundle b;
+ value<QString> tracker_dll, filter_dll, protocol_dll;
+ module_settings() :
+ b(bundle("modules")),
+ tracker_dll(b, "tracker-dll", ""),
+ filter_dll(b, "filter-dll", "Accela"),
+ protocol_dll(b, "protocol-dll", "freetrack 2.0 Enhanced")
+ {
+ }
+};
+
+struct main_settings
+{
+ pbundle b, b_map;
+ axis_opts a_x, a_y, a_z, a_yaw, a_pitch, a_roll;
+ value<bool> tcomp_p, tcomp_tz;
+ value<bool> tray_enabled;
+ value<int> camera_yaw, camera_pitch, camera_roll;
+ value<bool> use_camera_offset_from_centering;
+ value<bool> center_at_startup;
+ value<int> center_method;
+ key_opts key_start_tracking, key_stop_tracking, key_toggle_tracking, key_restart_tracking;
+ key_opts key_center, key_toggle, key_zero;
+ key_opts key_toggle_press, key_zero_press;
+ value<bool> tracklogging_enabled;
+ value<QString> tracklogging_filename;
+ main_settings() :
+ b(bundle("opentrack-ui")),
+ b_map(bundle("opentrack-mappings")),
+ a_x(b_map, "x", TX),
+ a_y(b_map, "y", TY),
+ a_z(b_map, "z", TZ),
+ a_yaw(b_map, "yaw", Yaw),
+ a_pitch(b_map, "pitch", Pitch),
+ a_roll(b_map, "roll", Roll),
+ tcomp_p(b, "compensate-translation", true),
+ tcomp_tz(b, "compensate-translation-disable-z-axis", false),
+ tray_enabled(b, "use-system-tray", false),
+ camera_yaw(b, "camera-yaw", 0),
+ camera_pitch(b, "camera-pitch", 0),
+ camera_roll(b, "camera-roll", 0),
+ use_camera_offset_from_centering(b, "use-camera-offset-from-centering", false),
+ center_at_startup(b, "center-at-startup", true),
+ center_method(b, "centering-method", true),
+ key_start_tracking(b, "start-tracking"),
+ key_stop_tracking(b, "stop-tracking"),
+ key_toggle_tracking(b, "toggle-tracking"),
+ key_restart_tracking(b, "restart-tracking"),
+ key_center(b, "center"),
+ key_toggle(b, "toggle"),
+ key_zero(b, "zero"),
+ key_toggle_press(b, "toggle-press"),
+ key_zero_press(b, "zero-press"),
+ tracklogging_enabled(b, "tracklogging-enabled", false),
+ tracklogging_filename(b, "tracklogging-filename", QString())
+ {
+ }
+};
diff --git a/logic/mappings.hpp b/logic/mappings.hpp
new file mode 100644
index 00000000..0b9373c0
--- /dev/null
+++ b/logic/mappings.hpp
@@ -0,0 +1,89 @@
+/* Copyright (c) 2014-2015 Stanislaw Halik
+ *
+ * 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 <QSettings>
+#include "compat/options.hpp"
+using namespace options;
+#include "spline-widget/spline.hpp"
+#include "main-settings.hpp"
+
+class Map {
+public:
+ Map(QString primary,
+ QString secondary,
+ int max_x,
+ int max_y,
+ axis_opts& opts) :
+ spline_main(max_x, max_y),
+ spline_alt(max_x, max_y),
+ opts(opts),
+ name1(primary),
+ name2(secondary)
+ {
+ mem<QSettings> iniFile = group::ini_file();
+ spline_main.loadSettings(*iniFile, primary);
+ spline_alt.loadSettings(*iniFile, secondary);
+ }
+ spline spline_main;
+ spline spline_alt;
+ axis_opts& opts;
+ QString name1, name2;
+};
+
+class Mappings {
+private:
+ Map axes[6];
+public:
+ Mappings(std::vector<axis_opts*> opts) :
+ axes {
+ Map("tx","tx_alt", 30, 75, *opts[TX]),
+ Map("ty","ty_alt", 30, 75, *opts[TY]),
+ Map("tz","tz_alt", 30, 75, *opts[TZ]),
+ Map("rx", "rx_alt", 180, 180, *opts[Yaw]),
+ Map("ry", "ry_alt", 180, 180, *opts[Pitch]),
+ Map("rz", "rz_alt", 180, 180, *opts[Roll])
+ }
+ {}
+
+ inline Map& operator()(int i) { return axes[i]; }
+ inline const Map& operator()(int i) const { return axes[i]; }
+
+ void load_mappings()
+ {
+ mem<QSettings> iniFile = group::ini_file();
+
+ for (int i = 0; i < 6; i++)
+ {
+ axes[i].spline_main.loadSettings(*iniFile, axes[i].name1);
+ axes[i].spline_alt.loadSettings(*iniFile, axes[i].name2);
+ axes[i].opts.b->reload();
+ }
+ }
+ void save_mappings()
+ {
+ mem<QSettings> iniFile = group::ini_file();
+
+ for (int i = 0; i < 6; i++)
+ {
+ axes[i].spline_main.saveSettings(*iniFile, axes[i].name1);
+ axes[i].spline_alt.saveSettings(*iniFile, axes[i].name2);
+ axes[i].opts.b->save();
+ }
+ }
+
+ void invalidate_unsaved()
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ axes[i].spline_main.invalidate_unsaved_settings();
+ axes[i].spline_alt.invalidate_unsaved_settings();
+ axes[i].opts.b->reload();
+ }
+ }
+};
diff --git a/logic/selected-libraries.cpp b/logic/selected-libraries.cpp
new file mode 100644
index 00000000..4a1a9f09
--- /dev/null
+++ b/logic/selected-libraries.cpp
@@ -0,0 +1,37 @@
+#include "selected-libraries.hpp"
+#include <QDebug>
+
+SelectedLibraries::SelectedLibraries(QFrame* frame, dylibptr t, dylibptr p, dylibptr f) :
+ pTracker(nullptr),
+ pFilter(nullptr),
+ pProtocol(nullptr),
+ correct(false)
+{
+ pProtocol = make_dylib_instance<IProtocol>(p);
+
+ if (!pProtocol)
+ {
+ qDebug() << "protocol dylib load failure";
+ return;
+ }
+
+ if(!pProtocol->correct())
+ {
+ qDebug() << "protocol load failure";
+ pProtocol = nullptr;
+ return;
+ }
+
+ pTracker = make_dylib_instance<ITracker>(t);
+ pFilter = make_dylib_instance<IFilter>(f);
+
+ if (!pTracker)
+ {
+ qDebug() << "tracker dylib load failure";
+ return;
+ }
+
+ pTracker->start_tracker(frame);
+
+ correct = true;
+}
diff --git a/logic/selected-libraries.hpp b/logic/selected-libraries.hpp
new file mode 100644
index 00000000..d782374c
--- /dev/null
+++ b/logic/selected-libraries.hpp
@@ -0,0 +1,25 @@
+/* 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 "api/plugin-support.hpp"
+#include <QFrame>
+
+#include "export.hpp"
+
+struct OPENTRACK_LOGIC_EXPORT SelectedLibraries
+{
+ using dylibptr = mem<dylib>;
+ mem<ITracker> pTracker;
+ mem<IFilter> pFilter;
+ mem<IProtocol> pProtocol;
+ SelectedLibraries(QFrame* frame, dylibptr t, dylibptr p, dylibptr f);
+ SelectedLibraries() : pTracker(nullptr), pFilter(nullptr), pProtocol(nullptr), correct(false) {}
+ bool correct;
+};
diff --git a/logic/shortcuts.cpp b/logic/shortcuts.cpp
new file mode 100644
index 00000000..8d818180
--- /dev/null
+++ b/logic/shortcuts.cpp
@@ -0,0 +1,130 @@
+/* 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 "shortcuts.h"
+#include "win32-shortcuts.h"
+
+void Shortcuts::free_binding(K& key)
+{
+#ifndef _WIN32
+ if (key)
+ {
+ key->setEnabled(false);
+ key->setShortcut(QKeySequence::UnknownKey);
+ std::shared_ptr<QxtGlobalShortcut> tmp(nullptr);
+ key.swap(tmp);
+ }
+#else
+ key.keycode = 0;
+ key.guid = "";
+#endif
+}
+
+void Shortcuts::bind_shortcut(K &key, const key_opts& k, unused_on_unix(bool, held))
+{
+#if !defined(_WIN32)
+ using sh = QxtGlobalShortcut;
+ if (key)
+ {
+ free_binding(key);
+ }
+
+ key = std::make_shared<sh>();
+
+ if (k.keycode != "")
+ {
+ key->setShortcut(QKeySequence::fromString(k.keycode, QKeySequence::PortableText));
+ key->setEnabled();
+ }
+}
+#else
+ key = K();
+ int idx = 0;
+ QKeySequence code;
+
+ if (k.guid != "")
+ {
+ key.guid = k.guid;
+ key.keycode = k.button & ~Qt::KeyboardModifierMask;
+ key.ctrl = !!(k.button & Qt::ControlModifier);
+ key.alt = !!(k.button & Qt::AltModifier);
+ key.shift = !!(k.button & Qt::ShiftModifier);
+ }
+ else
+ {
+ if (k.keycode == "")
+ code = QKeySequence(Qt::Key_unknown);
+ else
+ code = QKeySequence::fromString(k.keycode, QKeySequence::PortableText);
+
+ Qt::KeyboardModifiers mods = Qt::NoModifier;
+ if (code != Qt::Key_unknown)
+ win_key::from_qt(code, idx, mods);
+ key.shift = !!(mods & Qt::ShiftModifier);
+ key.alt = !!(mods & Qt::AltModifier);
+ key.ctrl = !!(mods & Qt::ControlModifier);
+ key.keycode = idx;
+ key.held = held;
+ }
+}
+#endif
+
+#ifdef _WIN32
+void Shortcuts::receiver(const Key& k)
+{
+ const unsigned sz = keys.size();
+ for (unsigned i = 0; i < sz; i++)
+ {
+ K& k_ = std::get<0>(keys[i]);
+ auto& fun = std::get<1>(keys[i]);
+ if (k.guid != k_.guid)
+ continue;
+ if (k.keycode != k_.keycode)
+ continue;
+ if (k_.held && !k.held) continue;
+ if (k_.alt != k.alt) continue;
+ if (k_.ctrl != k.ctrl) continue;
+ if (k_.shift != k.shift) continue;
+ if (!k_.should_process())
+ continue;
+
+ fun(k.held);
+ }
+}
+#endif
+
+void Shortcuts::reload(const t_keys& keys_)
+{
+ const unsigned sz = keys_.size();
+ keys = std::vector<tt>();
+
+ for (unsigned i = 0; i < sz; i++)
+ {
+ const auto& kk = keys_[i];
+ const key_opts& opts = std::get<0>(kk);
+ const bool held = std::get<2>(kk);
+ auto fun = std::get<1>(kk);
+ K k;
+ bind_shortcut(k, opts, held);
+ keys.push_back(tt(k, [=](unused_on_unix(bool, flag)) -> void
+ {
+#ifdef _WIN32
+ fun(flag);
+#else
+ fun(true);
+#endif
+ },
+ held));
+#ifndef _WIN32
+ const int idx = keys.size() - 1;
+ tt& kk_ = keys[idx];
+ auto& fn = std::get<1>(kk_);
+ connect(k.get(), &QxtGlobalShortcut::activated, [=]() -> void { fn(true); });
+#endif
+ }
+}
diff --git a/logic/shortcuts.h b/logic/shortcuts.h
new file mode 100644
index 00000000..d5dfd394
--- /dev/null
+++ b/logic/shortcuts.h
@@ -0,0 +1,68 @@
+/* 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 <QObject>
+#include <tuple>
+#include <vector>
+#include <functional>
+
+#include "export.hpp"
+
+#include "qxt-mini/QxtGlobalShortcut"
+#include "compat/options.hpp"
+#include "main-settings.hpp"
+
+#ifdef _WIN32
+# include "dinput/keybinding-worker.hpp"
+#endif
+
+#if defined(__GNUC__) && !defined(_WIN32)
+# define unused_on_unix(t, i) t __attribute__((unused)) i
+#else
+# define unused_on_unix(t, i) t i
+#endif
+
+using namespace options;
+
+struct OPENTRACK_LOGIC_EXPORT Shortcuts : public QObject
+{
+ Q_OBJECT
+
+public:
+ using K =
+#ifndef _WIN32
+ mem<QxtGlobalShortcut>
+#else
+ Key
+#endif
+ ;
+
+ using fun = std::function<void(bool)>;
+ using tt = std::tuple<K, fun, bool>;
+ using t_key = std::tuple<key_opts&, fun, bool>;
+ using t_keys = std::vector<t_key>;
+ std::vector<tt> keys;
+#ifdef _WIN32
+ KeybindingWorker::Token key_token;
+#endif
+
+ Shortcuts()
+#ifdef _WIN32
+ : key_token([&](const Key& k) { receiver(k); })
+#endif
+ {}
+
+ void reload(const t_keys& keys_);
+private:
+ void free_binding(K& key);
+ void bind_shortcut(K &key, const key_opts& k, bool held);
+#ifdef _WIN32
+ void receiver(const Key& k);
+#endif
+};
diff --git a/logic/simple-mat.cpp b/logic/simple-mat.cpp
new file mode 100644
index 00000000..f9ed8613
--- /dev/null
+++ b/logic/simple-mat.cpp
@@ -0,0 +1,97 @@
+#include "simple-mat.hpp"
+#include "compat/pi-constant.hpp"
+#include <cmath>
+
+namespace euler {
+
+euler_t OPENTRACK_LOGIC_EXPORT rmat_to_euler(const rmat& R)
+{
+ using std::atan2;
+ using std::sqrt;
+
+ const double cy = sqrt(R(2,2)*R(2, 2) + R(2, 1)*R(2, 1));
+ const bool large_enough = cy > 1e-10;
+ if (large_enough)
+ return euler_t(atan2(-R(1, 0), R(0, 0)),
+ atan2(R(2, 0), cy),
+ atan2(-R(2, 1), R(2, 2)));
+ else
+ return euler_t(atan2(R(0, 1), R(1, 1)),
+ atan2(R(2, 0), cy),
+ 0);
+}
+
+// tait-bryan angles, not euler
+rmat OPENTRACK_LOGIC_EXPORT euler_to_rmat(const euler_t& input)
+{
+ const double H = -input(0);
+ const double P = -input(1);
+ const double B = -input(2);
+
+ using std::cos;
+ using std::sin;
+
+ const auto c1 = cos(H);
+ const auto s1 = sin(H);
+ const auto c2 = cos(P);
+ const auto s2 = sin(P);
+ const auto c3 = cos(B);
+ const auto s3 = sin(B);
+
+ return rmat(
+ // z
+ c1 * c2,
+ c1 * s2 * s3 - c3 * s1,
+ s1 * s3 + c1 * c3 * s2,
+ // y
+ c2 * s1,
+ c1 * c3 + s1 * s2 * s3,
+ c3 * s1 * s2 - c1 * s3,
+ // x
+ -s2,
+ c2 * s3,
+ c2 * c3
+ );
+}
+
+// https://en.wikipedia.org/wiki/Davenport_chained_rotations#Tait.E2.80.93Bryan_chained_rotations
+void OPENTRACK_LOGIC_EXPORT tait_bryan_to_matrices(const euler_t& input,
+ rmat& r_roll,
+ rmat& r_pitch,
+ rmat& r_yaw)
+{
+ using std::cos;
+ using std::sin;
+
+ {
+ const double phi = -input(2);
+ const double sin_phi = sin(phi);
+ const double cos_phi = cos(phi);
+
+ r_roll = rmat(1, 0, 0,
+ 0, cos_phi, -sin_phi,
+ 0, sin_phi, cos_phi);
+ }
+
+ {
+ const double theta = -input(1);
+ const double sin_theta = sin(theta);
+ const double cos_theta = cos(theta);
+
+ r_pitch = rmat(cos_theta, 0, -sin_theta,
+ 0, 1, 0,
+ sin_theta, 0, cos_theta);
+ }
+
+ {
+ const double psi = -input(0);
+ const double sin_psi = sin(psi);
+ const double cos_psi = cos(psi);
+
+ r_yaw = rmat(cos_psi, -sin_psi, 0,
+ sin_psi, cos_psi, 0,
+ 0, 0, 1);
+ }
+}
+
+} // end ns euler
diff --git a/logic/simple-mat.hpp b/logic/simple-mat.hpp
new file mode 100644
index 00000000..514e845f
--- /dev/null
+++ b/logic/simple-mat.hpp
@@ -0,0 +1,279 @@
+/* Copyright (c) 2014-2016, 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 "export.hpp"
+
+#include <initializer_list>
+#include <type_traits>
+#include <utility>
+
+namespace {
+ // last param to fool SFINAE into overloading
+ template<int i, int j, int>
+ struct equals
+ {
+ enum { value = i == j };
+ };
+ template<int i, int j, int min>
+ struct maybe_add_swizzle
+ {
+ enum { value = (i == 1 || j == 1) && (i >= min || j >= min) };
+ };
+ template<int i1, int j1, int i2, int j2>
+ struct is_vector_pair
+ {
+ enum { value = (i1 == i2 && j1 == 1 && j2 == 1) || (j1 == j2 && i1 == 1 && i2 == 1) };
+ };
+ template<int i, int j>
+ struct vector_len
+ {
+ enum { value = i > j ? i : j };
+ };
+ template<int a, int b, int c, int d>
+ struct is_dim3
+ {
+ enum { value = (a == 1 && c == 1 && b == 3 && d == 3) || (a == 3 && c == 3 && b == 1 && d == 1) };
+ enum { P = a == 1 ? 1 : 3 };
+ enum { Q = a == 1 ? 3 : 1 };
+ };
+
+ template<typename num, int h, int w, typename...ts>
+ struct is_arglist_correct
+ {
+ enum { value = h * w == sizeof...(ts) };
+ };
+}
+
+template<typename num, int h_, int w_>
+class Mat
+{
+ static_assert(h_ > 0 && w_ > 0, "must have positive mat dimensions");
+ num data[h_][w_];
+
+public:
+ template<int Q = w_> typename std::enable_if<equals<Q, 1, 0>::value, num>::type
+ inline operator()(int i) const { return data[i][0]; }
+
+ template<int P = h_> typename std::enable_if<equals<P, 1, 1>::value, num>::type
+ inline operator()(int i) const { return data[0][i]; }
+
+ template<int Q = w_> typename std::enable_if<equals<Q, 1, 2>::value, num&>::type
+ inline operator()(int i) { return data[i][0]; }
+
+ template<int P = h_> typename std::enable_if<equals<P, 1, 3>::value, num&>::type
+ inline operator()(int i) { return data[0][i]; }
+
+#define OPENTRACK_ASSERT_SWIZZLE static_assert(P == h_ && Q == w_, "")
+
+ template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 1>::value, num>::type
+ x() const { OPENTRACK_ASSERT_SWIZZLE; return operator()(0); }
+
+ template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 2>::value, num>::type
+ y() const { OPENTRACK_ASSERT_SWIZZLE; return operator()(1); }
+
+ template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 3>::value, num>::type
+ z() const { OPENTRACK_ASSERT_SWIZZLE; return operator()(2); }
+
+ template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 4>::value, num>::type
+ w() const { OPENTRACK_ASSERT_SWIZZLE; return operator()(3); }
+
+ template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 1>::value, num&>::type
+ x() { OPENTRACK_ASSERT_SWIZZLE; return operator()(0); }
+
+ template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 2>::value, num&>::type
+ y() { OPENTRACK_ASSERT_SWIZZLE; return operator()(1); }
+
+ template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 3>::value, num&>::type
+ z() { OPENTRACK_ASSERT_SWIZZLE; return operator()(2); }
+
+ template<int P = h_, int Q = w_> typename std::enable_if<maybe_add_swizzle<P, Q, 4>::value, num&>::type
+ w() { OPENTRACK_ASSERT_SWIZZLE; return operator()(3); }
+ // parameters w_ and h_ are rebound so that SFINAE occurs
+ // removing them causes a compile-time error -sh 20150811
+
+ template<int R, int S, int P = h_, int Q = w_>
+ typename std::enable_if<is_vector_pair<R, S, P, Q>::value, num>::type
+ dot(const Mat<num, R, S>& p2) const
+ {
+ static_assert(P == h_ && Q == w_, "");
+
+ num ret = 0;
+ constexpr int len = vector_len<R, S>::value;
+ for (int i = 0; i < len; i++)
+ ret += operator()(i) * p2(i);
+ return ret;
+ }
+
+ template<int R, int S, int P = h_, int Q = w_>
+ typename std::enable_if<is_dim3<P, Q, R, S>::value, Mat<num, is_dim3<P, Q, R, S>::P, is_dim3<P, Q, R, S>::Q>>::type
+ cross(const Mat<num, R, S>& p2) const
+ {
+ static_assert(P == h_ && Q == w_, "");
+ decltype(*this)& p1 = *this;
+
+ return Mat<num, R, S>(p1.y() * p2.z() - p2.y() * p1.z(),
+ p2.x() * p1.z() - p1.x() * p2.z(),
+ p1.x() * p2.y() - p1.y() * p2.x());
+ }
+
+ Mat<num, h_, w_> operator+(const Mat<num, h_, w_>& other) const
+ {
+ Mat<num, h_, w_> ret;
+ for (int j = 0; j < h_; j++)
+ for (int i = 0; i < w_; i++)
+ ret(j, i) = data[j][i] + other.data[j][i];
+ return ret;
+ }
+
+ Mat<num, h_, w_> operator-(const Mat<num, h_, w_>& other) const
+ {
+ Mat<num, h_, w_> ret;
+ for (int j = 0; j < h_; j++)
+ for (int i = 0; i < w_; i++)
+ ret(j, i) = data[j][i] - other.data[j][i];
+ return ret;
+ }
+
+ Mat<num, h_, w_> operator+(const num& other) const
+ {
+ Mat<num, h_, w_> ret;
+ for (int j = 0; j < h_; j++)
+ for (int i = 0; i < w_; i++)
+ ret(j, i) = data[j][i] + other;
+ return ret;
+ }
+
+ Mat<num, h_, w_> operator-(const num& other) const
+ {
+ Mat<num, h_, w_> ret;
+ for (int j = 0; j < h_; j++)
+ for (int i = 0; i < w_; i++)
+ ret(j, i) = data[j][i] - other;
+ return ret;
+ }
+
+ template<int p>
+ Mat<num, h_, p> operator*(const Mat<num, w_, p>& other) const
+ {
+ Mat<num, h_, p> ret;
+ for (int k = 0; k < h_; k++)
+ for (int i = 0; i < p; i++)
+ {
+ ret(k, i) = 0;
+ for (int j = 0; j < w_; j++)
+ ret(k, i) += data[k][j] * other(j, i);
+ }
+ return ret;
+ }
+
+ inline num operator()(int j, int i) const { return data[j][i]; }
+ inline num& operator()(int j, int i) { return data[j][i]; }
+
+ template<typename... ts, int h__ = h_, int w__ = w_,
+ typename = typename std::enable_if<is_arglist_correct<num, h__, w__, ts...>::value>::type>
+ Mat(const ts... xs)
+ {
+ static_assert(h__ == h_ && w__ == w_, "");
+
+ std::initializer_list<num> init = { static_cast<num>(xs)... };
+
+ *this = Mat(std::move(init));
+ }
+
+ Mat()
+ {
+ for (int j = 0; j < h_; j++)
+ for (int i = 0; i < w_; i++)
+ data[j][i] = num(0);
+ }
+
+ Mat(const num* mem)
+ {
+ for (int j = 0; j < h_; j++)
+ for (int i = 0; i < w_; i++)
+ data[j][i] = mem[i*h_+j];
+ }
+
+ Mat(std::initializer_list<num>&& init)
+ {
+ auto iter = init.begin();
+ for (int j = 0; j < h_; j++)
+ for (int i = 0; i < w_; i++)
+ data[j][i] = *iter++;
+ }
+
+ operator num*() { return reinterpret_cast<num*>(data); }
+ operator const num*() const { return reinterpret_cast<const num*>(data); }
+
+ // XXX add more operators as needed, third-party dependencies mostly
+ // not needed merely for matrix algebra -sh 20141030
+
+ template<int h__ = h_>
+ static typename std::enable_if<h_ == w_, Mat<num, h__, h__>>::type eye()
+ {
+ static_assert(h_ == h__, "");
+
+ Mat<num, h_, h_> ret;
+ for (int j = 0; j < h_; j++)
+ for (int i = 0; i < w_; i++)
+ ret.data[j][i] = 0;
+
+ for (int i = 0; i < h_; i++)
+ ret.data[i][i] = 1;
+
+ return ret;
+ }
+
+ Mat<num, w_, h_> t() const
+ {
+ Mat<num, w_, h_> ret;
+
+ for (int j = 0; j < h_; j++)
+ for (int i = 0; i < w_; i++)
+ ret(i, j) = data[j][i];
+
+ return ret;
+ }
+};
+
+template<int h_, int w_> using dmat = Mat<double, h_, w_>;
+
+template<typename num, int h, int w>
+Mat<num, h, w> operator*(num scalar, const Mat<num, h, w>& mat)
+{
+ return mat * scalar;
+}
+
+template<typename num, int h_, int w_>
+Mat<num, h_, w_> operator*(const Mat<num, h_, w_>& self, num other)
+{
+ Mat<num, h_, w_> ret;
+ for (int j = 0; j < h_; j++)
+ for (int i = 0; i < w_; i++)
+ ret(j, i) = self(j, i) * other;
+ return ret;
+}
+
+namespace euler {
+
+template<int y, int x> using dmat = Mat<double, y, x>;
+using rmat = dmat<3, 3>;
+using euler_t = dmat<3, 1>;
+
+rmat OPENTRACK_LOGIC_EXPORT euler_to_rmat(const euler_t& input);
+
+euler_t OPENTRACK_LOGIC_EXPORT rmat_to_euler(const rmat& R);
+
+void OPENTRACK_LOGIC_EXPORT tait_bryan_to_matrices(const euler_t& input,
+ rmat& r_roll,
+ rmat& r_pitch,
+ rmat& r_yaw);
+
+} // end ns euler
diff --git a/logic/state.hpp b/logic/state.hpp
new file mode 100644
index 00000000..4c3bb7a0
--- /dev/null
+++ b/logic/state.hpp
@@ -0,0 +1,32 @@
+/* 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 "compat/options.hpp"
+using namespace options;
+#include "api/plugin-support.hpp"
+#include "main-settings.hpp"
+#include "mappings.hpp"
+#include "selected-libraries.hpp"
+#include "work.hpp"
+#include <vector>
+#include <QString>
+
+struct State
+{
+ State(const QString& library_path) :
+ modules(library_path),
+ pose(std::vector<axis_opts*>{&s.a_x, &s.a_y, &s.a_z, &s.a_yaw, &s.a_pitch, &s.a_roll})
+ {}
+ Modules modules;
+ SelectedLibraries libs;
+ main_settings s;
+ Mappings pose;
+ mem<Work> work;
+};
diff --git a/logic/tracker.cpp b/logic/tracker.cpp
new file mode 100644
index 00000000..c7580500
--- /dev/null
+++ b/logic/tracker.cpp
@@ -0,0 +1,385 @@
+/* Copyright (c) 2012-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.
+ */
+
+/*
+ * this file appeared originally in facetracknoir, was rewritten completely
+ * following opentrack fork.
+ *
+ * originally written by Wim Vriend.
+ */
+
+#include "compat/sleep.hpp"
+
+#include "tracker.h"
+#include <cmath>
+#include <algorithm>
+#include <cstdio>
+
+#if defined(_WIN32)
+# include <windows.h>
+#endif
+
+Tracker::Tracker(Mappings &m, SelectedLibraries &libs, TrackLogger &logger) :
+ m(m),
+ newpose {0,0,0, 0,0,0},
+ libs(libs),
+ logger(logger),
+ r_b(get_camera_offset_matrix(c_div).t()),
+ r_b_real(get_camera_offset_matrix(1).t()),
+ t_b {0,0,0},
+ centerp(s.center_at_startup),
+ enabledp(true),
+ zero_(false),
+ should_quit(false)
+{
+}
+
+Tracker::~Tracker()
+{
+ should_quit = true;
+ wait();
+}
+
+Tracker::rmat Tracker::get_camera_offset_matrix(double c)
+{
+ const double off[] =
+ {
+ d2r * c * (double)-s.camera_yaw,
+ d2r * c * (double)-s.camera_pitch,
+ d2r * c * (double)-s.camera_roll
+ };
+
+ return euler::euler_to_rmat(off);
+}
+
+double Tracker::map(double pos, Map& axis)
+{
+ bool altp = (pos < 0) && axis.opts.altp;
+ axis.spline_main.setTrackingActive( !altp );
+ axis.spline_alt.setTrackingActive( altp );
+ auto& fc = altp ? axis.spline_alt : axis.spline_main;
+ return fc.getValue(pos);
+}
+
+void Tracker::t_compensate(const rmat& rmat, const euler_t& xyz_, euler_t& output, bool rz)
+{
+ // TY is really yaw axis. need swapping accordingly.
+ const euler_t ret = rmat * euler_t(xyz_(TZ), -xyz_(TX), -xyz_(TY));
+ if (!rz)
+ output(2) = ret(0);
+ else
+ output(2) = xyz_(2);
+ output(1) = -ret(2);
+ output(0) = -ret(1);
+}
+
+#include "compat/nan.hpp"
+
+static inline double elide_nan(double value, double def)
+{
+ if (nanp(value))
+ {
+ if (nanp(def))
+ return 0;
+ return def;
+ }
+ return value;
+}
+
+static bool is_nan(const dmat<3,3>& r, const dmat<3, 1>& t)
+{
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ if (nanp(r(i, j)))
+ return true;
+
+ for (int i = 0; i < 3; i++)
+ if (nanp(t(i)))
+ return true;
+
+ return false;
+}
+
+static bool is_nan(const Pose& value)
+{
+ for (int i = 0; i < 6; i++)
+ if (nanp(value(i)))
+ return true;
+ return false;
+}
+
+constexpr double Tracker::c_mult;
+constexpr double Tracker::c_div;
+
+void Tracker::logic()
+{
+ using namespace euler;
+
+ logger.write_dt();
+ logger.reset_dt();
+
+ Pose value, raw;
+
+ for (int i = 0; i < 6; i++)
+ {
+ auto& axis = m(i);
+ int k = axis.opts.src;
+ if (k < 0 || k >= 6)
+ value(i) = 0;
+ else
+ value(i) = newpose[k];
+ raw(i) = newpose[i];
+ }
+
+ logger.write_pose(raw); // raw
+
+ if (is_nan(raw))
+ raw = last_raw;
+
+ // TODO fix gimbal lock by dividing euler angle input by >=3.
+ // maintain the real rmat separately for translation compensation
+ // TODO split this function, it's too big
+ rmat r, r_real;
+
+ {
+ euler_t tmp = d2r * euler_t(&value[Yaw]);
+ r = euler_to_rmat(c_div * tmp);
+ r_real = euler_to_rmat(tmp);
+ }
+
+ euler_t t(value(0), value(1), value(2));
+
+ bool do_center_now = false;
+ bool nan = is_nan(r, t);
+
+ if (centerp && !nan)
+ {
+ for (int i = 0; i < 6; i++)
+ if (fabs(newpose[i]) != 0)
+ {
+ do_center_now = true;
+ break;
+ }
+ }
+
+ const rmat cam = get_camera_offset_matrix(c_div);
+ const rmat cam_real = get_camera_offset_matrix(1);
+
+ r = r * cam;
+ r_real = r_real * cam_real;
+
+ if (do_center_now)
+ {
+ centerp = false;
+
+ if (libs.pFilter)
+ libs.pFilter->center();
+
+ if (libs.pTracker->center())
+ {
+ r_b = cam.t();
+ r_b_real = cam_real.t();
+ r = rmat::eye();
+ r_real = rmat::eye();
+ }
+ else
+ {
+ r_b = r.t();
+ r_b_real = r_real.t();
+ }
+
+ for (int i = 0; i < 3; i++)
+ t_b[i] = t(i);
+ }
+
+ {
+ switch (s.center_method)
+ {
+ // inertial
+ case 0:
+ default:
+ r = r_b * r;
+ break;
+ // camera
+ case 1:
+ r = r * r_b;
+ break;
+ }
+
+ const euler_t rot = r2d * c_mult * rmat_to_euler(r);
+ euler_t pos(t(0) - t_b[0], t(1) - t_b[1], t(2) - t_b[2]);
+
+ if (s.use_camera_offset_from_centering)
+ t_compensate(r_b_real.t() * cam_real.t(), pos, pos, false);
+ else
+ t_compensate(cam_real.t(), pos, pos, false);
+
+ for (int i = 0; i < 3; i++)
+ {
+ value(i) = pos(i);
+ value(i+3) = rot(i);
+ }
+ }
+
+ logger.write_pose(value); // "corrected" - after various transformations to account for camera position
+
+ // whenever something can corrupt its internal state due to nan/inf, elide the call
+ if (is_nan(value))
+ {
+ nan = true;
+ logger.write_pose(value); // "filtered"
+ }
+ else
+ {
+ {
+ Pose tmp = value;
+
+ if (libs.pFilter)
+ libs.pFilter->filter(tmp, value);
+ }
+ logger.write_pose(value); // "filtered"
+
+ // CAVEAT rotation only, due to tcomp
+ for (int i = 3; i < 6; i++)
+ value(i) = map(value(i), m(i));
+
+ for (int i = 0; i < 6; i++)
+ value(i) += m(i).opts.zero;
+
+ for (int i = 0; i < 6; i++)
+ value(i) *= int(m(i).opts.invert) * -2 + 1;
+
+ if (zero_)
+ for (int i = 0; i < 6; i++)
+ value(i) = 0;
+
+ if (is_nan(value))
+ nan = true;
+ }
+
+ if (s.tcomp_p)
+ {
+ euler_t value_(value(TX), value(TY), value(TZ));
+ t_compensate(euler_to_rmat(euler_t(value(Yaw) * d2r, value(Pitch) * d2r, value(Roll) * d2r)),
+ value_,
+ value_,
+ s.tcomp_tz);
+ if (is_nan(r, value_))
+ nan = true;
+ for (int i = 0; i < 3; i++)
+ value(i) = value_(i);
+ }
+
+ // CAVEAT translation only, due to tcomp
+ for (int i = 0; i < 3; i++)
+ value(i) = map(value(i), m(i));
+
+ logger.write_pose(value); // "mapped"
+
+ if (nan)
+ {
+ value = last_mapped;
+
+ // for widget last value display
+ for (int i = 0; i < 6; i++)
+ (void) map(value(i), m(i));
+ }
+
+ libs.pProtocol->pose(value);
+
+ last_mapped = value;
+ last_raw = raw;
+
+ QMutexLocker foo(&mtx);
+ output_pose = value;
+ raw_6dof = raw;
+
+ logger.reset_dt();
+ logger.next_line();
+}
+
+void Tracker::run()
+{
+#if defined(_WIN32)
+ (void) timeBeginPeriod(1);
+#endif
+
+ setPriority(QThread::HighPriority);
+
+ {
+ static constexpr const char* posechannels[6] = { "TX", "TY", "TZ", "Yaw", "Pitch", "Roll" };
+ static constexpr const char* datachannels[5] = { "dt", "raw", "corrected", "filtered", "mapped" };
+ logger.write(datachannels[0]);
+ char buffer[128];
+ for (unsigned j = 1; j < 5; ++j)
+ {
+ for (unsigned i = 0; i < 6; ++i)
+ {
+ std::sprintf(buffer, "%s%s", datachannels[j], posechannels[i]);
+ logger.write(buffer);
+ }
+ }
+ logger.next_line();
+ }
+
+ t.start();
+ logger.reset_dt();
+
+ while (!should_quit)
+ {
+ double tmp[6] {0,0,0, 0,0,0};
+ libs.pTracker->data(tmp);
+
+ if (enabledp)
+ for (int i = 0; i < 6; i++)
+ newpose[i] = elide_nan(tmp[i], newpose[i]);
+
+ logic();
+
+ static constexpr long const_sleep_us = 4000;
+
+ using std::max;
+ using std::min;
+
+ const long elapsed_usecs = t.elapsed_usecs();
+ const long sleep_us = const_sleep_us * 2 - elapsed_usecs;
+
+ const unsigned sleep_time = unsigned(max(1l, min(const_sleep_us * 4, max(1l, (sleep_us + 200l)/1000l))));
+
+ t.start();
+
+ portable::sleep(unsigned(max(1u, sleep_time)));
+ }
+
+ {
+ // filter may inhibit exact origin
+ Pose p;
+ libs.pProtocol->pose(p);
+ }
+
+#if defined(_WIN32)
+ (void) timeEndPeriod(1);
+#endif
+
+ for (int i = 0; i < 6; i++)
+ {
+ m(i).spline_main.setTrackingActive(false);
+ m(i).spline_alt.setTrackingActive(false);
+ }
+}
+
+void Tracker::get_raw_and_mapped_poses(double* mapped, double* raw) const
+{
+ QMutexLocker foo(&const_cast<Tracker&>(*this).mtx);
+
+ for (int i = 0; i < 6; i++)
+ {
+ raw[i] = raw_6dof(i);
+ mapped[i] = output_pose(i);
+ }
+}
+
diff --git a/logic/tracker.h b/logic/tracker.h
new file mode 100644
index 00000000..15fb9701
--- /dev/null
+++ b/logic/tracker.h
@@ -0,0 +1,85 @@
+/* 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 <vector>
+
+#include "compat/pi-constant.hpp"
+#include "compat/timer.hpp"
+#include "api/plugin-support.hpp"
+#include "mappings.hpp"
+#include "simple-mat.hpp"
+#include "selected-libraries.hpp"
+
+#include "spline-widget/spline.hpp"
+#include "main-settings.hpp"
+#include "compat/options.hpp"
+#include "tracklogger.hpp"
+
+#include <QMutex>
+#include <QThread>
+
+#include "export.hpp"
+
+using Pose = Mat<double, 6, 1>;
+
+class OPENTRACK_LOGIC_EXPORT Tracker : private QThread
+{
+ Q_OBJECT
+private:
+ using rmat = euler::rmat;
+ using euler_t = euler::euler_t;
+
+ QMutex mtx;
+ main_settings s;
+ Mappings& m;
+
+ Timer t;
+ Pose output_pose, raw_6dof, last_mapped, last_raw;
+
+ double newpose[6];
+ SelectedLibraries const& libs;
+ // The owner of the reference is the main window.
+ // This design might be usefull if we decide later on to swap out
+ // the logger while the tracker is running.
+ TrackLogger &logger;
+
+ rmat r_b, r_b_real;
+ double t_b[3];
+
+ volatile bool centerp;
+ volatile bool enabledp;
+ volatile bool zero_;
+ volatile bool should_quit;
+
+ double map(double pos, Map& axis);
+ void logic();
+ void t_compensate(const rmat& rmat, const euler_t& ypr, euler_t& output, bool rz);
+ void run() override;
+
+ static constexpr double pi = OPENTRACK_PI;
+ static constexpr double r2d = 180. / OPENTRACK_PI;
+ static constexpr double d2r = OPENTRACK_PI / 180.;
+
+ // note: float exponent base is 2
+ static constexpr double c_mult = 4;
+ static constexpr double c_div = 1./c_mult;
+public:
+ Tracker(Mappings& m, SelectedLibraries& libs, TrackLogger &logger);
+ ~Tracker();
+
+ rmat get_camera_offset_matrix(double c);
+ void get_raw_and_mapped_poses(double* mapped, double* raw) const;
+ void start() { QThread::start(); }
+ void toggle_enabled() { enabledp = !enabledp; }
+ void set_toggle(bool value) { enabledp = value; }
+ void set_zero(bool value) { zero_ = value; }
+ void center() { centerp = !centerp; }
+ void zero() { zero_ = !zero_; }
+};
diff --git a/logic/tracklogger.cpp b/logic/tracklogger.cpp
new file mode 100644
index 00000000..64dda579
--- /dev/null
+++ b/logic/tracklogger.cpp
@@ -0,0 +1,47 @@
+#include "tracklogger.hpp"
+#include "tracker.h"
+
+TrackLogger::~TrackLogger() {}
+
+void TrackLogger::reset_dt()
+{
+ t.start();
+}
+
+void TrackLogger::write_dt()
+{
+ const double dt = t.elapsed_seconds();
+ write(&dt, 1);
+}
+
+void TrackLoggerCSV::handle_first_col_sep()
+{
+ if (!first_col)
+ out.put(',');
+ first_col = false;
+}
+
+void TrackLoggerCSV::write(const char *s)
+{
+ handle_first_col_sep();
+ out << s;
+}
+
+
+void TrackLoggerCSV::write(const double *p, int n)
+{
+ handle_first_col_sep();
+ for (int i = 0; i < n-1; ++i)
+ {
+ out << p[i];
+ out.put(',');
+ }
+ out << p[n-1];
+}
+
+void TrackLoggerCSV::next_line()
+{
+ out << std::endl;
+ first_col = true;
+}
+
diff --git a/logic/tracklogger.hpp b/logic/tracklogger.hpp
new file mode 100644
index 00000000..52ab35bc
--- /dev/null
+++ b/logic/tracklogger.hpp
@@ -0,0 +1,65 @@
+#pragma once
+#include "main-settings.hpp"
+#include "compat/options.hpp"
+#include "compat/timer.hpp"
+
+#include <fstream>
+#include <QString>
+#include <QMessageBox>
+#include <QWidget>
+
+class OPENTRACK_LOGIC_EXPORT TrackLogger
+{
+ TrackLogger(TrackLogger&&) = delete;
+ TrackLogger(const TrackLogger&) = delete;
+ TrackLogger& operator=(const TrackLogger&) = delete;
+ TrackLogger& operator=(TrackLogger&&) = delete;
+
+ Timer t;
+
+public:
+ TrackLogger()
+ {
+ }
+
+ virtual ~TrackLogger();
+
+ virtual void write(const char *)
+ {
+ }
+
+ virtual void write(const double *, int)
+ {
+ }
+
+ virtual void next_line()
+ {
+ }
+
+ void write_pose(const double *p)
+ {
+ write(p, 6);
+ }
+
+ void reset_dt();
+ void write_dt();
+};
+
+
+class OPENTRACK_LOGIC_EXPORT TrackLoggerCSV : public TrackLogger
+{
+ std::ofstream out;
+ bool first_col;
+ inline void handle_first_col_sep();
+public:
+ TrackLoggerCSV(const QString &filename) : first_col(true)
+ {
+ out.open(filename.toStdString());
+ }
+
+ bool is_open() const { return out.is_open(); }
+ void write(const char *s) override;
+ void write(const double *p, int n) override;
+ void next_line() override;
+};
+
diff --git a/logic/win32-shortcuts.cpp b/logic/win32-shortcuts.cpp
new file mode 100644
index 00000000..df9145a0
--- /dev/null
+++ b/logic/win32-shortcuts.cpp
@@ -0,0 +1,196 @@
+/* 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.
+ */
+
+#if defined(_WIN32)
+# ifndef DIRECTINPUT_VERSION
+# define DIRECTINPUT_VERSION 0x800
+# endif
+# include <dinput.h>
+
+#include "win32-shortcuts.h"
+#include <QList>
+#include <QVariant>
+#include <QDebug>
+
+QList<win_key> windows_key_mods =
+ QList<win_key>({
+ win_key(DIK_LCONTROL, Qt::Key::Key_Control),
+ win_key(DIK_RCONTROL, Qt::Key::Key_Control),
+ win_key(DIK_LALT, Qt::Key::Key_Alt),
+ win_key(DIK_RALT, Qt::Key::Key_Alt),
+ win_key(DIK_LSHIFT, Qt::Key::Key_Shift),
+ win_key(DIK_RSHIFT, Qt::Key::Key_Shift),
+ win_key(DIK_LWIN, Qt::Key::Key_unknown),
+ win_key(DIK_RWIN, Qt::Key::Key_unknown)
+ });
+
+QList<win_key> windows_key_sequences =
+ QList<win_key>({
+ win_key(DIK_F1, Qt::Key::Key_F1 ),
+ win_key(DIK_F2, Qt::Key::Key_F2 ),
+ win_key(DIK_F3, Qt::Key::Key_F3 ),
+ win_key(DIK_F4, Qt::Key::Key_F4 ),
+ win_key(DIK_F5, Qt::Key::Key_F5 ),
+ win_key(DIK_F6, Qt::Key::Key_F6 ),
+ win_key(DIK_F7, Qt::Key::Key_F7 ),
+ win_key(DIK_F8, Qt::Key::Key_F8 ),
+ win_key(DIK_F9, Qt::Key::Key_F9 ),
+ win_key(DIK_F10, Qt::Key::Key_F10 ),
+ win_key(DIK_F11, Qt::Key::Key_F11 ),
+ win_key(DIK_F12, Qt::Key::Key_F12 ),
+ win_key(DIK_LEFT, Qt::Key::Key_Left ),
+ win_key(DIK_RIGHT, Qt::Key::Key_Right ),
+ win_key(DIK_UP, Qt::Key::Key_Up ),
+ win_key(DIK_DOWN, Qt::Key::Key_Down ),
+ win_key(DIK_PRIOR, Qt::Key::Key_PageUp ),
+ win_key(DIK_NEXT, Qt::Key::Key_PageDown ),
+ win_key(DIK_HOME, Qt::Key::Key_Home ),
+ win_key(DIK_END, Qt::Key::Key_End ),
+ win_key(DIK_BACK, Qt::Key::Key_Backspace ),
+ win_key(DIK_COMMA, Qt::Key::Key_Comma ),
+ win_key(DIK_PERIOD, Qt::Key::Key_Period ),
+ win_key(DIK_LBRACKET, Qt::Key::Key_BracketLeft ),
+ win_key(DIK_RBRACKET, Qt::Key::Key_BracketRight ),
+ win_key(DIK_SEMICOLON, Qt::Key::Key_Semicolon ),
+ win_key(DIK_SLASH, Qt::Key::Key_Slash ),
+ win_key(DIK_BACKSLASH, Qt::Key::Key_Backslash ),
+ win_key(DIK_BACKSPACE, Qt::Key::Key_Backspace ),
+ win_key(DIK_APOSTROPHE, Qt::Key::Key_Apostrophe ),
+ win_key(DIK_GRAVE, Qt::Key::Key_QuoteLeft ),
+ win_key(DIK_MINUS, Qt::Key::Key_Minus ),
+ win_key(DIK_EQUALS, Qt::Key::Key_Equal ),
+ win_key(DIK_PERIOD, Qt::Key::Key_Period ),
+ win_key(DIK_F1, Qt::Key::Key_F1 ),
+ win_key(DIK_F2, Qt::Key::Key_F2 ),
+ win_key(DIK_F3, Qt::Key::Key_F3 ),
+ win_key(DIK_F4, Qt::Key::Key_F4 ),
+ win_key(DIK_F5, Qt::Key::Key_F5 ),
+ win_key(DIK_F6, Qt::Key::Key_F6 ),
+ win_key(DIK_F7, Qt::Key::Key_F7 ),
+ win_key(DIK_F8, Qt::Key::Key_F8 ),
+ win_key(DIK_F9, Qt::Key::Key_F9 ),
+ win_key(DIK_F10, Qt::Key::Key_F10 ),
+ win_key(DIK_F11, Qt::Key::Key_F11 ),
+ win_key(DIK_F12, Qt::Key::Key_F12 ),
+ win_key(DIK_0, Qt::Key::Key_0 ),
+ win_key(DIK_1, Qt::Key::Key_1 ),
+ win_key(DIK_2, Qt::Key::Key_2 ),
+ win_key(DIK_3, Qt::Key::Key_3 ),
+ win_key(DIK_4, Qt::Key::Key_4 ),
+ win_key(DIK_5, Qt::Key::Key_5 ),
+ win_key(DIK_6, Qt::Key::Key_6 ),
+ win_key(DIK_7, Qt::Key::Key_7 ),
+ win_key(DIK_8, Qt::Key::Key_8 ),
+ win_key(DIK_9, Qt::Key::Key_9 ),
+ win_key(DIK_A, Qt::Key::Key_A ),
+ win_key(DIK_B, Qt::Key::Key_B ),
+ win_key(DIK_C, Qt::Key::Key_C ),
+ win_key(DIK_D, Qt::Key::Key_D ),
+ win_key(DIK_E, Qt::Key::Key_E ),
+ win_key(DIK_F, Qt::Key::Key_F ),
+ win_key(DIK_G, Qt::Key::Key_G ),
+ win_key(DIK_H, Qt::Key::Key_H ),
+ win_key(DIK_I, Qt::Key::Key_I ),
+ win_key(DIK_J, Qt::Key::Key_J ),
+ win_key(DIK_K, Qt::Key::Key_K ),
+ win_key(DIK_L, Qt::Key::Key_L ),
+ win_key(DIK_M, Qt::Key::Key_M ),
+ win_key(DIK_N, Qt::Key::Key_N ),
+ win_key(DIK_O, Qt::Key::Key_O ),
+ win_key(DIK_P, Qt::Key::Key_P ),
+ win_key(DIK_Q, Qt::Key::Key_Q ),
+ win_key(DIK_R, Qt::Key::Key_R ),
+ win_key(DIK_S, Qt::Key::Key_S ),
+ win_key(DIK_T, Qt::Key::Key_T ),
+ win_key(DIK_U, Qt::Key::Key_U ),
+ win_key(DIK_V, Qt::Key::Key_V ),
+ win_key(DIK_W, Qt::Key::Key_W ),
+ win_key(DIK_X, Qt::Key::Key_X ),
+ win_key(DIK_Y, Qt::Key::Key_Y ),
+ win_key(DIK_Z, Qt::Key::Key_Z ),
+ win_key(DIK_RETURN, Qt::Key::Key_Return),
+ win_key(DIK_INSERT, Qt::Key::Key_Insert),
+ win_key(DIK_DELETE, Qt::Key::Key_Delete),
+ win_key(DIK_SPACE, Qt::Key::Key_Space),
+ win_key(DIK_SYSRQ, Qt::Key::Key_Print),
+ win_key(DIK_SCROLL, Qt::Key::Key_ScrollLock),
+ win_key(DIK_PAUSE, Qt::Key::Key_Pause),
+ win_key(DIK_NUMLOCK, Qt::Key::Key_NumLock),
+#define mod(x, y) static_cast<Qt::Key>(x | y)
+ win_key(DIK_NUMPAD0, mod(Qt::Key::Key_0, Qt::KeypadModifier)),
+ win_key(DIK_NUMPAD0, mod(Qt::Key::Key_0, Qt::KeypadModifier)),
+ win_key(DIK_NUMPAD1, mod(Qt::Key::Key_1, Qt::KeypadModifier)),
+ win_key(DIK_NUMPAD2, mod(Qt::Key::Key_2, Qt::KeypadModifier)),
+ win_key(DIK_NUMPAD3, mod(Qt::Key::Key_3, Qt::KeypadModifier)),
+ win_key(DIK_NUMPAD4, mod(Qt::Key::Key_4, Qt::KeypadModifier)),
+ win_key(DIK_NUMPAD5, mod(Qt::Key::Key_5, Qt::KeypadModifier)),
+ win_key(DIK_NUMPAD6, mod(Qt::Key::Key_6, Qt::KeypadModifier)),
+ win_key(DIK_NUMPAD7, mod(Qt::Key::Key_7, Qt::KeypadModifier)),
+ win_key(DIK_NUMPAD8, mod(Qt::Key::Key_8, Qt::KeypadModifier)),
+ win_key(DIK_NUMPAD9, mod(Qt::Key::Key_9, Qt::KeypadModifier)),
+ win_key(DIK_NUMPADCOMMA, mod(Qt::Key::Key_Comma, Qt::KeypadModifier)),
+ win_key(DIK_NUMPADENTER, mod(Qt::Key::Key_Enter, Qt::KeypadModifier)),
+ win_key(DIK_NUMPADEQUALS, mod(Qt::Key::Key_Equal, Qt::KeypadModifier)),
+ win_key(DIK_NUMPADMINUS, mod(Qt::Key::Key_Minus, Qt::KeypadModifier)),
+ win_key(DIK_NUMPADPERIOD, mod(Qt::Key::Key_Period, Qt::KeypadModifier)),
+ win_key(DIK_NUMPADPLUS, mod(Qt::Key::Key_Plus, Qt::KeypadModifier)),
+ win_key(DIK_NUMPADSLASH, mod(Qt::Key::Key_Slash, Qt::KeypadModifier)),
+ win_key(DIK_NUMPADSTAR, mod(Qt::Key::Key_multiply, Qt::KeypadModifier)),
+ });
+
+bool win_key::to_qt(const Key& k, QKeySequence& qt_, Qt::KeyboardModifiers &mods)
+{
+ for (auto& wk : windows_key_sequences)
+ {
+ if (wk.win == k.keycode)
+ {
+ qt_ = wk.qt;
+ mods = Qt::NoModifier;
+ if (k.ctrl) mods |= Qt::ControlModifier;
+ if (k.shift) mods |= Qt::ShiftModifier;
+ if (k.alt) mods |= Qt::AltModifier;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool win_key::from_qt(QKeySequence qt_, int& dik, Qt::KeyboardModifiers& mods)
+{
+ // CAVEAT don't use QVariant::toUInt() or conversion fails
+ const unsigned qt = static_cast<unsigned>(QVariant(qt_).toInt());
+ const unsigned our_mods = qt & Qt::KeyboardModifierMask;
+
+ {
+ const auto key_ = qt;
+ for (auto& wk : windows_key_sequences)
+ {
+ if (wk.qt == key_)
+ {
+ dik = wk.win;
+ mods = Qt::NoModifier;
+ return true;
+ }
+ }
+ }
+ {
+ const unsigned key = qt & ~Qt::KeyboardModifierMask;
+ for (auto& wk : windows_key_sequences)
+ {
+ if (wk.qt == key)
+ {
+ dik = wk.win;
+ mods = static_cast<Qt::KeyboardModifiers>(our_mods);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+#endif
diff --git a/logic/win32-shortcuts.h b/logic/win32-shortcuts.h
new file mode 100644
index 00000000..7626a31f
--- /dev/null
+++ b/logic/win32-shortcuts.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#ifdef _WIN32
+
+#include <QKeySequence>
+#include "shortcuts.h"
+
+struct win_key;
+
+extern QList<win_key> windows_key_mods;
+extern QList<win_key> windows_key_sequences;
+
+#include "export.hpp"
+
+struct OPENTRACK_LOGIC_EXPORT win_key
+{
+ win_key(int win, Qt::Key qt) : win(win), qt(qt) {}
+ int win;
+ Qt::Key qt;
+ static bool from_qt(QKeySequence qt_, int& dik, Qt::KeyboardModifiers &mods);
+ static bool to_qt(const Key& k, QKeySequence& qt_, Qt::KeyboardModifiers &mods);
+};
+
+#endif
diff --git a/logic/work.cpp b/logic/work.cpp
new file mode 100644
index 00000000..8d00270b
--- /dev/null
+++ b/logic/work.cpp
@@ -0,0 +1,73 @@
+#include "work.hpp"
+
+#include <QMessageBox>
+
+
+std::shared_ptr<TrackLogger> Work::make_logger(const main_settings &s)
+{
+ if (s.tracklogging_enabled)
+ {
+ if (static_cast<QString>(s.tracklogging_filename).isEmpty())
+ {
+ QMessageBox::warning(nullptr, "Logging Error",
+ "No filename given for track logging. Proceeding without logging.",
+ QMessageBox::Ok,
+ QMessageBox::NoButton);
+ }
+ else
+ {
+ auto logger = std::make_shared<TrackLoggerCSV>(s.tracklogging_filename);
+ if (!logger->is_open())
+ {
+ logger = nullptr;
+ QMessageBox::warning(nullptr, "Logging Error",
+ "Unable to open file: " + s.tracklogging_filename + ". Proceeding without logging.",
+ QMessageBox::Ok,
+ QMessageBox::NoButton);
+ }
+ else
+ {
+ /* As this function has the potential to fill up the hard drive
+ of the unwary with junk data, a warning is in order. */
+ QMessageBox::warning(nullptr, "Logging Active",
+ "Just a heads up. You are recoding pose data to " + s.tracklogging_filename + "!",
+ QMessageBox::Ok,
+ QMessageBox::NoButton);
+ return logger;
+ }
+ }
+ }
+ return std::make_shared<TrackLogger>();
+}
+
+
+Work::Work(Mappings& m, SelectedLibraries& libs, WId handle) :
+ libs(libs),
+ logger(make_logger(s)),
+ tracker(std::make_shared<Tracker>(m, libs, *logger)),
+ sc(std::make_shared<Shortcuts>()),
+ handle(handle),
+ keys {
+ key_tuple(s.key_center, [&](bool) -> void { tracker->center(); }, true),
+ key_tuple(s.key_toggle, [&](bool) -> void { tracker->toggle_enabled(); }, true),
+ key_tuple(s.key_zero, [&](bool) -> void { tracker->zero(); }, true),
+ key_tuple(s.key_toggle_press, [&](bool x) -> void { tracker->set_toggle(!x); }, false),
+ key_tuple(s.key_zero_press, [&](bool x) -> void { tracker->set_zero(x); }, false),
+ }
+{
+ reload_shortcuts();
+ tracker->start();
+}
+
+void Work::reload_shortcuts()
+{
+ sc->reload(keys);
+}
+
+Work::~Work()
+{
+ sc = nullptr;
+ // order matters, otherwise use-after-free -sh
+ tracker = nullptr;
+ libs = SelectedLibraries();
+}
diff --git a/logic/work.hpp b/logic/work.hpp
new file mode 100644
index 00000000..b20b1824
--- /dev/null
+++ b/logic/work.hpp
@@ -0,0 +1,43 @@
+/* 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 "main-settings.hpp"
+#include "api/plugin-support.hpp"
+#include "tracker.h"
+#include "shortcuts.h"
+#include "export.hpp"
+#include "tracklogger.hpp"
+
+#include <QObject>
+#include <QFrame>
+#include <memory>
+#include <vector>
+#include <tuple>
+#include <functional>
+
+struct OPENTRACK_LOGIC_EXPORT Work
+{
+ using fn_t = std::function<void(bool)>;
+ using key_tuple = std::tuple<key_opts&, fn_t, bool>;
+ main_settings s; // tracker needs settings, so settings must come before it
+ SelectedLibraries& libs;
+ std::shared_ptr<TrackLogger> logger; // must come before tracker, since tracker depends on it
+ std::shared_ptr<Tracker> tracker;
+ std::shared_ptr<Shortcuts> sc;
+ WId handle;
+ std::vector<key_tuple> keys;
+
+ Work(Mappings& m, SelectedLibraries& libs, WId handle);
+ ~Work();
+ void reload_shortcuts();
+
+private:
+ std::shared_ptr<TrackLogger> make_logger(const main_settings &s);
+};