Merge pull request #2222 from ergo720/rawinput

Add controller hotplug support
This commit is contained in:
PatrickvL 2021-05-28 16:54:30 +02:00 committed by GitHub
commit 8087b157e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 202 additions and 2 deletions

View File

@ -65,6 +65,7 @@ file (GLOB CXBXR_HEADER_COMMON
"${CXBXR_ROOT_DIR}/src/common/input/InputManager.h"
"${CXBXR_ROOT_DIR}/src/common/input/SdlJoystick.h"
"${CXBXR_ROOT_DIR}/src/common/input/XInputPad.h"
"${CXBXR_ROOT_DIR}/src/common/input/RawDevice.h"
"${CXBXR_ROOT_DIR}/src/common/IPCHybrid.hpp"
"${CXBXR_ROOT_DIR}/src/common/Logging.h"
"${CXBXR_ROOT_DIR}/src/common/ReservedMemory.h"
@ -226,6 +227,7 @@ file (GLOB CXBXR_SOURCE_COMMON
"${CXBXR_ROOT_DIR}/src/common/input/InputManager.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/SdlJoystick.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/XInputPad.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/RawDevice.cpp"
"${CXBXR_ROOT_DIR}/src/common/Logging.cpp"
"${CXBXR_ROOT_DIR}/src/common/Settings.cpp"
"${CXBXR_ROOT_DIR}/src/common/Timer.cpp"

View File

@ -80,6 +80,7 @@ const char* g_EnumModules2String[to_underlying(CXBXR_MODULE::MAX)] = {
"DSSTREAM",
"DS3DCALC",
"XMO ",
"RINP ",
"KRNL ",
"LOG ",
"XBOX ",

View File

@ -88,6 +88,7 @@ typedef enum class _CXBXR_MODULE: unsigned int {
DSSTREAM,
DS3DCALC,
XMO,
RINP,
// kernel
KRNL,
LOG,

View File

@ -39,6 +39,7 @@
#include <core\kernel\exports\xboxkrnl.h> // For PKINTERRUPT, etc.
#include "SdlJoystick.h"
#include "XInputPad.h"
#include "RawDevice.h"
#include "DInputKeyboardMouse.h"
#include "InputManager.h"
#include "..\devices\usb\XidGamepad.h"
@ -88,16 +89,18 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
}
XInput::Init(m_Mtx);
RawInput::Init(m_Mtx, is_gui, m_hwnd);
Sdl::Init(m_Mtx, m_Cv, is_gui);
});
m_Cv.wait(lck, []() {
return (Sdl::SdlInitStatus != Sdl::SDL_NOT_INIT) &&
(XInput::XInputInitStatus != XInput::XINPUT_NOT_INIT);
(XInput::XInputInitStatus != XInput::XINPUT_NOT_INIT) &&
(RawInput::RawInputInitStatus != RawInput::RAWINPUT_NOT_INIT);
});
lck.unlock();
if (Sdl::SdlInitStatus < 0 || XInput::XInputInitStatus < 0) {
if (Sdl::SdlInitStatus < 0 || XInput::XInputInitStatus < 0 || RawInput::RawInputInitStatus < 0) {
CxbxKrnlCleanup("Failed to initialize input subsystem! Consult debug log for more information");
}
@ -110,6 +113,8 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
UpdateDevices(PORT_3, false);
UpdateDevices(PORT_4, false);
}
RawInput::IgnoreHotplug = false;
}
void InputDeviceManager::Shutdown()
@ -128,6 +133,7 @@ void InputDeviceManager::Shutdown()
m_Devices.clear();
XInput::DeInit();
RawInput::DeInit();
Sdl::DeInit(m_PollingThread);
}
@ -723,3 +729,40 @@ void InputDeviceManager::UpdateOpt(bool is_gui)
DInput::mo_wheel_range_neg = -(g_Settings->m_input_general.MoWheelRange);
}
}
void InputDeviceManager::HotplugHandler(bool is_sdl)
{
// RawInput will start to send WM_INPUT_DEVICE_CHANGE as soon as RegisterRawInputDevices succeeds, but at that point, the input manager
// is still not completely initialized, so we ignore hotplug events during initialization
if (m_bPendingShutdown || RawInput::IgnoreHotplug) {
return;
}
// NOTE1: sdl devices are monitored by sdl with the SDL_JOYDEVICEADDED and SDL_JOYDEVICEREMOVED messages,
// and xinput devices are monitored by rawinput with the WM_INPUT_DEVICE_CHANGE message
// NOTE2: sdl devices are already added/removed to/from m_Devices with the above events, so don't need to update m_Devices here again
if (!is_sdl) {
std::unique_lock<std::mutex> lck(m_Mtx);
auto it = std::remove_if(m_Devices.begin(), m_Devices.end(), [](const auto &Device) {
if (StrStartsWith(Device->GetAPI(), "XInput")) {
return true;
}
return false;
});
if (it != m_Devices.end()) {
m_Devices.erase(it, m_Devices.end());
}
lck.unlock();
XInput::PopulateDevices();
}
for (int port = PORT_1; port <= PORT_4; ++port) {
int type;
g_EmuShared->GetInputDevTypeSettings(&type, port);
if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
BindHostDevice(port, port, type);
}
}
}

View File

@ -148,6 +148,8 @@ public:
void UpdateDevices(int port, bool ack);
// update input options
void UpdateOpt(bool is_gui);
// device hotplug event handler
void HotplugHandler(bool is_sdl);
private:

View File

@ -0,0 +1,84 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2021 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#define LOG_PREFIX CXBXR_MODULE::RINP
#include "RawDevice.h"
#include "Logging.h"
#include <array>
// NOTE: we don't implement host input devices controlled by rawinput, we only use the api to detect device changes for xinput
namespace RawInput
{
int RawInputInitStatus = RAWINPUT_NOT_INIT;
bool IgnoreHotplug = true;
void Init(std::mutex &Mtx, bool is_gui, HWND hwnd)
{
std::unique_lock<std::mutex> lck(Mtx);
if (is_gui) {
// We don't need to monitor xinput device changes from the gui, because there we have the refresh button to detect changes
RawInputInitStatus = RAWINPUT_INIT_SUCCESS;
return;
}
std::array<RAWINPUTDEVICE, 3> devices;
// joystick devices
devices[0].usUsagePage = 0x01;
devices[0].usUsage = 0x04;
devices[0].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
devices[0].hwndTarget = hwnd;
// gamepad devices
devices[1].usUsagePage = 0x01;
devices[1].usUsage = 0x05;
devices[1].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
devices[1].hwndTarget = hwnd;
// multi axis controller devices
devices[2].usUsagePage = 0x01;
devices[2].usUsage = 0x08;
devices[2].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
devices[2].hwndTarget = hwnd;
if (!RegisterRawInputDevices(devices.data(), static_cast<UINT>(devices.size()),
static_cast<UINT>(sizeof(decltype(devices)::value_type))))
{
EmuLog(LOG_LEVEL::ERROR2, "RegisterRawInputDevices failed: %i", GetLastError());
RawInputInitStatus = RAWINPUT_INIT_ERROR;
return;
}
RawInputInitStatus = RAWINPUT_INIT_SUCCESS;
}
void DeInit()
{
RawInputInitStatus = RAWINPUT_NOT_INIT;
IgnoreHotplug = true;
}
}

View File

@ -0,0 +1,51 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2021 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#pragma once
#include "Windows.h"
#include <mutex>
namespace RawInput
{
typedef enum _RAWINPUT_INIT_STATUS : int
{
RAWINPUT_NOT_INIT = -2,
RAWINPUT_INIT_ERROR,
RAWINPUT_INIT_SUCCESS,
}
RAWINPUT_INIT_STATUS;
extern int RawInputInitStatus;
extern bool IgnoreHotplug;
// initialize RawInput
void Init(std::mutex &Mtx, bool is_gui, HWND hwnd);
// shutdown RawInput
void DeInit();
}

View File

@ -104,6 +104,7 @@ namespace Sdl
{
if (Event.type == SDL_JOYDEVICEADDED) {
OpenSdlDevice(Event.jdevice.which);
g_InputDeviceManager.HotplugHandler(true);
}
else if (Event.type == SDL_JOYDEVICEREMOVED) {
CloseSdlDevice(Event.jdevice.which);

View File

@ -58,6 +58,7 @@
#include "core\kernel\common\strings.hpp" // For uem_str
#include "common\input\SdlJoystick.h"
#include "common\input\DInputKeyboardMouse.h"
#include "common\input\InputManager.h"
#include "common/util/strConverter.hpp" // for utf8_to_utf16
#include "VertexShaderSource.h"
@ -1923,6 +1924,17 @@ static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPar
}
break;
case WM_INPUT_DEVICE_CHANGE:
{
// sent by rawinput when it detects changes for the registered device types
if (wParam == GIDC_ARRIVAL) {
g_InputDeviceManager.HotplugHandler(false);
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
break;
case WM_SYSKEYDOWN:
{
if(wParam == VK_RETURN)

View File

@ -79,6 +79,7 @@ static int g_DlgIndexes[] = {
IDC_LOG_DSSTREAM,
IDC_LOG_DS3DCALC,
IDC_LOG_XMO,
IDC_LOG_RINP,
// Kernel
IDC_LOG_KRNL,
IDC_LOG_LOG,

View File

@ -496,6 +496,7 @@ BEGIN
PUSHBUTTON "Cancel",IDC_LOG_CANCEL,161,333,40,14,BS_FLAT
PUSHBUTTON "Accept",IDC_LOG_ACCEPT,206,333,40,14,BS_FLAT
CONTROL "VSHCACHE",IDC_LOG_VSHCACHE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,68,140,53,10
CONTROL "RINP",IDC_LOG_RINP,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,202,195,32,10
END
IDD_ABOUT DIALOGEX 0, 0, 310, 177

View File

@ -92,6 +92,7 @@
#define IDC_LOG_DS3DCALC 960
#define IDC_LOG_XMO 961
#define IDC_LOG_VSHCACHE 962
#define IDC_LOG_RINP 963
#define IDC_SET_MOTOR 999
#define IDC_SET_X 1000
#define IDC_SET_Y 1001