From 94eeb587c1969bcfea79303273895619e9edd3c2 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 15 May 2022 20:01:12 -0700
Subject: [PATCH] First pass at extending virtual controller functionality
Added the ability to specify a name and the product VID/PID for a virtual controller
Also added a test case to testgamecontroller, if you pass --virtual as a parameter
---
include/SDL_joystick.h | 38 +++
src/dynapi/SDL_dynapi_overrides.h | 1 +
src/dynapi/SDL_dynapi_procs.h | 1 +
src/joystick/SDL_gamecontroller.c | 102 +-----
src/joystick/SDL_joystick.c | 16 +-
src/joystick/SDL_joystick_c.h | 5 +
src/joystick/virtual/SDL_virtualjoystick.c | 308 ++++++++++++++-----
src/joystick/virtual/SDL_virtualjoystick_c.h | 15 +-
test/testgamecontroller.c | 230 ++++++++++++--
9 files changed, 511 insertions(+), 205 deletions(-)
diff --git a/include/SDL_joystick.h b/include/SDL_joystick.h
index 1aa130f1a32..5231d023abe 100644
--- a/include/SDL_joystick.h
+++ b/include/SDL_joystick.h
@@ -348,6 +348,44 @@ extern DECLSPEC int SDLCALL SDL_JoystickAttachVirtual(SDL_JoystickType type,
int nbuttons,
int nhats);
+/**
+ * The structure that defines an extended virtual joystick description
+ *
+ * The caller must zero the structure and then initialize the version with `SDL_VIRTUAL_JOYSTICK_DESC_VERSION` before passing it to SDL_JoystickAttachVirtualEx()
+ *
+ * \sa SDL_JoystickAttachVirtualEx
+ */
+typedef struct SDL_VirtualJoystickDesc
+{
+ Uint16 version; /**< `SDL_VIRTUAL_JOYSTICK_DESC_VERSION` */
+ Uint16 type; /**< `SDL_JoystickType` */
+ Uint16 naxes; /**< the number of axes on this joystick */
+ Uint16 nbuttons; /**< the number of buttons on this joystick */
+ Uint16 nhats; /**< the number of hats on this joystick */
+ Uint16 vendor_id; /**< the USB vendor ID of this joystick */
+ Uint16 product_id; /**< the USB product ID of this joystick */
+ Uint16 padding; /**< unused */
+ const char *name; /**< the name of the joystick */
+
+ void *userdata; /**< User data pointer passed to callbacks */
+ void (*Update)(void *userdata); /**< Called when the joystick state should be updated */
+
+} SDL_VirtualJoystickDesc;
+
+/**
+ * \brief The current version of the SDL_VirtualJoystickDesc structure
+ */
+#define SDL_VIRTUAL_JOYSTICK_DESC_VERSION 1
+
+/**
+ * Attach a new virtual joystick with extended properties.
+ *
+ * \returns the joystick's device index, or -1 if an error occurred.
+ *
+ * \since This function is available since SDL 2.24.0.
+ */
+extern DECLSPEC int SDLCALL SDL_JoystickAttachVirtualEx(const SDL_VirtualJoystickDesc *desc);
+
/**
* Detach a virtual joystick.
*
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 90b2254edfb..15577b51e4b 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -870,3 +870,4 @@
#define SDL_GameControllerPath SDL_GameControllerPath_REAL
#define SDL_JoystickPathForIndex SDL_JoystickPathForIndex_REAL
#define SDL_JoystickPath SDL_JoystickPath_REAL
+#define SDL_JoystickAttachVirtualEx SDL_JoystickAttachVirtualEx_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index f78c0e4236b..45ee57547b2 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -941,3 +941,4 @@ SDL_DYNAPI_PROC(const char*,SDL_GameControllerPathForIndex,(int a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_GameControllerPath,(SDL_GameController *a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_JoystickPathForIndex,(int a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_JoystickPath,(SDL_Joystick *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_JoystickAttachVirtualEx,(const SDL_VirtualJoystickDesc *a),(a),return)
diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c
index 50eb190a53c..f0f932e5174 100644
--- a/src/joystick/SDL_gamecontroller.c
+++ b/src/joystick/SDL_gamecontroller.c
@@ -464,100 +464,6 @@ static int SDLCALL SDL_GameControllerEventWatcher(void *userdata, SDL_Event * ev
return 1;
}
-/*
- * Helper function to guess at a mapping for virtual controllers
- */
-static ControllerMapping_t *SDL_CreateMappingForVirtualController(SDL_JoystickGUID guid)
-{
- const int face_button_mask = ((1 << SDL_CONTROLLER_BUTTON_A) |
- (1 << SDL_CONTROLLER_BUTTON_B) |
- (1 << SDL_CONTROLLER_BUTTON_X) |
- (1 << SDL_CONTROLLER_BUTTON_Y));
- SDL_bool existing;
- char mapping_string[1024];
- int button_mask;
- int axis_mask;
-
- button_mask = SDL_SwapLE16(*(Uint16*)(&guid.data[sizeof(guid.data)-4]));
- axis_mask = SDL_SwapLE16(*(Uint16*)(&guid.data[sizeof(guid.data)-2]));
- if (!button_mask && !axis_mask) {
- return NULL;
- }
- if (!(button_mask & face_button_mask)) {
- /* We don't know what buttons or axes are supported, don't make up a mapping */
- return NULL;
- }
-
- SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string));
-
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_A)) {
- SDL_strlcat(mapping_string, "a:b0,", sizeof(mapping_string));
- }
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_B)) {
- SDL_strlcat(mapping_string, "b:b1,", sizeof(mapping_string));
- }
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_X)) {
- SDL_strlcat(mapping_string, "x:b2,", sizeof(mapping_string));
- }
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_Y)) {
- SDL_strlcat(mapping_string, "y:b3,", sizeof(mapping_string));
- }
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_BACK)) {
- SDL_strlcat(mapping_string, "back:b4,", sizeof(mapping_string));
- }
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_GUIDE)) {
- SDL_strlcat(mapping_string, "guide:b5,", sizeof(mapping_string));
- }
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_START)) {
- SDL_strlcat(mapping_string, "start:b6,", sizeof(mapping_string));
- }
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_LEFTSTICK)) {
- SDL_strlcat(mapping_string, "leftstick:b7,", sizeof(mapping_string));
- }
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_RIGHTSTICK)) {
- SDL_strlcat(mapping_string, "rightstick:b8,", sizeof(mapping_string));
- }
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) {
- SDL_strlcat(mapping_string, "leftshoulder:b9,", sizeof(mapping_string));
- }
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) {
- SDL_strlcat(mapping_string, "rightshoulder:b10,", sizeof(mapping_string));
- }
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_UP)) {
- SDL_strlcat(mapping_string, "dpup:b11,", sizeof(mapping_string));
- }
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN)) {
- SDL_strlcat(mapping_string, "dpdown:b12,", sizeof(mapping_string));
- }
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT)) {
- SDL_strlcat(mapping_string, "dpleft:b13,", sizeof(mapping_string));
- }
- if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) {
- SDL_strlcat(mapping_string, "dpright:b14,", sizeof(mapping_string));
- }
- if (axis_mask & (1 << SDL_CONTROLLER_AXIS_LEFTX)) {
- SDL_strlcat(mapping_string, "leftx:a0,", sizeof(mapping_string));
- }
- if (axis_mask & (1 << SDL_CONTROLLER_AXIS_LEFTY)) {
- SDL_strlcat(mapping_string, "lefty:a1,", sizeof(mapping_string));
- }
- if (axis_mask & (1 << SDL_CONTROLLER_AXIS_RIGHTX)) {
- SDL_strlcat(mapping_string, "rightx:a2,", sizeof(mapping_string));
- }
- if (axis_mask & (1 << SDL_CONTROLLER_AXIS_RIGHTY)) {
- SDL_strlcat(mapping_string, "righty:a3,", sizeof(mapping_string));
- }
- if (axis_mask & (1 << SDL_CONTROLLER_AXIS_TRIGGERLEFT)) {
- SDL_strlcat(mapping_string, "lefttrigger:a4,", sizeof(mapping_string));
- }
- if (axis_mask & (1 << SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) {
- SDL_strlcat(mapping_string, "righttrigger:a5,", sizeof(mapping_string));
- }
-
- return SDL_PrivateAddMappingForGUID(guid, mapping_string,
- &existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT);
-}
-
#ifdef __ANDROID__
/*
* Helper function to guess at a mapping based on the elements reported for this controller
@@ -790,9 +696,6 @@ static ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickG
return s_pXInputMapping;
}
#endif
- if (!mapping && SDL_IsJoystickVirtual(guid)) {
- mapping = SDL_CreateMappingForVirtualController(guid);
- }
#ifdef __ANDROID__
if (!mapping && !SDL_IsJoystickHIDAPI(guid)) {
mapping = SDL_CreateMappingForAndroidController(guid);
@@ -1355,6 +1258,11 @@ static ControllerMapping_t *SDL_PrivateGenerateAutomaticControllerMapping(const
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpdown", &raw_map->dpdown);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpleft", &raw_map->dpleft);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpright", &raw_map->dpright);
+ SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc1", &raw_map->misc1);
+ SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle1", &raw_map->paddle1);
+ SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle2", &raw_map->paddle2);
+ SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle3", &raw_map->paddle3);
+ SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle4", &raw_map->paddle4);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "leftx", &raw_map->leftx);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "lefty", &raw_map->lefty);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightx", &raw_map->rightx);
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index e5ec5b7f855..914f29e2d8e 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -522,9 +522,23 @@ SDL_JoystickOpen(int device_index)
int
SDL_JoystickAttachVirtual(SDL_JoystickType type,
int naxes, int nbuttons, int nhats)
+{
+ SDL_VirtualJoystickDesc desc;
+
+ SDL_zero(desc);
+ desc.version = SDL_VIRTUAL_JOYSTICK_DESC_VERSION;
+ desc.type = (Uint16)type;
+ desc.naxes = (Uint16)naxes;
+ desc.nbuttons = (Uint16)nbuttons;
+ desc.nhats = (Uint16)nhats;
+ return SDL_JoystickAttachVirtualEx(&desc);
+}
+
+int
+SDL_JoystickAttachVirtualEx(const SDL_VirtualJoystickDesc *desc)
{
#if SDL_JOYSTICK_VIRTUAL
- return SDL_JoystickAttachVirtualInner(type, naxes, nbuttons, nhats);
+ return SDL_JoystickAttachVirtualInner(desc);
#else
return SDL_SetError("SDL not built with virtual-joystick support");
#endif
diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h
index 312e113375f..a7aa6b7145d 100644
--- a/src/joystick/SDL_joystick_c.h
+++ b/src/joystick/SDL_joystick_c.h
@@ -168,6 +168,11 @@ typedef struct _SDL_GamepadMapping
SDL_InputMapping dpdown;
SDL_InputMapping dpleft;
SDL_InputMapping dpright;
+ SDL_InputMapping misc1;
+ SDL_InputMapping paddle1;
+ SDL_InputMapping paddle2;
+ SDL_InputMapping paddle3;
+ SDL_InputMapping paddle4;
SDL_InputMapping leftx;
SDL_InputMapping lefty;
SDL_InputMapping rightx;
diff --git a/src/joystick/virtual/SDL_virtualjoystick.c b/src/joystick/virtual/SDL_virtualjoystick.c
index 9be879b30aa..a2c290b422a 100644
--- a/src/joystick/virtual/SDL_virtualjoystick.c
+++ b/src/joystick/virtual/SDL_virtualjoystick.c
@@ -56,18 +56,6 @@ VIRTUAL_FreeHWData(joystick_hwdata *hwdata)
if (!hwdata) {
return;
}
- if (hwdata->axes) {
- SDL_free((void *)hwdata->axes);
- hwdata->axes = NULL;
- }
- if (hwdata->buttons) {
- SDL_free((void *)hwdata->buttons);
- hwdata->buttons = NULL;
- }
- if (hwdata->hats) {
- SDL_free(hwdata->hats);
- hwdata->hats = NULL;
- }
/* Remove hwdata from SDL-global list */
while (cur) {
@@ -83,81 +71,103 @@ VIRTUAL_FreeHWData(joystick_hwdata *hwdata)
cur = cur->next;
}
+ 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((void *)hwdata->buttons);
+ hwdata->buttons = NULL;
+ }
+ if (hwdata->hats) {
+ SDL_free(hwdata->hats);
+ hwdata->hats = NULL;
+ }
SDL_free(hwdata);
}
int
-SDL_JoystickAttachVirtualInner(SDL_JoystickType type,
- int naxes,
- int nbuttons,
- int nhats)
+SDL_JoystickAttachVirtualInner(const SDL_VirtualJoystickDesc *desc)
{
joystick_hwdata *hwdata = NULL;
int device_index = -1;
- const Uint16 vendor_id = 0;
- const Uint16 product_id = 0;
+ const char *name = NULL;
Uint16 button_mask = 0;
Uint16 axis_mask = 0;
Uint16 *guid16;
+ if (!desc) {
+ return SDL_InvalidParamError("desc");
+ }
+ if (desc->version != SDL_VIRTUAL_JOYSTICK_DESC_VERSION) {
+ /* Is this an old version that we can support? */
+ return SDL_SetError("Unsupported virtual joystick description version %d", desc->version);
+ }
+
hwdata = SDL_calloc(1, sizeof(joystick_hwdata));
if (!hwdata) {
VIRTUAL_FreeHWData(hwdata);
return SDL_OutOfMemory();
}
+ SDL_memcpy(&hwdata->desc, desc, sizeof(*desc));
+
+ if (desc->name) {
+ name = desc->name;
+ } else {
+ switch (desc->type) {
+ case SDL_JOYSTICK_TYPE_GAMECONTROLLER:
+ 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);
- hwdata->naxes = naxes;
- hwdata->nbuttons = nbuttons;
- hwdata->nhats = nhats;
-
- switch (type) {
- case SDL_JOYSTICK_TYPE_GAMECONTROLLER:
- hwdata->name = "Virtual Controller";
- break;
- case SDL_JOYSTICK_TYPE_WHEEL:
- hwdata->name = "Virtual Wheel";
- break;
- case SDL_JOYSTICK_TYPE_ARCADE_STICK:
- hwdata->name = "Virtual Arcade Stick";
- break;
- case SDL_JOYSTICK_TYPE_FLIGHT_STICK:
- hwdata->name = "Virtual Flight Stick";
- break;
- case SDL_JOYSTICK_TYPE_DANCE_PAD:
- hwdata->name = "Virtual Dance Pad";
- break;
- case SDL_JOYSTICK_TYPE_GUITAR:
- hwdata->name = "Virtual Guitar";
- break;
- case SDL_JOYSTICK_TYPE_DRUM_KIT:
- hwdata->name = "Virtual Drum Kit";
- break;
- case SDL_JOYSTICK_TYPE_ARCADE_PAD:
- hwdata->name = "Virtual Arcade Pad";
- break;
- case SDL_JOYSTICK_TYPE_THROTTLE:
- hwdata->name = "Virtual Throttle";
- break;
- default:
- hwdata->name = "Virtual Joystick";
- break;
- }
-
- if (type == SDL_JOYSTICK_TYPE_GAMECONTROLLER) {
+ if (desc->type == SDL_JOYSTICK_TYPE_GAMECONTROLLER) {
int i;
- if (naxes >= 2) {
+ if (desc->naxes >= 2) {
axis_mask |= ((1 << SDL_CONTROLLER_AXIS_LEFTX) | (1 << SDL_CONTROLLER_AXIS_LEFTY));
}
- if (naxes >= 4) {
+ if (desc->naxes >= 4) {
axis_mask |= ((1 << SDL_CONTROLLER_AXIS_RIGHTX) | (1 << SDL_CONTROLLER_AXIS_RIGHTY));
}
- if (naxes >= 6) {
+ if (desc->naxes >= 6) {
axis_mask |= ((1 << SDL_CONTROLLER_AXIS_TRIGGERLEFT) | (1 << SDL_CONTROLLER_AXIS_TRIGGERRIGHT));
}
- for (i = 0; i < nbuttons && i < sizeof(Uint16)*8; ++i) {
+ for (i = 0; i < desc->nbuttons && i < sizeof(Uint16)*8; ++i) {
button_mask |= (1 << i);
}
}
@@ -167,34 +177,41 @@ SDL_JoystickAttachVirtualInner(SDL_JoystickType type,
guid16 = (Uint16 *)hwdata->guid.data;
*guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_VIRTUAL);
*guid16++ = 0;
- *guid16++ = SDL_SwapLE16(vendor_id);
+ *guid16++ = SDL_SwapLE16(desc->vendor_id);
*guid16++ = 0;
- *guid16++ = SDL_SwapLE16(product_id);
+ *guid16++ = SDL_SwapLE16(desc->product_id);
*guid16++ = 0;
*guid16++ = SDL_SwapLE16(button_mask);
*guid16++ = SDL_SwapLE16(axis_mask);
/* Note that this is a Virtual device and what subtype it is */
hwdata->guid.data[14] = 'v';
- hwdata->guid.data[15] = (Uint8)type;
+ hwdata->guid.data[15] = (Uint8)desc->type;
/* Allocate fields for different control-types */
- if (naxes > 0) {
- hwdata->axes = SDL_calloc(naxes, sizeof(Sint16));
+ if (desc->naxes > 0) {
+ hwdata->axes = SDL_calloc(desc->naxes, sizeof(Sint16));
if (!hwdata->axes) {
VIRTUAL_FreeHWData(hwdata);
return SDL_OutOfMemory();
}
+
+ /* Trigger axes are at minimum value at rest */
+ if (desc->type == SDL_JOYSTICK_TYPE_GAMECONTROLLER &&
+ desc->naxes > SDL_CONTROLLER_AXIS_TRIGGERRIGHT) {
+ hwdata->axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT] = SDL_JOYSTICK_AXIS_MIN;
+ hwdata->axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT] = SDL_JOYSTICK_AXIS_MIN;
+ }
}
- if (nbuttons > 0) {
- hwdata->buttons = SDL_calloc(nbuttons, sizeof(Uint8));
+ if (desc->nbuttons > 0) {
+ hwdata->buttons = SDL_calloc(desc->nbuttons, sizeof(Uint8));
if (!hwdata->buttons) {
VIRTUAL_FreeHWData(hwdata);
return SDL_OutOfMemory();
}
}
- if (nhats > 0) {
- hwdata->hats = SDL_calloc(nhats, sizeof(Uint8));
+ if (desc->nhats > 0) {
+ hwdata->hats = SDL_calloc(desc->nhats, sizeof(Uint8));
if (!hwdata->hats) {
VIRTUAL_FreeHWData(hwdata);
return SDL_OutOfMemory();
@@ -243,7 +260,7 @@ SDL_JoystickSetVirtualAxisInner(SDL_Joystick *joystick, int axis, Sint16 value)
}
hwdata = (joystick_hwdata *)joystick->hwdata;
- if (axis < 0 || axis >= hwdata->naxes) {
+ if (axis < 0 || axis >= hwdata->desc.naxes) {
SDL_UnlockJoysticks();
return SDL_SetError("Invalid axis index");
}
@@ -268,7 +285,7 @@ SDL_JoystickSetVirtualButtonInner(SDL_Joystick *joystick, int button, Uint8 valu
}
hwdata = (joystick_hwdata *)joystick->hwdata;
- if (button < 0 || button >= hwdata->nbuttons) {
+ if (button < 0 || button >= hwdata->desc.nbuttons) {
SDL_UnlockJoysticks();
return SDL_SetError("Invalid button index");
}
@@ -293,7 +310,7 @@ SDL_JoystickSetVirtualHatInner(SDL_Joystick *joystick, int hat, Uint8 value)
}
hwdata = (joystick_hwdata *)joystick->hwdata;
- if (hat < 0 || hat >= hwdata->nhats) {
+ if (hat < 0 || hat >= hwdata->desc.nhats) {
SDL_UnlockJoysticks();
return SDL_SetError("Invalid hat index");
}
@@ -338,7 +355,7 @@ VIRTUAL_JoystickGetDeviceName(int device_index)
if (!hwdata) {
return NULL;
}
- return hwdata->name ? hwdata->name : "";
+ return hwdata->name;
}
@@ -399,9 +416,9 @@ VIRTUAL_JoystickOpen(SDL_Joystick *joystick, int device_index)
}
joystick->instance_id = hwdata->instance_id;
joystick->hwdata = hwdata;
- joystick->naxes = hwdata->naxes;
- joystick->nbuttons = hwdata->nbuttons;
- joystick->nhats = hwdata->nhats;
+ joystick->naxes = hwdata->desc.naxes;
+ joystick->nbuttons = hwdata->desc.nbuttons;
+ joystick->nhats = hwdata->desc.nhats;
hwdata->opened = SDL_TRUE;
return 0;
}
@@ -461,13 +478,17 @@ VIRTUAL_JoystickUpdate(SDL_Joystick *joystick)
hwdata = (joystick_hwdata *)joystick->hwdata;
- for (i = 0; i < hwdata->naxes; ++i) {
+ if (hwdata->desc.Update) {
+ hwdata->desc.Update(hwdata->desc.userdata);
+ }
+
+ for (i = 0; i < hwdata->desc.naxes; ++i) {
SDL_PrivateJoystickAxis(joystick, i, hwdata->axes[i]);
}
- for (i = 0; i < hwdata->nbuttons; ++i) {
+ for (i = 0; i < hwdata->desc.nbuttons; ++i) {
SDL_PrivateJoystickButton(joystick, i, hwdata->buttons[i]);
}
- for (i = 0; i < hwdata->nhats; ++i) {
+ for (i = 0; i < hwdata->desc.nhats; ++i) {
SDL_PrivateJoystickHat(joystick, i, hwdata->hats[i]);
}
}
@@ -501,7 +522,134 @@ VIRTUAL_JoystickQuit(void)
static SDL_bool
VIRTUAL_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
{
- return SDL_FALSE;
+ joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index);
+
+ if (hwdata->desc.type != SDL_JOYSTICK_TYPE_GAMECONTROLLER) {
+ return SDL_FALSE;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_A) {
+ out->a.kind = EMappingKind_Button;
+ out->a.target = SDL_CONTROLLER_BUTTON_A;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_B) {
+ out->b.kind = EMappingKind_Button;
+ out->b.target = SDL_CONTROLLER_BUTTON_B;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_X) {
+ out->x.kind = EMappingKind_Button;
+ out->x.target = SDL_CONTROLLER_BUTTON_X;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_Y) {
+ out->y.kind = EMappingKind_Button;
+ out->y.target = SDL_CONTROLLER_BUTTON_Y;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_BACK) {
+ out->back.kind = EMappingKind_Button;
+ out->back.target = SDL_CONTROLLER_BUTTON_BACK;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_GUIDE) {
+ out->guide.kind = EMappingKind_Button;
+ out->guide.target = SDL_CONTROLLER_BUTTON_GUIDE;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_START) {
+ out->start.kind = EMappingKind_Button;
+ out->start.target = SDL_CONTROLLER_BUTTON_START;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_LEFTSTICK) {
+ out->leftstick.kind = EMappingKind_Button;
+ out->leftstick.target = SDL_CONTROLLER_BUTTON_LEFTSTICK;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_RIGHTSTICK) {
+ out->rightstick.kind = EMappingKind_Button;
+ out->rightstick.target = SDL_CONTROLLER_BUTTON_RIGHTSTICK;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_LEFTSHOULDER) {
+ out->leftshoulder.kind = EMappingKind_Button;
+ out->leftshoulder.target = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) {
+ out->rightshoulder.kind = EMappingKind_Button;
+ out->rightshoulder.target = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_DPAD_UP) {
+ out->dpup.kind = EMappingKind_Button;
+ out->dpup.target = SDL_CONTROLLER_BUTTON_DPAD_UP;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_DPAD_DOWN) {
+ out->dpdown.kind = EMappingKind_Button;
+ out->dpdown.target = SDL_CONTROLLER_BUTTON_DPAD_DOWN;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_DPAD_LEFT) {
+ out->dpleft.kind = EMappingKind_Button;
+ out->dpleft.target = SDL_CONTROLLER_BUTTON_DPAD_LEFT;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_DPAD_RIGHT) {
+ out->dpright.kind = EMappingKind_Button;
+ out->dpright.target = SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_MISC1) {
+ out->misc1.kind = EMappingKind_Button;
+ out->misc1.target = SDL_CONTROLLER_BUTTON_MISC1;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_PADDLE1) {
+ out->paddle1.kind = EMappingKind_Button;
+ out->paddle1.target = SDL_CONTROLLER_BUTTON_PADDLE1;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_PADDLE2) {
+ out->paddle2.kind = EMappingKind_Button;
+ out->paddle2.target = SDL_CONTROLLER_BUTTON_PADDLE2;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_PADDLE3) {
+ out->paddle3.kind = EMappingKind_Button;
+ out->paddle3.target = SDL_CONTROLLER_BUTTON_PADDLE3;
+ }
+
+ if (hwdata->desc.nbuttons > SDL_CONTROLLER_BUTTON_PADDLE4) {
+ out->paddle4.kind = EMappingKind_Button;
+ out->paddle4.target = SDL_CONTROLLER_BUTTON_PADDLE4;
+ }
+
+ if (hwdata->desc.naxes > SDL_CONTROLLER_AXIS_LEFTY) {
+ out->leftx.kind = EMappingKind_Axis;
+ out->lefty.kind = EMappingKind_Axis;
+ out->leftx.target = SDL_CONTROLLER_AXIS_LEFTX;
+ out->lefty.target = SDL_CONTROLLER_AXIS_LEFTY;
+ }
+
+ if (hwdata->desc.naxes > SDL_CONTROLLER_AXIS_RIGHTY) {
+ out->rightx.kind = EMappingKind_Axis;
+ out->righty.kind = EMappingKind_Axis;
+ out->rightx.target = SDL_CONTROLLER_AXIS_RIGHTX;
+ out->righty.target = SDL_CONTROLLER_AXIS_RIGHTY;
+ }
+
+ if (hwdata->desc.naxes > SDL_CONTROLLER_AXIS_TRIGGERRIGHT) {
+ out->lefttrigger.kind = EMappingKind_Axis;
+ out->righttrigger.kind = EMappingKind_Axis;
+ out->lefttrigger.target = SDL_CONTROLLER_AXIS_TRIGGERLEFT;
+ out->righttrigger.target = SDL_CONTROLLER_AXIS_TRIGGERRIGHT;
+ }
+
+ return SDL_TRUE;
}
SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver =
diff --git a/src/joystick/virtual/SDL_virtualjoystick_c.h b/src/joystick/virtual/SDL_virtualjoystick_c.h
index b251c2d14af..a12d6c84a9c 100644
--- a/src/joystick/virtual/SDL_virtualjoystick_c.h
+++ b/src/joystick/virtual/SDL_virtualjoystick_c.h
@@ -34,24 +34,18 @@ typedef struct joystick_hwdata
{
SDL_JoystickType type;
SDL_bool attached;
- const char *name;
+ char *name;
SDL_JoystickGUID guid;
- int naxes;
+ SDL_VirtualJoystickDesc desc;
Sint16 *axes;
- int nbuttons;
Uint8 *buttons;
- int nhats;
Uint8 *hats;
SDL_JoystickID instance_id;
SDL_bool opened;
struct joystick_hwdata *next;
} joystick_hwdata;
-int SDL_JoystickAttachVirtualInner(SDL_JoystickType type,
- int naxes,
- int nbuttons,
- int nhats);
-
+int SDL_JoystickAttachVirtualInner(const SDL_VirtualJoystickDesc *desc);
int SDL_JoystickDetachVirtualInner(int device_index);
int SDL_JoystickSetVirtualAxisInner(SDL_Joystick * joystick, int axis, Sint16 value);
@@ -59,4 +53,7 @@ int SDL_JoystickSetVirtualButtonInner(SDL_Joystick * joystick, int button, Uint8
int SDL_JoystickSetVirtualHatInner(SDL_Joystick * joystick, int hat, Uint8 value);
#endif /* SDL_JOYSTICK_VIRTUAL */
+
#endif /* SDL_VIRTUALJOYSTICK_C_H */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/test/testgamecontroller.c b/test/testgamecontroller.c
index 82b6b6c980e..ef0838cb294 100644
--- a/test/testgamecontroller.c
+++ b/test/testgamecontroller.c
@@ -28,6 +28,10 @@
#define SCREEN_WIDTH 512
#define SCREEN_HEIGHT 320
+#define BUTTON_SIZE 50
+#define AXIS_SIZE 50
+
+
/* This is indexed by SDL_GameControllerButton. */
static const struct { int x; int y; } button_positions[] = {
{387, 167}, /* SDL_CONTROLLER_BUTTON_A */
@@ -50,7 +54,9 @@ static const struct { int x; int y; } button_positions[] = {
{330, 135}, /* SDL_CONTROLLER_BUTTON_PADDLE2 */
{132, 175}, /* SDL_CONTROLLER_BUTTON_PADDLE3 */
{330, 175}, /* SDL_CONTROLLER_BUTTON_PADDLE4 */
+ {0, 0}, /* SDL_CONTROLLER_BUTTON_TOUCHPAD */
};
+SDL_COMPILE_TIME_ASSERT(button_positions, SDL_arraysize(button_positions) == SDL_CONTROLLER_BUTTON_MAX);
/* This is indexed by SDL_GameControllerAxis. */
static const struct { int x; int y; double angle; } axis_positions[] = {
@@ -61,6 +67,7 @@ static const struct { int x; int y; double angle; } axis_positions[] = {
{91, -20, 0.0}, /* TRIGGERLEFT */
{375, -20, 0.0}, /* TRIGGERRIGHT */
};
+SDL_COMPILE_TIME_ASSERT(axis_positions, SDL_arraysize(axis_positions) == SDL_CONTROLLER_AXIS_MAX);
static SDL_Window *window = NULL;
static SDL_Renderer *screen = NULL;
@@ -72,6 +79,11 @@ static SDL_Texture *background_front, *background_back, *button, *axis;
static SDL_GameController *gamecontroller;
static SDL_GameController **gamecontrollers;
static int num_controllers = 0;
+static SDL_Joystick *virtual_joystick = NULL;
+static SDL_GameControllerAxis virtual_axis_active = SDL_CONTROLLER_AXIS_INVALID;
+static int virtual_axis_start_x;
+static int virtual_axis_start_y;
+static SDL_GameControllerButton virtual_button_active = SDL_CONTROLLER_BUTTON_INVALID;
static void UpdateWindowTitle()
{
@@ -280,12 +292,170 @@ static void CyclePS5TriggerEffect()
SDL_GameControllerSendEffect(gamecontroller, &state, sizeof(state));
}
+static SDL_bool ShowingFront()
+{
+ SDL_bool showing_front = SDL_TRUE;
+ int i;
+
+ if (gamecontroller) {
+ /* Show the back of the controller if the paddles are being held */
+ for (i = SDL_CONTROLLER_BUTTON_PADDLE1; i <= SDL_CONTROLLER_BUTTON_PADDLE4; ++i) {
+ if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) {
+ showing_front = SDL_FALSE;
+ break;
+ }
+ }
+ }
+ if ((SDL_GetModState() & KMOD_SHIFT) != 0) {
+ showing_front = SDL_FALSE;
+ }
+ return showing_front;
+}
+
+static int OpenVirtualController()
+{
+ SDL_VirtualJoystickDesc desc;
+
+ SDL_zero(desc);
+ desc.version = SDL_VIRTUAL_JOYSTICK_DESC_VERSION;
+ desc.type = SDL_JOYSTICK_TYPE_GAMECONTROLLER;
+ desc.naxes = SDL_CONTROLLER_AXIS_MAX;
+ desc.nbuttons = SDL_CONTROLLER_BUTTON_MAX;
+ return SDL_JoystickAttachVirtualEx(&desc);
+}
+
+static SDL_GameControllerButton FindButtonAtPosition(int x, int y)
+{
+ SDL_Point point;
+ int i;
+ SDL_bool showing_front = ShowingFront();
+
+ point.x = x;
+ point.y = y;
+ for (i = 0; i < SDL_CONTROLLER_BUTTON_TOUCHPAD; ++i) {
+ SDL_bool on_front = (i < SDL_CONTROLLER_BUTTON_PADDLE1 || i > SDL_CONTROLLER_BUTTON_PADDLE4);
+ if (on_front == showing_front) {
+ SDL_Rect rect;
+ rect.x = button_positions[i].x;
+ rect.y = button_positions[i].y;
+ rect.w = BUTTON_SIZE;
+ rect.h = BUTTON_SIZE;
+ if (SDL_PointInRect(&point, &rect)) {
+ return (SDL_GameControllerButton)i;
+ }
+ }
+ }
+ return SDL_CONTROLLER_BUTTON_INVALID;
+}
+
(Patch may be truncated, please check the link at the top of this post.)