mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-20 04:25:42 +08:00
Add support for multiple Joystick devices
This includes a major refactoring of the Linux and Windows implementations Each joystick device is now its own QObject. They all setup their own native device instances and poll the button states. On Windows, COM initialization has been removed since it caused segfaults while releasing the DirectInput8 instance. ref T254
This commit is contained in:
committed by
Klaus Basan
parent
fa74f28bca
commit
84f1e21cf8
@@ -31,26 +31,71 @@ namespace
|
|||||||
|
|
||||||
namespace BlackInput
|
namespace BlackInput
|
||||||
{
|
{
|
||||||
|
CJoystickDevice::CJoystickDevice(const QString &path, QFile *fd, QObject *parent)
|
||||||
|
: QObject(parent), m_path(path), m_fd(fd)
|
||||||
|
{
|
||||||
|
m_fd->setParent(this);
|
||||||
|
char deviceName[256];
|
||||||
|
if (ioctl(m_fd->handle(), JSIOCGNAME(sizeof(deviceName)), deviceName) < 0)
|
||||||
|
{
|
||||||
|
strncpy(deviceName, "Unknown", sizeof(deviceName));
|
||||||
|
}
|
||||||
|
|
||||||
|
CLogMessage(this).info("Found joystick: %1") << deviceName;
|
||||||
|
|
||||||
|
fcntl(m_fd->handle(), F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
|
/* Forward */
|
||||||
|
struct js_event event;
|
||||||
|
while (m_fd->read(reinterpret_cast<char *>(&event), sizeof(event)) == sizeof(event)) {}
|
||||||
|
QSocketNotifier *notifier = new QSocketNotifier(m_fd->handle(), QSocketNotifier::Read, m_fd);
|
||||||
|
connect(notifier, &QSocketNotifier::activated, this, &CJoystickDevice::processInput);
|
||||||
|
m_name = QString(deviceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
CJoystickDevice::~CJoystickDevice()
|
||||||
|
{
|
||||||
|
if (m_fd)
|
||||||
|
{
|
||||||
|
m_fd->close();
|
||||||
|
m_fd->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoystickDevice::processInput()
|
||||||
|
{
|
||||||
|
struct js_event event;
|
||||||
|
while (m_fd->read(reinterpret_cast<char *>(&event), sizeof(event)) == sizeof(event))
|
||||||
|
{
|
||||||
|
switch (event.type & ~JS_EVENT_INIT)
|
||||||
|
{
|
||||||
|
case JS_EVENT_BUTTON:
|
||||||
|
if (event.value) { emit buttonChanged(m_name, event.number, true); }
|
||||||
|
else { emit buttonChanged(m_name, event.number, false); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CJoystickLinux::CJoystickLinux(QObject *parent) :
|
CJoystickLinux::CJoystickLinux(QObject *parent) :
|
||||||
IJoystick(parent),
|
IJoystick(parent),
|
||||||
m_mapper(new QSignalMapper(this)),
|
|
||||||
m_inputWatcher(new QFileSystemWatcher(this))
|
m_inputWatcher(new QFileSystemWatcher(this))
|
||||||
{
|
{
|
||||||
connect(m_mapper, static_cast<void (QSignalMapper::*)(QObject *)>(&QSignalMapper::mapped), this, &CJoystickLinux::ps_readInput);
|
|
||||||
|
|
||||||
m_inputWatcher->addPath(inputDevicesDir());
|
m_inputWatcher->addPath(inputDevicesDir());
|
||||||
connect(m_inputWatcher, &QFileSystemWatcher::directoryChanged, this, &CJoystickLinux::ps_directoryChanged);
|
connect(m_inputWatcher, &QFileSystemWatcher::directoryChanged, this, &CJoystickLinux::reloadDevices);
|
||||||
ps_directoryChanged(inputDevicesDir());
|
reloadDevices(inputDevicesDir());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CJoystickLinux::cleanupJoysticks()
|
void CJoystickLinux::cleanupJoysticks()
|
||||||
{
|
{
|
||||||
for (auto it = m_joysticks.begin(); it != m_joysticks.end();)
|
for (auto it = m_joystickDevices.begin(); it != m_joystickDevices.end();)
|
||||||
{
|
{
|
||||||
if (!it.value()->exists())
|
// Remove all joysticks that do not exist anymore (/dev/input/js* removed).
|
||||||
|
if (!(*it)->isAttached())
|
||||||
{
|
{
|
||||||
it.value()->deleteLater();
|
CJoystickDevice *joystickDevice = *it;
|
||||||
it = m_joysticks.erase(it);
|
it = m_joystickDevices.erase(it);
|
||||||
|
joystickDevice->deleteLater();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -61,39 +106,39 @@ namespace BlackInput
|
|||||||
|
|
||||||
void CJoystickLinux::addJoystickDevice(const QString &path)
|
void CJoystickLinux::addJoystickDevice(const QString &path)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!m_joysticks.contains(path));
|
QFile *fd = new QFile(path);
|
||||||
|
if (fd->open(QIODevice::ReadOnly))
|
||||||
QFile *joystick = new QFile(path, this);
|
|
||||||
if (joystick->open(QIODevice::ReadOnly))
|
|
||||||
{
|
{
|
||||||
char name[256];
|
CJoystickDevice *joystickDevice = new CJoystickDevice(path, fd, this);
|
||||||
if (ioctl(joystick->handle(), JSIOCGNAME(sizeof(name)), name) < 0)
|
connect(joystickDevice, &CJoystickDevice::buttonChanged, this, &CJoystickLinux::joystickButtonChanged);
|
||||||
{
|
m_joystickDevices.push_back(joystickDevice);
|
||||||
strncpy(name, "Unknown", sizeof(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
CLogMessage(this).info("Found joystick: %1") << name;
|
|
||||||
|
|
||||||
fcntl(joystick->handle(), F_SETFL, O_NONBLOCK);
|
|
||||||
|
|
||||||
/* Forward */
|
|
||||||
struct js_event event;
|
|
||||||
while (joystick->read(reinterpret_cast<char *>(&event), sizeof(event)) == sizeof(event)) {}
|
|
||||||
|
|
||||||
QSocketNotifier *notifier = new QSocketNotifier(joystick->handle(), QSocketNotifier::Read, joystick);
|
|
||||||
m_mapper->setMapping(notifier, joystick);
|
|
||||||
connect(notifier, &QSocketNotifier::activated, m_mapper, static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
|
|
||||||
notifier->setEnabled(true);
|
|
||||||
|
|
||||||
m_joysticks.insert(path, joystick);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
joystick->deleteLater();
|
fd->close();
|
||||||
|
fd->deleteLater();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CJoystickLinux::ps_directoryChanged(QString path)
|
void CJoystickLinux::joystickButtonChanged(const QString &name, int index, bool isPressed)
|
||||||
|
{
|
||||||
|
BlackMisc::Input::CHotkeyCombination oldCombination(m_buttonCombination);
|
||||||
|
if (isPressed)
|
||||||
|
{
|
||||||
|
m_buttonCombination.addJoystickButton({name, index});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_buttonCombination.removeJoystickButton({name, index});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldCombination != m_buttonCombination)
|
||||||
|
{
|
||||||
|
emit buttonCombinationChanged(m_buttonCombination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoystickLinux::reloadDevices(QString path)
|
||||||
{
|
{
|
||||||
cleanupJoysticks();
|
cleanupJoysticks();
|
||||||
|
|
||||||
@@ -101,41 +146,15 @@ namespace BlackInput
|
|||||||
for (const auto &entry : dir.entryInfoList())
|
for (const auto &entry : dir.entryInfoList())
|
||||||
{
|
{
|
||||||
QString f = entry.absoluteFilePath();
|
QString f = entry.absoluteFilePath();
|
||||||
if (!m_joysticks.contains(f))
|
auto it = std::find_if(m_joystickDevices.begin(), m_joystickDevices.end(), [path] (const CJoystickDevice *device)
|
||||||
|
{
|
||||||
|
return device->getPath() == path;
|
||||||
|
});
|
||||||
|
if (it == m_joystickDevices.end())
|
||||||
{
|
{
|
||||||
addJoystickDevice(f);
|
addJoystickDevice(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CJoystickLinux::ps_readInput(QObject *object)
|
|
||||||
{
|
|
||||||
QFile *joystick = qobject_cast<QFile *>(object);
|
|
||||||
Q_ASSERT(joystick);
|
|
||||||
|
|
||||||
|
|
||||||
struct js_event event;
|
|
||||||
while (joystick->read(reinterpret_cast<char *>(&event), sizeof(event)) == sizeof(event))
|
|
||||||
{
|
|
||||||
BlackMisc::Input::CHotkeyCombination oldCombination(m_buttonCombination);
|
|
||||||
switch (event.type & ~JS_EVENT_INIT)
|
|
||||||
{
|
|
||||||
case JS_EVENT_BUTTON:
|
|
||||||
if (event.value)
|
|
||||||
{
|
|
||||||
m_buttonCombination.addJoystickButton(event.number);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_buttonCombination.removeJoystickButton(event.number);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldCombination != m_buttonCombination)
|
|
||||||
{
|
|
||||||
emit buttonCombinationChanged(m_buttonCombination);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // ns
|
} // ns
|
||||||
|
|||||||
@@ -24,6 +24,38 @@ class QSignalMapper;
|
|||||||
|
|
||||||
namespace BlackInput
|
namespace BlackInput
|
||||||
{
|
{
|
||||||
|
//! Linux Joystick device
|
||||||
|
class CJoystickDevice : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! Constructor
|
||||||
|
CJoystickDevice(const QString &path, QFile *fd, QObject *parent);
|
||||||
|
~CJoystickDevice();
|
||||||
|
|
||||||
|
//! Get device name
|
||||||
|
QString getName() const { return m_name; }
|
||||||
|
|
||||||
|
//! Get device path, e.g. /dev/input/js0
|
||||||
|
QString getPath() const { return m_path; }
|
||||||
|
|
||||||
|
//! Is joystick device still attached?
|
||||||
|
bool isAttached() const { return m_fd->exists(); }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
//! Joystick button changed
|
||||||
|
void buttonChanged(const QString &name, int index, bool isPressed);
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! Slot for reading the device handle
|
||||||
|
void processInput();
|
||||||
|
|
||||||
|
QString m_name; //!< Device name
|
||||||
|
QString m_path; //!< Device path, e.g. /dev/input/js0
|
||||||
|
QFile *m_fd = nullptr; //!< Linux file descriptor
|
||||||
|
};
|
||||||
|
|
||||||
//! Linux implemenation of IJoystick
|
//! Linux implemenation of IJoystick
|
||||||
//! \sa https://www.kernel.org/doc/Documentation/input/joystick-api.txt
|
//! \sa https://www.kernel.org/doc/Documentation/input/joystick-api.txt
|
||||||
class CJoystickLinux : public IJoystick
|
class CJoystickLinux : public IJoystick
|
||||||
@@ -42,7 +74,6 @@ namespace BlackInput
|
|||||||
virtual ~CJoystickLinux() = default;
|
virtual ~CJoystickLinux() = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
friend class IJoystick;
|
friend class IJoystick;
|
||||||
|
|
||||||
//! Removes all joysticks that are no longer present.
|
//! Removes all joysticks that are no longer present.
|
||||||
@@ -51,23 +82,18 @@ namespace BlackInput
|
|||||||
//! Adds new joystick input for reading
|
//! Adds new joystick input for reading
|
||||||
void addJoystickDevice(const QString &path);
|
void addJoystickDevice(const QString &path);
|
||||||
|
|
||||||
|
void joystickButtonChanged(const QString &name, int index, bool isPressed);
|
||||||
|
|
||||||
//! Constructor
|
//! Constructor
|
||||||
CJoystickLinux(QObject *parent = nullptr);
|
CJoystickLinux(QObject *parent = nullptr);
|
||||||
|
|
||||||
private slots:
|
|
||||||
|
|
||||||
//! Slot for handling directory changes
|
//! Slot for handling directory changes
|
||||||
//! \param path Watched directory path.
|
//! \param path Watched directory path.
|
||||||
void ps_directoryChanged(QString path);
|
void reloadDevices(QString path);
|
||||||
|
|
||||||
//! Slot for reading the device handle
|
|
||||||
//! \param object QFile that has data to be read.
|
|
||||||
void ps_readInput(QObject *object);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BlackMisc::Input::CHotkeyCombination m_buttonCombination;
|
BlackMisc::Input::CHotkeyCombination m_buttonCombination;
|
||||||
QSignalMapper *m_mapper = nullptr; //!< Maps device handles
|
QVector<CJoystickDevice *> m_joystickDevices; //!< All joystick devices
|
||||||
QMap<QString, QFile *> m_joysticks; //!< All read joysticks, file path <-> file instance pairs
|
|
||||||
QFileSystemWatcher *m_inputWatcher = nullptr;
|
QFileSystemWatcher *m_inputWatcher = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,168 +20,43 @@ using namespace BlackMisc::Input;
|
|||||||
|
|
||||||
namespace BlackInput
|
namespace BlackInput
|
||||||
{
|
{
|
||||||
const TCHAR *CJoystickWindows::m_helperWindowClassName = TEXT("HelperWindow");
|
CJoystickDevice::CJoystickDevice(DirectInput8Ptr directInputPtr, const DIDEVICEINSTANCE *pdidInstance, QObject *parent)
|
||||||
const TCHAR *CJoystickWindows::m_helperWindowName = TEXT("JoystickCatcherWindow");
|
: QObject(parent),
|
||||||
ATOM CJoystickWindows::m_helperWindowClass = 0;
|
m_directInput(directInputPtr)
|
||||||
HWND CJoystickWindows::m_helperWindow = nullptr;
|
|
||||||
|
|
||||||
CJoystickWindows::CJoystickWindows(QObject *parent) : IJoystick(parent)
|
|
||||||
{
|
{
|
||||||
// Initialize COM
|
m_deviceName = QString::fromWCharArray(pdidInstance->tszInstanceName);
|
||||||
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
m_productName = QString::fromWCharArray(pdidInstance->tszProductName);
|
||||||
|
m_guidDevice = pdidInstance->guidInstance;
|
||||||
this->initDirectInput();
|
m_guidProduct = pdidInstance->guidProduct;
|
||||||
this->enumJoystickDevices();
|
|
||||||
this->filterJoystickDevices();
|
|
||||||
if (!m_availableJoystickDevices.isEmpty()) { this->createJoystickDevice(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CJoystickWindows::~CJoystickWindows()
|
bool CJoystickDevice::init(HWND helperWindow)
|
||||||
{
|
{
|
||||||
if (m_directInputDevice)
|
|
||||||
{
|
|
||||||
// currently disabled as it causes crashi
|
|
||||||
// m_directInputDevice->Release();
|
|
||||||
// m_directInputDevice = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_directInput)
|
|
||||||
{
|
|
||||||
//! \todo hack without input device this crashes
|
|
||||||
if (m_directInputDevice) { m_directInput->Release(); }
|
|
||||||
m_directInput = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
CoUninitialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CJoystickWindows::timerEvent(QTimerEvent *event)
|
|
||||||
{
|
|
||||||
Q_UNUSED(event);
|
|
||||||
this->pollDeviceState();
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT CJoystickWindows::initDirectInput()
|
|
||||||
{
|
|
||||||
HRESULT hr = CoCreateInstance(CLSID_DirectInput8, nullptr, CLSCTX_INPROC_SERVER, IID_IDirectInput8, (LPVOID *)&m_directInput);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
CLogMessage(this).error("Cannot create instance %1") << GetLastError();
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
HINSTANCE instance = GetModuleHandle(nullptr);
|
|
||||||
if (instance == nullptr)
|
|
||||||
{
|
|
||||||
CLogMessage(this).error("GetModuleHandle() failed with error code: %1") << GetLastError();
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = m_directInput->Initialize(instance, DIRECTINPUT_VERSION);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
CLogMessage(this).error("Direct input init failed");
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT CJoystickWindows::enumJoystickDevices()
|
|
||||||
{
|
|
||||||
if (!m_directInput)
|
|
||||||
{
|
|
||||||
CLogMessage(this).warning("No direct input");
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
if (FAILED(hr = m_directInput->EnumDevices(DI8DEVCLASS_GAMECTRL, enumJoysticksCallback, this, DIEDFL_ATTACHEDONLY)))
|
|
||||||
{
|
|
||||||
CLogMessage(this).error("Error reading joystick devices");
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_availableJoystickDevices.isEmpty())
|
|
||||||
{
|
|
||||||
CLogMessage(this).info("No joystick device found");
|
|
||||||
}
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CJoystickWindows::filterJoystickDevices()
|
|
||||||
{
|
|
||||||
IDirectInputDevice8 *directInputDevice = nullptr;
|
|
||||||
DIDEVCAPS deviceCaps;
|
|
||||||
deviceCaps.dwSize = sizeof(DIDEVCAPS);
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
for (auto i = m_availableJoystickDevices.begin(); i != m_availableJoystickDevices.end();)
|
|
||||||
{
|
|
||||||
// Create device
|
|
||||||
hr = m_directInput->CreateDevice(i->guidDevice, &directInputDevice, nullptr);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
i = m_availableJoystickDevices.erase(i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = directInputDevice->GetCapabilities(&deviceCaps);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
i = m_availableJoystickDevices.erase(i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter devices with 0 buttons
|
|
||||||
if (deviceCaps.dwButtons == 0)
|
|
||||||
{
|
|
||||||
i = m_availableJoystickDevices.erase(i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (directInputDevice)
|
|
||||||
{
|
|
||||||
directInputDevice->Release();
|
|
||||||
directInputDevice = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT CJoystickWindows::createJoystickDevice()
|
|
||||||
{
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
|
|
||||||
// Check if device list is empty first
|
|
||||||
if (m_availableJoystickDevices.isEmpty()) { return E_FAIL; }
|
|
||||||
|
|
||||||
// FIXME: Take the first device with number of buttons > 0
|
|
||||||
// For the future, the user should be able to choose which device
|
|
||||||
// he wants to use.
|
|
||||||
const CJoystickDeviceData &deviceData = m_availableJoystickDevices.constFirst();
|
|
||||||
|
|
||||||
// Create device
|
// Create device
|
||||||
Q_ASSERT_X(m_directInput, Q_FUNC_INFO, "We should not get here without direct input");
|
|
||||||
if (FAILED(hr = m_directInput->CreateDevice(deviceData.guidDevice, &m_directInputDevice, nullptr)))
|
|
||||||
{
|
{
|
||||||
CLogMessage(this).warning("IDirectInput8::CreateDevice failed: ") << hr;
|
IDirectInputDevice8 *diDevice = nullptr;
|
||||||
return hr;
|
if (FAILED(hr = m_directInput->CreateDevice(m_guidDevice, &diDevice, nullptr)))
|
||||||
|
{
|
||||||
|
CLogMessage(this).warning("IDirectInput8::CreateDevice failed: ") << hr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_directInputDevice.reset(diDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->createHelperWindow();
|
|
||||||
|
|
||||||
// Set cooperative level
|
// Set cooperative level
|
||||||
if (FAILED(hr = m_directInputDevice->SetCooperativeLevel(m_helperWindow, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND)))
|
if(!helperWindow) { return false; }
|
||||||
|
if (FAILED(hr = m_directInputDevice->SetCooperativeLevel(helperWindow, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND)))
|
||||||
{
|
{
|
||||||
CLogMessage(this).warning("IDirectInputDevice8::SetCooperativeLevel failed: ") << hr;
|
CLogMessage(this).warning("IDirectInputDevice8::SetCooperativeLevel failed: ") << hr;
|
||||||
return hr;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set data format to c_dfDIJoystick2
|
// Set data format to c_dfDIJoystick2
|
||||||
if (FAILED(hr = m_directInputDevice->SetDataFormat(&c_dfDIJoystick2)))
|
if (FAILED(hr = m_directInputDevice->SetDataFormat(&c_dfDIJoystick2)))
|
||||||
{
|
{
|
||||||
CLogMessage(this).warning("IDirectInputDevice8::SetDataFormat failed: ") << hr;
|
CLogMessage(this).warning("IDirectInputDevice8::SetDataFormat failed: ") << hr;
|
||||||
return hr;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DIDEVCAPS deviceCaps;
|
DIDEVCAPS deviceCaps;
|
||||||
@@ -190,106 +65,34 @@ namespace BlackInput
|
|||||||
if (FAILED(hr = m_directInputDevice->GetCapabilities(&deviceCaps)))
|
if (FAILED(hr = m_directInputDevice->GetCapabilities(&deviceCaps)))
|
||||||
{
|
{
|
||||||
CLogMessage(this).warning("IDirectInputDevice8::GetCapabilities failed: ") << hr;
|
CLogMessage(this).warning("IDirectInputDevice8::GetCapabilities failed: ") << hr;
|
||||||
return hr;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_joystickDeviceInputs.clear();
|
// Filter devices with 0 buttons
|
||||||
|
if (deviceCaps.dwButtons == 0) { return false; }
|
||||||
|
|
||||||
if (FAILED(hr = m_directInputDevice->EnumObjects(enumObjectsCallback, this, DIDFT_BUTTON)))
|
if (FAILED(hr = m_directInputDevice->EnumObjects(enumObjectsCallback, this, DIDFT_BUTTON)))
|
||||||
{
|
{
|
||||||
CLogMessage(this).warning("IDirectInputDevice8::EnumObjects failed: ") << hr;
|
CLogMessage(this).warning("IDirectInputDevice8::EnumObjects failed: ") << hr;
|
||||||
return hr;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CLogMessage(this).info("Created joystick device '%1' with %2 buttons") << deviceData.deviceName << deviceCaps.dwButtons;
|
CLogMessage(this).info("Created joystick device '%1' with %2 buttons") << m_deviceName << deviceCaps.dwButtons;
|
||||||
|
|
||||||
this->startTimer(50);
|
this->startTimer(50);
|
||||||
return hr;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CJoystickWindows::createHelperWindow()
|
void CJoystickDevice::timerEvent(QTimerEvent *event)
|
||||||
{
|
{
|
||||||
HINSTANCE hInstance = GetModuleHandle(nullptr);
|
Q_UNUSED(event);
|
||||||
WNDCLASS wce;
|
pollDeviceState();
|
||||||
|
|
||||||
// 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)
|
HRESULT CJoystickDevice::pollDeviceState()
|
||||||
{
|
|
||||||
BlackMisc::Input::CHotkeyCombination oldCombination(m_buttonCombination);
|
|
||||||
if (isPressed) { m_buttonCombination.addJoystickButton(buttonIndex); }
|
|
||||||
else { m_buttonCombination.removeJoystickButton(buttonIndex); }
|
|
||||||
|
|
||||||
if (oldCombination != m_buttonCombination)
|
|
||||||
{
|
|
||||||
emit buttonCombinationChanged(m_buttonCombination);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
DIJOYSTATE2 state;
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
if (!m_directInputDevice)
|
|
||||||
{
|
|
||||||
CLogMessage(this).warning("No input device");
|
|
||||||
return S_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(hr = m_directInputDevice->Poll()))
|
if (FAILED(hr = m_directInputDevice->Poll()))
|
||||||
{
|
{
|
||||||
if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
|
if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
|
||||||
@@ -319,12 +122,149 @@ namespace BlackInput
|
|||||||
for (CJoystickDeviceInput input : m_joystickDeviceInputs)
|
for (CJoystickDeviceInput input : m_joystickDeviceInputs)
|
||||||
{
|
{
|
||||||
const qint32 buttonIndex = input.m_offset - DIJOFS_BUTTON0;
|
const qint32 buttonIndex = input.m_offset - DIJOFS_BUTTON0;
|
||||||
updateAndSendButtonStatus(buttonIndex, state.rgbButtons[buttonIndex] & 0x80);
|
bool isPressed = state.rgbButtons[buttonIndex] & 0x80;
|
||||||
|
|
||||||
|
if (isPressed) { emit buttonChanged(m_deviceName, buttonIndex, true); }
|
||||||
|
else { emit buttonChanged(m_deviceName, buttonIndex, false); }
|
||||||
|
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CALLBACK CJoystickDevice::enumObjectsCallback(const DIDEVICEOBJECTINSTANCE *dev, LPVOID pvRef)
|
||||||
|
{
|
||||||
|
CJoystickDevice *joystickDevice = static_cast<CJoystickDevice *>(pvRef);
|
||||||
|
|
||||||
|
// Make sure we only got GUID_Button types
|
||||||
|
if (dev->guidType != GUID_Button) return DIENUM_CONTINUE;
|
||||||
|
|
||||||
|
CJoystickDeviceInput deviceInput;
|
||||||
|
deviceInput.m_number = joystickDevice->m_joystickDeviceInputs.size();
|
||||||
|
deviceInput.m_offset = DIJOFS_BUTTON(deviceInput.m_number);
|
||||||
|
deviceInput.m_name = QString::fromWCharArray(dev->tszName);
|
||||||
|
|
||||||
|
joystickDevice->m_joystickDeviceInputs.append(deviceInput);
|
||||||
|
|
||||||
|
CLogMessage(static_cast<CJoystickWindows *>(nullptr)).debug() << "Found joystick button" << QString::fromWCharArray(dev->tszName);
|
||||||
|
|
||||||
|
return DIENUM_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
CJoystickWindows::CJoystickWindows(QObject *parent) : IJoystick(parent)
|
||||||
|
{
|
||||||
|
this->createHelperWindow();
|
||||||
|
this->initDirectInput();
|
||||||
|
this->enumJoystickDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
CJoystickWindows::~CJoystickWindows()
|
||||||
|
{
|
||||||
|
m_joystickDevices.clear();
|
||||||
|
m_directInput.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseDirectInput(IDirectInput8 *obj)
|
||||||
|
{
|
||||||
|
if (obj) { obj->Release(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
HRESULT CJoystickWindows::initDirectInput()
|
||||||
|
{
|
||||||
|
IDirectInput8 *directInput = nullptr;
|
||||||
|
HRESULT hr = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, reinterpret_cast<LPVOID *>(&directInput), nullptr);
|
||||||
|
m_directInput = DirectInput8Ptr(directInput, ReleaseDirectInput);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT CJoystickWindows::enumJoystickDevices()
|
||||||
|
{
|
||||||
|
if (!m_directInput)
|
||||||
|
{
|
||||||
|
CLogMessage(this).warning("No direct input");
|
||||||
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HRESULT hr;
|
||||||
|
if (FAILED(hr = m_directInput->EnumDevices(DI8DEVCLASS_GAMECTRL, enumJoysticksCallback, this, DIEDFL_ATTACHEDONLY)))
|
||||||
|
{
|
||||||
|
CLogMessage(this).error("Error reading joystick devices");
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_joystickDevices.empty())
|
||||||
|
{
|
||||||
|
CLogMessage(this).info("No joystick device found");
|
||||||
|
}
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CJoystickWindows::createHelperWindow()
|
||||||
|
{
|
||||||
|
HINSTANCE hInstance = GetModuleHandle(nullptr);
|
||||||
|
WNDCLASS wce;
|
||||||
|
|
||||||
|
// Make sure window isn't created twice
|
||||||
|
if (helperWindow != nullptr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the class
|
||||||
|
ZeroMemory(&wce, sizeof(WNDCLASS));
|
||||||
|
wce.lpfnWndProc = DefWindowProc;
|
||||||
|
wce.lpszClassName = (LPCWSTR) helperWindowClassName;
|
||||||
|
wce.hInstance = hInstance;
|
||||||
|
|
||||||
|
/* Register the class. */
|
||||||
|
helperWindowClass = RegisterClass(&wce);
|
||||||
|
if (helperWindowClass == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the window. */
|
||||||
|
helperWindow = CreateWindowEx(0, helperWindowClassName,
|
||||||
|
helperWindowName,
|
||||||
|
WS_OVERLAPPED, CW_USEDEFAULT,
|
||||||
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||||
|
CW_USEDEFAULT, HWND_MESSAGE, nullptr,
|
||||||
|
hInstance, nullptr);
|
||||||
|
if (helperWindow == nullptr)
|
||||||
|
{
|
||||||
|
UnregisterClass(helperWindowClassName, hInstance);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoystickWindows::addJoystickDevice(const DIDEVICEINSTANCE *pdidInstance)
|
||||||
|
{
|
||||||
|
CJoystickDevice *device = new CJoystickDevice(m_directInput, pdidInstance, this);
|
||||||
|
bool success = device->init(helperWindow);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
connect(device, &CJoystickDevice::buttonChanged, this, &CJoystickWindows::joystickButtonChanged);
|
||||||
|
m_joystickDevices.push_back(device);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
device->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoystickWindows::joystickButtonChanged(const QString &name, int index, bool isPressed)
|
||||||
|
{
|
||||||
|
BlackMisc::Input::CHotkeyCombination oldCombination(m_buttonCombination);
|
||||||
|
if (isPressed) { m_buttonCombination.addJoystickButton({name, index}); }
|
||||||
|
else { m_buttonCombination.removeJoystickButton({name, index}); }
|
||||||
|
|
||||||
|
if (oldCombination != m_buttonCombination)
|
||||||
|
{
|
||||||
|
emit buttonCombinationChanged(m_buttonCombination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BOOL CALLBACK CJoystickWindows::enumJoysticksCallback(const DIDEVICEINSTANCE *pdidInstance, VOID *pContext)
|
BOOL CALLBACK CJoystickWindows::enumJoysticksCallback(const DIDEVICEINSTANCE *pdidInstance, VOID *pContext)
|
||||||
{
|
{
|
||||||
CJoystickWindows *obj = static_cast<CJoystickWindows *>(pContext);
|
CJoystickWindows *obj = static_cast<CJoystickWindows *>(pContext);
|
||||||
@@ -337,25 +277,12 @@ namespace BlackInput
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL CALLBACK CJoystickWindows::enumObjectsCallback(const DIDEVICEOBJECTINSTANCE *dev, LPVOID pvRef)
|
bool operator == (const CJoystickDevice &lhs, const CJoystickDevice &rhs)
|
||||||
{
|
{
|
||||||
CJoystickWindows *joystick = static_cast<CJoystickWindows *>(pvRef);
|
return lhs.m_guidDevice == rhs.m_guidDevice &&
|
||||||
|
lhs.m_guidProduct == rhs.m_guidProduct &&
|
||||||
// Make sure we only got GUID_Button types
|
lhs.m_deviceName == rhs.m_deviceName &&
|
||||||
if (dev->guidType != GUID_Button) return DIENUM_CONTINUE;
|
lhs.m_productName == rhs.m_productName;
|
||||||
|
|
||||||
joystick->addJoystickDeviceInput(dev);
|
|
||||||
CLogMessage(static_cast<CJoystickWindows *>(nullptr)).debug() << "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)
|
bool operator == (CJoystickDeviceInput const &lhs, CJoystickDeviceInput const &rhs)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "blackmisc/input/joystickbutton.h"
|
#include "blackmisc/input/joystickbutton.h"
|
||||||
#include "blackmisc/collection.h"
|
#include "blackmisc/collection.h"
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#ifndef NOMINMAX
|
#ifndef NOMINMAX
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
@@ -26,14 +27,8 @@
|
|||||||
|
|
||||||
namespace BlackInput
|
namespace BlackInput
|
||||||
{
|
{
|
||||||
//! Joystick device data
|
//! Shared IDirectInput8 ptr
|
||||||
struct CJoystickDeviceData
|
using DirectInput8Ptr = std::shared_ptr<IDirectInput8>;
|
||||||
{
|
|
||||||
GUID guidDevice; //!< Device GUID
|
|
||||||
GUID guidProduct; //!< Product GUID
|
|
||||||
QString deviceName; //!< Device name
|
|
||||||
QString productName; //!< Product name
|
|
||||||
};
|
|
||||||
|
|
||||||
//! Joystick device input/button
|
//! Joystick device input/button
|
||||||
struct CJoystickDeviceInput
|
struct CJoystickDeviceInput
|
||||||
@@ -43,8 +38,60 @@ namespace BlackInput
|
|||||||
QString m_name; //!< Input name
|
QString m_name; //!< Input name
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! Joystick device
|
||||||
|
class CJoystickDevice : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! Constructor
|
||||||
|
CJoystickDevice(DirectInput8Ptr directInputPtr, const DIDEVICEINSTANCE *pdidInstance, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
//! Initialize DirectInput device
|
||||||
|
bool init(HWND helperWindow);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
//! Joystick button changed
|
||||||
|
void buttonChanged(const QString &name, int index, bool isPressed);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//! Timer based updates
|
||||||
|
virtual void timerEvent(QTimerEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend bool operator == (const CJoystickDevice &lhs, const CJoystickDevice &rhs);
|
||||||
|
|
||||||
|
struct DirectInputDevice8Deleter
|
||||||
|
{
|
||||||
|
void operator()(IDirectInputDevice8 *obj)
|
||||||
|
{
|
||||||
|
if (obj)
|
||||||
|
{
|
||||||
|
obj->Unacquire();
|
||||||
|
obj->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using DirectInputDevice8Ptr = std::unique_ptr<IDirectInputDevice8, DirectInputDevice8Deleter>;
|
||||||
|
|
||||||
|
//! Poll the device buttons
|
||||||
|
HRESULT pollDeviceState();
|
||||||
|
|
||||||
|
//! Joystick button enumeration callback
|
||||||
|
static BOOL CALLBACK enumObjectsCallback(const DIDEVICEOBJECTINSTANCE *dev, LPVOID pvRef);
|
||||||
|
|
||||||
|
GUID m_guidDevice; //!< Device GUID
|
||||||
|
GUID m_guidProduct; //!< Product GUID
|
||||||
|
QString m_deviceName; //!< Device name
|
||||||
|
QString m_productName; //!< Product name
|
||||||
|
DirectInput8Ptr m_directInput;
|
||||||
|
DirectInputDevice8Ptr m_directInputDevice;
|
||||||
|
QVector<CJoystickDeviceInput> m_joystickDeviceInputs;
|
||||||
|
};
|
||||||
|
|
||||||
//! Equal operator
|
//! Equal operator
|
||||||
bool operator == (CJoystickDeviceData const &lhs, CJoystickDeviceData const &rhs);
|
bool operator == (CJoystickDevice const &lhs, CJoystickDevice const &rhs);
|
||||||
|
|
||||||
//! Windows implemenation of IJoystick with DirectInput
|
//! Windows implemenation of IJoystick with DirectInput
|
||||||
class BLACKINPUT_EXPORT CJoystickWindows : public IJoystick
|
class BLACKINPUT_EXPORT CJoystickWindows : public IJoystick
|
||||||
@@ -61,10 +108,6 @@ namespace BlackInput
|
|||||||
//! \brief Destructor
|
//! \brief Destructor
|
||||||
virtual ~CJoystickWindows() override;
|
virtual ~CJoystickWindows() override;
|
||||||
|
|
||||||
protected:
|
|
||||||
//! Timer based updates
|
|
||||||
virtual void timerEvent(QTimerEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class IJoystick;
|
friend class IJoystick;
|
||||||
|
|
||||||
@@ -77,45 +120,26 @@ namespace BlackInput
|
|||||||
//! Enumerate all attached joystick devices
|
//! Enumerate all attached joystick devices
|
||||||
HRESULT enumJoystickDevices();
|
HRESULT enumJoystickDevices();
|
||||||
|
|
||||||
void filterJoystickDevices();
|
|
||||||
|
|
||||||
//! Create a joystick device
|
|
||||||
HRESULT createJoystickDevice();
|
|
||||||
|
|
||||||
//! Poll the device buttons
|
|
||||||
HRESULT pollDeviceState();
|
|
||||||
|
|
||||||
//! Creates a hidden DI helper window
|
//! Creates a hidden DI helper window
|
||||||
int createHelperWindow();
|
int createHelperWindow();
|
||||||
|
|
||||||
//! Update and signal button status to InputManager
|
|
||||||
void updateAndSendButtonStatus(qint32 buttonIndex, bool isPressed);
|
|
||||||
|
|
||||||
//! Add new joystick device
|
//! Add new joystick device
|
||||||
void addJoystickDevice(const DIDEVICEINSTANCE *pdidInstance);
|
void addJoystickDevice(const DIDEVICEINSTANCE *pdidInstance);
|
||||||
|
|
||||||
//! Add new joystick input/button
|
void joystickButtonChanged(const QString &name, int index, bool isPressed);
|
||||||
void addJoystickDeviceInput(const DIDEVICEOBJECTINSTANCE *dev);
|
|
||||||
|
|
||||||
//! Joystick enumeration callback
|
//! Joystick enumeration callback
|
||||||
static BOOL CALLBACK enumJoysticksCallback(const DIDEVICEINSTANCE *pdidInstance, VOID *pContext);
|
static BOOL CALLBACK enumJoysticksCallback(const DIDEVICEINSTANCE *pdidInstance, VOID *pContext);
|
||||||
|
|
||||||
//! Joystick button enumeration callback
|
DirectInput8Ptr m_directInput; //!< DirectInput object
|
||||||
static BOOL CALLBACK enumObjectsCallback(const DIDEVICEOBJECTINSTANCE *dev, LPVOID pvRef);
|
QVector<CJoystickDevice *> m_joystickDevices; //!< Joystick devices
|
||||||
|
|
||||||
// todo RW: Try to use QScopedPointer. So far I could not find out how to use it with IDirectInput8::CreateDevice
|
|
||||||
// remark KB: if created with CoCreateInstance we do not "own" the object and cannot free the memory, and must use release
|
|
||||||
IDirectInput8 *m_directInput = nullptr; //!< DirectInput object
|
|
||||||
IDirectInputDevice8 *m_directInputDevice = nullptr; //!< DirectInput device
|
|
||||||
QVector<CJoystickDeviceData> m_availableJoystickDevices; //!< List of found and available joystick devices
|
|
||||||
QVector<CJoystickDeviceInput> m_joystickDeviceInputs; //!< List of available device buttons
|
|
||||||
|
|
||||||
BlackMisc::Input::CHotkeyCombination m_buttonCombination;
|
BlackMisc::Input::CHotkeyCombination m_buttonCombination;
|
||||||
|
|
||||||
static const WCHAR *m_helperWindowClassName; //!< Helper window class name
|
const TCHAR *helperWindowClassName = TEXT("HelperWindow");
|
||||||
static const WCHAR *m_helperWindowName; //!< Helper window name
|
const TCHAR *helperWindowName = TEXT("JoystickCatcherWindow");
|
||||||
static ATOM m_helperWindowClass;
|
ATOM helperWindowClass = 0;
|
||||||
static HWND m_helperWindow; //!< Helper window handle
|
HWND helperWindow = nullptr;
|
||||||
};
|
};
|
||||||
} // ns
|
} // ns
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user