summaryrefslogtreecommitdiffhomepage
path: root/opentrack-logic
diff options
context:
space:
mode:
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();
+ }
+};