1/*
2 * fg_joystick_x11.c
3 *
4 * Joystick handling code
5 *
6 * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7 * Written by Steve Baker, <sjbaker1@airmail.net>
8 * Copied for Platform code by Evan Felix <karcaw at gmail.com>
9 * Creation date: Thur Feb 2 2012
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
25 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 */
28
29/*
30 * FreeBSD port by Stephen Montgomery-Smith <stephen@math.missouri.edu>
31 *
32 * Redone by John Fay 2/4/04 with another look from the PLIB "js" library.
33 * Many thanks for Steve Baker for permission to pull from that library.
34 */
35
36#include <GL/freeglut.h>
37#include "../fg_internal.h"
38#ifdef HAVE_SYS_PARAM_H
39# include <sys/param.h>
40#endif
41
42#include <fcntl.h>
43
44
45/*this should be defined in a header file */
46#define MAX_NUM_JOYSTICKS 2
47extern SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
48
49void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
50{
51 int status;
52
53#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
54 int len;
55
56 if ( joy->pJoystick.os->is_analog )
57 {
58 int status = read ( joy->pJoystick.os->fd, &joy->pJoystick.os->ajs, sizeof(joy->pJoystick.os->ajs) );
59 if ( status != sizeof(joy->pJoystick.os->ajs) ) {
60 perror ( joy->pJoystick.os->fname );
61 joy->error = GL_TRUE;
62 return;
63 }
64 if ( buttons != NULL )
65 *buttons = ( joy->pJoystick.os->ajs.b1 ? 1 : 0 ) | ( joy->pJoystick.os->ajs.b2 ? 2 : 0 );
66
67 if ( axes != NULL )
68 {
69 axes[0] = (float) joy->pJoystick.os->ajs.x;
70 axes[1] = (float) joy->pJoystick.os->ajs.y;
71 }
72
73 return;
74 }
75
76# ifdef HAVE_USB_JS
77 while ( ( len = read ( joy->pJoystick.os->fd, joy->pJoystick.os->hid_data_buf, joy->pJoystick.os->hid_dlen ) ) == joy->pJoystick.os->hid_dlen )
78 {
79 struct hid_item *h;
80
81 for ( h = joy->pJoystick.os->hids; h; h = h->next )
82 {
83 int d = hid_get_data ( joy->pJoystick.os->hid_data_buf, h );
84
85 int page = HID_PAGE ( h->usage );
86 int usage = HID_USAGE ( h->usage );
87
88 if ( page == HUP_GENERIC_DESKTOP )
89 {
90 int i;
91 for ( i = 0; i < joy->num_axes; i++ )
92 if (joy->pJoystick.os->axes_usage[i] == usage)
93 {
94 if (usage == HUG_HAT_SWITCH)
95 {
96 if (d < 0 || d > 8)
97 d = 0; /* safety */
98 joy->pJoystick.os->cache_axes[i] = (float)hatmap_x[d];
99 joy->pJoystick.os->cache_axes[i + 1] = (float)hatmap_y[d];
100 }
101 else
102 {
103 joy->pJoystick.os->cache_axes[i] = (float)d;
104 }
105 break;
106 }
107 }
108 else if (page == HUP_BUTTON)
109 {
110 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
111 {
112 if (d)
113 joy->pJoystick.os->cache_buttons |= (1 << ( usage - 1 ));
114 else
115 joy->pJoystick.os->cache_buttons &= ~(1 << ( usage - 1 ));
116 }
117 }
118 }
119 }
120 if ( len < 0 && errno != EAGAIN )
121 {
122 perror( joy->pJoystick.os->fname );
123 joy->error = 1;
124 }
125 if ( buttons != NULL ) *buttons = joy->pJoystick.os->cache_buttons;
126 if ( axes != NULL )
127 memcpy ( axes, joy->pJoystick.os->cache_axes, sizeof(float) * joy->num_axes );
128# endif
129#endif
130
131#ifdef JS_NEW
132
133 while ( 1 )
134 {
135 status = read ( joy->pJoystick.fd, &joy->pJoystick.js, sizeof(struct js_event) );
136
137 if ( status != sizeof( struct js_event ) )
138 {
139 if ( errno == EAGAIN )
140 {
141 /* Use the old values */
142 if ( buttons )
143 *buttons = joy->pJoystick.tmp_buttons;
144 if ( axes )
145 memcpy( axes, joy->pJoystick.tmp_axes,
146 sizeof( float ) * joy->num_axes );
147 return;
148 }
149
150 fgWarning ( "%s", joy->pJoystick.fname );
151 joy->error = GL_TRUE;
152 return;
153 }
154
155 switch ( joy->pJoystick.js.type & ~JS_EVENT_INIT )
156 {
157 case JS_EVENT_BUTTON:
158 if( joy->pJoystick.js.value == 0 ) /* clear the flag */
159 joy->pJoystick.tmp_buttons &= ~( 1 << joy->pJoystick.js.number );
160 else
161 joy->pJoystick.tmp_buttons |= ( 1 << joy->pJoystick.js.number );
162 break;
163
164 case JS_EVENT_AXIS:
165 if ( joy->pJoystick.js.number < joy->num_axes )
166 {
167 joy->pJoystick.tmp_axes[ joy->pJoystick.js.number ] = ( float )joy->pJoystick.js.value;
168
169 if( axes )
170 memcpy( axes, joy->pJoystick.tmp_axes, sizeof(float) * joy->num_axes );
171 }
172 break;
173
174 default:
175 fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
176
177 /* use the old values */
178
179 if ( buttons != NULL ) *buttons = joy->pJoystick.tmp_buttons;
180 if ( axes != NULL )
181 memcpy ( axes, joy->pJoystick.tmp_axes, sizeof(float) * joy->num_axes );
182
183 return;
184 }
185
186 if( buttons )
187 *buttons = joy->pJoystick.tmp_buttons;
188 }
189#else
190
191 status = read( joy->pJoystick.fd, &joy->pJoystick.js, JS_RETURN );
192
193 if ( status != JS_RETURN )
194 {
195 fgWarning( "%s", joy->pJoystick.fname );
196 joy->error = GL_TRUE;
197 return;
198 }
199
200 if ( buttons )
201# if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
202 *buttons = ( joy->pJoystick.js.b1 ? 1 : 0 ) | ( joy->pJoystick.js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
203# else
204 *buttons = joy->pJoystick.js.buttons;
205# endif
206
207 if ( axes )
208 {
209 axes[ 0 ] = (float) joy->pJoystick.js.x;
210 axes[ 1 ] = (float) joy->pJoystick.js.y;
211 }
212#endif
213}
214
215
216void fgPlatformJoystickOpen( SFG_Joystick* joy )
217{
218#if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
219 int i = 0;
220 char *cp;
221#endif
222#ifdef JS_NEW
223 unsigned char u;
224 int i=0;
225#else
226# if defined( __linux__ ) || TARGET_HOST_SOLARIS
227 int i = 0;
228 int counter = 0;
229# endif
230#endif
231
232#if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
233 for( i = 0; i < _JS_MAX_AXES; i++ )
234 joy->pJoystick.os->cache_axes[ i ] = 0.0f;
235
236 joy->pJoystick.os->cache_buttons = 0;
237
238 joy->pJoystick.os->fd = open( joy->pJoystick.os->fname, O_RDONLY | O_NONBLOCK);
239
240 if( joy->pJoystick.os->fd < 0 && errno == EACCES )
241 fgWarning ( "%s exists but is not readable by you", joy->pJoystick.os->fname );
242
243 joy->error =( joy->pJoystick.os->fd < 0 );
244
245 if( joy->error )
246 return;
247
248 joy->num_axes = 0;
249 joy->num_buttons = 0;
250 if( joy->pJoystick.os->is_analog )
251 {
252 FILE *joyfile;
253 char joyfname[ 1024 ];
254 int noargs, in_no_axes;
255
256 float axes [ _JS_MAX_AXES ];
257 int buttons[ _JS_MAX_AXES ];
258
259 joy->num_axes = 2;
260 joy->num_buttons = 32;
261
262 fghJoystickRawRead( joy, buttons, axes );
263 joy->error = axes[ 0 ] < -1000000000.0f;
264 if( joy->error )
265 return;
266
267 snprintf( joyfname, sizeof(joyfname), "%s/.joy%drc", getenv( "HOME" ), joy->id );
268
269 joyfile = fopen( joyfname, "r" );
270 joy->error =( joyfile == NULL );
271 if( joy->error )
272 return;
273
274 noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
275 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
276 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
277 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
278 fclose( joyfile );
279 if( joy->error )
280 return;
281
282 for( i = 0; i < _JS_MAX_AXES; i++ )
283 {
284 joy->dead_band[ i ] = 0.0f;
285 joy->saturate [ i ] = 1.0f;
286 }
287
288 return; /* End of analog code */
289 }
290
291# ifdef HAVE_USB_JS
292 if( ! fghJoystickInitializeHID( joy->pJoystick.os, &joy->num_axes,
293 &joy->num_buttons ) )
294 {
295 close( joy->pJoystick.os->fd );
296 joy->error = GL_TRUE;
297 return;
298 }
299
300 cp = strrchr( joy->pJoystick.os->fname, '/' );
301 if( cp )
302 {
303 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
304 0 )
305 strcpy( joy->name, &cp[1] );
306 }
307
308 if( joy->num_axes > _JS_MAX_AXES )
309 joy->num_axes = _JS_MAX_AXES;
310
311 for( i = 0; i < _JS_MAX_AXES; i++ )
312 {
313 /* We really should get this from the HID, but that data seems
314 * to be quite unreliable for analog-to-USB converters. Punt for
315 * now.
316 */
317 if( joy->pJoystick.os->axes_usage[ i ] == HUG_HAT_SWITCH )
318 {
319 joy->max [ i ] = 1.0f;
320 joy->center[ i ] = 0.0f;
321 joy->min [ i ] = -1.0f;
322 }
323 else
324 {
325 joy->max [ i ] = 255.0f;
326 joy->center[ i ] = 127.0f;
327 joy->min [ i ] = 0.0f;
328 }
329
330 joy->dead_band[ i ] = 0.0f;
331 joy->saturate[ i ] = 1.0f;
332 }
333# endif
334#endif
335
336#if defined( __linux__ ) || TARGET_HOST_SOLARIS
337 /* Default for older Linux systems. */
338 joy->num_axes = 2;
339 joy->num_buttons = 32;
340
341# ifdef JS_NEW
342 for( i = 0; i < _JS_MAX_AXES; i++ )
343 joy->pJoystick.tmp_axes[ i ] = 0.0f;
344
345 joy->pJoystick.tmp_buttons = 0;
346# endif
347
348 joy->pJoystick.fd = open( joy->pJoystick.fname, O_RDONLY );
349
350 joy->error =( joy->pJoystick.fd < 0 );
351
352 if( joy->error )
353 return;
354
355 /* Set the correct number of axes for the linux driver */
356# ifdef JS_NEW
357 /* Melchior Franz's fixes for big-endian Linuxes since writing
358 * to the upper byte of an uninitialized word doesn't work.
359 * 9 April 2003
360 */
361 ioctl( joy->pJoystick.fd, JSIOCGAXES, &u );
362 joy->num_axes = u;
363 ioctl( joy->pJoystick.fd, JSIOCGBUTTONS, &u );
364 joy->num_buttons = u;
365 ioctl( joy->pJoystick.fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
366 fcntl( joy->pJoystick.fd, F_SETFL, O_NONBLOCK );
367# endif
368
369 /*
370 * The Linux driver seems to return 512 for all axes
371 * when no stick is present - but there is a chance
372 * that could happen by accident - so it's gotta happen
373 * on both axes for at least 100 attempts.
374 *
375 * PWO: shouldn't be that done somehow wiser on the kernel level?
376 */
377# ifndef JS_NEW
378 counter = 0;
379
380 do
381 {
382 fghJoystickRawRead( joy, NULL, joy->center );
383 counter++;
384 } while( !joy->error &&
385 counter < 100 &&
386 joy->center[ 0 ] == 512.0f &&
387 joy->center[ 1 ] == 512.0f );
388
389 if ( counter >= 100 )
390 joy->error = GL_TRUE;
391# endif
392
393 for( i = 0; i < _JS_MAX_AXES; i++ )
394 {
395# ifdef JS_NEW
396 joy->max [ i ] = 32767.0f;
397 joy->center[ i ] = 0.0f;
398 joy->min [ i ] = -32767.0f;
399# else
400 joy->max[ i ] = joy->center[ i ] * 2.0f;
401 joy->min[ i ] = 0.0f;
402# endif
403 joy->dead_band[ i ] = 0.0f;
404 joy->saturate [ i ] = 1.0f;
405 }
406#endif
407}
408
409
410void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )
411{
412#if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
413 fgJoystick[ ident ]->id = ident;
414 fgJoystick[ ident ]->error = GL_FALSE;
415
416 fgJoystick[ ident ]->pJoystick.os = calloc( 1, sizeof( struct os_specific_s ) );
417 memset( fgJoystick[ ident ]->pJoystick.os, 0, sizeof( struct os_specific_s ) );
418 if( ident < USB_IDENT_OFFSET )
419 fgJoystick[ ident ]->pJoystick.os->is_analog = 1;
420 if( fgJoystick[ ident ]->pJoystick.os->is_analog )
421 snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d", AJSDEV, ident );
422 else
423 snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d", UHIDDEV,
424 ident - USB_IDENT_OFFSET );
425#elif defined( __linux__ )
426 fgJoystick[ ident ]->id = ident;
427 fgJoystick[ ident ]->error = GL_FALSE;
428
429 snprintf( fgJoystick[ident]->pJoystick.fname, sizeof(fgJoystick[ident]->pJoystick.fname), "/dev/input/js%d", ident );
430
431 if( access( fgJoystick[ ident ]->pJoystick.fname, F_OK ) != 0 )
432 snprintf( fgJoystick[ ident ]->pJoystick.fname, sizeof(fgJoystick[ ident ]->pJoystick.fname), "/dev/js%d", ident );
433#endif
434}
435
436
437void fgPlatformJoystickClose ( int ident )
438{
439#if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
440 if( fgJoystick[ident]->pJoystick.os )
441 {
442 if( ! fgJoystick[ ident ]->error )
443 close( fgJoystick[ ident ]->pJoystick.os->fd );
444#ifdef HAVE_USB_JS
445 if( fgJoystick[ ident ]->pJoystick.os->hids )
446 free (fgJoystick[ ident ]->pJoystick.os->hids);
447 if( fgJoystick[ ident ]->pJoystick.os->hid_data_buf )
448 free( fgJoystick[ ident ]->pJoystick.os->hid_data_buf );
449#endif
450 free( fgJoystick[ident]->pJoystick.os );
451 }
452#endif
453
454 if( ! fgJoystick[ident]->error )
455 close( fgJoystick[ ident ]->pJoystick.fd );
456}
457
458