mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-12 11:08:56 +00:00
meh
This commit is contained in:
parent
2c393d35f0
commit
98c174edc4
520 changed files with 74815 additions and 58942 deletions
|
@ -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<ciface::Core::Device> FindDevice(jint device_id)
|
|||
|
||||
namespace ciface::Android
|
||||
{
|
||||
class InputBackend final : public ciface::InputBackend
|
||||
{
|
||||
public:
|
||||
InputBackend(ControllerInterface* controller_interface);
|
||||
~InputBackend();
|
||||
void PopulateDevices() override;
|
||||
|
||||
private:
|
||||
void AddDevice(JNIEnv* env, int device_id);
|
||||
void AddSensorDevice(JNIEnv* env);
|
||||
};
|
||||
|
||||
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface)
|
||||
{
|
||||
return std::make_unique<InputBackend>(controller_interface);
|
||||
}
|
||||
|
||||
class AndroidInput : public Core::Device::Input
|
||||
{
|
||||
public:
|
||||
|
@ -779,7 +797,8 @@ static jintArray CreateKeyCodesArray(JNIEnv* env)
|
|||
return keycodes_array;
|
||||
}
|
||||
|
||||
void Init()
|
||||
InputBackend::InputBackend(ControllerInterface* controller_interface)
|
||||
: ciface::InputBackend(controller_interface)
|
||||
{
|
||||
JNIEnv* env = IDCache::GetEnvForThread();
|
||||
|
||||
|
@ -885,7 +904,7 @@ void Init()
|
|||
s_controller_interface_register_input_device_listener);
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
InputBackend::~InputBackend()
|
||||
{
|
||||
JNIEnv* env = IDCache::GetEnvForThread();
|
||||
|
||||
|
@ -903,7 +922,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 +940,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 +955,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 +965,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 +978,7 @@ static void AddSensorDevice(JNIEnv* env)
|
|||
env->DeleteLocalRef(j_qualifier);
|
||||
}
|
||||
|
||||
void PopulateDevices()
|
||||
void InputBackend::PopulateDevices()
|
||||
{
|
||||
INFO_LOG_FMT(CONTROLLERINTERFACE, "Android populating devices");
|
||||
|
||||
|
|
|
@ -3,11 +3,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "InputCommon/ControllerInterface/InputBackend.h"
|
||||
|
||||
namespace ciface::Android
|
||||
{
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
void PopulateDevices();
|
||||
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface);
|
||||
|
||||
} // namespace ciface::Android
|
||||
|
|
|
@ -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_ptr<Device>s 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;
|
||||
|
|
|
@ -122,6 +122,8 @@ public:
|
|||
static void SetCurrentInputChannel(ciface::InputChannel);
|
||||
static ciface::InputChannel GetCurrentInputChannel();
|
||||
|
||||
WindowSystemInfo GetWindowSystemInfo() const;
|
||||
|
||||
private:
|
||||
void ClearDevices();
|
||||
|
||||
|
|
|
@ -125,6 +125,11 @@ bool Device::Control::IsMatchingName(std::string_view name) const
|
|||
return GetName() == name;
|
||||
}
|
||||
|
||||
bool Device::Control::IsHidden() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ControlState Device::FullAnalogSurface::GetState() const
|
||||
{
|
||||
return (1 + std::max(0.0, m_high.GetState()) - std::max(0.0, m_low.GetState())) / 2;
|
||||
|
@ -141,6 +146,11 @@ bool Device::FullAnalogSurface::IsDetectable() const
|
|||
return m_low.IsDetectable() && m_high.IsDetectable();
|
||||
}
|
||||
|
||||
bool Device::FullAnalogSurface::IsHidden() const
|
||||
{
|
||||
return m_low.IsHidden() && m_high.IsHidden();
|
||||
}
|
||||
|
||||
bool Device::FullAnalogSurface::IsMatchingName(std::string_view name) const
|
||||
{
|
||||
if (Control::IsMatchingName(name))
|
||||
|
|
|
@ -64,6 +64,10 @@ public:
|
|||
// May be overridden to allow multiple valid names.
|
||||
// Useful for backwards-compatible configurations when names change.
|
||||
virtual bool IsMatchingName(std::string_view name) const;
|
||||
|
||||
// May be overridden to hide in UI.
|
||||
// Useful for backwards-compatible configurations when names change.
|
||||
virtual bool IsHidden() const;
|
||||
};
|
||||
|
||||
//
|
||||
|
@ -164,6 +168,7 @@ protected:
|
|||
ControlState GetState() const override;
|
||||
std::string GetName() const override;
|
||||
bool IsDetectable() const override;
|
||||
bool IsHidden() const override;
|
||||
bool IsMatchingName(std::string_view name) const override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -7,16 +7,13 @@
|
|||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include "Common/Event.h"
|
||||
#include "Common/Flag.h"
|
||||
#include "InputCommon/ControllerInterface/CoreDevice.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#include "InputCommon/ControllerInterface/CoreDevice.h"
|
||||
#include "InputCommon/ControllerInterface/DInput/DInput8.h"
|
||||
#elif __APPLE__
|
||||
#include "InputCommon/ControllerInterface/ForceFeedback/OSX/DirectInputAdapter.h"
|
||||
#endif
|
||||
|
||||
namespace ciface::ForceFeedback
|
||||
{
|
||||
|
|
|
@ -1,196 +0,0 @@
|
|||
// Copyright 2014 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
/*
|
||||
* The OS X Force Feedback API is very similar to the DirectInput API,
|
||||
* but it is no longer object-oriented and all prefixes have been changed.
|
||||
*
|
||||
* Our implementation uses the Windows API names so we need to adapt
|
||||
* for these differences on OS X.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
typedef LONG* LPLONG; // Missing type for ForceFeedback.h
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <ForceFeedback/ForceFeedback.h>
|
||||
#include "Common/CommonTypes.h" // for LONG
|
||||
#include "DirectInputConstants.h" // Not stricty necessary
|
||||
|
||||
namespace ciface::ForceFeedback
|
||||
{
|
||||
// Prototypes
|
||||
class IUnknownImpl;
|
||||
class FFEffectAdapter;
|
||||
class FFDeviceAdapter;
|
||||
|
||||
// Structs
|
||||
typedef FFCAPABILITIES DICAPABILITIES;
|
||||
typedef FFCONDITION DICONDITION;
|
||||
typedef FFCONSTANTFORCE DICONSTANTFORCE;
|
||||
typedef FFCUSTOMFORCE DICUSTOMFORCE;
|
||||
typedef FFEFFECT DIEFFECT;
|
||||
typedef FFEFFESCAPE DIEFFESCAPE;
|
||||
typedef FFENVELOPE DIENVELOPE;
|
||||
typedef FFPERIODIC DIPERIODIC;
|
||||
typedef FFRAMPFORCE DIRAMPFORCE;
|
||||
|
||||
// Other types
|
||||
typedef CFUUIDRef GUID;
|
||||
typedef FFDeviceAdapter* FFDeviceAdapterReference;
|
||||
typedef FFEffectAdapter* FFEffectAdapterReference;
|
||||
typedef FFDeviceAdapterReference LPDIRECTINPUTDEVICE8;
|
||||
typedef FFEffectAdapterReference LPDIRECTINPUTEFFECT;
|
||||
|
||||
// Property structures
|
||||
#define DIPH_DEVICE 0
|
||||
|
||||
typedef struct DIPROPHEADER
|
||||
{
|
||||
DWORD dwSize;
|
||||
DWORD dwHeaderSize;
|
||||
DWORD dwObj;
|
||||
DWORD dwHow;
|
||||
} DIPROPHEADER, *LPDIPROPHEADER;
|
||||
|
||||
typedef struct DIPROPDWORD
|
||||
{
|
||||
DIPROPHEADER diph;
|
||||
DWORD dwData;
|
||||
} DIPROPDWORD, *LPDIPROPDWORD;
|
||||
|
||||
class IUnknownImpl : public IUnknown
|
||||
{
|
||||
private:
|
||||
std::atomic<ULONG> m_cRef;
|
||||
|
||||
public:
|
||||
IUnknownImpl() : m_cRef(1) {}
|
||||
virtual ~IUnknownImpl() {}
|
||||
HRESULT QueryInterface(REFIID iid, LPVOID* ppv)
|
||||
{
|
||||
*ppv = nullptr;
|
||||
|
||||
if (CFEqual(&iid, IUnknownUUID))
|
||||
*ppv = this;
|
||||
if (nullptr == *ppv)
|
||||
return E_NOINTERFACE;
|
||||
|
||||
((IUnknown*)*ppv)->AddRef();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ULONG AddRef() { return ++m_cRef; }
|
||||
ULONG Release()
|
||||
{
|
||||
if (--m_cRef == 0)
|
||||
delete this;
|
||||
|
||||
return m_cRef;
|
||||
}
|
||||
};
|
||||
|
||||
class FFEffectAdapter : public IUnknownImpl
|
||||
{
|
||||
private:
|
||||
// Only used for destruction
|
||||
FFDeviceObjectReference m_device;
|
||||
|
||||
public:
|
||||
FFEffectObjectReference m_effect;
|
||||
|
||||
FFEffectAdapter(FFDeviceObjectReference device, FFEffectObjectReference effect)
|
||||
: m_device(device), m_effect(effect)
|
||||
{
|
||||
}
|
||||
~FFEffectAdapter() { FFDeviceReleaseEffect(m_device, m_effect); }
|
||||
HRESULT Download() { return FFEffectDownload(m_effect); }
|
||||
HRESULT Escape(FFEFFESCAPE* pFFEffectEscape) { return FFEffectEscape(m_effect, pFFEffectEscape); }
|
||||
HRESULT GetEffectStatus(FFEffectStatusFlag* pFlags)
|
||||
{
|
||||
return FFEffectGetEffectStatus(m_effect, pFlags);
|
||||
}
|
||||
|
||||
HRESULT GetParameters(FFEFFECT* pFFEffect, FFEffectParameterFlag flags)
|
||||
{
|
||||
return FFEffectGetParameters(m_effect, pFFEffect, flags);
|
||||
}
|
||||
|
||||
HRESULT SetParameters(FFEFFECT* pFFEffect, FFEffectParameterFlag flags)
|
||||
{
|
||||
return FFEffectSetParameters(m_effect, pFFEffect, flags);
|
||||
}
|
||||
|
||||
HRESULT Start(UInt32 iterations, FFEffectStartFlag flags)
|
||||
{
|
||||
return FFEffectStart(m_effect, iterations, flags);
|
||||
}
|
||||
|
||||
HRESULT Stop() { return FFEffectStop(m_effect); }
|
||||
HRESULT Unload() { return FFEffectUnload(m_effect); }
|
||||
};
|
||||
|
||||
class FFDeviceAdapter : public IUnknownImpl
|
||||
{
|
||||
public:
|
||||
FFDeviceObjectReference m_device;
|
||||
|
||||
FFDeviceAdapter(FFDeviceObjectReference device) : m_device(device) {}
|
||||
~FFDeviceAdapter() { FFReleaseDevice(m_device); }
|
||||
static HRESULT Create(io_service_t hidDevice, FFDeviceAdapterReference* pDeviceReference)
|
||||
{
|
||||
FFDeviceObjectReference ref;
|
||||
|
||||
HRESULT hr = FFCreateDevice(hidDevice, &ref);
|
||||
if (SUCCEEDED(hr))
|
||||
*pDeviceReference = new FFDeviceAdapter(ref);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CreateEffect(CFUUIDRef uuidRef, FFEFFECT* pEffectDefinition,
|
||||
FFEffectAdapterReference* pEffectReference, IUnknown* punkOuter)
|
||||
{
|
||||
FFEffectObjectReference ref;
|
||||
|
||||
HRESULT hr = FFDeviceCreateEffect(m_device, uuidRef, pEffectDefinition, &ref);
|
||||
if (SUCCEEDED(hr))
|
||||
*pEffectReference = new FFEffectAdapter(m_device, ref);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT Escape(FFEFFESCAPE* pFFEffectEscape) { return FFDeviceEscape(m_device, pFFEffectEscape); }
|
||||
HRESULT GetForceFeedbackState(FFState* pFFState)
|
||||
{
|
||||
return FFDeviceGetForceFeedbackState(m_device, pFFState);
|
||||
}
|
||||
|
||||
HRESULT SendForceFeedbackCommand(FFCommandFlag flags)
|
||||
{
|
||||
return FFDeviceSendForceFeedbackCommand(m_device, flags);
|
||||
}
|
||||
|
||||
HRESULT SetCooperativeLevel(void* taskIdentifier, FFCooperativeLevelFlag flags)
|
||||
{
|
||||
return FFDeviceSetCooperativeLevel(m_device, taskIdentifier, flags);
|
||||
}
|
||||
|
||||
HRESULT SetProperty(FFProperty property, const LPDIPROPHEADER pdiph)
|
||||
{
|
||||
// There are only two properties supported
|
||||
if (property != DIPROP_FFGAIN && property != DIPROP_AUTOCENTER)
|
||||
return DIERR_UNSUPPORTED;
|
||||
|
||||
// And they are both device properties
|
||||
if (pdiph->dwHow != DIPH_DEVICE)
|
||||
return DIERR_INVALIDPARAM;
|
||||
|
||||
UInt32 value = ((const LPDIPROPDWORD)pdiph)->dwData;
|
||||
return FFDeviceSetForceFeedbackProperty(m_device, property, &value);
|
||||
}
|
||||
};
|
||||
} // namespace ciface::ForceFeedback
|
|
@ -1,146 +0,0 @@
|
|||
// Copyright 2014 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Define all constants from ForceFeedbackConstants.h with DirectInput prefixes.
|
||||
*
|
||||
* No effort was made to confirm if all definitions are actually supported by
|
||||
* DirectInput, so some of these definitions may actually only exist on Mac OS X.
|
||||
*/
|
||||
|
||||
// UUIDs
|
||||
#define GUID_ConstantForce kFFEffectType_ConstantForce_ID
|
||||
#define GUID_CustomForce kFFEffectType_CustomForce_ID
|
||||
#define GUID_Damper kFFEffectType_Damper_ID
|
||||
#define GUID_Friction kFFEffectType_Friction_ID
|
||||
#define GUID_Inertia kFFEffectType_Inertia_ID
|
||||
#define GUID_RampForce kFFEffectType_RampForce_ID
|
||||
#define GUID_SawtoothDown kFFEffectType_SawtoothDown_ID
|
||||
#define GUID_SawtoothUp kFFEffectType_SawtoothUp_ID
|
||||
#define GUID_Sine kFFEffectType_Sine_ID
|
||||
#define GUID_Spring kFFEffectType_Spring_ID
|
||||
#define GUID_Square kFFEffectType_Square_ID
|
||||
#define GUID_Triangle kFFEffectType_Triangle_ID
|
||||
|
||||
// Miscellaneous
|
||||
#define DI_DEGREES FF_DEGREES
|
||||
#define DI_DOWNLOADSKIPPED FF_DOWNLOADSKIPPED
|
||||
#define DI_EFFECTRESTARTED FF_EFFECTRESTARTED
|
||||
#define DI_FALSE FF_FALSE
|
||||
#define DI_FFNOMINALMAX FF_FFNOMINALMAX
|
||||
#define DI_INFINITE FF_INFINITE
|
||||
#define DI_OK FF_OK
|
||||
#define DI_SECONDS FF_SECONDS
|
||||
#define DI_TRUNCATED FF_TRUNCATED
|
||||
#define DI_TRUNCATEDANDRESTARTED FF_TRUNCATEDANDRESTARTED
|
||||
#define DIEFF_OBJECTOFFSETS FFEFF_OBJECTOFFSETS
|
||||
#define DIERR_DEVICEFULL FFERR_DEVICEFULL
|
||||
#define DIERR_DEVICENOTREG FFERR_DEVICENOTREG
|
||||
#define DIERR_DEVICEPAUSED FFERR_DEVICEPAUSED
|
||||
#define DIERR_DEVICERELEASED FFERR_DEVICERELEASED
|
||||
#define DIERR_EFFECTPLAYING FFERR_EFFECTPLAYING
|
||||
#define DIERR_EFFECTTYPEMISMATCH FFERR_EFFECTTYPEMISMATCH
|
||||
#define DIERR_EFFECTTYPENOTSUPPORTED FFERR_EFFECTTYPENOTSUPPORTED
|
||||
#define DIERR_GENERIC FFERR_GENERIC
|
||||
#define DIERR_HASEFFECTS FFERR_HASEFFECTS
|
||||
#define DIERR_INCOMPLETEEFFECT FFERR_INCOMPLETEEFFECT
|
||||
#define DIERR_INTERNAL FFERR_INTERNAL
|
||||
#define DIERR_INVALIDDOWNLOADID FFERR_INVALIDDOWNLOADID
|
||||
#define DIERR_INVALIDPARAM FFERR_INVALIDPARAM
|
||||
#define DIERR_MOREDATA FFERR_MOREDATA
|
||||
#define DIERR_NOINTERFACE FFERR_NOINTERFACE
|
||||
#define DIERR_NOTDOWNLOADED FFERR_NOTDOWNLOADED
|
||||
#define DIERR_NOTINITIALIZED FFERR_NOTINITIALIZED
|
||||
#define DIERR_OUTOFMEMORY FFERR_OUTOFMEMORY
|
||||
#define DIERR_UNPLUGGED FFERR_UNPLUGGED
|
||||
#define DIERR_UNSUPPORTED FFERR_UNSUPPORTED
|
||||
#define DIERR_UNSUPPORTEDAXIS FFERR_UNSUPPORTEDAXIS
|
||||
#define DIJOFS_X FFJOFS_X
|
||||
#define DIJOFS_Y FFJOFS_Y
|
||||
#define DIJOFS_Z FFJOFS_Z
|
||||
|
||||
// FFCapabilitiesEffectSubType
|
||||
#define DICAP_ST_KINESTHETIC FFCAP_ST_KINESTHETIC
|
||||
#define DICAP_ST_VIBRATION FFCAP_ST_VIBRATION
|
||||
|
||||
// FFCapabilitiesEffectType
|
||||
#define DICAP_ET_CONSTANTFORCE FFCAP_ET_CONSTANTFORCE
|
||||
#define DICAP_ET_RAMPFORCE FFCAP_ET_RAMPFORCE
|
||||
#define DICAP_ET_SQUARE FFCAP_ET_SQUARE
|
||||
#define DICAP_ET_SINE FFCAP_ET_SINE
|
||||
#define DICAP_ET_TRIANGLE FFCAP_ET_TRIANGLE
|
||||
#define DICAP_ET_SAWTOOTHUP FFCAP_ET_SAWTOOTHUP
|
||||
#define DICAP_ET_SAWTOOTHDOWN FFCAP_ET_SAWTOOTHDOWN
|
||||
#define DICAP_ET_SPRING FFCAP_ET_SPRING
|
||||
#define DICAP_ET_DAMPER FFCAP_ET_DAMPER
|
||||
#define DICAP_ET_INERTIA FFCAP_ET_INERTIA
|
||||
#define DICAP_ET_FRICTION FFCAP_ET_FRICTION
|
||||
#define DICAP_ET_CUSTOMFORCE FFCAP_ET_CUSTOMFORCE
|
||||
|
||||
// FFCommandFlag
|
||||
#define DISFFC_RESET FFSFFC_RESET
|
||||
#define DISFFC_STOPALL FFSFFC_STOPALL
|
||||
#define DISFFC_PAUSE FFSFFC_PAUSE
|
||||
#define DISFFC_CONTINUE FFSFFC_CONTINUE
|
||||
#define DISFFC_SETACTUATORSON FFSFFC_SETACTUATORSON
|
||||
#define DISFFC_SETACTUATORSOFF FFSFFC_SETACTUATORSOFF
|
||||
|
||||
// FFCooperativeLevelFlag
|
||||
#define DISCL_EXCLUSIVE FFSCL_EXCLUSIVE
|
||||
#define DISCL_NONEXCLUSIVE FFSCL_NONEXCLUSIVE
|
||||
#define DISCL_FOREGROUND FFSCL_FOREGROUND
|
||||
#define DISCL_BACKGROUND FFSCL_BACKGROUND
|
||||
|
||||
// FFCoordinateSystemFlag
|
||||
#define DIEFF_CARTESIAN FFEFF_CARTESIAN
|
||||
#define DIEFF_POLAR FFEFF_POLAR
|
||||
#define DIEFF_SPHERICAL FFEFF_SPHERICAL
|
||||
|
||||
// FFEffectParameterFlag
|
||||
#define DIEP_DURATION FFEP_DURATION
|
||||
#define DIEP_SAMPLEPERIOD FFEP_SAMPLEPERIOD
|
||||
#define DIEP_GAIN FFEP_GAIN
|
||||
#define DIEP_TRIGGERBUTTON FFEP_TRIGGERBUTTON
|
||||
#define DIEP_TRIGGERREPEATINTERVAL FFEP_TRIGGERREPEATINTERVAL
|
||||
#define DIEP_AXES FFEP_AXES
|
||||
#define DIEP_DIRECTION FFEP_DIRECTION
|
||||
#define DIEP_ENVELOPE FFEP_ENVELOPE
|
||||
#define DIEP_TYPESPECIFICPARAMS FFEP_TYPESPECIFICPARAMS
|
||||
#define DIEP_STARTDELAY FFEP_STARTDELAY
|
||||
#define DIEP_ALLPARAMS FFEP_ALLPARAMS
|
||||
#define DIEP_START FFEP_START
|
||||
#define DIEP_NORESTART FFEP_NORESTART
|
||||
#define DIEP_NODOWNLOAD FFEP_NODOWNLOAD
|
||||
#define DIEB_NOTRIGGER FFEB_NOTRIGGER
|
||||
|
||||
// FFEffectStartFlag
|
||||
#define DIES_SOLO FFES_SOLO
|
||||
#define DIES_NODOWNLOAD FFES_NODOWNLOAD
|
||||
|
||||
// FFEffectStatusFlag
|
||||
#define DIEGES_NOTPLAYING FFEGES_NOTPLAYING
|
||||
#define DIEGES_PLAYING FFEGES_PLAYING
|
||||
#define DIEGES_EMULATED FFEGES_EMULATED
|
||||
|
||||
// FFProperty
|
||||
#define DIPROP_FFGAIN FFPROP_FFGAIN
|
||||
#define DIPROP_AUTOCENTER FFPROP_AUTOCENTER
|
||||
// not defined in ForceFeedbackConstants.h
|
||||
#define DIPROPAUTOCENTER_OFF 0
|
||||
#define DIPROPAUTOCENTER_ON 1
|
||||
|
||||
// FFState
|
||||
#define DIGFFS_EMPTY FFGFFS_EMPTY
|
||||
#define DIGFFS_STOPPED FFGFFS_STOPPED
|
||||
#define DIGFFS_PAUSED FFGFFS_PAUSED
|
||||
#define DIGFFS_ACTUATORSON FFGFFS_ACTUATORSON
|
||||
#define DIGFFS_ACTUATORSOFF FFGFFS_ACTUATORSOFF
|
||||
#define DIGFFS_POWERON FFGFFS_POWERON
|
||||
#define DIGFFS_POWEROFF FFGFFS_POWEROFF
|
||||
#define DIGFFS_SAFETYSWITCHON FFGFFS_SAFETYSWITCHON
|
||||
#define DIGFFS_SAFETYSWITCHOFF FFGFFS_SAFETYSWITCHOFF
|
||||
#define DIGFFS_USERFFSWITCHON FFGFFS_USERFFSWITCHON
|
||||
#define DIGFFS_USERFFSWITCHOFF FFGFFS_USERFFSWITCHOFF
|
||||
#define DIGFFS_DEVICELOST FFGFFS_DEVICELOST
|
|
@ -16,6 +16,10 @@ void InputBackend::UpdateInput(std::vector<std::weak_ptr<ciface::Core::Device>>&
|
|||
{
|
||||
}
|
||||
|
||||
void InputBackend::HandleWindowChange()
|
||||
{
|
||||
}
|
||||
|
||||
ControllerInterface& InputBackend::GetControllerInterface()
|
||||
{
|
||||
return m_controller_interface;
|
||||
|
|
|
@ -28,6 +28,8 @@ public:
|
|||
// just add them to the removal list if necessary.
|
||||
virtual void UpdateInput(std::vector<std::weak_ptr<ciface::Core::Device>>& devices_to_remove);
|
||||
|
||||
virtual void HandleWindowChange();
|
||||
|
||||
ControllerInterface& GetControllerInterface();
|
||||
|
||||
private:
|
||||
|
|
|
@ -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<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface)
|
||||
{
|
||||
return std::make_unique<InputBackend>(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.
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace ciface::Pipes
|
|||
// SET {L, R} [0, 1]
|
||||
// SET {MAIN, C} [0, 1] [0, 1]
|
||||
|
||||
void PopulateDevices();
|
||||
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface);
|
||||
|
||||
class PipeDevice : public Core::Device
|
||||
{
|
||||
|
|
|
@ -3,8 +3,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "InputCommon/ControllerInterface/InputBackend.h"
|
||||
|
||||
namespace ciface::Quartz
|
||||
{
|
||||
void PopulateDevices(void* window);
|
||||
void DeInit();
|
||||
std::string GetSourceName();
|
||||
|
||||
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface);
|
||||
} // namespace ciface::Quartz
|
||||
|
|
|
@ -7,15 +7,40 @@
|
|||
|
||||
namespace ciface::Quartz
|
||||
{
|
||||
void PopulateDevices(void* window)
|
||||
std::string GetSourceName()
|
||||
{
|
||||
if (!window)
|
||||
return "Quartz";
|
||||
}
|
||||
|
||||
class InputBackend final : public ciface::InputBackend
|
||||
{
|
||||
public:
|
||||
using ciface::InputBackend::InputBackend;
|
||||
void PopulateDevices() override;
|
||||
void HandleWindowChange() override;
|
||||
};
|
||||
|
||||
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface)
|
||||
{
|
||||
return std::make_unique<InputBackend>(controller_interface);
|
||||
}
|
||||
|
||||
void InputBackend::HandleWindowChange()
|
||||
{
|
||||
const std::string source_name = GetSourceName();
|
||||
GetControllerInterface().RemoveDevice(
|
||||
[&](const auto* dev) { return dev->GetSource() == source_name; }, true);
|
||||
|
||||
PopulateDevices();
|
||||
}
|
||||
|
||||
void InputBackend::PopulateDevices()
|
||||
{
|
||||
const WindowSystemInfo wsi = GetControllerInterface().GetWindowSystemInfo();
|
||||
if (wsi.type != WindowSystemType::MacOS)
|
||||
return;
|
||||
|
||||
g_controller_interface.AddDevice(std::make_shared<KeyboardAndMouse>(window));
|
||||
GetControllerInterface().AddDevice(std::make_shared<KeyboardAndMouse>(wsi.render_window));
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
{
|
||||
}
|
||||
} // namespace ciface::Quartz
|
||||
|
|
|
@ -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 @@ std::string KeyboardAndMouse::GetName() const
|
|||
|
||||
std::string KeyboardAndMouse::GetSource() const
|
||||
{
|
||||
return "Quartz";
|
||||
return Quartz::GetSourceName();
|
||||
}
|
||||
|
||||
ControlState KeyboardAndMouse::Cursor::GetState() const
|
||||
|
|
|
@ -3,14 +3,17 @@
|
|||
|
||||
#include "InputCommon/ControllerInterface/SDL/SDL.h"
|
||||
|
||||
#include <SDL_haptic.h>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_haptic.h>
|
||||
|
||||
#include "Common/Event.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/ScopeGuard.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -22,8 +25,258 @@ namespace ciface::Core
|
|||
class Device;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string GetLegacyButtonName(int index)
|
||||
{
|
||||
return "Button " + std::to_string(index);
|
||||
}
|
||||
|
||||
std::string GetLegacyAxisName(int index, int range)
|
||||
{
|
||||
return "Axis " + std::to_string(index) + (range < 0 ? '-' : '+');
|
||||
}
|
||||
|
||||
std::string GetLegacyHatName(int index, int direction)
|
||||
{
|
||||
return "Hat " + std::to_string(index) + ' ' + "NESW"[direction];
|
||||
}
|
||||
|
||||
constexpr int GetDirectionFromHatMask(u8 mask)
|
||||
{
|
||||
return MathUtil::IntLog2(mask);
|
||||
}
|
||||
|
||||
static_assert(GetDirectionFromHatMask(SDL_HAT_UP) == 0);
|
||||
static_assert(GetDirectionFromHatMask(SDL_HAT_LEFT) == 3);
|
||||
|
||||
bool IsTriggerAxis(int index)
|
||||
{
|
||||
// First 4 axes are for the analog sticks, the rest are for the triggers
|
||||
return index >= 4;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace ciface::SDL
|
||||
{
|
||||
|
||||
class GameController : public Core::Device
|
||||
{
|
||||
private:
|
||||
// GameController inputs
|
||||
class Button : public Core::Device::Input
|
||||
{
|
||||
public:
|
||||
std::string GetName() const override;
|
||||
Button(SDL_GameController* gc, SDL_GameControllerButton button) : m_gc(gc), m_button(button) {}
|
||||
ControlState GetState() const override;
|
||||
bool IsMatchingName(std::string_view name) const override;
|
||||
|
||||
private:
|
||||
SDL_GameController* const m_gc;
|
||||
const SDL_GameControllerButton m_button;
|
||||
};
|
||||
|
||||
class Axis : public Core::Device::Input
|
||||
{
|
||||
public:
|
||||
std::string GetName() const override;
|
||||
Axis(SDL_GameController* gc, Sint16 range, SDL_GameControllerAxis axis)
|
||||
: m_gc(gc), m_range(range), m_axis(axis)
|
||||
{
|
||||
}
|
||||
ControlState GetState() const override;
|
||||
|
||||
private:
|
||||
SDL_GameController* const m_gc;
|
||||
const Sint16 m_range;
|
||||
const SDL_GameControllerAxis m_axis;
|
||||
};
|
||||
|
||||
// Legacy inputs
|
||||
class LegacyButton : public Core::Device::Input
|
||||
{
|
||||
public:
|
||||
std::string GetName() const override { return GetLegacyButtonName(m_index); }
|
||||
LegacyButton(SDL_Joystick* js, int index) : m_js(js), m_index(index) {}
|
||||
ControlState GetState() const override;
|
||||
|
||||
private:
|
||||
SDL_Joystick* const m_js;
|
||||
const int m_index;
|
||||
};
|
||||
|
||||
class LegacyAxis : public Core::Device::Input
|
||||
{
|
||||
public:
|
||||
std::string GetName() const override { return GetLegacyAxisName(m_index, m_range); }
|
||||
LegacyAxis(SDL_Joystick* js, int index, s16 range, bool is_handled_elsewhere)
|
||||
: m_js(js), m_index(index), m_range(range), m_is_handled_elsewhere(is_handled_elsewhere)
|
||||
{
|
||||
}
|
||||
ControlState GetState() const override;
|
||||
bool IsHidden() const override { return m_is_handled_elsewhere; }
|
||||
bool IsDetectable() const override { return !IsHidden(); }
|
||||
|
||||
private:
|
||||
SDL_Joystick* const m_js;
|
||||
const int m_index;
|
||||
const s16 m_range;
|
||||
const bool m_is_handled_elsewhere;
|
||||
};
|
||||
|
||||
class LegacyHat : public Input
|
||||
{
|
||||
public:
|
||||
std::string GetName() const override { return GetLegacyHatName(m_index, m_direction); }
|
||||
LegacyHat(SDL_Joystick* js, int index, u8 direction)
|
||||
: m_js(js), m_index(index), m_direction(direction)
|
||||
{
|
||||
}
|
||||
ControlState GetState() const override;
|
||||
|
||||
private:
|
||||
SDL_Joystick* const m_js;
|
||||
const int m_index;
|
||||
const u8 m_direction;
|
||||
};
|
||||
|
||||
// Rumble
|
||||
template <int LowEnable, int HighEnable, int SuffixIndex>
|
||||
class GenericMotor : public Output
|
||||
{
|
||||
public:
|
||||
explicit GenericMotor(SDL_GameController* gc) : m_gc(gc) {}
|
||||
std::string GetName() const override
|
||||
{
|
||||
return std::string("Motor") + motor_suffixes[SuffixIndex];
|
||||
}
|
||||
void SetState(ControlState state) override
|
||||
{
|
||||
Uint16 rumble = state * std::numeric_limits<Uint16>::max();
|
||||
SDL_GameControllerRumble(m_gc, rumble * LowEnable, rumble * HighEnable, RUMBLE_LENGTH_MS);
|
||||
}
|
||||
|
||||
private:
|
||||
SDL_GameController* const m_gc;
|
||||
};
|
||||
|
||||
static constexpr const char* motor_suffixes[] = {"", " L", " R"};
|
||||
|
||||
using Motor = GenericMotor<1, 1, 0>;
|
||||
using MotorL = GenericMotor<1, 0, 1>;
|
||||
using MotorR = GenericMotor<0, 1, 2>;
|
||||
|
||||
class HapticEffect : public Output
|
||||
{
|
||||
public:
|
||||
HapticEffect(SDL_Haptic* haptic);
|
||||
~HapticEffect();
|
||||
|
||||
protected:
|
||||
virtual bool UpdateParameters(s16 value) = 0;
|
||||
static void SetDirection(SDL_HapticDirection* dir);
|
||||
|
||||
SDL_HapticEffect m_effect = {};
|
||||
|
||||
static constexpr u16 DISABLED_EFFECT_TYPE = 0;
|
||||
|
||||
private:
|
||||
virtual void SetState(ControlState state) override final;
|
||||
void UpdateEffect();
|
||||
SDL_Haptic* const m_haptic;
|
||||
int m_id = -1;
|
||||
};
|
||||
|
||||
class ConstantEffect : public HapticEffect
|
||||
{
|
||||
public:
|
||||
ConstantEffect(SDL_Haptic* haptic);
|
||||
std::string GetName() const override;
|
||||
|
||||
private:
|
||||
bool UpdateParameters(s16 value) override;
|
||||
};
|
||||
|
||||
class RampEffect : public HapticEffect
|
||||
{
|
||||
public:
|
||||
RampEffect(SDL_Haptic* haptic);
|
||||
std::string GetName() const override;
|
||||
|
||||
private:
|
||||
bool UpdateParameters(s16 value) override;
|
||||
};
|
||||
|
||||
class PeriodicEffect : public HapticEffect
|
||||
{
|
||||
public:
|
||||
PeriodicEffect(SDL_Haptic* haptic, u16 waveform);
|
||||
std::string GetName() const override;
|
||||
|
||||
private:
|
||||
bool UpdateParameters(s16 value) override;
|
||||
|
||||
const u16 m_waveform;
|
||||
};
|
||||
|
||||
class LeftRightEffect : public HapticEffect
|
||||
{
|
||||
public:
|
||||
enum class Motor : u8
|
||||
{
|
||||
Weak,
|
||||
Strong,
|
||||
};
|
||||
|
||||
LeftRightEffect(SDL_Haptic* haptic, Motor motor);
|
||||
std::string GetName() const override;
|
||||
|
||||
private:
|
||||
bool UpdateParameters(s16 value) override;
|
||||
|
||||
const Motor m_motor;
|
||||
};
|
||||
|
||||
class MotionInput : public Input
|
||||
{
|
||||
public:
|
||||
MotionInput(std::string name, SDL_GameController* gc, SDL_SensorType type, int index,
|
||||
ControlState scale)
|
||||
: m_name(std::move(name)), m_gc(gc), m_type(type), m_index(index), m_scale(scale){};
|
||||
|
||||
std::string GetName() const override { return m_name; };
|
||||
bool IsDetectable() const override { return false; };
|
||||
ControlState GetState() const override;
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
|
||||
SDL_GameController* const m_gc;
|
||||
SDL_SensorType const m_type;
|
||||
int const m_index;
|
||||
|
||||
ControlState const m_scale;
|
||||
};
|
||||
|
||||
public:
|
||||
GameController(SDL_GameController* const gamecontroller, SDL_Joystick* const joystick,
|
||||
const int sdl_index);
|
||||
~GameController();
|
||||
|
||||
std::string GetName() const override;
|
||||
std::string GetSource() const override;
|
||||
int GetSDLIndex() const;
|
||||
|
||||
private:
|
||||
SDL_GameController* const m_gamecontroller;
|
||||
std::string m_name;
|
||||
int m_sdl_index;
|
||||
SDL_Joystick* const m_joystick;
|
||||
SDL_Haptic* m_haptic = nullptr;
|
||||
};
|
||||
|
||||
class InputBackend final : public ciface::InputBackend
|
||||
{
|
||||
public:
|
||||
|
@ -340,48 +593,26 @@ GameController::GameController(SDL_GameController* const gamecontroller,
|
|||
name = SDL_JoystickName(joystick);
|
||||
m_name = name != nullptr ? name : "Unknown";
|
||||
|
||||
// If a Joystick Button has a GameController equivalent, don't detect it
|
||||
int n_legacy_buttons = SDL_JoystickNumButtons(joystick);
|
||||
if (n_legacy_buttons < 0)
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "Error in SDL_JoystickNumButtons(): {}", SDL_GetError());
|
||||
n_legacy_buttons = 0;
|
||||
}
|
||||
|
||||
int n_legacy_axes = SDL_JoystickNumAxes(joystick);
|
||||
if (n_legacy_axes < 0)
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "Error in SDL_JoystickNumAxes(): {}", SDL_GetError());
|
||||
n_legacy_axes = 0;
|
||||
}
|
||||
|
||||
int n_legacy_hats = SDL_JoystickNumHats(joystick);
|
||||
if (n_legacy_hats < 0)
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "Error in SDL_JoystickNumHats(): {}", SDL_GetError());
|
||||
n_legacy_hats = 0;
|
||||
}
|
||||
|
||||
std::vector<bool> is_button_mapped(static_cast<size_t>(n_legacy_buttons), false);
|
||||
std::vector<bool> is_axis_mapped(static_cast<size_t>(n_legacy_axes), false);
|
||||
std::vector<bool> is_hat_mapped(static_cast<size_t>(n_legacy_hats), false);
|
||||
|
||||
// If a Joystick input has a GameController equivalent button/hat we don't add it.
|
||||
// "Equivalent" axes are still added as hidden/undetectable inputs to handle
|
||||
// loading of existing configs which may use "full surface" inputs.
|
||||
// Otherwise handling those would require dealing with gamepad specific quirks.
|
||||
std::unordered_set<int> registered_buttons;
|
||||
std::unordered_set<int> registered_hats;
|
||||
std::unordered_set<int> registered_axes;
|
||||
const auto register_mapping = [&](const SDL_GameControllerButtonBind& bind) {
|
||||
switch (bind.bindType)
|
||||
{
|
||||
case SDL_CONTROLLER_BINDTYPE_NONE:
|
||||
return;
|
||||
case SDL_CONTROLLER_BINDTYPE_BUTTON:
|
||||
if (bind.value.button >= 0 && bind.value.button < n_legacy_buttons)
|
||||
is_button_mapped[bind.value.button] = true;
|
||||
break;
|
||||
case SDL_CONTROLLER_BINDTYPE_AXIS:
|
||||
if (bind.value.axis >= 0 && bind.value.axis < n_legacy_axes)
|
||||
is_axis_mapped[bind.value.axis] = true;
|
||||
registered_buttons.insert(bind.value.button);
|
||||
break;
|
||||
case SDL_CONTROLLER_BINDTYPE_HAT:
|
||||
if (bind.value.hat.hat >= 0 && bind.value.hat.hat < n_legacy_hats)
|
||||
is_hat_mapped[bind.value.hat.hat] = true;
|
||||
registered_hats.insert(bind.value.hat.hat);
|
||||
break;
|
||||
case SDL_CONTROLLER_BINDTYPE_AXIS:
|
||||
registered_axes.insert(bind.value.axis);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
@ -397,6 +628,7 @@ GameController::GameController(SDL_GameController* const gamecontroller,
|
|||
if (SDL_GameControllerHasButton(m_gamecontroller, button))
|
||||
{
|
||||
AddInput(new Button(gamecontroller, button));
|
||||
|
||||
register_mapping(SDL_GameControllerGetBindForButton(gamecontroller, button));
|
||||
}
|
||||
}
|
||||
|
@ -407,20 +639,21 @@ GameController::GameController(SDL_GameController* const gamecontroller,
|
|||
SDL_GameControllerAxis axis = static_cast<SDL_GameControllerAxis>(i);
|
||||
if (SDL_GameControllerHasAxis(m_gamecontroller, axis))
|
||||
{
|
||||
// First 4 axes are for the analog sticks, the rest are for the triggers
|
||||
if (i < 4)
|
||||
if (IsTriggerAxis(axis))
|
||||
{
|
||||
AddInput(new Axis(m_gamecontroller, 32767, axis));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Each axis gets a negative and a positive input instance associated with it
|
||||
AddInput(new Axis(m_gamecontroller, -32768, axis));
|
||||
AddInput(new Axis(m_gamecontroller, 32767, axis));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddInput(new Axis(m_gamecontroller, 32767, axis));
|
||||
}
|
||||
|
||||
register_mapping(SDL_GameControllerGetBindForAxis(gamecontroller, axis));
|
||||
}
|
||||
}
|
||||
|
||||
// Rumble
|
||||
if (SDL_GameControllerHasRumble(m_gamecontroller))
|
||||
{
|
||||
|
@ -430,14 +663,14 @@ GameController::GameController(SDL_GameController* const gamecontroller,
|
|||
}
|
||||
|
||||
// Motion
|
||||
const auto add_sensor = [this](SDL_SensorType type, std::string_view name,
|
||||
const auto add_sensor = [this](SDL_SensorType type, std::string_view sensor_name,
|
||||
const SDLMotionAxisList& axes) {
|
||||
if (SDL_GameControllerSetSensorEnabled(m_gamecontroller, type, SDL_TRUE) == 0)
|
||||
{
|
||||
for (const SDLMotionAxis& axis : axes)
|
||||
{
|
||||
AddInput(new MotionInput(fmt::format("{} {}", name, axis.name), m_gamecontroller, type,
|
||||
axis.index, axis.scale));
|
||||
AddInput(new MotionInput(fmt::format("{} {}", sensor_name, axis.name), m_gamecontroller,
|
||||
type, axis.index, axis.scale));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -453,23 +686,51 @@ GameController::GameController(SDL_GameController* const gamecontroller,
|
|||
// Legacy inputs
|
||||
|
||||
// Buttons
|
||||
int n_legacy_buttons = SDL_JoystickNumButtons(joystick);
|
||||
if (n_legacy_buttons < 0)
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "Error in SDL_JoystickNumButtons(): {}", SDL_GetError());
|
||||
n_legacy_buttons = 0;
|
||||
}
|
||||
for (int i = 0; i != n_legacy_buttons; ++i)
|
||||
AddInput(new LegacyButton(m_joystick, i, !is_button_mapped[i]));
|
||||
{
|
||||
if (registered_buttons.contains(i))
|
||||
continue;
|
||||
|
||||
AddInput(new LegacyButton(m_joystick, i));
|
||||
}
|
||||
|
||||
// Axes
|
||||
int n_legacy_axes = SDL_JoystickNumAxes(joystick);
|
||||
if (n_legacy_axes < 0)
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "Error in SDL_JoystickNumAxes(): {}", SDL_GetError());
|
||||
n_legacy_axes = 0;
|
||||
}
|
||||
for (int i = 0; i != n_legacy_axes; ++i)
|
||||
{
|
||||
const bool is_registered = registered_axes.contains(i);
|
||||
|
||||
// each axis gets a negative and a positive input instance associated with it
|
||||
AddAnalogInputs(new LegacyAxis(m_joystick, i, -32768, !is_axis_mapped[i]),
|
||||
new LegacyAxis(m_joystick, i, 32767, !is_axis_mapped[i]));
|
||||
AddAnalogInputs(new LegacyAxis(m_joystick, i, -32768, is_registered),
|
||||
new LegacyAxis(m_joystick, i, 32767, is_registered));
|
||||
}
|
||||
|
||||
// Hats
|
||||
int n_legacy_hats = SDL_JoystickNumHats(joystick);
|
||||
if (n_legacy_hats < 0)
|
||||
{
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "Error in SDL_JoystickNumHats(): {}", SDL_GetError());
|
||||
n_legacy_hats = 0;
|
||||
}
|
||||
for (int i = 0; i != n_legacy_hats; ++i)
|
||||
{
|
||||
if (registered_hats.contains(i))
|
||||
continue;
|
||||
|
||||
// each hat gets 4 input instances associated with it, (up down left right)
|
||||
for (u8 d = 0; d != 4; ++d)
|
||||
AddInput(new LegacyHat(m_joystick, i, d, !is_hat_mapped[i]));
|
||||
AddInput(new LegacyHat(m_joystick, i, d));
|
||||
}
|
||||
|
||||
// Haptics
|
||||
|
@ -531,39 +792,6 @@ GameController::~GameController()
|
|||
SDL_JoystickClose(m_joystick);
|
||||
}
|
||||
|
||||
std::string GameController::Motor::GetName() const
|
||||
{
|
||||
return "Motor";
|
||||
}
|
||||
|
||||
void GameController::Motor::SetState(ControlState state)
|
||||
{
|
||||
Uint16 rumble = state * std::numeric_limits<Uint16>::max();
|
||||
SDL_GameControllerRumble(m_gc, rumble, rumble, std::numeric_limits<Uint32>::max());
|
||||
}
|
||||
|
||||
std::string GameController::MotorL::GetName() const
|
||||
{
|
||||
return "Motor L";
|
||||
}
|
||||
|
||||
void GameController::MotorL::SetState(ControlState state)
|
||||
{
|
||||
Uint16 rumble = state * std::numeric_limits<Uint16>::max();
|
||||
SDL_GameControllerRumble(m_gc, rumble, 0, std::numeric_limits<Uint32>::max());
|
||||
}
|
||||
|
||||
std::string GameController::MotorR::GetName() const
|
||||
{
|
||||
return "Motor R";
|
||||
}
|
||||
|
||||
void GameController::MotorR::SetState(ControlState state)
|
||||
{
|
||||
Uint16 rumble = state * std::numeric_limits<Uint16>::max();
|
||||
SDL_GameControllerRumble(m_gc, 0, rumble, std::numeric_limits<Uint32>::max());
|
||||
}
|
||||
|
||||
void InputBackend::UpdateInput(std::vector<std::weak_ptr<ciface::Core::Device>>& devices_to_remove)
|
||||
{
|
||||
SDL_GameControllerUpdate();
|
||||
|
@ -591,8 +819,7 @@ std::string GameController::Button::GetName() const
|
|||
|
||||
std::string GameController::Axis::GetName() const
|
||||
{
|
||||
// The triggers are only positive, and must not have a sign
|
||||
if (m_axis >= 4)
|
||||
if (IsTriggerAxis(m_axis))
|
||||
return std::string(s_sdl_axis_names[m_axis]);
|
||||
|
||||
bool negative = m_range < 0;
|
||||
|
@ -628,7 +855,19 @@ bool GameController::Button::IsMatchingName(std::string_view name) const
|
|||
return GetName() == "Button W";
|
||||
if (name == "Button Y")
|
||||
return GetName() == "Button N";
|
||||
return false;
|
||||
|
||||
// Match legacy names.
|
||||
const auto bind = SDL_GameControllerGetBindForButton(m_gc, m_button);
|
||||
switch (bind.bindType)
|
||||
{
|
||||
case SDL_CONTROLLER_BINDTYPE_BUTTON:
|
||||
return name == GetLegacyButtonName(bind.value.button);
|
||||
case SDL_CONTROLLER_BINDTYPE_HAT:
|
||||
return name == GetLegacyHatName(bind.value.hat.hat,
|
||||
GetDirectionFromHatMask(u8(bind.value.hat.hat_mask)));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ControlState GameController::MotionInput::GetState() const
|
||||
|
@ -639,21 +878,6 @@ ControlState GameController::MotionInput::GetState() const
|
|||
}
|
||||
|
||||
// Legacy input
|
||||
std::string GameController::LegacyButton::GetName() const
|
||||
{
|
||||
return "Button " + std::to_string(m_index);
|
||||
}
|
||||
|
||||
std::string GameController::LegacyAxis::GetName() const
|
||||
{
|
||||
return "Axis " + std::to_string(m_index) + (m_range < 0 ? '-' : '+');
|
||||
}
|
||||
|
||||
std::string GameController::LegacyHat::GetName() const
|
||||
{
|
||||
return "Hat " + std::to_string(m_index) + ' ' + "NESW"[m_direction];
|
||||
}
|
||||
|
||||
ControlState GameController::LegacyButton::GetState() const
|
||||
{
|
||||
return SDL_JoystickGetButton(m_js, m_index);
|
||||
|
|
|
@ -3,242 +3,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "InputCommon/ControllerInterface/CoreDevice.h"
|
||||
#include "InputCommon/ControllerInterface/InputBackend.h"
|
||||
|
||||
namespace ciface::SDL
|
||||
{
|
||||
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface);
|
||||
|
||||
class GameController : public Core::Device
|
||||
{
|
||||
private:
|
||||
// GameController inputs
|
||||
class Button : public Core::Device::Input
|
||||
{
|
||||
public:
|
||||
std::string GetName() const override;
|
||||
Button(SDL_GameController* gc, SDL_GameControllerButton button) : m_gc(gc), m_button(button) {}
|
||||
ControlState GetState() const override;
|
||||
bool IsMatchingName(std::string_view name) const override;
|
||||
|
||||
private:
|
||||
SDL_GameController* const m_gc;
|
||||
const SDL_GameControllerButton m_button;
|
||||
};
|
||||
|
||||
class Axis : public Core::Device::Input
|
||||
{
|
||||
public:
|
||||
std::string GetName() const override;
|
||||
Axis(SDL_GameController* gc, Sint16 range, SDL_GameControllerAxis axis)
|
||||
: m_gc(gc), m_range(range), m_axis(axis)
|
||||
{
|
||||
}
|
||||
ControlState GetState() const override;
|
||||
|
||||
private:
|
||||
SDL_GameController* const m_gc;
|
||||
const Sint16 m_range;
|
||||
const SDL_GameControllerAxis m_axis;
|
||||
};
|
||||
|
||||
// Legacy inputs
|
||||
class LegacyButton : public Core::Device::Input
|
||||
{
|
||||
public:
|
||||
std::string GetName() const override;
|
||||
LegacyButton(SDL_Joystick* js, int index, bool is_detectable)
|
||||
: m_js(js), m_index(index), m_is_detectable(is_detectable)
|
||||
{
|
||||
}
|
||||
bool IsDetectable() const override { return m_is_detectable; }
|
||||
ControlState GetState() const override;
|
||||
|
||||
private:
|
||||
SDL_Joystick* const m_js;
|
||||
const int m_index;
|
||||
const bool m_is_detectable;
|
||||
};
|
||||
|
||||
class LegacyAxis : public Core::Device::Input
|
||||
{
|
||||
public:
|
||||
std::string GetName() const override;
|
||||
LegacyAxis(SDL_Joystick* js, int index, s16 range, bool is_detectable)
|
||||
: m_js(js), m_index(index), m_range(range), m_is_detectable(is_detectable)
|
||||
{
|
||||
}
|
||||
bool IsDetectable() const override { return m_is_detectable; }
|
||||
ControlState GetState() const override;
|
||||
|
||||
private:
|
||||
SDL_Joystick* const m_js;
|
||||
const int m_index;
|
||||
const s16 m_range;
|
||||
const bool m_is_detectable;
|
||||
};
|
||||
|
||||
class LegacyHat : public Input
|
||||
{
|
||||
public:
|
||||
std::string GetName() const override;
|
||||
LegacyHat(SDL_Joystick* js, int index, u8 direction, bool is_detectable)
|
||||
: m_js(js), m_index(index), m_direction(direction), m_is_detectable(is_detectable)
|
||||
{
|
||||
}
|
||||
bool IsDetectable() const override { return m_is_detectable; }
|
||||
ControlState GetState() const override;
|
||||
|
||||
private:
|
||||
SDL_Joystick* const m_js;
|
||||
const int m_index;
|
||||
const u8 m_direction;
|
||||
const bool m_is_detectable;
|
||||
};
|
||||
|
||||
// Rumble
|
||||
class Motor : public Output
|
||||
{
|
||||
public:
|
||||
explicit Motor(SDL_GameController* gc) : m_gc(gc) {}
|
||||
std::string GetName() const override;
|
||||
void SetState(ControlState state) override;
|
||||
|
||||
private:
|
||||
SDL_GameController* const m_gc;
|
||||
};
|
||||
|
||||
class MotorL : public Output
|
||||
{
|
||||
public:
|
||||
explicit MotorL(SDL_GameController* gc) : m_gc(gc) {}
|
||||
std::string GetName() const override;
|
||||
void SetState(ControlState state) override;
|
||||
|
||||
private:
|
||||
SDL_GameController* const m_gc;
|
||||
};
|
||||
|
||||
class MotorR : public Output
|
||||
{
|
||||
public:
|
||||
explicit MotorR(SDL_GameController* gc) : m_gc(gc) {}
|
||||
std::string GetName() const override;
|
||||
void SetState(ControlState state) override;
|
||||
|
||||
private:
|
||||
SDL_GameController* const m_gc;
|
||||
};
|
||||
|
||||
class HapticEffect : public Output
|
||||
{
|
||||
public:
|
||||
HapticEffect(SDL_Haptic* haptic);
|
||||
~HapticEffect();
|
||||
|
||||
protected:
|
||||
virtual bool UpdateParameters(s16 value) = 0;
|
||||
static void SetDirection(SDL_HapticDirection* dir);
|
||||
|
||||
SDL_HapticEffect m_effect = {};
|
||||
|
||||
static constexpr u16 DISABLED_EFFECT_TYPE = 0;
|
||||
|
||||
private:
|
||||
virtual void SetState(ControlState state) override final;
|
||||
void UpdateEffect();
|
||||
SDL_Haptic* const m_haptic;
|
||||
int m_id = -1;
|
||||
};
|
||||
|
||||
class ConstantEffect : public HapticEffect
|
||||
{
|
||||
public:
|
||||
ConstantEffect(SDL_Haptic* haptic);
|
||||
std::string GetName() const override;
|
||||
|
||||
private:
|
||||
bool UpdateParameters(s16 value) override;
|
||||
};
|
||||
|
||||
class RampEffect : public HapticEffect
|
||||
{
|
||||
public:
|
||||
RampEffect(SDL_Haptic* haptic);
|
||||
std::string GetName() const override;
|
||||
|
||||
private:
|
||||
bool UpdateParameters(s16 value) override;
|
||||
};
|
||||
|
||||
class PeriodicEffect : public HapticEffect
|
||||
{
|
||||
public:
|
||||
PeriodicEffect(SDL_Haptic* haptic, u16 waveform);
|
||||
std::string GetName() const override;
|
||||
|
||||
private:
|
||||
bool UpdateParameters(s16 value) override;
|
||||
|
||||
const u16 m_waveform;
|
||||
};
|
||||
|
||||
class LeftRightEffect : public HapticEffect
|
||||
{
|
||||
public:
|
||||
enum class Motor : u8
|
||||
{
|
||||
Weak,
|
||||
Strong,
|
||||
};
|
||||
|
||||
LeftRightEffect(SDL_Haptic* haptic, Motor motor);
|
||||
std::string GetName() const override;
|
||||
|
||||
private:
|
||||
bool UpdateParameters(s16 value) override;
|
||||
|
||||
const Motor m_motor;
|
||||
};
|
||||
|
||||
class MotionInput : public Input
|
||||
{
|
||||
public:
|
||||
MotionInput(std::string name, SDL_GameController* gc, SDL_SensorType type, int index,
|
||||
ControlState scale)
|
||||
: m_name(std::move(name)), m_gc(gc), m_type(type), m_index(index), m_scale(scale){};
|
||||
|
||||
std::string GetName() const override { return m_name; };
|
||||
bool IsDetectable() const override { return false; };
|
||||
ControlState GetState() const override;
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
|
||||
SDL_GameController* const m_gc;
|
||||
SDL_SensorType const m_type;
|
||||
int const m_index;
|
||||
|
||||
ControlState const m_scale;
|
||||
};
|
||||
|
||||
public:
|
||||
GameController(SDL_GameController* const gamecontroller, SDL_Joystick* const joystick,
|
||||
const int sdl_index);
|
||||
~GameController();
|
||||
|
||||
std::string GetName() const override;
|
||||
std::string GetSource() const override;
|
||||
int GetSDLIndex() const;
|
||||
|
||||
private:
|
||||
SDL_GameController* const m_gamecontroller;
|
||||
std::string m_name;
|
||||
int m_sdl_index;
|
||||
SDL_Joystick* const m_joystick;
|
||||
SDL_Haptic* m_haptic = nullptr;
|
||||
};
|
||||
} // namespace ciface::SDL
|
||||
|
|
|
@ -201,6 +201,17 @@ Device::Device(std::unique_ptr<WiimoteReal::Wiimote> wiimote) : m_wiimote(std::m
|
|||
|
||||
AddInput(new UndetectableAnalogInput<float>(&m_ir_state.distance, "IR Distance", 1));
|
||||
|
||||
// Raw IR Objects.
|
||||
for (std::size_t i = 0; i < 4; ++i)
|
||||
{
|
||||
AddInput(new UndetectableAnalogInput<float>(&m_ir_state.raw_ir_object_position[i].x,
|
||||
fmt::format("IR Object {} X", i + 1), 1));
|
||||
AddInput(new UndetectableAnalogInput<float>(&m_ir_state.raw_ir_object_position[i].y,
|
||||
fmt::format("IR Object {} Y", i + 1), 1));
|
||||
AddInput(new UndetectableAnalogInput<float>(&m_ir_state.raw_ir_object_size[i],
|
||||
fmt::format("IR Object {} Size", i + 1), 1));
|
||||
}
|
||||
|
||||
// Raw gyroscope.
|
||||
static constexpr std::array<std::array<const char*, 2>, 3> gyro_names = {{
|
||||
{"Gyro Pitch Down", "Gyro Pitch Up"},
|
||||
|
@ -1178,8 +1189,7 @@ void Device::ProcessInputReport(WiimoteReal::Report& report)
|
|||
// Process IR data.
|
||||
if (manipulator->HasIR() && m_ir_state.IsFullyConfigured())
|
||||
{
|
||||
m_ir_state.ProcessData(
|
||||
Common::BitCastPtr<std::array<WiimoteEmu::IRBasic, 2>>(manipulator->GetIRDataPtr()));
|
||||
m_ir_state.ProcessData(*manipulator);
|
||||
}
|
||||
|
||||
// Process extension data.
|
||||
|
@ -1251,7 +1261,7 @@ void Device::UpdateOrientation()
|
|||
float(MathUtil::PI);
|
||||
}
|
||||
|
||||
void Device::IRState::ProcessData(const std::array<WiimoteEmu::IRBasic, 2>& data)
|
||||
void Device::IRState::ProcessData(const DataReportManipulator& manipulator)
|
||||
{
|
||||
// A better implementation might extrapolate points when they fall out of camera view.
|
||||
// But just averaging visible points actually seems to work very well.
|
||||
|
@ -1263,18 +1273,54 @@ void Device::IRState::ProcessData(const std::array<WiimoteEmu::IRBasic, 2>& data
|
|||
const auto camera_max = IRObject(WiimoteEmu::CameraLogic::CAMERA_RES_X - 1,
|
||||
WiimoteEmu::CameraLogic::CAMERA_RES_Y - 1);
|
||||
|
||||
const auto add_point = [&](IRObject point) {
|
||||
const auto add_point = [&](IRObject point, u8 size, size_t idx) {
|
||||
// Non-visible points are 0xFF-filled.
|
||||
if (point.y > camera_max.y)
|
||||
{
|
||||
raw_ir_object_position[idx].x = 0.0f;
|
||||
raw_ir_object_position[idx].y = 0.0f;
|
||||
raw_ir_object_size[idx] = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
raw_ir_object_position[idx].x = static_cast<float>(point.x) / camera_max.x;
|
||||
raw_ir_object_position[idx].y = static_cast<float>(point.y) / camera_max.y;
|
||||
raw_ir_object_size[idx] = static_cast<float>(size) / 15.0f;
|
||||
|
||||
points.Push(Common::Vec2(point));
|
||||
};
|
||||
|
||||
for (auto& block : data)
|
||||
size_t object_index = 0;
|
||||
switch (manipulator.GetIRReportFormat())
|
||||
{
|
||||
add_point(block.GetObject1());
|
||||
add_point(block.GetObject2());
|
||||
case IRReportFormat::Basic:
|
||||
{
|
||||
const std::array<WiimoteEmu::IRBasic, 2> data =
|
||||
Common::BitCastPtr<std::array<WiimoteEmu::IRBasic, 2>>(manipulator.GetIRDataPtr());
|
||||
for (const auto& block : data)
|
||||
{
|
||||
// size is not reported by IRBasic, just assume a typical size
|
||||
add_point(block.GetObject1(), 2, object_index);
|
||||
++object_index;
|
||||
add_point(block.GetObject2(), 2, object_index);
|
||||
++object_index;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IRReportFormat::Extended:
|
||||
{
|
||||
const std::array<WiimoteEmu::IRExtended, 4> data =
|
||||
Common::BitCastPtr<std::array<WiimoteEmu::IRExtended, 4>>(manipulator.GetIRDataPtr());
|
||||
for (const auto& object : data)
|
||||
{
|
||||
add_point(object.GetPosition(), object.size, object_index);
|
||||
++object_index;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// unsupported format
|
||||
return;
|
||||
}
|
||||
|
||||
is_hidden = !points.Count();
|
||||
|
|
|
@ -126,7 +126,7 @@ private:
|
|||
{
|
||||
static u32 GetDesiredIRSensitivity();
|
||||
|
||||
void ProcessData(const std::array<WiimoteEmu::IRBasic, 2>&);
|
||||
void ProcessData(const DataReportManipulator& manipulator);
|
||||
bool IsFullyConfigured() const;
|
||||
|
||||
u32 current_sensitivity = u32(-1);
|
||||
|
@ -139,6 +139,9 @@ private:
|
|||
float distance = 0;
|
||||
|
||||
bool is_hidden = true;
|
||||
|
||||
std::array<Common::Vec2, 4> raw_ir_object_position;
|
||||
std::array<float, 4> raw_ir_object_size;
|
||||
};
|
||||
|
||||
class ReportHandler
|
||||
|
|
|
@ -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<ciface::Win32::InputBackend*>(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
|
||||
{
|
||||
s_hwnd = static_cast<HWND>(hwnd);
|
||||
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface)
|
||||
{
|
||||
return std::make_unique<InputBackend>(controller_interface);
|
||||
}
|
||||
|
||||
HWND InputBackend::GetHWND()
|
||||
{
|
||||
return static_cast<HWND>(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>(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>(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
|
||||
|
|
|
@ -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<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface);
|
||||
} // namespace ciface::Win32
|
||||
|
|
|
@ -66,9 +66,38 @@ constexpr int XINPUT_MAJOR = 2, XINPUT_MINOR = 1;
|
|||
|
||||
namespace ciface::XInput2
|
||||
{
|
||||
// This function will add zero or more KeyboardMouse objects to devices.
|
||||
void PopulateDevices(void* const hwnd)
|
||||
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<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface)
|
||||
{
|
||||
return std::make_unique<InputBackend>(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 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<KeyboardMouse>((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)
|
||||
|
|
|
@ -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<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface);
|
||||
|
||||
class KeyboardMouse : public Core::Device
|
||||
{
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "Common/ScopeGuard.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
||||
|
||||
namespace ciface::evdev
|
||||
|
@ -34,6 +35,12 @@ public:
|
|||
|
||||
void RemoveDevnodeObject(const std::string&);
|
||||
|
||||
// Linux has the strange behavior that closing file descriptors of event devices can be
|
||||
// surprisingly slow, in the range of 20-70 milliseconds. For modern systems that have maybe 30
|
||||
// event devices this can quickly add up, leading to visibly slow startup. So we close FDs on a
|
||||
// separate thread *shrug*
|
||||
void CloseDescriptor(int fd) { m_cleanup_thread.Push(fd); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<evdevDevice>
|
||||
FindDeviceWithUniqueIDAndPhysicalLocation(const char* unique_id, const char* physical_location);
|
||||
|
@ -55,6 +62,8 @@ private:
|
|||
// as devices can be destroyed by any thread at any time. As of now it's protected
|
||||
// by ControllerInterface::m_devices_population_mutex.
|
||||
std::map<std::string, std::weak_ptr<evdevDevice>> m_devnode_objects;
|
||||
|
||||
Common::WorkQueueThread<int> m_cleanup_thread;
|
||||
};
|
||||
|
||||
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface)
|
||||
|
@ -273,7 +282,7 @@ void InputBackend::AddDeviceNode(const char* devnode)
|
|||
if (libevdev_new_from_fd(fd, &dev) != 0)
|
||||
{
|
||||
// This usually fails because the device node isn't an evdev device, such as /dev/input/js0
|
||||
close(fd);
|
||||
CloseDescriptor(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -415,7 +424,7 @@ void InputBackend::StopHotplugThread()
|
|||
}
|
||||
|
||||
InputBackend::InputBackend(ControllerInterface* controller_interface)
|
||||
: ciface::InputBackend(controller_interface)
|
||||
: ciface::InputBackend(controller_interface), m_cleanup_thread("evdev cleanup", close)
|
||||
{
|
||||
StartHotplugThread();
|
||||
}
|
||||
|
@ -665,7 +674,7 @@ evdevDevice::~evdevDevice()
|
|||
{
|
||||
m_input_backend.RemoveDevnodeObject(node.devnode);
|
||||
libevdev_free(node.device);
|
||||
close(node.fd);
|
||||
m_input_backend.CloseDescriptor(node.fd);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue