Added game controller support for virtual joysticks Fixes https://github.com/libsdl-org/SDL/issues/5662
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c
index fad7d9a..50eb190 100644
--- a/src/joystick/SDL_gamecontroller.c
+++ b/src/joystick/SDL_gamecontroller.c
@@ -464,6 +464,100 @@ 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
@@ -696,6 +790,9 @@ 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);
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index 37025dd..04b6b7a 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -119,6 +119,7 @@ struct _SDL_Joystick
};
/* Device bus definitions */
+#define SDL_HARDWARE_BUS_VIRTUAL 0x00
#define SDL_HARDWARE_BUS_USB 0x03
#define SDL_HARDWARE_BUS_BLUETOOTH 0x05
diff --git a/src/joystick/virtual/SDL_virtualjoystick.c b/src/joystick/virtual/SDL_virtualjoystick.c
index 0e6525d..9be879b 100644
--- a/src/joystick/virtual/SDL_virtualjoystick.c
+++ b/src/joystick/virtual/SDL_virtualjoystick.c
@@ -24,6 +24,7 @@
/* This is the virtual implementation of the SDL joystick API */
+#include "SDL_endian.h"
#include "SDL_virtualjoystick_c.h"
#include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h"
@@ -94,6 +95,11 @@ SDL_JoystickAttachVirtualInner(SDL_JoystickType type,
{
joystick_hwdata *hwdata = NULL;
int device_index = -1;
+ const Uint16 vendor_id = 0;
+ const Uint16 product_id = 0;
+ Uint16 button_mask = 0;
+ Uint16 axis_mask = 0;
+ Uint16 *guid16;
hwdata = SDL_calloc(1, sizeof(joystick_hwdata));
if (!hwdata) {
@@ -104,7 +110,69 @@ SDL_JoystickAttachVirtualInner(SDL_JoystickType type,
hwdata->naxes = naxes;
hwdata->nbuttons = nbuttons;
hwdata->nhats = nhats;
- hwdata->name = "Virtual Joystick";
+
+ 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) {
+ int i;
+
+ if (naxes >= 2) {
+ axis_mask |= ((1 << SDL_CONTROLLER_AXIS_LEFTX) | (1 << SDL_CONTROLLER_AXIS_LEFTY));
+ }
+ if (naxes >= 4) {
+ axis_mask |= ((1 << SDL_CONTROLLER_AXIS_RIGHTX) | (1 << SDL_CONTROLLER_AXIS_RIGHTY));
+ }
+ if (naxes >= 6) {
+ axis_mask |= ((1 << SDL_CONTROLLER_AXIS_TRIGGERLEFT) | (1 << SDL_CONTROLLER_AXIS_TRIGGERRIGHT));
+ }
+
+ for (i = 0; i < nbuttons && i < sizeof(Uint16)*8; ++i) {
+ button_mask |= (1 << i);
+ }
+ }
+
+ /* We only need 16 bits for each of these; space them out to fill 128. */
+ /* Byteswap so devices get same GUID on little/big endian platforms. */
+ guid16 = (Uint16 *)hwdata->guid.data;
+ *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_VIRTUAL);
+ *guid16++ = 0;
+ *guid16++ = SDL_SwapLE16(vendor_id);
+ *guid16++ = 0;
+ *guid16++ = SDL_SwapLE16(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';
@@ -326,6 +394,7 @@ VIRTUAL_JoystickOpen(SDL_Joystick *joystick, int device_index)
return SDL_SetError("No such device");
}
if (hwdata->opened) {
+ /* This should never happen, it's handled by the higher joystick code */
return SDL_SetError("Joystick already opened");
}
joystick->instance_id = hwdata->instance_id;