18 using namespace swift::misc::input;
20 namespace swift::input
22 CJoystickDevice::CJoystickDevice(
DirectInput8Ptr directInputPtr,
const DIDEVICEINSTANCE *pdidInstance,
24 : QObject(parent), m_guidDevice(pdidInstance->guidInstance), m_guidProduct(pdidInstance->guidProduct),
25 m_deviceName(QString::fromWCharArray(pdidInstance->tszInstanceName).simplified()),
26 m_productName(QString::fromWCharArray(pdidInstance->tszProductName).simplified()),
27 m_directInput(directInputPtr)
37 IDirectInputDevice8 *diDevice =
nullptr;
38 if (FAILED(hr = m_directInput->CreateDevice(m_guidDevice, &diDevice,
nullptr)))
43 m_directInputDevice.reset(diDevice);
47 if (!helperWindow) {
return false; }
48 if (FAILED(hr = m_directInputDevice->SetCooperativeLevel(helperWindow, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND)))
55 if (FAILED(hr = m_directInputDevice->SetDataFormat(&c_dfDIJoystick2)))
62 deviceCaps.dwSize =
sizeof(DIDEVCAPS);
64 if (FAILED(hr = m_directInputDevice->GetCapabilities(&deviceCaps)))
71 if (deviceCaps.dwButtons == 0) {
return false; }
75 if (FAILED(hr = m_directInputDevice->EnumObjects(enumObjectsCallback,
this, DIDFT_PSHBUTTON)))
81 CLogMessage(
this).
info(u
"Created joystick device '%1' with %2 buttons") << m_deviceName << deviceCaps.dwButtons;
102 HRESULT CJoystickDevice::pollDeviceState()
104 m_directInputDevice->Poll();
107 HRESULT hr = m_directInputDevice->GetDeviceState(
sizeof(DIJOYSTATE2), &state);
108 if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
110 m_directInputDevice->Acquire();
111 m_directInputDevice->Poll();
112 hr = m_directInputDevice->GetDeviceState(
sizeof(DIJOYSTATE2), &state);
117 CLogMessage(
this).
warning(u
"Cannot acquire and poll joystick device %1. Removing it.") << m_deviceName;
122 for (
const CJoystickDeviceInput &input : std::as_const(m_joystickDeviceInputs))
124 const qint32 buttonIndex = input.m_offset - DIJOFS_BUTTON0;
125 const bool isPressed = state.rgbButtons[buttonIndex] & 0x80;
131 QString CJoystickDevice::hrString(HRESULT hr)
135 LPCTSTR errMsg = err.ErrorMessage();
136 return QString::fromWCharArray(errMsg);
139 BOOL CALLBACK CJoystickDevice::enumObjectsCallback(
const DIDEVICEOBJECTINSTANCE *dev, LPVOID pvRef)
144 if (dev->guidType != GUID_Button)
return DIENUM_CONTINUE;
146 CJoystickDeviceInput deviceInput;
147 const int number = joystickDevice->m_joystickDeviceInputs.size();
148 deviceInput.m_offset = DIJOFS_BUTTON(number);
149 deviceInput.m_button =
CJoystickButton(joystickDevice->m_deviceName, DIJOFS_BUTTON(number) - DIJOFS_BUTTON0);
151 joystickDevice->m_joystickDeviceInputs.append(deviceInput);
153 <<
"Found joystick button" << QString::fromWCharArray(dev->tszName) << joystickDevice->m_deviceName;
155 return DIENUM_CONTINUE;
162 HRESULT hr = CoInitializeEx(
nullptr, COINIT_MULTITHREADED);
165 if (hr == RPC_E_CHANGED_MODE)
167 CLogMessage(
this).
debug(u
"CoInitializeEx was already called with a different mode. Trying again.");
168 hr = CoInitializeEx(
nullptr, COINIT_APARTMENTTHREADED);
175 if (hr == S_OK || hr == S_FALSE)
177 m_coInitializeSucceeded =
true;
178 this->createHelperWindow();
182 this->initDirectInput();
183 this->enumJoystickDevices();
184 this->requestDeviceNotification();
193 for (
CJoystickDevice *joystickDevice : std::as_const(m_joystickDevices)) {
delete joystickDevice; }
194 m_joystickDevices.clear();
195 m_directInput.reset();
196 if (m_coInitializeSucceeded) { CoUninitialize(); }
197 if (hDevNotify) { UnregisterDeviceNotification(hDevNotify); }
198 destroyHelperWindow();
206 availableButtons.
push_back(device->getDeviceButtons());
208 return availableButtons;
211 void ReleaseDirectInput(IDirectInput8 *obj)
213 if (obj) { obj->Release(); }
216 HRESULT CJoystickWindows::initDirectInput()
218 IDirectInput8 *directInput =
nullptr;
221 HRESULT hr = CoCreateInstance(CLSID_DirectInput8,
nullptr, CLSCTX_INPROC_SERVER, IID_IDirectInput8,
222 reinterpret_cast<LPVOID *
>(&directInput));
223 if (FAILED(hr)) {
return hr; }
226 HINSTANCE instance = GetModuleHandle(
nullptr);
227 hr = m_directInput->Initialize(instance, DIRECTINPUT_VERSION);
231 HRESULT CJoystickWindows::enumJoystickDevices()
240 if (FAILED(hr = m_directInput->EnumDevices(DI8DEVCLASS_GAMECTRL, enumJoysticksCallback,
this,
241 DIEDFL_ATTACHEDONLY)))
247 if (m_joystickDevices.isEmpty()) {
CLogMessage(
this).
info(u
"No joystick device found"); }
251 int CJoystickWindows::createHelperWindow()
254 if (helperWindow !=
nullptr) {
return 0; }
256 HINSTANCE hInstance = GetModuleHandle(
nullptr);
258 ZeroMemory(&wce,
sizeof(wce));
259 wce.cbSize =
sizeof(wce);
260 wce.lpfnWndProc = windowProc;
261 wce.lpszClassName =
static_cast<LPCWSTR
>(helperWindowClassName);
262 wce.hInstance = hInstance;
265 if (!RegisterClassEx(&wce)) {
return -1; }
269 CreateWindowEx(0, helperWindowClassName, helperWindowName, WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT,
270 CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE,
nullptr, hInstance,
nullptr);
271 if (helperWindow ==
nullptr)
273 UnregisterClass(helperWindowClassName, hInstance);
277 SetProp(helperWindow, L
"CJoystickWindows",
this);
282 void CJoystickWindows::requestDeviceNotification()
284 DEV_BROADCAST_DEVICEINTERFACE notificationFilter;
285 ZeroMemory(¬ificationFilter,
sizeof(notificationFilter));
286 notificationFilter.dbcc_size =
sizeof(DEV_BROADCAST_DEVICEINTERFACE);
287 notificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
288 hDevNotify = RegisterDeviceNotification(helperWindow, ¬ificationFilter,
289 DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
292 void CJoystickWindows::destroyHelperWindow()
294 HINSTANCE hInstance = GetModuleHandle(
nullptr);
296 if (helperWindow ==
nullptr) {
return; }
298 DestroyWindow(helperWindow);
299 helperWindow =
nullptr;
301 UnregisterClass(helperWindowClassName, hInstance);
304 void CJoystickWindows::addJoystickDevice(
const DIDEVICEINSTANCE *pdidInstance)
306 CJoystickDevice *device =
new CJoystickDevice(m_directInput, pdidInstance,
this);
307 bool success = device->init(helperWindow);
312 m_joystickDevices.push_back(device);
314 else {
delete device; }
317 bool CJoystickWindows::isJoystickAlreadyAdded(
const DIDEVICEINSTANCE *pdidInstance)
const
319 for (
const CJoystickDevice *device : m_joystickDevices)
321 if (IsEqualGUID(device->getDeviceGuid(), pdidInstance->guidInstance)) {
return true; }
327 void CJoystickWindows::joystickButtonChanged(
const CJoystickButton &joystickButton,
bool isPressed)
336 void CJoystickWindows::removeJoystickDevice(
const GUID &guid)
338 for (
auto it = m_joystickDevices.begin(); it != m_joystickDevices.end(); ++it)
340 CJoystickDevice *device = *it;
341 if (IsEqualGUID(guid, device->getDeviceGuid()))
343 device->deleteLater();
344 m_joystickDevices.erase(it);
353 LRESULT CALLBACK CJoystickWindows::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
361 case WM_DEVICECHANGE:
363 if (wParam == DBT_DEVICEARRIVAL)
365 DEV_BROADCAST_HDR *dbh =
reinterpret_cast<DEV_BROADCAST_HDR *
>(lParam);
367 if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
369 joystickWindows->enumJoystickDevices();
376 return DefWindowProc(hWnd, uMsg, wParam, lParam);
379 BOOL CALLBACK CJoystickWindows::enumJoysticksCallback(
const DIDEVICEINSTANCE *pdidInstance, VOID *pContext)
386 if (!obj->isJoystickAlreadyAdded(pdidInstance))
388 obj->addJoystickDevice(pdidInstance);
390 <<
"Found joystick device" << QString::fromWCharArray(pdidInstance->tszInstanceName);
392 return DIENUM_CONTINUE;
397 return lhs.m_guidDevice == rhs.m_guidDevice && lhs.m_guidProduct == rhs.m_guidProduct &&
398 lhs.m_deviceName == rhs.m_deviceName && lhs.m_productName == rhs.m_productName;
Class for emitting a log message.
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Derived & debug()
Set the severity to debug.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
void push_back(const T &value)
Appends an element at the end of the sequence.
std::shared_ptr< IDirectInput8 > DirectInput8Ptr
Shared IDirectInput8 ptr.
Free functions in swift::misc.
QString classNameShort(const QObject *object)
Class name as from QMetaObject::className without namespace.