]> git.saurik.com Git - wxWidgets.git/blob - src/mac/corefoundation/hidjoystick.cpp
Now works when derived from wxTextCtrlBase, using wxScrollHelper.
[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 #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
31 enum {
32 wxJS_AXIS_X = 40,
33 wxJS_AXIS_Y,
34 wxJS_AXIS_Z,
35 wxJS_AXIS_RUDDER,
36 wxJS_AXIS_U,
37 wxJS_AXIS_V,
38
39 wxJS_AXIS_MAX = 255, //32767,
40 wxJS_AXIS_MIN = 0, //-32767
41 };
42
43 class wxHIDJoystick : public wxHIDDevice
44 {
45 public:
46 bool Create(int nWhich);
47 virtual void BuildCookies(wxCFArray& Array);
48 void MakeCookies(wxCFArray& Array);
49 IOHIDElementCookie* GetCookies() {return m_pCookies;}
50 IOHIDQueueInterface** GetQueue() {return m_ppQueue;}
51
52 friend class wxJoystick;
53 };
54
55
56 bool 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
73 void wxHIDJoystick::BuildCookies(wxCFArray& Array)
74 {
75 Array = CFDictionaryGetValue((CFDictionaryRef)Array[0], CFSTR(kIOHIDElementKey));
76 InitCookies(50, true);
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
85 void wxHIDJoystick::MakeCookies(wxCFArray& Array)
86 {
87 int i,
88 nUsage,
89 nPage;
90 for (i = 0; i < Array.Count(); ++i)
91 {
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(
103 (CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) Array[i], CFSTR(kIOHIDElementUsageKey)),
104 kCFNumberLongType, &nUsage);
105
106 CFNumberGetValue(
107 (CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) Array[i], CFSTR(kIOHIDElementUsagePageKey)),
108 kCFNumberLongType, &nPage);
109
110 if (nPage == kHIDPage_Button && nUsage <= 40)
111 AddCookieInQueue(Array[i], nUsage-1 );
112 else if (nPage == kHIDPage_GenericDesktop)
113 {
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 }
128 }
129 else if (nPage == kHIDPage_Simulation && nUsage == kHIDUsage_Sim_Rudder)
130 AddCookieInQueue(Array[i], wxJS_AXIS_RUDDER );
131 }
132 }
133 }
134
135
136
137 IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject)
138
139
140 ////////////////////////////////////////////////////////////////////////////
141 // Background thread for reading the joystick device
142 ////////////////////////////////////////////////////////////////////////////
143
144 class wxJoystickThread : public wxThread
145 {
146 public:
147 wxJoystickThread(wxHIDJoystick* hid, int joystick);
148 void* Entry();
149
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
251 private:
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
264 wxJoystickThread::wxJoystickThread(wxHIDJoystick* hid, int joystick)
265 : m_hid(hid),
266 m_joystick(joystick),
267 m_lastposition(127,127),
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
280 void* 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
292 wxJSVERIFY( (*m_hid->GetQueue())->start(m_hid->GetQueue()) == kIOReturnSuccess );
293 wxJSVERIFY( (*m_hid->GetQueue())->setEventCallout(m_hid->GetQueue(), &wxJoystickThread::HIDCallback, this, this) == kIOReturnSuccess );
294
295 double dTime;
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
307 CFRunLoopRunInMode(kCFRunLoopDefaultMode, dTime, true);
308 }
309
310 wxJSASSERT( CFRunLoopContainsSource(pRL, pRLSource, kCFRunLoopDefaultMode) );
311 CFRunLoopRemoveSource(pRL, pRLSource, kCFRunLoopDefaultMode);
312 CFRelease(pRLSource);
313
314 return NULL;
315 }
316
317
318 ////////////////////////////////////////////////////////////////////////////
319
320 wxJoystick::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
340 wxJoystick::~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
355 wxPoint wxJoystick::GetPosition() const
356 {
357 wxPoint pos(wxDefaultPosition);
358 if (m_thread) pos = m_thread->m_lastposition;
359 return pos;
360 }
361
362 int wxJoystick::GetZPosition() const
363 {
364 if (m_thread)
365 return m_thread->m_axe[wxJS_AXIS_Z];
366 return 0;
367 }
368
369 int wxJoystick::GetButtonState() const
370 {
371 if (m_thread)
372 return m_thread->m_buttons;
373 return 0;
374 }
375
376 int wxJoystick::GetPOVPosition() const
377 { return -1; }
378
379 int wxJoystick::GetPOVCTSPosition() const
380 { return -1; }
381
382 int wxJoystick::GetRudderPosition() const
383 {
384 if (m_thread)
385 return m_thread->m_axe[wxJS_AXIS_RUDDER];
386 return 0;
387 }
388
389 int wxJoystick::GetUPosition() const
390 {
391 if (m_thread)
392 return m_thread->m_axe[wxJS_AXIS_U];
393 return 0;
394 }
395
396 int wxJoystick::GetVPosition() const
397 {
398 if (m_thread)
399 return m_thread->m_axe[wxJS_AXIS_V];
400 return 0;
401 }
402
403 int wxJoystick::GetMovementThreshold() const
404 { return 0; }
405
406 void wxJoystick::SetMovementThreshold(int threshold)
407 { }
408
409 ////////////////////////////////////////////////////////////////////////////
410 // Capabilities
411 ////////////////////////////////////////////////////////////////////////////
412
413 bool wxJoystick::IsOk() const
414 { return m_hid != NULL; }
415
416 int wxJoystick::GetNumberJoysticks() const
417 { return wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick) +
418 wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad); }
419
420 int wxJoystick::GetManufacturerId() const
421 { return m_hid->m_nManufacturerId; }
422
423 int wxJoystick::GetProductId() const
424 { return m_hid->m_nProductId; }
425
426 wxString wxJoystick::GetProductName() const
427 { return m_hid->m_szProductName; }
428
429 int wxJoystick::GetXMin() const
430 { return wxJS_AXIS_MIN; }
431
432 int wxJoystick::GetYMin() const
433 { return wxJS_AXIS_MIN; }
434
435 int wxJoystick::GetZMin() const
436 { return wxJS_AXIS_MIN; }
437
438 int wxJoystick::GetXMax() const
439 { return wxJS_AXIS_MAX; }
440
441 int wxJoystick::GetYMax() const
442 { return wxJS_AXIS_MAX; }
443
444 int wxJoystick::GetZMax() const
445 { return wxJS_AXIS_MAX; }
446
447 int 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
460 int 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 //
476 int wxJoystick::GetMaxButtons() const
477 { return 15; }
478
479 int wxJoystick::GetMaxAxes() const
480 { return 10; }
481
482 int wxJoystick::GetPollingMin() const
483 { return 10; }
484
485 int wxJoystick::GetPollingMax() const
486 { return 1000; }
487
488 int wxJoystick::GetRudderMin() const
489 { return wxJS_AXIS_MIN; }
490
491 int wxJoystick::GetRudderMax() const
492 { return wxJS_AXIS_MAX; }
493
494 int wxJoystick::GetUMin() const
495 { return wxJS_AXIS_MIN; }
496
497 int wxJoystick::GetUMax() const
498 { return wxJS_AXIS_MAX; }
499
500 int wxJoystick::GetVMin() const
501 { return wxJS_AXIS_MIN; }
502
503 int wxJoystick::GetVMax() const
504 { return wxJS_AXIS_MAX; }
505
506 bool wxJoystick::HasRudder() const
507 { return m_hid->HasElement(wxJS_AXIS_RUDDER); }
508
509 bool wxJoystick::HasZ() const
510 { return m_hid->HasElement(wxJS_AXIS_Z); }
511
512 bool wxJoystick::HasU() const
513 { return m_hid->HasElement(wxJS_AXIS_U); }
514
515 bool wxJoystick::HasV() const
516 { return m_hid->HasElement(wxJS_AXIS_V); }
517
518 bool wxJoystick::HasPOV() const
519 { return false; }
520
521 bool wxJoystick::HasPOV4Dir() const
522 { return false; }
523
524 bool wxJoystick::HasPOVCTS() const
525 { return false; }
526
527 ////////////////////////////////////////////////////////////////////////////
528 // Operations
529 ////////////////////////////////////////////////////////////////////////////
530
531 bool 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
542 bool 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