diff options
Diffstat (limited to 'dinput/keybinding-worker.cpp')
-rw-r--r-- | dinput/keybinding-worker.cpp | 320 |
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; } } |