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