diff --git a/core/input/input.cpp b/core/input/input.cpp index e521bf16a765..0684e9314741 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -147,6 +147,10 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer); ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer); ClassDB::bind_method(D_METHOD("get_gyroscope"), &Input::get_gyroscope); + ClassDB::bind_method(D_METHOD("get_joy_touchpad_finger_position", "device", "touchpad", "finger"), &Input::get_joy_touchpad_finger_position); + ClassDB::bind_method(D_METHOD("get_joy_touchpad_finger_pressure", "device", "touchpad", "finger"), &Input::get_joy_touchpad_finger_pressure); + ClassDB::bind_method(D_METHOD("get_joy_touchpad_fingers", "device", "touchpad"), &Input::get_joy_touchpad_fingers); + ClassDB::bind_method(D_METHOD("get_joy_num_touchpads", "device"), &Input::get_joy_num_touchpads); ClassDB::bind_method(D_METHOD("set_gravity", "value"), &Input::set_gravity); ClassDB::bind_method(D_METHOD("set_accelerometer", "value"), &Input::set_accelerometer); ClassDB::bind_method(D_METHOD("set_magnetometer", "value"), &Input::set_magnetometer); @@ -731,6 +735,43 @@ Vector3 Input::get_gyroscope() const { return gyroscope; } +Vector2 Input::get_joy_touchpad_finger_position(int p_device, int p_touchpad, int p_finger) const { + _THREAD_SAFE_METHOD_ + if (!joy_touch.has(p_device) || !joy_touch[p_device].touchpad_fingers.has(p_touchpad) || !joy_touch[p_device].touchpad_fingers[p_touchpad].has(p_finger)) { + return Vector2(); + } + return joy_touch[p_device].touchpad_fingers[p_touchpad][p_finger].position; +} + +float Input::get_joy_touchpad_finger_pressure(int p_device, int p_touchpad, int p_finger) const { + _THREAD_SAFE_METHOD_ + if (!joy_touch.has(p_device) || !joy_touch[p_device].touchpad_fingers.has(p_touchpad) || !joy_touch[p_device].touchpad_fingers[p_touchpad].has(p_finger)) { + return 0.0f; + } + return joy_touch[p_device].touchpad_fingers[p_touchpad][p_finger].pressure; +} + +TypedArray Input::get_joy_touchpad_fingers(int p_device, int p_touchpad) const { + _THREAD_SAFE_METHOD_ + if (!joy_touch.has(p_device) || !joy_touch[p_device].touchpad_fingers.has(p_touchpad)) { + return TypedArray(); + } + + TypedArray result; + for (auto &i : joy_touch[p_device].touchpad_fingers[p_touchpad]) { + result.append(i.key); + } + return result; +} + +int Input::get_joy_num_touchpads(int p_device) const { + _THREAD_SAFE_METHOD_ + if (!joy_touch.has(p_device)) { + return 0; + } + return joy_touch[p_device].num_touchpads; +} + void Input::_parse_input_event_impl(const Ref &p_event, bool p_is_emulated) { // This function does the final delivery of the input event to user land. // Regardless where the event came from originally, this has to happen on the main thread. @@ -986,6 +1027,24 @@ void Input::set_joy_axis(int p_device, JoyAxis p_axis, float p_value) { _joy_axis[c] = p_value; } +void Input::set_joy_features(int p_device, JoypadFeatures *p_features) { + if (!joy_names.has(p_device)) { + return; + } + joy_names[p_device].features = p_features; + _update_joypad_features(p_device); +} + +void Input::set_joy_touchpad_finger(int p_device, int p_touchpad, int p_finger, float p_pressure, Vector2 p_value) { + _THREAD_SAFE_METHOD_ + if (p_pressure > 0.0f) { + joy_touch[p_device].touchpad_fingers[p_touchpad][p_finger] = TouchpadFingerInfo{ p_value, p_pressure }; + } else { + joy_touch[p_device].touchpad_fingers[p_touchpad].erase(p_finger); + } + // TODO: event +} + void Input::start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration) { _THREAD_SAFE_METHOD_ if (p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) { @@ -1478,6 +1537,15 @@ void Input::_update_action_cache(const StringName &p_action_name, ActionState &r } } +void Input::_update_joypad_features(int p_device) { + if (!joy_names.has(p_device) || joy_names[p_device].features == nullptr) { + return; + } + if (joy_names[p_device].features->get_joy_num_touchpads() > 0) { + joy_touch[p_device].num_touchpads = joy_names[p_device].features->get_joy_num_touchpads(); + } +} + Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button) { JoyEvent event; diff --git a/core/input/input.h b/core/input/input.h index 8e8e0098e5d1..e4e3a6375866 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -79,6 +79,13 @@ class Input : public Object { CURSOR_MAX }; + class JoypadFeatures { + public: + virtual ~JoypadFeatures() {} + + virtual int get_joy_num_touchpads() const { return 0; } + }; + static constexpr int32_t JOYPADS_MAX = 16; typedef void (*EventDispatchFunc)(const Ref &p_event); @@ -149,6 +156,19 @@ class Input : public Object { HashMap joy_vibration; + struct TouchpadFingerInfo { + Vector2 position = Vector2(); + float pressure = 0.0f; + }; + + struct TouchpadInfo { + int num_touchpads = 0; + // The first int index refers to touchpad ID, the second one refers to finger ID on that touchpad. + HashMap> touchpad_fingers; + }; + + HashMap joy_touch; + struct VelocityTrack { uint64_t last_tick = 0; Vector2 velocity; @@ -174,6 +194,7 @@ class Input : public Object { int mapping = -1; int hat_current = 0; Dictionary info; + Input::JoypadFeatures *features; }; VelocityTrack mouse_velocity_track; @@ -253,6 +274,7 @@ class Input : public Object { void _button_event(int p_device, JoyButton p_index, bool p_pressed); void _axis_event(int p_device, JoyAxis p_axis, float p_value); void _update_action_cache(const StringName &p_action_name, ActionState &r_action_state); + void _update_joypad_features(int p_device); void _parse_input_event_impl(const Ref &p_event, bool p_is_emulated); @@ -330,6 +352,12 @@ class Input : public Object { Vector3 get_magnetometer() const; Vector3 get_gyroscope() const; + Vector2 get_joy_touchpad_finger_position(int p_device, int p_touchpad, int p_finger) const; + float get_joy_touchpad_finger_pressure(int p_device, int p_touchpad, int p_finger) const; + TypedArray get_joy_touchpad_fingers(int p_device, int p_touchpad) const; + + int get_joy_num_touchpads(int p_device) const; + Point2 get_mouse_position() const; Vector2 get_last_mouse_velocity(); Vector2 get_last_mouse_screen_velocity(); @@ -346,6 +374,10 @@ class Input : public Object { void set_gyroscope(const Vector3 &p_gyroscope); void set_joy_axis(int p_device, JoyAxis p_axis, float p_value); + void set_joy_features(int p_device, JoypadFeatures *p_features); + + void set_joy_touchpad_finger(int p_device, int p_touchpad, int p_finger, float p_pressure, Vector2 p_value); + void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0); void stop_joy_vibration(int p_device); void vibrate_handheld(int p_duration_ms = 500, float p_amplitude = -1.0); diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index a8523b3ec7d3..6a86f6242c2e 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -147,6 +147,49 @@ Returns the name of the joypad at the specified device index, e.g. [code]PS4 Controller[/code]. Godot uses the [url=https://github.com/gabomdq/SDL_GameControllerDB]SDL2 game controller database[/url] to determine gamepad names. + + + + + Returns the number of touchpads on the joypad. + [b]Note:[/b] This method may return inaccurate results if this joypad is not in Godot's controller database. + [b]Note:[/b] This feature is only supported on Windows, Linux, and macOS. + + + + + + + + + Returns the position of the specified finger on the specified touchpad on the joypad. The X and Y values are in the range 0.0 to 1.0, with [code](0.0, 0.0)[/code] representing the top left corner of the touchpad and [code](1.0, 1.0)[/code] representing the bottom right corner. + If the joypad doesn't have the specified touchpad or the specified finger is not currently touching the touchpad, this method returns [constant Vector2.ZERO]. + [b]Note:[/b] This feature is only supported on Windows, Linux, and macOS. + + + + + + + + + Returns the pressure of the specified finger on the specified touchpad on the joypad. The pressure values are in range between 0.0 and 1.0. + If the touchpad doesn't support pressure sensitivity and the specified finger is currently touching the touchpad, this method returns [code]1.0[/code]. + If the joypad doesn't have the specified touchpad or the specified finger is not currently touching the touchpad, this method returns [code]0.0[/code]. + [b]Note:[/b] This feature is only supported on Windows, Linux, and macOS. + + + + + + + + Returns an array of finger IDs that are currently touching the specified touchpad on the joypad. + If the joypad doesn't have the specified touchpad or no fingers are currently touching this touchpad, this method returns an empty array. + [b]Note:[/b] To retrieve the positions of the touchpad fingers, use [method get_joy_touchpad_finger_position], and to retrieve their pressure value (if supported), use [method get_joy_touchpad_finger_pressure]. + [b]Note:[/b] This feature is only supported on Windows, Linux, and macOS. + + diff --git a/drivers/sdl/SCsub b/drivers/sdl/SCsub index dfd0807b7b66..a3631fedd6d1 100644 --- a/drivers/sdl/SCsub +++ b/drivers/sdl/SCsub @@ -178,6 +178,7 @@ if env["builtin_sdl"]: "joystick/windows/SDL_xinputjoystick.c", "thread/generic/SDL_syscond.c", "thread/generic/SDL_sysrwlock.c", + "sensor/windows/SDL_windowssensor.c", "thread/windows/SDL_syscond_cv.c", "thread/windows/SDL_sysmutex.c", "thread/windows/SDL_sysrwlock_srw.c", diff --git a/drivers/sdl/SDL_build_config_private.h b/drivers/sdl/SDL_build_config_private.h index ed0cc09c5ba3..d6e6cb72cc87 100644 --- a/drivers/sdl/SDL_build_config_private.h +++ b/drivers/sdl/SDL_build_config_private.h @@ -47,7 +47,6 @@ #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 @@ -73,6 +72,7 @@ #define SDL_THREAD_GENERIC_RWLOCK_SUFFIX 1 #define SDL_THREAD_WINDOWS 1 #define SDL_TIMER_WINDOWS 1 +#define SDL_SENSOR_WINDOWS 1 // Linux defines #elif defined(SDL_PLATFORM_LINUX) @@ -113,6 +113,7 @@ #define SDL_HAPTIC_LINUX 1 #define SDL_TIMER_UNIX 1 #define SDL_JOYSTICK_LINUX 1 +#define SDL_JOYSTICK_HIDAPI 1 #define SDL_INPUT_LINUXEV 1 #define SDL_THREAD_PTHREAD 1 @@ -126,8 +127,10 @@ #define SDL_HAPTIC_IOKIT 1 #define SDL_JOYSTICK_IOKIT 1 #define SDL_JOYSTICK_MFI 1 +#define SDL_JOYSTICK_HIDAPI 1 #define SDL_TIMER_UNIX 1 #define SDL_THREAD_PTHREAD 1 +#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX 1 // Other platforms are not supported (for now) #else diff --git a/drivers/sdl/joypad_sdl.cpp b/drivers/sdl/joypad_sdl.cpp index 43070a4b3415..086b0462c7c4 100644 --- a/drivers/sdl/joypad_sdl.cpp +++ b/drivers/sdl/joypad_sdl.cpp @@ -208,6 +208,8 @@ void JoypadSDL::process_events() { device_name, joypads[joy_id].guid, joypad_info); + + Input::get_singleton()->set_joy_features(joy_id, &joypads[joy_id]); } // 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)) { @@ -279,6 +281,17 @@ void JoypadSDL::process_events() { static_cast(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; + + case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: + case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: + case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: + Input::get_singleton()->set_joy_touchpad_finger( + joy_id, + sdl_event.gtouchpad.touchpad, + sdl_event.gtouchpad.finger, + sdl_event.gtouchpad.pressure, + Vector2(sdl_event.gtouchpad.x, sdl_event.gtouchpad.y)); + break; } } } @@ -299,4 +312,16 @@ void JoypadSDL::close_joypad(int p_pad_idx) { } } +int JoypadSDL::Joypad::get_joy_num_touchpads() const { + return SDL_GetNumGamepadTouchpads(get_sdl_gamepad()); +} + +SDL_Joystick *JoypadSDL::Joypad::get_sdl_joystick() const { + return SDL_GetJoystickFromID(sdl_instance_idx); +} + +SDL_Gamepad *JoypadSDL::Joypad::get_sdl_gamepad() const { + return SDL_GetGamepadFromID(sdl_instance_idx); +} + #endif // SDL_ENABLED diff --git a/drivers/sdl/joypad_sdl.h b/drivers/sdl/joypad_sdl.h index 2e3661337e72..dcf525ed62ec 100644 --- a/drivers/sdl/joypad_sdl.h +++ b/drivers/sdl/joypad_sdl.h @@ -35,6 +35,8 @@ typedef uint32_t SDL_JoystickID; typedef struct HWND__ *HWND; +typedef struct SDL_Joystick SDL_Joystick; +typedef struct SDL_Gamepad SDL_Gamepad; class JoypadSDL { public: @@ -50,7 +52,8 @@ class JoypadSDL { void process_events(); private: - struct Joypad { + class Joypad : public Input::JoypadFeatures { + public: bool attached = false; StringName guid; @@ -58,6 +61,11 @@ class JoypadSDL { bool supports_force_feedback = false; uint64_t ff_effect_timestamp = 0; + + int get_joy_num_touchpads() const override; + + SDL_Joystick *get_sdl_joystick() const; + SDL_Gamepad *get_sdl_gamepad() const; }; static JoypadSDL *singleton; diff --git a/thirdparty/sdl/sensor/windows/SDL_windowssensor.c b/thirdparty/sdl/sensor/windows/SDL_windowssensor.c new file mode 100644 index 000000000000..059747eeb420 --- /dev/null +++ b/thirdparty/sdl/sensor/windows/SDL_windowssensor.c @@ -0,0 +1,485 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_SENSOR_WINDOWS + +#include "SDL_windowssensor.h" +#include "../SDL_syssensor.h" +#include "../../core/windows/SDL_windows.h" + +#define COBJMACROS +#include +#include +#include + +DEFINE_GUID(SDL_CLSID_SensorManager, 0x77A1C827, 0xFCD2, 0x4689, 0x89, 0x15, 0x9D, 0x61, 0x3C, 0xC5, 0xFA, 0x3E); +DEFINE_GUID(SDL_IID_SensorManager, 0xBD77DB67, 0x45A8, 0x42DC, 0x8D, 0x00, 0x6D, 0xCF, 0x15, 0xF8, 0x37, 0x7A); +DEFINE_GUID(SDL_IID_SensorManagerEvents, 0x9B3B0B86, 0x266A, 0x4AAD, 0xB2, 0x1F, 0xFD, 0xE5, 0x50, 0x10, 0x01, 0xB7); +DEFINE_GUID(SDL_IID_SensorEvents, 0x5D8DCC91, 0x4641, 0x47E7, 0xB7, 0xC3, 0xB7, 0x4F, 0x48, 0xA6, 0xC3, 0x91); + +// These constants aren't available in Visual Studio 2015 or earlier Windows SDK +DEFINE_PROPERTYKEY(SDL_SENSOR_DATA_TYPE_ANGULAR_VELOCITY_X_DEGREES_PER_SECOND, 0X3F8A69A2, 0X7C5, 0X4E48, 0XA9, 0X65, 0XCD, 0X79, 0X7A, 0XAB, 0X56, 0XD5, 10); //[VT_R8] +DEFINE_PROPERTYKEY(SDL_SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Y_DEGREES_PER_SECOND, 0X3F8A69A2, 0X7C5, 0X4E48, 0XA9, 0X65, 0XCD, 0X79, 0X7A, 0XAB, 0X56, 0XD5, 11); //[VT_R8] +DEFINE_PROPERTYKEY(SDL_SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Z_DEGREES_PER_SECOND, 0X3F8A69A2, 0X7C5, 0X4E48, 0XA9, 0X65, 0XCD, 0X79, 0X7A, 0XAB, 0X56, 0XD5, 12); //[VT_R8] + +typedef struct +{ + SDL_SensorID id; + ISensor *sensor; + SENSOR_ID sensor_id; + char *name; + SDL_SensorType type; + SDL_Sensor *sensor_opened; + +} SDL_Windows_Sensor; + +static bool SDL_windowscoinit; +static ISensorManager *SDL_sensor_manager; +static int SDL_num_sensors; +static SDL_Windows_Sensor *SDL_sensors; + +static bool ConnectSensor(ISensor *sensor); +static bool DisconnectSensor(ISensor *sensor); + +static HRESULT STDMETHODCALLTYPE ISensorManagerEventsVtbl_QueryInterface(ISensorManagerEvents *This, REFIID riid, void **ppvObject) +{ + if (!ppvObject) { + return E_INVALIDARG; + } + + *ppvObject = NULL; + if (WIN_IsEqualIID(riid, &IID_IUnknown) || WIN_IsEqualIID(riid, &SDL_IID_SensorManagerEvents)) { + *ppvObject = This; + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG STDMETHODCALLTYPE ISensorManagerEventsVtbl_AddRef(ISensorManagerEvents *This) +{ + return 1; +} + +static ULONG STDMETHODCALLTYPE ISensorManagerEventsVtbl_Release(ISensorManagerEvents *This) +{ + return 1; +} + +static HRESULT STDMETHODCALLTYPE ISensorManagerEventsVtbl_OnSensorEnter(ISensorManagerEvents *This, ISensor *pSensor, SensorState state) +{ + ConnectSensor(pSensor); + return S_OK; +} + +static ISensorManagerEventsVtbl sensor_manager_events_vtbl = { + ISensorManagerEventsVtbl_QueryInterface, + ISensorManagerEventsVtbl_AddRef, + ISensorManagerEventsVtbl_Release, + ISensorManagerEventsVtbl_OnSensorEnter +}; +static ISensorManagerEvents sensor_manager_events = { + &sensor_manager_events_vtbl +}; + +static HRESULT STDMETHODCALLTYPE ISensorEventsVtbl_QueryInterface(ISensorEvents *This, REFIID riid, void **ppvObject) +{ + if (!ppvObject) { + return E_INVALIDARG; + } + + *ppvObject = NULL; + if (WIN_IsEqualIID(riid, &IID_IUnknown) || WIN_IsEqualIID(riid, &SDL_IID_SensorEvents)) { + *ppvObject = This; + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG STDMETHODCALLTYPE ISensorEventsVtbl_AddRef(ISensorEvents *This) +{ + return 1; +} + +static ULONG STDMETHODCALLTYPE ISensorEventsVtbl_Release(ISensorEvents *This) +{ + return 1; +} + +static HRESULT STDMETHODCALLTYPE ISensorEventsVtbl_OnStateChanged(ISensorEvents *This, ISensor *pSensor, SensorState state) +{ +#ifdef DEBUG_SENSORS + int i; + + SDL_LockSensors(); + for (i = 0; i < SDL_num_sensors; ++i) { + if (pSensor == SDL_sensors[i].sensor) { + SDL_Log("Sensor %s state changed to %d", SDL_sensors[i].name, state); + } + } + SDL_UnlockSensors(); +#endif + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE ISensorEventsVtbl_OnDataUpdated(ISensorEvents *This, ISensor *pSensor, ISensorDataReport *pNewData) +{ + int i; + Uint64 timestamp = SDL_GetTicksNS(); + + SDL_LockSensors(); + for (i = 0; i < SDL_num_sensors; ++i) { + if (pSensor == SDL_sensors[i].sensor) { + if (SDL_sensors[i].sensor_opened) { + HRESULT hrX, hrY, hrZ; + PROPVARIANT valueX = { 0 }, valueY = { 0 }, valueZ = { 0 }; + SYSTEMTIME sensor_systemtime; + FILETIME sensor_filetime; + Uint64 sensor_timestamp; + +#ifdef DEBUG_SENSORS + SDL_Log("Sensor %s data updated", SDL_sensors[i].name); +#endif + if (SUCCEEDED(ISensorDataReport_GetTimestamp(pNewData, &sensor_systemtime)) && + SystemTimeToFileTime(&sensor_systemtime, &sensor_filetime)) { + ULARGE_INTEGER sensor_time; + sensor_time.u.HighPart = sensor_filetime.dwHighDateTime; + sensor_time.u.LowPart = sensor_filetime.dwLowDateTime; + sensor_timestamp = sensor_time.QuadPart * 100; + } else { + sensor_timestamp = timestamp; + } + + switch (SDL_sensors[i].type) { + case SDL_SENSOR_ACCEL: + hrX = ISensorDataReport_GetSensorValue(pNewData, &SENSOR_DATA_TYPE_ACCELERATION_X_G, &valueX); + hrY = ISensorDataReport_GetSensorValue(pNewData, &SENSOR_DATA_TYPE_ACCELERATION_Y_G, &valueY); + hrZ = ISensorDataReport_GetSensorValue(pNewData, &SENSOR_DATA_TYPE_ACCELERATION_Z_G, &valueZ); + if (SUCCEEDED(hrX) && SUCCEEDED(hrY) && SUCCEEDED(hrZ) && + valueX.vt == VT_R8 && valueY.vt == VT_R8 && valueZ.vt == VT_R8) { + float values[3]; + + values[0] = (float)valueX.dblVal * SDL_STANDARD_GRAVITY; + values[1] = (float)valueY.dblVal * SDL_STANDARD_GRAVITY; + values[2] = (float)valueZ.dblVal * SDL_STANDARD_GRAVITY; + SDL_SendSensorUpdate(timestamp, SDL_sensors[i].sensor_opened, sensor_timestamp, values, 3); + } + break; + case SDL_SENSOR_GYRO: + hrX = ISensorDataReport_GetSensorValue(pNewData, &SDL_SENSOR_DATA_TYPE_ANGULAR_VELOCITY_X_DEGREES_PER_SECOND, &valueX); + hrY = ISensorDataReport_GetSensorValue(pNewData, &SDL_SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Y_DEGREES_PER_SECOND, &valueY); + hrZ = ISensorDataReport_GetSensorValue(pNewData, &SDL_SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Z_DEGREES_PER_SECOND, &valueZ); + if (SUCCEEDED(hrX) && SUCCEEDED(hrY) && SUCCEEDED(hrZ) && + valueX.vt == VT_R8 && valueY.vt == VT_R8 && valueZ.vt == VT_R8) { + const float DEGREES_TO_RADIANS = (SDL_PI_F / 180.0f); + float values[3]; + + values[0] = (float)valueX.dblVal * DEGREES_TO_RADIANS; + values[1] = (float)valueY.dblVal * DEGREES_TO_RADIANS; + values[2] = (float)valueZ.dblVal * DEGREES_TO_RADIANS; + SDL_SendSensorUpdate(timestamp, SDL_sensors[i].sensor_opened, sensor_timestamp, values, 3); + } + break; + default: + // FIXME: Need to know how to interpret the data for this sensor + break; + } + } + break; + } + } + SDL_UnlockSensors(); + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE ISensorEventsVtbl_OnEvent(ISensorEvents *This, ISensor *pSensor, REFGUID eventID, IPortableDeviceValues *pEventData) +{ +#ifdef DEBUG_SENSORS + int i; + + SDL_LockSensors(); + for (i = 0; i < SDL_num_sensors; ++i) { + if (pSensor == SDL_sensors[i].sensor) { + SDL_Log("Sensor %s event occurred", SDL_sensors[i].name); + } + } + SDL_UnlockSensors(); +#endif + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE ISensorEventsVtbl_OnLeave(ISensorEvents *This, REFSENSOR_ID ID) +{ + int i; + + SDL_LockSensors(); + for (i = 0; i < SDL_num_sensors; ++i) { + if (WIN_IsEqualIID(ID, &SDL_sensors[i].sensor_id)) { +#ifdef DEBUG_SENSORS + SDL_Log("Sensor %s disconnected", SDL_sensors[i].name); +#endif + DisconnectSensor(SDL_sensors[i].sensor); + } + } + SDL_UnlockSensors(); + + return S_OK; +} + +static ISensorEventsVtbl sensor_events_vtbl = { + ISensorEventsVtbl_QueryInterface, + ISensorEventsVtbl_AddRef, + ISensorEventsVtbl_Release, + ISensorEventsVtbl_OnStateChanged, + ISensorEventsVtbl_OnDataUpdated, + ISensorEventsVtbl_OnEvent, + ISensorEventsVtbl_OnLeave +}; +static ISensorEvents sensor_events = { + &sensor_events_vtbl +}; + +static bool ConnectSensor(ISensor *sensor) +{ + SDL_Windows_Sensor *new_sensor, *new_sensors; + HRESULT hr; + SENSOR_ID sensor_id; + SENSOR_TYPE_ID type_id; + SDL_SensorType type; + BSTR bstr_name = NULL; + char *name; + + hr = ISensor_GetID(sensor, &sensor_id); + if (FAILED(hr)) { + return WIN_SetErrorFromHRESULT("Couldn't get sensor ID", hr); + } + + hr = ISensor_GetType(sensor, &type_id); + if (FAILED(hr)) { + return WIN_SetErrorFromHRESULT("Couldn't get sensor type", hr); + } + + if (WIN_IsEqualIID(&type_id, &SENSOR_TYPE_ACCELEROMETER_3D)) { + type = SDL_SENSOR_ACCEL; + } else if (WIN_IsEqualIID(&type_id, &SENSOR_TYPE_GYROMETER_3D)) { + type = SDL_SENSOR_GYRO; + } else { + return SDL_SetError("Unknown sensor type"); + } + + hr = ISensor_GetFriendlyName(sensor, &bstr_name); + if (SUCCEEDED(hr) && bstr_name) { + name = WIN_StringToUTF8W(bstr_name); + } else { + name = SDL_strdup("Unknown Sensor"); + } + if (bstr_name != NULL) { + SysFreeString(bstr_name); + } + if (!name) { + return false; + } + + SDL_LockSensors(); + new_sensors = (SDL_Windows_Sensor *)SDL_realloc(SDL_sensors, (SDL_num_sensors + 1) * sizeof(SDL_Windows_Sensor)); + if (!new_sensors) { + SDL_UnlockSensors(); + SDL_free(name); + return false; + } + + ISensor_AddRef(sensor); + ISensor_SetEventSink(sensor, &sensor_events); + + SDL_sensors = new_sensors; + new_sensor = &SDL_sensors[SDL_num_sensors]; + ++SDL_num_sensors; + + SDL_zerop(new_sensor); + new_sensor->id = SDL_GetNextObjectID(); + new_sensor->sensor = sensor; + new_sensor->type = type; + new_sensor->name = name; + + SDL_UnlockSensors(); + + return true; +} + +static bool DisconnectSensor(ISensor *sensor) +{ + SDL_Windows_Sensor *old_sensor; + int i; + + SDL_LockSensors(); + for (i = 0; i < SDL_num_sensors; ++i) { + old_sensor = &SDL_sensors[i]; + if (sensor == old_sensor->sensor) { + /* This call hangs for some reason: + * https://github.com/libsdl-org/SDL/issues/5288 + */ + // ISensor_SetEventSink(sensor, NULL); + ISensor_Release(sensor); + SDL_free(old_sensor->name); + --SDL_num_sensors; + if (i < SDL_num_sensors) { + SDL_memmove(&SDL_sensors[i], &SDL_sensors[i + 1], (SDL_num_sensors - i) * sizeof(SDL_sensors[i])); + } + break; + } + } + SDL_UnlockSensors(); + + return true; +} + +static bool SDL_WINDOWS_SensorInit(void) +{ + HRESULT hr; + ISensorCollection *sensor_collection = NULL; + + if (WIN_CoInitialize() == S_OK) { + SDL_windowscoinit = true; + } + + hr = CoCreateInstance(&SDL_CLSID_SensorManager, NULL, CLSCTX_INPROC_SERVER, &SDL_IID_SensorManager, (LPVOID *)&SDL_sensor_manager); + if (FAILED(hr)) { + // If we can't create a sensor manager (i.e. on Wine), we won't have any sensors, but don't fail the init + return true; // WIN_SetErrorFromHRESULT("Couldn't create the sensor manager", hr); + } + + hr = ISensorManager_SetEventSink(SDL_sensor_manager, &sensor_manager_events); + if (FAILED(hr)) { + ISensorManager_Release(SDL_sensor_manager); + SDL_sensor_manager = NULL; + return WIN_SetErrorFromHRESULT("Couldn't set the sensor manager event sink", hr); + } + + hr = ISensorManager_GetSensorsByCategory(SDL_sensor_manager, &SENSOR_CATEGORY_ALL, &sensor_collection); + if (SUCCEEDED(hr)) { + ULONG i, count; + + hr = ISensorCollection_GetCount(sensor_collection, &count); + if (SUCCEEDED(hr)) { + for (i = 0; i < count; ++i) { + ISensor *sensor; + + hr = ISensorCollection_GetAt(sensor_collection, i, &sensor); + if (SUCCEEDED(hr)) { + SensorState state; + + hr = ISensor_GetState(sensor, &state); + if (SUCCEEDED(hr)) { + ISensorManagerEventsVtbl_OnSensorEnter(&sensor_manager_events, sensor, state); + } + ISensorManager_Release(sensor); + } + } + } + ISensorCollection_Release(sensor_collection); + } + return true; +} + +static int SDL_WINDOWS_SensorGetCount(void) +{ + return SDL_num_sensors; +} + +static void SDL_WINDOWS_SensorDetect(void) +{ +} + +static const char *SDL_WINDOWS_SensorGetDeviceName(int device_index) +{ + return SDL_sensors[device_index].name; +} + +static SDL_SensorType SDL_WINDOWS_SensorGetDeviceType(int device_index) +{ + return SDL_sensors[device_index].type; +} + +static int SDL_WINDOWS_SensorGetDeviceNonPortableType(int device_index) +{ + return -1; +} + +static SDL_SensorID SDL_WINDOWS_SensorGetDeviceInstanceID(int device_index) +{ + return SDL_sensors[device_index].id; +} + +static bool SDL_WINDOWS_SensorOpen(SDL_Sensor *sensor, int device_index) +{ + SDL_sensors[device_index].sensor_opened = sensor; + return true; +} + +static void SDL_WINDOWS_SensorUpdate(SDL_Sensor *sensor) +{ +} + +static void SDL_WINDOWS_SensorClose(SDL_Sensor *sensor) +{ + int i; + + for (i = 0; i < SDL_num_sensors; ++i) { + if (sensor == SDL_sensors[i].sensor_opened) { + SDL_sensors[i].sensor_opened = NULL; + break; + } + } +} + +static void SDL_WINDOWS_SensorQuit(void) +{ + while (SDL_num_sensors > 0) { + DisconnectSensor(SDL_sensors[0].sensor); + } + + if (SDL_sensor_manager) { + ISensorManager_SetEventSink(SDL_sensor_manager, NULL); + ISensorManager_Release(SDL_sensor_manager); + SDL_sensor_manager = NULL; + } + + if (SDL_windowscoinit) { + WIN_CoUninitialize(); + } +} + +SDL_SensorDriver SDL_WINDOWS_SensorDriver = { + SDL_WINDOWS_SensorInit, + SDL_WINDOWS_SensorGetCount, + SDL_WINDOWS_SensorDetect, + SDL_WINDOWS_SensorGetDeviceName, + SDL_WINDOWS_SensorGetDeviceType, + SDL_WINDOWS_SensorGetDeviceNonPortableType, + SDL_WINDOWS_SensorGetDeviceInstanceID, + SDL_WINDOWS_SensorOpen, + SDL_WINDOWS_SensorUpdate, + SDL_WINDOWS_SensorClose, + SDL_WINDOWS_SensorQuit, +}; + +#endif // SDL_SENSOR_WINDOWS diff --git a/thirdparty/sdl/sensor/windows/SDL_windowssensor.h b/thirdparty/sdl/sensor/windows/SDL_windowssensor.h new file mode 100644 index 000000000000..4b0c6f8fcc83 --- /dev/null +++ b/thirdparty/sdl/sensor/windows/SDL_windowssensor.h @@ -0,0 +1,21 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" diff --git a/thirdparty/sdl/update-sdl.sh b/thirdparty/sdl/update-sdl.sh index 7aa8c2b91521..c9be179969f9 100755 --- a/thirdparty/sdl/update-sdl.sh +++ b/thirdparty/sdl/update-sdl.sh @@ -61,7 +61,7 @@ mkdir $target/loadso cp -rv loadso/dlopen $target/loadso mkdir $target/sensor -cp -rv sensor/{*.{c,h},dummy} $target/sensor +cp -rv sensor/{*.{c,h},dummy,windows} $target/sensor mkdir $target/thread cp -rv thread/{*.{c,h},pthread,windows} $target/thread