Home | History | Annotate | Download | only in joystick
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2012 Sam Lantinga
      4 
      5     This library is free software; you can redistribute it and/or
      6     modify it under the terms of the GNU Lesser General Public
      7     License as published by the Free Software Foundation; either
      8     version 2.1 of the License, or (at your option) any later version.
      9 
     10     This library is distributed in the hope that it will be useful,
     11     but WITHOUT ANY WARRANTY; without even the implied warranty of
     12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13     Lesser General Public License for more details.
     14 
     15     You should have received a copy of the GNU Lesser General Public
     16     License along with this library; if not, write to the Free Software
     17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     18 
     19     Sam Lantinga
     20     slouken (at) libsdl.org
     21 */
     22 #include "SDL_config.h"
     23 
     24 /* This is the joystick API for Simple DirectMedia Layer */
     25 
     26 #include "SDL_events.h"
     27 #include "SDL_sysjoystick.h"
     28 #include "SDL_joystick_c.h"
     29 #if !SDL_EVENTS_DISABLED
     30 #include "../events/SDL_events_c.h"
     31 #endif
     32 
     33 /* This is used for Quake III Arena */
     34 #if SDL_EVENTS_DISABLED
     35 #define SDL_Lock_EventThread()
     36 #define SDL_Unlock_EventThread()
     37 #endif
     38 
     39 Uint8 SDL_numjoysticks = 0;
     40 int SDL_allocatedjoysticks = 0;
     41 SDL_Joystick **SDL_joysticks = NULL;
     42 
     43 int SDL_JoystickInit(void)
     44 {
     45 	int arraylen;
     46 	int status;
     47 
     48 	SDL_numjoysticks = 0;
     49 	status = SDL_SYS_JoystickInit();
     50 	if ( status >= 0 ) {
     51 		SDL_allocatedjoysticks = status;
     52 		arraylen = (SDL_allocatedjoysticks+1)*sizeof(*SDL_joysticks);
     53 		SDL_joysticks = (SDL_Joystick **)SDL_malloc(arraylen);
     54 		if ( SDL_joysticks == NULL ) {
     55 			SDL_numjoysticks = 0;
     56 			SDL_allocatedjoysticks = 0;
     57 		} else {
     58 			SDL_memset(SDL_joysticks, 0, arraylen);
     59 			SDL_numjoysticks = status;
     60 		}
     61 		status = 0;
     62 	}
     63 	return(status);
     64 }
     65 
     66 /*
     67  * Count the number of joysticks attached to the system
     68  */
     69 int SDL_NumJoysticks(void)
     70 {
     71 	return SDL_numjoysticks;
     72 }
     73 
     74 /*
     75  * Get the implementation dependent name of a joystick
     76  */
     77 const char *SDL_JoystickName(int device_index)
     78 {
     79 	if ( (device_index < 0) || (device_index >= SDL_numjoysticks) ) {
     80 		SDL_SetError("There are %d joysticks available",
     81 		             SDL_numjoysticks);
     82 		return(NULL);
     83 	}
     84 	return(SDL_SYS_JoystickName(device_index));
     85 }
     86 
     87 /*
     88  * Open a joystick for use - the index passed as an argument refers to
     89  * the N'th joystick on the system.  This index is the value which will
     90  * identify this joystick in future joystick events.
     91  *
     92  * This function returns a joystick identifier, or NULL if an error occurred.
     93  */
     94 SDL_Joystick *SDL_JoystickOpen(int device_index)
     95 {
     96 	int i;
     97 	SDL_Joystick *joystick;
     98 
     99 	if ( (device_index < 0) || (device_index >= SDL_numjoysticks) ) {
    100 		SDL_SetError("There are %d joysticks available",
    101 		             SDL_numjoysticks);
    102 		return(NULL);
    103 	}
    104 
    105 	/* If the joystick is already open, return it */
    106 	for ( i=0; SDL_joysticks[i]; ++i ) {
    107 		if ( device_index == SDL_joysticks[i]->index ) {
    108 			joystick = SDL_joysticks[i];
    109 			++joystick->ref_count;
    110 			return(joystick);
    111 		}
    112 	}
    113 
    114 	/* Create and initialize the joystick */
    115 	joystick = (SDL_Joystick *)SDL_malloc((sizeof *joystick));
    116 	if ( !joystick ) {
    117 		SDL_OutOfMemory();
    118 		return(NULL);
    119 	}
    120 
    121 	SDL_memset(joystick, 0, (sizeof *joystick));
    122 	joystick->index = device_index;
    123 	if ( SDL_SYS_JoystickOpen(joystick) < 0 ) {
    124 		SDL_free(joystick);
    125 		return(NULL);
    126 	}
    127 
    128 	if ( joystick->naxes > 0 ) {
    129 		joystick->axes = (Sint16 *)SDL_malloc
    130 			(joystick->naxes*sizeof(Sint16));
    131 	}
    132 	if ( joystick->nhats > 0 ) {
    133 		joystick->hats = (Uint8 *)SDL_malloc
    134 			(joystick->nhats*sizeof(Uint8));
    135 	}
    136 	if ( joystick->nballs > 0 ) {
    137 		joystick->balls = (struct balldelta *)SDL_malloc
    138 			(joystick->nballs*sizeof(*joystick->balls));
    139 	}
    140 	if ( joystick->nbuttons > 0 ) {
    141 		joystick->buttons = (Uint8 *)SDL_malloc
    142 			(joystick->nbuttons*sizeof(Uint8));
    143 	}
    144 	if ( ((joystick->naxes > 0) && !joystick->axes)
    145 	  || ((joystick->nhats > 0) && !joystick->hats)
    146 	  || ((joystick->nballs > 0) && !joystick->balls)
    147 	  || ((joystick->nbuttons > 0) && !joystick->buttons)) {
    148 		SDL_OutOfMemory();
    149 		SDL_JoystickClose(joystick);
    150 		return(NULL);
    151 	}
    152 
    153 	if ( joystick->axes ) {
    154 		SDL_memset(joystick->axes, 0,
    155 			joystick->naxes*sizeof(Sint16));
    156 	}
    157 	if ( joystick->hats ) {
    158 		SDL_memset(joystick->hats, 0,
    159 			joystick->nhats*sizeof(Uint8));
    160 	}
    161 	if ( joystick->balls ) {
    162 		SDL_memset(joystick->balls, 0,
    163 			joystick->nballs*sizeof(*joystick->balls));
    164 	}
    165 	if ( joystick->buttons ) {
    166 		SDL_memset(joystick->buttons, 0,
    167 			joystick->nbuttons*sizeof(Uint8));
    168 	}
    169 
    170 	/* Add joystick to list */
    171 	++joystick->ref_count;
    172 	SDL_Lock_EventThread();
    173 	for ( i=0; SDL_joysticks[i]; ++i )
    174 		/* Skip to next joystick */ ;
    175 	SDL_joysticks[i] = joystick;
    176 	SDL_Unlock_EventThread();
    177 
    178 	return(joystick);
    179 }
    180 
    181 /*
    182  * Returns 1 if the joystick has been opened, or 0 if it has not.
    183  */
    184 int SDL_JoystickOpened(int device_index)
    185 {
    186 	int i, opened;
    187 
    188 	opened = 0;
    189 	for ( i=0; SDL_joysticks[i]; ++i ) {
    190 		if ( SDL_joysticks[i]->index == (Uint8)device_index ) {
    191 			opened = 1;
    192 			break;
    193 		}
    194 	}
    195 	return(opened);
    196 }
    197 
    198 static int ValidJoystick(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 	return valid;
    209 }
    210 
    211 /*
    212  * Get the device index of an opened joystick.
    213  */
    214 int SDL_JoystickIndex(SDL_Joystick *joystick)
    215 {
    216 	if ( ! ValidJoystick(&joystick) ) {
    217 		return(-1);
    218 	}
    219 	return(joystick->index);
    220 }
    221 
    222 /*
    223  * Get the number of multi-dimensional axis controls on a joystick
    224  */
    225 int SDL_JoystickNumAxes(SDL_Joystick *joystick)
    226 {
    227 	if ( ! ValidJoystick(&joystick) ) {
    228 		return(-1);
    229 	}
    230 	return(joystick->naxes);
    231 }
    232 
    233 /*
    234  * Get the number of hats on a joystick
    235  */
    236 int SDL_JoystickNumHats(SDL_Joystick *joystick)
    237 {
    238 	if ( ! ValidJoystick(&joystick) ) {
    239 		return(-1);
    240 	}
    241 	return(joystick->nhats);
    242 }
    243 
    244 /*
    245  * Get the number of trackballs on a joystick
    246  */
    247 int SDL_JoystickNumBalls(SDL_Joystick *joystick)
    248 {
    249 	if ( ! ValidJoystick(&joystick) ) {
    250 		return(-1);
    251 	}
    252 	return(joystick->nballs);
    253 }
    254 
    255 /*
    256  * Get the number of buttons on a joystick
    257  */
    258 int SDL_JoystickNumButtons(SDL_Joystick *joystick)
    259 {
    260 	if ( ! ValidJoystick(&joystick) ) {
    261 		return(-1);
    262 	}
    263 	return(joystick->nbuttons);
    264 }
    265 
    266 /*
    267  * Get the current state of an axis control on a joystick
    268  */
    269 Sint16 SDL_JoystickGetAxis(SDL_Joystick *joystick, int axis)
    270 {
    271 	Sint16 state;
    272 
    273 	if ( ! ValidJoystick(&joystick) ) {
    274 		return(0);
    275 	}
    276 	if ( axis < joystick->naxes ) {
    277 		state = joystick->axes[axis];
    278 	} else {
    279 		SDL_SetError("Joystick only has %d axes", joystick->naxes);
    280 		state = 0;
    281 	}
    282 	return(state);
    283 }
    284 
    285 /*
    286  * Get the current state of a hat on a joystick
    287  */
    288 Uint8 SDL_JoystickGetHat(SDL_Joystick *joystick, int hat)
    289 {
    290 	Uint8 state;
    291 
    292 	if ( ! ValidJoystick(&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 SDL_JoystickGetBall(SDL_Joystick *joystick, int ball, int *dx, int *dy)
    308 {
    309 	int retval;
    310 
    311 	if ( ! ValidJoystick(&joystick) ) {
    312 		return(-1);
    313 	}
    314 
    315 	retval = 0;
    316 	if ( ball < joystick->nballs ) {
    317 		if ( dx ) {
    318 			*dx = joystick->balls[ball].dx;
    319 		}
    320 		if ( dy ) {
    321 			*dy = joystick->balls[ball].dy;
    322 		}
    323 		joystick->balls[ball].dx = 0;
    324 		joystick->balls[ball].dy = 0;
    325 	} else {
    326 		SDL_SetError("Joystick only has %d balls", joystick->nballs);
    327 		retval = -1;
    328 	}
    329 	return(retval);
    330 }
    331 
    332 /*
    333  * Get the current state of a button on a joystick
    334  */
    335 Uint8 SDL_JoystickGetButton(SDL_Joystick *joystick, int button)
    336 {
    337 	Uint8 state;
    338 
    339 	if ( ! ValidJoystick(&joystick) ) {
    340 		return(0);
    341 	}
    342 	if ( button < joystick->nbuttons ) {
    343 		state = joystick->buttons[button];
    344 	} else {
    345 		SDL_SetError("Joystick only has %d buttons",joystick->nbuttons);
    346 		state = 0;
    347 	}
    348 	return(state);
    349 }
    350 
    351 /*
    352  * Close a joystick previously opened with SDL_JoystickOpen()
    353  */
    354 void SDL_JoystickClose(SDL_Joystick *joystick)
    355 {
    356 	int i;
    357 
    358 	if ( ! ValidJoystick(&joystick) ) {
    359 		return;
    360 	}
    361 
    362 	/* First decrement ref count */
    363 	if ( --joystick->ref_count > 0 ) {
    364 		return;
    365 	}
    366 
    367 	/* Lock the event queue - prevent joystick polling */
    368 	SDL_Lock_EventThread();
    369 
    370 	SDL_SYS_JoystickClose(joystick);
    371 
    372 	/* Remove joystick from list */
    373 	for ( i=0; SDL_joysticks[i]; ++i ) {
    374 		if ( joystick == SDL_joysticks[i] ) {
    375 			SDL_memmove(&SDL_joysticks[i], &SDL_joysticks[i+1],
    376 			       (SDL_allocatedjoysticks-i)*sizeof(joystick));
    377 			break;
    378 		}
    379 	}
    380 
    381 	/* Let the event thread keep running */
    382 	SDL_Unlock_EventThread();
    383 
    384 	/* Free the data associated with this joystick */
    385 	if ( joystick->axes ) {
    386 		SDL_free(joystick->axes);
    387 	}
    388 	if ( joystick->hats ) {
    389 		SDL_free(joystick->hats);
    390 	}
    391 	if ( joystick->balls ) {
    392 		SDL_free(joystick->balls);
    393 	}
    394 	if ( joystick->buttons ) {
    395 		SDL_free(joystick->buttons);
    396 	}
    397 	SDL_free(joystick);
    398 }
    399 
    400 void SDL_JoystickQuit(void)
    401 {
    402 	const int numsticks = SDL_numjoysticks;
    403 	int i;
    404 
    405 	/* Stop the event polling */
    406 	SDL_Lock_EventThread();
    407 	SDL_numjoysticks = 0;
    408 	SDL_Unlock_EventThread();
    409 
    410 	if (SDL_joysticks != NULL) {
    411 		for (i = 0; i < numsticks; i++) {
    412 			SDL_Joystick *stick = SDL_joysticks[i];
    413 			if (stick && (stick->ref_count >= 1)) {
    414 				stick->ref_count = 1;
    415 				SDL_JoystickClose(stick);
    416 			}
    417 		}
    418 	}
    419 
    420 	/* Quit the joystick setup */
    421 	SDL_SYS_JoystickQuit();
    422 	if ( SDL_joysticks ) {
    423 		SDL_free(SDL_joysticks);
    424 		SDL_joysticks = NULL;
    425 		SDL_allocatedjoysticks = 0;
    426 	}
    427 }
    428 
    429 
    430 /* These are global for SDL_sysjoystick.c and SDL_events.c */
    431 
    432 int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
    433 {
    434 	int posted;
    435 
    436 	/* Make sure we're not getting garbage events */
    437 	if (axis >= joystick->naxes) {
    438 		return 0;
    439 	}
    440 
    441 	/* Update internal joystick state */
    442 	joystick->axes[axis] = value;
    443 
    444 	/* Post the event, if desired */
    445 	posted = 0;
    446 #if !SDL_EVENTS_DISABLED
    447 	if ( SDL_ProcessEvents[SDL_JOYAXISMOTION] == SDL_ENABLE ) {
    448 		SDL_Event event;
    449 		event.type = SDL_JOYAXISMOTION;
    450 		event.jaxis.which = joystick->index;
    451 		event.jaxis.axis = axis;
    452 		event.jaxis.value = value;
    453 		if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) {
    454 			posted = 1;
    455 			SDL_PushEvent(&event);
    456 		}
    457 	}
    458 #endif /* !SDL_EVENTS_DISABLED */
    459 	return(posted);
    460 }
    461 
    462 int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
    463 {
    464 	int posted;
    465 
    466 	/* Make sure we're not getting garbage events */
    467 	if (hat >= joystick->nhats) {
    468 		return 0;
    469 	}
    470 
    471 	/* Update internal joystick state */
    472 	joystick->hats[hat] = value;
    473 
    474 	/* Post the event, if desired */
    475 	posted = 0;
    476 #if !SDL_EVENTS_DISABLED
    477 	if ( SDL_ProcessEvents[SDL_JOYHATMOTION] == SDL_ENABLE ) {
    478 		SDL_Event event;
    479 		event.jhat.type = SDL_JOYHATMOTION;
    480 		event.jhat.which = joystick->index;
    481 		event.jhat.hat = hat;
    482 		event.jhat.value = value;
    483 		if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) {
    484 			posted = 1;
    485 			SDL_PushEvent(&event);
    486 		}
    487 	}
    488 #endif /* !SDL_EVENTS_DISABLED */
    489 	return(posted);
    490 }
    491 
    492 int SDL_PrivateJoystickBall(SDL_Joystick *joystick, Uint8 ball,
    493 					Sint16 xrel, Sint16 yrel)
    494 {
    495 	int posted;
    496 
    497 	/* Make sure we're not getting garbage events */
    498 	if (ball >= joystick->nballs) {
    499 		return 0;
    500 	}
    501 
    502 	/* Update internal mouse state */
    503 	joystick->balls[ball].dx += xrel;
    504 	joystick->balls[ball].dy += yrel;
    505 
    506 	/* Post the event, if desired */
    507 	posted = 0;
    508 #if !SDL_EVENTS_DISABLED
    509 	if ( SDL_ProcessEvents[SDL_JOYBALLMOTION] == SDL_ENABLE ) {
    510 		SDL_Event event;
    511 		event.jball.type = SDL_JOYBALLMOTION;
    512 		event.jball.which = joystick->index;
    513 		event.jball.ball = ball;
    514 		event.jball.xrel = xrel;
    515 		event.jball.yrel = yrel;
    516 		if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) {
    517 			posted = 1;
    518 			SDL_PushEvent(&event);
    519 		}
    520 	}
    521 #endif /* !SDL_EVENTS_DISABLED */
    522 	return(posted);
    523 }
    524 
    525 int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
    526 {
    527 	int posted;
    528 #if !SDL_EVENTS_DISABLED
    529 	SDL_Event event;
    530 
    531 	switch ( state ) {
    532 		case SDL_PRESSED:
    533 			event.type = SDL_JOYBUTTONDOWN;
    534 			break;
    535 		case SDL_RELEASED:
    536 			event.type = SDL_JOYBUTTONUP;
    537 			break;
    538 		default:
    539 			/* Invalid state -- bail */
    540 			return(0);
    541 	}
    542 #endif /* !SDL_EVENTS_DISABLED */
    543 
    544 	/* Make sure we're not getting garbage events */
    545 	if (button >= joystick->nbuttons) {
    546 		return 0;
    547 	}
    548 
    549 	/* Update internal joystick state */
    550 	joystick->buttons[button] = state;
    551 
    552 	/* Post the event, if desired */
    553 	posted = 0;
    554 #if !SDL_EVENTS_DISABLED
    555 	if ( SDL_ProcessEvents[event.type] == SDL_ENABLE ) {
    556 		event.jbutton.which = joystick->index;
    557 		event.jbutton.button = button;
    558 		event.jbutton.state = state;
    559 		if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) {
    560 			posted = 1;
    561 			SDL_PushEvent(&event);
    562 		}
    563 	}
    564 #endif /* !SDL_EVENTS_DISABLED */
    565 	return(posted);
    566 }
    567 
    568 void SDL_JoystickUpdate(void)
    569 {
    570 	int i;
    571 
    572 	for ( i=0; SDL_joysticks[i]; ++i ) {
    573 		SDL_SYS_JoystickUpdate(SDL_joysticks[i]);
    574 	}
    575 }
    576 
    577 int SDL_JoystickEventState(int state)
    578 {
    579 #if SDL_EVENTS_DISABLED
    580 	return SDL_IGNORE;
    581 #else
    582 	const Uint8 event_list[] = {
    583 		SDL_JOYAXISMOTION, SDL_JOYBALLMOTION, SDL_JOYHATMOTION,
    584 		SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP,
    585 	};
    586 	unsigned int i;
    587 
    588 	switch (state) {
    589 		case SDL_QUERY:
    590 			state = SDL_IGNORE;
    591 			for ( i=0; i<SDL_arraysize(event_list); ++i ) {
    592 				state = SDL_EventState(event_list[i],SDL_QUERY);
    593 				if ( state == SDL_ENABLE ) {
    594 					break;
    595 				}
    596 			}
    597 			break;
    598 		default:
    599 			for ( i=0; i<SDL_arraysize(event_list); ++i ) {
    600 				SDL_EventState(event_list[i], state);
    601 			}
    602 			break;
    603 	}
    604 	return(state);
    605 #endif /* SDL_EVENTS_DISABLED */
    606 }
    607