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
This commit is contained in:
Sam Lantinga 2022-05-15 20:01:12 -07:00
parent 7ad15c5b8f
commit 94eeb587c1
9 changed files with 509 additions and 203 deletions

View File

@ -348,6 +348,44 @@ extern DECLSPEC int SDLCALL SDL_JoystickAttachVirtual(SDL_JoystickType type,
int nbuttons, int nbuttons,
int nhats); 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. * Detach a virtual joystick.
* *

View File

@ -870,3 +870,4 @@
#define SDL_GameControllerPath SDL_GameControllerPath_REAL #define SDL_GameControllerPath SDL_GameControllerPath_REAL
#define SDL_JoystickPathForIndex SDL_JoystickPathForIndex_REAL #define SDL_JoystickPathForIndex SDL_JoystickPathForIndex_REAL
#define SDL_JoystickPath SDL_JoystickPath_REAL #define SDL_JoystickPath SDL_JoystickPath_REAL
#define SDL_JoystickAttachVirtualEx SDL_JoystickAttachVirtualEx_REAL

View File

@ -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_GameControllerPath,(SDL_GameController *a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_JoystickPathForIndex,(int 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(const char*,SDL_JoystickPath,(SDL_Joystick *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_JoystickAttachVirtualEx,(const SDL_VirtualJoystickDesc *a),(a),return)

View File

@ -464,100 +464,6 @@ static int SDLCALL SDL_GameControllerEventWatcher(void *userdata, SDL_Event * ev
return 1; 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__ #ifdef __ANDROID__
/* /*
* Helper function to guess at a mapping based on the elements reported for this controller * 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; return s_pXInputMapping;
} }
#endif #endif
if (!mapping && SDL_IsJoystickVirtual(guid)) {
mapping = SDL_CreateMappingForVirtualController(guid);
}
#ifdef __ANDROID__ #ifdef __ANDROID__
if (!mapping && !SDL_IsJoystickHIDAPI(guid)) { if (!mapping && !SDL_IsJoystickHIDAPI(guid)) {
mapping = SDL_CreateMappingForAndroidController(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), "dpdown", &raw_map->dpdown);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpleft", &raw_map->dpleft); SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpleft", &raw_map->dpleft);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpright", &raw_map->dpright); 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), "leftx", &raw_map->leftx);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "lefty", &raw_map->lefty); SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "lefty", &raw_map->lefty);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightx", &raw_map->rightx); SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightx", &raw_map->rightx);

View File

@ -522,9 +522,23 @@ SDL_JoystickOpen(int device_index)
int int
SDL_JoystickAttachVirtual(SDL_JoystickType type, SDL_JoystickAttachVirtual(SDL_JoystickType type,
int naxes, int nbuttons, int nhats) 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 #if SDL_JOYSTICK_VIRTUAL
return SDL_JoystickAttachVirtualInner(type, naxes, nbuttons, nhats); return SDL_JoystickAttachVirtualInner(desc);
#else #else
return SDL_SetError("SDL not built with virtual-joystick support"); return SDL_SetError("SDL not built with virtual-joystick support");
#endif #endif

View File

@ -168,6 +168,11 @@ typedef struct _SDL_GamepadMapping
SDL_InputMapping dpdown; SDL_InputMapping dpdown;
SDL_InputMapping dpleft; SDL_InputMapping dpleft;
SDL_InputMapping dpright; SDL_InputMapping dpright;
SDL_InputMapping misc1;
SDL_InputMapping paddle1;
SDL_InputMapping paddle2;
SDL_InputMapping paddle3;
SDL_InputMapping paddle4;
SDL_InputMapping leftx; SDL_InputMapping leftx;
SDL_InputMapping lefty; SDL_InputMapping lefty;
SDL_InputMapping rightx; SDL_InputMapping rightx;

View File

@ -56,18 +56,6 @@ VIRTUAL_FreeHWData(joystick_hwdata *hwdata)
if (!hwdata) { if (!hwdata) {
return; 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 */ /* Remove hwdata from SDL-global list */
while (cur) { while (cur) {
@ -83,81 +71,103 @@ VIRTUAL_FreeHWData(joystick_hwdata *hwdata)
cur = cur->next; 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); SDL_free(hwdata);
} }
int int
SDL_JoystickAttachVirtualInner(SDL_JoystickType type, SDL_JoystickAttachVirtualInner(const SDL_VirtualJoystickDesc *desc)
int naxes,
int nbuttons,
int nhats)
{ {
joystick_hwdata *hwdata = NULL; joystick_hwdata *hwdata = NULL;
int device_index = -1; int device_index = -1;
const Uint16 vendor_id = 0; const char *name = NULL;
const Uint16 product_id = 0;
Uint16 button_mask = 0; Uint16 button_mask = 0;
Uint16 axis_mask = 0; Uint16 axis_mask = 0;
Uint16 *guid16; 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)); hwdata = SDL_calloc(1, sizeof(joystick_hwdata));
if (!hwdata) { if (!hwdata) {
VIRTUAL_FreeHWData(hwdata); VIRTUAL_FreeHWData(hwdata);
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_memcpy(&hwdata->desc, desc, sizeof(*desc));
hwdata->naxes = naxes; if (desc->name) {
hwdata->nbuttons = nbuttons; name = desc->name;
hwdata->nhats = nhats; } else {
switch (desc->type) {
switch (type) {
case SDL_JOYSTICK_TYPE_GAMECONTROLLER: case SDL_JOYSTICK_TYPE_GAMECONTROLLER:
hwdata->name = "Virtual Controller"; name = "Virtual Controller";
break; break;
case SDL_JOYSTICK_TYPE_WHEEL: case SDL_JOYSTICK_TYPE_WHEEL:
hwdata->name = "Virtual Wheel"; name = "Virtual Wheel";
break; break;
case SDL_JOYSTICK_TYPE_ARCADE_STICK: case SDL_JOYSTICK_TYPE_ARCADE_STICK:
hwdata->name = "Virtual Arcade Stick"; name = "Virtual Arcade Stick";
break; break;
case SDL_JOYSTICK_TYPE_FLIGHT_STICK: case SDL_JOYSTICK_TYPE_FLIGHT_STICK:
hwdata->name = "Virtual Flight Stick"; name = "Virtual Flight Stick";
break; break;
case SDL_JOYSTICK_TYPE_DANCE_PAD: case SDL_JOYSTICK_TYPE_DANCE_PAD:
hwdata->name = "Virtual Dance Pad"; name = "Virtual Dance Pad";
break; break;
case SDL_JOYSTICK_TYPE_GUITAR: case SDL_JOYSTICK_TYPE_GUITAR:
hwdata->name = "Virtual Guitar"; name = "Virtual Guitar";
break; break;
case SDL_JOYSTICK_TYPE_DRUM_KIT: case SDL_JOYSTICK_TYPE_DRUM_KIT:
hwdata->name = "Virtual Drum Kit"; name = "Virtual Drum Kit";
break; break;
case SDL_JOYSTICK_TYPE_ARCADE_PAD: case SDL_JOYSTICK_TYPE_ARCADE_PAD:
hwdata->name = "Virtual Arcade Pad"; name = "Virtual Arcade Pad";
break; break;
case SDL_JOYSTICK_TYPE_THROTTLE: case SDL_JOYSTICK_TYPE_THROTTLE:
hwdata->name = "Virtual Throttle"; name = "Virtual Throttle";
break; break;
default: default:
hwdata->name = "Virtual Joystick"; name = "Virtual Joystick";
break; break;
} }
}
hwdata->name = SDL_strdup(name);
if (type == SDL_JOYSTICK_TYPE_GAMECONTROLLER) { if (desc->type == SDL_JOYSTICK_TYPE_GAMECONTROLLER) {
int i; int i;
if (naxes >= 2) { if (desc->naxes >= 2) {
axis_mask |= ((1 << SDL_CONTROLLER_AXIS_LEFTX) | (1 << SDL_CONTROLLER_AXIS_LEFTY)); 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)); 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)); 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); button_mask |= (1 << i);
} }
} }
@ -167,34 +177,41 @@ SDL_JoystickAttachVirtualInner(SDL_JoystickType type,
guid16 = (Uint16 *)hwdata->guid.data; guid16 = (Uint16 *)hwdata->guid.data;
*guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_VIRTUAL); *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_VIRTUAL);
*guid16++ = 0; *guid16++ = 0;
*guid16++ = SDL_SwapLE16(vendor_id); *guid16++ = SDL_SwapLE16(desc->vendor_id);
*guid16++ = 0; *guid16++ = 0;
*guid16++ = SDL_SwapLE16(product_id); *guid16++ = SDL_SwapLE16(desc->product_id);
*guid16++ = 0; *guid16++ = 0;
*guid16++ = SDL_SwapLE16(button_mask); *guid16++ = SDL_SwapLE16(button_mask);
*guid16++ = SDL_SwapLE16(axis_mask); *guid16++ = SDL_SwapLE16(axis_mask);
/* Note that this is a Virtual device and what subtype it is */ /* Note that this is a Virtual device and what subtype it is */
hwdata->guid.data[14] = 'v'; hwdata->guid.data[14] = 'v';
hwdata->guid.data[15] = (Uint8)type; hwdata->guid.data[15] = (Uint8)desc->type;
/* Allocate fields for different control-types */ /* Allocate fields for different control-types */
if (naxes > 0) { if (desc->naxes > 0) {
hwdata->axes = SDL_calloc(naxes, sizeof(Sint16)); hwdata->axes = SDL_calloc(desc->naxes, sizeof(Sint16));
if (!hwdata->axes) { if (!hwdata->axes) {
VIRTUAL_FreeHWData(hwdata); VIRTUAL_FreeHWData(hwdata);
return SDL_OutOfMemory(); 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) { if (!hwdata->buttons) {
VIRTUAL_FreeHWData(hwdata); VIRTUAL_FreeHWData(hwdata);
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
} }
if (nhats > 0) { if (desc->nhats > 0) {
hwdata->hats = SDL_calloc(nhats, sizeof(Uint8)); hwdata->hats = SDL_calloc(desc->nhats, sizeof(Uint8));
if (!hwdata->hats) { if (!hwdata->hats) {
VIRTUAL_FreeHWData(hwdata); VIRTUAL_FreeHWData(hwdata);
return SDL_OutOfMemory(); return SDL_OutOfMemory();
@ -243,7 +260,7 @@ SDL_JoystickSetVirtualAxisInner(SDL_Joystick *joystick, int axis, Sint16 value)
} }
hwdata = (joystick_hwdata *)joystick->hwdata; hwdata = (joystick_hwdata *)joystick->hwdata;
if (axis < 0 || axis >= hwdata->naxes) { if (axis < 0 || axis >= hwdata->desc.naxes) {
SDL_UnlockJoysticks(); SDL_UnlockJoysticks();
return SDL_SetError("Invalid axis index"); return SDL_SetError("Invalid axis index");
} }
@ -268,7 +285,7 @@ SDL_JoystickSetVirtualButtonInner(SDL_Joystick *joystick, int button, Uint8 valu
} }
hwdata = (joystick_hwdata *)joystick->hwdata; hwdata = (joystick_hwdata *)joystick->hwdata;
if (button < 0 || button >= hwdata->nbuttons) { if (button < 0 || button >= hwdata->desc.nbuttons) {
SDL_UnlockJoysticks(); SDL_UnlockJoysticks();
return SDL_SetError("Invalid button index"); return SDL_SetError("Invalid button index");
} }
@ -293,7 +310,7 @@ SDL_JoystickSetVirtualHatInner(SDL_Joystick *joystick, int hat, Uint8 value)
} }
hwdata = (joystick_hwdata *)joystick->hwdata; hwdata = (joystick_hwdata *)joystick->hwdata;
if (hat < 0 || hat >= hwdata->nhats) { if (hat < 0 || hat >= hwdata->desc.nhats) {
SDL_UnlockJoysticks(); SDL_UnlockJoysticks();
return SDL_SetError("Invalid hat index"); return SDL_SetError("Invalid hat index");
} }
@ -338,7 +355,7 @@ VIRTUAL_JoystickGetDeviceName(int device_index)
if (!hwdata) { if (!hwdata) {
return NULL; 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->instance_id = hwdata->instance_id;
joystick->hwdata = hwdata; joystick->hwdata = hwdata;
joystick->naxes = hwdata->naxes; joystick->naxes = hwdata->desc.naxes;
joystick->nbuttons = hwdata->nbuttons; joystick->nbuttons = hwdata->desc.nbuttons;
joystick->nhats = hwdata->nhats; joystick->nhats = hwdata->desc.nhats;
hwdata->opened = SDL_TRUE; hwdata->opened = SDL_TRUE;
return 0; return 0;
} }
@ -461,13 +478,17 @@ VIRTUAL_JoystickUpdate(SDL_Joystick *joystick)
hwdata = (joystick_hwdata *)joystick->hwdata; 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]); 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]); 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]); SDL_PrivateJoystickHat(joystick, i, hwdata->hats[i]);
} }
} }
@ -501,7 +522,134 @@ VIRTUAL_JoystickQuit(void)
static SDL_bool static SDL_bool
VIRTUAL_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) VIRTUAL_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
{ {
joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index);
if (hwdata->desc.type != SDL_JOYSTICK_TYPE_GAMECONTROLLER) {
return SDL_FALSE; 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 = SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver =

View File

@ -34,24 +34,18 @@ typedef struct joystick_hwdata
{ {
SDL_JoystickType type; SDL_JoystickType type;
SDL_bool attached; SDL_bool attached;
const char *name; char *name;
SDL_JoystickGUID guid; SDL_JoystickGUID guid;
int naxes; SDL_VirtualJoystickDesc desc;
Sint16 *axes; Sint16 *axes;
int nbuttons;
Uint8 *buttons; Uint8 *buttons;
int nhats;
Uint8 *hats; Uint8 *hats;
SDL_JoystickID instance_id; SDL_JoystickID instance_id;
SDL_bool opened; SDL_bool opened;
struct joystick_hwdata *next; struct joystick_hwdata *next;
} joystick_hwdata; } joystick_hwdata;
int SDL_JoystickAttachVirtualInner(SDL_JoystickType type, int SDL_JoystickAttachVirtualInner(const SDL_VirtualJoystickDesc *desc);
int naxes,
int nbuttons,
int nhats);
int SDL_JoystickDetachVirtualInner(int device_index); int SDL_JoystickDetachVirtualInner(int device_index);
int SDL_JoystickSetVirtualAxisInner(SDL_Joystick * joystick, int axis, Sint16 value); 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); int SDL_JoystickSetVirtualHatInner(SDL_Joystick * joystick, int hat, Uint8 value);
#endif /* SDL_JOYSTICK_VIRTUAL */ #endif /* SDL_JOYSTICK_VIRTUAL */
#endif /* SDL_VIRTUALJOYSTICK_C_H */ #endif /* SDL_VIRTUALJOYSTICK_C_H */
/* vi: set ts=4 sw=4 expandtab: */

View File

@ -28,6 +28,10 @@
#define SCREEN_WIDTH 512 #define SCREEN_WIDTH 512
#define SCREEN_HEIGHT 320 #define SCREEN_HEIGHT 320
#define BUTTON_SIZE 50
#define AXIS_SIZE 50
/* This is indexed by SDL_GameControllerButton. */ /* This is indexed by SDL_GameControllerButton. */
static const struct { int x; int y; } button_positions[] = { static const struct { int x; int y; } button_positions[] = {
{387, 167}, /* SDL_CONTROLLER_BUTTON_A */ {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 */ {330, 135}, /* SDL_CONTROLLER_BUTTON_PADDLE2 */
{132, 175}, /* SDL_CONTROLLER_BUTTON_PADDLE3 */ {132, 175}, /* SDL_CONTROLLER_BUTTON_PADDLE3 */
{330, 175}, /* SDL_CONTROLLER_BUTTON_PADDLE4 */ {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. */ /* This is indexed by SDL_GameControllerAxis. */
static const struct { int x; int y; double angle; } axis_positions[] = { 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 */ {91, -20, 0.0}, /* TRIGGERLEFT */
{375, -20, 0.0}, /* TRIGGERRIGHT */ {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_Window *window = NULL;
static SDL_Renderer *screen = 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 *gamecontroller;
static SDL_GameController **gamecontrollers; static SDL_GameController **gamecontrollers;
static int num_controllers = 0; 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() static void UpdateWindowTitle()
{ {
@ -280,12 +292,170 @@ static void CyclePS5TriggerEffect()
SDL_GameControllerSendEffect(gamecontroller, &state, sizeof(state)); 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;
}
static SDL_GameControllerAxis FindAxisAtPosition(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_AXIS_MAX; ++i) {
if (showing_front) {
SDL_Rect rect;
rect.x = axis_positions[i].x;
rect.y = axis_positions[i].y;
rect.w = AXIS_SIZE;
rect.h = AXIS_SIZE;
if (SDL_PointInRect(&point, &rect)) {
return (SDL_GameControllerAxis)i;
}
}
}
return SDL_CONTROLLER_AXIS_INVALID;
}
static void VirtualControllerMouseMotion(int x, int y)
{
if (virtual_button_active != SDL_CONTROLLER_BUTTON_INVALID) {
if (virtual_axis_active != SDL_CONTROLLER_AXIS_INVALID) {
const int MOVING_DISTANCE = 2;
if (SDL_abs(x - virtual_axis_start_x) >= MOVING_DISTANCE ||
SDL_abs(y - virtual_axis_start_y) >= MOVING_DISTANCE) {
SDL_JoystickSetVirtualButton(virtual_joystick, virtual_button_active, SDL_RELEASED);
virtual_button_active = SDL_CONTROLLER_BUTTON_INVALID;
}
}
}
if (virtual_axis_active != SDL_CONTROLLER_AXIS_INVALID) {
if (virtual_axis_active == SDL_CONTROLLER_AXIS_TRIGGERLEFT ||
virtual_axis_active == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) {
int range = (SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN);
float distance = SDL_clamp(((float)y - virtual_axis_start_y) / AXIS_SIZE, 0.0f, 1.0f);
Sint16 value = (Sint16)(SDL_JOYSTICK_AXIS_MIN + (distance * range));
SDL_JoystickSetVirtualAxis(virtual_joystick, virtual_axis_active, value);
} else {
float distanceX = SDL_clamp(((float)x - virtual_axis_start_x) / AXIS_SIZE, -1.0f, 1.0f);
float distanceY = SDL_clamp(((float)y - virtual_axis_start_y) / AXIS_SIZE, -1.0f, 1.0f);
Sint16 valueX, valueY;
if (distanceX >= 0) {
valueX = (Sint16)(distanceX * SDL_JOYSTICK_AXIS_MAX);
} else {
valueX = (Sint16)(distanceX * -SDL_JOYSTICK_AXIS_MIN);
}
if (distanceY >= 0) {
valueY = (Sint16)(distanceY * SDL_JOYSTICK_AXIS_MAX);
} else {
valueY = (Sint16)(distanceY * -SDL_JOYSTICK_AXIS_MIN);
}
SDL_JoystickSetVirtualAxis(virtual_joystick, virtual_axis_active, valueX);
SDL_JoystickSetVirtualAxis(virtual_joystick, virtual_axis_active+1, valueY);
}
}
}
static void VirtualControllerMouseDown(int x, int y)
{
SDL_GameControllerButton button;
SDL_GameControllerAxis axis;
button = FindButtonAtPosition(x, y);
if (button != SDL_CONTROLLER_BUTTON_INVALID) {
virtual_button_active = button;
SDL_JoystickSetVirtualButton(virtual_joystick, virtual_button_active, SDL_PRESSED);
}
axis = FindAxisAtPosition(x, y);
if (axis != SDL_CONTROLLER_AXIS_INVALID) {
virtual_axis_active = axis;
virtual_axis_start_x = x;
virtual_axis_start_y = y;
}
}
static void VirtualControllerMouseUp(int x, int y)
{
if (virtual_button_active != SDL_CONTROLLER_BUTTON_INVALID) {
SDL_JoystickSetVirtualButton(virtual_joystick, virtual_button_active, SDL_RELEASED);
virtual_button_active = SDL_CONTROLLER_BUTTON_INVALID;
}
if (virtual_axis_active != SDL_CONTROLLER_AXIS_INVALID) {
if (virtual_axis_active == SDL_CONTROLLER_AXIS_TRIGGERLEFT ||
virtual_axis_active == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) {
SDL_JoystickSetVirtualAxis(virtual_joystick, virtual_axis_active, SDL_JOYSTICK_AXIS_MIN);
} else {
SDL_JoystickSetVirtualAxis(virtual_joystick, virtual_axis_active, 0);
SDL_JoystickSetVirtualAxis(virtual_joystick, virtual_axis_active+1, 0);
}
virtual_axis_active = SDL_CONTROLLER_AXIS_INVALID;
}
}
void void
loop(void *arg) loop(void *arg)
{ {
SDL_Event event; SDL_Event event;
int i; int i;
SDL_bool showing_front = SDL_TRUE; SDL_bool showing_front;
/* Update to get the current event state */ /* Update to get the current event state */
SDL_PumpEvents(); SDL_PumpEvents();
@ -356,6 +526,24 @@ loop(void *arg)
} }
break; break;
case SDL_MOUSEBUTTONDOWN:
if (virtual_joystick) {
VirtualControllerMouseDown(event.button.x, event.button.y);
}
break;
case SDL_MOUSEBUTTONUP:
if (virtual_joystick) {
VirtualControllerMouseUp(event.button.x, event.button.y);
}
break;
case SDL_MOUSEMOTION:
if (virtual_joystick) {
VirtualControllerMouseMotion(event.motion.x, event.motion.y);
}
break;
case SDL_KEYDOWN: case SDL_KEYDOWN:
if (event.key.keysym.sym >= SDLK_0 && event.key.keysym.sym <= SDLK_9) { if (event.key.keysym.sym >= SDLK_0 && event.key.keysym.sym <= SDLK_9) {
if (gamecontroller) { if (gamecontroller) {
@ -377,15 +565,7 @@ loop(void *arg)
} }
} }
if (gamecontroller) { showing_front = ShowingFront();
/* 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;
}
}
}
/* blank screen, set up for drawing this frame. */ /* blank screen, set up for drawing this frame. */
SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE); SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
@ -401,8 +581,8 @@ loop(void *arg)
SDL_Rect dst; SDL_Rect dst;
dst.x = button_positions[i].x; dst.x = button_positions[i].x;
dst.y = button_positions[i].y; dst.y = button_positions[i].y;
dst.w = 50; dst.w = BUTTON_SIZE;
dst.h = 50; dst.h = BUTTON_SIZE;
SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, SDL_FLIP_NONE); SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, SDL_FLIP_NONE);
} }
} }
@ -417,16 +597,16 @@ loop(void *arg)
SDL_Rect dst; SDL_Rect dst;
dst.x = axis_positions[i].x; dst.x = axis_positions[i].x;
dst.y = axis_positions[i].y; dst.y = axis_positions[i].y;
dst.w = 50; dst.w = AXIS_SIZE;
dst.h = 50; dst.h = AXIS_SIZE;
SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE); SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
} else if (value > deadzone) { } else if (value > deadzone) {
const double angle = axis_positions[i].angle + 180.0; const double angle = axis_positions[i].angle + 180.0;
SDL_Rect dst; SDL_Rect dst;
dst.x = axis_positions[i].x; dst.x = axis_positions[i].x;
dst.y = axis_positions[i].y; dst.y = axis_positions[i].y;
dst.w = 50; dst.w = AXIS_SIZE;
dst.h = 50; dst.h = AXIS_SIZE;
SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE); SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
} }
} }
@ -627,8 +807,22 @@ main(int argc, char *argv[])
/* !!! FIXME: */ /* !!! FIXME: */
/*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/ /*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/
if (argv[1] && *argv[1] != '-') { for (i = 1; i < argc; ++i) {
if (SDL_strcmp(argv[i], "--virtual") == 0) {
int virtual_index = OpenVirtualController();
if (virtual_index < 0) {
SDL_Log("Couldn't open virtual device: %s\n", SDL_GetError());
} else {
virtual_joystick = SDL_JoystickOpen(virtual_index);
if (!virtual_joystick) {
SDL_Log("Couldn't open virtual device: %s\n", SDL_GetError());
}
}
}
if (argv[i] && *argv[i] != '-') {
controller_index = SDL_atoi(argv[1]); controller_index = SDL_atoi(argv[1]);
break;
}
} }
if (controller_index < num_controllers) { if (controller_index < num_controllers) {
gamecontroller = gamecontrollers[controller_index]; gamecontroller = gamecontrollers[controller_index];