src/joystick/hidapi/SDL_hidapi_gamecube.c
changeset 12641 64597a7e8771
child 12704 704ec541957d
equal deleted inserted replaced
12640:b708d2c3fe08 12641:64597a7e8771
       
     1 /*
       
     2   Simple DirectMedia Layer
       
     3   Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
       
     4 
       
     5   This software is provided 'as-is', without any express or implied
       
     6   warranty.  In no event will the authors be held liable for any damages
       
     7   arising from the use of this software.
       
     8 
       
     9   Permission is granted to anyone to use this software for any purpose,
       
    10   including commercial applications, and to alter it and redistribute it
       
    11   freely, subject to the following restrictions:
       
    12 
       
    13   1. The origin of this software must not be misrepresented; you must not
       
    14      claim that you wrote the original software. If you use this software
       
    15      in a product, an acknowledgment in the product documentation would be
       
    16      appreciated but is not required.
       
    17   2. Altered source versions must be plainly marked as such, and must not be
       
    18      misrepresented as being the original software.
       
    19   3. This notice may not be removed or altered from any source distribution.
       
    20 */
       
    21 #include "../../SDL_internal.h"
       
    22 
       
    23 #ifdef SDL_JOYSTICK_HIDAPI
       
    24 
       
    25 #include "SDL_hints.h"
       
    26 #include "SDL_log.h"
       
    27 #include "SDL_events.h"
       
    28 #include "SDL_timer.h"
       
    29 #include "SDL_haptic.h"
       
    30 #include "SDL_joystick.h"
       
    31 #include "SDL_gamecontroller.h"
       
    32 #include "../SDL_sysjoystick.h"
       
    33 #include "SDL_hidapijoystick_c.h"
       
    34 
       
    35 
       
    36 #ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE
       
    37 
       
    38 typedef struct {
       
    39     SDL_JoystickID joysticks[4];
       
    40     Uint8 rumble[5];
       
    41     Uint32 rumbleExpiration[4];
       
    42     /* Without this variable, hid_write starts to lag a TON */
       
    43     Uint8 rumbleUpdate;
       
    44 } SDL_DriverGameCube_Context;
       
    45 
       
    46 static SDL_bool
       
    47 HIDAPI_DriverGameCube_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number)
       
    48 {
       
    49     return SDL_IsJoystickGameCube(vendor_id, product_id);
       
    50 }
       
    51 
       
    52 static const char *
       
    53 HIDAPI_DriverGameCube_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
       
    54 {
       
    55     /* Give a user friendly name for this controller */
       
    56     if (SDL_IsJoystickGameCube(vendor_id, product_id)) {
       
    57         return "Nintendo GameCube Controller";
       
    58     }
       
    59     return NULL;
       
    60 }
       
    61 
       
    62 static SDL_bool
       
    63 HIDAPI_DriverGameCube_InitDriver(SDL_HIDAPI_DriverData *context, Uint16 vendor_id, Uint16 product_id, int *num_joysticks)
       
    64 {
       
    65     SDL_DriverGameCube_Context *ctx;
       
    66     Uint8 packet[37];
       
    67     Uint8 *curSlot;
       
    68     Uint8 i;
       
    69     int size;
       
    70     Uint8 initMagic = 0x13;
       
    71     Uint8 rumbleMagic = 0x11;
       
    72 
       
    73     ctx = (SDL_DriverGameCube_Context *)SDL_calloc(1, sizeof(*ctx));
       
    74     if (!ctx) {
       
    75         SDL_OutOfMemory();
       
    76         return SDL_FALSE;
       
    77     }
       
    78     ctx->joysticks[0] = -1;
       
    79     ctx->joysticks[1] = -1;
       
    80     ctx->joysticks[2] = -1;
       
    81     ctx->joysticks[3] = -1;
       
    82     ctx->rumble[0] = rumbleMagic;
       
    83 
       
    84     context->context = ctx;
       
    85 
       
    86     /* This is all that's needed to initialize the device. Really! */
       
    87     if (hid_write(context->device, &initMagic, sizeof(initMagic)) <= 0) {
       
    88         SDL_SetError("Couldn't initialize WUP-028");
       
    89         SDL_free(ctx);
       
    90         return SDL_FALSE;
       
    91     }
       
    92 
       
    93     /* Add all the applicable joysticks */
       
    94     while ((size = hid_read_timeout(context->device, packet, sizeof(packet), 0)) > 0) {
       
    95         if (size < 37 || packet[0] != 0x21) {
       
    96             continue; /* Nothing to do yet...? */
       
    97         }
       
    98 
       
    99         /* Go through all 4 slots */
       
   100         curSlot = packet + 1;
       
   101         for (i = 0; i < 4; i += 1, curSlot += 9) {
       
   102             if (curSlot[0] & 0x30) { /* 0x10 - Wired, 0x20 - Wireless */
       
   103                 if (ctx->joysticks[i] == -1) {
       
   104                     ctx->joysticks[i] = SDL_GetNextJoystickInstanceID();
       
   105 
       
   106                     *num_joysticks += 1;
       
   107 
       
   108                     SDL_PrivateJoystickAdded(ctx->joysticks[i]);
       
   109                 }
       
   110             } else {
       
   111                 if (ctx->joysticks[i] != -1) {
       
   112                     SDL_PrivateJoystickRemoved(ctx->joysticks[i]);
       
   113 
       
   114                     *num_joysticks -= 1;
       
   115 
       
   116                     ctx->joysticks[i] = -1;
       
   117                 }
       
   118                 continue;
       
   119             }
       
   120         }
       
   121     }
       
   122 
       
   123     return SDL_TRUE;
       
   124 }
       
   125 
       
   126 static void
       
   127 HIDAPI_DriverGameCube_QuitDriver(SDL_HIDAPI_DriverData *context, SDL_bool send_event, int *num_joysticks)
       
   128 {
       
   129     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
       
   130     Uint8 i;
       
   131 
       
   132     /* Stop all rumble activity */
       
   133     for (i = 1; i < 5; i += 1) {
       
   134         ctx->rumble[i] = 0;
       
   135     }
       
   136     hid_write(context->device, ctx->rumble, sizeof(ctx->rumble));
       
   137 
       
   138     /* Remove all joysticks! */
       
   139     for (i = 0; i < 4; i += 1) {
       
   140         if (ctx->joysticks[i] != -1) {
       
   141             *num_joysticks -= 1;
       
   142             if (send_event) {
       
   143                 SDL_PrivateJoystickRemoved(ctx->joysticks[i]);
       
   144             }
       
   145         }
       
   146     }
       
   147 
       
   148     SDL_free(context->context);
       
   149 }
       
   150 
       
   151 static SDL_bool
       
   152 HIDAPI_DriverGameCube_UpdateDriver(SDL_HIDAPI_DriverData *context, int *num_joysticks)
       
   153 {
       
   154     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
       
   155     SDL_Joystick *joystick;
       
   156     Uint8 packet[37];
       
   157     Uint8 *curSlot;
       
   158     Uint32 now;
       
   159     Uint8 i;
       
   160     int size;
       
   161 
       
   162     /* Read input packet */
       
   163     while ((size = hid_read_timeout(context->device, packet, sizeof(packet), 0)) > 0) {
       
   164         if (size < 37 || packet[0] != 0x21) {
       
   165             continue; /* Nothing to do right now...? */
       
   166         }
       
   167 
       
   168         /* Go through all 4 slots */
       
   169         curSlot = packet + 1;
       
   170         for (i = 0; i < 4; i += 1, curSlot += 9) {
       
   171             if (curSlot[0] & 0x30) { /* 0x10 - Wired, 0x20 - Wireless */
       
   172                 if (ctx->joysticks[i] == -1) {
       
   173                     ctx->joysticks[i] = SDL_GetNextJoystickInstanceID();
       
   174 
       
   175                     *num_joysticks += 1;
       
   176 
       
   177                     SDL_PrivateJoystickAdded(ctx->joysticks[i]);
       
   178                 }
       
   179                 joystick = SDL_JoystickFromInstanceID(ctx->joysticks[i]);
       
   180 
       
   181                 /* Hasn't been opened yet, skip */
       
   182                 if (joystick == NULL) {
       
   183                     continue;
       
   184                 }
       
   185             } else {
       
   186                 if (ctx->joysticks[i] != -1) {
       
   187                     SDL_PrivateJoystickRemoved(ctx->joysticks[i]);
       
   188 
       
   189                     *num_joysticks -= 1;
       
   190 
       
   191                     ctx->joysticks[i] = -1;
       
   192                 }
       
   193                 continue;
       
   194             }
       
   195 
       
   196             #define READ_BUTTON(off, flag, button) \
       
   197                 SDL_PrivateJoystickButton( \
       
   198                     joystick, \
       
   199                     button, \
       
   200                     (curSlot[off] & flag) ? SDL_PRESSED : SDL_RELEASED \
       
   201                 );
       
   202             READ_BUTTON(1, 0x01, 0) /* A */
       
   203             READ_BUTTON(1, 0x02, 1) /* B */
       
   204             READ_BUTTON(1, 0x04, 2) /* X */
       
   205             READ_BUTTON(1, 0x08, 3) /* Y */
       
   206             READ_BUTTON(1, 0x10, 4) /* DPAD_LEFT */
       
   207             READ_BUTTON(1, 0x20, 5) /* DPAD_RIGHT */
       
   208             READ_BUTTON(1, 0x40, 6) /* DPAD_DOWN */
       
   209             READ_BUTTON(1, 0x80, 7) /* DPAD_UP */
       
   210             READ_BUTTON(2, 0x01, 8) /* START */
       
   211             READ_BUTTON(2, 0x02, 9) /* RIGHTSHOULDER */
       
   212             /* [2] 0x04 - R, [2] 0x08 - L */
       
   213             #undef READ_BUTTON
       
   214 
       
   215             /* Axis math taken from SDL_xinputjoystick.c */
       
   216             #define READ_AXIS(off, axis) \
       
   217                 SDL_PrivateJoystickAxis( \
       
   218                     joystick, \
       
   219                     axis, \
       
   220                     (Sint16)(((int)curSlot[off] * 257) - 32768) \
       
   221                 );
       
   222             READ_AXIS(3, 0) /* LEFTX */
       
   223             READ_AXIS(4, 1) /* LEFTY */
       
   224             READ_AXIS(5, 2) /* RIGHTX */
       
   225             READ_AXIS(6, 3) /* RIGHTY */
       
   226             READ_AXIS(7, 4) /* TRIGGERLEFT */
       
   227             READ_AXIS(8, 5) /* TRIGGERRIGHT */
       
   228             #undef READ_AXIS
       
   229         }
       
   230     }
       
   231 
       
   232     /* Write rumble packet */
       
   233     now = SDL_GetTicks();
       
   234     for (i = 0; i < 4; i += 1) {
       
   235         if (ctx->rumbleExpiration[i] > 0) {
       
   236             if (SDL_TICKS_PASSED(now, ctx->rumbleExpiration[i])) {
       
   237                 ctx->rumble[i + 1] = 0;
       
   238                 ctx->rumbleExpiration[i] = 0;
       
   239                 ctx->rumbleUpdate = 1;
       
   240             }
       
   241         }
       
   242     }
       
   243     if (ctx->rumbleUpdate) {
       
   244         hid_write(context->device, ctx->rumble, sizeof(ctx->rumble));
       
   245         ctx->rumbleUpdate = 0;
       
   246     }
       
   247 
       
   248     /* If we got here, nothing bad happened! */
       
   249     return SDL_TRUE;
       
   250 }
       
   251 
       
   252 static int
       
   253 HIDAPI_DriverGameCube_NumJoysticks(SDL_HIDAPI_DriverData *context)
       
   254 {
       
   255     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
       
   256     int i, joysticks = 0;
       
   257     for (i = 0; i < 4; i += 1) {
       
   258         if (ctx->joysticks[i] != -1) {
       
   259             joysticks += 1;
       
   260         }
       
   261     }
       
   262     return joysticks;
       
   263 }
       
   264 
       
   265 static SDL_JoystickID
       
   266 HIDAPI_DriverGameCube_InstanceIDForIndex(SDL_HIDAPI_DriverData *context, int index)
       
   267 {
       
   268     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
       
   269     Uint8 i;
       
   270     for (i = 0; i < 4; i += 1) {
       
   271         if (ctx->joysticks[i] != -1) {
       
   272             if (index == 0) {
       
   273                 return ctx->joysticks[i];
       
   274             }
       
   275             index -= 1;
       
   276         }
       
   277     }
       
   278     return -1; /* Should never get here! */
       
   279 }
       
   280 
       
   281 static SDL_bool
       
   282 HIDAPI_DriverGameCube_OpenJoystick(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick)
       
   283 {
       
   284     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
       
   285     SDL_JoystickID instance = SDL_JoystickInstanceID(joystick);
       
   286     Uint8 i;
       
   287     for (i = 0; i < 4; i += 1) {
       
   288         if (instance == ctx->joysticks[i]) {
       
   289             joystick->nbuttons = 10;
       
   290             joystick->naxes = 6;
       
   291             joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
       
   292             return SDL_TRUE;
       
   293         }
       
   294     }
       
   295     return SDL_FALSE; /* Should never get here! */
       
   296 }
       
   297 
       
   298 static int
       
   299 HIDAPI_DriverGameCube_Rumble(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
       
   300 {
       
   301     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
       
   302     SDL_JoystickID instance = SDL_JoystickInstanceID(joystick);
       
   303     Uint8 i, val;
       
   304     for (i = 0; i < 4; i += 1) {
       
   305         if (instance == ctx->joysticks[i]) {
       
   306             val = (low_frequency_rumble > 0 || high_frequency_rumble > 0);
       
   307             if (val != ctx->rumble[i + 1]) {
       
   308                 ctx->rumble[i + 1] = val;
       
   309                 ctx->rumbleUpdate = 1;
       
   310             }
       
   311             if (val && duration_ms < SDL_HAPTIC_INFINITY) {
       
   312                 ctx->rumbleExpiration[i] = SDL_GetTicks() + duration_ms;
       
   313             } else {
       
   314                 ctx->rumbleExpiration[i] = 0;
       
   315             }
       
   316             return 0;
       
   317         }
       
   318     }
       
   319     return -1;
       
   320 }
       
   321 
       
   322 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube =
       
   323 {
       
   324     SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE,
       
   325     SDL_TRUE,
       
   326     HIDAPI_DriverGameCube_IsSupportedDevice,
       
   327     HIDAPI_DriverGameCube_GetDeviceName,
       
   328     HIDAPI_DriverGameCube_InitDriver,
       
   329     HIDAPI_DriverGameCube_QuitDriver,
       
   330     HIDAPI_DriverGameCube_UpdateDriver,
       
   331     HIDAPI_DriverGameCube_NumJoysticks,
       
   332     HIDAPI_DriverGameCube_InstanceIDForIndex,
       
   333     HIDAPI_DriverGameCube_OpenJoystick,
       
   334     HIDAPI_DriverGameCube_Rumble
       
   335 };
       
   336 
       
   337 #endif /* SDL_JOYSTICK_HIDAPI_GAMECUBE */
       
   338 
       
   339 #endif /* SDL_JOYSTICK_HIDAPI */