]> git.saurik.com Git - wxWidgets.git/blame - src/unix/joystick.cpp
Fix horizontal mouse wheel scrolling in wxGTK.
[wxWidgets.git] / src / unix / joystick.cpp
CommitLineData
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
101ceb40 7// Copyright: (c) Guilhem Lavaux
627b2b2a 8// Licence: wxWindows licence
101ceb40
JS
9/////////////////////////////////////////////////////////////////////////////
10
14f355c2
VS
11// for compilers that support precompilation, includes "wx.h".
12#include "wx/wxprec.h"
13
bb462424 14#if wxUSE_JOYSTICK
101ceb40 15
5260937e
VZ
16#include "wx/joystick.h"
17
d5da0ce7
WS
18#ifndef WX_PRECOMP
19 #include "wx/event.h"
cdccdfab 20 #include "wx/window.h"
cc7d80fe 21 #include "wx/log.h"
d5da0ce7
WS
22#endif //WX_PRECOMP
23
204abcd4
PC
24#include "wx/thread.h"
25
101ceb40
JS
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>
2b510feb 33
bc023abb
MW
34#ifdef HAVE_SYS_SELECT_H
35# include <sys/select.h>
36#endif
37
17a1ebd1 38#include "wx/unix/private.h"
101ceb40 39
a800dc50
RD
40enum {
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,
627b2b2a 47
a800dc50 48 wxJS_AXIS_MAX = 32767,
e6733873
VZ
49 wxJS_AXIS_MIN = -32767,
50 wxJS_MAX_AXES = 15,
51 wxJS_MAX_BUTTONS = sizeof(int) * 8
a800dc50 52};
101ceb40 53
101ceb40 54
a800dc50 55IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject)
101ceb40 56
101ceb40
JS
57
58////////////////////////////////////////////////////////////////////////////
a800dc50 59// Background thread for reading the joystick device
101ceb40 60////////////////////////////////////////////////////////////////////////////
101ceb40 61
a800dc50
RD
62class wxJoystickThread : public wxThread
63{
64public:
65 wxJoystickThread(int device, int joystick);
66 void* Entry();
67
68private:
e6733873 69 void SendEvent(wxEventType type, long ts, int change = 0);
a800dc50
RD
70 int m_device;
71 int m_joystick;
72 wxPoint m_lastposition;
e6733873 73 int m_axe[wxJS_MAX_AXES];
627b2b2a 74 int m_buttons;
a800dc50 75 wxWindow* m_catchwin;
627b2b2a 76 int m_polling;
e6733873 77 int m_threshold;
a800dc50
RD
78
79 friend class wxJoystick;
80};
81
82
83wxJoystickThread::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),
e6733873
VZ
89 m_polling(0),
90 m_threshold(0)
627b2b2a 91{
e6733873 92 memset(m_axe, 0, sizeof(m_axe));
a800dc50
RD
93}
94
e6733873
VZ
95void 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)
004867db 105 m_catchwin->GetEventHandler()->AddPendingEvent(jwx_event);
e6733873 106}
a800dc50
RD
107
108void* wxJoystickThread::Entry()
109{
110 struct js_event j_evt;
111 fd_set read_fds;
112 struct timeval time_out = {0, 0};
113
17a1ebd1 114 wxFD_ZERO(&read_fds);
627b2b2a
VZ
115 while (true)
116 {
a800dc50
RD
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
627b2b2a 126
17a1ebd1 127 wxFD_SET(m_device, &read_fds);
a800dc50 128 select(m_device+1, &read_fds, NULL, NULL, &time_out);
17a1ebd1 129 if (wxFD_ISSET(m_device, &read_fds))
a800dc50
RD
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
e6733873
VZ
137 if ((j_evt.type & JS_EVENT_AXIS) && (j_evt.number < wxJS_MAX_AXES))
138 {
f9209965
VZ
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
e6733873
VZ
147 if ( (m_axe[j_evt.number] + m_threshold < j_evt.value)
148 || (m_axe[j_evt.number] - m_threshold > j_evt.value) )
627b2b2a
VZ
149 {
150 m_axe[j_evt.number] = j_evt.value;
a800dc50 151
627b2b2a
VZ
152 switch (j_evt.number)
153 {
a800dc50
RD
154 case wxJS_AXIS_X:
155 m_lastposition.x = j_evt.value;
e6733873 156 SendEvent(wxEVT_JOY_MOVE, j_evt.time);
a800dc50
RD
157 break;
158 case wxJS_AXIS_Y:
159 m_lastposition.y = j_evt.value;
e6733873 160 SendEvent(wxEVT_JOY_MOVE, j_evt.time);
a800dc50
RD
161 break;
162 case wxJS_AXIS_Z:
e6733873 163 SendEvent(wxEVT_JOY_ZMOVE, j_evt.time);
a800dc50
RD
164 break;
165 default:
e6733873 166 SendEvent(wxEVT_JOY_MOVE, j_evt.time);
a800dc50
RD
167 // TODO: There should be a way to indicate that the event
168 // is for some other axes.
169 break;
a800dc50 170 }
627b2b2a 171 }
e6733873 172 }
627b2b2a 173
e6733873 174 if ( (j_evt.type & JS_EVENT_BUTTON) && (j_evt.number < wxJS_MAX_BUTTONS) )
627b2b2a
VZ
175 {
176 if (j_evt.value)
177 {
178 m_buttons |= (1 << j_evt.number);
e6733873 179 SendEvent(wxEVT_JOY_BUTTON_DOWN, j_evt.time, j_evt.number);
627b2b2a
VZ
180 }
181 else
182 {
183 m_buttons &= ~(1 << j_evt.number);
e6733873 184 SendEvent(wxEVT_JOY_BUTTON_UP, j_evt.time, j_evt.number);
a800dc50 185 }
a800dc50 186 }
a800dc50 187 }
101ceb40 188 }
627b2b2a 189
a800dc50
RD
190 close(m_device);
191 return NULL;
192}
193
194
195////////////////////////////////////////////////////////////////////////////
196
197wxJoystick::wxJoystick(int joystick)
198 : m_device(-1),
199 m_joystick(joystick),
200 m_thread(NULL)
201{
202 wxString dev_name;
627b2b2a 203
dcd5c3d0 204 // old /dev structure
f50de150 205 dev_name.Printf( wxT("/dev/js%d"), joystick);
a800dc50
RD
206 m_device = open(dev_name.fn_str(), O_RDONLY);
207
dcd5c3d0
JS
208 // new /dev structure with "input" subdirectory
209 if (m_device == -1)
210 {
f50de150 211 dev_name.Printf( wxT("/dev/input/js%d"), joystick);
7520f3da 212 m_device = open(dev_name.fn_str(), O_RDONLY);
dcd5c3d0
JS
213 }
214
a800dc50
RD
215 if (m_device != -1)
216 {
217 m_thread = new wxJoystickThread(m_device, m_joystick);
218 m_thread->Create();
219 m_thread->Run();
101ceb40 220 }
101ceb40
JS
221}
222
a800dc50
RD
223
224wxJoystick::~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
101ceb40
JS
233////////////////////////////////////////////////////////////////////////////
234// State
235////////////////////////////////////////////////////////////////////////////
236
a800dc50 237wxPoint wxJoystick::GetPosition() const
101ceb40 238{
a800dc50
RD
239 wxPoint pos(wxDefaultPosition);
240 if (m_thread) pos = m_thread->m_lastposition;
241 return pos;
101ceb40
JS
242}
243
e6733873
VZ
244int 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
a800dc50 251int wxJoystick::GetZPosition() const
101ceb40 252{
627b2b2a 253 if (m_thread)
a800dc50
RD
254 return m_thread->m_axe[wxJS_AXIS_Z];
255 return 0;
101ceb40
JS
256}
257
a800dc50 258int wxJoystick::GetButtonState() const
101ceb40 259{
627b2b2a 260 if (m_thread)
a800dc50
RD
261 return m_thread->m_buttons;
262 return 0;
101ceb40
JS
263}
264
e6733873
VZ
265bool 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
a800dc50 272int wxJoystick::GetPOVPosition() const
101ceb40 273{
a800dc50 274 return -1;
101ceb40
JS
275}
276
a800dc50 277int wxJoystick::GetPOVCTSPosition() const
101ceb40 278{
a800dc50 279 return -1;
101ceb40
JS
280}
281
a800dc50 282int wxJoystick::GetRudderPosition() const
101ceb40 283{
627b2b2a 284 if (m_thread)
a800dc50
RD
285 return m_thread->m_axe[wxJS_AXIS_RUDDER];
286 return 0;
101ceb40
JS
287}
288
a800dc50 289int wxJoystick::GetUPosition() const
101ceb40 290{
627b2b2a 291 if (m_thread)
a800dc50
RD
292 return m_thread->m_axe[wxJS_AXIS_U];
293 return 0;
101ceb40
JS
294}
295
a800dc50 296int wxJoystick::GetVPosition() const
101ceb40 297{
627b2b2a 298 if (m_thread)
a800dc50
RD
299 return m_thread->m_axe[wxJS_AXIS_V];
300 return 0;
101ceb40
JS
301}
302
a800dc50 303int wxJoystick::GetMovementThreshold() const
101ceb40 304{
e6733873
VZ
305 if (m_thread)
306 return m_thread->m_threshold;
a800dc50 307 return 0;
101ceb40
JS
308}
309
310void wxJoystick::SetMovementThreshold(int threshold)
311{
e6733873
VZ
312 if (m_thread)
313 m_thread->m_threshold = threshold;
101ceb40
JS
314}
315
316////////////////////////////////////////////////////////////////////////////
317// Capabilities
318////////////////////////////////////////////////////////////////////////////
319
a800dc50 320bool wxJoystick::IsOk() const
101ceb40 321{
a800dc50 322 return (m_device != -1);
101ceb40
JS
323}
324
da9e9563 325int wxJoystick::GetNumberJoysticks()
101ceb40 326{
a800dc50
RD
327 wxString dev_name;
328 int fd, j;
101ceb40 329
a800dc50
RD
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)
f50de150 334 break;
a800dc50
RD
335 close(fd);
336 }
7520f3da 337
f50de150
JS
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 }
7520f3da 347
a800dc50 348 return j;
101ceb40
JS
349}
350
a800dc50 351int wxJoystick::GetManufacturerId() const
101ceb40 352{
a800dc50 353 return 0;
101ceb40
JS
354}
355
a800dc50 356int wxJoystick::GetProductId() const
101ceb40 357{
a800dc50 358 return 0;
101ceb40
JS
359}
360
a800dc50 361wxString wxJoystick::GetProductName() const
101ceb40 362{
a800dc50 363 char name[128];
627b2b2a 364
a800dc50
RD
365 if (ioctl(m_device, JSIOCGNAME(sizeof(name)), name) < 0)
366 strcpy(name, "Unknown");
627b2b2a 367 return wxString(name, wxConvLibc);
101ceb40
JS
368}
369
a800dc50 370int wxJoystick::GetXMin() const
101ceb40 371{
a800dc50 372 return wxJS_AXIS_MIN;
101ceb40
JS
373}
374
a800dc50 375int wxJoystick::GetYMin() const
101ceb40 376{
a800dc50 377 return wxJS_AXIS_MIN;
101ceb40
JS
378}
379
a800dc50 380int wxJoystick::GetZMin() const
101ceb40 381{
a800dc50 382 return wxJS_AXIS_MIN;
101ceb40
JS
383}
384
a800dc50 385int wxJoystick::GetXMax() const
101ceb40 386{
a800dc50 387 return wxJS_AXIS_MAX;
101ceb40
JS
388}
389
a800dc50 390int wxJoystick::GetYMax() const
101ceb40 391{
a800dc50 392 return wxJS_AXIS_MAX;
101ceb40
JS
393}
394
a800dc50 395int wxJoystick::GetZMax() const
101ceb40 396{
a800dc50 397 return wxJS_AXIS_MAX;
101ceb40
JS
398}
399
a800dc50 400int wxJoystick::GetNumberButtons() const
101ceb40 401{
a800dc50 402 char nb=0;
101ceb40 403
a800dc50
RD
404 if (m_device != -1)
405 ioctl(m_device, JSIOCGBUTTONS, &nb);
101ceb40 406
c9f78968 407 if ((int)nb > wxJS_MAX_BUTTONS)
e6733873
VZ
408 nb = wxJS_MAX_BUTTONS;
409
a800dc50 410 return nb;
101ceb40
JS
411}
412
a800dc50 413int wxJoystick::GetNumberAxes() const
101ceb40 414{
a800dc50 415 char nb=0;
101ceb40 416
a800dc50
RD
417 if (m_device != -1)
418 ioctl(m_device, JSIOCGAXES, &nb);
101ceb40 419
c9f78968 420 if ((int)nb > wxJS_MAX_AXES)
e6733873
VZ
421 nb = wxJS_MAX_AXES;
422
a800dc50 423 return nb;
101ceb40
JS
424}
425
a800dc50 426int wxJoystick::GetMaxButtons() const
101ceb40 427{
e6733873 428 return wxJS_MAX_BUTTONS; // internal
101ceb40
JS
429}
430
a800dc50 431int wxJoystick::GetMaxAxes() const
101ceb40 432{
e6733873 433 return wxJS_MAX_AXES; // internal
101ceb40
JS
434}
435
a800dc50 436int wxJoystick::GetPollingMin() const
101ceb40 437{
a800dc50 438 return 10;
101ceb40
JS
439}
440
a800dc50 441int wxJoystick::GetPollingMax() const
101ceb40 442{
a800dc50 443 return 1000;
101ceb40
JS
444}
445
a800dc50 446int wxJoystick::GetRudderMin() const
101ceb40 447{
a800dc50 448 return wxJS_AXIS_MIN;
101ceb40
JS
449}
450
a800dc50 451int wxJoystick::GetRudderMax() const
101ceb40 452{
a800dc50 453 return wxJS_AXIS_MAX;
101ceb40
JS
454}
455
a800dc50 456int wxJoystick::GetUMin() const
101ceb40 457{
a800dc50 458 return wxJS_AXIS_MIN;
101ceb40
JS
459}
460
a800dc50 461int wxJoystick::GetUMax() const
101ceb40 462{
a800dc50 463 return wxJS_AXIS_MAX;
101ceb40
JS
464}
465
a800dc50 466int wxJoystick::GetVMin() const
101ceb40 467{
a800dc50 468 return wxJS_AXIS_MIN;
101ceb40
JS
469}
470
a800dc50 471int wxJoystick::GetVMax() const
101ceb40 472{
a800dc50 473 return wxJS_AXIS_MAX;
101ceb40
JS
474}
475
a800dc50 476bool wxJoystick::HasRudder() const
101ceb40 477{
a800dc50 478 return GetNumberAxes() >= wxJS_AXIS_RUDDER;
101ceb40
JS
479}
480
a800dc50 481bool wxJoystick::HasZ() const
101ceb40 482{
a800dc50 483 return GetNumberAxes() >= wxJS_AXIS_Z;
101ceb40
JS
484}
485
a800dc50 486bool wxJoystick::HasU() const
101ceb40 487{
a800dc50 488 return GetNumberAxes() >= wxJS_AXIS_U;
101ceb40
JS
489}
490
a800dc50 491bool wxJoystick::HasV() const
101ceb40 492{
a800dc50 493 return GetNumberAxes() >= wxJS_AXIS_V;
101ceb40
JS
494}
495
a800dc50 496bool wxJoystick::HasPOV() const
101ceb40 497{
a800dc50 498 return false;
101ceb40
JS
499}
500
a800dc50 501bool wxJoystick::HasPOV4Dir() const
101ceb40 502{
a800dc50 503 return false;
101ceb40
JS
504}
505
a800dc50 506bool wxJoystick::HasPOVCTS() const
101ceb40 507{
a800dc50 508 return false;
101ceb40
JS
509}
510
511////////////////////////////////////////////////////////////////////////////
512// Operations
513////////////////////////////////////////////////////////////////////////////
514
be5fced5 515bool wxJoystick::SetCapture(wxWindow* win, int pollingFreq)
101ceb40 516{
a800dc50
RD
517 if (m_thread)
518 {
519 m_thread->m_catchwin = win;
520 m_thread->m_polling = pollingFreq;
521 return true;
522 }
523 return false;
101ceb40
JS
524}
525
a800dc50 526bool wxJoystick::ReleaseCapture()
101ceb40 527{
a800dc50
RD
528 if (m_thread)
529 {
530 m_thread->m_catchwin = NULL;
531 m_thread->m_polling = 0;
532 return true;
533 }
534 return false;
101ceb40 535}
f6bcfd97 536#endif // wxUSE_JOYSTICK