src/joystick/SDL_joystick.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 24 Mar 2015 13:52:01 -0400
changeset 9433 bd062398b648
parent 8920 21ccd40c778a
child 9619 b94b6d0bff0f
permissions -rw-r--r--
Cleanups in the joystick code.

Removed some redundant state and other confusions.

Fixes Bugzilla #2738.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 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 /* This is the joystick API for Simple DirectMedia Layer */
    24 
    25 #include "SDL.h"
    26 #include "SDL_events.h"
    27 #include "SDL_sysjoystick.h"
    28 #include "SDL_assert.h"
    29 #include "SDL_hints.h"
    30 
    31 #if !SDL_EVENTS_DISABLED
    32 #include "../events/SDL_events_c.h"
    33 #endif
    34 
    35 static SDL_bool SDL_joystick_allows_background_events = SDL_FALSE;
    36 static SDL_Joystick *SDL_joysticks = NULL;
    37 static SDL_Joystick *SDL_updating_joystick = NULL;
    38 
    39 static void
    40 SDL_JoystickAllowBackgroundEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
    41 {
    42     if (hint && *hint == '1') {
    43         SDL_joystick_allows_background_events = SDL_TRUE;
    44     } else {
    45         SDL_joystick_allows_background_events = SDL_FALSE;
    46     }
    47 }
    48 
    49 int
    50 SDL_JoystickInit(void)
    51 {
    52     int status;
    53 
    54     /* See if we should allow joystick events while in the background */
    55     SDL_AddHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
    56                         SDL_JoystickAllowBackgroundEventsChanged, NULL);
    57 
    58 #if !SDL_EVENTS_DISABLED
    59     if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0) {
    60         return -1;
    61     }
    62 #endif /* !SDL_EVENTS_DISABLED */
    63 
    64     status = SDL_SYS_JoystickInit();
    65     if (status >= 0) {
    66         status = 0;
    67     }
    68     return (status);
    69 }
    70 
    71 /*
    72  * Count the number of joysticks attached to the system
    73  */
    74 int
    75 SDL_NumJoysticks(void)
    76 {
    77     return SDL_SYS_NumJoysticks();
    78 }
    79 
    80 /*
    81  * Get the implementation dependent name of a joystick
    82  */
    83 const char *
    84 SDL_JoystickNameForIndex(int device_index)
    85 {
    86     if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) {
    87         SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
    88         return (NULL);
    89     }
    90     return (SDL_SYS_JoystickNameForDeviceIndex(device_index));
    91 }
    92 
    93 /*
    94  * Open a joystick for use - the index passed as an argument refers to
    95  * the N'th joystick on the system.  This index is the value which will
    96  * identify this joystick in future joystick events.
    97  *
    98  * This function returns a joystick identifier, or NULL if an error occurred.
    99  */
   100 SDL_Joystick *
   101 SDL_JoystickOpen(int device_index)
   102 {
   103     SDL_Joystick *joystick;
   104     SDL_Joystick *joysticklist;
   105     const char *joystickname = NULL;
   106 
   107     if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) {
   108         SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
   109         return (NULL);
   110     }
   111 
   112     joysticklist = SDL_joysticks;
   113     /* If the joystick is already open, return it
   114     * it is important that we have a single joystick * for each instance id
   115     */
   116     while (joysticklist) {
   117         if (SDL_SYS_GetInstanceIdOfDeviceIndex(device_index) == joysticklist->instance_id) {
   118                 joystick = joysticklist;
   119                 ++joystick->ref_count;
   120                 return (joystick);
   121         }
   122         joysticklist = joysticklist->next;
   123     }
   124 
   125     /* Create and initialize the joystick */
   126     joystick = (SDL_Joystick *) SDL_malloc((sizeof *joystick));
   127     if (joystick == NULL) {
   128         SDL_OutOfMemory();
   129         return NULL;
   130     }
   131 
   132     SDL_memset(joystick, 0, (sizeof *joystick));
   133     if (SDL_SYS_JoystickOpen(joystick, device_index) < 0) {
   134         SDL_free(joystick);
   135         return NULL;
   136     }
   137 
   138     joystickname = SDL_SYS_JoystickNameForDeviceIndex(device_index);
   139     if (joystickname)
   140         joystick->name = SDL_strdup(joystickname);
   141     else
   142         joystick->name = NULL;
   143 
   144     if (joystick->naxes > 0) {
   145         joystick->axes = (Sint16 *) SDL_malloc
   146             (joystick->naxes * sizeof(Sint16));
   147     }
   148     if (joystick->nhats > 0) {
   149         joystick->hats = (Uint8 *) SDL_malloc
   150             (joystick->nhats * sizeof(Uint8));
   151     }
   152     if (joystick->nballs > 0) {
   153         joystick->balls = (struct balldelta *) SDL_malloc
   154             (joystick->nballs * sizeof(*joystick->balls));
   155     }
   156     if (joystick->nbuttons > 0) {
   157         joystick->buttons = (Uint8 *) SDL_malloc
   158             (joystick->nbuttons * sizeof(Uint8));
   159     }
   160     if (((joystick->naxes > 0) && !joystick->axes)
   161         || ((joystick->nhats > 0) && !joystick->hats)
   162         || ((joystick->nballs > 0) && !joystick->balls)
   163         || ((joystick->nbuttons > 0) && !joystick->buttons)) {
   164         SDL_OutOfMemory();
   165         SDL_JoystickClose(joystick);
   166         return NULL;
   167     }
   168     if (joystick->axes) {
   169         SDL_memset(joystick->axes, 0, joystick->naxes * sizeof(Sint16));
   170     }
   171     if (joystick->hats) {
   172         SDL_memset(joystick->hats, 0, joystick->nhats * sizeof(Uint8));
   173     }
   174     if (joystick->balls) {
   175         SDL_memset(joystick->balls, 0,
   176             joystick->nballs * sizeof(*joystick->balls));
   177     }
   178     if (joystick->buttons) {
   179         SDL_memset(joystick->buttons, 0, joystick->nbuttons * sizeof(Uint8));
   180     }
   181 
   182     /* Add joystick to list */
   183     ++joystick->ref_count;
   184     /* Link the joystick in the list */
   185     joystick->next = SDL_joysticks;
   186     SDL_joysticks = joystick;
   187 
   188     SDL_SYS_JoystickUpdate(joystick);
   189 
   190     return (joystick);
   191 }
   192 
   193 
   194 /*
   195  * Checks to make sure the joystick is valid.
   196  */
   197 int
   198 SDL_PrivateJoystickValid(SDL_Joystick * joystick)
   199 {
   200     int valid;
   201 
   202     if (joystick == NULL) {
   203         SDL_SetError("Joystick hasn't been opened yet");
   204         valid = 0;
   205     } else {
   206         valid = 1;
   207     }
   208 
   209     return valid;
   210 }
   211 
   212 /*
   213  * Get the number of multi-dimensional axis controls on a joystick
   214  */
   215 int
   216 SDL_JoystickNumAxes(SDL_Joystick * joystick)
   217 {
   218     if (!SDL_PrivateJoystickValid(joystick)) {
   219         return (-1);
   220     }
   221     return (joystick->naxes);
   222 }
   223 
   224 /*
   225  * Get the number of hats on a joystick
   226  */
   227 int
   228 SDL_JoystickNumHats(SDL_Joystick * joystick)
   229 {
   230     if (!SDL_PrivateJoystickValid(joystick)) {
   231         return (-1);
   232     }
   233     return (joystick->nhats);
   234 }
   235 
   236 /*
   237  * Get the number of trackballs on a joystick
   238  */
   239 int
   240 SDL_JoystickNumBalls(SDL_Joystick * joystick)
   241 {
   242     if (!SDL_PrivateJoystickValid(joystick)) {
   243         return (-1);
   244     }
   245     return (joystick->nballs);
   246 }
   247 
   248 /*
   249  * Get the number of buttons on a joystick
   250  */
   251 int
   252 SDL_JoystickNumButtons(SDL_Joystick * joystick)
   253 {
   254     if (!SDL_PrivateJoystickValid(joystick)) {
   255         return (-1);
   256     }
   257     return (joystick->nbuttons);
   258 }
   259 
   260 /*
   261  * Get the current state of an axis control on a joystick
   262  */
   263 Sint16
   264 SDL_JoystickGetAxis(SDL_Joystick * joystick, int axis)
   265 {
   266     Sint16 state;
   267 
   268     if (!SDL_PrivateJoystickValid(joystick)) {
   269         return (0);
   270     }
   271     if (axis < joystick->naxes) {
   272         state = joystick->axes[axis];
   273     } else {
   274         SDL_SetError("Joystick only has %d axes", joystick->naxes);
   275         state = 0;
   276     }
   277     return (state);
   278 }
   279 
   280 /*
   281  * Get the current state of a hat on a joystick
   282  */
   283 Uint8
   284 SDL_JoystickGetHat(SDL_Joystick * joystick, int hat)
   285 {
   286     Uint8 state;
   287 
   288     if (!SDL_PrivateJoystickValid(joystick)) {
   289         return (0);
   290     }
   291     if (hat < joystick->nhats) {
   292         state = joystick->hats[hat];
   293     } else {
   294         SDL_SetError("Joystick only has %d hats", joystick->nhats);
   295         state = 0;
   296     }
   297     return (state);
   298 }
   299 
   300 /*
   301  * Get the ball axis change since the last poll
   302  */
   303 int
   304 SDL_JoystickGetBall(SDL_Joystick * joystick, int ball, int *dx, int *dy)
   305 {
   306     int retval;
   307 
   308     if (!SDL_PrivateJoystickValid(joystick)) {
   309         return (-1);
   310     }
   311 
   312     retval = 0;
   313     if (ball < joystick->nballs) {
   314         if (dx) {
   315             *dx = joystick->balls[ball].dx;
   316         }
   317         if (dy) {
   318             *dy = joystick->balls[ball].dy;
   319         }
   320         joystick->balls[ball].dx = 0;
   321         joystick->balls[ball].dy = 0;
   322     } else {
   323         return SDL_SetError("Joystick only has %d balls", joystick->nballs);
   324     }
   325     return (retval);
   326 }
   327 
   328 /*
   329  * Get the current state of a button on a joystick
   330  */
   331 Uint8
   332 SDL_JoystickGetButton(SDL_Joystick * joystick, int button)
   333 {
   334     Uint8 state;
   335 
   336     if (!SDL_PrivateJoystickValid(joystick)) {
   337         return (0);
   338     }
   339     if (button < joystick->nbuttons) {
   340         state = joystick->buttons[button];
   341     } else {
   342         SDL_SetError("Joystick only has %d buttons", joystick->nbuttons);
   343         state = 0;
   344     }
   345     return (state);
   346 }
   347 
   348 /*
   349  * Return if the joystick in question is currently attached to the system,
   350  *  \return SDL_FALSE if not plugged in, SDL_TRUE if still present.
   351  */
   352 SDL_bool
   353 SDL_JoystickGetAttached(SDL_Joystick * joystick)
   354 {
   355     if (!SDL_PrivateJoystickValid(joystick)) {
   356         return SDL_FALSE;
   357     }
   358 
   359     return SDL_SYS_JoystickAttached(joystick);
   360 }
   361 
   362 /*
   363  * Get the instance id for this opened joystick
   364  */
   365 SDL_JoystickID
   366 SDL_JoystickInstanceID(SDL_Joystick * joystick)
   367 {
   368     if (!SDL_PrivateJoystickValid(joystick)) {
   369         return (-1);
   370     }
   371 
   372     return (joystick->instance_id);
   373 }
   374 
   375 /*
   376  * Get the friendly name of this joystick
   377  */
   378 const char *
   379 SDL_JoystickName(SDL_Joystick * joystick)
   380 {
   381     if (!SDL_PrivateJoystickValid(joystick)) {
   382         return (NULL);
   383     }
   384 
   385     return (joystick->name);
   386 }
   387 
   388 /*
   389  * Close a joystick previously opened with SDL_JoystickOpen()
   390  */
   391 void
   392 SDL_JoystickClose(SDL_Joystick * joystick)
   393 {
   394     SDL_Joystick *joysticklist;
   395     SDL_Joystick *joysticklistprev;
   396 
   397     if (!joystick) {
   398         return;
   399     }
   400 
   401     /* First decrement ref count */
   402     if (--joystick->ref_count > 0) {
   403         return;
   404     }
   405 
   406     if (joystick == SDL_updating_joystick) {
   407         return;
   408     }
   409 
   410     SDL_SYS_JoystickClose(joystick);
   411     joystick->hwdata = NULL;
   412 
   413     joysticklist = SDL_joysticks;
   414     joysticklistprev = NULL;
   415     while (joysticklist) {
   416         if (joystick == joysticklist) {
   417             if (joysticklistprev) {
   418                 /* unlink this entry */
   419                 joysticklistprev->next = joysticklist->next;
   420             } else {
   421                 SDL_joysticks = joystick->next;
   422             }
   423             break;
   424         }
   425         joysticklistprev = joysticklist;
   426         joysticklist = joysticklist->next;
   427     }
   428 
   429     SDL_free(joystick->name);
   430 
   431     /* Free the data associated with this joystick */
   432     SDL_free(joystick->axes);
   433     SDL_free(joystick->hats);
   434     SDL_free(joystick->balls);
   435     SDL_free(joystick->buttons);
   436     SDL_free(joystick);
   437 }
   438 
   439 void
   440 SDL_JoystickQuit(void)
   441 {
   442     /* Make sure we're not getting called in the middle of updating joysticks */
   443     SDL_assert(!SDL_updating_joystick);
   444 
   445     /* Stop the event polling */
   446     while (SDL_joysticks) {
   447         SDL_joysticks->ref_count = 1;
   448         SDL_JoystickClose(SDL_joysticks);
   449     }
   450 
   451     /* Quit the joystick setup */
   452     SDL_SYS_JoystickQuit();
   453 
   454 #if !SDL_EVENTS_DISABLED
   455     SDL_QuitSubSystem(SDL_INIT_EVENTS);
   456 #endif
   457 }
   458 
   459 
   460 static SDL_bool
   461 SDL_PrivateJoystickShouldIgnoreEvent()
   462 {
   463     if (SDL_joystick_allows_background_events) {
   464         return SDL_FALSE;
   465     }
   466 
   467     if (SDL_WasInit(SDL_INIT_VIDEO)) {
   468         if (SDL_GetKeyboardFocus() == NULL) {
   469             /* Video is initialized and we don't have focus, ignore the event. */
   470             return SDL_TRUE;
   471         } else {
   472             return SDL_FALSE;
   473         }
   474     }
   475 
   476     /* Video subsystem wasn't initialized, always allow the event */
   477     return SDL_FALSE;
   478 }
   479 
   480 /* These are global for SDL_sysjoystick.c and SDL_events.c */
   481 
   482 int
   483 SDL_PrivateJoystickAxis(SDL_Joystick * joystick, Uint8 axis, Sint16 value)
   484 {
   485     int posted;
   486 
   487     /* Make sure we're not getting garbage or duplicate events */
   488     if (axis >= joystick->naxes) {
   489         return 0;
   490     }
   491     if (value == joystick->axes[axis]) {
   492         return 0;
   493     }
   494 
   495     /* We ignore events if we don't have keyboard focus, except for centering
   496      * events.
   497      */
   498     if (SDL_PrivateJoystickShouldIgnoreEvent()) {
   499         if ((value > 0 && value >= joystick->axes[axis]) ||
   500             (value < 0 && value <= joystick->axes[axis])) {
   501             return 0;
   502         }
   503     }
   504 
   505     /* Update internal joystick state */
   506     joystick->axes[axis] = value;
   507 
   508     /* Post the event, if desired */
   509     posted = 0;
   510 #if !SDL_EVENTS_DISABLED
   511     if (SDL_GetEventState(SDL_JOYAXISMOTION) == SDL_ENABLE) {
   512         SDL_Event event;
   513         event.type = SDL_JOYAXISMOTION;
   514         event.jaxis.which = joystick->instance_id;
   515         event.jaxis.axis = axis;
   516         event.jaxis.value = value;
   517         posted = SDL_PushEvent(&event) == 1;
   518     }
   519 #endif /* !SDL_EVENTS_DISABLED */
   520     return (posted);
   521 }
   522 
   523 int
   524 SDL_PrivateJoystickHat(SDL_Joystick * joystick, Uint8 hat, Uint8 value)
   525 {
   526     int posted;
   527 
   528     /* Make sure we're not getting garbage or duplicate events */
   529     if (hat >= joystick->nhats) {
   530         return 0;
   531     }
   532     if (value == joystick->hats[hat]) {
   533         return 0;
   534     }
   535 
   536     /* We ignore events if we don't have keyboard focus, except for centering
   537      * events.
   538      */
   539     if (SDL_PrivateJoystickShouldIgnoreEvent()) {
   540         if (value != SDL_HAT_CENTERED) {
   541             return 0;
   542         }
   543     }
   544 
   545     /* Update internal joystick state */
   546     joystick->hats[hat] = value;
   547 
   548     /* Post the event, if desired */
   549     posted = 0;
   550 #if !SDL_EVENTS_DISABLED
   551     if (SDL_GetEventState(SDL_JOYHATMOTION) == SDL_ENABLE) {
   552         SDL_Event event;
   553         event.jhat.type = SDL_JOYHATMOTION;
   554         event.jhat.which = joystick->instance_id;
   555         event.jhat.hat = hat;
   556         event.jhat.value = value;
   557         posted = SDL_PushEvent(&event) == 1;
   558     }
   559 #endif /* !SDL_EVENTS_DISABLED */
   560     return (posted);
   561 }
   562 
   563 int
   564 SDL_PrivateJoystickBall(SDL_Joystick * joystick, Uint8 ball,
   565                         Sint16 xrel, Sint16 yrel)
   566 {
   567     int posted;
   568 
   569     /* Make sure we're not getting garbage events */
   570     if (ball >= joystick->nballs) {
   571         return 0;
   572     }
   573 
   574     /* We ignore events if we don't have keyboard focus. */
   575     if (SDL_PrivateJoystickShouldIgnoreEvent()) {
   576         return 0;
   577     }
   578 
   579     /* Update internal mouse state */
   580     joystick->balls[ball].dx += xrel;
   581     joystick->balls[ball].dy += yrel;
   582 
   583     /* Post the event, if desired */
   584     posted = 0;
   585 #if !SDL_EVENTS_DISABLED
   586     if (SDL_GetEventState(SDL_JOYBALLMOTION) == SDL_ENABLE) {
   587         SDL_Event event;
   588         event.jball.type = SDL_JOYBALLMOTION;
   589         event.jball.which = joystick->instance_id;
   590         event.jball.ball = ball;
   591         event.jball.xrel = xrel;
   592         event.jball.yrel = yrel;
   593         posted = SDL_PushEvent(&event) == 1;
   594     }
   595 #endif /* !SDL_EVENTS_DISABLED */
   596     return (posted);
   597 }
   598 
   599 int
   600 SDL_PrivateJoystickButton(SDL_Joystick * joystick, Uint8 button, Uint8 state)
   601 {
   602     int posted;
   603 #if !SDL_EVENTS_DISABLED
   604     SDL_Event event;
   605 
   606     switch (state) {
   607     case SDL_PRESSED:
   608         event.type = SDL_JOYBUTTONDOWN;
   609         break;
   610     case SDL_RELEASED:
   611         event.type = SDL_JOYBUTTONUP;
   612         break;
   613     default:
   614         /* Invalid state -- bail */
   615         return (0);
   616     }
   617 #endif /* !SDL_EVENTS_DISABLED */
   618 
   619     /* Make sure we're not getting garbage or duplicate events */
   620     if (button >= joystick->nbuttons) {
   621         return 0;
   622 	}
   623 	if (state == joystick->buttons[button]) {
   624 		return 0;
   625 	}
   626 
   627     /* We ignore events if we don't have keyboard focus, except for button
   628      * release. */
   629     if (SDL_PrivateJoystickShouldIgnoreEvent()) {
   630         if (state == SDL_PRESSED) {
   631             return 0;
   632         }
   633     }
   634 
   635     /* Update internal joystick state */
   636     joystick->buttons[button] = state;
   637 
   638     /* Post the event, if desired */
   639     posted = 0;
   640 #if !SDL_EVENTS_DISABLED
   641     if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   642         event.jbutton.which = joystick->instance_id;
   643         event.jbutton.button = button;
   644         event.jbutton.state = state;
   645         posted = SDL_PushEvent(&event) == 1;
   646     }
   647 #endif /* !SDL_EVENTS_DISABLED */
   648     return (posted);
   649 }
   650 
   651 void
   652 SDL_JoystickUpdate(void)
   653 {
   654     SDL_Joystick *joystick;
   655 
   656     joystick = SDL_joysticks;
   657     while (joystick) {
   658         SDL_Joystick *joysticknext;
   659         /* save off the next pointer, the Update call may cause a joystick removed event
   660          * and cause our joystick pointer to be freed
   661          */
   662         joysticknext = joystick->next;
   663 
   664         SDL_updating_joystick = joystick;
   665 
   666         SDL_SYS_JoystickUpdate(joystick);
   667 
   668         if (joystick->force_recentering) {
   669             int i;
   670 
   671             /* Tell the app that everything is centered/unpressed...  */
   672             for (i = 0; i < joystick->naxes; i++)
   673                 SDL_PrivateJoystickAxis(joystick, i, 0);
   674 
   675             for (i = 0; i < joystick->nbuttons; i++)
   676                 SDL_PrivateJoystickButton(joystick, i, 0);
   677 
   678             for (i = 0; i < joystick->nhats; i++)
   679                 SDL_PrivateJoystickHat(joystick, i, SDL_HAT_CENTERED);
   680 
   681             joystick->force_recentering = SDL_FALSE;
   682         }
   683 
   684         SDL_updating_joystick = NULL;
   685 
   686         /* If the joystick was closed while updating, free it here */
   687         if (joystick->ref_count <= 0) {
   688             SDL_JoystickClose(joystick);
   689         }
   690 
   691         joystick = joysticknext;
   692     }
   693 
   694     /* this needs to happen AFTER walking the joystick list above, so that any
   695        dangling hardware data from removed devices can be free'd
   696      */
   697     SDL_SYS_JoystickDetect();
   698 }
   699 
   700 int
   701 SDL_JoystickEventState(int state)
   702 {
   703 #if SDL_EVENTS_DISABLED
   704     return SDL_DISABLE;
   705 #else
   706     const Uint32 event_list[] = {
   707         SDL_JOYAXISMOTION, SDL_JOYBALLMOTION, SDL_JOYHATMOTION,
   708         SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP, SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED
   709     };
   710     unsigned int i;
   711 
   712     switch (state) {
   713     case SDL_QUERY:
   714         state = SDL_DISABLE;
   715         for (i = 0; i < SDL_arraysize(event_list); ++i) {
   716             state = SDL_EventState(event_list[i], SDL_QUERY);
   717             if (state == SDL_ENABLE) {
   718                 break;
   719             }
   720         }
   721         break;
   722     default:
   723         for (i = 0; i < SDL_arraysize(event_list); ++i) {
   724             SDL_EventState(event_list[i], state);
   725         }
   726         break;
   727     }
   728     return (state);
   729 #endif /* SDL_EVENTS_DISABLED */
   730 }
   731 
   732 /* return the guid for this index */
   733 SDL_JoystickGUID SDL_JoystickGetDeviceGUID(int device_index)
   734 {
   735     if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) {
   736         SDL_JoystickGUID emptyGUID;
   737         SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
   738         SDL_zero(emptyGUID);
   739         return emptyGUID;
   740     }
   741     return SDL_SYS_JoystickGetDeviceGUID(device_index);
   742 }
   743 
   744 /* return the guid for this opened device */
   745 SDL_JoystickGUID SDL_JoystickGetGUID(SDL_Joystick * joystick)
   746 {
   747     if (!SDL_PrivateJoystickValid(joystick)) {
   748         SDL_JoystickGUID emptyGUID;
   749         SDL_zero(emptyGUID);
   750         return emptyGUID;
   751     }
   752     return SDL_SYS_JoystickGetGUID(joystick);
   753 }
   754 
   755 /* convert the guid to a printable string */
   756 void SDL_JoystickGetGUIDString(SDL_JoystickGUID guid, char *pszGUID, int cbGUID)
   757 {
   758     static const char k_rgchHexToASCII[] = "0123456789abcdef";
   759     int i;
   760 
   761     if ((pszGUID == NULL) || (cbGUID <= 0)) {
   762         return;
   763     }
   764 
   765     for (i = 0; i < sizeof(guid.data) && i < (cbGUID-1)/2; i++) {
   766         /* each input byte writes 2 ascii chars, and might write a null byte. */
   767         /* If we don't have room for next input byte, stop */
   768         unsigned char c = guid.data[i];
   769 
   770         *pszGUID++ = k_rgchHexToASCII[c >> 4];
   771         *pszGUID++ = k_rgchHexToASCII[c & 0x0F];
   772     }
   773     *pszGUID = '\0';
   774 }
   775 
   776 
   777 /*-----------------------------------------------------------------------------
   778  * Purpose: Returns the 4 bit nibble for a hex character
   779  * Input  : c -
   780  * Output : unsigned char
   781  *-----------------------------------------------------------------------------*/
   782 static unsigned char nibble(char c)
   783 {
   784     if ((c >= '0') && (c <= '9')) {
   785         return (unsigned char)(c - '0');
   786     }
   787 
   788     if ((c >= 'A') && (c <= 'F')) {
   789         return (unsigned char)(c - 'A' + 0x0a);
   790     }
   791 
   792     if ((c >= 'a') && (c <= 'f')) {
   793         return (unsigned char)(c - 'a' + 0x0a);
   794     }
   795 
   796     /* received an invalid character, and no real way to return an error */
   797     /* AssertMsg1(false, "Q_nibble invalid hex character '%c' ", c); */
   798     return 0;
   799 }
   800 
   801 
   802 /* convert the string version of a joystick guid to the struct */
   803 SDL_JoystickGUID SDL_JoystickGetGUIDFromString(const char *pchGUID)
   804 {
   805     SDL_JoystickGUID guid;
   806     int maxoutputbytes= sizeof(guid);
   807     size_t len = SDL_strlen(pchGUID);
   808     Uint8 *p;
   809     size_t i;
   810 
   811     /* Make sure it's even */
   812     len = (len) & ~0x1;
   813 
   814     SDL_memset(&guid, 0x00, sizeof(guid));
   815 
   816     p = (Uint8 *)&guid;
   817     for (i = 0; (i < len) && ((p - (Uint8 *)&guid) < maxoutputbytes); i+=2, p++) {
   818         *p = (nibble(pchGUID[i]) << 4) | nibble(pchGUID[i+1]);
   819     }
   820 
   821     return guid;
   822 }
   823 
   824 
   825 /* vi: set ts=4 sw=4 expandtab: */