src/joystick/hidapi/SDL_hidapi_gamecube.c
changeset 12641 64597a7e8771
child 12704 704ec541957d
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/joystick/hidapi/SDL_hidapi_gamecube.c	Tue Mar 12 20:27:54 2019 -0400
     1.3 @@ -0,0 +1,339 @@
     1.4 +/*
     1.5 +  Simple DirectMedia Layer
     1.6 +  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     1.7 +
     1.8 +  This software is provided 'as-is', without any express or implied
     1.9 +  warranty.  In no event will the authors be held liable for any damages
    1.10 +  arising from the use of this software.
    1.11 +
    1.12 +  Permission is granted to anyone to use this software for any purpose,
    1.13 +  including commercial applications, and to alter it and redistribute it
    1.14 +  freely, subject to the following restrictions:
    1.15 +
    1.16 +  1. The origin of this software must not be misrepresented; you must not
    1.17 +     claim that you wrote the original software. If you use this software
    1.18 +     in a product, an acknowledgment in the product documentation would be
    1.19 +     appreciated but is not required.
    1.20 +  2. Altered source versions must be plainly marked as such, and must not be
    1.21 +     misrepresented as being the original software.
    1.22 +  3. This notice may not be removed or altered from any source distribution.
    1.23 +*/
    1.24 +#include "../../SDL_internal.h"
    1.25 +
    1.26 +#ifdef SDL_JOYSTICK_HIDAPI
    1.27 +
    1.28 +#include "SDL_hints.h"
    1.29 +#include "SDL_log.h"
    1.30 +#include "SDL_events.h"
    1.31 +#include "SDL_timer.h"
    1.32 +#include "SDL_haptic.h"
    1.33 +#include "SDL_joystick.h"
    1.34 +#include "SDL_gamecontroller.h"
    1.35 +#include "../SDL_sysjoystick.h"
    1.36 +#include "SDL_hidapijoystick_c.h"
    1.37 +
    1.38 +
    1.39 +#ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE
    1.40 +
    1.41 +typedef struct {
    1.42 +    SDL_JoystickID joysticks[4];
    1.43 +    Uint8 rumble[5];
    1.44 +    Uint32 rumbleExpiration[4];
    1.45 +    /* Without this variable, hid_write starts to lag a TON */
    1.46 +    Uint8 rumbleUpdate;
    1.47 +} SDL_DriverGameCube_Context;
    1.48 +
    1.49 +static SDL_bool
    1.50 +HIDAPI_DriverGameCube_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number)
    1.51 +{
    1.52 +    return SDL_IsJoystickGameCube(vendor_id, product_id);
    1.53 +}
    1.54 +
    1.55 +static const char *
    1.56 +HIDAPI_DriverGameCube_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
    1.57 +{
    1.58 +    /* Give a user friendly name for this controller */
    1.59 +    if (SDL_IsJoystickGameCube(vendor_id, product_id)) {
    1.60 +        return "Nintendo GameCube Controller";
    1.61 +    }
    1.62 +    return NULL;
    1.63 +}
    1.64 +
    1.65 +static SDL_bool
    1.66 +HIDAPI_DriverGameCube_InitDriver(SDL_HIDAPI_DriverData *context, Uint16 vendor_id, Uint16 product_id, int *num_joysticks)
    1.67 +{
    1.68 +    SDL_DriverGameCube_Context *ctx;
    1.69 +    Uint8 packet[37];
    1.70 +    Uint8 *curSlot;
    1.71 +    Uint8 i;
    1.72 +    int size;
    1.73 +    Uint8 initMagic = 0x13;
    1.74 +    Uint8 rumbleMagic = 0x11;
    1.75 +
    1.76 +    ctx = (SDL_DriverGameCube_Context *)SDL_calloc(1, sizeof(*ctx));
    1.77 +    if (!ctx) {
    1.78 +        SDL_OutOfMemory();
    1.79 +        return SDL_FALSE;
    1.80 +    }
    1.81 +    ctx->joysticks[0] = -1;
    1.82 +    ctx->joysticks[1] = -1;
    1.83 +    ctx->joysticks[2] = -1;
    1.84 +    ctx->joysticks[3] = -1;
    1.85 +    ctx->rumble[0] = rumbleMagic;
    1.86 +
    1.87 +    context->context = ctx;
    1.88 +
    1.89 +    /* This is all that's needed to initialize the device. Really! */
    1.90 +    if (hid_write(context->device, &initMagic, sizeof(initMagic)) <= 0) {
    1.91 +        SDL_SetError("Couldn't initialize WUP-028");
    1.92 +        SDL_free(ctx);
    1.93 +        return SDL_FALSE;
    1.94 +    }
    1.95 +
    1.96 +    /* Add all the applicable joysticks */
    1.97 +    while ((size = hid_read_timeout(context->device, packet, sizeof(packet), 0)) > 0) {
    1.98 +        if (size < 37 || packet[0] != 0x21) {
    1.99 +            continue; /* Nothing to do yet...? */
   1.100 +        }
   1.101 +
   1.102 +        /* Go through all 4 slots */
   1.103 +        curSlot = packet + 1;
   1.104 +        for (i = 0; i < 4; i += 1, curSlot += 9) {
   1.105 +            if (curSlot[0] & 0x30) { /* 0x10 - Wired, 0x20 - Wireless */
   1.106 +                if (ctx->joysticks[i] == -1) {
   1.107 +                    ctx->joysticks[i] = SDL_GetNextJoystickInstanceID();
   1.108 +
   1.109 +                    *num_joysticks += 1;
   1.110 +
   1.111 +                    SDL_PrivateJoystickAdded(ctx->joysticks[i]);
   1.112 +                }
   1.113 +            } else {
   1.114 +                if (ctx->joysticks[i] != -1) {
   1.115 +                    SDL_PrivateJoystickRemoved(ctx->joysticks[i]);
   1.116 +
   1.117 +                    *num_joysticks -= 1;
   1.118 +
   1.119 +                    ctx->joysticks[i] = -1;
   1.120 +                }
   1.121 +                continue;
   1.122 +            }
   1.123 +        }
   1.124 +    }
   1.125 +
   1.126 +    return SDL_TRUE;
   1.127 +}
   1.128 +
   1.129 +static void
   1.130 +HIDAPI_DriverGameCube_QuitDriver(SDL_HIDAPI_DriverData *context, SDL_bool send_event, int *num_joysticks)
   1.131 +{
   1.132 +    SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
   1.133 +    Uint8 i;
   1.134 +
   1.135 +    /* Stop all rumble activity */
   1.136 +    for (i = 1; i < 5; i += 1) {
   1.137 +        ctx->rumble[i] = 0;
   1.138 +    }
   1.139 +    hid_write(context->device, ctx->rumble, sizeof(ctx->rumble));
   1.140 +
   1.141 +    /* Remove all joysticks! */
   1.142 +    for (i = 0; i < 4; i += 1) {
   1.143 +        if (ctx->joysticks[i] != -1) {
   1.144 +            *num_joysticks -= 1;
   1.145 +            if (send_event) {
   1.146 +                SDL_PrivateJoystickRemoved(ctx->joysticks[i]);
   1.147 +            }
   1.148 +        }
   1.149 +    }
   1.150 +
   1.151 +    SDL_free(context->context);
   1.152 +}
   1.153 +
   1.154 +static SDL_bool
   1.155 +HIDAPI_DriverGameCube_UpdateDriver(SDL_HIDAPI_DriverData *context, int *num_joysticks)
   1.156 +{
   1.157 +    SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
   1.158 +    SDL_Joystick *joystick;
   1.159 +    Uint8 packet[37];
   1.160 +    Uint8 *curSlot;
   1.161 +    Uint32 now;
   1.162 +    Uint8 i;
   1.163 +    int size;
   1.164 +
   1.165 +    /* Read input packet */
   1.166 +    while ((size = hid_read_timeout(context->device, packet, sizeof(packet), 0)) > 0) {
   1.167 +        if (size < 37 || packet[0] != 0x21) {
   1.168 +            continue; /* Nothing to do right now...? */
   1.169 +        }
   1.170 +
   1.171 +        /* Go through all 4 slots */
   1.172 +        curSlot = packet + 1;
   1.173 +        for (i = 0; i < 4; i += 1, curSlot += 9) {
   1.174 +            if (curSlot[0] & 0x30) { /* 0x10 - Wired, 0x20 - Wireless */
   1.175 +                if (ctx->joysticks[i] == -1) {
   1.176 +                    ctx->joysticks[i] = SDL_GetNextJoystickInstanceID();
   1.177 +
   1.178 +                    *num_joysticks += 1;
   1.179 +
   1.180 +                    SDL_PrivateJoystickAdded(ctx->joysticks[i]);
   1.181 +                }
   1.182 +                joystick = SDL_JoystickFromInstanceID(ctx->joysticks[i]);
   1.183 +
   1.184 +                /* Hasn't been opened yet, skip */
   1.185 +                if (joystick == NULL) {
   1.186 +                    continue;
   1.187 +                }
   1.188 +            } else {
   1.189 +                if (ctx->joysticks[i] != -1) {
   1.190 +                    SDL_PrivateJoystickRemoved(ctx->joysticks[i]);
   1.191 +
   1.192 +                    *num_joysticks -= 1;
   1.193 +
   1.194 +                    ctx->joysticks[i] = -1;
   1.195 +                }
   1.196 +                continue;
   1.197 +            }
   1.198 +
   1.199 +            #define READ_BUTTON(off, flag, button) \
   1.200 +                SDL_PrivateJoystickButton( \
   1.201 +                    joystick, \
   1.202 +                    button, \
   1.203 +                    (curSlot[off] & flag) ? SDL_PRESSED : SDL_RELEASED \
   1.204 +                );
   1.205 +            READ_BUTTON(1, 0x01, 0) /* A */
   1.206 +            READ_BUTTON(1, 0x02, 1) /* B */
   1.207 +            READ_BUTTON(1, 0x04, 2) /* X */
   1.208 +            READ_BUTTON(1, 0x08, 3) /* Y */
   1.209 +            READ_BUTTON(1, 0x10, 4) /* DPAD_LEFT */
   1.210 +            READ_BUTTON(1, 0x20, 5) /* DPAD_RIGHT */
   1.211 +            READ_BUTTON(1, 0x40, 6) /* DPAD_DOWN */
   1.212 +            READ_BUTTON(1, 0x80, 7) /* DPAD_UP */
   1.213 +            READ_BUTTON(2, 0x01, 8) /* START */
   1.214 +            READ_BUTTON(2, 0x02, 9) /* RIGHTSHOULDER */
   1.215 +            /* [2] 0x04 - R, [2] 0x08 - L */
   1.216 +            #undef READ_BUTTON
   1.217 +
   1.218 +            /* Axis math taken from SDL_xinputjoystick.c */
   1.219 +            #define READ_AXIS(off, axis) \
   1.220 +                SDL_PrivateJoystickAxis( \
   1.221 +                    joystick, \
   1.222 +                    axis, \
   1.223 +                    (Sint16)(((int)curSlot[off] * 257) - 32768) \
   1.224 +                );
   1.225 +            READ_AXIS(3, 0) /* LEFTX */
   1.226 +            READ_AXIS(4, 1) /* LEFTY */
   1.227 +            READ_AXIS(5, 2) /* RIGHTX */
   1.228 +            READ_AXIS(6, 3) /* RIGHTY */
   1.229 +            READ_AXIS(7, 4) /* TRIGGERLEFT */
   1.230 +            READ_AXIS(8, 5) /* TRIGGERRIGHT */
   1.231 +            #undef READ_AXIS
   1.232 +        }
   1.233 +    }
   1.234 +
   1.235 +    /* Write rumble packet */
   1.236 +    now = SDL_GetTicks();
   1.237 +    for (i = 0; i < 4; i += 1) {
   1.238 +        if (ctx->rumbleExpiration[i] > 0) {
   1.239 +            if (SDL_TICKS_PASSED(now, ctx->rumbleExpiration[i])) {
   1.240 +                ctx->rumble[i + 1] = 0;
   1.241 +                ctx->rumbleExpiration[i] = 0;
   1.242 +                ctx->rumbleUpdate = 1;
   1.243 +            }
   1.244 +        }
   1.245 +    }
   1.246 +    if (ctx->rumbleUpdate) {
   1.247 +        hid_write(context->device, ctx->rumble, sizeof(ctx->rumble));
   1.248 +        ctx->rumbleUpdate = 0;
   1.249 +    }
   1.250 +
   1.251 +    /* If we got here, nothing bad happened! */
   1.252 +    return SDL_TRUE;
   1.253 +}
   1.254 +
   1.255 +static int
   1.256 +HIDAPI_DriverGameCube_NumJoysticks(SDL_HIDAPI_DriverData *context)
   1.257 +{
   1.258 +    SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
   1.259 +    int i, joysticks = 0;
   1.260 +    for (i = 0; i < 4; i += 1) {
   1.261 +        if (ctx->joysticks[i] != -1) {
   1.262 +            joysticks += 1;
   1.263 +        }
   1.264 +    }
   1.265 +    return joysticks;
   1.266 +}
   1.267 +
   1.268 +static SDL_JoystickID
   1.269 +HIDAPI_DriverGameCube_InstanceIDForIndex(SDL_HIDAPI_DriverData *context, int index)
   1.270 +{
   1.271 +    SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
   1.272 +    Uint8 i;
   1.273 +    for (i = 0; i < 4; i += 1) {
   1.274 +        if (ctx->joysticks[i] != -1) {
   1.275 +            if (index == 0) {
   1.276 +                return ctx->joysticks[i];
   1.277 +            }
   1.278 +            index -= 1;
   1.279 +        }
   1.280 +    }
   1.281 +    return -1; /* Should never get here! */
   1.282 +}
   1.283 +
   1.284 +static SDL_bool
   1.285 +HIDAPI_DriverGameCube_OpenJoystick(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick)
   1.286 +{
   1.287 +    SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
   1.288 +    SDL_JoystickID instance = SDL_JoystickInstanceID(joystick);
   1.289 +    Uint8 i;
   1.290 +    for (i = 0; i < 4; i += 1) {
   1.291 +        if (instance == ctx->joysticks[i]) {
   1.292 +            joystick->nbuttons = 10;
   1.293 +            joystick->naxes = 6;
   1.294 +            joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
   1.295 +            return SDL_TRUE;
   1.296 +        }
   1.297 +    }
   1.298 +    return SDL_FALSE; /* Should never get here! */
   1.299 +}
   1.300 +
   1.301 +static int
   1.302 +HIDAPI_DriverGameCube_Rumble(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   1.303 +{
   1.304 +    SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context;
   1.305 +    SDL_JoystickID instance = SDL_JoystickInstanceID(joystick);
   1.306 +    Uint8 i, val;
   1.307 +    for (i = 0; i < 4; i += 1) {
   1.308 +        if (instance == ctx->joysticks[i]) {
   1.309 +            val = (low_frequency_rumble > 0 || high_frequency_rumble > 0);
   1.310 +            if (val != ctx->rumble[i + 1]) {
   1.311 +                ctx->rumble[i + 1] = val;
   1.312 +                ctx->rumbleUpdate = 1;
   1.313 +            }
   1.314 +            if (val && duration_ms < SDL_HAPTIC_INFINITY) {
   1.315 +                ctx->rumbleExpiration[i] = SDL_GetTicks() + duration_ms;
   1.316 +            } else {
   1.317 +                ctx->rumbleExpiration[i] = 0;
   1.318 +            }
   1.319 +            return 0;
   1.320 +        }
   1.321 +    }
   1.322 +    return -1;
   1.323 +}
   1.324 +
   1.325 +SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube =
   1.326 +{
   1.327 +    SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE,
   1.328 +    SDL_TRUE,
   1.329 +    HIDAPI_DriverGameCube_IsSupportedDevice,
   1.330 +    HIDAPI_DriverGameCube_GetDeviceName,
   1.331 +    HIDAPI_DriverGameCube_InitDriver,
   1.332 +    HIDAPI_DriverGameCube_QuitDriver,
   1.333 +    HIDAPI_DriverGameCube_UpdateDriver,
   1.334 +    HIDAPI_DriverGameCube_NumJoysticks,
   1.335 +    HIDAPI_DriverGameCube_InstanceIDForIndex,
   1.336 +    HIDAPI_DriverGameCube_OpenJoystick,
   1.337 +    HIDAPI_DriverGameCube_Rumble
   1.338 +};
   1.339 +
   1.340 +#endif /* SDL_JOYSTICK_HIDAPI_GAMECUBE */
   1.341 +
   1.342 +#endif /* SDL_JOYSTICK_HIDAPI */