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