multiple fixes to wxJoystick under Unix and new index-based API for accessing joystic...
[wxWidgets.git] / src / unix / joystick.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/joystick.cpp
3 // Purpose: wxJoystick class
4 // Author: Ported to Linux by Guilhem Lavaux
5 // Modified by:
6 // Created: 05/23/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Guilhem Lavaux
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // for compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #if wxUSE_JOYSTICK
16
17 #include "wx/joystick.h"
18
19 #ifndef WX_PRECOMP
20 #include "wx/event.h"
21 #include "wx/window.h"
22 #endif //WX_PRECOMP
23
24 #include <linux/joystick.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/time.h>
28 #include <sys/ioctl.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31
32 #ifdef HAVE_SYS_SELECT_H
33 # include <sys/select.h>
34 #endif
35
36 #include "wx/unix/private.h"
37
38 enum {
39 wxJS_AXIS_X = 0,
40 wxJS_AXIS_Y,
41 wxJS_AXIS_Z,
42 wxJS_AXIS_RUDDER,
43 wxJS_AXIS_U,
44 wxJS_AXIS_V,
45
46 wxJS_AXIS_MAX = 32767,
47 wxJS_AXIS_MIN = -32767,
48 wxJS_MAX_AXES = 15,
49 wxJS_MAX_BUTTONS = sizeof(int) * 8
50 };
51
52
53 IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject)
54
55
56 ////////////////////////////////////////////////////////////////////////////
57 // Background thread for reading the joystick device
58 ////////////////////////////////////////////////////////////////////////////
59
60 class wxJoystickThread : public wxThread
61 {
62 public:
63 wxJoystickThread(int device, int joystick);
64 void* Entry();
65
66 private:
67 void SendEvent(wxEventType type, long ts, int change = 0);
68 int m_device;
69 int m_joystick;
70 wxPoint m_lastposition;
71 int m_axe[wxJS_MAX_AXES];
72 int m_buttons;
73 wxWindow* m_catchwin;
74 int m_polling;
75 int m_threshold;
76
77 friend class wxJoystick;
78 };
79
80
81 wxJoystickThread::wxJoystickThread(int device, int joystick)
82 : m_device(device),
83 m_joystick(joystick),
84 m_lastposition(wxDefaultPosition),
85 m_buttons(0),
86 m_catchwin(NULL),
87 m_polling(0),
88 m_threshold(0)
89 {
90 memset(m_axe, 0, sizeof(m_axe));
91 }
92
93 void wxJoystickThread::SendEvent(wxEventType type, long ts, int change)
94 {
95 wxJoystickEvent jwx_event(type, m_buttons, m_joystick, change);
96
97 jwx_event.SetTimestamp(ts);
98 jwx_event.SetPosition(m_lastposition);
99 jwx_event.SetZPosition(m_axe[wxJS_AXIS_Z]);
100 jwx_event.SetEventObject(m_catchwin);
101
102 if (m_catchwin)
103 m_catchwin->AddPendingEvent(jwx_event);
104 }
105
106 void* wxJoystickThread::Entry()
107 {
108 struct js_event j_evt;
109 fd_set read_fds;
110 struct timeval time_out = {0, 0};
111
112 wxFD_ZERO(&read_fds);
113 while (true)
114 {
115 if (TestDestroy())
116 break;
117
118 // We use select when either polling or 'blocking' as even in the
119 // blocking case we need to check TestDestroy periodically
120 if (m_polling)
121 time_out.tv_usec = m_polling * 1000;
122 else
123 time_out.tv_usec = 10 * 1000; // check at least every 10 msec in blocking case
124
125 wxFD_SET(m_device, &read_fds);
126 select(m_device+1, &read_fds, NULL, NULL, &time_out);
127 if (wxFD_ISSET(m_device, &read_fds))
128 {
129 memset(&j_evt, 0, sizeof(j_evt));
130 read(m_device, &j_evt, sizeof(j_evt));
131
132 //printf("time: %d\t value: %d\t type: %d\t number: %d\n",
133 // j_evt.time, j_evt.value, j_evt.type, j_evt.number);
134
135 if ((j_evt.type & JS_EVENT_AXIS) && (j_evt.number < wxJS_MAX_AXES))
136 {
137 if ( (m_axe[j_evt.number] + m_threshold < j_evt.value)
138 || (m_axe[j_evt.number] - m_threshold > j_evt.value) )
139 {
140 m_axe[j_evt.number] = j_evt.value;
141
142 switch (j_evt.number)
143 {
144 case wxJS_AXIS_X:
145 m_lastposition.x = j_evt.value;
146 SendEvent(wxEVT_JOY_MOVE, j_evt.time);
147 break;
148 case wxJS_AXIS_Y:
149 m_lastposition.y = j_evt.value;
150 SendEvent(wxEVT_JOY_MOVE, j_evt.time);
151 break;
152 case wxJS_AXIS_Z:
153 SendEvent(wxEVT_JOY_ZMOVE, j_evt.time);
154 break;
155 default:
156 SendEvent(wxEVT_JOY_MOVE, j_evt.time);
157 // TODO: There should be a way to indicate that the event
158 // is for some other axes.
159 break;
160 }
161 }
162 }
163
164 if ( (j_evt.type & JS_EVENT_BUTTON) && (j_evt.number < wxJS_MAX_BUTTONS) )
165 {
166 if (j_evt.value)
167 {
168 m_buttons |= (1 << j_evt.number);
169 SendEvent(wxEVT_JOY_BUTTON_DOWN, j_evt.time, j_evt.number);
170 }
171 else
172 {
173 m_buttons &= ~(1 << j_evt.number);
174 SendEvent(wxEVT_JOY_BUTTON_UP, j_evt.time, j_evt.number);
175 }
176 }
177 }
178 }
179
180 close(m_device);
181 return NULL;
182 }
183
184
185 ////////////////////////////////////////////////////////////////////////////
186
187 wxJoystick::wxJoystick(int joystick)
188 : m_device(-1),
189 m_joystick(joystick),
190 m_thread(NULL)
191 {
192 wxString dev_name;
193
194 // old /dev structure
195 dev_name.Printf( wxT("/dev/js%d"), joystick);
196 m_device = open(dev_name.fn_str(), O_RDONLY);
197
198 // new /dev structure with "input" subdirectory
199 if (m_device == -1)
200 {
201 dev_name.Printf( wxT("/dev/input/js%d"), joystick);
202 m_device = open(dev_name.fn_str(), O_RDONLY);
203 }
204
205 if (m_device != -1)
206 {
207 m_thread = new wxJoystickThread(m_device, m_joystick);
208 m_thread->Create();
209 m_thread->Run();
210 }
211 }
212
213
214 wxJoystick::~wxJoystick()
215 {
216 ReleaseCapture();
217 if (m_thread)
218 m_thread->Delete(); // It's detached so it will delete itself
219 m_device = -1;
220 }
221
222
223 ////////////////////////////////////////////////////////////////////////////
224 // State
225 ////////////////////////////////////////////////////////////////////////////
226
227 wxPoint wxJoystick::GetPosition() const
228 {
229 wxPoint pos(wxDefaultPosition);
230 if (m_thread) pos = m_thread->m_lastposition;
231 return pos;
232 }
233
234 int wxJoystick::GetPosition(unsigned axis) const
235 {
236 if (m_thread && (axis < wxJS_MAX_AXES))
237 return m_thread->m_axe[axis];
238 return 0;
239 }
240
241 int wxJoystick::GetZPosition() const
242 {
243 if (m_thread)
244 return m_thread->m_axe[wxJS_AXIS_Z];
245 return 0;
246 }
247
248 int wxJoystick::GetButtonState() const
249 {
250 if (m_thread)
251 return m_thread->m_buttons;
252 return 0;
253 }
254
255 bool wxJoystick::GetButtonState(unsigned id) const
256 {
257 if (m_thread && (id < wxJS_MAX_BUTTONS))
258 return (m_thread->m_buttons & (1 << id)) != 0;
259 return false;
260 }
261
262 int wxJoystick::GetPOVPosition() const
263 {
264 return -1;
265 }
266
267 int wxJoystick::GetPOVCTSPosition() const
268 {
269 return -1;
270 }
271
272 int wxJoystick::GetRudderPosition() const
273 {
274 if (m_thread)
275 return m_thread->m_axe[wxJS_AXIS_RUDDER];
276 return 0;
277 }
278
279 int wxJoystick::GetUPosition() const
280 {
281 if (m_thread)
282 return m_thread->m_axe[wxJS_AXIS_U];
283 return 0;
284 }
285
286 int wxJoystick::GetVPosition() const
287 {
288 if (m_thread)
289 return m_thread->m_axe[wxJS_AXIS_V];
290 return 0;
291 }
292
293 int wxJoystick::GetMovementThreshold() const
294 {
295 if (m_thread)
296 return m_thread->m_threshold;
297 return 0;
298 }
299
300 void wxJoystick::SetMovementThreshold(int threshold)
301 {
302 if (m_thread)
303 m_thread->m_threshold = threshold;
304 }
305
306 ////////////////////////////////////////////////////////////////////////////
307 // Capabilities
308 ////////////////////////////////////////////////////////////////////////////
309
310 bool wxJoystick::IsOk() const
311 {
312 return (m_device != -1);
313 }
314
315 int wxJoystick::GetNumberJoysticks()
316 {
317 wxString dev_name;
318 int fd, j;
319
320 for (j=0; j<4; j++) {
321 dev_name.Printf(wxT("/dev/js%d"), j);
322 fd = open(dev_name.fn_str(), O_RDONLY);
323 if (fd == -1)
324 break;
325 close(fd);
326 }
327
328 if (j == 0) {
329 for (j=0; j<4; j++) {
330 dev_name.Printf(wxT("/dev/input/js%d"), j);
331 fd = open(dev_name.fn_str(), O_RDONLY);
332 if (fd == -1)
333 return j;
334 close(fd);
335 }
336 }
337
338 return j;
339 }
340
341 int wxJoystick::GetManufacturerId() const
342 {
343 return 0;
344 }
345
346 int wxJoystick::GetProductId() const
347 {
348 return 0;
349 }
350
351 wxString wxJoystick::GetProductName() const
352 {
353 char name[128];
354
355 if (ioctl(m_device, JSIOCGNAME(sizeof(name)), name) < 0)
356 strcpy(name, "Unknown");
357 return wxString(name, wxConvLibc);
358 }
359
360 int wxJoystick::GetXMin() const
361 {
362 return wxJS_AXIS_MIN;
363 }
364
365 int wxJoystick::GetYMin() const
366 {
367 return wxJS_AXIS_MIN;
368 }
369
370 int wxJoystick::GetZMin() const
371 {
372 return wxJS_AXIS_MIN;
373 }
374
375 int wxJoystick::GetXMax() const
376 {
377 return wxJS_AXIS_MAX;
378 }
379
380 int wxJoystick::GetYMax() const
381 {
382 return wxJS_AXIS_MAX;
383 }
384
385 int wxJoystick::GetZMax() const
386 {
387 return wxJS_AXIS_MAX;
388 }
389
390 int wxJoystick::GetNumberButtons() const
391 {
392 char nb=0;
393
394 if (m_device != -1)
395 ioctl(m_device, JSIOCGBUTTONS, &nb);
396
397 if (nb > wxJS_MAX_BUTTONS)
398 nb = wxJS_MAX_BUTTONS;
399
400 return nb;
401 }
402
403 int wxJoystick::GetNumberAxes() const
404 {
405 char nb=0;
406
407 if (m_device != -1)
408 ioctl(m_device, JSIOCGAXES, &nb);
409
410 if (nb > wxJS_MAX_AXES)
411 nb = wxJS_MAX_AXES;
412
413 return nb;
414 }
415
416 int wxJoystick::GetMaxButtons() const
417 {
418 return wxJS_MAX_BUTTONS; // internal
419 }
420
421 int wxJoystick::GetMaxAxes() const
422 {
423 return wxJS_MAX_AXES; // internal
424 }
425
426 int wxJoystick::GetPollingMin() const
427 {
428 return 10;
429 }
430
431 int wxJoystick::GetPollingMax() const
432 {
433 return 1000;
434 }
435
436 int wxJoystick::GetRudderMin() const
437 {
438 return wxJS_AXIS_MIN;
439 }
440
441 int wxJoystick::GetRudderMax() const
442 {
443 return wxJS_AXIS_MAX;
444 }
445
446 int wxJoystick::GetUMin() const
447 {
448 return wxJS_AXIS_MIN;
449 }
450
451 int wxJoystick::GetUMax() const
452 {
453 return wxJS_AXIS_MAX;
454 }
455
456 int wxJoystick::GetVMin() const
457 {
458 return wxJS_AXIS_MIN;
459 }
460
461 int wxJoystick::GetVMax() const
462 {
463 return wxJS_AXIS_MAX;
464 }
465
466 bool wxJoystick::HasRudder() const
467 {
468 return GetNumberAxes() >= wxJS_AXIS_RUDDER;
469 }
470
471 bool wxJoystick::HasZ() const
472 {
473 return GetNumberAxes() >= wxJS_AXIS_Z;
474 }
475
476 bool wxJoystick::HasU() const
477 {
478 return GetNumberAxes() >= wxJS_AXIS_U;
479 }
480
481 bool wxJoystick::HasV() const
482 {
483 return GetNumberAxes() >= wxJS_AXIS_V;
484 }
485
486 bool wxJoystick::HasPOV() const
487 {
488 return false;
489 }
490
491 bool wxJoystick::HasPOV4Dir() const
492 {
493 return false;
494 }
495
496 bool wxJoystick::HasPOVCTS() const
497 {
498 return false;
499 }
500
501 ////////////////////////////////////////////////////////////////////////////
502 // Operations
503 ////////////////////////////////////////////////////////////////////////////
504
505 bool wxJoystick::SetCapture(wxWindow* win, int pollingFreq)
506 {
507 if (m_thread)
508 {
509 m_thread->m_catchwin = win;
510 m_thread->m_polling = pollingFreq;
511 return true;
512 }
513 return false;
514 }
515
516 bool wxJoystick::ReleaseCapture()
517 {
518 if (m_thread)
519 {
520 m_thread->m_catchwin = NULL;
521 m_thread->m_polling = 0;
522 return true;
523 }
524 return false;
525 }
526 #endif // wxUSE_JOYSTICK