diff options
| author | Stanislaw Halik <sthalik@misaki.pl> | 2016-08-12 18:00:49 +0200 | 
|---|---|---|
| committer | Stanislaw Halik <sthalik@misaki.pl> | 2016-08-12 18:00:49 +0200 | 
| commit | 9040b187a1c4fa380f8a12207b9dd6d04b3a10ac (patch) | |
| tree | 115e1351571d690c1261a9d512e6d44e717f3051 /dinput | |
| parent | 13a18b149764509a3f460be86590250cdcf690fb (diff) | |
all: rename modules s#^opentrack-##. and opentrack -> api
Adjust usages.
Diffstat (limited to 'dinput')
| -rw-r--r-- | dinput/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | dinput/dinput.cpp | 98 | ||||
| -rw-r--r-- | dinput/dinput.hpp | 58 | ||||
| -rw-r--r-- | dinput/export.hpp | 28 | ||||
| -rw-r--r-- | dinput/keybinding-worker.cpp | 201 | ||||
| -rw-r--r-- | dinput/keybinding-worker.hpp | 80 | ||||
| -rw-r--r-- | dinput/win32-joystick.cpp | 359 | ||||
| -rw-r--r-- | dinput/win32-joystick.hpp | 101 | 
8 files changed, 929 insertions, 0 deletions
| diff --git a/dinput/CMakeLists.txt b/dinput/CMakeLists.txt new file mode 100644 index 00000000..ed0b5990 --- /dev/null +++ b/dinput/CMakeLists.txt @@ -0,0 +1,4 @@ +if(WIN32) +    opentrack_boilerplate(opentrack-dinput BIN) +    target_link_libraries(opentrack-dinput dinput) +endif() diff --git a/dinput/dinput.cpp b/dinput/dinput.cpp new file mode 100644 index 00000000..d408ff2f --- /dev/null +++ b/dinput/dinput.cpp @@ -0,0 +1,98 @@ +#ifdef _WIN32 + +#include "dinput.hpp" +#include "compat/win32-com.hpp" +#include <QDebug> + +std::atomic<int> dinput_handle::refcnt; +std::atomic_flag dinput_handle::init_lock = ATOMIC_FLAG_INIT; +dinput_handle::di_t dinput_handle::handle(dinput_handle::make_di()); + +LPDIRECTINPUT8& dinput_handle::init_di() +{ +    init_com_threading(com_multithreaded); + +    static LPDIRECTINPUT8 di_ = nullptr; +    if (di_ == nullptr) +    { +        if (!SUCCEEDED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&di_, NULL))) +        { +            di_ = nullptr; +        } +    } +    return di_; +} + +dinput_handle::di_t dinput_handle::make_di() +{ +    while (init_lock.test_and_set()) { /* busy loop */ } + +    LPDIRECTINPUT8& ret = init_di(); + +    init_lock.clear(); + +    return di_t(ret); +} + +void dinput_handle::di_t::free_di() +{ +    if (handle && *handle) +        (*handle)->Release(); +    *handle = nullptr; +    handle = nullptr; +} + +void dinput_handle::di_t::ref_di() +{ +    while (init_lock.test_and_set()) { /* busy loop */ } + +    const int refcnt_ = refcnt.fetch_add(1) + 1; +    qDebug() << "start: dinput refcount now" << (refcnt_); + +    init_lock.clear(); +} + +dinput_handle::di_t& dinput_handle::di_t::operator=(const di_t& new_di) +{ +    if (handle) +        unref_di(); + +    handle = new_di.handle; + +    if (handle) +        ref_di(); + +    return *this; +} + +void dinput_handle::di_t::unref_di() +{ +    while (init_lock.test_and_set()) { /* busy loop */ } + +    const int refcnt_ = refcnt.fetch_sub(1) - 1; + +    qDebug() << "exit: dinput refcount now" << refcnt_; + +    if (refcnt_ == 0) +    { +        qDebug() << "exit: deleting di handle"; +        free_di(); +    } + +    init_lock.clear(); +} + +dinput_handle::di_t::di_t(LPDIRECTINPUT8& handle) : handle(&handle) +{ +    ref_di(); +} + +dinput_handle::di_t::di_t() : handle(nullptr) {} + +dinput_handle::di_t::~di_t() +{ +    if (handle) +        unref_di(); +} + +#endif diff --git a/dinput/dinput.hpp b/dinput/dinput.hpp new file mode 100644 index 00000000..53f1c4af --- /dev/null +++ b/dinput/dinput.hpp @@ -0,0 +1,58 @@ +/* Copyright (c) 2016, 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 _WIN32 + +#include "export.hpp" + +#ifndef DIRECTINPUT_VERSION +#   define DIRECTINPUT_VERSION 0x800 +#endif +#include <dinput.h> + +#include <atomic> + +class OPENTRACK_DINPUT_EXPORT dinput_handle final +{ +public: +    class di_t; + +private: +    static std::atomic<int> refcnt; +    static std::atomic_flag init_lock; +    static di_t handle; + +    static LPDIRECTINPUT8& init_di(); +public: +    class di_t final +    { +        friend class dinput_handle; + +        LPDIRECTINPUT8* handle; + +        di_t(LPDIRECTINPUT8& handle); +        void free_di(); +        void unref_di(); +        void ref_di(); + +    public: +        LPDIRECTINPUT8 operator->() { return *handle; } +        operator LPDIRECTINPUT8() { return *handle; } +        LPDIRECTINPUT8 di() { return *handle; } +        di_t& operator=(const di_t& new_di); +        di_t(); +        ~di_t(); +    }; + +    static di_t make_di(); +    dinput_handle() = delete; +}; + +#endif diff --git a/dinput/export.hpp b/dinput/export.hpp new file mode 100644 index 00000000..51ee4531 --- /dev/null +++ b/dinput/export.hpp @@ -0,0 +1,28 @@ +#pragma once + +#ifdef BUILD_dinput +#   ifdef _WIN32 +#       define OPENTRACK_DINPUT_LINKAGE __declspec(dllexport) +#   else +#       define OPENTRACK_DINPUT_LINKAGE +#   endif + +#   ifndef _MSC_VER +#       define OPENTRACK_DINPUT_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_DINPUT_LINKAGE +#   else +#       define OPENTRACK_DINPUT_EXPORT OPENTRACK_DINPUT_LINKAGE +#   endif + +#else +#ifdef _WIN32 +#    define OPENTRACK_DINPUT_LINKAGE __declspec(dllimport) +#else +#    define OPENTRACK_DINPUT_LINKAGE +#endif + +#ifndef _MSC_VER +#    define OPENTRACK_DINPUT_EXPORT __attribute__ ((visibility ("default"))) OPENTRACK_DINPUT_LINKAGE +#else +#    define OPENTRACK_DINPUT_EXPORT OPENTRACK_DINPUT_LINKAGE +#endif +#endif diff --git a/dinput/keybinding-worker.cpp b/dinput/keybinding-worker.cpp new file mode 100644 index 00000000..32bfc6f0 --- /dev/null +++ b/dinput/keybinding-worker.cpp @@ -0,0 +1,201 @@ +/* Copyright (c) 2014-2015, Stanislaw Halik <sthalik@misaki.pl> + + * Permission to use, copy, modify, and/or distribute this + * software for any purpose with or without fee is hereby granted, + * provided that the above copyright notice and this permission + * notice appear in all copies. + */ + +#ifdef _WIN32 + +#include "keybinding-worker.hpp" +#include <functional> +#include <windows.h> +#include <QDebug> +#include <QMutexLocker> + +bool Key::should_process() +{ +    if (!enabled || (keycode == 0 && guid == "")) +        return false; +    bool ret = timer.elapsed_ms() > 100; +    timer.start(); +    return ret; +} + +KeybindingWorker::~KeybindingWorker() +{ +    qDebug() << "exit: destroying keybinding worker"; + +    should_quit = true; +    wait(); +    if (dinkeyboard) { +        dinkeyboard->Unacquire(); +        dinkeyboard->Release(); +    } +} + +void KeybindingWorker::init() +{ +    din = dinput_handle::make_di(); + +    if (!din) +    { +        qDebug() << "can't create dinput handle"; +        return; +    } + +    if (din->CreateDevice(GUID_SysKeyboard, &dinkeyboard, NULL) != DI_OK) { +        qDebug() << "setup CreateDevice function failed!" << GetLastError(); +        return; +    } + +    if (dinkeyboard->SetDataFormat(&c_dfDIKeyboard) != DI_OK) { +        qDebug() << "setup SetDataFormat function failed!" << GetLastError(); +        dinkeyboard->Release(); +        dinkeyboard = 0; +        return; +    } + +    if (dinkeyboard->SetCooperativeLevel((HWND) fake_main_window.winId(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND) != DI_OK) { +        dinkeyboard->Release(); +        dinkeyboard = 0; +        qDebug() << "setup SetCooperativeLevel function failed!" << GetLastError(); +        return; +    } + +    if (dinkeyboard->Acquire() != DI_OK) +    { +        dinkeyboard->Release(); +        dinkeyboard = 0; +        qDebug() << "setup dinkeyboard Acquire failed!" << GetLastError(); +        return; +    } +} + +KeybindingWorker::KeybindingWorker() : should_quit(false) +{ +    start(); +} + +KeybindingWorker& KeybindingWorker::make() +{ +    static KeybindingWorker k; +    return k; +} + +void KeybindingWorker::run() +{ +    init(); + +    BYTE keystate[256] = {0}; +    BYTE old_keystate[256] = {0}; + +    while (!should_quit) +    { +        { +            QMutexLocker l(&mtx); + +            if (receivers.size()) +            { +                { +                    const HRESULT hr = dinkeyboard->GetDeviceState(256, (LPVOID)keystate); + +                    if (hr != DI_OK) { +                        qDebug() << "Tracker::run GetDeviceState function failed!" << GetLastError(); +                        Sleep(25); +                        continue; +                    } +                } + +                { +                    using joy_fn = std::function<void(const QString& guid, int idx, bool held)>; + +                    joy_fn f = [&](const QString& guid, int idx, bool held) -> void { +                        Key k; +                        k.keycode = idx; +                        k.shift = !!(keystate[DIK_LSHIFT] & 0x80 || keystate[DIK_RSHIFT] & 0x80); +                        k.alt = !!(keystate[DIK_LALT] & 0x80 || keystate[DIK_RALT] & 0x80); +                        k.ctrl = !!(keystate[DIK_LCONTROL] & 0x80 || keystate[DIK_RCONTROL] & 0x80); +                        k.guid = guid; +                        k.held = held; + +                        for (auto& r : receivers) +                            r->operator()(k); +                    }; + +                    joy_ctx.poll(f); +                } + +                for (int i = 0; i < 256; i++) +                { +                    Key k; +                    if (old_keystate[i] != keystate[i]) +                    { +                        const bool held = keystate[i] & 0x80; +                        switch (i) +                        { +                        case DIK_LCONTROL: +                        case DIK_LSHIFT: +                        case DIK_LALT: +                        case DIK_RCONTROL: +                        case DIK_RSHIFT: +                        case DIK_RALT: +                            break; +                        default: +                            k.shift = !!(keystate[DIK_LSHIFT] & 0x80) || !!(keystate[DIK_RSHIFT] & 0x80); +                            k.alt = !!(keystate[DIK_LALT] & 0x80) || !!(keystate[DIK_RALT] & 0x80); +                            k.ctrl = !!(keystate[DIK_LCONTROL] & 0x80) || !!(keystate[DIK_RCONTROL] & 0x80); +                            k.keycode = i; +                            k.held = held; + +                            for (auto& r : receivers) +                                r->operator()(k); +                            break; +                        } +                    } +                    old_keystate[i] = keystate[i]; +                } +            } +        } + +        // keypresses get dropped with high values +        Sleep(4); +    } +} + +KeybindingWorker::fun* KeybindingWorker::_add_receiver(fun& receiver) +{ +    QMutexLocker l(&mtx); +    receivers.push_back(std::unique_ptr<fun>(new fun(receiver))); +    fun* f = receivers[receivers.size() - 1].get(); +    //qDebug() << "add receiver" << (long) f; +    joy_ctx.refresh(); +    return f; +} + +void KeybindingWorker::remove_receiver(KeybindingWorker::fun* pos) +{ +    QMutexLocker l(&mtx); +    bool ok = false; + +    using s = int; + +    for (int i = s(receivers.size()) - 1; i >= 0; i--) +    { +        using u = unsigned; +        if (receivers[u(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/dinput/keybinding-worker.hpp b/dinput/keybinding-worker.hpp new file mode 100644 index 00000000..39b850e0 --- /dev/null +++ b/dinput/keybinding-worker.hpp @@ -0,0 +1,80 @@ +/* Copyright (c) 2014-2015, Stanislaw Halik <sthalik@misaki.pl> + + * Permission to use, copy, modify, and/or distribute this + * software for any purpose with or without fee is hereby granted, + * provided that the above copyright notice and this permission + * notice appear in all copies. + */ + +#pragma once + +#include "export.hpp" + +#include "compat/timer.hpp" +#include "win32-joystick.hpp" +#include "dinput.hpp" +#include <QThread> +#include <QMutex> +#include <QWidget> +#include <QMainWindow> +#include <QDebug> +#include <functional> +#include <vector> + +struct OPENTRACK_DINPUT_EXPORT Key +{ +    BYTE keycode; +    QString guid; +    bool shift; +    bool ctrl; +    bool alt; +    bool held; +    bool enabled; +    Timer timer; +public: +    Key() : keycode(0), shift(false), ctrl(false), alt(false), held(true), enabled(true) {} + +    bool should_process(); +}; + +struct OPENTRACK_DINPUT_EXPORT KeybindingWorker : private QThread +{ +    using fun = std::function<void(const Key&)>; + +private: +    LPDIRECTINPUTDEVICE8 dinkeyboard; +    win32_joy_ctx joy_ctx; +    std::vector<std::unique_ptr<fun>> receivers; +    QMutex mtx; +    QMainWindow fake_main_window; +    dinput_handle::di_t din; +    volatile bool should_quit; + +    void run() override; +    void init(); +    KeybindingWorker(); + +    static KeybindingWorker& make(); +    fun* _add_receiver(fun &receiver); +    void remove_receiver(fun* pos); +    ~KeybindingWorker(); + +    KeybindingWorker(const KeybindingWorker&) = delete; +    KeybindingWorker& operator=(KeybindingWorker&) = delete; +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/dinput/win32-joystick.cpp b/dinput/win32-joystick.cpp new file mode 100644 index 00000000..9502bcd2 --- /dev/null +++ b/dinput/win32-joystick.cpp @@ -0,0 +1,359 @@ +#ifdef _WIN32 + +#undef NDEBUG +#include "win32-joystick.hpp" +#include "compat/sleep.hpp" +#include <cassert> +#include <cstring> +#include <algorithm> +#include <cmath> +#include <objbase.h> + +#include <QDebug> + +QMutex win32_joy_ctx::enum_state::mtx; +win32_joy_ctx::enum_state win32_joy_ctx::enumerator; + +void win32_joy_ctx::poll(fn f) +{ +    //refresh(false); + +    QMutexLocker l(&enumerator.mtx); + +    auto& joys = enumerator.get_joys(); + +    for (auto& j : joys) +    { +        j.second->poll(f); +    } +} + +bool win32_joy_ctx::poll_axis(const QString &guid, int* axes) +{ +    QMutexLocker l(&enumerator.mtx); + +    for (int k = 0; k < 10; k++) +    { +        if (k > 0) +            enumerator.refresh(); + +        const joys_t& joys = enumerator.get_joys(); +        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; + +        if (!FAILED(hr = joy_handle->Poll())) +        { +            ok = true; +        } + +        if (!ok && FAILED(hr = joy_handle->Acquire())) +        { +            //qDebug() << "joy acquire failed" << hr; +        } + +        if (!ok) +        { +            portable::sleep(25); +            (void) joy_handle->Unacquire(); +            continue; +        } + +        DIJOYSTATE2 js; +        std::memset(&js, 0, sizeof(js)); + +        if (FAILED(hr = joy_handle->GetDeviceState(sizeof(js), &js))) +        { +            //qDebug() << "joy get state failed" << guid << hr; +            portable::sleep(50); +            continue; +        } + +        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; +    } + +    return false; +} + +std::vector<win32_joy_ctx::joy_info> win32_joy_ctx::get_joy_info() +{ +    std::vector<joy_info> ret; +    QMutexLocker l(&enumerator.mtx); +    auto& joys = enumerator.get_joys(); +    ret.reserve(joys.size()); + +    for (auto& j : joys) +        ret.push_back(joy_info { j.second->name, j.first }); + +    std::sort(ret.begin(), ret.end(), [&](const joy_info& fst, const joy_info& snd) -> bool { return fst.name < snd.name; }); + +    return ret; +} + +win32_joy_ctx::win32_joy_ctx() +{ +    refresh(); +} + +void win32_joy_ctx::refresh() +{ +    QMutexLocker l(&enumerator.mtx); +    enumerator.refresh(); +} + +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; +    std::memset(&js, 0, sizeof(js)); + +    if (FAILED(hr = joy_handle->GetDeviceState(sizeof(js), &js))) +    { +        qDebug() << "joy get state failed" << guid << hr; +        return false; +    } + +    for (unsigned i = 0; i < 4; i++) +    { +        using std::round; + +        unsigned char pos; +        unsigned pos_ = js.rgdwPOV[i]; +        if ((pos_ & 0xffff) == 0xffff) +            pos = 0; +        else if (pos_ == ~0u) +            pos = 0; +        else +        { +            using uc = unsigned char; +            pos = uc(((pos_ / 9000u) % 4u) + 1u); +        } + +        const bool state[] = +        { +            pos == 1, +            pos == 2, +            pos == 3, +            pos == 4 +        }; + +        unsigned idx = 128u + i * 4u; + +        for (unsigned j = 0; j < 4; j++, idx++) +        { +            if (state[j] != pressed[idx]) +            { +                f(guid, int(idx), state[j]); +                pressed[idx] = state[j]; +            } +        } +    } + +    for (int i = 0; i < 128; i++) +    { +        const bool state = !!(js.rgbButtons[i] & 0x80); +        if (state != pressed[i]) +        { +            f(guid, i, state); +        } +        pressed[i] = state; +    } + +    return true; +} + +win32_joy_ctx::enum_state::enum_state() : di(dinput_handle::make_di()) +{ +} + +win32_joy_ctx::enum_state::~enum_state() +{ +    QMutexLocker l(&mtx); + +    joys = std::unordered_map<QString, std::shared_ptr<joy>>(); +} + +void win32_joy_ctx::enum_state::refresh() +{ +    all.clear(); + +    if (!di) +    { +        qDebug() << "can't create dinput"; +        return; +    } + +    HRESULT hr; + +    if(FAILED(hr = di->EnumDevices(DI8DEVCLASS_GAMECTRL, +                                   EnumJoysticksCallback, +                                   this, +                                   DIEDFL_ATTACHEDONLY))) +    { +        qDebug() << "failed enum joysticks" << hr; +        return; +    } + +    for (auto it = joys.begin(); it != joys.end(); ) +    { +        if (std::find_if(all.cbegin(), all.cend(), [&](const QString& guid2) -> bool { return it->second->guid == guid2; }) == all.end()) +        { +            it = joys.erase(it); +        } +        else +        { +            ++it; +        } +    } +} + +const win32_joy_ctx::joys_t& win32_joy_ctx::enum_state::get_joys() const { return joys; } + +BOOL CALLBACK 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); + +    const bool exists = state.joys.find(guid) != state.joys.end(); + +    state.all.push_back(guid); + +    if (exists) +        goto end; + +    { +        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; +        } + +        // not a static member - need main() to run for some time first +        static const QWidget fake_window; + +        if (FAILED(h->SetCooperativeLevel(reinterpret_cast<HWND>(fake_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; +        } + +        state.joys[guid] = std::make_shared<joy>(h, guid, name); +    } +end: +    return DIENUM_CONTINUE; +} + +BOOL CALLBACK win32_joy_ctx::enum_state::EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE *pdidoi, void *ctx) +{ +    if (pdidoi->dwType & DIDFT_AXIS) +    { +        DIPROPRANGE diprg; +        std::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() << "make joy" << guid << name << joy_handle; +    std::memset(pressed, 0, sizeof(pressed)); +} + +win32_joy_ctx::joy::~joy() +{ +    qDebug() << "nix joy" << guid; +    release(); +} + +#endif diff --git a/dinput/win32-joystick.hpp b/dinput/win32-joystick.hpp new file mode 100644 index 00000000..4b48d4a1 --- /dev/null +++ b/dinput/win32-joystick.hpp @@ -0,0 +1,101 @@ +/* Copyright (c) 2015-2016, 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 _WIN32 + +#include "dinput.hpp" +#include "compat/timer.hpp" +#include "export.hpp" +#include <cstring> +#include <memory> +#include <vector> +#include <functional> +#include <algorithm> +#include <unordered_map> +#include <QString> +#include <QDebug> +#include <QMutex> +#include <QMutexLocker> +#include <QWidget> + +namespace std { +template<> +struct hash<QString> +{ +    inline std::size_t operator()(const QString& value) const +    { +        return qHash(value); +    } +}; +} + +struct OPENTRACK_DINPUT_EXPORT win32_joy_ctx +{ +    using fn = std::function<void(const QString& guid, int btn, bool held)>; + +    struct joy +    { +        LPDIRECTINPUTDEVICE8 joy_handle; +        QString guid, name; +        bool pressed[128 + 4 * 4]; +        Timer first_timer; + +        joy(LPDIRECTINPUTDEVICE8 handle, const QString& guid, const QString& name); +        ~joy(); + +        void release(); +        bool poll(fn f); +    }; + +    using joys_t = std::unordered_map<QString, std::shared_ptr<joy>>; + +    static constexpr int joy_axis_size = 65535; + +    struct joy_info +    { +        QString name, guid; +    }; + +    void poll(fn f); +    bool poll_axis(const QString& guid, int* axes); +    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(); +    void refresh(); + +    using di_t = dinput_handle::di_t; + +private: +    static QString guid_to_string(const GUID& guid); + +    class OPENTRACK_DINPUT_EXPORT enum_state final +    { +        std::vector<QString> all; +        joys_t joys; +        dinput_handle::di_t di; + +        static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, VOID* pContext); +        static BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* ctx); + +    public: +        static QMutex mtx; + +        enum_state(); +        ~enum_state(); +        void refresh(); +        const joys_t& get_joys() const; +    }; + +    static enum_state enumerator; +}; + +#endif | 
