sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_hidapi_rumble.c (7444B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 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 /* Handle rumble on a separate thread so it doesn't block the application */
     26 
     27 #include "SDL_thread.h"
     28 #include "SDL_hidapijoystick_c.h"
     29 #include "SDL_hidapi_rumble.h"
     30 #include "../../thread/SDL_systhread.h"
     31 
     32 
     33 typedef struct SDL_HIDAPI_RumbleRequest
     34 {
     35     SDL_HIDAPI_Device *device;
     36     Uint8 data[2*USB_PACKET_LENGTH]; /* need enough space for the biggest report: dualshock4 is 78 bytes */
     37     int size;
     38     struct SDL_HIDAPI_RumbleRequest *prev;
     39 
     40 } SDL_HIDAPI_RumbleRequest;
     41 
     42 typedef struct SDL_HIDAPI_RumbleContext
     43 {
     44     SDL_atomic_t initialized;
     45     SDL_atomic_t running;
     46     SDL_Thread *thread;
     47     SDL_mutex *lock;
     48     SDL_sem *request_sem;
     49     SDL_HIDAPI_RumbleRequest *requests_head;
     50     SDL_HIDAPI_RumbleRequest *requests_tail;
     51 } SDL_HIDAPI_RumbleContext;
     52 
     53 static SDL_HIDAPI_RumbleContext rumble_context;
     54 
     55 static int SDL_HIDAPI_RumbleThread(void *data)
     56 {
     57     SDL_HIDAPI_RumbleContext *ctx = (SDL_HIDAPI_RumbleContext *)data;
     58 
     59     SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
     60 
     61     while (SDL_AtomicGet(&ctx->running)) {
     62         SDL_HIDAPI_RumbleRequest *request = NULL;
     63 
     64         SDL_SemWait(ctx->request_sem);
     65 
     66         SDL_LockMutex(ctx->lock);
     67         request = ctx->requests_tail;
     68         if (request) {
     69             if (request == ctx->requests_head) {
     70                 ctx->requests_head = NULL;
     71             }
     72             ctx->requests_tail = request->prev;
     73         }
     74         SDL_UnlockMutex(ctx->lock);
     75 
     76         if (request) {
     77             SDL_LockMutex(request->device->dev_lock);
     78             if (request->device->dev) {
     79                 hid_write( request->device->dev, request->data, request->size );
     80             }
     81             SDL_UnlockMutex(request->device->dev_lock);
     82             (void)SDL_AtomicDecRef(&request->device->rumble_pending);
     83             SDL_free(request);
     84         }
     85     }
     86     return 0;
     87 }
     88 
     89 static void
     90 SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
     91 {
     92     SDL_HIDAPI_RumbleRequest *request;
     93 
     94     SDL_AtomicSet(&ctx->running, SDL_FALSE);
     95 
     96     if (ctx->thread) {
     97         int result;
     98 
     99         SDL_SemPost(ctx->request_sem);
    100         SDL_WaitThread(ctx->thread, &result);
    101         ctx->thread = NULL;
    102     }
    103 
    104     SDL_LockMutex(ctx->lock);
    105     while (ctx->requests_tail) {
    106         request = ctx->requests_tail;
    107         if (request == ctx->requests_head) {
    108             ctx->requests_head = NULL;
    109         }
    110         ctx->requests_tail = request->prev;
    111 
    112         (void)SDL_AtomicDecRef(&request->device->rumble_pending);
    113         SDL_free(request);
    114     }
    115     SDL_UnlockMutex(ctx->lock);
    116 
    117     if (ctx->request_sem) {
    118         SDL_DestroySemaphore(ctx->request_sem);
    119         ctx->request_sem = NULL;
    120     }
    121 
    122     if (ctx->lock) {
    123         SDL_DestroyMutex(ctx->lock);
    124         ctx->lock = NULL;
    125     }
    126 
    127     SDL_AtomicSet(&ctx->initialized, SDL_FALSE);
    128 }
    129 
    130 static int
    131 SDL_HIDAPI_StartRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
    132 {
    133     ctx->lock = SDL_CreateMutex();
    134     if (!ctx->lock) {
    135         SDL_HIDAPI_StopRumbleThread(ctx);
    136         return -1;
    137     }
    138 
    139     ctx->request_sem = SDL_CreateSemaphore(0);
    140     if (!ctx->request_sem) {
    141         SDL_HIDAPI_StopRumbleThread(ctx);
    142         return -1;
    143     }
    144 
    145     SDL_AtomicSet(&ctx->running, SDL_TRUE);
    146     ctx->thread = SDL_CreateThreadInternal(SDL_HIDAPI_RumbleThread, "HIDAPI Rumble", 0, ctx);
    147     if (!ctx->thread) {
    148         SDL_HIDAPI_StopRumbleThread(ctx);
    149         return -1;
    150     }
    151     return 0;
    152 }
    153 
    154 int SDL_HIDAPI_LockRumble(void)
    155 {
    156     SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
    157 
    158     if (SDL_AtomicCAS(&ctx->initialized, SDL_FALSE, SDL_TRUE)) {
    159         if (SDL_HIDAPI_StartRumbleThread(ctx) < 0) {
    160             return -1;
    161         }
    162     }
    163 
    164     return SDL_LockMutex(ctx->lock);
    165 }
    166 
    167 SDL_bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data, int **size, int *maximum_size)
    168 {
    169     SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
    170     SDL_HIDAPI_RumbleRequest *request, *found;
    171 
    172     found = NULL;
    173     for (request = ctx->requests_tail; request; request = request->prev) {
    174         if (request->device == device) {
    175             found = request;
    176         }
    177     }
    178     if (found) {
    179         *data = found->data;
    180         *size = &found->size;
    181         *maximum_size = sizeof(found->data);
    182         return SDL_TRUE;
    183     }
    184     return SDL_FALSE;
    185 }
    186 
    187 int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
    188 {
    189     SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
    190     SDL_HIDAPI_RumbleRequest *request;
    191 
    192     if (size > sizeof(request->data)) {
    193         SDL_HIDAPI_UnlockRumble();
    194         return SDL_SetError("Couldn't send rumble, size %d is greater than %d", size, (int)sizeof(request->data));
    195     }
    196 
    197     request = (SDL_HIDAPI_RumbleRequest *)SDL_calloc(1, sizeof(*request));
    198     if (!request) {
    199         SDL_HIDAPI_UnlockRumble();
    200         return SDL_OutOfMemory();
    201     }
    202     request->device = device;
    203     SDL_memcpy(request->data, data, size);
    204     request->size = size;
    205 
    206     SDL_AtomicIncRef(&device->rumble_pending);
    207     
    208     if (ctx->requests_head) {
    209         ctx->requests_head->prev = request;
    210     } else {
    211         ctx->requests_tail = request;
    212     }
    213     ctx->requests_head = request;
    214 
    215     /* Make sure we unlock before posting the semaphore so the rumble thread can run immediately */
    216     SDL_HIDAPI_UnlockRumble();
    217 
    218     SDL_SemPost(ctx->request_sem);
    219 
    220     return size;
    221 }
    222 
    223 void SDL_HIDAPI_UnlockRumble(void)
    224 {
    225     SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
    226 
    227     SDL_UnlockMutex(ctx->lock);
    228 }
    229 
    230 int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
    231 {
    232     Uint8 *pending_data;
    233     int *pending_size;
    234     int maximum_size;
    235 
    236     if (SDL_HIDAPI_LockRumble() < 0) {
    237         return -1;
    238     }
    239 
    240     /* check if there is a pending request for the device and update it */
    241     if (SDL_HIDAPI_GetPendingRumbleLocked(device, &pending_data, &pending_size, &maximum_size)) {
    242         if (size > maximum_size) {
    243             SDL_HIDAPI_UnlockRumble();
    244             return SDL_SetError("Couldn't send rumble, size %d is greater than %d", size, maximum_size);
    245         }
    246 
    247         SDL_memcpy(pending_data, data, size);
    248         *pending_size = size;
    249         SDL_HIDAPI_UnlockRumble();
    250         return size;
    251     }
    252 
    253     return SDL_HIDAPI_SendRumbleAndUnlock(device, data, size);
    254 }
    255 
    256 void SDL_HIDAPI_QuitRumble(void)
    257 {
    258     SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
    259 
    260     if (SDL_AtomicGet(&ctx->running)) {
    261         SDL_HIDAPI_StopRumbleThread(ctx);
    262     }
    263 }
    264 
    265 #endif /* SDL_JOYSTICK_HIDAPI */
    266 
    267 /* vi: set ts=4 sw=4 expandtab: */