diff options
Diffstat (limited to 'dinput/win32-joystick.cpp')
-rw-r--r-- | dinput/win32-joystick.cpp | 215 |
1 files changed, 91 insertions, 124 deletions
diff --git a/dinput/win32-joystick.cpp b/dinput/win32-joystick.cpp index 9127b6a8..cb7a3837 100644 --- a/dinput/win32-joystick.cpp +++ b/dinput/win32-joystick.cpp @@ -1,40 +1,39 @@ #ifdef _WIN32 -#undef NDEBUG #include "win32-joystick.hpp" -#include "compat/sleep.hpp" -#include <cassert> -#include <cstring> +#include "compat/macros.h" + +#include <cstddef> #include <algorithm> #include <cmath> -#include <objbase.h> +#include <iterator> +#include <QWidget> #include <QDebug> -// XXX how many axis update events can we reasonably get in a short time frame? -enum { num_buffers = 256 }; -DIDEVICEOBJECTDATA win32_joy_ctx::joy::keystate_buffers[num_buffers]; +#include <dinput.h> +#include <objbase.h> + +namespace win32_joy_impl { QMutex win32_joy_ctx::enum_state::mtx; win32_joy_ctx::enum_state win32_joy_ctx::enumerator; -void win32_joy_ctx::poll(fn f) +void win32_joy_ctx::poll(fn const& f) { //refresh(false); - QMutexLocker l(&enumerator.mtx); + QMutexLocker l(&enum_state::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); + QMutexLocker l(&enum_state::mtx); for (int k = 0; k < 10; k++) { @@ -48,35 +47,15 @@ bool win32_joy_ctx::poll_axis(const QString &guid, int* axes) 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; - } + DIJOYSTATE2 js; - if (!ok) - { - portable::sleep(25); - (void) joy_handle->Unacquire(); + if (!di_t::poll_device(joy_handle)) continue; - } - DIJOYSTATE2 js; - std::memset(&js, 0, sizeof(js)); - - if (FAILED(hr = joy_handle->GetDeviceState(sizeof(js), &js))) + if (FAILED(joy_handle->GetDeviceState(sizeof(js), &js))) { - //qDebug() << "joy get state failed" << guid << hr; - portable::sleep(50); + //qDebug() << "joy get state failed" << guid; continue; } @@ -92,7 +71,7 @@ bool win32_joy_ctx::poll_axis(const QString &guid, int* axes) js.rglSlider[1] }; - for (int i = 0; i < 8; i++) + for (unsigned i = 0; i < std::size(values); i++) axes[i] = values[i]; return true; @@ -104,7 +83,7 @@ bool win32_joy_ctx::poll_axis(const QString &guid, int* axes) std::vector<win32_joy_ctx::joy_info> win32_joy_ctx::get_joy_info() { std::vector<joy_info> ret; - QMutexLocker l(&enumerator.mtx); + QMutexLocker l(&enum_state::mtx); auto& joys = enumerator.get_joys(); ret.reserve(joys.size()); @@ -123,17 +102,18 @@ win32_joy_ctx::win32_joy_ctx() void win32_joy_ctx::refresh() { - QMutexLocker l(&enumerator.mtx); + QMutexLocker l(&enum_state::mtx); enumerator.refresh(); } QString win32_joy_ctx::guid_to_string(const GUID& guid) { - char buf[40] = {0}; - wchar_t szGuidW[40] = {0}; + char buf[40] = {}; + wchar_t szGuidW[40] = {}; - StringFromGUID2(guid, szGuidW, 40); - WideCharToMultiByte(0, 0, szGuidW, -1, buf, 40, NULL, NULL); + StringFromGUID2(guid, szGuidW, sizeof(buf)); + WideCharToMultiByte(0, 0, szGuidW, -1, buf, sizeof(buf), nullptr, nullptr); + buf[sizeof(buf)-1] = 0; return QString(buf); } @@ -150,27 +130,24 @@ void win32_joy_ctx::joy::release() } } -bool win32_joy_ctx::joy::poll(fn f) +bool win32_joy_ctx::joy::poll(fn const& f) { HRESULT hr; - bool ok = false; - - (void) joy_handle->Acquire(); - if (!FAILED(hr = joy_handle->Poll())) - ok = true; - - if (!ok) + if (!di_t::poll_device(joy_handle)) { - //qDebug() << "joy acquire failed" << guid << hr; - (void) joy_handle->Unacquire(); + eval_once(qDebug() << "joy poll failed" << guid << (void*)hr); + //(void)joy_handle->Unacquire(); + //Sleep(0); return false; } + DIDEVICEOBJECTDATA keystate_buffers[num_buffers]; + DWORD sz = num_buffers; if (FAILED(hr = joy_handle->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), keystate_buffers, &sz, 0))) { - //qDebug() << "joy get state failed" << guid << hr; + eval_once(qDebug() << "joy GetDeviceData failed" << guid << (void*)hr); return false; } @@ -181,74 +158,65 @@ bool win32_joy_ctx::joy::poll(fn f) bool is_pov = false; int i = -1; +#define POV_HAT_OFFSET(k) \ + (offsetof(DIJOYSTATE2, rgdwPOV) + (k) * sizeof(DWORD)) +#define BUTTON_OFFSET(k) \ + (offsetof(DIJOYSTATE2, rgbButtons) + (k) * sizeof(BYTE)) + switch (event.dwOfs) { - case DIJOFS_POV(0): i = 0, is_pov = true; break; - case DIJOFS_POV(2): i = 1, is_pov = true; break; - case DIJOFS_POV(3): i = 2, is_pov = true; break; - case DIJOFS_POV(4): i = 3, is_pov = true; break; + case POV_HAT_OFFSET(0): i = 0; is_pov = true; break; + case POV_HAT_OFFSET(1): i = 1; is_pov = true; break; + case POV_HAT_OFFSET(2): i = 2; is_pov = true; break; + case POV_HAT_OFFSET(3): i = 3; is_pov = true; break; default: - if (event.dwOfs >= DIJOFS_BUTTON0 && event.dwOfs <= DIJOFS_BUTTON(127)) + if (event.dwOfs >= BUTTON_OFFSET(0) && event.dwOfs <= BUTTON_OFFSET(max_buttons - 1)) { - unsigned tmp = event.dwOfs; - tmp -= DIJOFS_BUTTON0; - tmp /= DIJOFS_BUTTON1 - DIJOFS_BUTTON0; - tmp &= 127; - i = tmp; + i = int(event.dwOfs - BUTTON_OFFSET(0)); + i /= sizeof(DIJOYSTATE2().rgbButtons[0]); + i %= max_buttons; // defensive programming } break; } if (is_pov) { - //qDebug() << "DBG: pov" << i << event.dwData; - - using std::round; + unsigned pos = event.dwData / value_per_pov_hat_direction; - unsigned char pos; - unsigned pos_ = event.dwData; - if ((pos_ & 0xffff) == 0xffff) - pos = 0; - else if (pos_ == ~0u) - pos = 0; - else - { - using uc = unsigned char; - pos = uc(((pos_ / 9000u) % 4u) + 1u); - } + i = max_buttons + i * pov_hat_directions; - const bool state[] = + for (unsigned j = 0; j < pov_hat_directions; j++) { - pos == 1, - pos == 2, - pos == 3, - pos == 4 - }; - - i = 128u + i * 4u; - - for (unsigned j = 0; j < 4; j++) - { - //pressed[i] = state[j]; - f(guid, i, state[j]); + const unsigned idx = i + j; + const bool new_value = pos == j; + if (last_state[idx] != new_value) + { +#ifdef WIN32_JOY_DEBUG + qDebug() << "DBG: pov" << idx << (pos == j); +#endif + last_state[idx] = new_value; + f(guid, idx, new_value); + } } } - else if (i != -1) + else if ((unsigned)i < max_buttons) { - const bool state = !!(event.dwData & 0x80); - //qDebug() << "DBG: btn" << i << state; - //pressed[i] = state; - f(guid, i, state); + const bool new_value = !!(event.dwData & 0x80); + if (last_state[i] != new_value) + { +#ifdef WIN32_JOY_DEBUG + qDebug() << "DBG: btn" << i << new_value; +#endif + last_state[i] = new_value; + f(guid, i, new_value); + } } - } return true; } -win32_joy_ctx::enum_state::enum_state() : di(dinput_handle::make_di()) -{ -} +win32_joy_ctx::enum_state::enum_state() = default; win32_joy_ctx::enum_state::~enum_state() { @@ -260,10 +228,13 @@ win32_joy_ctx::enum_state::~enum_state() void win32_joy_ctx::enum_state::refresh() { all.clear(); +#ifdef __SANITIZE_ADDRESS__ + return; +#endif if (!di) { - qDebug() << "can't create dinput"; + qDebug() << "dinput: can't create dinput"; return; } @@ -274,20 +245,16 @@ void win32_joy_ctx::enum_state::refresh() this, DIEDFL_ATTACHEDONLY))) { - qDebug() << "failed enum joysticks" << hr; + eval_once(qDebug() << "dinput: failed enum joysticks" << (void*)hr); return; } for (auto it = joys.begin(); it != joys.end(); ) { if (std::find_if(all.cbegin(), all.cend(), [&](const QString& guid2) { return it->second->guid == guid2; }) == all.end()) - { it = joys.erase(it); - } else - { ++it; - } } } @@ -308,20 +275,20 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumJoysticksCallback(const DIDEVICEINS { HRESULT hr; - LPDIRECTINPUTDEVICE8 h; + IDirectInputDevice8A* h; if (FAILED(hr = state.di->CreateDevice(pdidInstance->guidInstance, &h, nullptr))) { - qDebug() << "createdevice" << guid << hr; + qDebug() << "dinput: failed joystick CreateDevice" << guid << (void*)hr; goto end; } - if (FAILED(h->SetDataFormat(&c_dfDIJoystick2))) + if (FAILED(hr = h->SetDataFormat(&c_dfDIJoystick2))) { - qDebug() << "format"; + qDebug() << "dinput: failed joystick SetDataFormat" << (void*)hr; h->Release(); goto end; } - // not a static member - need main() to run for some time first + // not a library-load-time 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))) @@ -332,16 +299,16 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumJoysticksCallback(const DIDEVICEINS } { - DIPROPDWORD dipdw; - dipdw.dwData = 128; + DIPROPDWORD dipdw = {}; dipdw.diph.dwHeaderSize = sizeof(dipdw.diph); + dipdw.diph.dwSize = sizeof(dipdw); dipdw.diph.dwHow = DIPH_DEVICE; dipdw.diph.dwObj = 0; - dipdw.diph.dwSize = sizeof(dipdw); + dipdw.dwData = num_buffers; if (h->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph) != DI_OK) { - qDebug() << "setup joystick buffer mode failed!"; + qDebug() << "dinput: joystick DIPROP_BUFFERSIZE"; h->Release(); goto end; } @@ -349,7 +316,7 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumJoysticksCallback(const DIDEVICEINS if (FAILED(hr = h->EnumObjects(EnumObjectsCallback, h, DIDFT_ALL))) { - qDebug() << "enum-objects"; + qDebug() << "dinput: joystick EnumObjects"; h->Release(); goto end; } @@ -364,10 +331,9 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumObjectsCallback(const DIDEVICEOBJEC { if (pdidoi->dwType & DIDFT_AXIS) { - DIPROPRANGE diprg; - std::memset(&diprg, 0, sizeof(diprg)); - diprg.diph.dwSize = sizeof( DIPROPRANGE ); - diprg.diph.dwHeaderSize = sizeof( DIPROPHEADER ); + DIPROPRANGE 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; @@ -375,9 +341,9 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumObjectsCallback(const DIDEVICEOBJEC HRESULT hr; - if (FAILED(hr = reinterpret_cast<LPDIRECTINPUTDEVICE8>(ctx)->SetProperty(DIPROP_RANGE, &diprg.diph))) + if (FAILED(hr = reinterpret_cast<IDirectInputDevice8A*>(ctx)->SetProperty(DIPROP_RANGE, &diprg.diph))) { - qDebug() << "DIPROP_RANGE" << hr; + qDebug() << "dinput: failed joystick DIPROP_RANGE" << (void*)hr; return DIENUM_STOP; } } @@ -385,7 +351,7 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumObjectsCallback(const DIDEVICEOBJEC return DIENUM_CONTINUE; } -win32_joy_ctx::joy::joy(LPDIRECTINPUTDEVICE8 handle, const QString &guid, const QString &name) +win32_joy_ctx::joy::joy(IDirectInputDevice8A* handle, const QString& guid, const QString &name) : joy_handle(handle), guid(guid), name(name) { //qDebug() << "make joy" << guid << name << joy_handle; @@ -397,4 +363,5 @@ win32_joy_ctx::joy::~joy() release(); } +} // ns win32_joy_impl #endif |