summaryrefslogtreecommitdiffhomepage
path: root/opentrack-logic
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2016-06-16 12:34:31 +0200
committerStanislaw Halik <sthalik@misaki.pl>2016-06-16 12:35:51 +0200
commit0760fe011114fa440275b487eaf766b015f40e5b (patch)
treea0141fb291b7dc1e38d16dd0eb2c767ec93b310a /opentrack-logic
parent60460f56cabe0155996adf8ba5e9f6730ef0b203 (diff)
all: split "api" into "api" and "logic"
Here, the "logic" module has all the stuff for building one's own graphical user interface. The "api" module has stuff used by other trackers. While at it, each of "api", "logic", and "compat" need their own export headers. This is because of preprocessor symbol clashes. This is all because a change in the "gui"-only dependency required a relink of all the trackers, protocols, and flters. It takes too long when building in the release configuration. With the split, only the "gui" module gets rebuilt. Since it has close to no static dependencies, it's fast enough.
Diffstat (limited to 'opentrack-logic')
-rw-r--r--opentrack-logic/CMakeLists.txt10
-rw-r--r--opentrack-logic/export.hpp18
-rw-r--r--opentrack-logic/import.hpp13
-rw-r--r--opentrack-logic/keybinding-worker.cpp194
-rw-r--r--opentrack-logic/keybinding-worker.hpp78
-rw-r--r--opentrack-logic/main-settings.hpp92
-rw-r--r--opentrack-logic/mappings.hpp89
-rw-r--r--opentrack-logic/nan.cpp16
-rw-r--r--opentrack-logic/selected-libraries.cpp37
-rw-r--r--opentrack-logic/selected-libraries.hpp25
-rw-r--r--opentrack-logic/shortcuts.cpp130
-rw-r--r--opentrack-logic/shortcuts.h66
-rw-r--r--opentrack-logic/state.hpp29
-rw-r--r--opentrack-logic/tracker.cpp305
-rw-r--r--opentrack-logic/tracker.h85
-rw-r--r--opentrack-logic/win32-joystick.cpp297
-rw-r--r--opentrack-logic/win32-joystick.hpp96
-rw-r--r--opentrack-logic/win32-shortcuts.cpp197
-rw-r--r--opentrack-logic/win32-shortcuts.h24
-rw-r--r--opentrack-logic/work.hpp63
20 files changed, 1864 insertions, 0 deletions
diff --git a/opentrack-logic/CMakeLists.txt b/opentrack-logic/CMakeLists.txt
new file mode 100644
index 00000000..000276d5
--- /dev/null
+++ b/opentrack-logic/CMakeLists.txt
@@ -0,0 +1,10 @@
+opentrack_boilerplate(opentrack-logic)
+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 winmm)
+endif()
+if(CMAKE_COMPILER_IS_GNUCXX)
+ set_source_files_properties(nan.cpp PROPERTIES COMPILE_FLAGS "-fno-fast-math -fno-finite-math-only -fno-fast-math -O2")
+endif()
diff --git a/opentrack-logic/export.hpp b/opentrack-logic/export.hpp
new file mode 100644
index 00000000..699fdd6a
--- /dev/null
+++ b/opentrack-logic/export.hpp
@@ -0,0 +1,18 @@
+#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_LINKAGE
+# endif
+
+#else
+# include "import.hpp"
+#endif \ No newline at end of file
diff --git a/opentrack-logic/import.hpp b/opentrack-logic/import.hpp
new file mode 100644
index 00000000..8a3e80c1
--- /dev/null
+++ b/opentrack-logic/import.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#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
diff --git a/opentrack-logic/keybinding-worker.cpp b/opentrack-logic/keybinding-worker.cpp
new file mode 100644
index 00000000..a0c7178c
--- /dev/null
+++ b/opentrack-logic/keybinding-worker.cpp
@@ -0,0 +1,194 @@
+/* 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.
+ */
+
+#ifdef _WIN32
+
+#include "keybinding-worker.hpp"
+#include <functional>
+#include <windows.h>
+#include <QDebug>
+#include <QMutexLocker>
+
+bool Key::should_process()
+{
+ if (!enabled || (keycode == 0 && guid == ""))
+ return false;
+ bool ret = timer.elapsed_ms() > 100;
+ timer.start();
+ return ret;
+}
+
+KeybindingWorker::~KeybindingWorker() {
+ should_quit = true;
+ wait();
+ if (dinkeyboard) {
+ dinkeyboard->Unacquire();
+ dinkeyboard->Release();
+ }
+ if (din)
+ din->Release();
+}
+
+KeybindingWorker::KeybindingWorker() :
+ should_quit(true)
+{
+ if (DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&din, NULL) != DI_OK) {
+ qDebug() << "setup DirectInput8 Creation failed!" << GetLastError();
+ return;
+ }
+ if (din->CreateDevice(GUID_SysKeyboard, &dinkeyboard, NULL) != DI_OK) {
+ din->Release();
+ din = 0;
+ qDebug() << "setup CreateDevice function failed!" << GetLastError();
+ return;
+ }
+ if (dinkeyboard->SetDataFormat(&c_dfDIKeyboard) != DI_OK) {
+ qDebug() << "setup SetDataFormat function failed!" << GetLastError();
+ dinkeyboard->Release();
+ dinkeyboard = 0;
+ din->Release();
+ din = 0;
+ return;
+ }
+
+ if (dinkeyboard->SetCooperativeLevel((HWND) fake_main_window.winId(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND) != DI_OK) {
+ dinkeyboard->Release();
+ din->Release();
+ din = 0;
+ dinkeyboard = 0;
+ qDebug() << "setup SetCooperativeLevel function failed!" << GetLastError();
+ return;
+ }
+ if (dinkeyboard->Acquire() != DI_OK)
+ {
+ dinkeyboard->Release();
+ din->Release();
+ din = 0;
+ dinkeyboard = 0;
+ qDebug() << "setup dinkeyboard Acquire failed!" << GetLastError();
+ return;
+ }
+ should_quit = false;
+ start();
+}
+
+KeybindingWorker& KeybindingWorker::make()
+{
+ static KeybindingWorker k;
+ return k;
+}
+
+void KeybindingWorker::run() {
+ BYTE keystate[256] = {0};
+ BYTE old_keystate[256] = {0};
+
+ while (!should_quit)
+ {
+ {
+ QMutexLocker l(&mtx);
+
+ if (receivers.size())
+ {
+ {
+ const HRESULT hr = dinkeyboard->GetDeviceState(256, (LPVOID)keystate);
+
+ if (hr != DI_OK) {
+ qDebug() << "Tracker::run GetDeviceState function failed!" << GetLastError();
+ Sleep(25);
+ continue;
+ }
+ }
+
+ {
+ using joy_fn = std::function<void(const QString& guid, int idx, bool held)>;
+
+ joy_fn f = [&](const QString& guid, int idx, bool held) -> void {
+ Key k;
+ k.keycode = idx;
+ k.shift = !!(keystate[DIK_LSHIFT] & 0x80 || keystate[DIK_RSHIFT] & 0x80);
+ k.alt = !!(keystate[DIK_LALT] & 0x80 || keystate[DIK_RALT] & 0x80);
+ k.ctrl = !!(keystate[DIK_LCONTROL] & 0x80 || keystate[DIK_RCONTROL] & 0x80);
+ k.guid = guid;
+ k.held = held;
+
+ for (auto& r : receivers)
+ r->operator()(k);
+ };
+
+ joy_ctx.poll(f);
+ }
+
+ for (int i = 0; i < 256; i++)
+ {
+ Key k;
+ if (old_keystate[i] != keystate[i])
+ {
+ const bool held = keystate[i] & 0x80;
+ switch (i)
+ {
+ case DIK_LCONTROL:
+ case DIK_LSHIFT:
+ case DIK_LALT:
+ case DIK_RCONTROL:
+ case DIK_RSHIFT:
+ case DIK_RALT:
+ break;
+ default:
+ k.shift = !!(keystate[DIK_LSHIFT] & 0x80) || !!(keystate[DIK_RSHIFT] & 0x80);
+ k.alt = !!(keystate[DIK_LALT] & 0x80) || !!(keystate[DIK_RALT] & 0x80);
+ k.ctrl = !!(keystate[DIK_LCONTROL] & 0x80) || !!(keystate[DIK_RCONTROL] & 0x80);
+ k.keycode = i;
+ k.held = held;
+
+ for (auto& r : receivers)
+ r->operator()(k);
+ break;
+ }
+ }
+ old_keystate[i] = keystate[i];
+ }
+ }
+ }
+
+ // keypresses get dropped with high values
+ Sleep(4);
+ }
+}
+
+KeybindingWorker::fun* KeybindingWorker::_add_receiver(fun& receiver)
+{
+ QMutexLocker l(&mtx);
+ receivers.push_back(std::unique_ptr<fun>(new fun(receiver)));
+ fun* f = receivers[receivers.size() - 1].get();
+ qDebug() << "add receiver" << (long) f;
+ joy_ctx.refresh();
+ return f;
+}
+
+void KeybindingWorker::remove_receiver(KeybindingWorker::fun* pos)
+{
+ QMutexLocker l(&mtx);
+ bool ok = false;
+
+ for (int i = receivers.size() - 1; i >= 0; i--)
+ {
+ if (receivers[i].get() == pos)
+ {
+ ok = true;
+ qDebug() << "remove receiver" << (long) pos;
+ receivers.erase(receivers.begin() + i);
+ break;
+ }
+ }
+ if (!ok)
+ {
+ qDebug() << "bad remove receiver" << (long) pos;
+ }
+}
+
+#endif
diff --git a/opentrack-logic/keybinding-worker.hpp b/opentrack-logic/keybinding-worker.hpp
new file mode 100644
index 00000000..12237ab0
--- /dev/null
+++ b/opentrack-logic/keybinding-worker.hpp
@@ -0,0 +1,78 @@
+/* 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 "export.hpp"
+
+#include "opentrack-compat/timer.hpp"
+#include "win32-joystick.hpp"
+#include <QThread>
+#include <QMutex>
+#include <QWidget>
+#include <QMainWindow>
+#include <functional>
+#include <vector>
+
+#undef DIRECTINPUT_VERSION
+#define DIRECTINPUT_VERSION 0x0800
+#include <windows.h>
+#include <dinput.h>
+struct Key {
+ BYTE keycode;
+ QString guid;
+ bool shift;
+ bool ctrl;
+ bool alt;
+ bool held;
+ bool enabled;
+ Timer timer;
+public:
+ Key() : keycode(0), shift(false), ctrl(false), alt(false), held(true), enabled(true) {}
+
+ bool should_process();
+};
+
+struct OPENTRACK_LOGIC_EXPORT KeybindingWorker : private QThread
+{
+private:
+ LPDIRECTINPUT8 din;
+ LPDIRECTINPUTDEVICE8 dinkeyboard;
+ win32_joy_ctx joy_ctx;
+ volatile bool should_quit;
+ using fun = std::function<void(const Key&)>;
+ std::vector<std::unique_ptr<fun>> receivers;
+ QMutex mtx;
+ QMainWindow fake_main_window;
+
+ void run() override;
+ KeybindingWorker();
+
+ KeybindingWorker(const KeybindingWorker&) = delete;
+ KeybindingWorker& operator=(KeybindingWorker&) = delete;
+ static KeybindingWorker& make();
+ fun* _add_receiver(fun &receiver);
+ void remove_receiver(fun* pos);
+ ~KeybindingWorker();
+public:
+ class Token
+ {
+ fun* pos;
+ Token(const Token&) = delete;
+ Token& operator=(Token&) = delete;
+ public:
+ ~Token()
+ {
+ make().remove_receiver(pos);
+ }
+ Token(fun receiver)
+ {
+ pos = make()._add_receiver(receiver);
+ }
+ };
+};
diff --git a/opentrack-logic/main-settings.hpp b/opentrack-logic/main-settings.hpp
new file mode 100644
index 00000000..16ff59e4
--- /dev/null
+++ b/opentrack-logic/main-settings.hpp
@@ -0,0 +1,92 @@
+/* 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 "opentrack-compat/options.hpp"
+#include "opentrack/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 main_settings : opts
+{
+ value<QString> tracker_dll, filter_dll, protocol_dll;
+ 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> 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;
+ main_settings() :
+ opts("opentrack-ui"),
+ tracker_dll(b, "tracker-dll", ""),
+ filter_dll(b, "filter-dll", "Accela"),
+ protocol_dll(b, "protocol-dll", "freetrack 2.0 Enhanced"),
+ a_x(b, "x", TX),
+ a_y(b, "y", TY),
+ a_z(b, "z", TZ),
+ a_yaw(b, "yaw", Yaw),
+ a_pitch(b, "pitch", Pitch),
+ a_roll(b, "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),
+ 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")
+ {
+ }
+};
diff --git a/opentrack-logic/mappings.hpp b/opentrack-logic/mappings.hpp
new file mode 100644
index 00000000..087ea7f3
--- /dev/null
+++ b/opentrack-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 "opentrack-compat/options.hpp"
+using namespace options;
+#include "spline-widget/functionconfig.h"
+#include "main-settings.hpp"
+
+class Mapping {
+public:
+ Mapping(QString primary,
+ QString secondary,
+ int max_x,
+ int max_y,
+ axis_opts& opts) :
+ curve(max_x, max_y),
+ curveAlt(max_x, max_y),
+ opts(opts),
+ name1(primary),
+ name2(secondary)
+ {
+ mem<QSettings> iniFile = group::ini_file();
+ curve.loadSettings(*iniFile, primary);
+ curveAlt.loadSettings(*iniFile, secondary);
+ }
+ Map curve;
+ Map curveAlt;
+ axis_opts& opts;
+ QString name1, name2;
+};
+
+class Mappings {
+private:
+ Mapping axes[6];
+public:
+ Mappings(std::vector<axis_opts*> opts) :
+ axes {
+ Mapping("tx","tx_alt", 100, 100, *opts[TX]),
+ Mapping("ty","ty_alt", 100, 100, *opts[TY]),
+ Mapping("tz","tz_alt", 100, 100, *opts[TZ]),
+ Mapping("rx", "rx_alt", 180, 180, *opts[Yaw]),
+ Mapping("ry", "ry_alt", 180, 180, *opts[Pitch]),
+ Mapping("rz", "rz_alt", 180, 180, *opts[Roll])
+ }
+ {}
+
+ inline Mapping& operator()(int i) { return axes[i]; }
+ inline const Mapping& 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].curve.loadSettings(*iniFile, axes[i].name1);
+ axes[i].curveAlt.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].curve.saveSettings(*iniFile, axes[i].name1);
+ axes[i].curveAlt.saveSettings(*iniFile, axes[i].name2);
+ axes[i].opts.b->save();
+ }
+ }
+
+ void invalidate_unsaved()
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ axes[i].curve.invalidate_unsaved_settings();
+ axes[i].curveAlt.invalidate_unsaved_settings();
+ axes[i].opts.b->reload();
+ }
+ }
+};
diff --git a/opentrack-logic/nan.cpp b/opentrack-logic/nan.cpp
new file mode 100644
index 00000000..2522ba38
--- /dev/null
+++ b/opentrack-logic/nan.cpp
@@ -0,0 +1,16 @@
+#include <cmath>
+
+#if defined(__GNUC__)
+bool __attribute__ ((noinline)) nanp(double value)
+#elif defined(_WIN32)
+__declspec(noinline) bool nanp(double value)
+#else
+bool nanp(double value)
+#endif
+{
+ using std::isnan;
+ using std::isinf;
+
+ const volatile double x = value;
+ return isnan(x) || isinf(x);
+} \ No newline at end of file
diff --git a/opentrack-logic/selected-libraries.cpp b/opentrack-logic/selected-libraries.cpp
new file mode 100644
index 00000000..4a1a9f09
--- /dev/null
+++ b/opentrack-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/opentrack-logic/selected-libraries.hpp b/opentrack-logic/selected-libraries.hpp
new file mode 100644
index 00000000..2538adff
--- /dev/null
+++ b/opentrack-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 "opentrack/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/opentrack-logic/shortcuts.cpp b/opentrack-logic/shortcuts.cpp
new file mode 100644
index 00000000..06c246af
--- /dev/null
+++ b/opentrack-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_keyboard_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 std::vector<std::tuple<key_opts&, fun, bool>> &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_keyboard_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/opentrack-logic/shortcuts.h b/opentrack-logic/shortcuts.h
new file mode 100644
index 00000000..87e24d4f
--- /dev/null
+++ b/opentrack-logic/shortcuts.h
@@ -0,0 +1,66 @@
+/* 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 "opentrack-compat/options.hpp"
+#include "main-settings.hpp"
+
+#ifdef _WIN32
+# include "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>;
+ 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 std::vector<std::tuple<key_opts&, fun, bool>> &keys_);
+private:
+ void free_binding(K& key);
+ void bind_keyboard_shortcut(K &key, const key_opts& k, bool held);
+#ifdef _WIN32
+ void receiver(const Key& k);
+#endif
+};
diff --git a/opentrack-logic/state.hpp b/opentrack-logic/state.hpp
new file mode 100644
index 00000000..ff46cc36
--- /dev/null
+++ b/opentrack-logic/state.hpp
@@ -0,0 +1,29 @@
+/* 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 "opentrack-compat/options.hpp"
+using namespace options;
+#include "opentrack/plugin-support.hpp"
+#include "main-settings.hpp"
+#include "mappings.hpp"
+#include "selected-libraries.hpp"
+#include "work.hpp"
+
+struct State {
+ State() :
+ 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/opentrack-logic/tracker.cpp b/opentrack-logic/tracker.cpp
new file mode 100644
index 00000000..3b3f35e6
--- /dev/null
+++ b/opentrack-logic/tracker.cpp
@@ -0,0 +1,305 @@
+/* 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 "tracker.h"
+#include <cmath>
+#include <algorithm>
+
+#if defined(_WIN32)
+# include <windows.h>
+#endif
+
+Tracker::Tracker(main_settings& s, Mappings &m, SelectedLibraries &libs) :
+ s(s),
+ m(m),
+ newpose {0,0,0, 0,0,0},
+ centerp(s.center_at_startup),
+ enabledp(true),
+ zero_(false),
+ should_quit(false),
+ libs(libs),
+ r_b(rmat::eye()),
+ t_b {0,0,0}
+{
+}
+
+Tracker::~Tracker()
+{
+ should_quit = true;
+ wait();
+}
+
+double Tracker::map(double pos, Mapping& axis)
+{
+ bool altp = (pos < 0) && axis.opts.altp;
+ axis.curve.setTrackingActive( !altp );
+ axis.curveAlt.setTrackingActive( altp );
+ auto& fc = altp ? axis.curveAlt : axis.curve;
+ return fc.getValue(pos);
+}
+
+void Tracker::t_compensate(const rmat& rmat, const double* xyz, double* output, bool rz)
+{
+ // TY is really yaw axis. need swapping accordingly.
+ dmat<3, 1> tvec( xyz[2], -xyz[0], -xyz[1] );
+ const dmat<3, 1> ret = rmat * tvec;
+ if (!rz)
+ output[2] = ret(0);
+ else
+ output[2] = xyz[2];
+ output[1] = -ret(2);
+ output[0] = -ret(1);
+}
+
+#ifdef _WIN32
+__declspec(noinline) bool nanp(double value);
+#elif defined(__GNUC__)
+bool __attribute__ ((noinline)) nanp(double value);
+#else
+bool nanp(double value);
+#endif
+
+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;
+}
+
+void Tracker::logic()
+{
+ bool inverts[6] = {
+ m(0).opts.invert,
+ m(1).opts.invert,
+ m(2).opts.invert,
+ m(3).opts.invert,
+ m(4).opts.invert,
+ m(5).opts.invert,
+ };
+
+ static constexpr double pi = 3.141592653;
+ static constexpr double r2d = 180. / pi;
+
+ using namespace euler;
+
+ 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];
+ }
+
+ if (is_nan(raw))
+ raw = last_raw;
+
+ const double off[] =
+ {
+ (double)-s.camera_yaw,
+ (double)-s.camera_pitch,
+ (double)-s.camera_roll
+ };
+ const rmat cam = euler_to_rmat(off);
+ rmat r = euler_to_rmat(&value[Yaw]);
+ euler_t t(value(0), value(1), value(2));
+
+ r = cam * r;
+
+ bool can_center = false;
+ const bool nan = is_nan(r, t);
+
+ if (centerp && !nan)
+ {
+ for (int i = 0; i < 6; i++)
+ if (fabs(newpose[i]) != 0)
+ {
+ can_center = true;
+ break;
+ }
+ }
+
+ if (can_center)
+ {
+ if (libs.pFilter)
+ libs.pFilter->center();
+ centerp = false;
+ for (int i = 0; i < 3; i++)
+ t_b[i] = t(i);
+ r_b = r;
+ }
+
+ {
+ double tmp[3] = { t(0) - t_b[0], t(1) - t_b[1], t(2) - t_b[2] };
+ rmat m_;
+ switch (s.center_method)
+ {
+ case 0:
+ default:
+ m_ = r * r_b.t();
+ break;
+ case 1:
+ m_ = r_b.t() * r;
+ }
+
+ const euler_t euler = rmat_to_euler(m_);
+
+ for (int i = 0; i < 3; i++)
+ {
+ value(i) = tmp[i];
+ value(i+3) = euler(i) * r2d;
+ }
+ }
+
+ bool nan_ = false;
+ // whenever something can corrupt its internal state due to nan/inf, elide the call
+ if (is_nan(value))
+ {
+ nan_ = true;
+ }
+ else
+ {
+ {
+ Pose tmp = value;
+
+ if (libs.pFilter)
+ libs.pFilter->filter(tmp, value);
+ }
+
+ for (int i = 0; i < 6; i++)
+ value(i) = map(value(i), m(i));
+
+ if (s.tcomp_p)
+ t_compensate(euler_to_rmat(&value[Yaw]),
+ value,
+ value,
+ s.tcomp_tz);
+
+ for (int i = 0; i < 6; i++)
+ value(i) += m(i).opts.zero;
+
+ for (int i = 0; i < 6; i++)
+ value[i] *= inverts[i] ? -1. : 1.;
+
+ if (zero_)
+ for (int i = 0; i < 6; i++)
+ value(i) = 0;
+
+ if (is_nan(value))
+ nan_ = true;
+ }
+
+ 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;
+}
+
+void Tracker::run()
+{
+ const int sleep_ms = 3;
+
+#if defined(_WIN32)
+ (void) timeBeginPeriod(1);
+#endif
+
+ while (!should_quit)
+ {
+ t.start();
+
+ double tmp[6] {0,0,0, 0,0,0};
+ t_compensate(cam, tmp, tmp, false);
+ libs.pTracker->data(tmp);
+
+ if (enabledp)
+ for (int i = 0; i < 6; i++)
+ newpose[i] = elide_nan(tmp[i], newpose[i]);
+
+ logic();
+
+ long q = sleep_ms * 1000L - t.elapsed()/1000L;
+ using std::max;
+ usleep(max(1L, q));
+ }
+
+ {
+ // 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).curve.setTrackingActive(false);
+ m(i).curveAlt.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/opentrack-logic/tracker.h b/opentrack-logic/tracker.h
new file mode 100644
index 00000000..591ca03c
--- /dev/null
+++ b/opentrack-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 "opentrack-compat/timer.hpp"
+#include "opentrack/plugin-support.hpp"
+#include "mappings.hpp"
+#include "opentrack/simple-mat.hpp"
+#include "selected-libraries.hpp"
+
+#include "spline-widget/functionconfig.h"
+#include "main-settings.hpp"
+#include "opentrack-compat/options.hpp"
+
+#include <QMutex>
+#include <QThread>
+
+#include "export.hpp"
+
+class Pose
+{
+private:
+ static constexpr double pi = 3.141592653;
+ static constexpr double d2r = pi/180.0;
+ static constexpr double r2d = 180./pi;
+
+ double axes[6];
+public:
+ Pose() : axes {0,0,0, 0,0,0} {}
+
+ inline operator double*() { return axes; }
+ inline operator const double*() const { return axes; }
+
+ inline double& operator()(int i) { return axes[i]; }
+ inline double operator()(int i) const { return axes[i]; }
+};
+
+class OPENTRACK_LOGIC_EXPORT Tracker : private QThread
+{
+ Q_OBJECT
+private:
+ QMutex mtx;
+ main_settings& s;
+ Mappings& m;
+
+ Timer t;
+ Pose output_pose, raw_6dof, last_mapped, last_raw;
+
+ double newpose[6];
+ volatile bool centerp;
+ volatile bool enabledp;
+ volatile bool zero_;
+ volatile bool should_quit;
+ SelectedLibraries const& libs;
+
+ using rmat = dmat<3, 3>;
+
+ rmat r_b;
+ double t_b[3];
+
+ double map(double pos, Mapping& axis);
+ void logic();
+
+ void t_compensate(const rmat& rmat, const double* ypr, double* output, bool rz);
+ void run() override;
+public:
+ Tracker(main_settings& s, Mappings& m, SelectedLibraries& libs);
+ ~Tracker();
+
+ void get_raw_and_mapped_poses(double* mapped, double* raw) const;
+ void start() { QThread::start(); }
+ void toggle_enabled() { qDebug() << "toggle enabled"; enabledp = !enabledp; }
+ void set_toggle(bool value) { qDebug() << "enabled" << value; enabledp = value; }
+ void set_zero(bool value) { qDebug() << "zero" << value; zero_ = value; }
+ void center() { qDebug() << "toggle center"; centerp = !centerp; }
+ void zero() { qDebug() << "toggle zero"; zero_ = !zero_; }
+};
diff --git a/opentrack-logic/win32-joystick.cpp b/opentrack-logic/win32-joystick.cpp
new file mode 100644
index 00000000..bf919f4a
--- /dev/null
+++ b/opentrack-logic/win32-joystick.cpp
@@ -0,0 +1,297 @@
+#undef NDEBUG
+#include <cassert>
+#include "win32-joystick.hpp"
+
+#ifdef _WIN32
+
+void win32_joy_ctx::poll(fn f)
+{
+ //refresh(false);
+
+ QMutexLocker l(&mtx);
+
+ for (auto& j : joys)
+ {
+ j.second->poll(f);
+ }
+}
+
+bool win32_joy_ctx::poll_axis(const QString &guid, int axes[])
+{
+ //refresh(false);
+
+ QMutexLocker l(&mtx);
+
+ auto iter = joys.find(guid);
+
+ if (iter == joys.end())
+ return false;
+
+ auto& j = iter->second;
+
+ auto& joy_handle = j->joy_handle;
+ bool ok = false;
+ HRESULT hr;
+
+ (void) joy_handle->Acquire();
+
+ if (!FAILED(hr = joy_handle->Poll()))
+ ok = true;
+
+ if (!ok)
+ {
+ qDebug() << "joy acquire failed" << guid << hr;
+ (void) joy_handle->Unacquire();
+ return false;
+ }
+
+ DIJOYSTATE2 js;
+ memset(&js, 0, sizeof(js));
+
+ if (FAILED(hr = joy_handle->GetDeviceState(sizeof(js), &js)))
+ {
+ qDebug() << "joy get state failed" << guid << hr;
+ return false;
+ }
+
+ const int values[] = {
+ js.lX,
+ js.lY,
+ js.lZ,
+ js.lRx,
+ js.lRy,
+ js.lRz,
+ js.rglSlider[0],
+ js.rglSlider[1]
+ };
+
+ for (int i = 0; i < 8; i++)
+ axes[i] = values[i];
+
+ return true;
+}
+
+win32_joy_ctx::~win32_joy_ctx()
+{
+ release();
+}
+
+std::vector<win32_joy_ctx::joy_info> win32_joy_ctx::get_joy_info()
+{
+ QMutexLocker l(&mtx);
+
+ std::vector<joy_info> ret;
+
+ for (auto& j : joys)
+ ret.push_back(joy_info { j.second->name, j.first });
+
+ return ret;
+}
+
+win32_joy_ctx::win32_joy_ctx()
+{
+ if (DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&di, NULL) != DI_OK) {
+ qDebug() << "setup DirectInput8 Creation failed!" << GetLastError();
+ assert(!"direct input handle can't be created");
+ }
+ refresh();
+}
+
+void win32_joy_ctx::release()
+{
+ joys = std::unordered_map<QString, std::shared_ptr<joy>>();
+ di->Release();
+ di = nullptr;
+}
+
+void win32_joy_ctx::refresh()
+{
+ QMutexLocker l(&mtx);
+
+ qDebug() << "joy list refresh";
+ enum_state st(joys, fake_main_window, di);
+}
+
+QString win32_joy_ctx::guid_to_string(const GUID guid)
+{
+ char buf[40] = {0};
+ wchar_t szGuidW[40] = {0};
+
+ StringFromGUID2(guid, szGuidW, 40);
+ WideCharToMultiByte(0, 0, szGuidW, -1, buf, 40, NULL, NULL);
+
+ return QString(buf);
+}
+
+using fn = win32_joy_ctx::fn;
+
+void win32_joy_ctx::joy::release()
+{
+ if (joy_handle)
+ {
+ (void) joy_handle->Unacquire();
+ joy_handle->Release();
+ joy_handle = nullptr;
+ }
+}
+
+bool win32_joy_ctx::joy::poll(fn f)
+{
+ HRESULT hr;
+ bool ok = false;
+
+ (void) joy_handle->Acquire();
+
+ if (!FAILED(hr = joy_handle->Poll()))
+ ok = true;
+
+ if (!ok)
+ {
+ qDebug() << "joy acquire failed" << guid << hr;
+ (void) joy_handle->Unacquire();
+ return false;
+ }
+
+ DIJOYSTATE2 js;
+ memset(&js, 0, sizeof(js));
+
+ if (FAILED(hr = joy_handle->GetDeviceState(sizeof(js), &js)))
+ {
+ qDebug() << "joy get state failed" << guid << hr;
+ return false;
+ }
+
+ for (int i = 0; i < 128; i++)
+ {
+ const bool state = !!(js.rgbButtons[i] & 0x80) && js.rgbButtons[i] != js_old.rgbButtons[i];
+ if (state != pressed[i])
+ {
+ f(guid, i, state);
+ qDebug() << "btn" << guid << i << state;
+ }
+ pressed[i] = state;
+ }
+
+ js_old = js;
+
+ return true;
+}
+
+win32_joy_ctx::enum_state::enum_state(std::unordered_map<QString, std::shared_ptr<joy>> &joys_,
+ QMainWindow &fake_main_window,
+ LPDIRECTINPUT8 di) :
+ fake_main_window(fake_main_window),
+ di(di)
+{
+ joys = joys_;
+
+ HRESULT hr;
+
+ if(FAILED(hr = di->EnumDevices(DI8DEVCLASS_GAMECTRL,
+ EnumJoysticksCallback,
+ this,
+ DIEDFL_ATTACHEDONLY)))
+ {
+ qDebug() << "failed enum joysticks" << hr;
+ return;
+ }
+
+ for (auto it = joys.begin(); it != joys.end(); )
+ {
+ if (std::find_if(all.cbegin(), all.cend(), [&](const QString& guid2) -> bool { return it->second->guid == guid2; }) == all.cend())
+ it = joys.erase(it);
+ else
+ it++;
+ }
+
+ joys_ = joys;
+}
+
+win32_joy_ctx::enum_state::EnumJoysticksCallback(const DIDEVICEINSTANCE *pdidInstance, void *pContext)
+{
+ enum_state& state = *reinterpret_cast<enum_state*>(pContext);
+ const QString guid = guid_to_string(pdidInstance->guidInstance);
+ const QString name = QString(pdidInstance->tszInstanceName);
+
+ auto it = state.joys.find(guid);
+ const bool exists = it != state.joys.end();
+
+ state.all.push_back(guid);
+
+ if (!exists)
+ {
+ HRESULT hr;
+ LPDIRECTINPUTDEVICE8 h;
+ if (FAILED(hr = state.di->CreateDevice(pdidInstance->guidInstance, &h, nullptr)))
+ {
+ qDebug() << "createdevice" << guid << hr;
+ goto end;
+ }
+ if (FAILED(h->SetDataFormat(&c_dfDIJoystick2)))
+ {
+ qDebug() << "format";
+ h->Release();
+ goto end;
+ }
+
+ if (FAILED(h->SetCooperativeLevel((HWND) state.fake_main_window.winId(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND)))
+ {
+ qDebug() << "coop";
+ h->Release();
+ goto end;
+ }
+ if (FAILED(hr = h->EnumObjects(EnumObjectsCallback, h, DIDFT_ALL)))
+ {
+ qDebug() << "enum-objects";
+ h->Release();
+ goto end;
+ }
+
+ qDebug() << "add joy" << guid;
+ state.joys[guid] = std::make_shared<joy>(h, guid, name);
+ }
+end:
+ return DIENUM_CONTINUE;
+}
+
+win32_joy_ctx::enum_state::EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE *pdidoi, void *ctx)
+{
+ if (pdidoi->dwType & DIDFT_AXIS)
+ {
+ DIPROPRANGE diprg;
+ memset(&diprg, 0, sizeof(diprg));
+ diprg.diph.dwSize = sizeof( DIPROPRANGE );
+ diprg.diph.dwHeaderSize = sizeof( DIPROPHEADER );
+ diprg.diph.dwHow = DIPH_BYID;
+ diprg.diph.dwObj = pdidoi->dwType;
+ diprg.lMax = joy_axis_size;
+ diprg.lMin = -joy_axis_size;
+
+ HRESULT hr;
+
+ if (FAILED(hr = reinterpret_cast<LPDIRECTINPUTDEVICE8>(ctx)->SetProperty(DIPROP_RANGE, &diprg.diph)))
+ {
+ qDebug() << "DIPROP_RANGE" << hr;
+ return DIENUM_STOP;
+ }
+ }
+
+ return DIENUM_CONTINUE;
+}
+
+win32_joy_ctx::joy::joy(LPDIRECTINPUTDEVICE8 handle, const QString &guid, const QString &name)
+ : joy_handle(handle), guid(guid), name(name)
+{
+ qDebug() << "got joy" << guid;
+ for (int i = 0; i < 128; i++)
+ pressed[i] = false;
+ memset(&js_old, 0, sizeof(js_old));
+}
+
+win32_joy_ctx::joy::~joy()
+{
+ qDebug() << "nix joy" << guid;
+ release();
+}
+
+#endif
diff --git a/opentrack-logic/win32-joystick.hpp b/opentrack-logic/win32-joystick.hpp
new file mode 100644
index 00000000..d9b62e45
--- /dev/null
+++ b/opentrack-logic/win32-joystick.hpp
@@ -0,0 +1,96 @@
+#pragma once
+
+#ifdef _WIN32
+
+#include "export.hpp"
+
+#include <cstring>
+#include <memory>
+#include <vector>
+#include <functional>
+#include <algorithm>
+#include <unordered_map>
+#ifndef DIRECTINPUT_VERSION
+# define DIRECTINPUT_VERSION 0x800
+#endif
+#include <dinput.h>
+#include <windows.h>
+#include "opentrack-compat/timer.hpp"
+#include <QString>
+#include <QDebug>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QMainWindow>
+
+namespace std {
+template<>
+struct hash<QString>
+{
+ inline std::size_t operator()(const QString& value) const
+ {
+ return qHash(value);
+ }
+};
+}
+
+struct OPENTRACK_LOGIC_EXPORT win32_joy_ctx
+{
+ using fn = std::function<void(const QString& guid, int btn, bool held)>;
+
+ enum { joy_axis_size = 65535 };
+
+ struct joy_info
+ {
+ QString name, guid;
+ };
+
+ void poll(fn f);
+ bool poll_axis(const QString& guid, int axes[8]);
+ std::vector<joy_info> get_joy_info();
+
+ win32_joy_ctx(const win32_joy_ctx&) = delete;
+ win32_joy_ctx& operator=(const win32_joy_ctx&) = delete;
+
+ win32_joy_ctx();
+ ~win32_joy_ctx();
+ void refresh();
+private:
+ QMutex mtx;
+ QMainWindow fake_main_window;
+ LPDIRECTINPUT8 di;
+
+ static QString guid_to_string(const GUID guid);
+ void release();
+
+ struct joy
+ {
+ LPDIRECTINPUTDEVICE8 joy_handle;
+ QString guid, name;
+ bool pressed[128];
+ Timer first_timer;
+ DIJOYSTATE2 js_old;
+
+ joy(LPDIRECTINPUTDEVICE8 handle, const QString& guid, const QString& name);
+ ~joy();
+
+ void release();
+ bool poll(fn f);
+ };
+
+ std::unordered_map<QString, std::shared_ptr<joy>> joys;
+
+ class enum_state
+ {
+ std::unordered_map<QString, std::shared_ptr<joy>> joys;
+ QMainWindow& fake_main_window;
+ LPDIRECTINPUT8 di;
+
+ std::vector<QString> all;
+ static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, VOID* pContext);
+ static BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* ctx);
+ public:
+ enum_state(std::unordered_map<QString, std::shared_ptr<joy>>& joys, QMainWindow& fake_main_window, LPDIRECTINPUT8 di);
+ };
+};
+
+#endif
diff --git a/opentrack-logic/win32-shortcuts.cpp b/opentrack-logic/win32-shortcuts.cpp
new file mode 100644
index 00000000..a93803a3
--- /dev/null
+++ b/opentrack-logic/win32-shortcuts.cpp
@@ -0,0 +1,197 @@
+/* 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 <windows.h>
+# include <dinput.h>
+
+#include <QList>
+#include <QVariant>
+#include <QDebug>
+#include "win32-shortcuts.h"
+
+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/opentrack-logic/win32-shortcuts.h b/opentrack-logic/win32-shortcuts.h
new file mode 100644
index 00000000..7626a31f
--- /dev/null
+++ b/opentrack-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/opentrack-logic/work.hpp b/opentrack-logic/work.hpp
new file mode 100644
index 00000000..2377be5a
--- /dev/null
+++ b/opentrack-logic/work.hpp
@@ -0,0 +1,63 @@
+/* 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 "opentrack/plugin-support.hpp"
+#include "tracker.h"
+#include "shortcuts.h"
+
+#include <QObject>
+#include <QFrame>
+#include <memory>
+#include <vector>
+#include <functional>
+#include <tuple>
+
+struct Work
+{
+ main_settings& s;
+ SelectedLibraries& libs;
+ mem<Tracker> tracker;
+ mem<Shortcuts> sc;
+ WId handle;
+ using fn = std::function<void(bool)>;
+ using tt = std::tuple<key_opts&, fn, bool>;
+ std::vector<tt> keys;
+
+ Work(main_settings& s, Mappings& m, SelectedLibraries& libs, WId handle) :
+ s(s), libs(libs),
+ tracker(std::make_shared<Tracker>(s, m, libs)),
+ sc(std::make_shared<Shortcuts>()),
+ handle(handle),
+ keys {
+ tt(s.key_center, [&](bool) -> void { tracker->center(); }, true),
+ tt(s.key_toggle, [&](bool) -> void { tracker->toggle_enabled(); }, true),
+ tt(s.key_zero, [&](bool) -> void { tracker->zero(); }, true),
+ tt(s.key_toggle_press, [&](bool x) -> void { tracker->set_toggle(!x); }, false),
+ tt(s.key_zero_press, [&](bool x) -> void { tracker->set_zero(x); }, false),
+ }
+ {
+ reload_shortcuts();
+ tracker->start();
+ }
+
+ void reload_shortcuts()
+ {
+ sc->reload(keys);
+ }
+
+ ~Work()
+ {
+ sc = nullptr;
+ // order matters, otherwise use-after-free -sh
+ tracker = nullptr;
+ libs = SelectedLibraries();
+ }
+};