mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-31 12:55:33 +08:00
refs #317 IJoystick windows implementation
This commit is contained in:
328
src/blackinput/win/joystick_windows.cpp
Normal file
328
src/blackinput/win/joystick_windows.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
/* Copyright (C) 2014
|
||||
* swift Project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project,
|
||||
* including this file, may be copied, modified, propagated, or distributed except according to the terms
|
||||
* contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "joystick_windows.h"
|
||||
#include <QDebug>
|
||||
|
||||
// Qt5 defines UNICODE, hence we can expect an wchar_t strings.
|
||||
// If it fails to compile, because of char/wchar_t errors, you are most likely
|
||||
// using ascii functions of WINAPI. To fix it, introduce #ifdef UNICODE and add char
|
||||
// handling in the second branch.
|
||||
|
||||
using namespace BlackMisc::Hardware;
|
||||
|
||||
namespace BlackInput
|
||||
{
|
||||
|
||||
TCHAR *CJoystickWindows::m_helperWindowClassName = TEXT("SDLHelperWindowInputCatcher");
|
||||
TCHAR *CJoystickWindows::m_helperWindowName = TEXT("SDLHelperWindowInputMsgWindow");
|
||||
ATOM CJoystickWindows::m_helperWindowClass = 0;
|
||||
HWND CJoystickWindows::m_helperWindow = nullptr;
|
||||
|
||||
CJoystickWindows::CJoystickWindows(QObject *parent) :
|
||||
IJoystick(parent)
|
||||
{
|
||||
// Initialize COM
|
||||
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
|
||||
initDirectInput();
|
||||
enumJoystickDevices();
|
||||
if (!m_availableJoystickDevices.isEmpty()) createJoystickDevice();
|
||||
}
|
||||
|
||||
CJoystickWindows::~CJoystickWindows()
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
void CJoystickWindows::startCapture()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void CJoystickWindows::triggerButton(const CJoystickButton button, bool isPressed)
|
||||
{
|
||||
if(!isPressed) emit buttonUp(button);
|
||||
else emit buttonDown(button);
|
||||
}
|
||||
|
||||
void CJoystickWindows::timerEvent(QTimerEvent * /* event */)
|
||||
{
|
||||
pollDeviceState();
|
||||
}
|
||||
|
||||
HRESULT CJoystickWindows::initDirectInput()
|
||||
{
|
||||
HRESULT hr;
|
||||
if (FAILED(hr = CoCreateInstance(CLSID_DirectInput8, nullptr, CLSCTX_INPROC_SERVER,
|
||||
IID_IDirectInput8, (LPVOID *)&m_directInput)))
|
||||
{
|
||||
// TODO Print an error
|
||||
return hr;
|
||||
}
|
||||
|
||||
HINSTANCE instance = GetModuleHandle(nullptr);
|
||||
if (instance == nullptr)
|
||||
{
|
||||
qWarning() << "GetModuleHandle() failed with error code" << GetLastError();
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if (FAILED(hr = m_directInput->Initialize(instance, DIRECTINPUT_VERSION)))
|
||||
{
|
||||
// TODO Print an error
|
||||
return hr;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CJoystickWindows::enumJoystickDevices()
|
||||
{
|
||||
HRESULT hr;
|
||||
if (FAILED(hr = m_directInput->EnumDevices(DI8DEVTYPE_JOYSTICK, enumJoysticksCallback, this, DIEDFL_ATTACHEDONLY)))
|
||||
{
|
||||
// TODO print error message
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (m_availableJoystickDevices.isEmpty()) qWarning() << "No joystick device found!";
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CJoystickWindows::createJoystickDevice()
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
DIPROPDWORD dipdw;
|
||||
|
||||
// Check if device list is empty first
|
||||
if (m_availableJoystickDevices.isEmpty()) return E_FAIL;
|
||||
|
||||
ZeroMemory(&dipdw, sizeof(DIPROPDWORD));
|
||||
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
||||
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
||||
|
||||
// FIXME: Take the first device for the time being
|
||||
// For the future, the user should be able to choose which device
|
||||
// he wants to use.
|
||||
CJoystickDeviceData deviceData = m_availableJoystickDevices.first();
|
||||
|
||||
// Create device
|
||||
if (FAILED(hr = m_directInput->CreateDevice(deviceData.guidDevice, &m_directInputDevice, nullptr)))
|
||||
{
|
||||
// FIXME: print error message
|
||||
return hr;
|
||||
}
|
||||
|
||||
createHelperWindow();
|
||||
|
||||
// Set cooperative level
|
||||
if (FAILED(hr = m_directInputDevice->SetCooperativeLevel(m_helperWindow, DISCL_NONEXCLUSIVE |
|
||||
DISCL_BACKGROUND)))
|
||||
{
|
||||
// FIXME: print error message
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Set data format to c_dfDIJoystick2
|
||||
if (FAILED(hr = m_directInputDevice->SetDataFormat(&c_dfDIJoystick2)))
|
||||
{
|
||||
// FIXME: print error message
|
||||
return hr;
|
||||
}
|
||||
|
||||
DIDEVCAPS deviceCaps;
|
||||
deviceCaps.dwSize = sizeof(DIDEVCAPS);
|
||||
// Get device capabilities - we are interested in the number of buttons.
|
||||
if (FAILED(hr = m_directInputDevice->GetCapabilities(&deviceCaps)))
|
||||
{
|
||||
// FIXME: print error message
|
||||
return hr;
|
||||
}
|
||||
|
||||
m_joystickDeviceInputs.clear();
|
||||
if (FAILED(hr = m_directInputDevice->EnumObjects(enumObjectsCallback, this, DIDFT_BUTTON)))
|
||||
{
|
||||
// FIXME: print error message
|
||||
return hr;
|
||||
}
|
||||
|
||||
qDebug() << "No. of buttons:" << deviceCaps.dwButtons;
|
||||
|
||||
startTimer(50);
|
||||
return hr;
|
||||
}
|
||||
|
||||
int CJoystickWindows::createHelperWindow()
|
||||
{
|
||||
HINSTANCE hInstance = GetModuleHandle(nullptr);
|
||||
WNDCLASS wce;
|
||||
|
||||
/* Make sure window isn't created twice. */
|
||||
if (m_helperWindow != nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create the class. */
|
||||
ZeroMemory(&wce, sizeof(WNDCLASS));
|
||||
wce.lpfnWndProc = DefWindowProc;
|
||||
wce.lpszClassName = (LPCWSTR) m_helperWindowClassName;
|
||||
wce.hInstance = hInstance;
|
||||
|
||||
/* Register the class. */
|
||||
m_helperWindowClass = RegisterClass(&wce);
|
||||
if (m_helperWindowClass == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create the window. */
|
||||
m_helperWindow = CreateWindowEx(0, m_helperWindowClassName,
|
||||
m_helperWindowName,
|
||||
WS_OVERLAPPED, CW_USEDEFAULT,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
CW_USEDEFAULT, HWND_MESSAGE, nullptr,
|
||||
hInstance, nullptr);
|
||||
if (m_helperWindow == nullptr)
|
||||
{
|
||||
UnregisterClass(m_helperWindowClassName, hInstance);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void CJoystickWindows::updateAndSendButtonStatus(qint32 buttonIndex, bool isPressed)
|
||||
{
|
||||
if (isPressed)
|
||||
{
|
||||
if (!m_pressedButtons.contains(buttonIndex))
|
||||
{
|
||||
CJoystickButton joystickButton(buttonIndex);
|
||||
emit buttonDown(joystickButton);
|
||||
m_pressedButtons.push_back(buttonIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_pressedButtons.contains(buttonIndex))
|
||||
{
|
||||
CJoystickButton joystickButton(buttonIndex);
|
||||
emit buttonUp(joystickButton);
|
||||
m_pressedButtons.remove(buttonIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CJoystickWindows::addJoystickDevice(const DIDEVICEINSTANCE *pdidInstance)
|
||||
{
|
||||
CJoystickDeviceData deviceData;
|
||||
deviceData.deviceName = QString::fromWCharArray(pdidInstance->tszInstanceName);
|
||||
deviceData.productName = QString::fromWCharArray(pdidInstance->tszProductName);
|
||||
deviceData.guidDevice = pdidInstance->guidInstance;
|
||||
deviceData.guidProduct = pdidInstance->guidProduct;
|
||||
|
||||
if (!m_availableJoystickDevices.contains(deviceData)) m_availableJoystickDevices.push_back(deviceData);
|
||||
}
|
||||
|
||||
void CJoystickWindows::addJoystickDeviceInput(const DIDEVICEOBJECTINSTANCE *dev)
|
||||
{
|
||||
CJoystickDeviceInput deviceInput;
|
||||
deviceInput.m_number = m_joystickDeviceInputs.size();
|
||||
deviceInput.m_offset = DIJOFS_BUTTON(deviceInput.m_number);
|
||||
deviceInput.m_name = QString::fromWCharArray(dev->tszName);
|
||||
|
||||
m_joystickDeviceInputs.append(deviceInput);
|
||||
}
|
||||
|
||||
HRESULT CJoystickWindows::pollDeviceState()
|
||||
{
|
||||
DIJOYSTATE2 state;
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (FAILED(hr = m_directInputDevice->Poll()))
|
||||
{
|
||||
if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
|
||||
{
|
||||
m_directInputDevice->Acquire();
|
||||
if (FAILED(hr = m_directInputDevice->Poll()))
|
||||
{
|
||||
// FIXME: print error message
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FAILED(hr = m_directInputDevice->GetDeviceState(sizeof(DIJOYSTATE2), &state)))
|
||||
{
|
||||
if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
|
||||
{
|
||||
m_directInputDevice->Acquire();
|
||||
if (FAILED(hr = m_directInputDevice->GetDeviceState(sizeof(DIJOYSTATE2), &state)))
|
||||
{
|
||||
// FIXME: print error message
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (CJoystickDeviceInput input : m_joystickDeviceInputs)
|
||||
{
|
||||
qint32 buttonIndex = input.m_offset - DIJOFS_BUTTON0;
|
||||
updateAndSendButtonStatus(buttonIndex, state.rgbButtons[buttonIndex] & 0x80);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
BOOL CALLBACK CJoystickWindows::enumJoysticksCallback(const DIDEVICEINSTANCE *pdidInstance, VOID *pContext)
|
||||
{
|
||||
CJoystickWindows *obj = static_cast<CJoystickWindows *>(pContext);
|
||||
|
||||
/* ignore XInput devices here, keep going. */
|
||||
//if (isXInputDevice( &pdidInstance->guidProduct )) return DIENUM_CONTINUE;
|
||||
|
||||
obj->addJoystickDevice(pdidInstance);
|
||||
qDebug() << "Found joystick device" << QString::fromWCharArray(pdidInstance->tszInstanceName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CALLBACK CJoystickWindows::enumObjectsCallback(const DIDEVICEOBJECTINSTANCE *dev, LPVOID pvRef)
|
||||
{
|
||||
CJoystickWindows *joystick = static_cast<CJoystickWindows *>(pvRef);
|
||||
|
||||
// Make sure we only got GUID_Button types
|
||||
if (dev->guidType != GUID_Button) return DIENUM_CONTINUE;
|
||||
|
||||
joystick->addJoystickDeviceInput(dev);
|
||||
qDebug() << "Found joystick button" << QString::fromWCharArray(dev->tszName);
|
||||
|
||||
return DIENUM_CONTINUE;
|
||||
}
|
||||
|
||||
bool operator == (CJoystickDeviceData const &lhs, CJoystickDeviceData const &rhs)
|
||||
{
|
||||
return lhs.guidDevice == rhs.guidDevice &&
|
||||
lhs.guidProduct == rhs.guidProduct &&
|
||||
lhs.deviceName == rhs.deviceName &&
|
||||
lhs.productName == rhs.productName;
|
||||
}
|
||||
|
||||
bool operator == (CJoystickDeviceInput const &lhs, CJoystickDeviceInput const &rhs)
|
||||
{
|
||||
return lhs.m_number == rhs.m_number &&
|
||||
lhs.m_offset == rhs.m_offset &&
|
||||
lhs.m_name == rhs.m_name;
|
||||
}
|
||||
|
||||
} // namespace BlackInput
|
||||
131
src/blackinput/win/joystick_windows.h
Normal file
131
src/blackinput/win/joystick_windows.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/* Copyright (C) 2014
|
||||
* swift Project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project,
|
||||
* including this file, may be copied, modified, propagated, or distributed except according to the terms
|
||||
* contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef BLACKINPUT_JOYSTICKWINDOWS_H
|
||||
#define BLACKINPUT_JOYSTICKWINDOWS_H
|
||||
|
||||
//! \file
|
||||
|
||||
#include "blackinput/joystick.h"
|
||||
#include "blackmisc/hwjoystickbutton.h"
|
||||
#include "blackmisc/collection.h"
|
||||
#include <QSet>
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <dinput.h>
|
||||
|
||||
namespace BlackInput
|
||||
{
|
||||
//! Joystick device data
|
||||
struct CJoystickDeviceData
|
||||
{
|
||||
GUID guidDevice; //!< Device GUID
|
||||
GUID guidProduct; //!< Product GUID
|
||||
QString deviceName; //!< Device name
|
||||
QString productName; //!< Product name
|
||||
};
|
||||
|
||||
//! Joystick device input/button
|
||||
struct CJoystickDeviceInput
|
||||
{
|
||||
int m_number; //!< Input number
|
||||
int m_offset; //!< Input offset
|
||||
QString m_name; //!< Input name
|
||||
};
|
||||
|
||||
//! Equal operator
|
||||
bool operator == (CJoystickDeviceData const &lhs, CJoystickDeviceData const &rhs);
|
||||
|
||||
//! Windows implemenation of IJoystick with DirectInput
|
||||
class CJoystickWindows : public IJoystick
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
//! \brief Destructor
|
||||
virtual ~CJoystickWindows();
|
||||
|
||||
//! \copydoc IJoystick::startCapture()
|
||||
virtual void startCapture() override;
|
||||
|
||||
//! \copydoc IJoystick::triggerButton()
|
||||
virtual void triggerButton(const BlackMisc::Hardware::CJoystickButton button, bool isPressed) override;
|
||||
|
||||
protected:
|
||||
|
||||
friend class IJoystick;
|
||||
|
||||
//! Destructor
|
||||
CJoystickWindows(QObject *parent = nullptr);
|
||||
|
||||
//! Copy Constructor
|
||||
CJoystickWindows(CJoystickWindows const &);
|
||||
|
||||
//! Assignment operator
|
||||
void operator=(CJoystickWindows const &);
|
||||
|
||||
//! Timer based updates
|
||||
virtual void timerEvent(QTimerEvent *event);
|
||||
|
||||
private:
|
||||
|
||||
//! Initialize DirectInput
|
||||
HRESULT initDirectInput();
|
||||
|
||||
//! Enumerate all attached joystick devices
|
||||
HRESULT enumJoystickDevices();
|
||||
|
||||
//! Create a joystick device
|
||||
HRESULT createJoystickDevice();
|
||||
|
||||
//! Poll the device buttons
|
||||
HRESULT pollDeviceState();
|
||||
|
||||
//! Creates a hidden DI helper window
|
||||
int createHelperWindow();
|
||||
|
||||
//! Update and signal button status to InputManager
|
||||
void updateAndSendButtonStatus(qint32 buttonIndex, bool isPressed);
|
||||
|
||||
//! Add new joystick device
|
||||
void addJoystickDevice(const DIDEVICEINSTANCE *pdidInstance);
|
||||
|
||||
//! Add new joystick input/button
|
||||
void addJoystickDeviceInput(const DIDEVICEOBJECTINSTANCE *dev);
|
||||
|
||||
//! Joystick enumeration callback
|
||||
static BOOL CALLBACK enumJoysticksCallback(const DIDEVICEINSTANCE *
|
||||
pdidInstance, VOID *pContext);
|
||||
|
||||
//! Joystick button enumeration callback
|
||||
static BOOL CALLBACK enumObjectsCallback(const DIDEVICEOBJECTINSTANCE *dev, LPVOID pvRef);
|
||||
|
||||
IDirectInput8 *m_directInput = nullptr; //!< DirectInput object
|
||||
IDirectInputDevice8 *m_directInputDevice = nullptr; //!< DirectInput device
|
||||
QList<CJoystickDeviceData> m_availableJoystickDevices; //!< List of found and available joystick devices
|
||||
|
||||
QList<CJoystickDeviceInput> m_joystickDeviceInputs; //!< List of available device buttons
|
||||
BlackMisc::CCollection<qint32> m_pressedButtons; //!< Collection of pressed buttons
|
||||
|
||||
IJoystick::Mode m_mode = ModeNominal; //!< Current working mode
|
||||
|
||||
static WCHAR *m_helperWindowClassName; //!< Helper window class name
|
||||
static WCHAR *m_helperWindowName; //!< Helper window name
|
||||
static ATOM m_helperWindowClass;
|
||||
static HWND m_helperWindow; //!< Helper window handle
|
||||
|
||||
};
|
||||
|
||||
} // namespace BlackInput
|
||||
|
||||
#endif // BLACKINPUT_JOYSTICK_WINDOWS_H
|
||||
Reference in New Issue
Block a user