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