]> git.saurik.com Git - wxWidgets.git/blob - src/unix/joystick.cpp
Implement support for button mnemonics in wxOSX/Cocoa.
[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 "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 if ( (m_axe[j_evt.number] + m_threshold < j_evt.value)
140 || (m_axe[j_evt.number] - m_threshold > j_evt.value) )
141 {
142 m_axe[j_evt.number] = j_evt.value;
143
144 switch (j_evt.number)
145 {
146 case wxJS_AXIS_X:
147 m_lastposition.x = j_evt.value;
148 SendEvent(wxEVT_JOY_MOVE, j_evt.time);
149 break;
150 case wxJS_AXIS_Y:
151 m_lastposition.y = j_evt.value;
152 SendEvent(wxEVT_JOY_MOVE, j_evt.time);
153 break;
154 case wxJS_AXIS_Z:
155 SendEvent(wxEVT_JOY_ZMOVE, j_evt.time);
156 break;
157 default:
158 SendEvent(wxEVT_JOY_MOVE, j_evt.time);
159 // TODO: There should be a way to indicate that the event
160 // is for some other axes.
161 break;
162 }
163 }
164 }
165
166 if ( (j_evt.type & JS_EVENT_BUTTON) && (j_evt.number < wxJS_MAX_BUTTONS) )
167 {
168 if (j_evt.value)
169 {
170 m_buttons |= (1 << j_evt.number);
171 SendEvent(wxEVT_JOY_BUTTON_DOWN, j_evt.time, j_evt.number);
172 }
173 else
174 {
175 m_buttons &= ~(1 << j_evt.number);
176 SendEvent(wxEVT_JOY_BUTTON_UP, j_evt.time, j_evt.number);
177 }
178 }
179 }
180 }
181
182 close(m_device);
183 return NULL;
184 }
185
186
187 ////////////////////////////////////////////////////////////////////////////
188
189 wxJoystick::wxJoystick(int joystick)
190 : m_device(-1),
191 m_joystick(joystick),
192 m_thread(NULL)
193 {
194 wxString dev_name;
195
196 // old /dev structure
197 dev_name.Printf( wxT("/dev/js%d"), joystick);
198 m_device = open(dev_name.fn_str(), O_RDONLY);
199
200 // new /dev structure with "input" subdirectory
201 if (m_device == -1)
202 {
203 dev_name.Printf( wxT("/dev/input/js%d"), joystick);
204 m_device = open(dev_name.fn_str(), O_RDONLY);
205 }
206
207 if (m_device != -1)
208 {
209 m_thread = new wxJoystickThread(m_device, m_joystick);
210 m_thread->Create();
211 m_thread->Run();
212 }
213 }
214
215
216 wxJoystick::~wxJoystick()
217 {
218 ReleaseCapture();
219 if (m_thread)
220 m_thread->Delete(); // It's detached so it will delete itself
221 m_device = -1;
222 }
223
224
225 ////////////////////////////////////////////////////////////////////////////
226 // State
227 ////////////////////////////////////////////////////////////////////////////
228
229 wxPoint wxJoystick::GetPosition() const
230 {
231 wxPoint pos(wxDefaultPosition);
232 if (m_thread) pos = m_thread->m_lastposition;
233 return pos;
234 }
235
236 int wxJoystick::GetPosition(unsigned axis) const
237 {
238 if (m_thread && (axis < wxJS_MAX_AXES))
239 return m_thread->m_axe[axis];
240 return 0;
241 }
242
243 int wxJoystick::GetZPosition() const
244 {
245 if (m_thread)
246 return m_thread->m_axe[wxJS_AXIS_Z];
247 return 0;
248 }
249
250 int wxJoystick::GetButtonState() const
251 {
252 if (m_thread)
253 return m_thread->m_buttons;
254 return 0;
255 }
256
257 bool wxJoystick::GetButtonState(unsigned id) const
258 {
259 if (m_thread && (id < wxJS_MAX_BUTTONS))
260 return (m_thread->m_buttons & (1 << id)) != 0;
261 return false;
262 }
263
264 int wxJoystick::GetPOVPosition() const
265 {
266 return -1;
267 }
268
269 int wxJoystick::GetPOVCTSPosition() const
270 {
271 return -1;
272 }
273
274 int wxJoystick::GetRudderPosition() const
275 {
276 if (m_thread)
277 return m_thread->m_axe[wxJS_AXIS_RUDDER];
278 return 0;
279 }
280
281 int wxJoystick::GetUPosition() const
282 {
283 if (m_thread)
284 return m_thread->m_axe[wxJS_AXIS_U];
285 return 0;
286 }
287
288 int wxJoystick::GetVPosition() const
289 {
290 if (m_thread)
291 return m_thread->m_axe[wxJS_AXIS_V];
292 return 0;
293 }
294
295 int wxJoystick::GetMovementThreshold() const
296 {
297 if (m_thread)
298 return m_thread->m_threshold;
299 return 0;
300 }
301
302 void wxJoystick::SetMovementThreshold(int threshold)
303 {
304 if (m_thread)
305 m_thread->m_threshold = threshold;
306 }
307
308 ////////////////////////////////////////////////////////////////////////////
309 // Capabilities
310 ////////////////////////////////////////////////////////////////////////////
311
312 bool wxJoystick::IsOk() const
313 {
314 return (m_device != -1);
315 }
316
317 int wxJoystick::GetNumberJoysticks()
318 {
319 wxString dev_name;
320 int fd, j;
321
322 for (j=0; j<4; j++) {
323 dev_name.Printf(wxT("/dev/js%d"), j);
324 fd = open(dev_name.fn_str(), O_RDONLY);
325 if (fd == -1)
326 break;
327 close(fd);
328 }
329
330 if (j == 0) {
331 for (j=0; j<4; j++) {
332 dev_name.Printf(wxT("/dev/input/js%d"), j);
333 fd = open(dev_name.fn_str(), O_RDONLY);
334 if (fd == -1)
335 return j;
336 close(fd);
337 }
338 }
339
340 return j;
341 }
342
343 int wxJoystick::GetManufacturerId() const
344 {
345 return 0;
346 }
347
348 int wxJoystick::GetProductId() const
349 {
350 return 0;
351 }
352
353 wxString wxJoystick::GetProductName() const
354 {
355 char name[128];
356
357 if (ioctl(m_device, JSIOCGNAME(sizeof(name)), name) < 0)
358 strcpy(name, "Unknown");
359 return wxString(name, wxConvLibc);
360 }
361
362 int wxJoystick::GetXMin() const
363 {
364 return wxJS_AXIS_MIN;
365 }
366
367 int wxJoystick::GetYMin() const
368 {
369 return wxJS_AXIS_MIN;
370 }
371
372 int wxJoystick::GetZMin() const
373 {
374 return wxJS_AXIS_MIN;
375 }
376
377 int wxJoystick::GetXMax() const
378 {
379 return wxJS_AXIS_MAX;
380 }
381
382 int wxJoystick::GetYMax() const
383 {
384 return wxJS_AXIS_MAX;
385 }
386
387 int wxJoystick::GetZMax() const
388 {
389 return wxJS_AXIS_MAX;
390 }
391
392 int wxJoystick::GetNumberButtons() const
393 {
394 char nb=0;
395
396 if (m_device != -1)
397 ioctl(m_device, JSIOCGBUTTONS, &nb);
398
399 if ((int)nb > wxJS_MAX_BUTTONS)
400 nb = wxJS_MAX_BUTTONS;
401
402 return nb;
403 }
404
405 int wxJoystick::GetNumberAxes() const
406 {
407 char nb=0;
408
409 if (m_device != -1)
410 ioctl(m_device, JSIOCGAXES, &nb);
411
412 if ((int)nb > wxJS_MAX_AXES)
413 nb = wxJS_MAX_AXES;
414
415 return nb;
416 }
417
418 int wxJoystick::GetMaxButtons() const
419 {
420 return wxJS_MAX_BUTTONS; // internal
421 }
422
423 int wxJoystick::GetMaxAxes() const
424 {
425 return wxJS_MAX_AXES; // internal
426 }
427
428 int wxJoystick::GetPollingMin() const
429 {
430 return 10;
431 }
432
433 int wxJoystick::GetPollingMax() const
434 {
435 return 1000;
436 }
437
438 int wxJoystick::GetRudderMin() const
439 {
440 return wxJS_AXIS_MIN;
441 }
442
443 int wxJoystick::GetRudderMax() const
444 {
445 return wxJS_AXIS_MAX;
446 }
447
448 int wxJoystick::GetUMin() const
449 {
450 return wxJS_AXIS_MIN;
451 }
452
453 int wxJoystick::GetUMax() const
454 {
455 return wxJS_AXIS_MAX;
456 }
457
458 int wxJoystick::GetVMin() const
459 {
460 return wxJS_AXIS_MIN;
461 }
462
463 int wxJoystick::GetVMax() const
464 {
465 return wxJS_AXIS_MAX;
466 }
467
468 bool wxJoystick::HasRudder() const
469 {
470 return GetNumberAxes() >= wxJS_AXIS_RUDDER;
471 }
472
473 bool wxJoystick::HasZ() const
474 {
475 return GetNumberAxes() >= wxJS_AXIS_Z;
476 }
477
478 bool wxJoystick::HasU() const
479 {
480 return GetNumberAxes() >= wxJS_AXIS_U;
481 }
482
483 bool wxJoystick::HasV() const
484 {
485 return GetNumberAxes() >= wxJS_AXIS_V;
486 }
487
488 bool wxJoystick::HasPOV() const
489 {
490 return false;
491 }
492
493 bool wxJoystick::HasPOV4Dir() const
494 {
495 return false;
496 }
497
498 bool wxJoystick::HasPOVCTS() const
499 {
500 return false;
501 }
502
503 ////////////////////////////////////////////////////////////////////////////
504 // Operations
505 ////////////////////////////////////////////////////////////////////////////
506
507 bool wxJoystick::SetCapture(wxWindow* win, int pollingFreq)
508 {
509 if (m_thread)
510 {
511 m_thread->m_catchwin = win;
512 m_thread->m_polling = pollingFreq;
513 return true;
514 }
515 return false;
516 }
517
518 bool wxJoystick::ReleaseCapture()
519 {
520 if (m_thread)
521 {
522 m_thread->m_catchwin = NULL;
523 m_thread->m_polling = 0;
524 return true;
525 }
526 return false;
527 }
528 #endif // wxUSE_JOYSTICK