Fixed bug 5028 - Virtual Joysticks (new joystick backend)
authorSam Lantinga <slouken@libsdl.org>
Fri, 13 Mar 2020 19:08:45 -0700
changeset 13621958bd91f4f4c
parent 13620 317366b2c172
child 13622 1daea6276f1b
Fixed bug 5028 - Virtual Joysticks (new joystick backend)

David Ludwig

I have created a new driver for SDL's Joystick and Game-Controller subsystem: a Virtual driver. This driver allows one to create a software-based joystick, which to SDL applications will look and react like a real joystick, but whose state can be set programmatically. A primary use case for this is to help enable developers to add touch-screen joysticks to their apps.

The driver comes with a set of new, public APIs, with functions to attach and detach joysticks, set virtual-joystick state, and to determine if a joystick is a virtual-one.

Use of virtual joysticks goes as such:

1. Attach one or more virtual joysticks by calling SDL_JoystickAttachVirtual. If successful, this returns the virtual-device's joystick-index.
2. Open the virtual joysticks (using indicies returned by SDL_JoystickAttachVirtual).
3. Call any of the SDL_JoystickSetVirtual* functions when joystick-state changes. Please note that virtual-joystick state will only get applied on the next call to SDL_JoystickUpdate, or when pumping or polling for SDL events (via SDL_PumpEvents or SDL_PollEvent).


Here is a listing of the new, public APIs, at present and subject to change:

------------------------------------------------------------

/**
* 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);

------------------------------------------------------------

Miscellaneous notes on the initial patch, which are also subject to change:

1. no test code is present in SDL, yet. This should, perhaps, change. Initial development was done with an ImGui-based app, which potentially is too thick for use in SDL-official. If tests are to be added, what kind of tests? Automated? Graphical?

2. virtual game controllers can be created by calling SDL_JoystickAttachVirtual with a joystick-type of SDL_JOYSTICK_TYPE_GAME_CONTROLLER, with naxes (num axes) set to SDL_CONTROLLER_AXIS_MAX, and with nbuttons (num buttons) set to SDL_CONTROLLER_BUTTON_MAX. When updating their state, values of type SDL_GameControllerAxis or SDL_GameControllerButton can be casted to an int and used for the control-index (in calls to SDL_JoystickSetVirtual* functions).

3. virtual joysticks' guids are mostly all-zeros with the exception of the last two bytes, the first of which is a 'v', to indicate that the guid is a virtual one, and the second of which is a SDL_JoystickType that has been converted into a Uint8.

4. virtual joysticks are ONLY turned into virtual game-controllers if and when their joystick-type is set to SDL_JOYSTICK_TYPE_GAMECONTROLLER. This is controlled by having SDL's default list of game-controllers have a single entry for a virtual game controller (of guid, "00000000000000000000000000007601", which is subject to the guid-encoding described above).

5. regarding having to call SDL_JoystickUpdate, either directly or indirectly via SDL_PumpEvents or SDL_PollEvents, before new virtual-joystick state becomes active (as specified via SDL_JoystickSetVirtual* function-calls), this was done to match behavior found in SDL's other joystick drivers, almost all of which will only update SDL-state during SDL_JoystickUpdate.

6. the initial patch is based off of SDL 2.0.12

7. the virtual joystick subsystem is disabled by default. It should be possible to enable it by building with SDL_JOYSTICK_VIRTUAL=1



Questions, comments, suggestions, or bug reports very welcome!
CMakeLists.txt
include/SDL_config.h.cmake
include/SDL_gamecontroller.h
include/SDL_joystick.h
src/dynapi/SDL_dynapi_overrides.h
src/dynapi/SDL_dynapi_procs.h
src/joystick/SDL_gamecontrollerdb.h
src/joystick/SDL_joystick.c
src/joystick/SDL_joystick_c.h
src/joystick/SDL_sysjoystick.h
src/joystick/virtual/SDL_sysjoystick.c
src/joystick/virtual/SDL_sysjoystick_c.h
test/testgamecontroller.c
     1.1 --- a/CMakeLists.txt	Fri Mar 13 19:00:24 2020 -0700
     1.2 +++ b/CMakeLists.txt	Fri Mar 13 19:08:45 2020 -0700
     1.3 @@ -387,6 +387,7 @@
     1.4  option_string(BACKGROUNDING_SIGNAL "number to use for magic backgrounding signal or 'OFF'" "OFF")
     1.5  option_string(FOREGROUNDING_SIGNAL "number to use for magic foregrounding signal or 'OFF'" "OFF")
     1.6  set_option(HIDAPI              "Use HIDAPI for low level joystick drivers" ${OPT_DEF_HIDAPI})
     1.7 +set_option(JOYSTICK_VIRTUAL    "Enable the virtual-joystick driver" ON)
     1.8  
     1.9  set(SDL_SHARED ${SDL_SHARED_ENABLED_BY_DEFAULT} CACHE BOOL "Build a shared version of the library")
    1.10  set(SDL_STATIC ${SDL_STATIC_ENABLED_BY_DEFAULT} CACHE BOOL "Build a static version of the library")
    1.11 @@ -905,6 +906,14 @@
    1.12    endif()
    1.13  endif()
    1.14  
    1.15 +if(SDL_JOYSTICK)
    1.16 +  if(JOYSTICK_VIRTUAL)
    1.17 +    set(SDL_JOYSTICK_VIRTUAL 1)
    1.18 +    file(GLOB JOYSTICK_VIRTUAL_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/virtual/*.c)
    1.19 +    set(SOURCE_FILES ${SOURCE_FILES} ${JOYSTICK_VIRTUAL_SOURCES})
    1.20 +  endif()
    1.21 +endif()
    1.22 +
    1.23  if(SDL_VIDEO)
    1.24    if(VIDEO_DUMMY)
    1.25      set(SDL_VIDEO_DRIVER_DUMMY 1)
     2.1 --- a/include/SDL_config.h.cmake	Fri Mar 13 19:00:24 2020 -0700
     2.2 +++ b/include/SDL_config.h.cmake	Fri Mar 13 19:08:45 2020 -0700
     2.3 @@ -293,6 +293,7 @@
     2.4  #cmakedefine SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H @SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H@
     2.5  #cmakedefine SDL_JOYSTICK_HIDAPI @SDL_JOYSTICK_HIDAPI@
     2.6  #cmakedefine SDL_JOYSTICK_EMSCRIPTEN @SDL_JOYSTICK_EMSCRIPTEN@
     2.7 +#cmakedefine SDL_JOYSTICK_VIRTUAL @SDL_JOYSTICK_VIRTUAL@
     2.8  #cmakedefine SDL_HAPTIC_DUMMY @SDL_HAPTIC_DUMMY@
     2.9  #cmakedefine SDL_HAPTIC_LINUX @SDL_HAPTIC_LINUX@
    2.10  #cmakedefine SDL_HAPTIC_IOKIT @SDL_HAPTIC_IOKIT@
     3.1 --- a/include/SDL_gamecontroller.h	Fri Mar 13 19:00:24 2020 -0700
     3.2 +++ b/include/SDL_gamecontroller.h	Fri Mar 13 19:08:45 2020 -0700
     3.3 @@ -64,7 +64,8 @@
     3.4      SDL_CONTROLLER_TYPE_XBOXONE,
     3.5      SDL_CONTROLLER_TYPE_PS3,
     3.6      SDL_CONTROLLER_TYPE_PS4,
     3.7 -    SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO
     3.8 +    SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO,
     3.9 +    SDL_CONTROLLER_TYPE_VIRTUAL
    3.10  } SDL_GameControllerType;
    3.11  
    3.12  typedef enum
     4.1 --- a/include/SDL_joystick.h	Fri Mar 13 19:00:24 2020 -0700
     4.2 +++ b/include/SDL_joystick.h	Fri Mar 13 19:08:45 2020 -0700
     4.3 @@ -105,6 +105,7 @@
     4.4      SDL_JOYSTICK_POWER_MAX
     4.5  } SDL_JoystickPowerLevel;
     4.6  
     4.7 +
     4.8  /* Function prototypes */
     4.9  
    4.10  /**
    4.11 @@ -200,6 +201,36 @@
    4.12  extern DECLSPEC SDL_Joystick *SDLCALL SDL_JoystickFromPlayerIndex(int player_index);
    4.13  
    4.14  /**
    4.15 + * Attaches a new virtual joystick.
    4.16 + * Returns the joystick's device index, or -1 if an error occurred.
    4.17 + */
    4.18 +extern DECLSPEC int SDLCALL SDL_JoystickAttachVirtual(SDL_JoystickType type,
    4.19 +                                                      int naxes,
    4.20 +                                                      int nballs,
    4.21 +                                                      int nbuttons,
    4.22 +                                                      int nhats);
    4.23 +
    4.24 +/**
    4.25 + * Detaches a virtual joystick
    4.26 + * Returns 0 on success, or -1 if an error occurred.
    4.27 + */
    4.28 +extern DECLSPEC int SDLCALL SDL_JoystickDetachVirtual(int device_index);
    4.29 +
    4.30 +/**
    4.31 + * Indicates whether or not a virtual-joystick is at a given device index.
    4.32 + */
    4.33 +extern DECLSPEC SDL_bool SDLCALL SDL_JoystickIsVirtual(int device_index);
    4.34 +
    4.35 +/**
    4.36 + * Set values on an opened, virtual-joystick's controls.
    4.37 + * Returns 0 on success, -1 on error.
    4.38 + */
    4.39 +extern DECLSPEC int SDLCALL SDL_JoystickSetVirtualAxis(SDL_Joystick * joystick, int axis, Sint16 value);
    4.40 +extern DECLSPEC int SDLCALL SDL_JoystickSetVirtualBall(SDL_Joystick * joystick, int ball, Sint16 xrel, Sint16 yrel);
    4.41 +extern DECLSPEC int SDLCALL SDL_JoystickSetVirtualButton(SDL_Joystick * joystick, int button, Uint8 value);
    4.42 +extern DECLSPEC int SDLCALL SDL_JoystickSetVirtualHat(SDL_Joystick * joystick, int hat, Uint8 value);
    4.43 +
    4.44 +/**
    4.45   *  Return the name for this currently opened joystick.
    4.46   *  If no name can be found, this function returns NULL.
    4.47   */
     5.1 --- a/src/dynapi/SDL_dynapi_overrides.h	Fri Mar 13 19:00:24 2020 -0700
     5.2 +++ b/src/dynapi/SDL_dynapi_overrides.h	Fri Mar 13 19:08:45 2020 -0700
     5.3 @@ -749,3 +749,10 @@
     5.4  #define SDL_GetAndroidSDKVersion SDL_GetAndroidSDKVersion_REAL
     5.5  #define SDL_isupper SDL_isupper_REAL
     5.6  #define SDL_islower SDL_islower_REAL
     5.7 +#define SDL_JoystickAttachVirtual SDL_JoystickAttachVirtual_REAL
     5.8 +#define SDL_JoystickDetachVirtual SDL_JoystickDetachVirtual_REAL
     5.9 +#define SDL_JoystickIsVirtual SDL_JoystickIsVirtual_REAL
    5.10 +#define SDL_JoystickSetVirtualAxis SDL_JoystickSetVirtualAxis_REAL
    5.11 +#define SDL_JoystickSetVirtualBall SDL_JoystickSetVirtualBall_REAL
    5.12 +#define SDL_JoystickSetVirtualButton SDL_JoystickSetVirtualButton_REAL
    5.13 +#define SDL_JoystickSetVirtualHat SDL_JoystickSetVirtualHat_REAL
     6.1 --- a/src/dynapi/SDL_dynapi_procs.h	Fri Mar 13 19:00:24 2020 -0700
     6.2 +++ b/src/dynapi/SDL_dynapi_procs.h	Fri Mar 13 19:08:45 2020 -0700
     6.3 @@ -809,3 +809,10 @@
     6.4  #endif
     6.5  SDL_DYNAPI_PROC(int,SDL_isupper,(int a),(a),return)
     6.6  SDL_DYNAPI_PROC(int,SDL_islower,(int a),(a),return)
     6.7 +SDL_DYNAPI_PROC(int,SDL_JoystickAttachVirtual,(SDL_JoystickType a, int b, int c, int d, int e),(a,b,c,d,e),return)
     6.8 +SDL_DYNAPI_PROC(int,SDL_JoystickDetachVirtual,(int a),(a),return)
     6.9 +SDL_DYNAPI_PROC(SDL_bool,SDL_JoystickIsVirtual,(int a),(a),return)
    6.10 +SDL_DYNAPI_PROC(int,SDL_JoystickSetVirtualAxis,(SDL_Joystick *a, int b, Sint16 c),(a,b,c),return)
    6.11 +SDL_DYNAPI_PROC(int,SDL_JoystickSetVirtualBall,(SDL_Joystick *a, int b, Sint16 c, Sint16 d),(a,b,c,d),return)
    6.12 +SDL_DYNAPI_PROC(int,SDL_JoystickSetVirtualButton,(SDL_Joystick *a, int b, Uint8 c),(a,b,c),return)
    6.13 +SDL_DYNAPI_PROC(int,SDL_JoystickSetVirtualHat,(SDL_Joystick *a, int b, Uint8 c),(a,b,c),return)
     7.1 --- a/src/joystick/SDL_gamecontrollerdb.h	Fri Mar 13 19:00:24 2020 -0700
     7.2 +++ b/src/joystick/SDL_gamecontrollerdb.h	Fri Mar 13 19:08:45 2020 -0700
     7.3 @@ -707,6 +707,9 @@
     7.4      "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,",
     7.5      "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,",
     7.6  #endif
     7.7 +#if defined(SDL_JOYSTICK_VIRTUAL)
     7.8 +    "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",
     7.9 +#endif
    7.10  #if defined(SDL_JOYSTICK_EMSCRIPTEN)
    7.11      "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,",
    7.12  #endif
     8.1 --- a/src/joystick/SDL_joystick.c	Fri Mar 13 19:00:24 2020 -0700
     8.2 +++ b/src/joystick/SDL_joystick.c	Fri Mar 13 19:08:45 2020 -0700
     8.3 @@ -46,6 +46,10 @@
     8.4  #include <tlhelp32.h>
     8.5  #endif
     8.6  
     8.7 +#if SDL_JOYSTICK_VIRTUAL
     8.8 +#include "./virtual/SDL_sysjoystick_c.h"
     8.9 +#endif
    8.10 +
    8.11  static SDL_JoystickDriver *SDL_joystick_drivers[] = {
    8.12  #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
    8.13      &SDL_WINDOWS_JoystickDriver,
    8.14 @@ -74,6 +78,9 @@
    8.15  #ifdef SDL_JOYSTICK_HIDAPI
    8.16      &SDL_HIDAPI_JoystickDriver,
    8.17  #endif
    8.18 +#ifdef SDL_JOYSTICK_VIRTUAL
    8.19 +    &SDL_VIRTUAL_JoystickDriver,
    8.20 +#endif
    8.21  #if defined(SDL_JOYSTICK_DUMMY) || defined(SDL_JOYSTICK_DISABLED)
    8.22      &SDL_DUMMY_JoystickDriver
    8.23  #endif
    8.24 @@ -456,6 +463,115 @@
    8.25  }
    8.26  
    8.27  
    8.28 +int
    8.29 +SDL_JoystickAttachVirtual(SDL_JoystickType type,
    8.30 +                          int naxes,
    8.31 +                          int nballs,
    8.32 +                          int nbuttons,
    8.33 +                          int nhats)
    8.34 +{
    8.35 +#if SDL_JOYSTICK_VIRTUAL
    8.36 +    return SDL_JoystickAttachVirtualInner(type,
    8.37 +                                          naxes,
    8.38 +                                          nballs,
    8.39 +                                          nbuttons,
    8.40 +                                          nhats);
    8.41 +#else
    8.42 +    return SDL_SetError("SDL not built with virtual-joystick support");
    8.43 +#endif
    8.44 +}
    8.45 +
    8.46 +
    8.47 +int
    8.48 +SDL_JoystickDetachVirtual(int device_index)
    8.49 +{
    8.50 +#if SDL_JOYSTICK_VIRTUAL
    8.51 +    SDL_JoystickDriver *driver;
    8.52 +
    8.53 +    SDL_LockJoysticks();
    8.54 +    if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
    8.55 +        if (driver == &SDL_VIRTUAL_JoystickDriver) {
    8.56 +            const int result = SDL_JoystickDetachVirtualInner(device_index);
    8.57 +            SDL_UnlockJoysticks();
    8.58 +            return result;
    8.59 +        }
    8.60 +    }
    8.61 +    SDL_UnlockJoysticks();
    8.62 +
    8.63 +    return SDL_SetError("Virtual joystick not found at provided index");
    8.64 +#else
    8.65 +    return SDL_SetError("SDL not built with virtual-joystick support");
    8.66 +#endif
    8.67 +}
    8.68 +
    8.69 +
    8.70 +SDL_bool
    8.71 +SDL_JoystickIsVirtual(int device_index)
    8.72 +{
    8.73 +#if SDL_JOYSTICK_VIRTUAL
    8.74 +    SDL_JoystickDriver *driver;
    8.75 +    int driver_device_index;
    8.76 +    SDL_bool is_virtual = SDL_FALSE;
    8.77 +
    8.78 +    SDL_LockJoysticks();
    8.79 +    if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &driver_device_index)) {
    8.80 +        if (driver == &SDL_VIRTUAL_JoystickDriver) {
    8.81 +            is_virtual = SDL_TRUE;
    8.82 +        }
    8.83 +    }
    8.84 +    SDL_UnlockJoysticks();
    8.85 +
    8.86 +    return is_virtual;
    8.87 +#else
    8.88 +    return SDL_FALSE;
    8.89 +#endif
    8.90 +}
    8.91 +
    8.92 +
    8.93 +int
    8.94 +SDL_JoystickSetVirtualAxis(SDL_Joystick * joystick, int axis, Sint16 value)
    8.95 +{
    8.96 +#if SDL_JOYSTICK_VIRTUAL
    8.97 +    return SDL_JoystickSetVirtualAxisInner(joystick, axis, value);
    8.98 +#else
    8.99 +    return SDL_SetError("SDL not built with virtual-joystick support");
   8.100 +#endif
   8.101 +}
   8.102 +
   8.103 +
   8.104 +int
   8.105 +SDL_JoystickSetVirtualBall(SDL_Joystick * joystick, int axis, Sint16 xrel, Sint16 yrel)
   8.106 +{
   8.107 +#if SDL_JOYSTICK_VIRTUAL
   8.108 +    return SDL_JoystickSetVirtualBallInner(joystick, axis, xrel, yrel);
   8.109 +#else
   8.110 +    return SDL_SetError("SDL not built with virtual-joystick support");
   8.111 +#endif
   8.112 +}
   8.113 +
   8.114 +
   8.115 +int
   8.116 +SDL_JoystickSetVirtualButton(SDL_Joystick * joystick, int button, Uint8 value)
   8.117 +{
   8.118 +#if SDL_JOYSTICK_VIRTUAL
   8.119 +    return SDL_JoystickSetVirtualButtonInner(joystick, button, value);
   8.120 +#else
   8.121 +    return SDL_SetError("SDL not built with virtual-joystick support");
   8.122 +#endif
   8.123 +}
   8.124 +
   8.125 +
   8.126 +int
   8.127 +SDL_JoystickSetVirtualHat(SDL_Joystick * joystick, int hat, Uint8 value)
   8.128 +{
   8.129 +#if SDL_JOYSTICK_VIRTUAL
   8.130 +    return SDL_JoystickSetVirtualHatInner(joystick, hat, value);
   8.131 +#else
   8.132 +    return SDL_SetError("SDL not built with virtual-joystick support");
   8.133 +#endif
   8.134 +}
   8.135 +
   8.136 +
   8.137  /*
   8.138   * Checks to make sure the joystick is valid.
   8.139   */
   8.140 @@ -1563,6 +1679,8 @@
   8.141                  SDL_strcmp(name, "Wireless Gamepad") == 0) {
   8.142                  /* HORI or PowerA Switch Pro Controller clone */
   8.143                  type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO;
   8.144 +            } else if (SDL_strcmp(name, "Virtual Joystick") == 0) {
   8.145 +                type = SDL_CONTROLLER_TYPE_VIRTUAL;
   8.146              } else {
   8.147                  type = SDL_CONTROLLER_TYPE_UNKNOWN;
   8.148              }
   8.149 @@ -1624,6 +1742,12 @@
   8.150      return (guid.data[14] == 'h') ? SDL_TRUE : SDL_FALSE;
   8.151  }
   8.152  
   8.153 +SDL_bool
   8.154 +SDL_IsJoystickVirtual(SDL_JoystickGUID guid)
   8.155 +{
   8.156 +    return (guid.data[14] == 'v') ? SDL_TRUE : SDL_FALSE;
   8.157 +}
   8.158 +
   8.159  static SDL_bool SDL_IsJoystickProductWheel(Uint32 vidpid)
   8.160  {
   8.161      static Uint32 wheel_joysticks[] = {
   8.162 @@ -1715,6 +1839,10 @@
   8.163          }
   8.164      }
   8.165  
   8.166 +    if (SDL_IsJoystickVirtual(guid)) {
   8.167 +        return (SDL_JoystickType)guid.data[15];
   8.168 +    }
   8.169 +
   8.170      SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
   8.171      vidpid = MAKE_VIDPID(vendor, product);
   8.172  
     9.1 --- a/src/joystick/SDL_joystick_c.h	Fri Mar 13 19:00:24 2020 -0700
     9.2 +++ b/src/joystick/SDL_joystick_c.h	Fri Mar 13 19:08:45 2020 -0700
     9.3 @@ -73,6 +73,9 @@
     9.4  /* Function to return whether a joystick guid comes from the HIDAPI driver */
     9.5  extern SDL_bool SDL_IsJoystickHIDAPI(SDL_JoystickGUID guid);
     9.6  
     9.7 +/* Function to return whether a joystick guid comes from the Virtual driver */
     9.8 +extern SDL_bool SDL_IsJoystickVirtual(SDL_JoystickGUID guid);
     9.9 +
    9.10  /* Function to return whether a joystick should be ignored */
    9.11  extern SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid);
    9.12  
    10.1 --- a/src/joystick/SDL_sysjoystick.h	Fri Mar 13 19:00:24 2020 -0700
    10.2 +++ b/src/joystick/SDL_sysjoystick.h	Fri Mar 13 19:08:45 2020 -0700
    10.3 @@ -152,6 +152,7 @@
    10.4  extern SDL_JoystickDriver SDL_HIDAPI_JoystickDriver;
    10.5  extern SDL_JoystickDriver SDL_IOS_JoystickDriver;
    10.6  extern SDL_JoystickDriver SDL_LINUX_JoystickDriver;
    10.7 +extern SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver;
    10.8  extern SDL_JoystickDriver SDL_WINDOWS_JoystickDriver;
    10.9  
   10.10  #endif /* SDL_sysjoystick_h_ */
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/src/joystick/virtual/SDL_sysjoystick.c	Fri Mar 13 19:08:45 2020 -0700
    11.3 @@ -0,0 +1,455 @@
    11.4 +/*
    11.5 +  Simple DirectMedia Layer
    11.6 +  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
    11.7 +
    11.8 +  This software is provided 'as-is', without any express or implied
    11.9 +  warranty.  In no event will the authors be held liable for any damages
   11.10 +  arising from the use of this software.
   11.11 +
   11.12 +  Permission is granted to anyone to use this software for any purpose,
   11.13 +  including commercial applications, and to alter it and redistribute it
   11.14 +  freely, subject to the following restrictions:
   11.15 +
   11.16 +  1. The origin of this software must not be misrepresented; you must not
   11.17 +     claim that you wrote the original software. If you use this software
   11.18 +     in a product, an acknowledgment in the product documentation would be
   11.19 +     appreciated but is not required.
   11.20 +  2. Altered source versions must be plainly marked as such, and must not be
   11.21 +     misrepresented as being the original software.
   11.22 +  3. This notice may not be removed or altered from any source distribution.
   11.23 +*/
   11.24 +#include "../../SDL_internal.h"
   11.25 +
   11.26 +#if defined(SDL_JOYSTICK_VIRTUAL)
   11.27 +
   11.28 +/* This is the virtual implementation of the SDL joystick API */
   11.29 +
   11.30 +#include "SDL_sysjoystick_c.h"
   11.31 +#include "../SDL_sysjoystick.h"
   11.32 +#include "../SDL_joystick_c.h"
   11.33 +
   11.34 +extern SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver;
   11.35 +
   11.36 +static joystick_hwdata * g_VJoys = NULL;
   11.37 +
   11.38 +
   11.39 +static joystick_hwdata *
   11.40 +VIRTUAL_HWDataForIndex(int device_index)
   11.41 +{
   11.42 +    joystick_hwdata *vjoy = g_VJoys;
   11.43 +    while (vjoy) {
   11.44 +        if (device_index == 0)
   11.45 +            break;
   11.46 +        --device_index;
   11.47 +        vjoy = vjoy->next;
   11.48 +    }
   11.49 +    return vjoy;
   11.50 +}
   11.51 +
   11.52 +
   11.53 +static void
   11.54 +VIRTUAL_FreeHWData(joystick_hwdata *hwdata)
   11.55 +{
   11.56 +    if (!hwdata) {
   11.57 +        return;
   11.58 +    }
   11.59 +    if (hwdata->axes) {
   11.60 +        SDL_free((void *)hwdata->axes);
   11.61 +        hwdata->axes = NULL;
   11.62 +    }
   11.63 +    if (hwdata->balls) {
   11.64 +        SDL_free((void *)hwdata->balls);
   11.65 +        hwdata->balls = NULL;
   11.66 +    }
   11.67 +    if (hwdata->buttons) {
   11.68 +        SDL_free((void *)hwdata->buttons);
   11.69 +        hwdata->buttons = NULL;
   11.70 +    }
   11.71 +    if (hwdata->hats) {
   11.72 +        SDL_free(hwdata->hats);
   11.73 +        hwdata->hats = NULL;
   11.74 +    }
   11.75 +
   11.76 +    /* Remove hwdata from SDL-global list */
   11.77 +    joystick_hwdata * cur = g_VJoys;
   11.78 +    joystick_hwdata * prev = NULL;
   11.79 +    while (cur) {
   11.80 +        if (hwdata == cur) {
   11.81 +            if (prev) {
   11.82 +                prev->next = cur->next;
   11.83 +            } else {
   11.84 +                g_VJoys = cur->next;
   11.85 +            }
   11.86 +            break;
   11.87 +        }
   11.88 +        prev = cur;
   11.89 +        cur = cur->next;
   11.90 +    }
   11.91 +
   11.92 +    SDL_free(hwdata);
   11.93 +}
   11.94 +
   11.95 +
   11.96 +int
   11.97 +SDL_JoystickAttachVirtualInner(SDL_JoystickType type,
   11.98 +                               int naxes,
   11.99 +                               int nballs,
  11.100 +                               int nbuttons,
  11.101 +                               int nhats)
  11.102 +{
  11.103 +    joystick_hwdata *hwdata = NULL;
  11.104 +    int device_index = -1;
  11.105 +
  11.106 +    hwdata = SDL_calloc(1, sizeof(joystick_hwdata));
  11.107 +    if (!hwdata) {
  11.108 +        VIRTUAL_FreeHWData(hwdata);
  11.109 +        return SDL_OutOfMemory();
  11.110 +    }
  11.111 +
  11.112 +    hwdata->naxes = naxes;
  11.113 +    hwdata->nballs = nballs;
  11.114 +    hwdata->nbuttons = nbuttons;
  11.115 +    hwdata->nhats = nhats;
  11.116 +    hwdata->name = "Virtual Joystick";
  11.117 +
  11.118 +    /* Note that this is a Virtual device and what subtype it is */
  11.119 +    hwdata->guid.data[14] = 'v';
  11.120 +    hwdata->guid.data[15] = (Uint8)type;
  11.121 +
  11.122 +    /* Allocate fields for different control-types */
  11.123 +    if (naxes > 0) {
  11.124 +        hwdata->axes = SDL_calloc(naxes, sizeof(Sint16));
  11.125 +        if (!hwdata->axes) {
  11.126 +            VIRTUAL_FreeHWData(hwdata);
  11.127 +            return SDL_OutOfMemory();
  11.128 +        }
  11.129 +    }
  11.130 +    if (nballs > 0) {
  11.131 +        hwdata->balls = SDL_calloc(nballs, sizeof(hwdata->balls[0]));
  11.132 +        if (!hwdata->balls) {
  11.133 +            VIRTUAL_FreeHWData(hwdata);
  11.134 +            return SDL_OutOfMemory();
  11.135 +        }
  11.136 +    }
  11.137 +    if (nbuttons > 0) {
  11.138 +        hwdata->buttons = SDL_calloc(nbuttons, sizeof(Uint8));
  11.139 +        if (!hwdata->buttons) {
  11.140 +            VIRTUAL_FreeHWData(hwdata);
  11.141 +            return SDL_OutOfMemory();
  11.142 +        }
  11.143 +    }
  11.144 +    if (nhats > 0) {
  11.145 +        hwdata->hats = SDL_calloc(nhats, sizeof(Uint8));
  11.146 +        if (!hwdata->hats) {
  11.147 +            VIRTUAL_FreeHWData(hwdata);
  11.148 +            return SDL_OutOfMemory();
  11.149 +        }
  11.150 +    }
  11.151 +
  11.152 +    /* Allocate an instance ID for this device */
  11.153 +    hwdata->instance_id = SDL_GetNextJoystickInstanceID();
  11.154 +
  11.155 +    /* Add virtual joystick to SDL-global lists */
  11.156 +    hwdata->next = g_VJoys;
  11.157 +    g_VJoys = hwdata;
  11.158 +    SDL_PrivateJoystickAdded(hwdata->instance_id);
  11.159 +
  11.160 +    /* Return the new virtual-device's index */
  11.161 +    device_index = SDL_JoystickGetDeviceIndexFromInstanceID(hwdata->instance_id);
  11.162 +    return device_index;
  11.163 +}
  11.164 +
  11.165 +
  11.166 +int
  11.167 +SDL_JoystickDetachVirtualInner(int device_index)
  11.168 +{
  11.169 +    SDL_JoystickID instance_id;
  11.170 +    joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index);
  11.171 +    if (!hwdata) {
  11.172 +        return SDL_SetError("Virtual joystick data not found");
  11.173 +    }
  11.174 +    instance_id = hwdata->instance_id;
  11.175 +    VIRTUAL_FreeHWData(hwdata);
  11.176 +    SDL_PrivateJoystickRemoved(instance_id);
  11.177 +    return 0;
  11.178 +}
  11.179 +
  11.180 +
  11.181 +int
  11.182 +SDL_JoystickSetVirtualAxisInner(SDL_Joystick * joystick, int axis, Sint16 value)
  11.183 +{
  11.184 +    joystick_hwdata *hwdata;
  11.185 +
  11.186 +    SDL_LockJoysticks();
  11.187 +
  11.188 +    if (!joystick || !joystick->hwdata) {
  11.189 +        SDL_UnlockJoysticks();
  11.190 +        return SDL_SetError("Invalid joystick");
  11.191 +    }
  11.192 +
  11.193 +    hwdata = (joystick_hwdata *)joystick->hwdata;
  11.194 +    if (axis < 0 || axis >= hwdata->nbuttons) {
  11.195 +        SDL_UnlockJoysticks();
  11.196 +        return SDL_SetError("Invalid axis index");
  11.197 +    }
  11.198 +
  11.199 +    hwdata->axes[axis] = value;
  11.200 +
  11.201 +    SDL_UnlockJoysticks();
  11.202 +    return 0;
  11.203 +}
  11.204 +
  11.205 +
  11.206 +int
  11.207 +SDL_JoystickSetVirtualBallInner(SDL_Joystick * joystick, int ball, Sint16 xrel, Sint16 yrel)
  11.208 +{
  11.209 +    joystick_hwdata *hwdata;
  11.210 +
  11.211 +    SDL_LockJoysticks();
  11.212 +
  11.213 +    if (!joystick || !joystick->hwdata) {
  11.214 +        SDL_UnlockJoysticks();
  11.215 +        return SDL_SetError("Invalid joystick");
  11.216 +    }
  11.217 +
  11.218 +    hwdata = (joystick_hwdata *)joystick->hwdata;
  11.219 +    if (ball < 0 || ball >= hwdata->nbuttons) {
  11.220 +        SDL_UnlockJoysticks();
  11.221 +        return SDL_SetError("Invalid ball index");
  11.222 +    }
  11.223 +
  11.224 +    hwdata->balls[ball].xrel = xrel;
  11.225 +    hwdata->balls[ball].yrel = yrel;
  11.226 +
  11.227 +    SDL_UnlockJoysticks();
  11.228 +    return 0;
  11.229 +}
  11.230 +
  11.231 +
  11.232 +int
  11.233 +SDL_JoystickSetVirtualButtonInner(SDL_Joystick * joystick, int button, Uint8 value)
  11.234 +{
  11.235 +    joystick_hwdata *hwdata;
  11.236 +
  11.237 +    SDL_LockJoysticks();
  11.238 +
  11.239 +    if (!joystick || !joystick->hwdata) {
  11.240 +        SDL_UnlockJoysticks();
  11.241 +        return SDL_SetError("Invalid joystick");
  11.242 +    }
  11.243 +
  11.244 +    hwdata = (joystick_hwdata *)joystick->hwdata;
  11.245 +    if (button < 0 || button >= hwdata->nbuttons) {
  11.246 +        SDL_UnlockJoysticks();
  11.247 +        return SDL_SetError("Invalid button index");
  11.248 +    }
  11.249 +
  11.250 +    hwdata->buttons[button] = value;
  11.251 +
  11.252 +    SDL_UnlockJoysticks();
  11.253 +    return 0;
  11.254 +}
  11.255 +
  11.256 +
  11.257 +int
  11.258 +SDL_JoystickSetVirtualHatInner(SDL_Joystick * joystick, int hat, Uint8 value)
  11.259 +{
  11.260 +    joystick_hwdata *hwdata;
  11.261 +
  11.262 +    SDL_LockJoysticks();
  11.263 +
  11.264 +    if (!joystick || !joystick->hwdata) {
  11.265 +        SDL_UnlockJoysticks();
  11.266 +        return SDL_SetError("Invalid joystick");
  11.267 +    }
  11.268 +
  11.269 +    hwdata = (joystick_hwdata *)joystick->hwdata;
  11.270 +    if (hat < 0 || hat >= hwdata->nbuttons) {
  11.271 +        SDL_UnlockJoysticks();
  11.272 +        return SDL_SetError("Invalid hat index");
  11.273 +    }
  11.274 +
  11.275 +    hwdata->hats[hat] = value;
  11.276 +
  11.277 +    SDL_UnlockJoysticks();
  11.278 +    return 0;
  11.279 +}
  11.280 +
  11.281 +
  11.282 +static int
  11.283 +VIRTUAL_JoystickInit(void)
  11.284 +{
  11.285 +    return 0;
  11.286 +}
  11.287 +
  11.288 +
  11.289 +static int
  11.290 +VIRTUAL_JoystickGetCount(void)
  11.291 +{
  11.292 +    int count = 0;
  11.293 +    joystick_hwdata *cur = g_VJoys;
  11.294 +    while (cur) {
  11.295 +        ++count;
  11.296 +        cur = cur->next;
  11.297 +    }
  11.298 +    return count;
  11.299 +}
  11.300 +
  11.301 +
  11.302 +static void
  11.303 +VIRTUAL_JoystickDetect(void)
  11.304 +{
  11.305 +}
  11.306 +
  11.307 +
  11.308 +static const char *
  11.309 +VIRTUAL_JoystickGetDeviceName(int device_index)
  11.310 +{
  11.311 +    joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index);
  11.312 +    if (!hwdata) {
  11.313 +        return NULL;
  11.314 +    }
  11.315 +    return hwdata->name ? hwdata->name : "";
  11.316 +}
  11.317 +
  11.318 +
  11.319 +static int
  11.320 +VIRTUAL_JoystickGetDevicePlayerIndex(int device_index)
  11.321 +{
  11.322 +    return -1;
  11.323 +}
  11.324 +
  11.325 +
  11.326 +static void
  11.327 +VIRTUAL_JoystickSetDevicePlayerIndex(int device_index, int player_index)
  11.328 +{
  11.329 +}
  11.330 +
  11.331 +
  11.332 +static SDL_JoystickGUID
  11.333 +VIRTUAL_JoystickGetDeviceGUID(int device_index)
  11.334 +{
  11.335 +    joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index);
  11.336 +    if (!hwdata) {
  11.337 +        SDL_JoystickGUID guid;
  11.338 +        SDL_zero(guid);
  11.339 +        return guid;
  11.340 +    }
  11.341 +    return hwdata->guid;
  11.342 +}
  11.343 +
  11.344 +
  11.345 +static SDL_JoystickID
  11.346 +VIRTUAL_JoystickGetDeviceInstanceID(int device_index)
  11.347 +{
  11.348 +    joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index);
  11.349 +    if (!hwdata) {
  11.350 +        return -1;
  11.351 +    }
  11.352 +    return hwdata->instance_id;
  11.353 +}
  11.354 +
  11.355 +
  11.356 +static int
  11.357 +VIRTUAL_JoystickOpen(SDL_Joystick * joystick, int device_index)
  11.358 +{
  11.359 +    joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index);
  11.360 +    if (!hwdata) {
  11.361 +        return SDL_SetError("No such device");
  11.362 +    }
  11.363 +    if (hwdata->opened) {
  11.364 +        return SDL_SetError("Joystick already opened");
  11.365 +    }
  11.366 +    joystick->instance_id = hwdata->instance_id;
  11.367 +    joystick->hwdata = hwdata;
  11.368 +    joystick->naxes = hwdata->naxes;
  11.369 +    joystick->nballs = hwdata->nballs;
  11.370 +    joystick->nbuttons = hwdata->nbuttons;
  11.371 +    joystick->nhats = hwdata->nhats;
  11.372 +    hwdata->opened = SDL_TRUE;
  11.373 +    return 0;
  11.374 +}
  11.375 +
  11.376 +
  11.377 +static int
  11.378 +VIRTUAL_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
  11.379 +{
  11.380 +    return SDL_Unsupported();
  11.381 +}
  11.382 +
  11.383 +
  11.384 +static void
  11.385 +VIRTUAL_JoystickUpdate(SDL_Joystick * joystick)
  11.386 +{
  11.387 +    joystick_hwdata *hwdata;
  11.388 +
  11.389 +    if (!joystick) {
  11.390 +        return;
  11.391 +    }
  11.392 +    if (!joystick->hwdata) {
  11.393 +        return;
  11.394 +    }
  11.395 +
  11.396 +    hwdata = (joystick_hwdata *)joystick->hwdata;
  11.397 +
  11.398 +    for (int i = 0; i < hwdata->naxes; ++i) {
  11.399 +        SDL_PrivateJoystickAxis(joystick, i, hwdata->axes[i]);
  11.400 +    }
  11.401 +    for (int i = 0; i < hwdata->nballs; ++i) {
  11.402 +        SDL_PrivateJoystickBall(joystick, i, hwdata->balls[i].xrel, hwdata->balls[i].yrel);
  11.403 +    }
  11.404 +    for (int i = 0; i < hwdata->nbuttons; ++i) {
  11.405 +        SDL_PrivateJoystickButton(joystick, i, hwdata->buttons[i]);
  11.406 +    }
  11.407 +    for (int i = 0; i < hwdata->nhats; ++i) {
  11.408 +        SDL_PrivateJoystickHat(joystick, i, hwdata->hats[i]);
  11.409 +    }
  11.410 +}
  11.411 +
  11.412 +
  11.413 +static void
  11.414 +VIRTUAL_JoystickClose(SDL_Joystick * joystick)
  11.415 +{
  11.416 +    joystick_hwdata *hwdata;
  11.417 +
  11.418 +    if (!joystick) {
  11.419 +        return;
  11.420 +    }
  11.421 +    if (!joystick->hwdata) {
  11.422 +        return;
  11.423 +    }
  11.424 +
  11.425 +    hwdata = (joystick_hwdata *)joystick->hwdata;
  11.426 +    hwdata->opened = SDL_FALSE;
  11.427 +}
  11.428 +
  11.429 +
  11.430 +static void
  11.431 +VIRTUAL_JoystickQuit(void)
  11.432 +{
  11.433 +    while (g_VJoys) {
  11.434 +        VIRTUAL_FreeHWData(g_VJoys);
  11.435 +    }
  11.436 +}
  11.437 +
  11.438 +
  11.439 +SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver =
  11.440 +{
  11.441 +    VIRTUAL_JoystickInit,
  11.442 +    VIRTUAL_JoystickGetCount,
  11.443 +    VIRTUAL_JoystickDetect,
  11.444 +    VIRTUAL_JoystickGetDeviceName,
  11.445 +    VIRTUAL_JoystickGetDevicePlayerIndex,
  11.446 +    VIRTUAL_JoystickSetDevicePlayerIndex,
  11.447 +    VIRTUAL_JoystickGetDeviceGUID,
  11.448 +    VIRTUAL_JoystickGetDeviceInstanceID,
  11.449 +    VIRTUAL_JoystickOpen,
  11.450 +    VIRTUAL_JoystickRumble,
  11.451 +    VIRTUAL_JoystickUpdate,
  11.452 +    VIRTUAL_JoystickClose,
  11.453 +    VIRTUAL_JoystickQuit,
  11.454 +};
  11.455 +
  11.456 +#endif /* SDL_JOYSTICK_VIRTUAL || SDL_JOYSTICK_DISABLED */
  11.457 +
  11.458 +/* vi: set ts=4 sw=4 expandtab: */
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/src/joystick/virtual/SDL_sysjoystick_c.h	Fri Mar 13 19:08:45 2020 -0700
    12.3 @@ -0,0 +1,69 @@
    12.4 +/*
    12.5 +  Simple DirectMedia Layer
    12.6 +  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
    12.7 +
    12.8 +  This software is provided 'as-is', without any express or implied
    12.9 +  warranty.  In no event will the authors be held liable for any damages
   12.10 +  arising from the use of this software.
   12.11 +
   12.12 +  Permission is granted to anyone to use this software for any purpose,
   12.13 +  including commercial applications, and to alter it and redistribute it
   12.14 +  freely, subject to the following restrictions:
   12.15 +
   12.16 +  1. The origin of this software must not be misrepresented; you must not
   12.17 +     claim that you wrote the original software. If you use this software
   12.18 +     in a product, an acknowledgment in the product documentation would be
   12.19 +     appreciated but is not required.
   12.20 +  2. Altered source versions must be plainly marked as such, and must not be
   12.21 +     misrepresented as being the original software.
   12.22 +  3. This notice may not be removed or altered from any source distribution.
   12.23 +*/
   12.24 +#include "../../SDL_internal.h"
   12.25 +
   12.26 +#ifndef SDL_JOYSTICK_VIRTUAL_H
   12.27 +#define SDL_JOYSTICK_VIRTUAL_H
   12.28 +
   12.29 +#if SDL_JOYSTICK_VIRTUAL
   12.30 +
   12.31 +#include "SDL_joystick.h"
   12.32 +
   12.33 +/**
   12.34 + * Data for a virtual, software-only joystick.
   12.35 + */
   12.36 +typedef struct joystick_hwdata
   12.37 +{
   12.38 +    SDL_JoystickType type;
   12.39 +    SDL_bool attached;
   12.40 +    const char *name;
   12.41 +    SDL_JoystickGUID guid;
   12.42 +    int naxes;
   12.43 +    Sint16 *axes;
   12.44 +    int nballs;
   12.45 +    struct {
   12.46 +        Sint16 xrel;
   12.47 +        Sint16 yrel;
   12.48 +    } *balls;
   12.49 +    int nbuttons;
   12.50 +    Uint8 *buttons;
   12.51 +    int nhats;
   12.52 +    Uint8 *hats;
   12.53 +    SDL_JoystickID instance_id;
   12.54 +    SDL_bool opened;
   12.55 +    struct joystick_hwdata *next;
   12.56 +} joystick_hwdata;
   12.57 +
   12.58 +int SDL_JoystickAttachVirtualInner(SDL_JoystickType type,
   12.59 +                                   int naxes,
   12.60 +                                   int nballs,
   12.61 +                                   int nbuttons,
   12.62 +                                   int nhats);
   12.63 +
   12.64 +int SDL_JoystickDetachVirtualInner(int device_index);
   12.65 +
   12.66 +int SDL_JoystickSetVirtualAxisInner(SDL_Joystick * joystick, int axis, Sint16 value);
   12.67 +int SDL_JoystickSetVirtualBallInner(SDL_Joystick * joystick, int ball, Sint16 xrel, Sint16 yrel);
   12.68 +int SDL_JoystickSetVirtualButtonInner(SDL_Joystick * joystick, int button, Uint8 value);
   12.69 +int SDL_JoystickSetVirtualHatInner(SDL_Joystick * joystick, int hat, Uint8 value);
   12.70 +
   12.71 +#endif  /* SDL_JOYSTICK_VIRTUAL */
   12.72 +#endif  /* SDL_JOYSTICK_VIRTUAL_H */
    13.1 --- a/test/testgamecontroller.c	Fri Mar 13 19:00:24 2020 -0700
    13.2 +++ b/test/testgamecontroller.c	Fri Mar 13 19:08:45 2020 -0700
    13.3 @@ -310,6 +310,9 @@
    13.4              case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
    13.5                  description = "Nintendo Switch Pro Controller";
    13.6                  break;
    13.7 +            case SDL_CONTROLLER_TYPE_VIRTUAL:
    13.8 +                description = "Virtual Game Controller";
    13.9 +                break;
   13.10              default:
   13.11                  description = "Game Controller";
   13.12                  break;