]> git.saurik.com Git - wxWidgets.git/blame - src/mac/corefoundation/hidjoystick.cpp
Move wxMacExecute into base
[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
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
35enum {
65442ab6 36 wxJS_AXIS_X = 40,
4cb1d3da
RN
37 wxJS_AXIS_Y,
38 wxJS_AXIS_Z,
39 wxJS_AXIS_RUDDER,
40 wxJS_AXIS_U,
41 wxJS_AXIS_V,
42
65442ab6
RN
43 wxJS_AXIS_MAX = 255, //32767,
44 wxJS_AXIS_MIN = 0, //-32767
4cb1d3da
RN
45};
46
47class wxHIDJoystick : public wxHIDDevice
48{
49public:
50 bool Create(int nWhich);
51 virtual void BuildCookies(wxCFArray& Array);
65442ab6 52 void MakeCookies(wxCFArray& Array);
4cb1d3da 53 IOHIDElementCookie* GetCookies() {return m_pCookies;}
4cb1d3da 54 IOHIDQueueInterface** GetQueue() {return m_ppQueue;}
65442ab6
RN
55
56 friend class wxJoystick;
4cb1d3da
RN
57};
58
59
60bool 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
77void wxHIDJoystick::BuildCookies(wxCFArray& Array)
78{
79 Array = CFDictionaryGetValue((CFDictionaryRef)Array[0], CFSTR(kIOHIDElementKey));
80 InitCookies(50, true);
65442ab6
RN
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
89void wxHIDJoystick::MakeCookies(wxCFArray& Array)
90{
4cb1d3da
RN
91 int i,
92 nUsage,
93 nPage;
2a32f173 94 for (i = 0; i < Array.Count(); ++i)
4cb1d3da 95 {
65442ab6
RN
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(
4cb1d3da
RN
107 (CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) Array[i], CFSTR(kIOHIDElementUsageKey)),
108 kCFNumberLongType, &nUsage);
109
65442ab6 110 CFNumberGetValue(
4cb1d3da
RN
111 (CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) Array[i], CFSTR(kIOHIDElementUsagePageKey)),
112 kCFNumberLongType, &nPage);
113
65442ab6
RN
114 if (nPage == kHIDPage_Button && nUsage <= 40)
115 AddCookieInQueue(Array[i], nUsage-1 );
116 else if (nPage == kHIDPage_GenericDesktop)
4cb1d3da 117 {
65442ab6
RN
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 }
4cb1d3da 132 }
65442ab6
RN
133 else if (nPage == kHIDPage_Simulation && nUsage == kHIDUsage_Sim_Rudder)
134 AddCookieInQueue(Array[i], wxJS_AXIS_RUDDER );
4cb1d3da 135 }
4cb1d3da 136 }
65442ab6 137}
4cb1d3da
RN
138
139
140
141IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject)
142
143
144////////////////////////////////////////////////////////////////////////////
145// Background thread for reading the joystick device
146////////////////////////////////////////////////////////////////////////////
147
148class wxJoystickThread : public wxThread
149{
150public:
151 wxJoystickThread(wxHIDJoystick* hid, int joystick);
152 void* Entry();
153
65442ab6
RN
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
4cb1d3da
RN
255private:
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
268wxJoystickThread::wxJoystickThread(wxHIDJoystick* hid, int joystick)
269 : m_hid(hid),
270 m_joystick(joystick),
65442ab6 271 m_lastposition(127,127),
4cb1d3da
RN
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
284void* 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
65442ab6
RN
296 wxJSVERIFY( (*m_hid->GetQueue())->start(m_hid->GetQueue()) == kIOReturnSuccess );
297 wxJSVERIFY( (*m_hid->GetQueue())->setEventCallout(m_hid->GetQueue(), &wxJoystickThread::HIDCallback, this, this) == kIOReturnSuccess );
4cb1d3da
RN
298
299 double dTime;
4cb1d3da
RN
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
65442ab6 311 CFRunLoopRunInMode(kCFRunLoopDefaultMode, dTime, true);
4cb1d3da 312 }
65442ab6
RN
313
314 wxJSASSERT( CFRunLoopContainsSource(pRL, pRLSource, kCFRunLoopDefaultMode) );
315 CFRunLoopRemoveSource(pRL, pRLSource, kCFRunLoopDefaultMode);
316 CFRelease(pRLSource);
317
4cb1d3da
RN
318 return NULL;
319}
320
321
322////////////////////////////////////////////////////////////////////////////
323
324wxJoystick::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
344wxJoystick::~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
359wxPoint wxJoystick::GetPosition() const
360{
361 wxPoint pos(wxDefaultPosition);
362 if (m_thread) pos = m_thread->m_lastposition;
363 return pos;
364}
365
366int wxJoystick::GetZPosition() const
367{
368 if (m_thread)
369 return m_thread->m_axe[wxJS_AXIS_Z];
370 return 0;
371}
372
373int wxJoystick::GetButtonState() const
374{
375 if (m_thread)
376 return m_thread->m_buttons;
377 return 0;
378}
379
380int wxJoystick::GetPOVPosition() const
381{ return -1; }
382
383int wxJoystick::GetPOVCTSPosition() const
384{ return -1; }
385
386int wxJoystick::GetRudderPosition() const
387{
388 if (m_thread)
389 return m_thread->m_axe[wxJS_AXIS_RUDDER];
390 return 0;
391}
392
393int wxJoystick::GetUPosition() const
394{
395 if (m_thread)
396 return m_thread->m_axe[wxJS_AXIS_U];
397 return 0;
398}
399
400int wxJoystick::GetVPosition() const
401{
402 if (m_thread)
403 return m_thread->m_axe[wxJS_AXIS_V];
404 return 0;
405}
406
407int wxJoystick::GetMovementThreshold() const
408{ return 0; }
409
410void wxJoystick::SetMovementThreshold(int threshold)
411{ }
412
413////////////////////////////////////////////////////////////////////////////
414// Capabilities
415////////////////////////////////////////////////////////////////////////////
416
417bool wxJoystick::IsOk() const
418{ return m_hid != NULL; }
419
420int wxJoystick::GetNumberJoysticks() const
421{ return wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick) +
422 wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad); }
423
424int wxJoystick::GetManufacturerId() const
65442ab6 425{ return m_hid->m_nManufacturerId; }
4cb1d3da
RN
426
427int wxJoystick::GetProductId() const
65442ab6 428{ return m_hid->m_nProductId; }
4cb1d3da
RN
429
430wxString wxJoystick::GetProductName() const
65442ab6 431{ return m_hid->m_szProductName; }
4cb1d3da
RN
432
433int wxJoystick::GetXMin() const
434{ return wxJS_AXIS_MIN; }
435
436int wxJoystick::GetYMin() const
437{ return wxJS_AXIS_MIN; }
438
439int wxJoystick::GetZMin() const
440{ return wxJS_AXIS_MIN; }
441
442int wxJoystick::GetXMax() const
443{ return wxJS_AXIS_MAX; }
444
445int wxJoystick::GetYMax() const
446{ return wxJS_AXIS_MAX; }
447
448int wxJoystick::GetZMax() const
449{ return wxJS_AXIS_MAX; }
450
451int 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
464int 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//
480int wxJoystick::GetMaxButtons() const
481{ return 15; }
482
483int wxJoystick::GetMaxAxes() const
484{ return 10; }
485
486int wxJoystick::GetPollingMin() const
487{ return 10; }
488
489int wxJoystick::GetPollingMax() const
490{ return 1000; }
491
492int wxJoystick::GetRudderMin() const
493{ return wxJS_AXIS_MIN; }
494
495int wxJoystick::GetRudderMax() const
496{ return wxJS_AXIS_MAX; }
497
498int wxJoystick::GetUMin() const
499{ return wxJS_AXIS_MIN; }
500
501int wxJoystick::GetUMax() const
502{ return wxJS_AXIS_MAX; }
503
504int wxJoystick::GetVMin() const
505{ return wxJS_AXIS_MIN; }
506
507int wxJoystick::GetVMax() const
508{ return wxJS_AXIS_MAX; }
509
510bool wxJoystick::HasRudder() const
511{ return m_hid->HasElement(wxJS_AXIS_RUDDER); }
512
513bool wxJoystick::HasZ() const
514{ return m_hid->HasElement(wxJS_AXIS_Z); }
515
516bool wxJoystick::HasU() const
517{ return m_hid->HasElement(wxJS_AXIS_U); }
518
519bool wxJoystick::HasV() const
520{ return m_hid->HasElement(wxJS_AXIS_V); }
521
522bool wxJoystick::HasPOV() const
523{ return false; }
524
525bool wxJoystick::HasPOV4Dir() const
526{ return false; }
527
528bool wxJoystick::HasPOVCTS() const
529{ return false; }
530
531////////////////////////////////////////////////////////////////////////////
532// Operations
533////////////////////////////////////////////////////////////////////////////
534
535bool 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
546bool 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