diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b8de809e..d764aa57a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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" diff --git a/src/common/Logging.cpp b/src/common/Logging.cpp index 8e4392534..004032eeb 100644 --- a/src/common/Logging.cpp +++ b/src/common/Logging.cpp @@ -80,6 +80,7 @@ const char* g_EnumModules2String[to_underlying(CXBXR_MODULE::MAX)] = { "DSSTREAM", "DS3DCALC", "XMO ", + "RINP ", "KRNL ", "LOG ", "XBOX ", diff --git a/src/common/Logging.h b/src/common/Logging.h index 4bec93945..b5871fd99 100644 --- a/src/common/Logging.h +++ b/src/common/Logging.h @@ -88,6 +88,7 @@ typedef enum class _CXBXR_MODULE: unsigned int { DSSTREAM, DS3DCALC, XMO, + RINP, // kernel KRNL, LOG, diff --git a/src/common/input/InputManager.cpp b/src/common/input/InputManager.cpp index 1be97c882..7bab6f1b5 100644 --- a/src/common/input/InputManager.cpp +++ b/src/common/input/InputManager.cpp @@ -39,6 +39,7 @@ #include // 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 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); + } + } +} diff --git a/src/common/input/InputManager.h b/src/common/input/InputManager.h index 550a40c6b..8edf43dd1 100644 --- a/src/common/input/InputManager.h +++ b/src/common/input/InputManager.h @@ -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: diff --git a/src/common/input/RawDevice.cpp b/src/common/input/RawDevice.cpp new file mode 100644 index 000000000..a4c6c4343 --- /dev/null +++ b/src/common/input/RawDevice.cpp @@ -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 + +// 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 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 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(devices.size()), + static_cast(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; + } +} diff --git a/src/common/input/RawDevice.h b/src/common/input/RawDevice.h new file mode 100644 index 000000000..7be2bc3f5 --- /dev/null +++ b/src/common/input/RawDevice.h @@ -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 + + +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(); +} diff --git a/src/common/input/SdlJoystick.cpp b/src/common/input/SdlJoystick.cpp index 7cdbf9179..530686f5f 100644 --- a/src/common/input/SdlJoystick.cpp +++ b/src/common/input/SdlJoystick.cpp @@ -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); diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index dc7c7b4ed..c704d7342 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -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) diff --git a/src/gui/DlgLoggingConfig.cpp b/src/gui/DlgLoggingConfig.cpp index 5ca384a8f..b9826aac7 100644 --- a/src/gui/DlgLoggingConfig.cpp +++ b/src/gui/DlgLoggingConfig.cpp @@ -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, diff --git a/src/gui/resource/Cxbx.rc b/src/gui/resource/Cxbx.rc index a7f0ddf2e..0b35c0bd4 100644 --- a/src/gui/resource/Cxbx.rc +++ b/src/gui/resource/Cxbx.rc @@ -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 diff --git a/src/gui/resource/ResCxbx.h b/src/gui/resource/ResCxbx.h index 28edb0a0b..883a5e16a 100644 --- a/src/gui/resource/ResCxbx.h +++ b/src/gui/resource/ResCxbx.h @@ -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