From 5a079a2d114f96d4847d1ee305d5b7c16eeec50e Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 27 Dec 2025 12:03:39 -0800 Subject: Initial commit --- .../src/joystick/virtual/SDL_virtualjoystick.c | 990 +++++++++++++++++++++ .../src/joystick/virtual/SDL_virtualjoystick_c.h | 84 ++ 2 files changed, 1074 insertions(+) create mode 100644 contrib/SDL-3.2.8/src/joystick/virtual/SDL_virtualjoystick.c create mode 100644 contrib/SDL-3.2.8/src/joystick/virtual/SDL_virtualjoystick_c.h (limited to 'contrib/SDL-3.2.8/src/joystick/virtual') diff --git a/contrib/SDL-3.2.8/src/joystick/virtual/SDL_virtualjoystick.c b/contrib/SDL-3.2.8/src/joystick/virtual/SDL_virtualjoystick.c new file mode 100644 index 0000000..6925662 --- /dev/null +++ b/contrib/SDL-3.2.8/src/joystick/virtual/SDL_virtualjoystick.c @@ -0,0 +1,990 @@ +/* + 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_JOYSTICK_VIRTUAL + +// This is the virtual implementation of the SDL joystick API + +#include "SDL_virtualjoystick_c.h" +#include "../SDL_sysjoystick.h" +#include "../SDL_joystick_c.h" + +static joystick_hwdata *g_VJoys SDL_GUARDED_BY(SDL_joystick_lock) = NULL; + +static joystick_hwdata *VIRTUAL_HWDataForInstance(SDL_JoystickID instance_id) +{ + joystick_hwdata *vjoy; + + SDL_AssertJoysticksLocked(); + + for (vjoy = g_VJoys; vjoy; vjoy = vjoy->next) { + if (instance_id == vjoy->instance_id) { + return vjoy; + } + } + return NULL; +} + +static joystick_hwdata *VIRTUAL_HWDataForIndex(int device_index) +{ + joystick_hwdata *vjoy; + + SDL_AssertJoysticksLocked(); + + for (vjoy = g_VJoys; vjoy; vjoy = vjoy->next) { + if (device_index == 0) { + break; + } + --device_index; + } + return vjoy; +} + +static void VIRTUAL_FreeHWData(joystick_hwdata *hwdata) +{ + joystick_hwdata *cur; + joystick_hwdata *prev = NULL; + + SDL_AssertJoysticksLocked(); + + if (!hwdata) { + return; + } + + if (hwdata->desc.Cleanup) { + hwdata->desc.Cleanup(hwdata->desc.userdata); + } + + // Remove hwdata from SDL-global list + for (cur = g_VJoys; cur; prev = cur, cur = cur->next) { + if (hwdata == cur) { + if (prev) { + prev->next = cur->next; + } else { + g_VJoys = cur->next; + } + break; + } + } + + if (hwdata->joystick) { + hwdata->joystick->hwdata = NULL; + hwdata->joystick = NULL; + } + if (hwdata->name) { + SDL_free(hwdata->name); + hwdata->name = NULL; + } + if (hwdata->axes) { + SDL_free((void *)hwdata->axes); + hwdata->axes = NULL; + } + if (hwdata->buttons) { + SDL_free(hwdata->buttons); + hwdata->buttons = NULL; + } + if (hwdata->hats) { + SDL_free(hwdata->hats); + hwdata->hats = NULL; + } + if (hwdata->balls) { + SDL_free(hwdata->balls); + hwdata->balls = NULL; + } + if (hwdata->touchpads) { + for (Uint16 i = 0; i < hwdata->desc.ntouchpads; ++i) { + SDL_free(hwdata->touchpads[i].fingers); + hwdata->touchpads[i].fingers = NULL; + } + SDL_free(hwdata->touchpads); + hwdata->touchpads = NULL; + } + if (hwdata->sensors) { + SDL_free(hwdata->sensors); + hwdata->sensors = NULL; + } + if (hwdata->sensor_events) { + SDL_free(hwdata->sensor_events); + hwdata->sensor_events = NULL; + } + SDL_free(hwdata); +} + +SDL_JoystickID SDL_JoystickAttachVirtualInner(const SDL_VirtualJoystickDesc *desc) +{ + joystick_hwdata *hwdata = NULL; + const char *name = NULL; + int axis_triggerleft = -1; + int axis_triggerright = -1; + + SDL_AssertJoysticksLocked(); + + if (!desc) { + SDL_InvalidParamError("desc"); + return 0; + } + if (desc->version < sizeof(*desc)) { + // Update this to handle older versions of this interface + SDL_SetError("Invalid desc, should be initialized with SDL_INIT_INTERFACE()"); + return 0; + } + + hwdata = (joystick_hwdata *)SDL_calloc(1, sizeof(joystick_hwdata)); + if (!hwdata) { + VIRTUAL_FreeHWData(hwdata); + return 0; + } + SDL_copyp(&hwdata->desc, desc); + hwdata->desc.touchpads = NULL; + hwdata->desc.sensors = NULL; + + if (hwdata->desc.name) { + name = hwdata->desc.name; + } else { + switch (hwdata->desc.type) { + case SDL_JOYSTICK_TYPE_GAMEPAD: + name = "Virtual Controller"; + break; + case SDL_JOYSTICK_TYPE_WHEEL: + name = "Virtual Wheel"; + break; + case SDL_JOYSTICK_TYPE_ARCADE_STICK: + name = "Virtual Arcade Stick"; + break; + case SDL_JOYSTICK_TYPE_FLIGHT_STICK: + name = "Virtual Flight Stick"; + break; + case SDL_JOYSTICK_TYPE_DANCE_PAD: + name = "Virtual Dance Pad"; + break; + case SDL_JOYSTICK_TYPE_GUITAR: + name = "Virtual Guitar"; + break; + case SDL_JOYSTICK_TYPE_DRUM_KIT: + name = "Virtual Drum Kit"; + break; + case SDL_JOYSTICK_TYPE_ARCADE_PAD: + name = "Virtual Arcade Pad"; + break; + case SDL_JOYSTICK_TYPE_THROTTLE: + name = "Virtual Throttle"; + break; + default: + name = "Virtual Joystick"; + break; + } + } + hwdata->name = SDL_strdup(name); + + if (hwdata->desc.type == SDL_JOYSTICK_TYPE_GAMEPAD) { + int i, axis; + + if (hwdata->desc.button_mask == 0) { + for (i = 0; i < hwdata->desc.nbuttons && i < sizeof(hwdata->desc.button_mask) * 8; ++i) { + hwdata->desc.button_mask |= (1 << i); + } + } + + if (hwdata->desc.axis_mask == 0) { + if (hwdata->desc.naxes >= 2) { + hwdata->desc.axis_mask |= ((1 << SDL_GAMEPAD_AXIS_LEFTX) | (1 << SDL_GAMEPAD_AXIS_LEFTY)); + } + if (hwdata->desc.naxes >= 4) { + hwdata->desc.axis_mask |= ((1 << SDL_GAMEPAD_AXIS_RIGHTX) | (1 << SDL_GAMEPAD_AXIS_RIGHTY)); + } + if (hwdata->desc.naxes >= 6) { + hwdata->desc.axis_mask |= ((1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)); + } + } + + // Find the trigger axes + axis = 0; + for (i = 0; axis < hwdata->desc.naxes && i < SDL_GAMEPAD_AXIS_COUNT; ++i) { + if (hwdata->desc.axis_mask & (1 << i)) { + if (i == SDL_GAMEPAD_AXIS_LEFT_TRIGGER) { + axis_triggerleft = axis; + } + if (i == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) { + axis_triggerright = axis; + } + ++axis; + } + } + } + + hwdata->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_VIRTUAL, hwdata->desc.vendor_id, hwdata->desc.product_id, 0, NULL, name, 'v', (Uint8)hwdata->desc.type); + + // Allocate fields for different control-types + if (hwdata->desc.naxes > 0) { + hwdata->axes = (Sint16 *)SDL_calloc(hwdata->desc.naxes, sizeof(*hwdata->axes)); + if (!hwdata->axes) { + VIRTUAL_FreeHWData(hwdata); + return 0; + } + + // Trigger axes are at minimum value at rest + if (axis_triggerleft >= 0) { + hwdata->axes[axis_triggerleft] = SDL_JOYSTICK_AXIS_MIN; + } + if (axis_triggerright >= 0) { + hwdata->axes[axis_triggerright] = SDL_JOYSTICK_AXIS_MIN; + } + } + if (hwdata->desc.nbuttons > 0) { + hwdata->buttons = (bool *)SDL_calloc(hwdata->desc.nbuttons, sizeof(*hwdata->buttons)); + if (!hwdata->buttons) { + VIRTUAL_FreeHWData(hwdata); + return 0; + } + } + if (hwdata->desc.nhats > 0) { + hwdata->hats = (Uint8 *)SDL_calloc(hwdata->desc.nhats, sizeof(*hwdata->hats)); + if (!hwdata->hats) { + VIRTUAL_FreeHWData(hwdata); + return 0; + } + } + if (hwdata->desc.nballs > 0) { + hwdata->balls = (SDL_JoystickBallData *)SDL_calloc(hwdata->desc.nballs, sizeof(*hwdata->balls)); + if (!hwdata->balls) { + VIRTUAL_FreeHWData(hwdata); + return 0; + } + } + if (hwdata->desc.ntouchpads > 0) { + if (!desc->touchpads) { + VIRTUAL_FreeHWData(hwdata); + SDL_SetError("desc missing touchpad descriptions"); + return 0; + } + hwdata->touchpads = (SDL_JoystickTouchpadInfo *)SDL_calloc(hwdata->desc.ntouchpads, sizeof(*hwdata->touchpads)); + if (!hwdata->touchpads) { + VIRTUAL_FreeHWData(hwdata); + return 0; + } + for (Uint16 i = 0; i < hwdata->desc.ntouchpads; ++i) { + const SDL_VirtualJoystickTouchpadDesc *touchpad_desc = &desc->touchpads[i]; + hwdata->touchpads[i].nfingers = touchpad_desc->nfingers; + hwdata->touchpads[i].fingers = (SDL_JoystickTouchpadFingerInfo *)SDL_calloc(touchpad_desc->nfingers, sizeof(*hwdata->touchpads[i].fingers)); + if (!hwdata->touchpads[i].fingers) { + VIRTUAL_FreeHWData(hwdata); + return 0; + } + } + } + if (hwdata->desc.nsensors > 0) { + if (!desc->sensors) { + VIRTUAL_FreeHWData(hwdata); + SDL_SetError("desc missing sensor descriptions"); + return 0; + } + hwdata->sensors = (SDL_JoystickSensorInfo *)SDL_calloc(hwdata->desc.nsensors, sizeof(*hwdata->sensors)); + if (!hwdata->sensors) { + VIRTUAL_FreeHWData(hwdata); + return 0; + } + for (Uint16 i = 0; i < hwdata->desc.nsensors; ++i) { + const SDL_VirtualJoystickSensorDesc *sensor_desc = &desc->sensors[i]; + hwdata->sensors[i].type = sensor_desc->type; + hwdata->sensors[i].rate = sensor_desc->rate; + } + } + + // Allocate an instance ID for this device + hwdata->instance_id = SDL_GetNextObjectID(); + + // Add virtual joystick to SDL-global lists + if (g_VJoys) { + joystick_hwdata *last; + + for (last = g_VJoys; last->next; last = last->next) { + } + last->next = hwdata; + } else { + g_VJoys = hwdata; + } + SDL_PrivateJoystickAdded(hwdata->instance_id); + + return hwdata->instance_id; +} + +bool SDL_JoystickDetachVirtualInner(SDL_JoystickID instance_id) +{ + joystick_hwdata *hwdata = VIRTUAL_HWDataForInstance(instance_id); + if (!hwdata) { + return SDL_SetError("Virtual joystick data not found"); + } + VIRTUAL_FreeHWData(hwdata); + SDL_PrivateJoystickRemoved(instance_id); + return true; +} + +bool SDL_SetJoystickVirtualAxisInner(SDL_Joystick *joystick, int axis, Sint16 value) +{ + joystick_hwdata *hwdata; + + SDL_AssertJoysticksLocked(); + + if (!joystick || !joystick->hwdata) { + return SDL_SetError("Invalid joystick"); + } + + hwdata = (joystick_hwdata *)joystick->hwdata; + if (axis < 0 || axis >= hwdata->desc.naxes) { + return SDL_SetError("Invalid axis index"); + } + + hwdata->axes[axis] = value; + hwdata->changes |= AXES_CHANGED; + + return true; +} + +bool SDL_SetJoystickVirtualBallInner(SDL_Joystick *joystick, int ball, Sint16 xrel, Sint16 yrel) +{ + joystick_hwdata *hwdata; + + SDL_AssertJoysticksLocked(); + + if (!joystick || !joystick->hwdata) { + return SDL_SetError("Invalid joystick"); + } + + hwdata = (joystick_hwdata *)joystick->hwdata; + if (ball < 0 || ball >= hwdata->desc.nballs) { + return SDL_SetError("Invalid ball index"); + } + + hwdata->balls[ball].dx += xrel; + hwdata->balls[ball].dx = SDL_clamp(hwdata->balls[ball].dx, SDL_MIN_SINT16, SDL_MAX_SINT16); + hwdata->balls[ball].dy += yrel; + hwdata->balls[ball].dy = SDL_clamp(hwdata->balls[ball].dy, SDL_MIN_SINT16, SDL_MAX_SINT16); + hwdata->changes |= BALLS_CHANGED; + + return true; +} + +bool SDL_SetJoystickVirtualButtonInner(SDL_Joystick *joystick, int button, bool down) +{ + joystick_hwdata *hwdata; + + SDL_AssertJoysticksLocked(); + + if (!joystick || !joystick->hwdata) { + return SDL_SetError("Invalid joystick"); + } + + hwdata = (joystick_hwdata *)joystick->hwdata; + if (button < 0 || button >= hwdata->desc.nbuttons) { + return SDL_SetError("Invalid button index"); + } + + hwdata->buttons[button] = down; + hwdata->changes |= BUTTONS_CHANGED; + + return true; +} + +bool SDL_SetJoystickVirtualHatInner(SDL_Joystick *joystick, int hat, Uint8 value) +{ + joystick_hwdata *hwdata; + + SDL_AssertJoysticksLocked(); + + if (!joystick || !joystick->hwdata) { + return SDL_SetError("Invalid joystick"); + } + + hwdata = (joystick_hwdata *)joystick->hwdata; + if (hat < 0 || hat >= hwdata->desc.nhats) { + return SDL_SetError("Invalid hat index"); + } + + hwdata->hats[hat] = value; + hwdata->changes |= HATS_CHANGED; + + return true; +} + +bool SDL_SetJoystickVirtualTouchpadInner(SDL_Joystick *joystick, int touchpad, int finger, bool down, float x, float y, float pressure) +{ + joystick_hwdata *hwdata; + + SDL_AssertJoysticksLocked(); + + if (!joystick || !joystick->hwdata) { + return SDL_SetError("Invalid joystick"); + } + + hwdata = (joystick_hwdata *)joystick->hwdata; + if (touchpad < 0 || touchpad >= hwdata->desc.ntouchpads) { + return SDL_SetError("Invalid touchpad index"); + } + if (finger < 0 || finger >= hwdata->touchpads[touchpad].nfingers) { + return SDL_SetError("Invalid finger index"); + } + + SDL_JoystickTouchpadFingerInfo *info = &hwdata->touchpads[touchpad].fingers[finger]; + info->down = down; + info->x = x; + info->y = y; + info->pressure = pressure; + hwdata->changes |= TOUCHPADS_CHANGED; + + return true; +} + +bool SDL_SendJoystickVirtualSensorDataInner(SDL_Joystick *joystick, SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values) +{ + joystick_hwdata *hwdata; + + SDL_AssertJoysticksLocked(); + + if (!joystick || !joystick->hwdata) { + return SDL_SetError("Invalid joystick"); + } + + hwdata = (joystick_hwdata *)joystick->hwdata; + if (hwdata->num_sensor_events == hwdata->max_sensor_events) { + int new_max_sensor_events = (hwdata->max_sensor_events + 1); + VirtualSensorEvent *sensor_events = (VirtualSensorEvent *)SDL_realloc(hwdata->sensor_events, new_max_sensor_events * sizeof(*sensor_events)); + if (!sensor_events) { + return false; + } + hwdata->sensor_events = sensor_events; + hwdata->max_sensor_events = hwdata->max_sensor_events; + } + + VirtualSensorEvent *event = &hwdata->sensor_events[hwdata->num_sensor_events++]; + event->type = type; + event->sensor_timestamp = sensor_timestamp; + event->num_values = SDL_min(num_values, SDL_arraysize(event->data)); + SDL_memcpy(event->data, data, (event->num_values * sizeof(*event->data))); + + return true; +} + +static bool VIRTUAL_JoystickInit(void) +{ + return true; +} + +static int VIRTUAL_JoystickGetCount(void) +{ + joystick_hwdata *cur; + int count = 0; + + SDL_AssertJoysticksLocked(); + + for (cur = g_VJoys; cur; cur = cur->next) { + ++count; + } + return count; +} + +static void VIRTUAL_JoystickDetect(void) +{ +} + +static bool VIRTUAL_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + // We don't override any other drivers... or do we? + return false; +} + +static const char *VIRTUAL_JoystickGetDeviceName(int device_index) +{ + joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); + if (!hwdata) { + return NULL; + } + return hwdata->name; +} + +static const char *VIRTUAL_JoystickGetDevicePath(int device_index) +{ + return NULL; +} + +static int VIRTUAL_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) +{ + return -1; +} + +static int VIRTUAL_JoystickGetDevicePlayerIndex(int device_index) +{ + return -1; +} + +static void VIRTUAL_JoystickSetDevicePlayerIndex(int device_index, int player_index) +{ + joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); + + if (hwdata && hwdata->desc.SetPlayerIndex) { + hwdata->desc.SetPlayerIndex(hwdata->desc.userdata, player_index); + } +} + +static SDL_GUID VIRTUAL_JoystickGetDeviceGUID(int device_index) +{ + joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); + if (!hwdata) { + SDL_GUID guid; + SDL_zero(guid); + return guid; + } + return hwdata->guid; +} + +static SDL_JoystickID VIRTUAL_JoystickGetDeviceInstanceID(int device_index) +{ + joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); + if (!hwdata) { + return true; + } + return hwdata->instance_id; +} + +static bool VIRTUAL_JoystickOpen(SDL_Joystick *joystick, int device_index) +{ + joystick_hwdata *hwdata; + + SDL_AssertJoysticksLocked(); + + hwdata = VIRTUAL_HWDataForIndex(device_index); + if (!hwdata) { + return SDL_SetError("No such device"); + } + joystick->hwdata = hwdata; + joystick->naxes = hwdata->desc.naxes; + joystick->nbuttons = hwdata->desc.nbuttons; + joystick->nhats = hwdata->desc.nhats; + hwdata->joystick = joystick; + + for (Uint16 i = 0; i < hwdata->desc.ntouchpads; ++i) { + const SDL_JoystickTouchpadInfo *touchpad = &hwdata->touchpads[i]; + SDL_PrivateJoystickAddTouchpad(joystick, touchpad->nfingers); + } + for (Uint16 i = 0; i < hwdata->desc.nsensors; ++i) { + const SDL_JoystickSensorInfo *sensor = &hwdata->sensors[i]; + SDL_PrivateJoystickAddSensor(joystick, sensor->type, sensor->rate); + } + + if (hwdata->desc.SetLED) { + SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN, true); + } + if (hwdata->desc.Rumble) { + SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true); + } + if (hwdata->desc.RumbleTriggers) { + SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, true); + } + return true; +} + +static bool VIRTUAL_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +{ + bool result; + + SDL_AssertJoysticksLocked(); + + if (joystick->hwdata) { + joystick_hwdata *hwdata = joystick->hwdata; + if (hwdata->desc.Rumble) { + result = hwdata->desc.Rumble(hwdata->desc.userdata, low_frequency_rumble, high_frequency_rumble); + } else { + result = SDL_Unsupported(); + } + } else { + result = SDL_SetError("Rumble failed, device disconnected"); + } + + return result; +} + +static bool VIRTUAL_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) +{ + bool result; + + SDL_AssertJoysticksLocked(); + + if (joystick->hwdata) { + joystick_hwdata *hwdata = joystick->hwdata; + if (hwdata->desc.RumbleTriggers) { + result = hwdata->desc.RumbleTriggers(hwdata->desc.userdata, left_rumble, right_rumble); + } else { + result = SDL_Unsupported(); + } + } else { + result = SDL_SetError("Rumble failed, device disconnected"); + } + + return result; +} + +static bool VIRTUAL_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) +{ + bool result; + + SDL_AssertJoysticksLocked(); + + if (joystick->hwdata) { + joystick_hwdata *hwdata = joystick->hwdata; + if (hwdata->desc.SetLED) { + result = hwdata->desc.SetLED(hwdata->desc.userdata, red, green, blue); + } else { + result = SDL_Unsupported(); + } + } else { + result = SDL_SetError("SetLED failed, device disconnected"); + } + + return result; +} + +static bool VIRTUAL_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size) +{ + bool result; + + SDL_AssertJoysticksLocked(); + + if (joystick->hwdata) { + joystick_hwdata *hwdata = joystick->hwdata; + if (hwdata->desc.SendEffect) { + result = hwdata->desc.SendEffect(hwdata->desc.userdata, data, size); + } else { + result = SDL_Unsupported(); + } + } else { + result = SDL_SetError("SendEffect failed, device disconnected"); + } + + return result; +} + +static bool VIRTUAL_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled) +{ + bool result; + + SDL_AssertJoysticksLocked(); + + if (joystick->hwdata) { + joystick_hwdata *hwdata = joystick->hwdata; + if (hwdata->desc.SetSensorsEnabled) { + result = hwdata->desc.SetSensorsEnabled(hwdata->desc.userdata, enabled); + } else { + result = true; + } + if (result) { + hwdata->sensors_enabled = enabled; + } + } else { + result = SDL_SetError("SetSensorsEnabled failed, device disconnected"); + } + + return result; +} + +static void VIRTUAL_JoystickUpdate(SDL_Joystick *joystick) +{ + joystick_hwdata *hwdata; + Uint64 timestamp = SDL_GetTicksNS(); + + SDL_AssertJoysticksLocked(); + + if (!joystick) { + return; + } + if (!joystick->hwdata) { + return; + } + + hwdata = (joystick_hwdata *)joystick->hwdata; + + if (hwdata->desc.Update) { + hwdata->desc.Update(hwdata->desc.userdata); + } + + if (hwdata->changes & AXES_CHANGED) { + for (Uint8 i = 0; i < hwdata->desc.naxes; ++i) { + SDL_SendJoystickAxis(timestamp, joystick, i, hwdata->axes[i]); + } + } + if (hwdata->changes & BALLS_CHANGED) { + for (Uint8 i = 0; i < hwdata->desc.nballs; ++i) { + SDL_JoystickBallData *ball = &hwdata->balls[i]; + if (ball->dx || ball->dy) { + SDL_SendJoystickBall(timestamp, joystick, i, (Sint16)ball->dx, (Sint16)ball->dy); + ball->dx = 0; + ball->dy = 0; + } + } + } + if (hwdata->changes & BUTTONS_CHANGED) { + for (Uint8 i = 0; i < hwdata->desc.nbuttons; ++i) { + SDL_SendJoystickButton(timestamp, joystick, i, hwdata->buttons[i]); + } + } + if (hwdata->changes & HATS_CHANGED) { + for (Uint8 i = 0; i < hwdata->desc.nhats; ++i) { + SDL_SendJoystickHat(timestamp, joystick, i, hwdata->hats[i]); + } + } + if (hwdata->changes & TOUCHPADS_CHANGED) { + for (Uint16 i = 0; i < hwdata->desc.ntouchpads; ++i) { + const SDL_JoystickTouchpadInfo *touchpad = &hwdata->touchpads[i]; + for (int j = 0; j < touchpad->nfingers; ++j) { + const SDL_JoystickTouchpadFingerInfo *finger = &touchpad->fingers[j]; + SDL_SendJoystickTouchpad(timestamp, joystick, i, j, finger->down, finger->x, finger->y, finger->pressure); + } + } + } + if (hwdata->num_sensor_events > 0) { + if (hwdata->sensors_enabled) { + for (int i = 0; i < hwdata->num_sensor_events; ++i) { + const VirtualSensorEvent *event = &hwdata->sensor_events[i]; + SDL_SendJoystickSensor(timestamp, joystick, event->type, event->sensor_timestamp, event->data, event->num_values); + } + } + hwdata->num_sensor_events = 0; + } + hwdata->changes = 0; +} + +static void VIRTUAL_JoystickClose(SDL_Joystick *joystick) +{ + SDL_AssertJoysticksLocked(); + + if (joystick->hwdata) { + joystick_hwdata *hwdata = joystick->hwdata; + hwdata->joystick = NULL; + joystick->hwdata = NULL; + } +} + +static void VIRTUAL_JoystickQuit(void) +{ + SDL_AssertJoysticksLocked(); + + while (g_VJoys) { + VIRTUAL_FreeHWData(g_VJoys); + } +} + +static bool VIRTUAL_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) +{ + joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); + Uint8 current_button = 0; + Uint8 current_axis = 0; + + if (!hwdata || hwdata->desc.type != SDL_JOYSTICK_TYPE_GAMEPAD) { + return false; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_SOUTH))) { + out->a.kind = EMappingKind_Button; + out->a.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_EAST))) { + out->b.kind = EMappingKind_Button; + out->b.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_WEST))) { + out->x.kind = EMappingKind_Button; + out->x.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_NORTH))) { + out->y.kind = EMappingKind_Button; + out->y.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK))) { + out->back.kind = EMappingKind_Button; + out->back.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_GUIDE))) { + out->guide.kind = EMappingKind_Button; + out->guide.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_START))) { + out->start.kind = EMappingKind_Button; + out->start.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_STICK))) { + out->leftstick.kind = EMappingKind_Button; + out->leftstick.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_STICK))) { + out->rightstick.kind = EMappingKind_Button; + out->rightstick.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_SHOULDER))) { + out->leftshoulder.kind = EMappingKind_Button; + out->leftshoulder.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER))) { + out->rightshoulder.kind = EMappingKind_Button; + out->rightshoulder.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_UP))) { + out->dpup.kind = EMappingKind_Button; + out->dpup.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_DOWN))) { + out->dpdown.kind = EMappingKind_Button; + out->dpdown.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_LEFT))) { + out->dpleft.kind = EMappingKind_Button; + out->dpleft.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_RIGHT))) { + out->dpright.kind = EMappingKind_Button; + out->dpright.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_MISC1))) { + out->misc1.kind = EMappingKind_Button; + out->misc1.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1))) { + out->right_paddle1.kind = EMappingKind_Button; + out->right_paddle1.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE1))) { + out->left_paddle1.kind = EMappingKind_Button; + out->left_paddle1.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2))) { + out->right_paddle2.kind = EMappingKind_Button; + out->right_paddle2.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE2))) { + out->left_paddle2.kind = EMappingKind_Button; + out->left_paddle2.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_TOUCHPAD))) { + out->touchpad.kind = EMappingKind_Button; + out->touchpad.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_MISC2))) { + out->misc2.kind = EMappingKind_Button; + out->misc2.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_MISC3))) { + out->misc3.kind = EMappingKind_Button; + out->misc3.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_MISC4))) { + out->misc4.kind = EMappingKind_Button; + out->misc4.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_MISC5))) { + out->misc5.kind = EMappingKind_Button; + out->misc5.target = current_button++; + } + + if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_MISC6))) { + out->misc6.kind = EMappingKind_Button; + out->misc6.target = current_button++; + } + + if (current_axis < hwdata->desc.naxes && (hwdata->desc.axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTX))) { + out->leftx.kind = EMappingKind_Axis; + out->leftx.target = current_axis++; + } + + if (current_axis < hwdata->desc.naxes && (hwdata->desc.axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTY))) { + out->lefty.kind = EMappingKind_Axis; + out->lefty.target = current_axis++; + } + + if (current_axis < hwdata->desc.naxes && (hwdata->desc.axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHTX))) { + out->rightx.kind = EMappingKind_Axis; + out->rightx.target = current_axis++; + } + + if (current_axis < hwdata->desc.naxes && (hwdata->desc.axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHTY))) { + out->righty.kind = EMappingKind_Axis; + out->righty.target = current_axis++; + } + + if (current_axis < hwdata->desc.naxes && (hwdata->desc.axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER))) { + out->lefttrigger.kind = EMappingKind_Axis; + out->lefttrigger.target = current_axis++; + } + + if (current_axis < hwdata->desc.naxes && (hwdata->desc.axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER))) { + out->righttrigger.kind = EMappingKind_Axis; + out->righttrigger.target = current_axis++; + } + + return true; +} + +SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver = { + VIRTUAL_JoystickInit, + VIRTUAL_JoystickGetCount, + VIRTUAL_JoystickDetect, + VIRTUAL_JoystickIsDevicePresent, + VIRTUAL_JoystickGetDeviceName, + VIRTUAL_JoystickGetDevicePath, + VIRTUAL_JoystickGetDeviceSteamVirtualGamepadSlot, + VIRTUAL_JoystickGetDevicePlayerIndex, + VIRTUAL_JoystickSetDevicePlayerIndex, + VIRTUAL_JoystickGetDeviceGUID, + VIRTUAL_JoystickGetDeviceInstanceID, + VIRTUAL_JoystickOpen, + VIRTUAL_JoystickRumble, + VIRTUAL_JoystickRumbleTriggers, + VIRTUAL_JoystickSetLED, + VIRTUAL_JoystickSendEffect, + VIRTUAL_JoystickSetSensorsEnabled, + VIRTUAL_JoystickUpdate, + VIRTUAL_JoystickClose, + VIRTUAL_JoystickQuit, + VIRTUAL_JoystickGetGamepadMapping +}; + +#endif // SDL_JOYSTICK_VIRTUAL diff --git a/contrib/SDL-3.2.8/src/joystick/virtual/SDL_virtualjoystick_c.h b/contrib/SDL-3.2.8/src/joystick/virtual/SDL_virtualjoystick_c.h new file mode 100644 index 0000000..14fe59e --- /dev/null +++ b/contrib/SDL-3.2.8/src/joystick/virtual/SDL_virtualjoystick_c.h @@ -0,0 +1,84 @@ +/* + 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" + +#ifndef SDL_VIRTUALJOYSTICK_C_H +#define SDL_VIRTUALJOYSTICK_C_H + +#ifdef SDL_JOYSTICK_VIRTUAL + +#include "../SDL_sysjoystick.h" + +#define AXES_CHANGED 0x00000001 +#define BALLS_CHANGED 0x00000002 +#define BUTTONS_CHANGED 0x00000004 +#define HATS_CHANGED 0x00000008 +#define TOUCHPADS_CHANGED 0x00000010 + +/** + * Data for a virtual, software-only joystick. + */ +typedef struct VirtualSensorEvent +{ + SDL_SensorType type; + Uint64 sensor_timestamp; + float data[3]; + int num_values; +} VirtualSensorEvent; + +typedef struct joystick_hwdata +{ + SDL_JoystickID instance_id; + bool attached; + char *name; + SDL_JoystickType type; + SDL_GUID guid; + SDL_VirtualJoystickDesc desc; + Uint32 changes; + Sint16 *axes; + bool *buttons; + Uint8 *hats; + SDL_JoystickBallData *balls; + SDL_JoystickTouchpadInfo *touchpads; + SDL_JoystickSensorInfo *sensors; + bool sensors_enabled; + int num_sensor_events; + int max_sensor_events; + VirtualSensorEvent *sensor_events; + + SDL_Joystick *joystick; + + struct joystick_hwdata *next; +} joystick_hwdata; + +extern SDL_JoystickID SDL_JoystickAttachVirtualInner(const SDL_VirtualJoystickDesc *desc); +extern bool SDL_JoystickDetachVirtualInner(SDL_JoystickID instance_id); + +extern bool SDL_SetJoystickVirtualAxisInner(SDL_Joystick *joystick, int axis, Sint16 value); +extern bool SDL_SetJoystickVirtualBallInner(SDL_Joystick *joystick, int ball, Sint16 xrel, Sint16 yrel); +extern bool SDL_SetJoystickVirtualButtonInner(SDL_Joystick *joystick, int button, bool down); +extern bool SDL_SetJoystickVirtualHatInner(SDL_Joystick *joystick, int hat, Uint8 value); +extern bool SDL_SetJoystickVirtualTouchpadInner(SDL_Joystick *joystick, int touchpad, int finger, bool down, float x, float y, float pressure); +extern bool SDL_SendJoystickVirtualSensorDataInner(SDL_Joystick *joystick, SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values); + +#endif // SDL_JOYSTICK_VIRTUAL + +#endif // SDL_VIRTUALJOYSTICK_C_H -- cgit v1.2.3