src/joystick/hidapi/SDL_hidapi_gamecube.c
author Ethan Lee
Sun, 17 Mar 2019 12:36:40 -0400
changeset 12704 704ec541957d
parent 12641 64597a7e8771
child 12787 0411f841b035
permissions -rw-r--r--
hidapi: Add GCN L/R buttons, just in case someone wants them...
     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             /* These two buttons are for the bottoms of the analog triggers.
   213              * More than likely, you're going to want to read the axes instead!
   214              * -flibit
   215              */
   216             READ_BUTTON(2, 0x04, 10) /* TRIGGERRIGHT */
   217             READ_BUTTON(2, 0x08, 11) /* TRIGGERLEFT */
   218             #undef READ_BUTTON
   219 
   220             /* Axis math taken from SDL_xinputjoystick.c */
   221             #define READ_AXIS(off, axis) \
   222                 SDL_PrivateJoystickAxis( \
   223                     joystick, \
   224                     axis, \
   225                     (Sint16)(((int)curSlot[off] * 257) - 32768) \
   226                 );
   227             READ_AXIS(3, 0) /* LEFTX */
   228             READ_AXIS(4, 1) /* LEFTY */
   229             READ_AXIS(5, 2) /* RIGHTX */
   230             READ_AXIS(6, 3) /* RIGHTY */
   231             READ_AXIS(7, 4) /* TRIGGERLEFT */
   232             READ_AXIS(8, 5) /* TRIGGERRIGHT */
   233             #undef READ_AXIS
   234         }
   235     }
   236 
   237     /* Write rumble packet */
   238     now = SDL_GetTicks();
   239     for (i = 0; i < 4; i += 1) {
   240         if (ctx->rumbleExpiration[i] > 0) {
   241             if (SDL_TICKS_PASSED(now, ctx->rumbleExpiration[i])) {
   242                 ctx->rumble[i + 1] = 0;
   243                 ctx->rumbleExpiration[i] = 0;
   244                 ctx->rumbleUpdate = 1;
   245             }
   246         }
   247     }
   248     if (ctx->rumbleUpdate) {
   249         hid_write(context->device, ctx->rumble, sizeof(ctx->rumble));
   250         ctx->rumbleUpdate = 0;
   251     }
   252 
   253     /* If we got here, nothing bad happened! */
   254     return SDL_TRUE;
   255 }
   256 
   257 static int
   258 HIDAPI_DriverGameCube_NumJoysticks(SDL_HIDAPI_DriverData *context)
   259 {
   260     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
   261     int i, joysticks = 0;
   262     for (i = 0; i < 4; i += 1) {
   263         if (ctx->joysticks[i] != -1) {
   264             joysticks += 1;
   265         }
   266     }
   267     return joysticks;
   268 }
   269 
   270 static SDL_JoystickID
   271 HIDAPI_DriverGameCube_InstanceIDForIndex(SDL_HIDAPI_DriverData *context, int index)
   272 {
   273     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
   274     Uint8 i;
   275     for (i = 0; i < 4; i += 1) {
   276         if (ctx->joysticks[i] != -1) {
   277             if (index == 0) {
   278                 return ctx->joysticks[i];
   279             }
   280             index -= 1;
   281         }
   282     }
   283     return -1; /* Should never get here! */
   284 }
   285 
   286 static SDL_bool
   287 HIDAPI_DriverGameCube_OpenJoystick(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick)
   288 {
   289     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
   290     SDL_JoystickID instance = SDL_JoystickInstanceID(joystick);
   291     Uint8 i;
   292     for (i = 0; i < 4; i += 1) {
   293         if (instance == ctx->joysticks[i]) {
   294             joystick->nbuttons = 12;
   295             joystick->naxes = 6;
   296             joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
   297             return SDL_TRUE;
   298         }
   299     }
   300     return SDL_FALSE; /* Should never get here! */
   301 }
   302 
   303 static int
   304 HIDAPI_DriverGameCube_Rumble(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   305 {
   306     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
   307     SDL_JoystickID instance = SDL_JoystickInstanceID(joystick);
   308     Uint8 i, val;
   309     for (i = 0; i < 4; i += 1) {
   310         if (instance == ctx->joysticks[i]) {
   311             val = (low_frequency_rumble > 0 || high_frequency_rumble > 0);
   312             if (val != ctx->rumble[i + 1]) {
   313                 ctx->rumble[i + 1] = val;
   314                 ctx->rumbleUpdate = 1;
   315             }
   316             if (val && duration_ms < SDL_HAPTIC_INFINITY) {
   317                 ctx->rumbleExpiration[i] = SDL_GetTicks() + duration_ms;
   318             } else {
   319                 ctx->rumbleExpiration[i] = 0;
   320             }
   321             return 0;
   322         }
   323     }
   324     return -1;
   325 }
   326 
   327 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube =
   328 {
   329     SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE,
   330     SDL_TRUE,
   331     HIDAPI_DriverGameCube_IsSupportedDevice,
   332     HIDAPI_DriverGameCube_GetDeviceName,
   333     HIDAPI_DriverGameCube_InitDriver,
   334     HIDAPI_DriverGameCube_QuitDriver,
   335     HIDAPI_DriverGameCube_UpdateDriver,
   336     HIDAPI_DriverGameCube_NumJoysticks,
   337     HIDAPI_DriverGameCube_InstanceIDForIndex,
   338     HIDAPI_DriverGameCube_OpenJoystick,
   339     HIDAPI_DriverGameCube_Rumble
   340 };
   341 
   342 #endif /* SDL_JOYSTICK_HIDAPI_GAMECUBE */
   343 
   344 #endif /* SDL_JOYSTICK_HIDAPI */