summaryrefslogtreecommitdiffhomepage
path: root/dinput/win32-joystick.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dinput/win32-joystick.cpp')
-rw-r--r--dinput/win32-joystick.cpp215
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