summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2018-07-22 11:52:51 +0200
committerStanislaw Halik <sthalik@misaki.pl>2018-07-24 00:41:32 +0200
commitce787c22098f32ea88c1c49efd786cf4adcf318b (patch)
tree4cbf5a883496ba93a9e32953c653d5b8cdd13118
parent90e7bf2962a798a0a89c92a31a2141946221012a (diff)
dinput: fix POV hats, spurious events
-rw-r--r--dinput/win32-joystick.cpp121
-rw-r--r--dinput/win32-joystick.hpp36
2 files changed, 84 insertions, 73 deletions
diff --git a/dinput/win32-joystick.cpp b/dinput/win32-joystick.cpp
index 823d9ae3..8f2b4085 100644
--- a/dinput/win32-joystick.cpp
+++ b/dinput/win32-joystick.cpp
@@ -1,21 +1,30 @@
#ifdef _WIN32
-#undef NDEBUG
#include "win32-joystick.hpp"
-#include "compat/sleep.hpp"
+#include "compat/macros.hpp"
+
+// doesn't play well with Qt Creator clang code model
+#if defined Q_CREATOR_RUN && defined _MSC_VER
+# undef offsetof
+# define offsetof(type, member) __builtin_offsetof(type, member)
+#else
+# include <cstddef>
+#endif
-#include <cstddef>
#include <algorithm>
#include <cmath>
-#include <objbase.h>
+
+#include <QWidget>
#include <QDebug>
-// XXX how many axis update events can we reasonably get in a short time frame?
-DIDEVICEOBJECTDATA win32_joy_ctx::joy::keystate_buffers[win32_joy_ctx::joy::num_buffers];
+#include <objbase.h>
+
+namespace win32_joy_impl {
QMutex win32_joy_ctx::enum_state::mtx;
win32_joy_ctx::enum_state win32_joy_ctx::enumerator;
+DIDEVICEOBJECTDATA win32_joy_ctx::joy::keystate_buffers[num_buffers] = {};
void win32_joy_ctx::poll(fn const& f)
{
@@ -26,9 +35,7 @@ void win32_joy_ctx::poll(fn const& f)
auto& joys = enumerator.get_joys();
for (auto& j : joys)
- {
j.second->poll(f);
- }
}
bool win32_joy_ctx::poll_axis(const QString &guid, int* axes)
@@ -53,19 +60,18 @@ bool win32_joy_ctx::poll_axis(const QString &guid, int* axes)
HRESULT hr;
if (!FAILED(hr = joy_handle->Poll()))
- {
ok = true;
- }
if (!ok && FAILED(hr = joy_handle->Acquire()))
{
+ (void)0;
//qDebug() << "joy acquire failed" << hr;
}
if (!ok)
{
- portable::sleep(25);
- (void) joy_handle->Unacquire();
+ (void)joy_handle->Unacquire();
+ Sleep(100);
continue;
}
@@ -74,7 +80,7 @@ bool win32_joy_ctx::poll_axis(const QString &guid, int* axes)
if (FAILED(hr = joy_handle->GetDeviceState(sizeof(js), &js)))
{
//qDebug() << "joy get state failed" << guid << hr;
- portable::sleep(50);
+ Sleep(500);
continue;
}
@@ -164,7 +170,7 @@ bool win32_joy_ctx::joy::poll(fn const& f)
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 get state failed" << guid << hr);
return false;
}
@@ -175,13 +181,10 @@ bool win32_joy_ctx::joy::poll(fn const& f)
bool is_pov = false;
int i = -1;
- // redefine since MSVC headers don't define as proper constants
-
#define POV_HAT_OFFSET(k) \
- (offsetof(DIJOYSTATE, rgdwPOV) + (k) * sizeof(DWORD))
-
+ (offsetof(DIJOYSTATE2, rgdwPOV) + (k) * sizeof(DWORD))
#define BUTTON_OFFSET(k) \
- (offsetof(DIJOYSTATE, rgbButtons) + (k) * sizeof(BYTE))
+ (offsetof(DIJOYSTATE2, rgbButtons) + (k) * sizeof(BYTE))
switch (event.dwOfs)
{
@@ -190,65 +193,53 @@ bool win32_joy_ctx::joy::poll(fn const& f)
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 >= BUTTON_OFFSET(0) && event.dwOfs <= BUTTON_OFFSET(127))
+ if (event.dwOfs >= BUTTON_OFFSET(0) && event.dwOfs <= BUTTON_OFFSET(max_buttons - 1))
{
- unsigned tmp = event.dwOfs;
- tmp -= BUTTON_OFFSET(0);
- tmp /= BUTTON_OFFSET(1) - BUTTON_OFFSET(0);
- tmp &= 127;
- i = tmp;
+ i = 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;
-
- 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);
- }
+ unsigned pos = event.dwData / value_per_pov_hat_direction;
- const bool state[] =
- {
- pos == 1,
- pos == 2,
- pos == 3,
- pos == 4
- };
-
- i = 128u + i * 4u;
+ i = max_buttons + i * pov_hat_directions;
- for (unsigned j = 0; j < 4; j++)
+ for (unsigned j = 0; j < pov_hat_directions; j++)
{
- //pressed[i] = state[j];
- f(guid, i + j, 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 (i >= 0 && 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()
-{
-}
+win32_joy_ctx::enum_state::enum_state() = default;
win32_joy_ctx::enum_state::~enum_state()
{
@@ -274,7 +265,7 @@ void win32_joy_ctx::enum_state::refresh()
this,
DIEDFL_ATTACHEDONLY)))
{
- qDebug() << "failed enum joysticks" << hr;
+ eval_once(qDebug() << "failed enum joysticks" << hr);
return;
}
@@ -317,7 +308,7 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumJoysticksCallback(const DIDEVICEINS
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)))
@@ -328,12 +319,12 @@ 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)
{
@@ -392,4 +383,6 @@ win32_joy_ctx::joy::~joy()
release();
}
+} // ns win32_joy_impl
+
#endif
diff --git a/dinput/win32-joystick.hpp b/dinput/win32-joystick.hpp
index 757a9706..1eef5bfa 100644
--- a/dinput/win32-joystick.hpp
+++ b/dinput/win32-joystick.hpp
@@ -12,32 +12,47 @@
#include "compat/qhash.hpp"
#include "export.hpp"
-#include <cstring>
#include <memory>
#include <vector>
#include <functional>
-#include <algorithm>
#include <unordered_map>
+#include <iterator>
#include <QString>
-#include <QDebug>
#include <QMutex>
-#include <QMutexLocker>
-#include <QWidget>
+
+namespace win32_joy_impl {
+
+static constexpr inline unsigned max_buttons = std::size(DIJOYSTATE2().rgbButtons);
+static constexpr inline unsigned max_pov_hats = std::size(DIJOYSTATE2().rgdwPOV);
+
+static constexpr inline unsigned pov_hat_directions = 8;
+
+// cf. https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v=vs.85)
+// see also remarks on the page
+// no need to check for pos == unsigned(-1) || pos == 0xffff,
+// this logic doesn't require that
+static constexpr inline unsigned value_per_pov_hat_direction = 36000 / pov_hat_directions;
+static constexpr inline unsigned max_buttons_and_pov_hats = max_buttons + max_pov_hats * pov_hat_directions;
+
+//static_assert(pov_hat_directions == 4 || pov_hat_directions == 8);
+
+// XXX how many axis update events can we reasonably get in a short time frame?
+static constexpr inline unsigned num_buffers = 16;
+
+#define WIN32_JOY_DEBUG
struct OTR_DINPUT_EXPORT win32_joy_ctx final
{
using fn = std::function<void(const QString& guid, int btn, bool held)>;
- struct joy
+ struct joy final
{
LPDIRECTINPUTDEVICE8 joy_handle;
QString guid, name;
- enum { num_pressed_keys = 128 + 4 * 4 };
- //bool pressed[num_pressed_keys] {};
+ bool last_state[max_buttons_and_pov_hats] {};
Timer first_timer;
- static constexpr int num_buffers = 16;
static DIDEVICEOBJECTDATA keystate_buffers[num_buffers];
joy(LPDIRECTINPUTDEVICE8 handle, const QString& guid, const QString& name);
@@ -92,3 +107,6 @@ private:
static enum_state enumerator;
};
+} // ns win32_joy_impl
+
+using win32_joy_ctx = win32_joy_impl::win32_joy_ctx;