diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2016-08-12 18:00:49 +0200 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2016-08-12 18:00:49 +0200 |
commit | 9040b187a1c4fa380f8a12207b9dd6d04b3a10ac (patch) | |
tree | 115e1351571d690c1261a9d512e6d44e717f3051 /logic | |
parent | 13a18b149764509a3f460be86590250cdcf690fb (diff) |
all: rename modules s#^opentrack-##. and opentrack -> api
Adjust usages.
Diffstat (limited to 'logic')
-rw-r--r-- | logic/CMakeLists.txt | 7 | ||||
-rw-r--r-- | logic/export.hpp | 28 | ||||
-rw-r--r-- | logic/main-settings.hpp | 109 | ||||
-rw-r--r-- | logic/mappings.hpp | 89 | ||||
-rw-r--r-- | logic/selected-libraries.cpp | 37 | ||||
-rw-r--r-- | logic/selected-libraries.hpp | 25 | ||||
-rw-r--r-- | logic/shortcuts.cpp | 130 | ||||
-rw-r--r-- | logic/shortcuts.h | 68 | ||||
-rw-r--r-- | logic/simple-mat.cpp | 97 | ||||
-rw-r--r-- | logic/simple-mat.hpp | 279 | ||||
-rw-r--r-- | logic/state.hpp | 32 | ||||
-rw-r--r-- | logic/tracker.cpp | 385 | ||||
-rw-r--r-- | logic/tracker.h | 85 | ||||
-rw-r--r-- | logic/tracklogger.cpp | 47 | ||||
-rw-r--r-- | logic/tracklogger.hpp | 65 | ||||
-rw-r--r-- | logic/win32-shortcuts.cpp | 196 | ||||
-rw-r--r-- | logic/win32-shortcuts.h | 24 | ||||
-rw-r--r-- | logic/work.cpp | 73 | ||||
-rw-r--r-- | logic/work.hpp | 43 |
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); +}; |