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