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