src/joystick/SDL_joystick.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 24 Dec 2014 22:22:35 -0500
changeset 9289 5f857c00d25d
parent 8920 21ccd40c778a
child 9433 bd062398b648
permissions -rw-r--r--
Maybe actually patched to compile this time?
     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     if (joystick && joystick->closed) {
   210         valid = 0;
   211     }
   212 
   213     return valid;
   214 }
   215 
   216 /*
   217  * Get the number of multi-dimensional axis controls on a joystick
   218  */
   219 int
   220 SDL_JoystickNumAxes(SDL_Joystick * joystick)
   221 {
   222     if (!SDL_PrivateJoystickValid(joystick)) {
   223         return (-1);
   224     }
   225     return (joystick->naxes);
   226 }
   227 
   228 /*
   229  * Get the number of hats on a joystick
   230  */
   231 int
   232 SDL_JoystickNumHats(SDL_Joystick * joystick)
   233 {
   234     if (!SDL_PrivateJoystickValid(joystick)) {
   235         return (-1);
   236     }
   237     return (joystick->nhats);
   238 }
   239 
   240 /*
   241  * Get the number of trackballs on a joystick
   242  */
   243 int
   244 SDL_JoystickNumBalls(SDL_Joystick * joystick)
   245 {
   246     if (!SDL_PrivateJoystickValid(joystick)) {
   247         return (-1);
   248     }
   249     return (joystick->nballs);
   250 }
   251 
   252 /*
   253  * Get the number of buttons on a joystick
   254  */
   255 int
   256 SDL_JoystickNumButtons(SDL_Joystick * joystick)
   257 {
   258     if (!SDL_PrivateJoystickValid(joystick)) {
   259         return (-1);
   260     }
   261     return (joystick->nbuttons);
   262 }
   263 
   264 /*
   265  * Get the current state of an axis control on a joystick
   266  */
   267 Sint16
   268 SDL_JoystickGetAxis(SDL_Joystick * joystick, int axis)
   269 {
   270     Sint16 state;
   271 
   272     if (!SDL_PrivateJoystickValid(joystick)) {
   273         return (0);
   274     }
   275     if (axis < joystick->naxes) {
   276         state = joystick->axes[axis];
   277     } else {
   278         SDL_SetError("Joystick only has %d axes", joystick->naxes);
   279         state = 0;
   280     }
   281     return (state);
   282 }
   283 
   284 /*
   285  * Get the current state of a hat on a joystick
   286  */
   287 Uint8
   288 SDL_JoystickGetHat(SDL_Joystick * joystick, int hat)
   289 {
   290     Uint8 state;
   291 
   292     if (!SDL_PrivateJoystickValid(joystick)) {
   293         return (0);
   294     }
   295     if (hat < joystick->nhats) {
   296         state = joystick->hats[hat];
   297     } else {
   298         SDL_SetError("Joystick only has %d hats", joystick->nhats);
   299         state = 0;
   300     }
   301     return (state);
   302 }
   303 
   304 /*
   305  * Get the ball axis change since the last poll
   306  */
   307 int
   308 SDL_JoystickGetBall(SDL_Joystick * joystick, int ball, int *dx, int *dy)
   309 {
   310     int retval;
   311 
   312     if (!SDL_PrivateJoystickValid(joystick)) {
   313         return (-1);
   314     }
   315 
   316     retval = 0;
   317     if (ball < joystick->nballs) {
   318         if (dx) {
   319             *dx = joystick->balls[ball].dx;
   320         }
   321         if (dy) {
   322             *dy = joystick->balls[ball].dy;
   323         }
   324         joystick->balls[ball].dx = 0;
   325         joystick->balls[ball].dy = 0;
   326     } else {
   327         return SDL_SetError("Joystick only has %d balls", joystick->nballs);
   328     }
   329     return (retval);
   330 }
   331 
   332 /*
   333  * Get the current state of a button on a joystick
   334  */
   335 Uint8
   336 SDL_JoystickGetButton(SDL_Joystick * joystick, int button)
   337 {
   338     Uint8 state;
   339 
   340     if (!SDL_PrivateJoystickValid(joystick)) {
   341         return (0);
   342     }
   343     if (button < joystick->nbuttons) {
   344         state = joystick->buttons[button];
   345     } else {
   346         SDL_SetError("Joystick only has %d buttons", joystick->nbuttons);
   347         state = 0;
   348     }
   349     return (state);
   350 }
   351 
   352 /*
   353  * Return if the joystick in question is currently attached to the system,
   354  *  \return SDL_FALSE if not plugged in, SDL_TRUE if still present.
   355  */
   356 SDL_bool
   357 SDL_JoystickGetAttached(SDL_Joystick * joystick)
   358 {
   359     if (!SDL_PrivateJoystickValid(joystick)) {
   360         return SDL_FALSE;
   361     }
   362 
   363     return SDL_SYS_JoystickAttached(joystick);
   364 }
   365 
   366 /*
   367  * Get the instance id for this opened joystick
   368  */
   369 SDL_JoystickID
   370 SDL_JoystickInstanceID(SDL_Joystick * joystick)
   371 {
   372     if (!SDL_PrivateJoystickValid(joystick)) {
   373         return (-1);
   374     }
   375 
   376     return (joystick->instance_id);
   377 }
   378 
   379 /*
   380  * Get the friendly name of this joystick
   381  */
   382 const char *
   383 SDL_JoystickName(SDL_Joystick * joystick)
   384 {
   385     if (!SDL_PrivateJoystickValid(joystick)) {
   386         return (NULL);
   387     }
   388 
   389     return (joystick->name);
   390 }
   391 
   392 /*
   393  * Close a joystick previously opened with SDL_JoystickOpen()
   394  */
   395 void
   396 SDL_JoystickClose(SDL_Joystick * joystick)
   397 {
   398     SDL_Joystick *joysticklist;
   399     SDL_Joystick *joysticklistprev;
   400 
   401     if (!joystick) {
   402         return;
   403     }
   404 
   405     /* First decrement ref count */
   406     if (--joystick->ref_count > 0) {
   407         return;
   408     }
   409 
   410     if (joystick == SDL_updating_joystick) {
   411         return;
   412     }
   413 
   414     SDL_SYS_JoystickClose(joystick);
   415 
   416     joysticklist = SDL_joysticks;
   417     joysticklistprev = NULL;
   418     while (joysticklist) {
   419         if (joystick == joysticklist) {
   420             if (joysticklistprev) {
   421                 /* unlink this entry */
   422                 joysticklistprev->next = joysticklist->next;
   423             } else {
   424                 SDL_joysticks = joystick->next;
   425             }
   426             break;
   427         }
   428         joysticklistprev = joysticklist;
   429         joysticklist = joysticklist->next;
   430     }
   431 
   432     SDL_free(joystick->name);
   433 
   434     /* Free the data associated with this joystick */
   435     SDL_free(joystick->axes);
   436     SDL_free(joystick->hats);
   437     SDL_free(joystick->balls);
   438     SDL_free(joystick->buttons);
   439     SDL_free(joystick);
   440 }
   441 
   442 void
   443 SDL_JoystickQuit(void)
   444 {
   445     /* Make sure we're not getting called in the middle of updating joysticks */
   446     SDL_assert(!SDL_updating_joystick);
   447 
   448     /* Stop the event polling */
   449     while (SDL_joysticks) {
   450         SDL_joysticks->ref_count = 1;
   451         SDL_JoystickClose(SDL_joysticks);
   452     }
   453 
   454     /* Quit the joystick setup */
   455     SDL_SYS_JoystickQuit();
   456 
   457 #if !SDL_EVENTS_DISABLED
   458     SDL_QuitSubSystem(SDL_INIT_EVENTS);
   459 #endif
   460 }
   461 
   462 
   463 static SDL_bool
   464 SDL_PrivateJoystickShouldIgnoreEvent()
   465 {
   466     if (SDL_joystick_allows_background_events) {
   467         return SDL_FALSE;
   468     }
   469 
   470     if (SDL_WasInit(SDL_INIT_VIDEO)) {
   471         if (SDL_GetKeyboardFocus() == NULL) {
   472             /* Video is initialized and we don't have focus, ignore the event. */
   473             return SDL_TRUE;
   474         } else {
   475             return SDL_FALSE;
   476         }
   477     }
   478 
   479     /* Video subsystem wasn't initialized, always allow the event */
   480     return SDL_FALSE;
   481 }
   482 
   483 /* These are global for SDL_sysjoystick.c and SDL_events.c */
   484 
   485 int
   486 SDL_PrivateJoystickAxis(SDL_Joystick * joystick, Uint8 axis, Sint16 value)
   487 {
   488     int posted;
   489 
   490     /* Make sure we're not getting garbage or duplicate events */
   491     if (axis >= joystick->naxes) {
   492         return 0;
   493     }
   494     if (value == joystick->axes[axis]) {
   495         return 0;
   496     }
   497 
   498     /* We ignore events if we don't have keyboard focus, except for centering
   499      * events.
   500      */
   501     if (SDL_PrivateJoystickShouldIgnoreEvent()) {
   502         if ((value > 0 && value >= joystick->axes[axis]) ||
   503             (value < 0 && value <= joystick->axes[axis])) {
   504             return 0;
   505         }
   506     }
   507 
   508     /* Update internal joystick state */
   509     joystick->axes[axis] = value;
   510 
   511     /* Post the event, if desired */
   512     posted = 0;
   513 #if !SDL_EVENTS_DISABLED
   514     if (SDL_GetEventState(SDL_JOYAXISMOTION) == SDL_ENABLE) {
   515         SDL_Event event;
   516         event.type = SDL_JOYAXISMOTION;
   517         event.jaxis.which = joystick->instance_id;
   518         event.jaxis.axis = axis;
   519         event.jaxis.value = value;
   520         posted = SDL_PushEvent(&event) == 1;
   521     }
   522 #endif /* !SDL_EVENTS_DISABLED */
   523     return (posted);
   524 }
   525 
   526 int
   527 SDL_PrivateJoystickHat(SDL_Joystick * joystick, Uint8 hat, Uint8 value)
   528 {
   529     int posted;
   530 
   531     /* Make sure we're not getting garbage or duplicate events */
   532     if (hat >= joystick->nhats) {
   533         return 0;
   534     }
   535     if (value == joystick->hats[hat]) {
   536         return 0;
   537     }
   538 
   539     /* We ignore events if we don't have keyboard focus, except for centering
   540      * events.
   541      */
   542     if (SDL_PrivateJoystickShouldIgnoreEvent()) {
   543         if (value != SDL_HAT_CENTERED) {
   544             return 0;
   545         }
   546     }
   547 
   548     /* Update internal joystick state */
   549     joystick->hats[hat] = value;
   550 
   551     /* Post the event, if desired */
   552     posted = 0;
   553 #if !SDL_EVENTS_DISABLED
   554     if (SDL_GetEventState(SDL_JOYHATMOTION) == SDL_ENABLE) {
   555         SDL_Event event;
   556         event.jhat.type = SDL_JOYHATMOTION;
   557         event.jhat.which = joystick->instance_id;
   558         event.jhat.hat = hat;
   559         event.jhat.value = value;
   560         posted = SDL_PushEvent(&event) == 1;
   561     }
   562 #endif /* !SDL_EVENTS_DISABLED */
   563     return (posted);
   564 }
   565 
   566 int
   567 SDL_PrivateJoystickBall(SDL_Joystick * joystick, Uint8 ball,
   568                         Sint16 xrel, Sint16 yrel)
   569 {
   570     int posted;
   571 
   572     /* Make sure we're not getting garbage events */
   573     if (ball >= joystick->nballs) {
   574         return 0;
   575     }
   576 
   577     /* We ignore events if we don't have keyboard focus. */
   578     if (SDL_PrivateJoystickShouldIgnoreEvent()) {
   579         return 0;
   580     }
   581 
   582     /* Update internal mouse state */
   583     joystick->balls[ball].dx += xrel;
   584     joystick->balls[ball].dy += yrel;
   585 
   586     /* Post the event, if desired */
   587     posted = 0;
   588 #if !SDL_EVENTS_DISABLED
   589     if (SDL_GetEventState(SDL_JOYBALLMOTION) == SDL_ENABLE) {
   590         SDL_Event event;
   591         event.jball.type = SDL_JOYBALLMOTION;
   592         event.jball.which = joystick->instance_id;
   593         event.jball.ball = ball;
   594         event.jball.xrel = xrel;
   595         event.jball.yrel = yrel;
   596         posted = SDL_PushEvent(&event) == 1;
   597     }
   598 #endif /* !SDL_EVENTS_DISABLED */
   599     return (posted);
   600 }
   601 
   602 int
   603 SDL_PrivateJoystickButton(SDL_Joystick * joystick, Uint8 button, Uint8 state)
   604 {
   605     int posted;
   606 #if !SDL_EVENTS_DISABLED
   607     SDL_Event event;
   608 
   609     switch (state) {
   610     case SDL_PRESSED:
   611         event.type = SDL_JOYBUTTONDOWN;
   612         break;
   613     case SDL_RELEASED:
   614         event.type = SDL_JOYBUTTONUP;
   615         break;
   616     default:
   617         /* Invalid state -- bail */
   618         return (0);
   619     }
   620 #endif /* !SDL_EVENTS_DISABLED */
   621 
   622     /* Make sure we're not getting garbage or duplicate events */
   623     if (button >= joystick->nbuttons) {
   624         return 0;
   625 	}
   626 	if (state == joystick->buttons[button]) {
   627 		return 0;
   628 	}
   629 
   630     /* We ignore events if we don't have keyboard focus, except for button
   631      * release. */
   632     if (SDL_PrivateJoystickShouldIgnoreEvent()) {
   633         if (state == SDL_PRESSED) {
   634             return 0;
   635         }
   636     }
   637 
   638     /* Update internal joystick state */
   639     joystick->buttons[button] = state;
   640 
   641     /* Post the event, if desired */
   642     posted = 0;
   643 #if !SDL_EVENTS_DISABLED
   644     if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   645         event.jbutton.which = joystick->instance_id;
   646         event.jbutton.button = button;
   647         event.jbutton.state = state;
   648         posted = SDL_PushEvent(&event) == 1;
   649     }
   650 #endif /* !SDL_EVENTS_DISABLED */
   651     return (posted);
   652 }
   653 
   654 void
   655 SDL_JoystickUpdate(void)
   656 {
   657     SDL_Joystick *joystick;
   658 
   659     joystick = SDL_joysticks;
   660     while (joystick) {
   661         SDL_Joystick *joysticknext;
   662         /* save off the next pointer, the Update call may cause a joystick removed event
   663          * and cause our joystick pointer to be freed
   664          */
   665         joysticknext = joystick->next;
   666 
   667         SDL_updating_joystick = joystick;
   668 
   669         SDL_SYS_JoystickUpdate(joystick);
   670 
   671         if (joystick->closed && joystick->uncentered) {
   672             int i;
   673 
   674             /* Tell the app that everything is centered/unpressed...  */
   675             for (i = 0; i < joystick->naxes; i++)
   676                 SDL_PrivateJoystickAxis(joystick, i, 0);
   677 
   678             for (i = 0; i < joystick->nbuttons; i++)
   679                 SDL_PrivateJoystickButton(joystick, i, 0);
   680 
   681             for (i = 0; i < joystick->nhats; i++)
   682                 SDL_PrivateJoystickHat(joystick, i, SDL_HAT_CENTERED);
   683 
   684             joystick->uncentered = SDL_FALSE;
   685         }
   686 
   687         SDL_updating_joystick = NULL;
   688 
   689         /* If the joystick was closed while updating, free it here */
   690         if (joystick->ref_count <= 0) {
   691             SDL_JoystickClose(joystick);
   692         }
   693 
   694         joystick = joysticknext;
   695     }
   696 
   697     /* this needs to happen AFTER walking the joystick list above, so that any
   698        dangling hardware data from removed devices can be free'd
   699      */
   700     SDL_SYS_JoystickDetect();
   701 }
   702 
   703 int
   704 SDL_JoystickEventState(int state)
   705 {
   706 #if SDL_EVENTS_DISABLED
   707     return SDL_DISABLE;
   708 #else
   709     const Uint32 event_list[] = {
   710         SDL_JOYAXISMOTION, SDL_JOYBALLMOTION, SDL_JOYHATMOTION,
   711         SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP, SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED
   712     };
   713     unsigned int i;
   714 
   715     switch (state) {
   716     case SDL_QUERY:
   717         state = SDL_DISABLE;
   718         for (i = 0; i < SDL_arraysize(event_list); ++i) {
   719             state = SDL_EventState(event_list[i], SDL_QUERY);
   720             if (state == SDL_ENABLE) {
   721                 break;
   722             }
   723         }
   724         break;
   725     default:
   726         for (i = 0; i < SDL_arraysize(event_list); ++i) {
   727             SDL_EventState(event_list[i], state);
   728         }
   729         break;
   730     }
   731     return (state);
   732 #endif /* SDL_EVENTS_DISABLED */
   733 }
   734 
   735 /* return the guid for this index */
   736 SDL_JoystickGUID SDL_JoystickGetDeviceGUID(int device_index)
   737 {
   738     if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) {
   739         SDL_JoystickGUID emptyGUID;
   740         SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
   741         SDL_zero(emptyGUID);
   742         return emptyGUID;
   743     }
   744     return SDL_SYS_JoystickGetDeviceGUID(device_index);
   745 }
   746 
   747 /* return the guid for this opened device */
   748 SDL_JoystickGUID SDL_JoystickGetGUID(SDL_Joystick * joystick)
   749 {
   750     if (!SDL_PrivateJoystickValid(joystick)) {
   751         SDL_JoystickGUID emptyGUID;
   752         SDL_zero(emptyGUID);
   753         return emptyGUID;
   754     }
   755     return SDL_SYS_JoystickGetGUID(joystick);
   756 }
   757 
   758 /* convert the guid to a printable string */
   759 void SDL_JoystickGetGUIDString(SDL_JoystickGUID guid, char *pszGUID, int cbGUID)
   760 {
   761     static const char k_rgchHexToASCII[] = "0123456789abcdef";
   762     int i;
   763 
   764     if ((pszGUID == NULL) || (cbGUID <= 0)) {
   765         return;
   766     }
   767 
   768     for (i = 0; i < sizeof(guid.data) && i < (cbGUID-1)/2; i++) {
   769         /* each input byte writes 2 ascii chars, and might write a null byte. */
   770         /* If we don't have room for next input byte, stop */
   771         unsigned char c = guid.data[i];
   772 
   773         *pszGUID++ = k_rgchHexToASCII[c >> 4];
   774         *pszGUID++ = k_rgchHexToASCII[c & 0x0F];
   775     }
   776     *pszGUID = '\0';
   777 }
   778 
   779 
   780 /*-----------------------------------------------------------------------------
   781  * Purpose: Returns the 4 bit nibble for a hex character
   782  * Input  : c -
   783  * Output : unsigned char
   784  *-----------------------------------------------------------------------------*/
   785 static unsigned char nibble(char c)
   786 {
   787     if ((c >= '0') && (c <= '9')) {
   788         return (unsigned char)(c - '0');
   789     }
   790 
   791     if ((c >= 'A') && (c <= 'F')) {
   792         return (unsigned char)(c - 'A' + 0x0a);
   793     }
   794 
   795     if ((c >= 'a') && (c <= 'f')) {
   796         return (unsigned char)(c - 'a' + 0x0a);
   797     }
   798 
   799     /* received an invalid character, and no real way to return an error */
   800     /* AssertMsg1(false, "Q_nibble invalid hex character '%c' ", c); */
   801     return 0;
   802 }
   803 
   804 
   805 /* convert the string version of a joystick guid to the struct */
   806 SDL_JoystickGUID SDL_JoystickGetGUIDFromString(const char *pchGUID)
   807 {
   808     SDL_JoystickGUID guid;
   809     int maxoutputbytes= sizeof(guid);
   810     size_t len = SDL_strlen(pchGUID);
   811     Uint8 *p;
   812     size_t i;
   813 
   814     /* Make sure it's even */
   815     len = (len) & ~0x1;
   816 
   817     SDL_memset(&guid, 0x00, sizeof(guid));
   818 
   819     p = (Uint8 *)&guid;
   820     for (i = 0; (i < len) && ((p - (Uint8 *)&guid) < maxoutputbytes); i+=2, p++) {
   821         *p = (nibble(pchGUID[i]) << 4) | nibble(pchGUID[i+1]);
   822     }
   823 
   824     return guid;
   825 }
   826 
   827 
   828 /* vi: set ts=4 sw=4 expandtab: */