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();  | 
