diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2016-01-06 20:07:13 +0100 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2016-01-06 20:16:18 +0100 |
commit | 82f3d7373234cc0db79a22d476cb54b5eda7a0ea (patch) | |
tree | 65ee0194ad064cc470f95f7ca8efd533b089ca96 /opentrack | |
parent | 7e3807d048c5e0a8e0aa64fb49807bf5dfd11fc1 (diff) | |
parent | f02baa0868f219076a641634625f7c032d3a9eef (diff) |
Merge branch 'unstable' into trackhat
* unstable: (140 commits)
tracker/pt: increase auto threshold bucket size again
tracker/pt: limit max amount of extracted blobs
gui: don't update main window if we're minimized
tracker/pt: only show widget if the frame is visible
tracker/pt: don't resize twice in widget
freetrack/games: regen
contrib/make-csv: perl sort isn't stable, don't ignore case
tracker/pt: avoid widget temp QImage allocation
spline-widget: oops, pass by reference
tracker/pt: don't allocate temporary dynamic size arrays
tracker/pt: don't copy points array needlessly
tracker/pt: don't allocate temporary frame
tracker/pt: cv::Mat::at<T> is slow, use cv::Mat::ptr
tracker/pt: avoid widget malloc when able
tracker/pt: optimize widget
tracker/pt: update video widget at 40 -> 50 ms
cmake/mingw-w64: update
tracker/pt: reduce mutex contention
gui: fix left margin
tracker/pt: remove krap
tracker/pt: move ctor out of the loop
tracker/pt: nix unused
tracker/pt: don't fill mask on frame
pose-widget: also bilinear interpolation of alpha value
ui: adjust margin
ui: make more compact
glwidget: use transparent octopus background
api/mat: fix typos/breakage
api/joy: refresh only manually on certain events
pt: histogram more granular 6 -> 8
cmake/api: link with strmiids.lib on win32
tracker/pt: reduce auto thresholding histogram bucket size
api/keys: prevent idempotent keys
api/joy: move from header
api/joy: prevent idempotent keypressed passed to receiver
compat/options: get rid of std::string usage
compat/options: move from header
gui/settings: set parent, otherwise not modal
gui/settings: don't forget to show a modal dialog before executing
gui/main: don't raise a new window, it's enough to set visible
api/joy: speed up poll_axis path
api/joy: nix static, now that we're not a singleton
tracker/joy: adapt to non-singleton joy worker
joystick: no longer singleton, use fake window handle
api/keys: use a fake window for DirectInput handle
gui/keys: allow for pausing global keystrokes for options dialog
api/keys: nix tautological #ifdef
contrib/aruco: oops, right extension
contrib/aruco: use @frost555's marker image
api/camera-names: move to compat/
...
Diffstat (limited to 'opentrack')
-rw-r--r-- | opentrack/CMakeLists.txt | 4 | ||||
-rw-r--r-- | opentrack/camera-names.hpp | 125 | ||||
-rw-r--r-- | opentrack/keybinding-worker.cpp | 192 | ||||
-rw-r--r-- | opentrack/keybinding-worker.hpp | 81 | ||||
-rw-r--r-- | opentrack/main-settings.hpp | 30 | ||||
-rw-r--r-- | opentrack/mappings.hpp | 2 | ||||
-rw-r--r-- | opentrack/opencv-camera-dialog.hpp | 2 | ||||
-rw-r--r-- | opentrack/options.hpp | 481 | ||||
-rw-r--r-- | opentrack/plugin-api.hpp | 40 | ||||
-rw-r--r-- | opentrack/plugin-support.hpp | 2 | ||||
-rw-r--r-- | opentrack/selected-libraries.hpp | 9 | ||||
-rw-r--r-- | opentrack/shortcuts.cpp | 194 | ||||
-rw-r--r-- | opentrack/shortcuts.h | 126 | ||||
-rw-r--r-- | opentrack/simple-mat.hpp | 30 | ||||
-rw-r--r-- | opentrack/state.hpp | 2 | ||||
-rw-r--r-- | opentrack/tracker.h | 16 | ||||
-rw-r--r-- | opentrack/win32-joystick.cpp | 299 | ||||
-rw-r--r-- | opentrack/win32-joystick.hpp | 100 | ||||
-rw-r--r-- | opentrack/win32-shortcuts.cpp | 1 | ||||
-rw-r--r-- | opentrack/win32-shortcuts.h | 8 | ||||
-rw-r--r-- | opentrack/work.hpp | 34 |
21 files changed, 872 insertions, 906 deletions
diff --git a/opentrack/CMakeLists.txt b/opentrack/CMakeLists.txt index 590eaca5..6baa05db 100644 --- a/opentrack/CMakeLists.txt +++ b/opentrack/CMakeLists.txt @@ -1,6 +1,8 @@ -opentrack_boilerplate(opentrack-api NO-LINKER-SCRIPT NO-COMPAT) +opentrack_boilerplate(opentrack-api NO-LINKER-SCRIPT NO-COMPAT LINKAGE) if(NOT WIN32) target_link_libraries(opentrack-api opentrack-qxt-mini opentrack-compat dl) else() target_link_libraries(opentrack-api winmm) endif() +target_link_libraries(opentrack-api opentrack-spline-widget) +link_with_dinput8(opentrack-api) diff --git a/opentrack/camera-names.hpp b/opentrack/camera-names.hpp deleted file mode 100644 index f6ab736e..00000000 --- a/opentrack/camera-names.hpp +++ /dev/null @@ -1,125 +0,0 @@ -/* 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 <QList> -#include <QString> -#include <QDebug> - -#if defined(OPENTRACK_API) && defined(_WIN32) -# define NO_DSHOW_STRSAFE -# include <windows.h> -# include <dshow.h> -#endif - -#if defined(OPENTRACK_API) && (defined(__unix) || defined(__linux) || defined(__APPLE__)) -# include <unistd.h> -#endif - -#ifdef __linux -#include <fcntl.h> -#include <sys/ioctl.h> -#include <linux/videodev2.h> -#include <cerrno> -#endif - -template<typename = void> -QList<QString> get_camera_names() { - QList<QString> ret; -#if defined(_WIN32) - // Create the System Device Enumerator. - HRESULT hr; - hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); - if (FAILED(hr)) - qDebug() << "failed CoInitializeEx" << hr; - ICreateDevEnum *pSysDevEnum = NULL; - hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum); - if (FAILED(hr)) - { - qDebug() << "failed CLSID_SystemDeviceEnum" << hr; - return ret; - } - // Obtain a class enumerator for the video compressor category. - IEnumMoniker *pEnumCat = NULL; - hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0); - - if (hr == S_OK) { - // Enumerate the monikers. - IMoniker *pMoniker = NULL; - ULONG cFetched; - while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) { - IPropertyBag *pPropBag; - hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag); - if (SUCCEEDED(hr)) { - // To retrieve the filter's friendly name, do the following: - VARIANT varName; - VariantInit(&varName); - hr = pPropBag->Read(L"FriendlyName", &varName, 0); - if (SUCCEEDED(hr)) - { - // Display the name in your UI somehow. - QString str((QChar*)varName.bstrVal, wcslen(varName.bstrVal)); - ret.append(str); - } - VariantClear(&varName); - - ////// To create an instance of the filter, do the following: - ////IBaseFilter *pFilter; - ////hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, - //// (void**)&pFilter); - // Now add the filter to the graph. - //Remember to release pFilter later. - pPropBag->Release(); - } - pMoniker->Release(); - } - pEnumCat->Release(); - } - else - qDebug() << "failed CLSID_VideoInputDeviceCategory" << hr; - - pSysDevEnum->Release(); -#endif -#ifdef __linux - for (int i = 0; i < 16; i++) { - char buf[128]; - sprintf(buf, "/dev/video%d", i); - if (access(buf, F_OK) == 0) - ret.append(buf); - else - continue; - - if (access(buf, R_OK | W_OK) == 0) { - int fd = open(buf, O_RDONLY); - if (fd == -1) - continue; - struct v4l2_capability video_cap; - if(ioctl(fd, VIDIOC_QUERYCAP, &video_cap) == -1) - { - qDebug() << "VIDIOC_QUERYCAP" << errno; - close(fd); - continue; - } - ret[ret.size()-1] = reinterpret_cast<const char*>(video_cap.card); - close(fd); - } - } -#endif - return ret; -} - -template<typename = void> -int camera_name_to_index(const QString &name) -{ - auto list = get_camera_names(); - int ret = list.indexOf(name); - if (ret < 0) - ret = 0; - return ret; -} diff --git a/opentrack/keybinding-worker.cpp b/opentrack/keybinding-worker.cpp new file mode 100644 index 00000000..39693a7c --- /dev/null +++ b/opentrack/keybinding-worker.cpp @@ -0,0 +1,192 @@ +/* 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 (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] && 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; + + 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/keybinding-worker.hpp b/opentrack/keybinding-worker.hpp new file mode 100644 index 00000000..fa50a974 --- /dev/null +++ b/opentrack/keybinding-worker.hpp @@ -0,0 +1,81 @@ +/* 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 + +#ifdef BUILD_api +# include "opentrack-compat/export.hpp" +#else +# include "opentrack-compat/import.hpp" +#endif + +#include "opentrack-compat/timer.hpp" +#include "opentrack/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; + Timer timer; +public: + Key() : keycode(0), shift(false), ctrl(false), alt(false), held(true) {} + + bool should_process(); +}; + +struct OPENTRACK_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/main-settings.hpp b/opentrack/main-settings.hpp index dd61143e..2d1c1f22 100644 --- a/opentrack/main-settings.hpp +++ b/opentrack/main-settings.hpp @@ -9,7 +9,7 @@ #pragma once #include <QString> -#include "opentrack/options.hpp" +#include "opentrack-compat/options.hpp" #include "opentrack/plugin-api.hpp" using namespace options; @@ -30,13 +30,27 @@ private: } }; -struct main_settings : opts { +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> 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, wizard_done; + value<int> center_method; + key_opts key_start_tracking, key_stop_tracking, key_toggle_tracking; + key_opts key_center, key_toggle, key_zero; main_settings() : opts("opentrack-ui"), protocol_dll(b, "protocol-dll", "freetrack 2.0 Enhanced"), @@ -53,6 +67,14 @@ struct main_settings : opts { camera_pitch(b, "camera-pitch", 0), camera_roll(b, "camera-roll", 0), center_at_startup(b, "center-at-startup", true), - wizard_done(b, "wizard-done", false) - {} + wizard_done(b, "wizard-done", false), + 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_center(b, "center"), + key_toggle(b, "toggle"), + key_zero(b, "zero") + { + } }; diff --git a/opentrack/mappings.hpp b/opentrack/mappings.hpp index bb38a3ca..087ea7f3 100644 --- a/opentrack/mappings.hpp +++ b/opentrack/mappings.hpp @@ -8,7 +8,7 @@ #pragma once #include <QSettings> -#include "options.hpp" +#include "opentrack-compat/options.hpp" using namespace options; #include "spline-widget/functionconfig.h" #include "main-settings.hpp" diff --git a/opentrack/opencv-camera-dialog.hpp b/opentrack/opencv-camera-dialog.hpp index 0d4a51af..96c7a643 100644 --- a/opentrack/opencv-camera-dialog.hpp +++ b/opentrack/opencv-camera-dialog.hpp @@ -12,7 +12,7 @@ #include <QMutex> #include <QMutexLocker> #include <opencv2/videoio.hpp> -#include "opentrack/camera-names.hpp" +#include "opentrack-compat/camera-names.hpp" #ifdef __linux #include <QProcess> diff --git a/opentrack/options.hpp b/opentrack/options.hpp deleted file mode 100644 index f2c09479..00000000 --- a/opentrack/options.hpp +++ /dev/null @@ -1,481 +0,0 @@ -/* Copyright (c) 2013-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 <memory> -#include <tuple> -#include <map> -#include <string> - -#include <QObject> -#include <QSettings> -#include <QString> -#include <QVariant> -#include <QMutex> -#include <QMutexLocker> -#include <QWidget> -#include <QComboBox> -#include <QCheckBox> -#include <QDoubleSpinBox> -#include <QSpinBox> -#include <QSlider> -#include <QLineEdit> -#include <QLabel> -#include <QTabWidget> -#include <QCoreApplication> -#include <QFileInfo> -#include <QDir> -#include <QStandardPaths> - -#include <cinttypes> - -#include <QDebug> - -#include <memory> -template<typename t> using mem = std::shared_ptr<t>; - -#define OPENTRACK_CONFIG_FILENAME_KEY "settings-filename" -#define OPENTRACK_DEFAULT_CONFIG "default.ini" -#define OPENTRACK_ORG "TrackHat opentrack-2.3" - -namespace options { - template<typename k, typename v> - using map = std::map<k, v>; - using std::string; - - template<typename t> - // don't elide usages of the function, qvariant default implicit - // conversion results in nonsensical runtime behavior -sh - inline t qcruft_to_t (const QVariant& datum); - - template<> - inline int qcruft_to_t<int>(const QVariant& t) - { - return t.toInt(); - } - - template<> - inline QString qcruft_to_t<QString>(const QVariant& t) - { - return t.toString(); - } - - template<> - inline bool qcruft_to_t<bool>(const QVariant& t) - { - return t.toBool(); - } - - template<> - inline double qcruft_to_t<double>(const QVariant& t) - { - return t.toDouble(); - } - - template<> - inline QVariant qcruft_to_t<QVariant>(const QVariant& t) - { - return t; - } - - // snapshot of qsettings group at given time - class group { - private: - map<string, QVariant> kvs; - string name; - public: - group(const string& name) : name(name) - { - auto conf = ini_file(); - auto q_name = QString::fromStdString(name); - conf->beginGroup(q_name); - for (auto& k_ : conf->childKeys()) - { - auto tmp = k_.toUtf8(); - string k(tmp); - kvs[k] = conf->value(k_); - } - conf->endGroup(); - } - - void save() - { - auto s = ini_file(); - auto q_name = QString::fromStdString(name); - s->beginGroup(q_name); - for (auto& i : kvs) - { - auto k = QString::fromStdString(i.first); - s->setValue(k, i.second); - } - s->endGroup(); - s->sync(); - } - - template<typename t> - t get(const string& k) - { - return qcruft_to_t<t>(kvs[k]); - } - - void put(const string& s, const QVariant& d) - { - kvs[s] = d; - } - - bool contains(const string& s) - { - return kvs.count(s) != 0; - } - - static QString ini_directory() - { - const auto dirs = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation); - if (dirs.size() == 0) - return ""; - if (QDir(dirs[0]).mkpath(OPENTRACK_ORG)) - return dirs[0] + "/" OPENTRACK_ORG; - return ""; - } - - static QString ini_filename() - { - QSettings settings(OPENTRACK_ORG); - return settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString(); - } - - static QString ini_pathname() - { - const auto dir = ini_directory(); - if (dir == "") - return ""; - QSettings settings(OPENTRACK_ORG); - return dir + "/" + settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString(); - } - - static const QStringList ini_list() - { - const auto dirname = ini_directory(); - if (dirname == "") - return QStringList(); - QDir settings_dir(dirname); - return settings_dir.entryList( QStringList { "*.ini" } , QDir::Files, QDir::Name ); - } - - static const mem<QSettings> ini_file() - { - const auto pathname = ini_pathname(); - if (pathname != "") - return std::make_shared<QSettings>(ini_pathname(), QSettings::IniFormat); - return std::make_shared<QSettings>(); - } - }; - - class impl_bundle : public QObject { - Q_OBJECT - protected: - QMutex mtx; - const string group_name; - group saved; - group transient; - bool modified; - impl_bundle(const impl_bundle&) = delete; - impl_bundle& operator=(const impl_bundle&) = delete; - signals: - void reloading(); - void saving(); - public: - impl_bundle(const string& group_name) : - mtx(QMutex::Recursive), - group_name(group_name), - saved(group_name), - transient(saved), - modified(false) - { - } - - string name() { return group_name; } - - void reload() { - { - QMutexLocker l(&mtx); - saved = group(group_name); - transient = saved; - modified = false; - } - emit reloading(); - } - - void store_kv(const string& name, const QVariant& datum) - { - QMutexLocker l(&mtx); - - auto old = transient.get<QVariant>(name); - if (!transient.contains(name) || datum != old) - { - modified = true; - transient.put(name, datum); - } - } - bool contains(const string& name) - { - QMutexLocker l(&mtx); - return transient.contains(name); - } - template<typename t> - t get(const string& name) - { - QMutexLocker l(&mtx); - return transient.get<t>(name); - } - void save() - { - { - QMutexLocker l(&mtx); - modified = false; - saved = transient; - transient.save(); - } - emit saving(); - } - - bool modifiedp() { - QMutexLocker l(&mtx); - return modified; - } - }; - - class opt_bundle; - - namespace - { - template<typename k, typename v, typename cnt = int> - struct opt_singleton - { - public: - using pbundle = std::shared_ptr<v>; - using tt = std::tuple<cnt, std::weak_ptr<v>>; - private: - QMutex implsgl_mtx; - map<k, tt> implsgl_data; - public: - opt_singleton() : implsgl_mtx(QMutex::Recursive) {} - - static opt_singleton<k, v>& datum() - { - static auto ret = std::make_shared<opt_singleton<k, v>>(); - return *ret; - } - - pbundle bundle(const k& key) - { - QMutexLocker l(&implsgl_mtx); - - if (implsgl_data.count(key) != 0) - { - auto shared = std::get<1>(implsgl_data[key]).lock(); - if (shared != nullptr) - return shared; - } - - qDebug() << "bundle +" << QString::fromStdString(key); - - auto shr = std::make_shared<v>(key); - implsgl_data[key] = tt(cnt(1), shr); - return shr; - } - - void bundle_decf(const k& key) - { - QMutexLocker l(&implsgl_mtx); - - if (--std::get<0>(implsgl_data[key]) == 0) - implsgl_data.erase(key); - } - }; - - using pbundle = std::shared_ptr<opt_bundle>; - using t_fact = opt_singleton<string, opt_bundle>; - } - - static inline t_fact::pbundle bundle(const string name) { return t_fact::datum().bundle(name); } - - class opt_bundle : public impl_bundle - { - public: - opt_bundle() : impl_bundle("i-have-no-name") {} - opt_bundle(const string& group_name) : impl_bundle(group_name) - { - } - - ~opt_bundle() - { - qDebug() << "bundle -" << QString::fromStdString(group_name); - t_fact::datum().bundle_decf(group_name); - } - }; - - class base_value : public QObject - { - Q_OBJECT -#define DEFINE_SLOT(t) void setValue(t datum) { store(datum); } -#define DEFINE_SIGNAL(t) void valueChanged(t) - public: - string name() { return self_name; } - base_value(pbundle b, const string& name) : b(b), self_name(name) {} - signals: - DEFINE_SIGNAL(double); - DEFINE_SIGNAL(int); - DEFINE_SIGNAL(bool); - DEFINE_SIGNAL(QString); - protected: - pbundle b; - string self_name; - - template<typename t> - void store(const t& datum) - { - b->store_kv(self_name, datum); - emit valueChanged(static_cast<t>(datum)); - } - public slots: - DEFINE_SLOT(double) - DEFINE_SLOT(int) - DEFINE_SLOT(QString) - DEFINE_SLOT(bool) - public slots: - virtual void reload() = 0; - }; - - static inline string string_from_qstring(const QString& datum) - { - auto tmp = datum.toUtf8(); - return string(tmp.constData()); - } - - template<typename t> - class value : public base_value { - public: - t operator=(const t datum) - { - store(datum); - return datum; - } - static constexpr const Qt::ConnectionType DIRECT_CONNTYPE = Qt::DirectConnection; - static constexpr const Qt::ConnectionType SAFE_CONNTYPE = Qt::UniqueConnection; - value(pbundle b, const string& name, t def) : base_value(b, name), def(def) - { - QObject::connect(b.get(), SIGNAL(reloading()), - this, SLOT(reload()), - DIRECT_CONNTYPE); - if (!b->contains(name) || b->get<QVariant>(name).type() == QVariant::Invalid) - *this = def; - } - value(pbundle b, const QString& name, t def) : value(b, string_from_qstring(name), def) {} - value(pbundle b, const char* name, t def) : value(b, string(name), def) {} - - operator t() - { - return b->contains(self_name) ? b->get<t>(self_name) : def; - } - void reload() override { - *this = static_cast<t>(*this); - } - private: - t def; - }; - - struct opts - { - pbundle b; - - opts(const std::string& name) : b(bundle(name)) {} - - ~opts() - { - b->reload(); - } - }; - - template<typename t, typename q> - inline void tie_setting(value<t>&, q*); - - template<> - inline void tie_setting(value<int>& v, QComboBox* cb) - { - cb->setCurrentIndex(v); - v = cb->currentIndex(); - base_value::connect(cb, SIGNAL(currentIndexChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE); - base_value::connect(&v, SIGNAL(valueChanged(int)), cb, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE); - } - - template<> - inline void tie_setting(value<QString>& v, QComboBox* cb) - { - cb->setCurrentText(v); - v = cb->currentText(); - base_value::connect(cb, SIGNAL(currentTextChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE); - base_value::connect(&v, SIGNAL(valueChanged(QString)), cb, SLOT(setCurrentText(QString)), v.SAFE_CONNTYPE); - } - - template<> - inline void tie_setting(value<bool>& v, QCheckBox* cb) - { - cb->setChecked(v); - base_value::connect(cb, SIGNAL(toggled(bool)), &v, SLOT(setValue(bool)), v.DIRECT_CONNTYPE); - base_value::connect(&v, SIGNAL(valueChanged(bool)), cb, SLOT(setChecked(bool)), v.SAFE_CONNTYPE); - } - - template<> - inline void tie_setting(value<double>& v, QDoubleSpinBox* dsb) - { - dsb->setValue(v); - base_value::connect(dsb, SIGNAL(valueChanged(double)), &v, SLOT(setValue(double)), v.DIRECT_CONNTYPE); - base_value::connect(&v, SIGNAL(valueChanged(double)), dsb, SLOT(setValue(double)), v.SAFE_CONNTYPE); - } - - template<> - inline void tie_setting(value<int>& v, QSpinBox* sb) - { - sb->setValue(v); - base_value::connect(sb, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE); - base_value::connect(&v, SIGNAL(valueChanged(int)), sb, SLOT(setValue(int)), v.SAFE_CONNTYPE); - } - - template<> - inline void tie_setting(value<int>& v, QSlider* sl) - { - sl->setValue(v); - v = sl->value(); - base_value::connect(sl, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE); - base_value::connect(&v, SIGNAL(valueChanged(int)), sl, SLOT(setValue(int)), v.SAFE_CONNTYPE); - } - - template<> - inline void tie_setting(value<QString>& v, QLineEdit* le) - { - le->setText(v); - base_value::connect(le, SIGNAL(textChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE); - base_value::connect(&v, SIGNAL(valueChanged(QString)),le, SLOT(setText(QString)), v.SAFE_CONNTYPE); - } - - template<> - inline void tie_setting(value<QString>& v, QLabel* lb) - { - lb->setText(v); - base_value::connect(&v, SIGNAL(valueChanged(QString)), lb, SLOT(setText(QString)), v.SAFE_CONNTYPE); - } - - template<> - inline void tie_setting(value<int>& v, QTabWidget* t) - { - t->setCurrentIndex(v); - base_value::connect(t, SIGNAL(currentChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE); - base_value::connect(&v, SIGNAL(valueChanged(int)), t, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE); - } -} diff --git a/opentrack/plugin-api.hpp b/opentrack/plugin-api.hpp index a57077ab..5fdc3bcb 100644 --- a/opentrack/plugin-api.hpp +++ b/opentrack/plugin-api.hpp @@ -8,17 +8,38 @@ #pragma once -#include "../opentrack-compat/export.hpp" #include <QString> #include <QWidget> #include <QFrame> #include <QIcon> +#ifdef BUILD_api +# include "opentrack-compat/export.hpp" +#else +# include "opentrack-compat/import.hpp" +#endif + +#ifndef OPENTRACK_PLUGIN_EXPORT +# ifdef _WIN32 +# define OPENTRACK_PLUGIN_LINKAGE __declspec(dllexport) +# else +# define OPENTRACK_PLUGIN_LINKAGE +# endif +# ifndef _MSC_VER +# define OPENTRACK_PLUGIN_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_PLUGIN_LINKAGE +# else +# define OPENTRACK_PLUGIN_EXPORT OPENTRACK_PLUGIN_LINKAGE +# endif +#endif + enum Axis { TX = 0, TY, TZ, Yaw, Pitch, Roll }; -class BaseDialog : public QWidget +namespace plugin_api { +namespace detail { + +class OPENTRACK_EXPORT BaseDialog : public QWidget { Q_OBJECT public: @@ -27,16 +48,19 @@ signals: void closing(); }; +} // ns +} // ns + #define OPENTRACK_DECLARE_PLUGIN_INTERNAL(ctor_class, ctor_ret_class, metadata_class, dialog_class, dialog_ret_class) \ - extern "C" OPENTRACK_EXPORT ctor_ret_class* GetConstructor() \ + extern "C" OPENTRACK_PLUGIN_EXPORT ctor_ret_class* GetConstructor() \ { \ return new ctor_class; \ } \ - extern "C" OPENTRACK_EXPORT Metadata* GetMetadata() \ + extern "C" OPENTRACK_PLUGIN_EXPORT Metadata* GetMetadata() \ { \ return new metadata_class; \ } \ - extern "C" OPENTRACK_EXPORT dialog_ret_class* GetDialog() \ + extern "C" OPENTRACK_PLUGIN_EXPORT dialog_ret_class* GetDialog() \ { \ return new dialog_class; \ } @@ -65,7 +89,7 @@ struct IFilter virtual void center() {} }; -struct IFilterDialog : public BaseDialog +struct IFilterDialog : public plugin_api::detail::BaseDialog { // optional destructor virtual ~IFilterDialog() {} @@ -93,7 +117,7 @@ struct IProtocol virtual QString game_name() = 0; }; -struct IProtocolDialog : public BaseDialog +struct IProtocolDialog : public plugin_api::detail::BaseDialog { // optional destructor virtual ~IProtocolDialog() {} @@ -118,7 +142,7 @@ struct ITracker virtual void data(double *data) = 0; }; -struct ITrackerDialog : public BaseDialog +struct ITrackerDialog : public plugin_api::detail::BaseDialog { // optional destructor virtual ~ITrackerDialog() {} diff --git a/opentrack/plugin-support.hpp b/opentrack/plugin-support.hpp index 3a0a3420..9968890d 100644 --- a/opentrack/plugin-support.hpp +++ b/opentrack/plugin-support.hpp @@ -8,7 +8,7 @@ #pragma once #include "plugin-api.hpp" -#include "options.hpp" +#include "opentrack-compat/options.hpp" #include <QWidget> #include <QDebug> diff --git a/opentrack/selected-libraries.hpp b/opentrack/selected-libraries.hpp index 7779c231..5384f9e2 100644 --- a/opentrack/selected-libraries.hpp +++ b/opentrack/selected-libraries.hpp @@ -11,7 +11,14 @@ #include "opentrack/plugin-support.hpp" #include <QFrame> -struct SelectedLibraries { + +#ifdef BUILD_api +# include "opentrack-compat/export.hpp" +#else +# include "opentrack-compat/import.hpp" +#endif + +struct OPENTRACK_EXPORT SelectedLibraries { using dylibptr = mem<dylib>; mem<ITracker> pTracker; mem<IFilter> pFilter; diff --git a/opentrack/shortcuts.cpp b/opentrack/shortcuts.cpp index a33cf088..bab1283a 100644 --- a/opentrack/shortcuts.cpp +++ b/opentrack/shortcuts.cpp @@ -7,112 +7,9 @@ */ #include "shortcuts.h" -#include <QMutexLocker> - -#if defined(_WIN32) -#include <functional> -#include <windows.h> #include "win32-shortcuts.h" -KeybindingWorker::~KeybindingWorker() { - should_quit = true; - wait(); - if (dinkeyboard) { - dinkeyboard->Unacquire(); - dinkeyboard->Release(); - } - if (din) - din->Release(); -} - -KeybindingWorker::KeybindingWorker(std::function<void(Key&)> receiver, WId h) : - should_quit(true), receiver(receiver) -{ - HWND handle = reinterpret_cast<HWND>(h); - - 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) handle, 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; -} - -void KeybindingWorker::run() { - BYTE keystate[256]; - - while (!should_quit) - { - if (dinkeyboard->GetDeviceState(256, (LPVOID)keystate) != DI_OK) { - qDebug() << "Tracker::run GetDeviceState function failed!" << GetLastError(); - Sleep(25); - continue; - } - - QMutexLocker l(&mtx); - - for (int i = 0; i < 256; i++) - { - Key k; - if (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; - receiver(k); - break; - } - } - } - - // keypresses get dropped with high values - Sleep(4); - } -} -#endif - -void Shortcuts::bind_keyboard_shortcut(K &key, key_opts& k) +void Shortcuts::bind_keyboard_shortcut(K &key, const key_opts& k) { #if !defined(_WIN32) using sh = QxtGlobalShortcut; @@ -136,56 +33,73 @@ void Shortcuts::bind_keyboard_shortcut(K &key, key_opts& k) key = K(); int idx = 0; QKeySequence code; - - if (k.keycode == "") - code = QKeySequence(Qt::Key_unknown); + + 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 - 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; + { + 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; + } } #endif #ifdef _WIN32 -void Shortcuts::receiver(Key &k) +void Shortcuts::receiver(const Key& k) { - std::vector<K*> ks { &keyCenter, &keyToggle, &keyZero }; - for (K* k_ : ks) + const int sz = keys.size(); + for (int i = 0; i < sz; i++) { - if (k.keycode != k_->keycode) + 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) continue; - if (!k_->should_process()) + if (!k_.should_process()) continue; - if (k_->alt && !k.alt) continue; - if (k_->ctrl && !k.ctrl) continue; - if (k_->shift && !k.shift) continue; + if (k_.alt && !k.alt) continue; + if (k_.ctrl && !k.ctrl) continue; + if (k_.shift && !k.shift) continue; - if (k_ == &keyCenter) - emit center(); - else if (k_ == &keyToggle) - emit toggle(); - else if (k_ == &keyZero) - emit zero(); + fun(); } } #endif -void Shortcuts::reload() { - bind_keyboard_shortcut(keyCenter, s.center); - bind_keyboard_shortcut(keyToggle, s.toggle); - bind_keyboard_shortcut(keyZero, s.zero); -#ifdef _WIN32 - bool is_new = keybindingWorker == nullptr; - if (is_new) +void Shortcuts::reload(const std::vector<std::tuple<key_opts&, fun> > &keys_) +{ + const int sz = keys_.size(); + keys = std::vector<tt>(); + + for (int i = 0; i < sz; i++) { - keybindingWorker = std::make_shared<KeybindingWorker>([&](Key& k) { receiver(k); }, handle); - keybindingWorker->start(); - } + const auto& kk = keys_[i]; + const key_opts& opts = std::get<0>(kk); + auto& fun = std::get<1>(kk); + K k; + bind_keyboard_shortcut(k, opts); + keys.push_back(std::tuple<K, Shortcuts::fun>(k, fun)); +#ifndef _WIN32 + connect(k.get(), &QxtGlobalShortcut::activated, fun); #endif + } } diff --git a/opentrack/shortcuts.h b/opentrack/shortcuts.h index 63d91829..38037923 100644 --- a/opentrack/shortcuts.h +++ b/opentrack/shortcuts.h @@ -8,86 +8,27 @@ #pragma once #include <QObject> -#include <QWidget> -#include "opentrack-compat/timer.hpp" -#include <QThread> -#include <QMessageBox> -#include <QCheckBox> -#include <QComboBox> -#include <QSettings> -#include <QMutex> +#include <tuple> +#include <vector> +#include <functional> -#include "qxt-mini/QxtGlobalShortcut" -#include "opentrack/plugin-support.hpp" -#include "opentrack/options.hpp" -#include "opentrack/main-settings.hpp" - -using namespace options; - -extern QList<QString> global_key_sequences; - -struct key_opts { - value<QString> keycode; - - key_opts(pbundle b, const QString& name) : - keycode(b, QString("keycode-%1").arg(name), "") - {} -}; - -#if defined(_WIN32) -extern QList<int> global_windows_key_sequences; -# undef DIRECTINPUT_VERSION -# define DIRECTINPUT_VERSION 0x0800 -# include <windows.h> -# include <dinput.h> - -struct Key { - BYTE keycode; - bool shift; - bool ctrl; - bool alt; - Timer timer; -public: - Key() : keycode(0), shift(false), ctrl(false), alt(false) - { - } - - bool should_process() - { - if (keycode == 0) - return false; - bool ret = timer.elapsed_ms() > 100; - timer.start(); - return ret; - } -}; +#ifdef BUILD_api +# include "opentrack-compat/export.hpp" #else -typedef unsigned char BYTE; -struct Key { int foo; }; +# include "opentrack-compat/import.hpp" #endif -struct Shortcuts; +#include "qxt-mini/QxtGlobalShortcut" +#include "opentrack-compat/options.hpp" +#include "opentrack/main-settings.hpp" -struct KeybindingWorker : public QThread { #ifdef _WIN32 -private: - LPDIRECTINPUT8 din; - LPDIRECTINPUTDEVICE8 dinkeyboard; - QMutex mtx; -public: - volatile bool should_quit; - std::function<void(Key&)> receiver; - ~KeybindingWorker(); - KeybindingWorker(std::function<void(Key&)> receiver, WId h); - void run(); -#else -public: - KeybindingWorker(Key, Key, Key, WId) {} - void run() {} +# include "keybinding-worker.hpp" #endif -}; -struct Shortcuts : public QObject { +using namespace options; + +struct OPENTRACK_EXPORT Shortcuts : public QObject { Q_OBJECT public: @@ -98,39 +39,24 @@ public: Key #endif ; - - K keyCenter; - K keyToggle; - K keyZero; - - WId handle; + + using fun = std::function<void(void)>; + using tt = std::tuple<K, fun>; + std::vector<tt> keys; #ifdef _WIN32 - mem<KeybindingWorker> keybindingWorker; + KeybindingWorker::Token key_token; #endif - struct settings : opts { - key_opts center, toggle, zero; - main_settings s_main; - settings() : - opts("keyboard-shortcuts"), - center(b, "center"), - toggle(b, "toggle"), - zero(b, "zero") - {} - } s; - - Shortcuts(WId handle) : handle(handle) { reload(); } + Shortcuts() +#ifdef _WIN32 + : key_token([&](const Key& k) { receiver(k); }) +#endif + {} - void reload(); + void reload(const std::vector<std::tuple<key_opts &, fun> > &keys); private: - void bind_keyboard_shortcut(K &key, key_opts& k); + void bind_keyboard_shortcut(K &key, const key_opts& k); #ifdef _WIN32 - void receiver(Key& k); + void receiver(const Key& k); #endif -signals: - void center(); - void toggle(); - void zero(); }; - - diff --git a/opentrack/simple-mat.hpp b/opentrack/simple-mat.hpp index e462812b..4f885f4f 100644 --- a/opentrack/simple-mat.hpp +++ b/opentrack/simple-mat.hpp @@ -157,7 +157,7 @@ public: return ret; } - Mat<num, h_, w_> operator*(const num& other) const + Mat<num, h_, w_> operator*(const num other) const { Mat<num, h_, w_> ret; for (int j = 0; j < h_; j++) @@ -167,20 +167,16 @@ public: } template<int p> - Mat<num, w_, p> operator*(const Mat<num, w_, p>& other) const + Mat<num, h_, p> operator*(const Mat<num, w_, p>& other) const { - Mat<num, w_, p> ret; - for (int j = 0; j < w_; j++) + Mat<num, h_, p> ret; + for (int k = 0; k < h_; k++) for (int i = 0; i < p; i++) { - num sum = num(0); - - for (int k = 0; k < h_; k++) - sum += data[j][k]*other(k, i); - - ret(j, i) = sum; + ret(k, i) = 0; + for (int j = 0; j < w_; j++) + ret(k, i) += data[k][j] * other(j, i); } - return ret; } @@ -189,7 +185,7 @@ public: template<typename... ts, int h__ = h_, int w__ = w_, typename = typename std::enable_if<is_arglist_correct<num, h__, w__, ts...>::value>::type> - Mat(ts const&... xs) + Mat(const ts... xs) { const std::initializer_list<num> init = { static_cast<num>(xs)... }; auto iter = init.begin(); @@ -198,14 +194,6 @@ public: data[j][i] = *iter++; } - template<typename t> - Mat(const t* xs) - { - for (int j = 0; j < h_; j++) - for (int i = 0; i < w_; i++) - data[j][i] = num(*xs++); - } - Mat() { for (int j = 0; j < h_; j++) @@ -244,7 +232,7 @@ public: for (int j = 0; j < h_; j++) for (int i = 0; i < w_; i++) - ret.data[i][j] = data[j][i]; + ret(i, j) = data[j][i]; return ret; } diff --git a/opentrack/state.hpp b/opentrack/state.hpp index e4cb0f04..dcb18293 100644 --- a/opentrack/state.hpp +++ b/opentrack/state.hpp @@ -9,7 +9,7 @@ #pragma once #include <vector> -#include "opentrack/options.hpp" +#include "opentrack-compat/options.hpp" using namespace options; #include "opentrack/plugin-support.hpp" #include "opentrack/main-settings.hpp" diff --git a/opentrack/tracker.h b/opentrack/tracker.h index 890660e1..36b5cad4 100644 --- a/opentrack/tracker.h +++ b/opentrack/tracker.h @@ -18,7 +18,7 @@ #include "spline-widget/functionconfig.h" #include "main-settings.hpp" -#include "options.hpp" +#include "opentrack-compat/options.hpp" #include <QMutex> #include <QThread> @@ -40,7 +40,13 @@ public: inline double operator()(int i) const { return axes[i]; } }; -class Tracker : private QThread { +#ifdef BUILD_api +# include "opentrack-compat/export.hpp" +#else +# include "opentrack-compat/import.hpp" +#endif + +class OPENTRACK_EXPORT Tracker : private QThread { Q_OBJECT private: QMutex mtx; @@ -74,7 +80,7 @@ public: void get_raw_and_mapped_poses(double* mapped, double* raw) const; void start() { QThread::start(); } - void toggle_enabled() { enabledp = !enabledp; } - void center() { centerp = !centerp; } - void zero() { zero_ = !zero_; } + void toggle_enabled() { qDebug() << "toggle enabled"; enabledp = !enabledp; } + void center() { qDebug() << "toggle center"; centerp = !centerp; } + void zero() { qDebug() << "toggle zero"; zero_ = !zero_; } }; diff --git a/opentrack/win32-joystick.cpp b/opentrack/win32-joystick.cpp new file mode 100644 index 00000000..5e6f2011 --- /dev/null +++ b/opentrack/win32-joystick.cpp @@ -0,0 +1,299 @@ +#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) +{ + this->joys = joys; + + HRESULT hr; + + if(FAILED(hr = di->EnumDevices(DI8DEVCLASS_GAMECTRL, + EnumJoysticksCallback, + this, + DIEDFL_ATTACHEDONLY))) + { + qDebug() << "failed enum joysticks" << hr; + return; + } + + auto& js = this->joys; + + for (auto it = js.begin(); it != js.end(); ) + { + if (std::find_if(all.cbegin(), all.cend(), [&](const QString& guid2) -> bool { return it->second->guid == guid2; }) == all.cend()) + it = js.erase(it); + else + it++; + } + + joys = this->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/win32-joystick.hpp b/opentrack/win32-joystick.hpp new file mode 100644 index 00000000..9c888326 --- /dev/null +++ b/opentrack/win32-joystick.hpp @@ -0,0 +1,100 @@ +#pragma once + +#ifdef _WIN32 + +#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); + } +}; +} + +#ifdef BUILD_api +# include "opentrack-compat/export.hpp" +#else +# include "opentrack-compat/import.hpp" +#endif + +struct OPENTRACK_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/win32-shortcuts.cpp b/opentrack/win32-shortcuts.cpp index a0ed51b3..fb84e709 100644 --- a/opentrack/win32-shortcuts.cpp +++ b/opentrack/win32-shortcuts.cpp @@ -116,6 +116,7 @@ QList<win_key> windows_key_sequences = 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), diff --git a/opentrack/win32-shortcuts.h b/opentrack/win32-shortcuts.h index fe92ae53..9b2c6121 100644 --- a/opentrack/win32-shortcuts.h +++ b/opentrack/win32-shortcuts.h @@ -10,7 +10,13 @@ struct win_key; extern QList<win_key> windows_key_mods; extern QList<win_key> windows_key_sequences; -struct win_key +#ifdef BUILD_api +# include "opentrack-compat/export.hpp" +#else +# include "opentrack-compat/import.hpp" +#endif + +struct OPENTRACK_EXPORT win_key { win_key(int win, Qt::Key qt) : win(win), qt(qt) {} int win; diff --git a/opentrack/work.hpp b/opentrack/work.hpp index 5d1f6b54..4188b937 100644 --- a/opentrack/work.hpp +++ b/opentrack/work.hpp @@ -16,6 +16,9 @@ #include <QObject> #include <QFrame> #include <memory> +#include <vector> +#include <functional> +#include <tuple> struct Work { @@ -24,32 +27,33 @@ struct Work mem<Tracker> tracker; mem<Shortcuts> sc; WId handle; - - Work(main_settings& s, Mappings& m, SelectedLibraries& libs, QObject* recv, WId handle) : + using fn = std::function<void(void)>; + using tt = std::tuple<key_opts&, fn>; + std::vector<std::tuple<key_opts&, fn>> 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(handle) + sc(std::make_shared<Shortcuts>()), + handle(handle), + keys { + tt(s.key_center, [&]() -> void { tracker->center(); }), + tt(s.key_toggle, [&]() -> void { tracker->toggle_enabled(); }), + tt(s.key_zero, [&]() -> void { tracker->zero(); }), + } { -#ifndef _WIN32 - QObject::connect(sc->keyCenter.get(), SIGNAL(activated()), recv, SLOT(shortcutRecentered())); - QObject::connect(sc->keyToggle.get(), SIGNAL(activated()), recv, SLOT(shortcutToggled())); - QObject::connect(sc->keyZero.get(), SIGNAL(activated()), recv, SLOT(shortcutZeroed())); -#else - QObject::connect(sc.get(), SIGNAL(center()), recv, SLOT(shortcutRecentered())); - QObject::connect(sc.get(), SIGNAL(toggle()), recv, SLOT(shortcutToggled())); - QObject::connect(sc.get(), SIGNAL(zero()), recv, SLOT(shortcutZeroed())); -#endif - tracker->start(); + reload_shortcuts(); + tracker->start(); } void reload_shortcuts() { - sc->reload(); + sc->reload(keys); } ~Work() { + sc = nullptr; // order matters, otherwise use-after-free -sh tracker = nullptr; libs = SelectedLibraries(); |