summaryrefslogtreecommitdiffhomepage
path: root/dinput/keybinding-worker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dinput/keybinding-worker.cpp')
-rw-r--r--dinput/keybinding-worker.cpp320
1 files changed, 198 insertions, 122 deletions
diff --git a/dinput/keybinding-worker.cpp b/dinput/keybinding-worker.cpp
index 30d21534..7cd8d663 100644
--- a/dinput/keybinding-worker.cpp
+++ b/dinput/keybinding-worker.cpp
@@ -9,21 +9,29 @@
#ifdef _WIN32
#include "keybinding-worker.hpp"
-#include "compat/meta.hpp"
+#include "compat/macros.h"
+#include "compat/thread-name.hpp"
#include <QDebug>
#include <QMutexLocker>
-#include <windows.h>
+#include <dinput.h>
-Key::Key() {}
+static void destroy(IDirectInputDevice8A*& dev)
+{
+ if (dev)
+ dev->Release();
+ dev = nullptr;
+}
+
+Key::Key() = default;
bool Key::should_process()
{
if (!enabled || (keycode == 0 && guid == ""))
return false;
- bool ret = prog1(!held || timer.elapsed_ms() > 100,
- timer.start());
+ bool ret = !held || timer.elapsed_ms() > 100;
+ timer.start();
return ret;
}
@@ -33,68 +41,82 @@ KeybindingWorker::~KeybindingWorker()
requestInterruption();
wait();
- if (dinkeyboard) {
- dinkeyboard->Unacquire();
- dinkeyboard->Release();
- }
+
+ destroy(dinkeyboard);
+ destroy(dinmouse);
}
-bool KeybindingWorker::init()
+bool KeybindingWorker::init_(IDirectInputDevice8A*& dev, const char* name, const GUID& guid, const DIDATAFORMAT& fmt)
{
+ if (dev)
+ return true;
+
if (!din)
{
qDebug() << "can't create dinput handle";
- return false;
+ goto fail;
}
- if (din->CreateDevice(GUID_SysKeyboard, &dinkeyboard, NULL) != DI_OK) {
- qDebug() << "setup CreateDevice function failed!" << GetLastError();
- return false;
+ if (auto hr = din->CreateDevice(guid, &dev, nullptr); hr != DI_OK)
+ {
+ qDebug() << "dinput: create" << name << "failed" << (void*)hr;
+ goto fail;
}
- if (dinkeyboard->SetDataFormat(&c_dfDIKeyboard) != DI_OK) {
- qDebug() << "setup SetDataFormat function failed!" << GetLastError();
- dinkeyboard->Release();
- dinkeyboard = 0;
- return false;
+ if (auto hr = dev->SetDataFormat(&fmt); hr != DI_OK)
+ {
+ qDebug() << "dinput:" << name << "SetDataFormat" << (void*)hr;
+ goto fail;
}
- 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 false;
+ if (auto hr = dev->SetCooperativeLevel((HWND) fake_main_window.winId(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
+ hr != DI_OK)
+ {
+ qDebug() << "dinput:" << name << "SetCooperativeLevel" << (void*)hr;
+ goto fail;
}
+ return true;
+fail:
+ destroy(dev);
+ return false;
+}
+
+bool KeybindingWorker::init()
+{
+ bool ret = init_(dinkeyboard, "keyboard", GUID_SysKeyboard, c_dfDIKeyboard) &&
+ init_(dinmouse, "mouse", GUID_SysMouse, c_dfDIMouse2);
+
+ if (!ret)
+ goto fail;
+
{
DIPROPDWORD dipdw;
- dipdw.dwData = 128;
+ dipdw.dwData = num_keyboard_states;
dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.diph.dwObj = 0;
dipdw.diph.dwSize = sizeof(dipdw);
- if ( dinkeyboard->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph) != DI_OK)
+
+ if (auto hr = dinkeyboard->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph); hr != DI_OK)
{
- qDebug() << "setup keyboard buffer mode failed!";
- dinkeyboard->Release();
- dinkeyboard = 0;
- return false;
+ qDebug() << "dinput: keyboard DIPROP_BUFFERSIZE" << (void*)hr;
+ goto fail;
}
}
- if (dinkeyboard->Acquire() != DI_OK)
- {
- dinkeyboard->Release();
- dinkeyboard = 0;
- qDebug() << "setup dinkeyboard Acquire failed!" << GetLastError();
- return false;
- }
-
return true;
+
+fail:
+ destroy(dinkeyboard);
+ destroy(dinmouse);
+ return false;
}
-KeybindingWorker::KeybindingWorker() : dinkeyboard(nullptr), din(dinput_handle::make_di())
+KeybindingWorker::KeybindingWorker()
{
+ fake_main_window.setAttribute(Qt::WA_NativeWindow);
+
if (init())
start(QThread::HighPriority);
}
@@ -107,102 +129,156 @@ KeybindingWorker& KeybindingWorker::make()
void KeybindingWorker::run()
{
+ portable::set_curthread_name("keybinding worker");
+
while (!isInterruptionRequested())
{
{
QMutexLocker l(&mtx);
- if (receivers.size())
+ if (!receivers.empty())
{
- /* There are some problems reported on various forums
- * with regard to key-up events. But that's what I dug up:
- *
- * https://www.gamedev.net/forums/topic/633011-keyboard-getdevicedata-buffered-never-releases-keys/
- *
- * "Over in the xna forums (http://xboxforums.create.msdn.com/forums/p/108722/642144.aspx#642144)
- * we discovered this behavior is caused by calling Unacquire in your event processing loop.
- * Funnily enough only the keyboard seems to be affected."
- *
- * Key-up events work on my end.
- */
-
- {
- DWORD sz = num_keyboard_states;
- const HRESULT hr = dinkeyboard->GetDeviceData(sizeof(*keyboard_states), keyboard_states, &sz, 0);
-
- if (hr != DI_OK)
- {
- qDebug() << "Tracker::run GetDeviceData function failed!" << hr;
- Sleep(25);
- continue;
- }
- else
- {
- for (unsigned k = 0; k < sz; k++)
- {
- const unsigned idx = keyboard_states[k].dwOfs & 0xff; // defensive programming
- const bool held = !!(keyboard_states[k].dwData & 0x80);
-
- switch (idx)
- {
- case DIK_LCONTROL:
- case DIK_LSHIFT:
- case DIK_LALT:
- case DIK_RCONTROL:
- case DIK_RSHIFT:
- case DIK_RALT:
- case DIK_LWIN:
- case DIK_RWIN:
- break;
- default:
- {
- Key k;
- k.shift = keystate[DIK_LSHIFT] | keystate[DIK_RSHIFT];
- k.alt = keystate[DIK_LALT] | keystate[DIK_RALT];
- k.ctrl = keystate[DIK_LCONTROL] | keystate[DIK_RCONTROL];
- k.keycode = idx;
- k.held = held;
-
- for (auto& r : receivers)
- (*r)(k);
- break;
- }
- }
- keystate[idx] = held;
- }
- }
- }
-
- {
- using joy_fn = std::function<void(const QString& guid, int idx, bool held)>;
-
- joy_fn f = [&](const QString& guid, int idx, bool held) {
- Key k;
- k.keycode = idx;
- k.shift = keystate[DIK_LSHIFT] | keystate[DIK_RSHIFT];
- k.alt = keystate[DIK_LALT] | keystate[DIK_RALT];
- k.ctrl = keystate[DIK_LCONTROL] | keystate[DIK_RCONTROL];
- k.guid = guid;
- k.held = held;
-
- for (auto& r : receivers)
- (*r)(k);
- };
-
- joy_ctx.poll(f);
- }
+ bool ok = true;
+
+ ok &= run_keyboard_nolock();
+ ok &= run_mouse_nolock();
+ ok &= run_joystick_nolock();
+
+ if (!ok)
+ Sleep(500);
}
}
- Sleep(100);
+ Sleep(25);
}
}
-KeybindingWorker::fun* KeybindingWorker::_add_receiver(fun& receiver)
+bool KeybindingWorker::run_mouse_nolock()
+{
+ DIMOUSESTATE2 state;
+
+ if (!di_t::poll_device(dinmouse))
+ eval_once(qDebug() << "dinput: mouse poll failed");
+
+ if (auto hr = dinmouse->GetDeviceState(sizeof(state), &state); hr != DI_OK)
+ {
+ eval_once(qDebug() << "dinput: mouse GetDeviceState failed" << (void*) hr << GetLastError);
+ return false;
+ }
+
+ Key k;
+ k.guid = QStringLiteral("mouse");
+
+ for (int i = first_mouse_button; i < num_mouse_buttons; i++)
+ {
+ const bool new_state = state.rgbButtons[i] & 0x80;
+ k.held = new_state;
+ k.keycode = i;
+ bool& old_state = mouse_state[i - first_mouse_button];
+ if (old_state != new_state)
+ {
+ for (auto& r : receivers)
+ (*r)(k);
+ }
+ old_state = new_state;
+ }
+ return true;
+}
+
+bool KeybindingWorker::run_keyboard_nolock()
+{
+ /* There are some problems reported on various forums
+ * with regard to key-up events. But that's what I dug up:
+ *
+ * https://www.gamedev.net/forums/topic/633011-keyboard-getdevicedata-buffered-never-releases-keys/
+ *
+ * "Over in the xna forums (http://xboxforums.create.msdn.com/forums/p/108722/642144.aspx#642144)
+ * we discovered this behavior is caused by calling Unacquire in your event processing loop.
+ * Funnily enough only the keyboard seems to be affected."
+ *
+ * Key-up events work on my end.
+ */
+
+ if (!di_t::poll_device(dinkeyboard))
+ eval_once(qDebug() << "dinput: keyboard poll failed");
+
+ DIDEVICEOBJECTDATA keyboard_states[num_keyboard_states];
+
+ DWORD sz = num_keyboard_states;
+ HRESULT hr = dinkeyboard->GetDeviceData(sizeof(*keyboard_states), keyboard_states, &sz, 0);
+
+ if (FAILED(hr))
+ {
+ eval_once(qDebug() << "dinput: keyboard GetDeviceData failed" << (void*)hr);
+ return false;
+ }
+
+ for (unsigned k = 0; k < sz; k++)
+ {
+ const int idx = keyboard_states[k].dwOfs & 0xff; // defensive programming
+ const bool held = !!(keyboard_states[k].dwData & 0x80);
+
+ if (held == keystate[idx])
+ continue;
+ keystate[idx] = held;
+
+ switch (idx)
+ {
+ case DIK_LCONTROL:
+ case DIK_LSHIFT:
+ case DIK_LALT:
+ case DIK_RCONTROL:
+ case DIK_RSHIFT:
+ case DIK_RALT:
+ case DIK_LWIN:
+ case DIK_RWIN:
+ break;
+ default:
+ {
+ Key k;
+ k.shift = keystate[DIK_LSHIFT] | keystate[DIK_RSHIFT];
+ k.alt = keystate[DIK_LALT] | keystate[DIK_RALT];
+ k.ctrl = keystate[DIK_LCONTROL] | keystate[DIK_RCONTROL];
+ k.keycode = idx;
+ k.held = held;
+
+ for (auto& r : receivers)
+ (*r)(k);
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool KeybindingWorker::run_joystick_nolock()
+{
+ using joy_fn = std::function<void(const QString& guid, int idx, bool held)>;
+
+ joy_fn f = [&](const QString& guid, int idx, bool held) {
+ Key k;
+ k.keycode = idx;
+ k.shift = keystate[DIK_LSHIFT] | keystate[DIK_RSHIFT];
+ k.alt = keystate[DIK_LALT] | keystate[DIK_RALT];
+ k.ctrl = keystate[DIK_LCONTROL] | keystate[DIK_RCONTROL];
+ k.guid = guid;
+ k.held = held;
+
+ for (auto& r : receivers)
+ (*r)(k);
+ };
+
+ joy_ctx.poll(f);
+
+ return true;
+}
+
+KeybindingWorker::fun* KeybindingWorker::add_receiver(fun& receiver)
{
QMutexLocker l(&mtx);
receivers.push_back(std::make_unique<fun>(receiver));
- fun* f = receivers[receivers.size() - 1].get();
+ fun* f = &*receivers[receivers.size() - 1];
//qDebug() << "add receiver" << (long) f;
joy_ctx.refresh();
return f;
@@ -218,7 +294,7 @@ void KeybindingWorker::remove_receiver(KeybindingWorker::fun* pos)
for (int i = s(receivers.size()) - 1; i >= 0; i--)
{
using u = unsigned;
- if (receivers[u(i)].get() == pos)
+ if (&*receivers[u(i)] == pos)
{
ok = true;
//qDebug() << "remove receiver" << (long) pos;
@@ -228,7 +304,7 @@ void KeybindingWorker::remove_receiver(KeybindingWorker::fun* pos)
}
if (!ok)
{
- qDebug() << "bad remove receiver" << (long) pos;
+ qDebug() << "bad remove receiver" << (void*) pos;
}
}