mirror of https://github.com/stenzek/duckstation
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
178 lines
5.9 KiB
C++
178 lines
5.9 KiB
C++
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
|
|
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
|
|
|
#pragma once
|
|
|
|
#include "input_source.h"
|
|
|
|
#include <SDL3/SDL.h>
|
|
|
|
#include <array>
|
|
#include <functional>
|
|
#include <mutex>
|
|
#include <span>
|
|
#include <vector>
|
|
|
|
class SettingsInterface;
|
|
struct SettingInfo;
|
|
|
|
class SDLInputSource final : public InputSource
|
|
{
|
|
public:
|
|
static constexpr u32 MAX_LED_COLORS = 4;
|
|
|
|
SDLInputSource();
|
|
~SDLInputSource();
|
|
|
|
bool Initialize(const SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock) override;
|
|
void UpdateSettings(const SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock) override;
|
|
bool ReloadDevices() override;
|
|
void Shutdown() override;
|
|
|
|
void PollEvents() override;
|
|
std::optional<float> GetCurrentValue(InputBindingKey key) override;
|
|
InputManager::DeviceList EnumerateDevices() override;
|
|
InputManager::DeviceEffectList EnumerateEffects(std::optional<InputBindingInfo::Type> type,
|
|
std::optional<InputBindingKey> for_device) override;
|
|
u32 GetPollableDeviceCount() const override;
|
|
bool GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping) override;
|
|
void UpdateMotorState(InputBindingKey key, float intensity) override;
|
|
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
|
float small_intensity) override;
|
|
void UpdateLEDState(InputBindingKey key, float intensity) override;
|
|
|
|
bool ContainsDevice(std::string_view device) const override;
|
|
std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) override;
|
|
TinyString ConvertKeyToString(InputBindingKey key) override;
|
|
TinyString ConvertKeyToDisplayString(InputBindingKey key, bool allow_icon,
|
|
InputManager::BindingIconMappingFunction mapper) override;
|
|
void SetSubclassPollDeviceList(InputSubclass subclass, const std::span<const InputBindingKey>* devices) override;
|
|
std::unique_ptr<ForceFeedbackDevice> CreateForceFeedbackDevice(std::string_view device, Error* error) override;
|
|
|
|
bool ProcessSDLEvent(const SDL_Event* event);
|
|
|
|
SDL_Joystick* GetJoystickForDevice(std::string_view device);
|
|
|
|
static u32 GetRGBForPlayerId(const SettingsInterface& si, u32 player_id, bool active);
|
|
static u32 ParseRGBForPlayerId(std::string_view str, u32 player_id, bool active);
|
|
|
|
static std::span<const SettingInfo> GetAdvancedSettingsInfo();
|
|
|
|
static bool IsHandledInputEvent(const SDL_Event* ev);
|
|
|
|
static bool ALLOW_EVENT_POLLING;
|
|
|
|
private:
|
|
struct ControllerData
|
|
{
|
|
SDL_Haptic* haptic;
|
|
SDL_Gamepad* gamepad;
|
|
SDL_Joystick* joystick;
|
|
u16 rumble_intensity[2];
|
|
int haptic_left_right_effect;
|
|
SDL_JoystickID joystick_id;
|
|
int player_id;
|
|
float last_touch_x;
|
|
float last_touch_y;
|
|
float last_accel_turn;
|
|
float last_accel_tilt;
|
|
float rgb_led_intensity;
|
|
bool accel_enabled;
|
|
u8 gamepad_type;
|
|
bool use_gamepad_rumble : 1;
|
|
bool has_led : 1;
|
|
bool has_rgb_led : 1;
|
|
bool has_mode_led : 1;
|
|
bool mode_led_state : 1;
|
|
bool has_accel : 1;
|
|
|
|
// Used to disable Joystick controls that are used in GameController inputs so we don't get double events
|
|
std::vector<bool> joy_button_used_in_gc;
|
|
std::vector<bool> joy_axis_used_in_gc;
|
|
std::vector<bool> joy_hat_used_in_gc;
|
|
|
|
// Track last hat state so we can send "unpressed" events.
|
|
std::vector<u8> last_hat_state;
|
|
};
|
|
|
|
using ControllerDataVector = std::vector<ControllerData>;
|
|
|
|
bool InitializeSubsystem();
|
|
void ShutdownSubsystem();
|
|
void LoadSettings(const SettingsInterface& si);
|
|
void SetHints();
|
|
|
|
ControllerDataVector::iterator GetControllerDataForJoystickId(SDL_JoystickID id);
|
|
ControllerDataVector::iterator GetControllerDataForPlayerId(int id);
|
|
int GetFreePlayerId() const;
|
|
|
|
bool OpenDevice(int index, bool is_gamecontroller);
|
|
bool CloseDevice(SDL_JoystickID joystick_index);
|
|
bool HandleGamepadAxisMotionEvent(const SDL_GamepadAxisEvent* ev);
|
|
bool HandleGamepadButtonEvent(const SDL_GamepadButtonEvent* ev);
|
|
bool HandleGamepadTouchpadEvent(const SDL_GamepadTouchpadEvent* ev);
|
|
bool HandleJoystickAxisEvent(const SDL_JoyAxisEvent* ev);
|
|
bool HandleJoystickButtonEvent(const SDL_JoyButtonEvent* ev);
|
|
bool HandleJoystickHatEvent(const SDL_JoyHatEvent* ev);
|
|
bool HandleGamepadSensorEvent(const SDL_GamepadSensorEvent* ev);
|
|
void SendRumbleUpdate(ControllerData* cd);
|
|
|
|
static bool ControllerHasMicLED(SDL_Gamepad* gp);
|
|
static void SetControllerRGBLED(SDL_Gamepad* gp, bool has_rgb_led, const std::array<u32, 2>& colors, float intensity);
|
|
static void SetControllerMicMuteLED(SDL_Gamepad* gp, bool enabled);
|
|
|
|
ControllerDataVector m_controllers;
|
|
|
|
std::array<std::array<u32, 2>, MAX_LED_COLORS> m_led_colors{};
|
|
std::vector<std::pair<std::string, std::string>> m_sdl_hints;
|
|
|
|
bool m_sdl_subsystem_initialized = false;
|
|
bool m_controller_touchpad_as_pointer = false;
|
|
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
bool m_controller_enhanced_mode : 1;
|
|
bool m_controller_ps5_player_led : 1;
|
|
|
|
bool m_joystick_xbox_hidapi : 1;
|
|
|
|
#if defined(_WIN32)
|
|
bool m_joystick_rawinput : 1;
|
|
bool m_joystick_directinput : 1;
|
|
bool m_joystick_xinput : 1;
|
|
bool m_joystick_wgi : 1;
|
|
bool m_joystick_gameinput : 1;
|
|
#elif defined(__APPLE__)
|
|
bool m_enable_iokit_driver : 1;
|
|
bool m_enable_mfi_driver : 1;
|
|
#else
|
|
bool m_joystick_force_hat_input : 1;
|
|
#endif
|
|
};
|
|
|
|
u8 m_advanced_options_bits = 0;
|
|
};
|
|
};
|
|
|
|
class SDLForceFeedbackDevice : public ForceFeedbackDevice
|
|
{
|
|
public:
|
|
SDLForceFeedbackDevice(SDL_Joystick* joystick, SDL_Haptic* haptic);
|
|
~SDLForceFeedbackDevice() override;
|
|
|
|
void SetConstantForce(s32 level) override;
|
|
void DisableForce(Effect force) override;
|
|
|
|
private:
|
|
void CreateEffects(SDL_Joystick* joystick);
|
|
void DestroyEffects();
|
|
|
|
SDL_Haptic* m_haptic = nullptr;
|
|
|
|
SDL_HapticEffect m_constant_effect;
|
|
int m_constant_effect_id = -1;
|
|
bool m_constant_effect_running = false;
|
|
};
|