diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index 50858cd3de24..69f0e5b5ec4f 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -23,6 +23,7 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "InputCommon/ControllerInterface/InputBackend.h" #include "jni/AndroidCommon/AndroidCommon.h" #include "jni/AndroidCommon/IDCache.h" #include "jni/Input/CoreDevice.h" @@ -444,6 +445,23 @@ std::shared_ptr FindDevice(jint device_id) namespace ciface::Android { +class InputBackend final : public ciface::InputBackend +{ +public: + using ciface::InputBackend::InputBackend; + ~InputBackend(); + void PopulateDevices() override; + +private: + void AddDevice(JNIEnv* env, int device_id); + void AddSensorDevice(JNIEnv* env); +}; + +std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface) +{ + return std::make_unique(controller_interface); +} + class AndroidInput : public Core::Device::Input { public: @@ -885,7 +903,7 @@ void Init() s_controller_interface_register_input_device_listener); } -void Shutdown() +InputBackend::~InputBackend() { JNIEnv* env = IDCache::GetEnvForThread(); @@ -903,7 +921,7 @@ void Shutdown() env->DeleteGlobalRef(s_keycodes_array); } -static void AddDevice(JNIEnv* env, int device_id) +void InputBackend::AddDevice(JNIEnv* env, int device_id) { jobject input_device = env->CallStaticObjectMethod(s_input_device_class, s_input_device_get_device, device_id); @@ -921,7 +939,7 @@ static void AddDevice(JNIEnv* env, int device_id) if (device->Inputs().empty() && device->Outputs().empty()) return; - g_controller_interface.AddDevice(device); + GetControllerInterface().AddDevice(device); Core::DeviceQualifier qualifier; qualifier.FromDevice(device.get()); @@ -936,7 +954,7 @@ static void AddDevice(JNIEnv* env, int device_id) env->DeleteLocalRef(j_qualifier); } -static void AddSensorDevice(JNIEnv* env) +void InputBackend::AddSensorDevice(JNIEnv* env) { // Device sensors (accelerometer, etc.) aren't associated with any Android InputDevice. // Create an otherwise empty Dolphin input device so that they have somewhere to live. @@ -946,7 +964,7 @@ static void AddSensorDevice(JNIEnv* env) if (device->Inputs().empty() && device->Outputs().empty()) return; - g_controller_interface.AddDevice(device); + GetControllerInterface().AddDevice(device); Core::DeviceQualifier qualifier; qualifier.FromDevice(device.get()); @@ -959,7 +977,7 @@ static void AddSensorDevice(JNIEnv* env) env->DeleteLocalRef(j_qualifier); } -void PopulateDevices() +void InputBackend::PopulateDevices() { INFO_LOG_FMT(CONTROLLERINTERFACE, "Android populating devices"); diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.h b/Source/Core/InputCommon/ControllerInterface/Android/Android.h index 8b8478d7acd2..e63ac4f2da1d 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.h +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.h @@ -3,11 +3,10 @@ #pragma once +#include "InputCommon/ControllerInterface/InputBackend.h" + namespace ciface::Android { -void Init(); -void Shutdown(); - -void PopulateDevices(); +std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface); } // namespace ciface::Android diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index b7a9c7f307da..ab266292bcd5 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -59,25 +59,25 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi) m_populating_devices_counter = 1; #ifdef CIFACE_USE_WIN32 - ciface::Win32::Init(wsi.render_window); + m_input_backends.emplace_back(ciface::Win32::CreateInputBackend(this)); #endif #ifdef CIFACE_USE_XLIB -// nothing needed + m_input_backends.emplace_back(ciface::XInput2::CreateInputBackend(this)); #endif #ifdef CIFACE_USE_OSX -// nothing needed for Quartz + m_input_backends.emplace_back(ciface::Quartz::CreateInputBackend(this)); #endif #ifdef CIFACE_USE_SDL m_input_backends.emplace_back(ciface::SDL::CreateInputBackend(this)); #endif #ifdef CIFACE_USE_ANDROID - ciface::Android::Init(); + m_input_backends.emplace_back(ciface::Android::CreateInputBackend(this)); #endif #ifdef CIFACE_USE_EVDEV m_input_backends.emplace_back(ciface::evdev::CreateInputBackend(this)); #endif #ifdef CIFACE_USE_PIPES -// nothing needed + m_input_backends.emplace_back(ciface::Pipes::CreateInputBackend(this)); #endif #ifdef CIFACE_USE_DUALSHOCKUDPCLIENT m_input_backends.emplace_back(ciface::DualShockUDPClient::CreateInputBackend(this)); @@ -128,22 +128,20 @@ void ControllerInterface::RefreshDevices(RefreshReason reason) // or removing them as we are populating them (causing missing or duplicate devices). std::lock_guard lk_population(m_devices_population_mutex); -#if defined(CIFACE_USE_WIN32) && !defined(CIFACE_USE_XLIB) && !defined(CIFACE_USE_OSX) // If only the window changed, avoid removing and re-adding all devices. // Instead only refresh devices that require the window handle. if (reason == RefreshReason::WindowChangeOnly) { m_populating_devices_counter.fetch_add(1); - // No need to do anything else in this case. - // Only (Win32) DInput needs the window handle to be updated. - ciface::Win32::ChangeWindow(m_wsi.render_window); + for (auto& backend : m_input_backends) + backend->HandleWindowChange(); if (m_populating_devices_counter.fetch_sub(1) == 1) InvokeDevicesChangedCallbacks(); + return; } -#endif m_populating_devices_counter.fetch_add(1); @@ -159,26 +157,6 @@ void ControllerInterface::RefreshDevices(RefreshReason reason) // do it async, to not risk the emulated controllers default config loading not finding a default // device. -#ifdef CIFACE_USE_WIN32 - ciface::Win32::PopulateDevices(m_wsi.render_window); -#endif -#ifdef CIFACE_USE_XLIB - if (m_wsi.type == WindowSystemType::X11) - ciface::XInput2::PopulateDevices(m_wsi.render_window); -#endif -#ifdef CIFACE_USE_OSX - if (m_wsi.type == WindowSystemType::MacOS) - { - ciface::Quartz::PopulateDevices(m_wsi.render_window); - } -#endif -#ifdef CIFACE_USE_ANDROID - ciface::Android::PopulateDevices(); -#endif -#ifdef CIFACE_USE_PIPES - ciface::Pipes::PopulateDevices(); -#endif - for (auto& backend : m_input_backends) backend->PopulateDevices(); @@ -217,19 +195,6 @@ void ControllerInterface::Shutdown() // Update control references so shared_ptrs are freed up BEFORE we shutdown the backends. ClearDevices(); -#ifdef CIFACE_USE_WIN32 - ciface::Win32::DeInit(); -#endif -#ifdef CIFACE_USE_XLIB -// nothing needed -#endif -#ifdef CIFACE_USE_OSX - ciface::Quartz::DeInit(); -#endif -#ifdef CIFACE_USE_ANDROID - ciface::Android::Shutdown(); -#endif - // Empty the container of input backends to deconstruct and deinitialize them. m_input_backends.clear(); @@ -423,6 +388,11 @@ ciface::InputChannel ControllerInterface::GetCurrentInputChannel() return tls_input_channel; } +WindowSystemInfo ControllerInterface::GetWindowSystemInfo() const +{ + return m_wsi; +} + void ControllerInterface::SetAspectRatioAdjustment(float value) { m_aspect_ratio_adjustment = value; diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h index 837e5135d5b6..d3998a48390e 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -122,6 +122,8 @@ class ControllerInterface : public ciface::Core::DeviceContainer static void SetCurrentInputChannel(ciface::InputChannel); static ciface::InputChannel GetCurrentInputChannel(); + WindowSystemInfo GetWindowSystemInfo() const; + private: void ClearDevices(); diff --git a/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp b/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp index 422d7e911c6e..7b52f55caa4d 100644 --- a/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp +++ b/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp @@ -16,6 +16,10 @@ void InputBackend::UpdateInput(std::vector>& { } +void InputBackend::HandleWindowChange() +{ +} + ControllerInterface& InputBackend::GetControllerInterface() { return m_controller_interface; diff --git a/Source/Core/InputCommon/ControllerInterface/InputBackend.h b/Source/Core/InputCommon/ControllerInterface/InputBackend.h index 80ced7e19417..7d7560385120 100644 --- a/Source/Core/InputCommon/ControllerInterface/InputBackend.h +++ b/Source/Core/InputCommon/ControllerInterface/InputBackend.h @@ -28,6 +28,8 @@ class InputBackend // just add them to the removal list if necessary. virtual void UpdateInput(std::vector>& devices_to_remove); + virtual void HandleWindowChange(); + ControllerInterface& GetControllerInterface(); private: diff --git a/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp index af7ad7dc6fdf..1a2052089eac 100644 --- a/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp @@ -39,7 +39,19 @@ static double StringToDouble(const std::string& text) return result; } -void PopulateDevices() +class InputBackend final : public ciface::InputBackend +{ +public: + using ciface::InputBackend::InputBackend; + void PopulateDevices() override; +}; + +std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface) +{ + return std::make_unique(controller_interface); +} + +void InputBackend::PopulateDevices() { // Search the Pipes directory for files that we can open in read-only, // non-blocking mode. The device name is the virtual name of the file. diff --git a/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h index 53fefd0cb50e..857d5e81285a 100644 --- a/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h +++ b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h @@ -21,7 +21,7 @@ namespace ciface::Pipes // SET {L, R} [0, 1] // SET {MAIN, C} [0, 1] [0, 1] -void PopulateDevices(); +std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface); class PipeDevice : public Core::Device { diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/Quartz.h b/Source/Core/InputCommon/ControllerInterface/Quartz/Quartz.h index 8818425344f4..b2c6906e42e9 100644 --- a/Source/Core/InputCommon/ControllerInterface/Quartz/Quartz.h +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/Quartz.h @@ -3,8 +3,13 @@ #pragma once +#include + +#include "InputCommon/ControllerInterface/InputBackend.h" + namespace ciface::Quartz { -void PopulateDevices(void* window); -void DeInit(); +std::string GetSourceName(); + +std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface); } // namespace ciface::Quartz diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/Quartz.mm b/Source/Core/InputCommon/ControllerInterface/Quartz/Quartz.mm index 22dc4074d9aa..5d525958358d 100644 --- a/Source/Core/InputCommon/ControllerInterface/Quartz/Quartz.mm +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/Quartz.mm @@ -7,15 +7,40 @@ namespace ciface::Quartz { -void PopulateDevices(void* window) +std::string GetSourceName() { - if (!window) - return; + return "Quartz"; +} + +class InputBackend final : public ciface::InputBackend +{ +public: + using ciface::InputBackend::InputBackend; + void PopulateDevices() override; + void HandleWindowChange() override; +}; + +std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface) +{ + return std::make_unique(controller_interface); +} + +void InputBackend::HandleWindowChange() +{ + const std::string source_name = GetSourceName(); + GetControllerInterface().RemoveDevice( + [&](const auto* dev) { return dev->GetSource() == source_name; }, true); - g_controller_interface.AddDevice(std::make_shared(window)); + PopulateDevices(); } -void DeInit() +void InputBackend::PopulateDevices() { + const WindowSystemInfo wsi = GetControllerInterface().GetWindowSystemInfo(); + if (wsi.type != WindowSystemType::MacOS) + return; + + GetControllerInterface().AddDevice(std::make_shared(wsi.render_window)); } + } // namespace ciface::Quartz diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm index e41c370edf2f..55f212900d7d 100644 --- a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm @@ -12,6 +12,7 @@ #include "Core/Host.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "InputCommon/ControllerInterface/Quartz/Quartz.h" /// Helper class to get window position data from threads other than the main thread @interface DolWindowPositionObserver : NSObject @@ -279,7 +280,7 @@ - (void)dealloc std::string KeyboardAndMouse::GetSource() const { - return "Quartz"; + return Quartz::GetSourceName(); } ControlState KeyboardAndMouse::Cursor::GetState() const diff --git a/Source/Core/InputCommon/ControllerInterface/Win32/Win32.cpp b/Source/Core/InputCommon/ControllerInterface/Win32/Win32.cpp index 21e984796468..36b847aa91f9 100644 --- a/Source/Core/InputCommon/ControllerInterface/Win32/Win32.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Win32/Win32.cpp @@ -20,13 +20,25 @@ #pragma comment(lib, "OneCoreUAP.Lib") -// Dolphin's render window -static HWND s_hwnd; static std::mutex s_populate_mutex; // TODO is this really needed? static Common::Flag s_first_populate_devices_asked; static HCMNOTIFICATION s_notify_handle; +namespace ciface::Win32 +{ +class InputBackend final : public ciface::InputBackend +{ +public: + InputBackend(ControllerInterface* controller_interface); + ~InputBackend(); + + void PopulateDevices() override; + void HandleWindowChange() override; + HWND GetHWND(); +}; +} // namespace ciface::Win32 + _Pre_satisfies_(EventDataSize >= sizeof(CM_NOTIFY_EVENT_DATA)) static DWORD CALLBACK OnDevicesChanged(_In_ HCMNOTIFICATION hNotify, _In_opt_ PVOID Context, _In_ CM_NOTIFY_ACTION Action, @@ -43,8 +55,9 @@ _Pre_satisfies_(EventDataSize >= sizeof(CM_NOTIFY_EVENT_DATA)) static DWORD CALL std::lock_guard lk_population(s_populate_mutex); // TODO: we could easily use the message passed alongside this event, which tells // whether a device was added or removed, to avoid removing old, still connected, devices - g_controller_interface.PlatformPopulateDevices([] { - ciface::DInput::PopulateDevices(s_hwnd); + g_controller_interface.PlatformPopulateDevices([&] { + ciface::DInput::PopulateDevices( + static_cast(Context)->GetHWND()); ciface::XInput::PopulateDevices(); }); } @@ -52,10 +65,21 @@ _Pre_satisfies_(EventDataSize >= sizeof(CM_NOTIFY_EVENT_DATA)) static DWORD CALL return ERROR_SUCCESS; } -void ciface::Win32::Init(void* hwnd) +namespace ciface::Win32 +{ +std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface) { - s_hwnd = static_cast(hwnd); + return std::make_unique(controller_interface); +} +HWND InputBackend::GetHWND() +{ + return static_cast(GetControllerInterface().GetWindowSystemInfo().render_window); +} + +InputBackend::InputBackend(ControllerInterface* controller_interface) + : ciface::InputBackend(controller_interface) +{ XInput::Init(); WGInput::Init(); @@ -63,35 +87,32 @@ void ciface::Win32::Init(void* hwnd) .FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE, .u{.DeviceInterface{.ClassGuid = GUID_DEVINTERFACE_HID}}}; const CONFIGRET cfg_rv = - CM_Register_Notification(¬ify_filter, nullptr, OnDevicesChanged, &s_notify_handle); + CM_Register_Notification(¬ify_filter, this, OnDevicesChanged, &s_notify_handle); if (cfg_rv != CR_SUCCESS) { ERROR_LOG_FMT(CONTROLLERINTERFACE, "CM_Register_Notification failed: {:x}", cfg_rv); } } -void ciface::Win32::PopulateDevices(void* hwnd) +void InputBackend::PopulateDevices() { - s_hwnd = static_cast(hwnd); std::lock_guard lk_population(s_populate_mutex); s_first_populate_devices_asked.Set(); - ciface::DInput::PopulateDevices(s_hwnd); + ciface::DInput::PopulateDevices(GetHWND()); ciface::XInput::PopulateDevices(); ciface::WGInput::PopulateDevices(); } -void ciface::Win32::ChangeWindow(void* hwnd) +void InputBackend::HandleWindowChange() { - s_hwnd = static_cast(hwnd); std::lock_guard lk_population(s_populate_mutex); - ciface::DInput::ChangeWindow(s_hwnd); + ciface::DInput::ChangeWindow(GetHWND()); } -void ciface::Win32::DeInit() +InputBackend::~InputBackend() { s_first_populate_devices_asked.Clear(); DInput::DeInit(); - s_hwnd = nullptr; if (s_notify_handle) { @@ -106,3 +127,5 @@ void ciface::Win32::DeInit() XInput::DeInit(); WGInput::DeInit(); } + +} // namespace ciface::Win32 diff --git a/Source/Core/InputCommon/ControllerInterface/Win32/Win32.h b/Source/Core/InputCommon/ControllerInterface/Win32/Win32.h index c842b5c6b5c6..17e46c54b900 100644 --- a/Source/Core/InputCommon/ControllerInterface/Win32/Win32.h +++ b/Source/Core/InputCommon/ControllerInterface/Win32/Win32.h @@ -3,10 +3,9 @@ #pragma once +#include "InputCommon/ControllerInterface/InputBackend.h" + namespace ciface::Win32 { -void Init(void* hwnd); -void PopulateDevices(void* hwnd); -void ChangeWindow(void* hwnd); -void DeInit(); +std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface); } // namespace ciface::Win32 diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp index f041dd16a144..e4b717e3af33 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp @@ -66,9 +66,38 @@ constexpr int XINPUT_MAJOR = 2, XINPUT_MINOR = 1; namespace ciface::XInput2 { +constexpr std::string_view SOURCE_NAME = "XInput2"; + +class InputBackend final : public ciface::InputBackend +{ +public: + using ciface::InputBackend::InputBackend; + void PopulateDevices() override; + void HandleWindowChange() override; +}; + +std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface) +{ + return std::make_unique(controller_interface); +} + +void InputBackend::HandleWindowChange() +{ + GetControllerInterface().RemoveDevice( + [](const auto* dev) { return dev->GetSource() == SOURCE_NAME; }, true); + + PopulateDevices(); +} + // This function will add zero or more KeyboardMouse objects to devices. -void PopulateDevices(void* const hwnd) +void InputBackend::PopulateDevices() { + const WindowSystemInfo wsi = GetControllerInterface().GetWindowSystemInfo(); + if (wsi.type != WindowSystemType::X11) + return; + + const auto hwnd = wsi.render_window; + Display* dpy = XOpenDisplay(nullptr); // xi_opcode is important; it will be used to identify XInput events by @@ -119,7 +148,7 @@ void PopulateDevices(void* const hwnd) } // Since current_master is a master pointer, its attachment must // be a master keyboard. - g_controller_interface.AddDevice( + GetControllerInterface().AddDevice( std::make_shared((Window)hwnd, xi_opcode, current_master->deviceid, current_master->attachment, scroll_increment)); } @@ -382,7 +411,7 @@ std::string KeyboardMouse::GetName() const std::string KeyboardMouse::GetSource() const { - return "XInput2"; + return std::string(SOURCE_NAME); } KeyboardMouse::Key::Key(Display* const display, KeyCode keycode, const char* keyboard) diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h index a8960c1d23fc..427c81b18ad2 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h @@ -16,10 +16,11 @@ extern "C" { #include "Common/CommonTypes.h" #include "Common/Matrix.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "InputCommon/ControllerInterface/InputBackend.h" namespace ciface::XInput2 { -void PopulateDevices(void* const hwnd); +std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface); class KeyboardMouse : public Core::Device {