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@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;
40int SDL_allocatedjoysticks = 0;
41SDL_Joystick **SDL_joysticks = 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		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 */
69int SDL_NumJoysticks(void)
70{
71	return SDL_numjoysticks;
72}
73
74/*
75 * Get the implementation dependent name of a joystick
76 */
77const 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 */
94SDL_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 */
184int 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
198static 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 */
214int 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 */
225int 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 */
236int 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 */
247int 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 */
258int 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 */
269Sint16 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 */
288Uint8 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 */
307int 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 */
335Uint8 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 */
354void 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
400void 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
432int 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
462int 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
492int 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
525int 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
568void 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
577int 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