diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -387,6 +387,7 @@ option_string(BACKGROUNDING_SIGNAL "number to use for magic backgrounding signal or 'OFF'" "OFF") option_string(FOREGROUNDING_SIGNAL "number to use for magic foregrounding signal or 'OFF'" "OFF") set_option(HIDAPI "Use HIDAPI for low level joystick drivers" ${OPT_DEF_HIDAPI}) +set_option(JOYSTICK_VIRTUAL "Enable the virtual-joystick driver" ON) set(SDL_SHARED ${SDL_SHARED_ENABLED_BY_DEFAULT} CACHE BOOL "Build a shared version of the library") set(SDL_STATIC ${SDL_STATIC_ENABLED_BY_DEFAULT} CACHE BOOL "Build a static version of the library") @@ -905,6 +906,14 @@ endif() endif() +if(SDL_JOYSTICK) + if(JOYSTICK_VIRTUAL) + set(SDL_JOYSTICK_VIRTUAL 1) + file(GLOB JOYSTICK_VIRTUAL_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/virtual/*.c) + set(SOURCE_FILES ${SOURCE_FILES} ${JOYSTICK_VIRTUAL_SOURCES}) + endif() +endif() + if(SDL_VIDEO) if(VIDEO_DUMMY) set(SDL_VIDEO_DRIVER_DUMMY 1) diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake --- a/include/SDL_config.h.cmake +++ b/include/SDL_config.h.cmake @@ -293,6 +293,7 @@ #cmakedefine SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H @SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H@ #cmakedefine SDL_JOYSTICK_HIDAPI @SDL_JOYSTICK_HIDAPI@ #cmakedefine SDL_JOYSTICK_EMSCRIPTEN @SDL_JOYSTICK_EMSCRIPTEN@ +#cmakedefine SDL_JOYSTICK_VIRTUAL @SDL_JOYSTICK_VIRTUAL@ #cmakedefine SDL_HAPTIC_DUMMY @SDL_HAPTIC_DUMMY@ #cmakedefine SDL_HAPTIC_LINUX @SDL_HAPTIC_LINUX@ #cmakedefine SDL_HAPTIC_IOKIT @SDL_HAPTIC_IOKIT@ diff --git a/include/SDL_gamecontroller.h b/include/SDL_gamecontroller.h --- a/include/SDL_gamecontroller.h +++ b/include/SDL_gamecontroller.h @@ -64,7 +64,8 @@ SDL_CONTROLLER_TYPE_XBOXONE, SDL_CONTROLLER_TYPE_PS3, SDL_CONTROLLER_TYPE_PS4, - SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO + SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO, + SDL_CONTROLLER_TYPE_VIRTUAL } SDL_GameControllerType; typedef enum diff --git a/include/SDL_joystick.h b/include/SDL_joystick.h --- a/include/SDL_joystick.h +++ b/include/SDL_joystick.h @@ -105,6 +105,7 @@ SDL_JOYSTICK_POWER_MAX } SDL_JoystickPowerLevel; + /* Function prototypes */ /** @@ -200,6 +201,36 @@ extern DECLSPEC SDL_Joystick *SDLCALL SDL_JoystickFromPlayerIndex(int player_index); /** + * Attaches a new virtual joystick. + * Returns the joystick's device index, or -1 if an error occurred. + */ +extern DECLSPEC int SDLCALL SDL_JoystickAttachVirtual(SDL_JoystickType type, + int naxes, + int nballs, + int nbuttons, + int nhats); + +/** + * Detaches a virtual joystick + * Returns 0 on success, or -1 if an error occurred. + */ +extern DECLSPEC int SDLCALL SDL_JoystickDetachVirtual(int device_index); + +/** + * Indicates whether or not a virtual-joystick is at a given device index. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_JoystickIsVirtual(int device_index); + +/** + * Set values on an opened, virtual-joystick's controls. + * Returns 0 on success, -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_JoystickSetVirtualAxis(SDL_Joystick * joystick, int axis, Sint16 value); +extern DECLSPEC int SDLCALL SDL_JoystickSetVirtualBall(SDL_Joystick * joystick, int ball, Sint16 xrel, Sint16 yrel); +extern DECLSPEC int SDLCALL SDL_JoystickSetVirtualButton(SDL_Joystick * joystick, int button, Uint8 value); +extern DECLSPEC int SDLCALL SDL_JoystickSetVirtualHat(SDL_Joystick * joystick, int hat, Uint8 value); + +/** * Return the name for this currently opened joystick. * If no name can be found, this function returns NULL. */ diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -749,3 +749,10 @@ #define SDL_GetAndroidSDKVersion SDL_GetAndroidSDKVersion_REAL #define SDL_isupper SDL_isupper_REAL #define SDL_islower SDL_islower_REAL +#define SDL_JoystickAttachVirtual SDL_JoystickAttachVirtual_REAL +#define SDL_JoystickDetachVirtual SDL_JoystickDetachVirtual_REAL +#define SDL_JoystickIsVirtual SDL_JoystickIsVirtual_REAL +#define SDL_JoystickSetVirtualAxis SDL_JoystickSetVirtualAxis_REAL +#define SDL_JoystickSetVirtualBall SDL_JoystickSetVirtualBall_REAL +#define SDL_JoystickSetVirtualButton SDL_JoystickSetVirtualButton_REAL +#define SDL_JoystickSetVirtualHat SDL_JoystickSetVirtualHat_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -809,3 +809,10 @@ #endif SDL_DYNAPI_PROC(int,SDL_isupper,(int a),(a),return) SDL_DYNAPI_PROC(int,SDL_islower,(int a),(a),return) +SDL_DYNAPI_PROC(int,SDL_JoystickAttachVirtual,(SDL_JoystickType a, int b, int c, int d, int e),(a,b,c,d,e),return) +SDL_DYNAPI_PROC(int,SDL_JoystickDetachVirtual,(int a),(a),return) +SDL_DYNAPI_PROC(SDL_bool,SDL_JoystickIsVirtual,(int a),(a),return) +SDL_DYNAPI_PROC(int,SDL_JoystickSetVirtualAxis,(SDL_Joystick *a, int b, Sint16 c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_JoystickSetVirtualBall,(SDL_Joystick *a, int b, Sint16 c, Sint16 d),(a,b,c,d),return) +SDL_DYNAPI_PROC(int,SDL_JoystickSetVirtualButton,(SDL_Joystick *a, int b, Uint8 c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_JoystickSetVirtualHat,(SDL_Joystick *a, int b, Uint8 c),(a,b,c),return) diff --git a/src/joystick/SDL_gamecontrollerdb.h b/src/joystick/SDL_gamecontrollerdb.h --- a/src/joystick/SDL_gamecontrollerdb.h +++ b/src/joystick/SDL_gamecontrollerdb.h @@ -666,6 +666,9 @@ "05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,", "050000005e040000e0020000df070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,", #endif +#if defined(SDL_JOYSTICK_VIRTUAL) + "00000000000000000000000000007601,Virtual Joystick,a:b0,b:b1,x:b2,y:b3,back:b4,guide:b5,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b9,rightshoulder:b10,dpup:b11,dpdown:b12,dpleft:b13,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5", +#endif #if defined(SDL_JOYSTICK_EMSCRIPTEN) "default,Standard Gamepad,a:b0,b:b1,back:b8,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b16,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,", #endif diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -46,6 +46,10 @@ #include #endif +#if SDL_JOYSTICK_VIRTUAL +#include "./virtual/SDL_sysjoystick_c.h" +#endif + static SDL_JoystickDriver *SDL_joystick_drivers[] = { #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) &SDL_WINDOWS_JoystickDriver, @@ -74,6 +78,9 @@ #ifdef SDL_JOYSTICK_HIDAPI &SDL_HIDAPI_JoystickDriver, #endif +#ifdef SDL_JOYSTICK_VIRTUAL + &SDL_VIRTUAL_JoystickDriver, +#endif #if defined(SDL_JOYSTICK_DUMMY) || defined(SDL_JOYSTICK_DISABLED) &SDL_DUMMY_JoystickDriver #endif @@ -473,6 +480,115 @@ } +int +SDL_JoystickAttachVirtual(SDL_JoystickType type, + int naxes, + int nballs, + int nbuttons, + int nhats) +{ +#if SDL_JOYSTICK_VIRTUAL + return SDL_JoystickAttachVirtualInner(type, + naxes, + nballs, + nbuttons, + nhats); +#else + return SDL_SetError("SDL not built with virtual-joystick support"); +#endif +} + + +int +SDL_JoystickDetachVirtual(int device_index) +{ +#if SDL_JOYSTICK_VIRTUAL + SDL_JoystickDriver *driver; + + SDL_LockJoysticks(); + if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) { + if (driver == &SDL_VIRTUAL_JoystickDriver) { + const int result = SDL_JoystickDetachVirtualInner(device_index); + SDL_UnlockJoysticks(); + return result; + } + } + SDL_UnlockJoysticks(); + + return SDL_SetError("Virtual joystick not found at provided index"); +#else + return SDL_SetError("SDL not built with virtual-joystick support"); +#endif +} + + +SDL_bool +SDL_JoystickIsVirtual(int device_index) +{ +#if SDL_JOYSTICK_VIRTUAL + SDL_JoystickDriver *driver; + int driver_device_index; + SDL_bool is_virtual = SDL_FALSE; + + SDL_LockJoysticks(); + if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &driver_device_index)) { + if (driver == &SDL_VIRTUAL_JoystickDriver) { + is_virtual = SDL_TRUE; + } + } + SDL_UnlockJoysticks(); + + return is_virtual; +#else + return SDL_FALSE; +#endif +} + + +int +SDL_JoystickSetVirtualAxis(SDL_Joystick * joystick, int axis, Sint16 value) +{ +#if SDL_JOYSTICK_VIRTUAL + return SDL_JoystickSetVirtualAxisInner(joystick, axis, value); +#else + return SDL_SetError("SDL not built with virtual-joystick support"); +#endif +} + + +int +SDL_JoystickSetVirtualBall(SDL_Joystick * joystick, int axis, Sint16 xrel, Sint16 yrel) +{ +#if SDL_JOYSTICK_VIRTUAL + return SDL_JoystickSetVirtualBallInner(joystick, axis, xrel, yrel); +#else + return SDL_SetError("SDL not built with virtual-joystick support"); +#endif +} + + +int +SDL_JoystickSetVirtualButton(SDL_Joystick * joystick, int button, Uint8 value) +{ +#if SDL_JOYSTICK_VIRTUAL + return SDL_JoystickSetVirtualButtonInner(joystick, button, value); +#else + return SDL_SetError("SDL not built with virtual-joystick support"); +#endif +} + + +int +SDL_JoystickSetVirtualHat(SDL_Joystick * joystick, int hat, Uint8 value) +{ +#if SDL_JOYSTICK_VIRTUAL + return SDL_JoystickSetVirtualHatInner(joystick, hat, value); +#else + return SDL_SetError("SDL not built with virtual-joystick support"); +#endif +} + + /* * Checks to make sure the joystick is valid. */ @@ -1488,6 +1604,8 @@ SDL_strcmp(name, "Wireless Gamepad") == 0) { /* HORI or PowerA Switch Pro Controller clone */ type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; + } else if (SDL_strcmp(name, "Virtual Joystick") == 0) { + type = SDL_CONTROLLER_TYPE_VIRTUAL; } else { type = SDL_CONTROLLER_TYPE_UNKNOWN; } @@ -1549,6 +1667,12 @@ return (guid.data[14] == 'h') ? SDL_TRUE : SDL_FALSE; } +SDL_bool +SDL_IsJoystickVirtual(SDL_JoystickGUID guid) +{ + return (guid.data[14] == 'v') ? SDL_TRUE : SDL_FALSE; +} + static SDL_bool SDL_IsJoystickProductWheel(Uint32 vidpid) { static Uint32 wheel_joysticks[] = { @@ -1640,6 +1764,10 @@ } } + if (SDL_IsJoystickVirtual(guid)) { + return (SDL_JoystickType)guid.data[15]; + } + SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL); vidpid = MAKE_VIDPID(vendor, product); diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h --- a/src/joystick/SDL_joystick_c.h +++ b/src/joystick/SDL_joystick_c.h @@ -74,6 +74,9 @@ /* Function to return whether a joystick guid comes from the HIDAPI driver */ extern SDL_bool SDL_IsJoystickHIDAPI(SDL_JoystickGUID guid); +/* Function to return whether a joystick guid comes from the Virtual driver */ +extern SDL_bool SDL_IsJoystickVirtual(SDL_JoystickGUID guid); + /* Function to return whether a joystick should be ignored */ extern SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid); diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h --- a/src/joystick/SDL_sysjoystick.h +++ b/src/joystick/SDL_sysjoystick.h @@ -152,6 +152,7 @@ extern SDL_JoystickDriver SDL_HIDAPI_JoystickDriver; extern SDL_JoystickDriver SDL_IOS_JoystickDriver; extern SDL_JoystickDriver SDL_LINUX_JoystickDriver; +extern SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver; extern SDL_JoystickDriver SDL_WINDOWS_JoystickDriver; #endif /* SDL_sysjoystick_h_ */ diff --git a/src/joystick/virtual/SDL_sysjoystick.c b/src/joystick/virtual/SDL_sysjoystick.c new file mode 100644 --- /dev/null +++ b/src/joystick/virtual/SDL_sysjoystick.c @@ -0,0 +1,455 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2020 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" + +#if defined(SDL_JOYSTICK_VIRTUAL) + +/* This is the virtual implementation of the SDL joystick API */ + +#include "SDL_sysjoystick_c.h" +#include "../SDL_sysjoystick.h" +#include "../SDL_joystick_c.h" + +extern SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver; + +static joystick_hwdata * g_VJoys = NULL; + + +static joystick_hwdata * +VIRTUAL_HWDataForIndex(int device_index) +{ + joystick_hwdata *vjoy = g_VJoys; + while (vjoy) { + if (device_index == 0) + break; + --device_index; + vjoy = vjoy->next; + } + return vjoy; +} + + +static void +VIRTUAL_FreeHWData(joystick_hwdata *hwdata) +{ + if (!hwdata) { + return; + } + if (hwdata->axes) { + SDL_free((void *)hwdata->axes); + hwdata->axes = NULL; + } + if (hwdata->balls) { + SDL_free((void *)hwdata->balls); + hwdata->balls = 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 */ + joystick_hwdata * cur = g_VJoys; + joystick_hwdata * prev = NULL; + while (cur) { + if (hwdata == cur) { + if (prev) { + prev->next = cur->next; + } else { + g_VJoys = cur->next; + } + break; + } + prev = cur; + cur = cur->next; + } + + SDL_free(hwdata); +} + + +int +SDL_JoystickAttachVirtualInner(SDL_JoystickType type, + int naxes, + int nballs, + int nbuttons, + int nhats) +{ + joystick_hwdata *hwdata = NULL; + int device_index = -1; + + hwdata = SDL_calloc(1, sizeof(joystick_hwdata)); + if (!hwdata) { + VIRTUAL_FreeHWData(hwdata); + return SDL_OutOfMemory(); + } + + hwdata->naxes = naxes; + hwdata->nballs = nballs; + hwdata->nbuttons = nbuttons; + hwdata->nhats = nhats; + hwdata->name = "Virtual Joystick"; + + /* Note that this is a Virtual device and what subtype it is */ + hwdata->guid.data[14] = 'v'; + hwdata->guid.data[15] = (Uint8)type; + + /* Allocate fields for different control-types */ + if (naxes > 0) { + hwdata->axes = SDL_calloc(naxes, sizeof(Sint16)); + if (!hwdata->axes) { + VIRTUAL_FreeHWData(hwdata); + return SDL_OutOfMemory(); + } + } + if (nballs > 0) { + hwdata->balls = SDL_calloc(nballs, sizeof(hwdata->balls[0])); + if (!hwdata->balls) { + VIRTUAL_FreeHWData(hwdata); + return SDL_OutOfMemory(); + } + } + if (nbuttons > 0) { + hwdata->buttons = SDL_calloc(nbuttons, sizeof(Uint8)); + if (!hwdata->buttons) { + VIRTUAL_FreeHWData(hwdata); + return SDL_OutOfMemory(); + } + } + if (nhats > 0) { + hwdata->hats = SDL_calloc(nhats, sizeof(Uint8)); + if (!hwdata->hats) { + VIRTUAL_FreeHWData(hwdata); + return SDL_OutOfMemory(); + } + } + + /* Allocate an instance ID for this device */ + hwdata->instance_id = SDL_GetNextJoystickInstanceID(); + + /* Add virtual joystick to SDL-global lists */ + hwdata->next = g_VJoys; + g_VJoys = hwdata; + SDL_PrivateJoystickAdded(hwdata->instance_id); + + /* Return the new virtual-device's index */ + device_index = SDL_JoystickGetDeviceIndexFromInstanceID(hwdata->instance_id); + return device_index; +} + + +int +SDL_JoystickDetachVirtualInner(int device_index) +{ + SDL_JoystickID instance_id; + joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); + if (!hwdata) { + return SDL_SetError("Virtual joystick data not found"); + } + instance_id = hwdata->instance_id; + VIRTUAL_FreeHWData(hwdata); + SDL_PrivateJoystickRemoved(instance_id); + return 0; +} + + +int +SDL_JoystickSetVirtualAxisInner(SDL_Joystick * joystick, int axis, Sint16 value) +{ + joystick_hwdata *hwdata; + + SDL_LockJoysticks(); + + if (!joystick || !joystick->hwdata) { + SDL_UnlockJoysticks(); + return SDL_SetError("Invalid joystick"); + } + + hwdata = (joystick_hwdata *)joystick->hwdata; + if (axis < 0 || axis >= hwdata->nbuttons) { + SDL_UnlockJoysticks(); + return SDL_SetError("Invalid axis index"); + } + + hwdata->axes[axis] = value; + + SDL_UnlockJoysticks(); + return 0; +} + + +int +SDL_JoystickSetVirtualBallInner(SDL_Joystick * joystick, int ball, Sint16 xrel, Sint16 yrel) +{ + joystick_hwdata *hwdata; + + SDL_LockJoysticks(); + + if (!joystick || !joystick->hwdata) { + SDL_UnlockJoysticks(); + return SDL_SetError("Invalid joystick"); + } + + hwdata = (joystick_hwdata *)joystick->hwdata; + if (ball < 0 || ball >= hwdata->nbuttons) { + SDL_UnlockJoysticks(); + return SDL_SetError("Invalid ball index"); + } + + hwdata->balls[ball].xrel = xrel; + hwdata->balls[ball].yrel = yrel; + + SDL_UnlockJoysticks(); + return 0; +} + + +int +SDL_JoystickSetVirtualButtonInner(SDL_Joystick * joystick, int button, Uint8 value) +{ + joystick_hwdata *hwdata; + + SDL_LockJoysticks(); + + if (!joystick || !joystick->hwdata) { + SDL_UnlockJoysticks(); + return SDL_SetError("Invalid joystick"); + } + + hwdata = (joystick_hwdata *)joystick->hwdata; + if (button < 0 || button >= hwdata->nbuttons) { + SDL_UnlockJoysticks(); + return SDL_SetError("Invalid button index"); + } + + hwdata->buttons[button] = value; + + SDL_UnlockJoysticks(); + return 0; +} + + +int +SDL_JoystickSetVirtualHatInner(SDL_Joystick * joystick, int hat, Uint8 value) +{ + joystick_hwdata *hwdata; + + SDL_LockJoysticks(); + + if (!joystick || !joystick->hwdata) { + SDL_UnlockJoysticks(); + return SDL_SetError("Invalid joystick"); + } + + hwdata = (joystick_hwdata *)joystick->hwdata; + if (hat < 0 || hat >= hwdata->nbuttons) { + SDL_UnlockJoysticks(); + return SDL_SetError("Invalid hat index"); + } + + hwdata->hats[hat] = value; + + SDL_UnlockJoysticks(); + return 0; +} + + +static int +VIRTUAL_JoystickInit(void) +{ + return 0; +} + + +static int +VIRTUAL_JoystickGetCount(void) +{ + int count = 0; + joystick_hwdata *cur = g_VJoys; + while (cur) { + ++count; + cur = cur->next; + } + return count; +} + + +static void +VIRTUAL_JoystickDetect(void) +{ +} + + +static const char * +VIRTUAL_JoystickGetDeviceName(int device_index) +{ + joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); + if (!hwdata) { + return NULL; + } + return hwdata->name ? hwdata->name : ""; +} + + +static int +VIRTUAL_JoystickGetDevicePlayerIndex(int device_index) +{ + return -1; +} + + +static void +VIRTUAL_JoystickSetDevicePlayerIndex(int device_index, int player_index) +{ +} + + +static SDL_JoystickGUID +VIRTUAL_JoystickGetDeviceGUID(int device_index) +{ + joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); + if (!hwdata) { + SDL_JoystickGUID 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 -1; + } + return hwdata->instance_id; +} + + +static int +VIRTUAL_JoystickOpen(SDL_Joystick * joystick, int device_index) +{ + joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); + if (!hwdata) { + return SDL_SetError("No such device"); + } + if (hwdata->opened) { + return SDL_SetError("Joystick already opened"); + } + joystick->instance_id = hwdata->instance_id; + joystick->hwdata = hwdata; + joystick->naxes = hwdata->naxes; + joystick->nballs = hwdata->nballs; + joystick->nbuttons = hwdata->nbuttons; + joystick->nhats = hwdata->nhats; + hwdata->opened = SDL_TRUE; + return 0; +} + + +static int +VIRTUAL_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +{ + return SDL_Unsupported(); +} + + +static void +VIRTUAL_JoystickUpdate(SDL_Joystick * joystick) +{ + joystick_hwdata *hwdata; + + if (!joystick) { + return; + } + if (!joystick->hwdata) { + return; + } + + hwdata = (joystick_hwdata *)joystick->hwdata; + + for (int i = 0; i < hwdata->naxes; ++i) { + SDL_PrivateJoystickAxis(joystick, i, hwdata->axes[i]); + } + for (int i = 0; i < hwdata->nballs; ++i) { + SDL_PrivateJoystickBall(joystick, i, hwdata->balls[i].xrel, hwdata->balls[i].yrel); + } + for (int i = 0; i < hwdata->nbuttons; ++i) { + SDL_PrivateJoystickButton(joystick, i, hwdata->buttons[i]); + } + for (int i = 0; i < hwdata->nhats; ++i) { + SDL_PrivateJoystickHat(joystick, i, hwdata->hats[i]); + } +} + + +static void +VIRTUAL_JoystickClose(SDL_Joystick * joystick) +{ + joystick_hwdata *hwdata; + + if (!joystick) { + return; + } + if (!joystick->hwdata) { + return; + } + + hwdata = (joystick_hwdata *)joystick->hwdata; + hwdata->opened = SDL_FALSE; +} + + +static void +VIRTUAL_JoystickQuit(void) +{ + while (g_VJoys) { + VIRTUAL_FreeHWData(g_VJoys); + } +} + + +SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver = +{ + VIRTUAL_JoystickInit, + VIRTUAL_JoystickGetCount, + VIRTUAL_JoystickDetect, + VIRTUAL_JoystickGetDeviceName, + VIRTUAL_JoystickGetDevicePlayerIndex, + VIRTUAL_JoystickSetDevicePlayerIndex, + VIRTUAL_JoystickGetDeviceGUID, + VIRTUAL_JoystickGetDeviceInstanceID, + VIRTUAL_JoystickOpen, + VIRTUAL_JoystickRumble, + VIRTUAL_JoystickUpdate, + VIRTUAL_JoystickClose, + VIRTUAL_JoystickQuit, +}; + +#endif /* SDL_JOYSTICK_VIRTUAL || SDL_JOYSTICK_DISABLED */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/virtual/SDL_sysjoystick_c.h b/src/joystick/virtual/SDL_sysjoystick_c.h new file mode 100644 --- /dev/null +++ b/src/joystick/virtual/SDL_sysjoystick_c.h @@ -0,0 +1,69 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2020 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_JOYSTICK_VIRTUAL_H +#define SDL_JOYSTICK_VIRTUAL_H + +#if SDL_JOYSTICK_VIRTUAL + +#include "SDL_joystick.h" + +/** + * Data for a virtual, software-only joystick. + */ +typedef struct joystick_hwdata +{ + SDL_JoystickType type; + SDL_bool attached; + const char *name; + SDL_JoystickGUID guid; + int naxes; + Sint16 *axes; + int nballs; + struct { + Sint16 xrel; + Sint16 yrel; + } *balls; + 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 nballs, + int nbuttons, + int nhats); + +int SDL_JoystickDetachVirtualInner(int device_index); + +int SDL_JoystickSetVirtualAxisInner(SDL_Joystick * joystick, int axis, Sint16 value); +int SDL_JoystickSetVirtualBallInner(SDL_Joystick * joystick, int ball, Sint16 xrel, Sint16 yrel); +int SDL_JoystickSetVirtualButtonInner(SDL_Joystick * joystick, int button, Uint8 value); +int SDL_JoystickSetVirtualHatInner(SDL_Joystick * joystick, int hat, Uint8 value); + +#endif /* SDL_JOYSTICK_VIRTUAL */ +#endif /* SDL_JOYSTICK_VIRTUAL_H */ diff --git a/test/testgamecontroller.c b/test/testgamecontroller.c --- a/test/testgamecontroller.c +++ b/test/testgamecontroller.c @@ -310,6 +310,9 @@ case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO: description = "Nintendo Switch Pro Controller"; break; + case SDL_CONTROLLER_TYPE_VIRTUAL: + description = "Virtual Game Controller"; + break; default: description = "Game Controller"; break;