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