1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2006 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@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
39Uint8 SDL_numjoysticks = 0;
40SDL_Joystick **SDL_joysticks = NULL;
41static SDL_Joystick *default_joystick = NULL;
42
43int 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		arraylen = (status+1)*sizeof(*SDL_joysticks);
52		SDL_joysticks = (SDL_Joystick **)SDL_malloc(arraylen);
53		if ( SDL_joysticks == NULL ) {
54			SDL_numjoysticks = 0;
55		} else {
56			SDL_memset(SDL_joysticks, 0, arraylen);
57			SDL_numjoysticks = status;
58		}
59		status = 0;
60	}
61	default_joystick = NULL;
62	return(status);
63}
64
65/*
66 * Count the number of joysticks attached to the system
67 */
68int SDL_NumJoysticks(void)
69{
70	return SDL_numjoysticks;
71}
72
73/*
74 * Get the implementation dependent name of a joystick
75 */
76const char *SDL_JoystickName(int device_index)
77{
78	if ( (device_index < 0) || (device_index >= SDL_numjoysticks) ) {
79		SDL_SetError("There are %d joysticks available",
80		             SDL_numjoysticks);
81		return(NULL);
82	}
83	return(SDL_SYS_JoystickName(device_index));
84}
85
86/*
87 * Open a joystick for use - the index passed as an argument refers to
88 * the N'th joystick on the system.  This index is the value which will
89 * identify this joystick in future joystick events.
90 *
91 * This function returns a joystick identifier, or NULL if an error occurred.
92 */
93SDL_Joystick *SDL_JoystickOpen(int device_index)
94{
95	int i;
96	SDL_Joystick *joystick;
97
98	if ( (device_index < 0) || (device_index >= SDL_numjoysticks) ) {
99		SDL_SetError("There are %d joysticks available",
100		             SDL_numjoysticks);
101		return(NULL);
102	}
103
104	/* If the joystick is already open, return it */
105	for ( i=0; SDL_joysticks[i]; ++i ) {
106		if ( device_index == SDL_joysticks[i]->index ) {
107			joystick = SDL_joysticks[i];
108			++joystick->ref_count;
109			return(joystick);
110		}
111	}
112
113	/* Create and initialize the joystick */
114	joystick = (SDL_Joystick *)SDL_malloc((sizeof *joystick));
115	if ( joystick != NULL ) {
116		SDL_memset(joystick, 0, (sizeof *joystick));
117		joystick->index = device_index;
118		if ( SDL_SYS_JoystickOpen(joystick) < 0 ) {
119			SDL_free(joystick);
120			joystick = NULL;
121		} else {
122			if ( joystick->naxes > 0 ) {
123				joystick->axes = (Sint16 *)SDL_malloc
124					(joystick->naxes*sizeof(Sint16));
125			}
126			if ( joystick->nhats > 0 ) {
127				joystick->hats = (Uint8 *)SDL_malloc
128					(joystick->nhats*sizeof(Uint8));
129			}
130			if ( joystick->nballs > 0 ) {
131				joystick->balls = (struct balldelta *)SDL_malloc
132				  (joystick->nballs*sizeof(*joystick->balls));
133			}
134			if ( joystick->nbuttons > 0 ) {
135				joystick->buttons = (Uint8 *)SDL_malloc
136					(joystick->nbuttons*sizeof(Uint8));
137			}
138			if ( ((joystick->naxes > 0) && !joystick->axes)
139			  || ((joystick->nhats > 0) && !joystick->hats)
140			  || ((joystick->nballs > 0) && !joystick->balls)
141			  || ((joystick->nbuttons > 0) && !joystick->buttons)) {
142				SDL_OutOfMemory();
143				SDL_JoystickClose(joystick);
144				joystick = NULL;
145			}
146			if ( joystick->axes ) {
147				SDL_memset(joystick->axes, 0,
148					joystick->naxes*sizeof(Sint16));
149			}
150			if ( joystick->hats ) {
151				SDL_memset(joystick->hats, 0,
152					joystick->nhats*sizeof(Uint8));
153			}
154			if ( joystick->balls ) {
155				SDL_memset(joystick->balls, 0,
156				  joystick->nballs*sizeof(*joystick->balls));
157			}
158			if ( joystick->buttons ) {
159				SDL_memset(joystick->buttons, 0,
160					joystick->nbuttons*sizeof(Uint8));
161			}
162		}
163	}
164	if ( joystick ) {
165		/* Add joystick to list */
166		++joystick->ref_count;
167		SDL_Lock_EventThread();
168		for ( i=0; SDL_joysticks[i]; ++i )
169			/* Skip to next joystick */;
170		SDL_joysticks[i] = joystick;
171		SDL_Unlock_EventThread();
172	}
173	return(joystick);
174}
175
176/*
177 * Returns 1 if the joystick has been opened, or 0 if it has not.
178 */
179int SDL_JoystickOpened(int device_index)
180{
181	int i, opened;
182
183	opened = 0;
184	for ( i=0; SDL_joysticks[i]; ++i ) {
185		if ( SDL_joysticks[i]->index == (Uint8)device_index ) {
186			opened = 1;
187			break;
188		}
189	}
190	return(opened);
191}
192
193static int ValidJoystick(SDL_Joystick **joystick)
194{
195	int valid;
196
197	if ( *joystick == NULL ) {
198		*joystick = default_joystick;
199	}
200	if ( *joystick == NULL ) {
201		SDL_SetError("Joystick hasn't been opened yet");
202		valid = 0;
203	} else {
204		valid = 1;
205	}
206	return valid;
207}
208
209/*
210 * Get the device index of an opened joystick.
211 */
212int SDL_JoystickIndex(SDL_Joystick *joystick)
213{
214	if ( ! ValidJoystick(&joystick) ) {
215		return(-1);
216	}
217	return(joystick->index);
218}
219
220/*
221 * Get the number of multi-dimensional axis controls on a joystick
222 */
223int SDL_JoystickNumAxes(SDL_Joystick *joystick)
224{
225	if ( ! ValidJoystick(&joystick) ) {
226		return(-1);
227	}
228	return(joystick->naxes);
229}
230
231/*
232 * Get the number of hats on a joystick
233 */
234int SDL_JoystickNumHats(SDL_Joystick *joystick)
235{
236	if ( ! ValidJoystick(&joystick) ) {
237		return(-1);
238	}
239	return(joystick->nhats);
240}
241
242/*
243 * Get the number of trackballs on a joystick
244 */
245int SDL_JoystickNumBalls(SDL_Joystick *joystick)
246{
247	if ( ! ValidJoystick(&joystick) ) {
248		return(-1);
249	}
250	return(joystick->nballs);
251}
252
253/*
254 * Get the number of buttons on a joystick
255 */
256int SDL_JoystickNumButtons(SDL_Joystick *joystick)
257{
258	if ( ! ValidJoystick(&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 */
267Sint16 SDL_JoystickGetAxis(SDL_Joystick *joystick, int axis)
268{
269	Sint16 state;
270
271	if ( ! ValidJoystick(&joystick) ) {
272		return(0);
273	}
274	if ( axis < joystick->naxes ) {
275		state = joystick->axes[axis];
276	} else {
277		SDL_SetError("Joystick only has %d axes", joystick->naxes);
278		state = 0;
279	}
280	return(state);
281}
282
283/*
284 * Get the current state of a hat on a joystick
285 */
286Uint8 SDL_JoystickGetHat(SDL_Joystick *joystick, int hat)
287{
288	Uint8 state;
289
290	if ( ! ValidJoystick(&joystick) ) {
291		return(0);
292	}
293	if ( hat < joystick->nhats ) {
294		state = joystick->hats[hat];
295	} else {
296		SDL_SetError("Joystick only has %d hats", joystick->nhats);
297		state = 0;
298	}
299	return(state);
300}
301
302/*
303 * Get the ball axis change since the last poll
304 */
305int SDL_JoystickGetBall(SDL_Joystick *joystick, int ball, int *dx, int *dy)
306{
307	int retval;
308
309	if ( ! ValidJoystick(&joystick) ) {
310		return(-1);
311	}
312
313	retval = 0;
314	if ( ball < joystick->nballs ) {
315		if ( dx ) {
316			*dx = joystick->balls[ball].dx;
317		}
318		if ( dy ) {
319			*dy = joystick->balls[ball].dy;
320		}
321		joystick->balls[ball].dx = 0;
322		joystick->balls[ball].dy = 0;
323	} else {
324		SDL_SetError("Joystick only has %d balls", joystick->nballs);
325		retval = -1;
326	}
327	return(retval);
328}
329
330/*
331 * Get the current state of a button on a joystick
332 */
333Uint8 SDL_JoystickGetButton(SDL_Joystick *joystick, int button)
334{
335	Uint8 state;
336
337	if ( ! ValidJoystick(&joystick) ) {
338		return(0);
339	}
340	if ( button < joystick->nbuttons ) {
341		state = joystick->buttons[button];
342	} else {
343		SDL_SetError("Joystick only has %d buttons",joystick->nbuttons);
344		state = 0;
345	}
346	return(state);
347}
348
349/*
350 * Close a joystick previously opened with SDL_JoystickOpen()
351 */
352void SDL_JoystickClose(SDL_Joystick *joystick)
353{
354	int i;
355
356	if ( ! ValidJoystick(&joystick) ) {
357		return;
358	}
359
360	/* First decrement ref count */
361	if ( --joystick->ref_count > 0 ) {
362		return;
363	}
364
365	/* Lock the event queue - prevent joystick polling */
366	SDL_Lock_EventThread();
367
368	if ( joystick == default_joystick ) {
369		default_joystick = NULL;
370	}
371	SDL_SYS_JoystickClose(joystick);
372
373	/* Remove joystick from list */
374	for ( i=0; SDL_joysticks[i]; ++i ) {
375		if ( joystick == SDL_joysticks[i] ) {
376			SDL_memcpy(&SDL_joysticks[i], &SDL_joysticks[i+1],
377			       (SDL_numjoysticks-i)*sizeof(joystick));
378			break;
379		}
380	}
381
382	/* Let the event thread keep running */
383	SDL_Unlock_EventThread();
384
385	/* Free the data associated with this joystick */
386	if ( joystick->axes ) {
387		SDL_free(joystick->axes);
388	}
389	if ( joystick->hats ) {
390		SDL_free(joystick->hats);
391	}
392	if ( joystick->balls ) {
393		SDL_free(joystick->balls);
394	}
395	if ( joystick->buttons ) {
396		SDL_free(joystick->buttons);
397	}
398	SDL_free(joystick);
399}
400
401void SDL_JoystickQuit(void)
402{
403	/* Stop the event polling */
404	SDL_Lock_EventThread();
405	SDL_numjoysticks = 0;
406	SDL_Unlock_EventThread();
407
408	/* Quit the joystick setup */
409	SDL_SYS_JoystickQuit();
410	if ( SDL_joysticks ) {
411		SDL_free(SDL_joysticks);
412		SDL_joysticks = NULL;
413	}
414}
415
416
417/* These are global for SDL_sysjoystick.c and SDL_events.c */
418
419int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
420{
421	int posted;
422
423	/* Update internal joystick state */
424	joystick->axes[axis] = value;
425
426	/* Post the event, if desired */
427	posted = 0;
428#if !SDL_EVENTS_DISABLED
429	if ( SDL_ProcessEvents[SDL_JOYAXISMOTION] == SDL_ENABLE ) {
430		SDL_Event event;
431		event.type = SDL_JOYAXISMOTION;
432		event.jaxis.which = joystick->index;
433		event.jaxis.axis = axis;
434		event.jaxis.value = value;
435		if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) {
436			posted = 1;
437			SDL_PushEvent(&event);
438		}
439	}
440#endif /* !SDL_EVENTS_DISABLED */
441	return(posted);
442}
443
444int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
445{
446	int posted;
447
448	/* Update internal joystick state */
449	joystick->hats[hat] = value;
450
451	/* Post the event, if desired */
452	posted = 0;
453#if !SDL_EVENTS_DISABLED
454	if ( SDL_ProcessEvents[SDL_JOYHATMOTION] == SDL_ENABLE ) {
455		SDL_Event event;
456		event.jhat.type = SDL_JOYHATMOTION;
457		event.jhat.which = joystick->index;
458		event.jhat.hat = hat;
459		event.jhat.value = value;
460		if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) {
461			posted = 1;
462			SDL_PushEvent(&event);
463		}
464	}
465#endif /* !SDL_EVENTS_DISABLED */
466	return(posted);
467}
468
469int SDL_PrivateJoystickBall(SDL_Joystick *joystick, Uint8 ball,
470					Sint16 xrel, Sint16 yrel)
471{
472	int posted;
473
474	/* Update internal mouse state */
475	joystick->balls[ball].dx += xrel;
476	joystick->balls[ball].dy += yrel;
477
478	/* Post the event, if desired */
479	posted = 0;
480#if !SDL_EVENTS_DISABLED
481	if ( SDL_ProcessEvents[SDL_JOYBALLMOTION] == SDL_ENABLE ) {
482		SDL_Event event;
483		event.jball.type = SDL_JOYBALLMOTION;
484		event.jball.which = joystick->index;
485		event.jball.ball = ball;
486		event.jball.xrel = xrel;
487		event.jball.yrel = yrel;
488		if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) {
489			posted = 1;
490			SDL_PushEvent(&event);
491		}
492	}
493#endif /* !SDL_EVENTS_DISABLED */
494	return(posted);
495}
496
497int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
498{
499	int posted;
500#if !SDL_EVENTS_DISABLED
501	SDL_Event event;
502
503	switch ( state ) {
504		case SDL_PRESSED:
505			event.type = SDL_JOYBUTTONDOWN;
506			break;
507		case SDL_RELEASED:
508			event.type = SDL_JOYBUTTONUP;
509			break;
510		default:
511			/* Invalid state -- bail */
512			return(0);
513	}
514#endif /* !SDL_EVENTS_DISABLED */
515
516	/* Update internal joystick state */
517	joystick->buttons[button] = state;
518
519	/* Post the event, if desired */
520	posted = 0;
521#if !SDL_EVENTS_DISABLED
522	if ( SDL_ProcessEvents[event.type] == SDL_ENABLE ) {
523		event.jbutton.which = joystick->index;
524		event.jbutton.button = button;
525		event.jbutton.state = state;
526		if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) {
527			posted = 1;
528			SDL_PushEvent(&event);
529		}
530	}
531#endif /* !SDL_EVENTS_DISABLED */
532	return(posted);
533}
534
535void SDL_JoystickUpdate(void)
536{
537	int i;
538
539	for ( i=0; SDL_joysticks[i]; ++i ) {
540		SDL_SYS_JoystickUpdate(SDL_joysticks[i]);
541	}
542}
543
544int SDL_JoystickEventState(int state)
545{
546#if SDL_EVENTS_DISABLED
547	return SDL_IGNORE;
548#else
549	const Uint8 event_list[] = {
550		SDL_JOYAXISMOTION, SDL_JOYBALLMOTION, SDL_JOYHATMOTION,
551		SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP,
552	};
553	unsigned int i;
554
555	switch (state) {
556		case SDL_QUERY:
557			state = SDL_IGNORE;
558			for ( i=0; i<SDL_arraysize(event_list); ++i ) {
559				state = SDL_EventState(event_list[i],SDL_QUERY);
560				if ( state == SDL_ENABLE ) {
561					break;
562				}
563			}
564			break;
565		default:
566			for ( i=0; i<SDL_arraysize(event_list); ++i ) {
567				SDL_EventState(event_list[i], state);
568			}
569			break;
570	}
571	return(state);
572#endif /* SDL_EVENTS_DISABLED */
573}
574