diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c index fad7d9a09..50eb190a5 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 37025ddde..04b6b7a0c 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 0e6525d5f..9be879b30 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;