You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
Add support for SDL3 joystick input driver
Made possible by EIREXE, xsellier and the SDL team. This commit includes statically linked SDL3 for Windows, Linux and macOS. The vendored copy of SDL3 was setup to only build the required subsystems for gamepad/joystick support, with some patches to be able to make it as minimal as possible and reduce the impact on binary size and code size. Co-authored-by: Álex Román Núñez <eirexe123@gmail.com> Co-authored-by: Xavier Sellier <xsellier@gmail.com> Co-authored-by: Rémi Verschelde <rverschelde@gmail.com>
This commit is contained in:
committed by
Rémi Verschelde
parent
987832be46
commit
0b3496fb4f
209
drivers/sdl/SCsub
Normal file
209
drivers/sdl/SCsub
Normal file
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
env_sdl = env.Clone()
|
||||
|
||||
# Thirdparty source files
|
||||
|
||||
thirdparty_obj = []
|
||||
|
||||
if env["builtin_sdl"]:
|
||||
thirdparty_dir = "#thirdparty/sdl/"
|
||||
|
||||
# Use our own SDL_build_config_private.h.
|
||||
env_sdl.Prepend(CPPDEFINES=["SDL_PLATFORM_PRIVATE"])
|
||||
|
||||
# Common sources.
|
||||
|
||||
env_sdl.Prepend(
|
||||
CPPPATH=[
|
||||
thirdparty_dir,
|
||||
thirdparty_dir + "include",
|
||||
thirdparty_dir + "include/build_config",
|
||||
".", # SDL_build_config_private.h
|
||||
]
|
||||
)
|
||||
|
||||
thirdparty_sources = [
|
||||
"SDL.c",
|
||||
"SDL_assert.c",
|
||||
"SDL_error.c",
|
||||
"SDL_guid.c",
|
||||
"SDL_hashtable.c",
|
||||
"SDL_hints.c",
|
||||
"SDL_list.c",
|
||||
"SDL_log.c",
|
||||
"SDL_properties.c",
|
||||
"SDL_utils.c",
|
||||
"atomic/SDL_atomic.c",
|
||||
"atomic/SDL_spinlock.c",
|
||||
"events/SDL_events.c",
|
||||
"events/SDL_eventwatch.c",
|
||||
"haptic/SDL_haptic.c",
|
||||
"io/SDL_iostream.c",
|
||||
"joystick/SDL_gamepad.c",
|
||||
"joystick/SDL_joystick.c",
|
||||
"joystick/SDL_steam_virtual_gamepad.c",
|
||||
"joystick/controller_type.c",
|
||||
"libm/e_atan2.c",
|
||||
"libm/e_exp.c",
|
||||
"libm/e_fmod.c",
|
||||
"libm/e_log.c",
|
||||
"libm/e_log10.c",
|
||||
"libm/e_pow.c",
|
||||
"libm/e_rem_pio2.c",
|
||||
"libm/e_sqrt.c",
|
||||
"libm/k_cos.c",
|
||||
"libm/k_rem_pio2.c",
|
||||
"libm/k_sin.c",
|
||||
"libm/k_tan.c",
|
||||
"libm/s_atan.c",
|
||||
"libm/s_copysign.c",
|
||||
"libm/s_cos.c",
|
||||
"libm/s_fabs.c",
|
||||
"libm/s_floor.c",
|
||||
"libm/s_isinf.c",
|
||||
"libm/s_isinff.c",
|
||||
"libm/s_isnan.c",
|
||||
"libm/s_isnanf.c",
|
||||
"libm/s_modf.c",
|
||||
"libm/s_scalbn.c",
|
||||
"libm/s_sin.c",
|
||||
"libm/s_tan.c",
|
||||
"sensor/SDL_sensor.c",
|
||||
"sensor/dummy/SDL_dummysensor.c",
|
||||
"stdlib/SDL_crc16.c",
|
||||
"stdlib/SDL_crc32.c",
|
||||
"stdlib/SDL_getenv.c",
|
||||
"stdlib/SDL_iconv.c",
|
||||
"stdlib/SDL_malloc.c",
|
||||
"stdlib/SDL_memcpy.c",
|
||||
"stdlib/SDL_memmove.c",
|
||||
"stdlib/SDL_memset.c",
|
||||
"stdlib/SDL_mslibc.c",
|
||||
"stdlib/SDL_murmur3.c",
|
||||
"stdlib/SDL_qsort.c",
|
||||
"stdlib/SDL_random.c",
|
||||
"stdlib/SDL_stdlib.c",
|
||||
"stdlib/SDL_string.c",
|
||||
"stdlib/SDL_strtokr.c",
|
||||
"thread/SDL_thread.c",
|
||||
"thread/generic/SDL_syscond.c",
|
||||
"thread/generic/SDL_sysrwlock.c",
|
||||
"thread/generic/SDL_systhread.c",
|
||||
"timer/SDL_timer.c",
|
||||
]
|
||||
|
||||
# HIDAPI
|
||||
thirdparty_sources += [
|
||||
"hidapi/SDL_hidapi.c",
|
||||
"joystick/hidapi/SDL_hidapi_combined.c",
|
||||
"joystick/hidapi/SDL_hidapi_gamecube.c",
|
||||
"joystick/hidapi/SDL_hidapijoystick.c",
|
||||
"joystick/hidapi/SDL_hidapi_luna.c",
|
||||
"joystick/hidapi/SDL_hidapi_ps3.c",
|
||||
"joystick/hidapi/SDL_hidapi_ps4.c",
|
||||
"joystick/hidapi/SDL_hidapi_ps5.c",
|
||||
"joystick/hidapi/SDL_hidapi_rumble.c",
|
||||
"joystick/hidapi/SDL_hidapi_shield.c",
|
||||
"joystick/hidapi/SDL_hidapi_stadia.c",
|
||||
"joystick/hidapi/SDL_hidapi_steam.c",
|
||||
"joystick/hidapi/SDL_hidapi_steamdeck.c",
|
||||
"joystick/hidapi/SDL_hidapi_steam_hori.c",
|
||||
"joystick/hidapi/SDL_hidapi_switch.c",
|
||||
"joystick/hidapi/SDL_hidapi_wii.c",
|
||||
"joystick/hidapi/SDL_hidapi_xbox360.c",
|
||||
"joystick/hidapi/SDL_hidapi_xbox360w.c",
|
||||
"joystick/hidapi/SDL_hidapi_xboxone.c",
|
||||
]
|
||||
|
||||
# Platform specific sources.
|
||||
|
||||
if env["platform"] == "linuxbsd":
|
||||
# TODO: Check support for BSD systems.
|
||||
env_sdl.Append(CPPDEFINES=["SDL_PLATFORM_LINUX"])
|
||||
thirdparty_sources += [
|
||||
"core/linux/SDL_dbus.c",
|
||||
"core/linux/SDL_evdev.c",
|
||||
"core/linux/SDL_evdev_capabilities.c",
|
||||
"core/linux/SDL_evdev_kbd.c",
|
||||
"core/linux/SDL_fcitx.c",
|
||||
"core/linux/SDL_ibus.c",
|
||||
"core/linux/SDL_ime.c",
|
||||
"core/linux/SDL_system_theme.c",
|
||||
"core/linux/SDL_threadprio.c",
|
||||
"core/linux/SDL_udev.c",
|
||||
"core/unix/SDL_appid.c",
|
||||
"core/unix/SDL_poll.c",
|
||||
"haptic/linux/SDL_syshaptic.c",
|
||||
"joystick/linux/SDL_sysjoystick.c",
|
||||
"loadso/dlopen/SDL_sysloadso.c",
|
||||
"thread/pthread/SDL_syscond.c",
|
||||
"thread/pthread/SDL_sysmutex.c",
|
||||
"thread/pthread/SDL_sysrwlock.c",
|
||||
"thread/pthread/SDL_syssem.c",
|
||||
"thread/pthread/SDL_systhread.c",
|
||||
"thread/pthread/SDL_systls.c",
|
||||
"timer/unix/SDL_systimer.c",
|
||||
]
|
||||
|
||||
elif env["platform"] == "macos":
|
||||
env_sdl.Append(CPPDEFINES=["SDL_PLATFORM_MACOS"])
|
||||
thirdparty_sources += [
|
||||
"core/unix/SDL_appid.c",
|
||||
"core/unix/SDL_poll.c",
|
||||
"haptic/darwin/SDL_syshaptic.c",
|
||||
"joystick/darwin/SDL_iokitjoystick.c",
|
||||
"joystick/apple/SDL_mfijoystick.m",
|
||||
"thread/pthread/SDL_syscond.c",
|
||||
"thread/pthread/SDL_sysmutex.c",
|
||||
"thread/pthread/SDL_sysrwlock.c",
|
||||
"thread/pthread/SDL_syssem.c",
|
||||
"thread/pthread/SDL_systhread.c",
|
||||
"thread/pthread/SDL_systls.c",
|
||||
"timer/unix/SDL_systimer.c",
|
||||
]
|
||||
|
||||
elif env["platform"] == "windows":
|
||||
env_sdl.Append(CPPDEFINES=["SDL_PLATFORM_WINDOWS"])
|
||||
thirdparty_sources += [
|
||||
"core/windows/SDL_gameinput.c",
|
||||
"core/windows/SDL_hid.c",
|
||||
"core/windows/SDL_immdevice.c",
|
||||
"core/windows/SDL_windows.c",
|
||||
"core/windows/SDL_xinput.c",
|
||||
"core/windows/pch.c",
|
||||
"haptic/windows/SDL_dinputhaptic.c",
|
||||
"haptic/windows/SDL_windowshaptic.c",
|
||||
"joystick/windows/SDL_dinputjoystick.c",
|
||||
"joystick/windows/SDL_rawinputjoystick.c",
|
||||
"joystick/windows/SDL_windows_gaming_input.c",
|
||||
"joystick/windows/SDL_windowsjoystick.c",
|
||||
"joystick/windows/SDL_xinputjoystick.c",
|
||||
"thread/windows/SDL_syscond_cv.c",
|
||||
"thread/windows/SDL_sysmutex.c",
|
||||
"thread/windows/SDL_sysrwlock_srw.c",
|
||||
"thread/windows/SDL_syssem.c",
|
||||
"thread/windows/SDL_systhread.c",
|
||||
"thread/windows/SDL_systls.c",
|
||||
"timer/windows/SDL_systimer.c",
|
||||
]
|
||||
|
||||
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
|
||||
|
||||
env_thirdparty = env_sdl.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
|
||||
env.drivers_sources += thirdparty_obj
|
||||
|
||||
# Godot source files
|
||||
|
||||
driver_obj = []
|
||||
|
||||
env_sdl.add_source_files(driver_obj, "*.cpp")
|
||||
env.drivers_sources += driver_obj
|
||||
|
||||
# Needed to force rebuilding the driver files when the thirdparty library is updated.
|
||||
env.Depends(driver_obj, thirdparty_obj)
|
||||
130
drivers/sdl/SDL_build_config_private.h
Normal file
130
drivers/sdl/SDL_build_config_private.h
Normal file
@@ -0,0 +1,130 @@
|
||||
/**************************************************************************/
|
||||
/* SDL_build_config_private.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define SDL_build_config_h_
|
||||
|
||||
#include <SDL3/SDL_platform_defines.h>
|
||||
|
||||
#define HAVE_STDARG_H 1
|
||||
#define HAVE_STDDEF_H 1
|
||||
|
||||
// Here we disable SDL subsystems that are not going to be used
|
||||
#define SDL_CPUINFO_DISABLED 1
|
||||
#define SDL_AUDIO_DISABLED 1
|
||||
#define SDL_PROCESS_DUMMY 1
|
||||
#define SDL_LOADSO_DUMMY 1
|
||||
#define SDL_VIDEO_DISABLED 1
|
||||
#define SDL_CAMERA_DISABLED 1
|
||||
#define SDL_DIALOG_DISABLED 1
|
||||
#define SDL_FILESYSTEM_DUMMY 1
|
||||
#define SDL_FSOPS_DUMMY 1
|
||||
#define SDL_SENSOR_DISABLED 1
|
||||
#define SDL_GPU_DISABLED 1
|
||||
#define SDL_RENDER_DISABLED 1
|
||||
#define SDL_POWER_DISABLED 1
|
||||
#define SDL_LEAN_AND_MEAN 1
|
||||
|
||||
// Windows defines
|
||||
#if defined(SDL_PLATFORM_WINDOWS)
|
||||
|
||||
#define SDL_PLATFORM_PRIVATE_NAME "Windows"
|
||||
#define HAVE_LIBC 1
|
||||
#define HAVE_DINPUT_H 1
|
||||
#define HAVE_XINPUT_H 1
|
||||
#if defined(_WIN32_MAXVER) && _WIN32_MAXVER >= 0x0A00 /* Windows 10 SDK */
|
||||
#define HAVE_WINDOWS_GAMING_INPUT_H 1
|
||||
#define SDL_JOYSTICK_WGI 1
|
||||
#endif
|
||||
#define SDL_JOYSTICK_DINPUT 1
|
||||
#define SDL_JOYSTICK_HIDAPI 1
|
||||
#define SDL_JOYSTICK_RAWINPUT 1
|
||||
#define SDL_JOYSTICK_XINPUT 1
|
||||
#define SDL_HAPTIC_DINPUT 1
|
||||
#define SDL_THREAD_GENERIC_COND_SUFFIX 1
|
||||
#define SDL_THREAD_GENERIC_RWLOCK_SUFFIX 1
|
||||
#define SDL_THREAD_WINDOWS 1
|
||||
#define SDL_TIMER_WINDOWS 1
|
||||
|
||||
// Linux defines
|
||||
#elif defined(SDL_PLATFORM_LINUX)
|
||||
|
||||
#define SDL_PLATFORM_PRIVATE_NAME "Linux"
|
||||
#define SDL_PLATFORM_UNIX 1
|
||||
|
||||
#define HAVE_STDIO_H 1
|
||||
#define HAVE_LINUX_INPUT_H 1
|
||||
#define HAVE_POLL 1
|
||||
|
||||
// TODO: handle dynamic loading with SOWRAP_ENABLED
|
||||
|
||||
// (even though DBus can also be loaded with SOWRAP_ENABLED, we load it
|
||||
// statically regardless of SOWRAP_ENABLED, because otherwise SDL won't compile)
|
||||
#ifdef DBUS_ENABLED
|
||||
#define HAVE_DBUS_DBUS_H 1
|
||||
#endif
|
||||
|
||||
#if defined(UDEV_ENABLED) && !defined(SOWRAP_ENABLED)
|
||||
#define HAVE_LIBUDEV_H 1
|
||||
#endif
|
||||
|
||||
#define SDL_LOADSO_DLOPEN 1
|
||||
#define SDL_HAPTIC_LINUX 1
|
||||
#define SDL_TIMER_UNIX 1
|
||||
#define SDL_JOYSTICK_LINUX 1
|
||||
#define SDL_INPUT_LINUXEV 1
|
||||
#define SDL_THREAD_PTHREAD 1
|
||||
|
||||
// MacOS defines
|
||||
#elif defined(SDL_PLATFORM_MACOS)
|
||||
|
||||
#define SDL_PLATFORM_PRIVATE_NAME "macOS"
|
||||
#define SDL_PLATFORM_UNIX 1
|
||||
#define HAVE_STDIO_H 1
|
||||
#define HAVE_LIBC 1
|
||||
#define SDL_HAPTIC_IOKIT 1
|
||||
#define SDL_JOYSTICK_IOKIT 1
|
||||
#define SDL_JOYSTICK_MFI 1
|
||||
#define SDL_TIMER_UNIX 1
|
||||
#define SDL_THREAD_PTHREAD 1
|
||||
|
||||
// Other platforms are not supported (for now)
|
||||
#else
|
||||
#error "No SDL build config was found for this platform. Setup one before compiling the engine."
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_STDINT_H) && !defined(_STDINT_H_)
|
||||
#define HAVE_STDINT_H 1
|
||||
#endif /* !_STDINT_H_ && !HAVE_STDINT_H */
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define HAVE_GCC_SYNC_LOCK_TEST_AND_SET 1
|
||||
#endif
|
||||
273
drivers/sdl/joypad_sdl.cpp
Normal file
273
drivers/sdl/joypad_sdl.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
/**************************************************************************/
|
||||
/* joypad_sdl.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "joypad_sdl.h"
|
||||
|
||||
#ifdef SDL_ENABLED
|
||||
|
||||
#include "core/input/default_controller_mappings.h"
|
||||
#include "core/os/time.h"
|
||||
#include "core/variant/dictionary.h"
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_error.h>
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <SDL3/SDL_gamepad.h>
|
||||
#include <SDL3/SDL_iostream.h>
|
||||
#include <SDL3/SDL_joystick.h>
|
||||
|
||||
JoypadSDL *JoypadSDL::singleton = nullptr;
|
||||
|
||||
// Macro to skip the SDL joystick event handling if the device is an SDL gamepad, because
|
||||
// there are separate events for SDL gamepads
|
||||
#define SKIP_EVENT_FOR_GAMEPAD \
|
||||
if (SDL_IsGamepad(sdl_event.jdevice.which)) { \
|
||||
continue; \
|
||||
}
|
||||
|
||||
JoypadSDL::JoypadSDL() {
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
JoypadSDL::~JoypadSDL() {
|
||||
// Process any remaining input events
|
||||
process_events();
|
||||
for (int i = 0; i < Input::JOYPADS_MAX; i++) {
|
||||
if (joypads[i].attached) {
|
||||
close_joypad(i);
|
||||
}
|
||||
}
|
||||
SDL_Quit();
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
||||
JoypadSDL *JoypadSDL::get_singleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
Error JoypadSDL::initialize() {
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_THREAD, "1");
|
||||
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
|
||||
ERR_FAIL_COND_V_MSG(!SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD), FAILED, SDL_GetError());
|
||||
|
||||
// Add Godot's mapping database from memory
|
||||
int i = 0;
|
||||
while (DefaultControllerMappings::mappings[i]) {
|
||||
String mapping_string = DefaultControllerMappings::mappings[i++];
|
||||
CharString data = mapping_string.utf8();
|
||||
SDL_IOStream *rw = SDL_IOFromMem((void *)data.ptr(), data.size());
|
||||
SDL_AddGamepadMappingsFromIO(rw, 1);
|
||||
}
|
||||
|
||||
print_verbose("SDL: Init OK!");
|
||||
return OK;
|
||||
}
|
||||
|
||||
void JoypadSDL::process_events() {
|
||||
// Update rumble first for it to be applied when we handle SDL events
|
||||
for (int i = 0; i < Input::JOYPADS_MAX; i++) {
|
||||
Joypad &joy = joypads[i];
|
||||
if (joy.attached && joy.supports_force_feedback) {
|
||||
uint64_t timestamp = Input::get_singleton()->get_joy_vibration_timestamp(i);
|
||||
|
||||
// Update the joypad rumble only if there was a new vibration request
|
||||
if (timestamp > joy.ff_effect_timestamp) {
|
||||
joy.ff_effect_timestamp = timestamp;
|
||||
|
||||
SDL_Joystick *sdl_joy = SDL_GetJoystickFromID(joypads[i].sdl_instance_idx);
|
||||
Vector2 strength = Input::get_singleton()->get_joy_vibration_strength(i);
|
||||
|
||||
// If the vibration was requested to start, SDL_RumbleJoystick will start it.
|
||||
// If the vibration was requested to stop, strength and duration will be 0, so SDL will stop the rumble.
|
||||
SDL_RumbleJoystick(
|
||||
sdl_joy,
|
||||
// Rumble strength goes from 0 to 0xFFFF
|
||||
strength.x * UINT16_MAX,
|
||||
strength.y * UINT16_MAX,
|
||||
Input::get_singleton()->get_joy_vibration_duration(i) * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Event sdl_event;
|
||||
while (SDL_PollEvent(&sdl_event)) {
|
||||
// A new joypad was attached
|
||||
if (sdl_event.type == SDL_EVENT_JOYSTICK_ADDED) {
|
||||
int joy_id = Input::get_singleton()->get_unused_joy_id();
|
||||
if (joy_id == -1) {
|
||||
// There is no space for more joypads...
|
||||
print_error("A new joypad was attached but couldn't allocate a new id for it because joypad limit was reached.");
|
||||
} else {
|
||||
SDL_Joystick *joy = nullptr;
|
||||
String device_name;
|
||||
|
||||
// Gamepads must be opened with SDL_OpenGamepad to get their special remapped events
|
||||
if (SDL_IsGamepad(sdl_event.jdevice.which)) {
|
||||
SDL_Gamepad *gamepad = SDL_OpenGamepad(sdl_event.jdevice.which);
|
||||
|
||||
ERR_CONTINUE_MSG(!gamepad,
|
||||
vformat("Error opening gamepad at index %d: %s", sdl_event.jdevice.which, SDL_GetError()));
|
||||
|
||||
device_name = SDL_GetGamepadName(gamepad);
|
||||
joy = SDL_GetGamepadJoystick(gamepad);
|
||||
|
||||
print_verbose(vformat("SDL: Gamepad %s connected", SDL_GetGamepadName(gamepad)));
|
||||
} else {
|
||||
joy = SDL_OpenJoystick(sdl_event.jdevice.which);
|
||||
ERR_CONTINUE_MSG(!joy,
|
||||
vformat("Error opening joystick at index %d: %s", sdl_event.jdevice.which, SDL_GetError()));
|
||||
|
||||
device_name = SDL_GetJoystickName(joy);
|
||||
|
||||
print_verbose(vformat("SDL: Joystick %s connected", SDL_GetJoystickName(joy)));
|
||||
}
|
||||
|
||||
const int MAX_GUID_SIZE = 64;
|
||||
char guid[MAX_GUID_SIZE] = {};
|
||||
|
||||
SDL_GUIDToString(SDL_GetJoystickGUID(joy), guid, MAX_GUID_SIZE);
|
||||
SDL_PropertiesID propertiesID = SDL_GetJoystickProperties(joy);
|
||||
|
||||
joypads[joy_id].attached = true;
|
||||
joypads[joy_id].sdl_instance_idx = sdl_event.jdevice.which;
|
||||
joypads[joy_id].supports_force_feedback = SDL_GetBooleanProperty(propertiesID, SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, false);
|
||||
joypads[joy_id].guid = StringName(String(guid));
|
||||
|
||||
sdl_instance_id_to_joypad_id.insert(sdl_event.jdevice.which, joy_id);
|
||||
|
||||
// Skip Godot's mapping system because SDL already handles the joypad's mapping
|
||||
Dictionary joypad_info;
|
||||
joypad_info["mapping_handled"] = true;
|
||||
|
||||
Input::get_singleton()->joy_connection_changed(
|
||||
joy_id,
|
||||
true,
|
||||
device_name,
|
||||
joypads[joy_id].guid,
|
||||
joypad_info);
|
||||
}
|
||||
// An event for an attached joypad
|
||||
} else if (sdl_event.type >= SDL_EVENT_JOYSTICK_AXIS_MOTION && sdl_event.type < SDL_EVENT_FINGER_DOWN && sdl_instance_id_to_joypad_id.has(sdl_event.jdevice.which)) {
|
||||
int joy_id = sdl_instance_id_to_joypad_id.get(sdl_event.jdevice.which);
|
||||
|
||||
switch (sdl_event.type) {
|
||||
case SDL_EVENT_JOYSTICK_REMOVED:
|
||||
Input::get_singleton()->joy_connection_changed(joy_id, false, "");
|
||||
close_joypad(joy_id);
|
||||
break;
|
||||
|
||||
case SDL_EVENT_JOYSTICK_AXIS_MOTION:
|
||||
SKIP_EVENT_FOR_GAMEPAD;
|
||||
|
||||
Input::get_singleton()->joy_axis(
|
||||
joy_id,
|
||||
static_cast<JoyAxis>(sdl_event.jaxis.axis), // Godot joy axis constants are already intentionally the same as SDL's
|
||||
((sdl_event.jaxis.value - SDL_JOYSTICK_AXIS_MIN) / (float)(SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN) - 0.5f) * 2.0f);
|
||||
break;
|
||||
|
||||
case SDL_EVENT_JOYSTICK_BUTTON_UP:
|
||||
case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
|
||||
SKIP_EVENT_FOR_GAMEPAD;
|
||||
|
||||
Input::get_singleton()->joy_button(
|
||||
joy_id,
|
||||
static_cast<JoyButton>(sdl_event.jbutton.button), // Godot button constants are intentionally the same as SDL's, so we can just straight up use them
|
||||
sdl_event.jbutton.down);
|
||||
break;
|
||||
|
||||
case SDL_EVENT_JOYSTICK_HAT_MOTION:
|
||||
SKIP_EVENT_FOR_GAMEPAD;
|
||||
|
||||
Input::get_singleton()->joy_hat(
|
||||
joy_id,
|
||||
(HatMask)sdl_event.jhat.value // Godot hat masks are identical to SDL hat masks, so we can just use them as-is.
|
||||
);
|
||||
break;
|
||||
|
||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION: {
|
||||
float axis_value;
|
||||
|
||||
if (sdl_event.gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || sdl_event.gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) {
|
||||
// Gamepad triggers go from 0 to SDL_JOYSTICK_AXIS_MAX
|
||||
axis_value = sdl_event.gaxis.value / (float)SDL_JOYSTICK_AXIS_MAX;
|
||||
} else {
|
||||
// Other axis go from SDL_JOYSTICK_AXIS_MIN to SDL_JOYSTICK_AXIS_MAX
|
||||
axis_value =
|
||||
((sdl_event.gaxis.value - SDL_JOYSTICK_AXIS_MIN) / (float)(SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN) - 0.5f) * 2.0f;
|
||||
}
|
||||
|
||||
Input::get_singleton()->joy_axis(
|
||||
joy_id,
|
||||
static_cast<JoyAxis>(sdl_event.gaxis.axis), // Godot joy axis constants are already intentionally the same as SDL's
|
||||
axis_value);
|
||||
} break;
|
||||
|
||||
// Do note SDL gamepads do not have separate events for the dpad
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||
Input::get_singleton()->joy_button(
|
||||
joy_id,
|
||||
static_cast<JoyButton>(sdl_event.gbutton.button), // Godot button constants are intentionally the same as SDL's, so we can just straight up use them
|
||||
sdl_event.gbutton.down);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
extern "C" {
|
||||
HWND SDL_HelperWindow;
|
||||
}
|
||||
|
||||
// Required for DInput joypads to work
|
||||
void JoypadSDL::setup_sdl_helper_window(HWND p_hwnd) {
|
||||
SDL_HelperWindow = p_hwnd;
|
||||
}
|
||||
#endif
|
||||
|
||||
void JoypadSDL::close_joypad(int p_pad_idx) {
|
||||
int sdl_instance_idx = joypads[p_pad_idx].sdl_instance_idx;
|
||||
|
||||
joypads[p_pad_idx].attached = false;
|
||||
sdl_instance_id_to_joypad_id.erase(sdl_instance_idx);
|
||||
|
||||
if (SDL_IsGamepad(sdl_instance_idx)) {
|
||||
SDL_Gamepad *gamepad = SDL_GetGamepadFromID(sdl_instance_idx);
|
||||
SDL_CloseGamepad(gamepad);
|
||||
} else {
|
||||
SDL_Joystick *joy = SDL_GetJoystickFromID(sdl_instance_idx);
|
||||
SDL_CloseJoystick(joy);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDL_ENABLED
|
||||
69
drivers/sdl/joypad_sdl.h
Normal file
69
drivers/sdl/joypad_sdl.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/**************************************************************************/
|
||||
/* joypad_sdl.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/input/input.h"
|
||||
#include "core/os/thread.h"
|
||||
|
||||
typedef uint32_t SDL_JoystickID;
|
||||
typedef struct HWND__ *HWND;
|
||||
|
||||
class JoypadSDL {
|
||||
public:
|
||||
JoypadSDL();
|
||||
~JoypadSDL();
|
||||
|
||||
static JoypadSDL *get_singleton();
|
||||
|
||||
Error initialize();
|
||||
void process_events();
|
||||
#ifdef WINDOWS_ENABLED
|
||||
void setup_sdl_helper_window(HWND p_hwnd);
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct Joypad {
|
||||
bool attached = false;
|
||||
StringName guid;
|
||||
|
||||
SDL_JoystickID sdl_instance_idx;
|
||||
|
||||
bool supports_force_feedback = false;
|
||||
uint64_t ff_effect_timestamp = 0;
|
||||
};
|
||||
|
||||
static JoypadSDL *singleton;
|
||||
|
||||
Joypad joypads[Input::JOYPADS_MAX];
|
||||
HashMap<SDL_JoystickID, int> sdl_instance_id_to_joypad_id;
|
||||
|
||||
void close_joypad(int p_pad_idx);
|
||||
};
|
||||
Reference in New Issue
Block a user