diff options
Diffstat (limited to 'opentrack-logic')
-rw-r--r-- | opentrack-logic/CMakeLists.txt | 10 | ||||
-rw-r--r-- | opentrack-logic/export.hpp | 18 | ||||
-rw-r--r-- | opentrack-logic/import.hpp | 13 | ||||
-rw-r--r-- | opentrack-logic/keybinding-worker.cpp | 194 | ||||
-rw-r--r-- | opentrack-logic/keybinding-worker.hpp | 78 | ||||
-rw-r--r-- | opentrack-logic/main-settings.hpp | 92 | ||||
-rw-r--r-- | opentrack-logic/mappings.hpp | 89 | ||||
-rw-r--r-- | opentrack-logic/nan.cpp | 16 | ||||
-rw-r--r-- | opentrack-logic/selected-libraries.cpp | 37 | ||||
-rw-r--r-- | opentrack-logic/selected-libraries.hpp | 25 | ||||
-rw-r--r-- | opentrack-logic/shortcuts.cpp | 130 | ||||
-rw-r--r-- | opentrack-logic/shortcuts.h | 66 | ||||
-rw-r--r-- | opentrack-logic/state.hpp | 29 | ||||
-rw-r--r-- | opentrack-logic/tracker.cpp | 305 | ||||
-rw-r--r-- | opentrack-logic/tracker.h | 85 | ||||
-rw-r--r-- | opentrack-logic/win32-joystick.cpp | 297 | ||||
-rw-r--r-- | opentrack-logic/win32-joystick.hpp | 96 | ||||
-rw-r--r-- | opentrack-logic/win32-shortcuts.cpp | 197 | ||||
-rw-r--r-- | opentrack-logic/win32-shortcuts.h | 24 | ||||
-rw-r--r-- | opentrack-logic/work.hpp | 63 |
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(); + } +}; |