]> git.saurik.com Git - wxWidgets.git/blame - src/mac/corefoundation/hidjoystick.cpp
Introduced invalidation of ranges for later optimization code
[wxWidgets.git] / src / mac / corefoundation / hidjoystick.cpp
CommitLineData
4cb1d3da
RN
1/////////////////////////////////////////////////////////////////////////////
2// Name: joystick.cpp
3// Purpose: wxJoystick class
4// Author: Ryan Norton
5// Modified by:
6// Created: 2/13/2005
7// RCS-ID: $Id$
8// Copyright: (c) Ryan Norton
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
4cb1d3da
RN
12#include "wx/wxprec.h"
13
14#ifdef __DARWIN__
15
16#if wxUSE_JOYSTICK
17
18#include "wx/event.h"
19#include "wx/log.h"
20#include "wx/joystick.h"
21#include "wx/thread.h"
22#include "wx/window.h"
23
24#include "wx/mac/corefoundation/hid.h"
25
26#include <CoreServices/CoreServices.h>
27#include <mach/mach.h>
28#include <mach/mach_time.h>
29#include <unistd.h>
30
31enum {
65442ab6 32 wxJS_AXIS_X = 40,
4cb1d3da
RN
33 wxJS_AXIS_Y,
34 wxJS_AXIS_Z,
35 wxJS_AXIS_RUDDER,
36 wxJS_AXIS_U,
37 wxJS_AXIS_V,
38
65442ab6
RN
39 wxJS_AXIS_MAX = 255, //32767,
40 wxJS_AXIS_MIN = 0, //-32767
4cb1d3da
RN
41};
42
43class wxHIDJoystick : public wxHIDDevice
44{
45public:
46 bool Create(int nWhich);
47 virtual void BuildCookies(wxCFArray& Array);
65442ab6 48 void MakeCookies(wxCFArray& Array);
4cb1d3da 49 IOHIDElementCookie* GetCookies() {return m_pCookies;}
4cb1d3da 50 IOHIDQueueInterface** GetQueue() {return m_ppQueue;}
65442ab6
RN
51
52 friend class wxJoystick;
4cb1d3da
RN
53};
54
55
56bool wxHIDJoystick::Create(int nWhich)
57{
58 int nJoysticks = GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
59
60 if (nWhich <= nJoysticks)
61 return wxHIDDevice::Create(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
62 else
63 nWhich -= nJoysticks;
64
65 int nGamePads = GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
66
67 if (nWhich <= nGamePads)
68 return wxHIDDevice::Create(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
69 else
70 return false;
71}
72
73void wxHIDJoystick::BuildCookies(wxCFArray& Array)
74{
75 Array = CFDictionaryGetValue((CFDictionaryRef)Array[0], CFSTR(kIOHIDElementKey));
76 InitCookies(50, true);
65442ab6
RN
77
78 memset(m_pCookies, 0, sizeof(*m_pCookies) * 50);
79 MakeCookies(Array);
80
81// for(int i = 0; i < 50; ++i)
82// wxPrintf(wxT("\nVAL #%i:[%i]"), i, m_pCookies[i]);
83}//end buildcookies
84
85void wxHIDJoystick::MakeCookies(wxCFArray& Array)
86{
4cb1d3da
RN
87 int i,
88 nUsage,
89 nPage;
2a32f173 90 for (i = 0; i < Array.Count(); ++i)
4cb1d3da 91 {
65442ab6
RN
92 const void* ref = CFDictionaryGetValue((CFDictionaryRef)Array[i], CFSTR(kIOHIDElementKey));
93
94// wxPrintf(wxT("ELM\n"));
95 if (ref != NULL)
96 {
97 wxCFArray newarray(ref);
98 MakeCookies(newarray);
99 }
100 else
101 {
102 CFNumberGetValue(
4cb1d3da
RN
103 (CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) Array[i], CFSTR(kIOHIDElementUsageKey)),
104 kCFNumberLongType, &nUsage);
105
65442ab6 106 CFNumberGetValue(
4cb1d3da
RN
107 (CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) Array[i], CFSTR(kIOHIDElementUsagePageKey)),
108 kCFNumberLongType, &nPage);
109
65442ab6
RN
110 if (nPage == kHIDPage_Button && nUsage <= 40)
111 AddCookieInQueue(Array[i], nUsage-1 );
112 else if (nPage == kHIDPage_GenericDesktop)
4cb1d3da 113 {
65442ab6
RN
114 switch(nUsage)
115 {
116 case kHIDUsage_GD_X:
117 AddCookieInQueue(Array[i], wxJS_AXIS_X);
118 break;
119 case kHIDUsage_GD_Y:
120 AddCookieInQueue(Array[i], wxJS_AXIS_Y);
121 break;
122 case kHIDUsage_GD_Z:
123 AddCookieInQueue(Array[i], wxJS_AXIS_Z);
124 break;
125 default:
126 break;
127 }
4cb1d3da 128 }
65442ab6
RN
129 else if (nPage == kHIDPage_Simulation && nUsage == kHIDUsage_Sim_Rudder)
130 AddCookieInQueue(Array[i], wxJS_AXIS_RUDDER );
4cb1d3da 131 }
4cb1d3da 132 }
65442ab6 133}
4cb1d3da
RN
134
135
136
137IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject)
138
139
140////////////////////////////////////////////////////////////////////////////
141// Background thread for reading the joystick device
142////////////////////////////////////////////////////////////////////////////
143
144class wxJoystickThread : public wxThread
145{
146public:
147 wxJoystickThread(wxHIDJoystick* hid, int joystick);
148 void* Entry();
149
65442ab6
RN
150 static void HIDCallback(void* target, IOReturn res, void* context, void* sender)
151 {
152 IOHIDEventStruct hidevent;
153 AbsoluteTime bogustime = {0,0};
154 IOReturn ret;
155 wxJoystickThread* pThis = (wxJoystickThread*) context;
156 wxHIDJoystick* m_hid = pThis->m_hid;
157
158// wxMutexGuiEnter();
159 ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(),
160 &hidevent, bogustime, 0);
161 // wxMutexGuiLeave();
162 while ( ret != kIOReturnUnderrun )
163 {
164 if (pThis->TestDestroy())
165 break;
166
167// wxPrintf(wxT("ENTER\n"));
168 if(ret != kIOReturnSuccess)
169 {
170 wxLogSysError(wxString::Format(wxT("wxJoystick Error:[%i]"), ret));
171 return;
172 }
173
174 wxJoystickEvent wxevent;
175
176 int nIndex = 0;
177 IOHIDElementCookie* pCookies = m_hid->GetCookies();
178 while(nIndex < 50)
179 {
180 if(hidevent.elementCookie == pCookies[nIndex])
181 break;
182
183 ++nIndex;
184 }
185 if(nIndex == 50)
186 {
187 wxLogSysError(wxString::Format(wxT("wxJoystick Out Of Bounds Error")));
188 break;
189 }
190
191 if (nIndex < 40)
192 {
193 if (hidevent.value)
194 {
195 pThis->m_buttons |= (1 << nIndex);
196 wxevent.SetEventType(wxEVT_JOY_BUTTON_DOWN);
197 }
198 else
199 {
200 pThis->m_buttons &= ~(1 << nIndex);
201 wxevent.SetEventType(wxEVT_JOY_BUTTON_UP);
202 }
203
204 wxevent.SetButtonChange(nIndex+1);
205 }
206 else if (nIndex == wxJS_AXIS_X)
207 {
208 pThis->m_lastposition.x = hidevent.value;
209 wxevent.SetEventType(wxEVT_JOY_MOVE);
210 pThis->m_axe[0] = hidevent.value;
211 }
212 else if (nIndex == wxJS_AXIS_Y)
213 {
214 pThis->m_lastposition.y = hidevent.value;
215 wxevent.SetEventType(wxEVT_JOY_MOVE);
216 pThis->m_axe[1] = hidevent.value;
217 }
218 else if (nIndex == wxJS_AXIS_Z)
219 {
220 wxevent.SetEventType(wxEVT_JOY_ZMOVE);
221 pThis->m_axe[2] = hidevent.value;
222 }
223 else
224 wxevent.SetEventType(wxEVT_JOY_MOVE);
225
226 Nanoseconds timestamp = AbsoluteToNanoseconds(hidevent.timestamp);
227
228 wxULongLong llTime(timestamp.hi, timestamp.lo);
229
230 llTime /= 1000000;
231
232 wxevent.SetTimestamp(llTime.GetValue());
233 wxevent.SetJoystick(pThis->m_joystick);
234 wxevent.SetButtonState(pThis->m_buttons);
235 wxevent.SetPosition(pThis->m_lastposition);
236 wxevent.SetZPosition(pThis->m_axe[2]);
237 wxevent.SetEventObject(pThis->m_catchwin);
238
239// wxPrintf(wxT("SEND\n"));
240
241 if (pThis->m_catchwin)
242 pThis->m_catchwin->AddPendingEvent(wxevent);
243
244 // wxMutexGuiEnter();
245 ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(),
246 &hidevent, bogustime, 0);
247 // wxMutexGuiLeave();
248 }
249 }
250
4cb1d3da
RN
251private:
252 wxHIDJoystick* m_hid;
253 int m_joystick;
254 wxPoint m_lastposition;
255 int m_axe[15];
256 int m_buttons;
257 wxWindow* m_catchwin;
258 int m_polling;
259
260 friend class wxJoystick;
261};
262
263
264wxJoystickThread::wxJoystickThread(wxHIDJoystick* hid, int joystick)
265 : m_hid(hid),
266 m_joystick(joystick),
65442ab6 267 m_lastposition(127,127),
4cb1d3da
RN
268 m_buttons(0),
269 m_catchwin(NULL),
270 m_polling(0)
271{
272 for (int i=0; i<15; i++)
273 m_axe[i] = 0;
274}
275
276
277# define wxJSVERIFY(arg) if(!(arg)) {wxLogSysError(wxT(#arg)); return NULL;}
278# define wxJSASSERT(arg) wxJSVERIFY(arg)
279
280void* wxJoystickThread::Entry()
281{
282 CFRunLoopSourceRef pRLSource = NULL;
283
284 wxJSVERIFY( (*m_hid->GetQueue())->createAsyncEventSource(m_hid->GetQueue(), &pRLSource)
285 == kIOReturnSuccess );
286 wxJSASSERT(pRLSource != NULL);
287
288 //attach runloop source to main run loop in thread
289 CFRunLoopRef pRL = CFRunLoopGetCurrent();
290 CFRunLoopAddSource(pRL, pRLSource, kCFRunLoopDefaultMode);
291
65442ab6
RN
292 wxJSVERIFY( (*m_hid->GetQueue())->start(m_hid->GetQueue()) == kIOReturnSuccess );
293 wxJSVERIFY( (*m_hid->GetQueue())->setEventCallout(m_hid->GetQueue(), &wxJoystickThread::HIDCallback, this, this) == kIOReturnSuccess );
4cb1d3da
RN
294
295 double dTime;
4cb1d3da
RN
296
297 while(true)
298 {
299 if (TestDestroy())
300 break;
301
302 if (m_polling)
303 dTime = 0.0001 * m_polling;
304 else
305 dTime = 0.0001 * 10; // check at least every 10 msec in blocking case
306
65442ab6 307 CFRunLoopRunInMode(kCFRunLoopDefaultMode, dTime, true);
4cb1d3da 308 }
65442ab6
RN
309
310 wxJSASSERT( CFRunLoopContainsSource(pRL, pRLSource, kCFRunLoopDefaultMode) );
311 CFRunLoopRemoveSource(pRL, pRLSource, kCFRunLoopDefaultMode);
312 CFRelease(pRLSource);
313
4cb1d3da
RN
314 return NULL;
315}
316
317
318////////////////////////////////////////////////////////////////////////////
319
320wxJoystick::wxJoystick(int joystick)
321 : m_joystick(joystick),
322 m_thread(NULL)
323{
324 m_hid = new wxHIDJoystick();
325
326 if (m_hid->Create(m_joystick))
327 {
328 m_thread = new wxJoystickThread(m_hid, m_joystick);
329 m_thread->Create();
330 m_thread->Run();
331 }
332 else
333 {
334 delete m_hid;
335 m_hid = NULL;
336 }
337}
338
339
340wxJoystick::~wxJoystick()
341{
342 ReleaseCapture();
343 if (m_thread)
344 m_thread->Delete(); // It's detached so it will delete itself
345
346 if (m_hid)
347 delete m_hid;
348}
349
350
351////////////////////////////////////////////////////////////////////////////
352// State
353////////////////////////////////////////////////////////////////////////////
354
355wxPoint wxJoystick::GetPosition() const
356{
357 wxPoint pos(wxDefaultPosition);
358 if (m_thread) pos = m_thread->m_lastposition;
359 return pos;
360}
361
362int wxJoystick::GetZPosition() const
363{
364 if (m_thread)
365 return m_thread->m_axe[wxJS_AXIS_Z];
366 return 0;
367}
368
369int wxJoystick::GetButtonState() const
370{
371 if (m_thread)
372 return m_thread->m_buttons;
373 return 0;
374}
375
376int wxJoystick::GetPOVPosition() const
377{ return -1; }
378
379int wxJoystick::GetPOVCTSPosition() const
380{ return -1; }
381
382int wxJoystick::GetRudderPosition() const
383{
384 if (m_thread)
385 return m_thread->m_axe[wxJS_AXIS_RUDDER];
386 return 0;
387}
388
389int wxJoystick::GetUPosition() const
390{
391 if (m_thread)
392 return m_thread->m_axe[wxJS_AXIS_U];
393 return 0;
394}
395
396int wxJoystick::GetVPosition() const
397{
398 if (m_thread)
399 return m_thread->m_axe[wxJS_AXIS_V];
400 return 0;
401}
402
403int wxJoystick::GetMovementThreshold() const
404{ return 0; }
405
406void wxJoystick::SetMovementThreshold(int threshold)
407{ }
408
409////////////////////////////////////////////////////////////////////////////
410// Capabilities
411////////////////////////////////////////////////////////////////////////////
412
413bool wxJoystick::IsOk() const
414{ return m_hid != NULL; }
415
416int wxJoystick::GetNumberJoysticks() const
417{ return wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick) +
418 wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad); }
419
420int wxJoystick::GetManufacturerId() const
65442ab6 421{ return m_hid->m_nManufacturerId; }
4cb1d3da
RN
422
423int wxJoystick::GetProductId() const
65442ab6 424{ return m_hid->m_nProductId; }
4cb1d3da
RN
425
426wxString wxJoystick::GetProductName() const
65442ab6 427{ return m_hid->m_szProductName; }
4cb1d3da
RN
428
429int wxJoystick::GetXMin() const
430{ return wxJS_AXIS_MIN; }
431
432int wxJoystick::GetYMin() const
433{ return wxJS_AXIS_MIN; }
434
435int wxJoystick::GetZMin() const
436{ return wxJS_AXIS_MIN; }
437
438int wxJoystick::GetXMax() const
439{ return wxJS_AXIS_MAX; }
440
441int wxJoystick::GetYMax() const
442{ return wxJS_AXIS_MAX; }
443
444int wxJoystick::GetZMax() const
445{ return wxJS_AXIS_MAX; }
446
447int wxJoystick::GetNumberButtons() const
448{
449 int nCount = 0;
450
451 for(int nIndex = 0; nIndex < 40; ++nIndex)
452 {
453 if(m_hid->HasElement(nIndex))
454 ++nCount;
455 }
456
457 return nCount;
458}
459
460int wxJoystick::GetNumberAxes() const
461{
462 int nCount = 0;
463
464 for(int nIndex = 40; nIndex < 50; ++nIndex)
465 {
466 if(m_hid->HasElement(nIndex))
467 ++nCount;
468 }
469
470 return nCount;
471}
472
473//
474// internal
475//
476int wxJoystick::GetMaxButtons() const
477{ return 15; }
478
479int wxJoystick::GetMaxAxes() const
480{ return 10; }
481
482int wxJoystick::GetPollingMin() const
483{ return 10; }
484
485int wxJoystick::GetPollingMax() const
486{ return 1000; }
487
488int wxJoystick::GetRudderMin() const
489{ return wxJS_AXIS_MIN; }
490
491int wxJoystick::GetRudderMax() const
492{ return wxJS_AXIS_MAX; }
493
494int wxJoystick::GetUMin() const
495{ return wxJS_AXIS_MIN; }
496
497int wxJoystick::GetUMax() const
498{ return wxJS_AXIS_MAX; }
499
500int wxJoystick::GetVMin() const
501{ return wxJS_AXIS_MIN; }
502
503int wxJoystick::GetVMax() const
504{ return wxJS_AXIS_MAX; }
505
506bool wxJoystick::HasRudder() const
507{ return m_hid->HasElement(wxJS_AXIS_RUDDER); }
508
509bool wxJoystick::HasZ() const
510{ return m_hid->HasElement(wxJS_AXIS_Z); }
511
512bool wxJoystick::HasU() const
513{ return m_hid->HasElement(wxJS_AXIS_U); }
514
515bool wxJoystick::HasV() const
516{ return m_hid->HasElement(wxJS_AXIS_V); }
517
518bool wxJoystick::HasPOV() const
519{ return false; }
520
521bool wxJoystick::HasPOV4Dir() const
522{ return false; }
523
524bool wxJoystick::HasPOVCTS() const
525{ return false; }
526
527////////////////////////////////////////////////////////////////////////////
528// Operations
529////////////////////////////////////////////////////////////////////////////
530
531bool wxJoystick::SetCapture(wxWindow* win, int pollingFreq)
532{
533 if (m_thread)
534 {
535 m_thread->m_catchwin = win;
536 m_thread->m_polling = pollingFreq;
537 return true;
538 }
539 return false;
540}
541
542bool wxJoystick::ReleaseCapture()
543{
544 if (m_thread)
545 {
546 m_thread->m_catchwin = NULL;
547 m_thread->m_polling = 0;
548 return true;
549 }
550 return false;
551}
552#endif
553 //OSX
554
555#endif
556 // wxUSE_JOYSTICK
557