1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxJoystick class
8 // Copyright: (c) Ryan Norton
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 //===========================================================================
14 //===========================================================================
16 //---------------------------------------------------------------------------
17 // Pre-compiled header stuff
18 //---------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
23 //---------------------------------------------------------------------------
25 //---------------------------------------------------------------------------
27 //we only support HID on OSX (DARWIN), since it requires DARWIN...
28 #if wxUSE_JOYSTICK && defined(__DARWIN__)
30 //---------------------------------------------------------------------------
32 //---------------------------------------------------------------------------
33 #include "wx/event.h" //joystick wxEvents
34 #include "wx/log.h" //logging...
35 #include "wx/joystick.h" //...
36 #include "wx/thread.h" //wxThread for polling thread/ wxCriticalSection
37 #include "wx/window.h" //for wxWindow to "capture" joystick
40 #include "wx/mac/corefoundation/hid.h" //private mac hid stuff
43 #include <CoreServices/CoreServices.h>
44 #include <mach/mach.h>
45 #include <mach/mach_time.h>
48 //---------------------------------------------------------------------------
49 // Definitions/Enumerations
50 //---------------------------------------------------------------------------
52 #define wxJS_MAX_AXES 10 /*max number of axes*/
53 #define wxJS_MAX_BUTTONS 40 /*max number of buttons*/
57 //These are positions within the cookie array
58 //in wxHIDJoystick that the cookies that store the axis' are
67 //---------------------------------------------------------------------------
69 //---------------------------------------------------------------------------
70 class wxHIDJoystick
: public wxHIDDevice
76 bool Create(int nWhich
);
77 virtual void BuildCookies(CFArrayRef Array
);
78 void MakeCookies(CFArrayRef Array
);
79 IOHIDElementCookie
* GetCookies();
80 IOHIDQueueInterface
** GetQueue();
82 int m_nXMax
, m_nYMax
, m_nZMax
, m_nRudderMax
, m_nUMax
, m_nVMax
,
83 m_nXMin
, m_nYMin
, m_nZMin
, m_nRudderMin
, m_nUMin
, m_nVMin
;
85 friend class wxJoystick
;
88 //---------------------------------------------------------------------------
90 //---------------------------------------------------------------------------
91 class wxJoystickThread
: public wxThread
94 wxJoystickThread(wxHIDJoystick
* hid
, int joystick
);
96 static void HIDCallback(void* target
, IOReturn res
, void* context
, void* sender
);
101 wxPoint m_lastposition
;
102 int m_axe
[wxJS_MAX_AXES
];
104 wxWindow
* m_catchwin
;
107 friend class wxJoystick
;
110 //===========================================================================
112 //===========================================================================
114 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115 // wxGetIntFromCFDictionary
117 // Helper function that gets a integer from a dictionary key
118 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119 void wxGetIntFromCFDictionary(CFTypeRef cfDict
, CFStringRef key
, int* pOut
)
122 (CFNumberRef
) CFDictionaryGetValue((CFDictionaryRef
) cfDict
,
124 kCFNumberIntType
, pOut
);
127 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
131 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
133 IMPLEMENT_DYNAMIC_CLASS(wxJoystick
, wxObject
)
135 //---------------------------------------------------------------------------
136 // wxJoystick Constructor
138 // 1) Initializes member variables
139 // 2) Attempts to create the native HID joystick implementation - if none
140 // could be found (no joysticks, etc.) then it sets it to NULL
141 //---------------------------------------------------------------------------
142 wxJoystick::wxJoystick(int joystick
)
143 : m_joystick(joystick
),
146 m_hid
= new wxHIDJoystick();
148 if (m_hid
->Create(m_joystick
+1)) //wxHIDDevice is 1-based while this is 0
150 m_thread
= new wxJoystickThread(m_hid
, m_joystick
);
161 //---------------------------------------------------------------------------
162 // wxJoystick Destructor
164 // Releases the capture of the thread, deletes it, and deletes
165 // the native implementation.
166 //---------------------------------------------------------------------------
167 wxJoystick::~wxJoystick()
171 m_thread
->Delete(); // It's detached so it will delete itself
177 //---------------------------------------------------------------------------
178 // wxJoystick::Get[XXX]Position
180 // Returns the value of an axis that was polled from the thread. In the
181 // case of GetPosition returns the X and Y values in a wxPoint
182 //---------------------------------------------------------------------------
183 wxPoint
wxJoystick::GetPosition() const
185 wxPoint
pos(wxDefaultPosition
);
186 if (m_thread
) pos
= m_thread
->m_lastposition
;
189 int wxJoystick::GetZPosition() const
192 return m_thread
->m_axe
[wxJS_AXIS_Z
];
195 int wxJoystick::GetRudderPosition() const
198 return m_thread
->m_axe
[wxJS_AXIS_RUDDER
];
201 int wxJoystick::GetUPosition() const
204 return m_thread
->m_axe
[wxJS_AXIS_U
];
207 int wxJoystick::GetVPosition() const
210 return m_thread
->m_axe
[wxJS_AXIS_V
];
214 //---------------------------------------------------------------------------
215 // wxJoystick::GetButtonState
217 // Returns the state of the buttons in a bitmask as dictated by the
218 // wx manual (the real work takes place in the thread, as always)
219 //---------------------------------------------------------------------------
220 int wxJoystick::GetButtonState() const
223 return m_thread
->m_buttons
;
227 //---------------------------------------------------------------------------
230 // Returns whether the joystick initialized successfully - in this case
231 // if the native implementation doesn't exist (in constructor)
232 //---------------------------------------------------------------------------
233 bool wxJoystick::IsOk() const
235 return m_hid
!= NULL
;
238 //---------------------------------------------------------------------------
239 // wxJoystick::Get[XXX](Id/Name)
241 // Simple accessors to the native HID implementation
242 //---------------------------------------------------------------------------
243 int wxJoystick::GetManufacturerId() const
244 { return m_hid
->m_nManufacturerId
; }
245 int wxJoystick::GetProductId() const
246 { return m_hid
->m_nProductId
; }
247 wxString
wxJoystick::GetProductName() const
248 { return m_hid
->m_szProductName
; }
250 //---------------------------------------------------------------------------
251 // wxJoystick::GetNumberButtons
252 // wxJoystick::GetNumberAxes
254 // Queries the joystick for an active number of buttons/axes.
256 // In the native HID implementation, the cookies:
257 // 0-40 are the buttons of the joystick
258 // 40-50 are the axes of the joystick
260 // These just query the native HID implementation as above.
261 //---------------------------------------------------------------------------
262 int wxJoystick::GetNumberButtons() const
266 for(int nIndex
= 0; nIndex
< 40; ++nIndex
)
268 if(m_hid
->HasElement(nIndex
))
274 int wxJoystick::GetNumberAxes() const
278 for(int nIndex
= 40; nIndex
< 50; ++nIndex
)
280 if(m_hid
->HasElement(nIndex
))
287 //---------------------------------------------------------------------------
288 // wxJoystick::GetNumberJoysticks
290 // Gets the number of joysticks on the system. In HID that
291 // is all devices with the kHIDUsage_GD_Joystick or kHIDUsage_GD_GamePad
293 //---------------------------------------------------------------------------
294 int wxJoystick::GetNumberJoysticks()
297 wxHIDDevice::GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
) +
298 wxHIDDevice::GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
);
301 //---------------------------------------------------------------------------
302 // wxJoystick::SetCapture
304 // Stops sending events from the thread to the window set in
305 // SetCapture and stops polling the joystick
306 //---------------------------------------------------------------------------
307 bool wxJoystick::SetCapture(wxWindow
* win
, int pollingFreq
)
311 m_thread
->m_catchwin
= win
;
312 m_thread
->m_polling
= pollingFreq
;
318 //---------------------------------------------------------------------------
319 // wxJoystick::ReleaseCapture
321 // Stops sending events from the thread to the window set in
322 // SetCapture and stops polling the joystick
323 //---------------------------------------------------------------------------
324 bool wxJoystick::ReleaseCapture()
328 m_thread
->m_catchwin
= NULL
;
329 m_thread
->m_polling
= 0;
335 //---------------------------------------------------------------------------
336 // wxJoystick::Get[XXX]
338 // Gets the minimum and maximum values for each axis, returning 0 if the
339 // axis doesn't exist.
340 //---------------------------------------------------------------------------
341 int wxJoystick::GetXMin() const
342 { return m_hid
->m_nXMin
; }
343 int wxJoystick::GetYMin() const
344 { return m_hid
->m_nYMin
; }
345 int wxJoystick::GetZMin() const
346 { return m_hid
->m_nZMin
; }
347 int wxJoystick::GetRudderMin() const
348 { return m_hid
->m_nRudderMin
; }
349 int wxJoystick::GetUMin() const
350 { return m_hid
->m_nUMin
; }
351 int wxJoystick::GetVMin() const
352 { return m_hid
->m_nVMin
; }
354 int wxJoystick::GetXMax() const
355 { return m_hid
->m_nXMax
; }
356 int wxJoystick::GetYMax() const
357 { return m_hid
->m_nYMax
; }
358 int wxJoystick::GetZMax() const
359 { return m_hid
->m_nZMax
; }
360 int wxJoystick::GetRudderMax() const
361 { return m_hid
->m_nRudderMax
; }
362 int wxJoystick::GetUMax() const
363 { return m_hid
->m_nUMax
; }
364 int wxJoystick::GetVMax() const
365 { return m_hid
->m_nVMax
; }
367 //---------------------------------------------------------------------------
368 // wxJoystick::Get[XXX]
370 // Min/Max values for buttons, axes, etc.. Polling in this case is just
371 // what the linux port has.
372 //---------------------------------------------------------------------------
373 int wxJoystick::GetMaxButtons() const
374 { return wxJS_MAX_BUTTONS
; }
375 int wxJoystick::GetMaxAxes() const
376 { return wxJS_MAX_AXES
; }
377 int wxJoystick::GetPollingMin() const
379 int wxJoystick::GetPollingMax() const
382 //---------------------------------------------------------------------------
383 // wxJoystick::Has[XXX]
385 // Just queries the native hid implementation if the cookie was found
386 // when enumerating the cookies of the joystick device
387 //---------------------------------------------------------------------------
388 bool wxJoystick::HasZ() const
389 { return m_hid
->HasElement(wxJS_AXIS_Z
); }
390 bool wxJoystick::HasRudder() const
391 { return m_hid
->HasElement(wxJS_AXIS_RUDDER
); }
392 bool wxJoystick::HasU() const
393 { return m_hid
->HasElement(wxJS_AXIS_U
); }
394 bool wxJoystick::HasV() const
395 { return m_hid
->HasElement(wxJS_AXIS_V
); }
397 //---------------------------------------------------------------------------
399 //---------------------------------------------------------------------------
400 int wxJoystick::GetPOVPosition() const
402 int wxJoystick::GetPOVCTSPosition() const
404 int wxJoystick::GetMovementThreshold() const
406 void wxJoystick::SetMovementThreshold(int threshold
)
408 bool wxJoystick::HasPOV() const
410 bool wxJoystick::HasPOV4Dir() const
412 bool wxJoystick::HasPOVCTS() const
415 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
419 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
421 //---------------------------------------------------------------------------
422 // wxHIDJoystick ctor
424 // Initializes the min/max members
425 //---------------------------------------------------------------------------
426 wxHIDJoystick::wxHIDJoystick() :
427 m_nXMax(0), m_nYMax(0), m_nZMax(0), m_nRudderMax(0), m_nUMax(0), m_nVMax(0),
428 m_nXMin(0), m_nYMin(0), m_nZMin(0), m_nRudderMin(0), m_nUMin(0), m_nVMin(0)
432 //---------------------------------------------------------------------------
433 // wxHIDJoystick dtor
436 //---------------------------------------------------------------------------
437 wxHIDJoystick::~wxHIDJoystick()
441 //---------------------------------------------------------------------------
442 // wxHIDJoystick::Create
444 // Creates the native HID device (joysticks are of either
445 // kHIDUsage_GD_Joystick or kHIDUsage_GD_GamePad)
446 //---------------------------------------------------------------------------
447 bool wxHIDJoystick::Create(int nWhich
)
449 int nJoysticks
= GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
);
451 if (nWhich
<= nJoysticks
)
452 return wxHIDDevice::Create(kHIDPage_GenericDesktop
,
453 kHIDUsage_GD_Joystick
,
456 nWhich
-= nJoysticks
;
458 int nGamePads
= GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
);
460 if (nWhich
<= nGamePads
)
461 return wxHIDDevice::Create(kHIDPage_GenericDesktop
,
462 kHIDUsage_GD_GamePad
,
468 //---------------------------------------------------------------------------
469 // wxHIDJoystick::BuildCookies
470 // wxHIDJoystick::MakeCookies
472 // Sets up the cookies for the HID device (called from Create) - as
473 // mentioned 0-40 are the buttons and 40-50 are the axes.
475 // MakeCookies is just a recursive function for each array within
477 //---------------------------------------------------------------------------
478 void wxHIDJoystick::BuildCookies(CFArrayRef Array
)
480 InitCookies(50, true);
483 // I wasted two hours of my life on this line :(
484 // accidently removed it during some source cleaning...
488 //paranoid debugging stuff
490 for(int i
= 0; i
< 50; ++i
)
491 wxPrintf(wxT("\nVAL #%i:[%i]"), i
, m_pCookies
[i
]);
495 void wxHIDJoystick::MakeCookies(CFArrayRef Array
)
497 int i
, nUsage
, nPage
;
499 for (i
= 0; i
< CFArrayGetCount(Array
); ++i
)
501 const void* ref
= CFDictionaryGetValue(
502 (CFDictionaryRef
)CFArrayGetValueAtIndex(Array
, i
),
503 CFSTR(kIOHIDElementKey
)
508 MakeCookies((CFArrayRef
) ref
);
514 CFDictionaryGetValue(
515 (CFDictionaryRef
) CFArrayGetValueAtIndex(Array
, i
),
516 CFSTR(kIOHIDElementUsageKey
)
523 CFDictionaryGetValue(
524 (CFDictionaryRef
) CFArrayGetValueAtIndex(Array
, i
),
525 CFSTR(kIOHIDElementUsagePageKey
)
531 wxLogSysError(wxT("[%i][%i]"), nUsage
, nPage
);
533 if (nPage
== kHIDPage_Button
&& nUsage
<= 40)
534 AddCookieInQueue(CFArrayGetValueAtIndex(Array
, i
), nUsage
-1 );
535 else if (nPage
== kHIDPage_GenericDesktop
)
541 AddCookieInQueue(CFArrayGetValueAtIndex(Array
, i
), wxJS_AXIS_X
);
542 wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array
, i
),
543 CFSTR(kIOHIDElementMaxKey
),
545 wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array
, i
),
546 CFSTR(kIOHIDElementMinKey
),
550 AddCookieInQueue(CFArrayGetValueAtIndex(Array
, i
), wxJS_AXIS_Y
);
551 wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array
, i
),
552 CFSTR(kIOHIDElementMaxKey
),
554 wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array
, i
),
555 CFSTR(kIOHIDElementMinKey
),
559 AddCookieInQueue(CFArrayGetValueAtIndex(Array
, i
), wxJS_AXIS_Z
);
560 wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array
, i
),
561 CFSTR(kIOHIDElementMaxKey
),
563 wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array
, i
),
564 CFSTR(kIOHIDElementMinKey
),
571 else if (nPage
== kHIDPage_Simulation
&& nUsage
== kHIDUsage_Sim_Rudder
)
574 AddCookieInQueue(CFArrayGetValueAtIndex(Array
, i
), wxJS_AXIS_RUDDER
);
575 wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array
, i
),
576 CFSTR(kIOHIDElementMaxKey
),
578 wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array
, i
),
579 CFSTR(kIOHIDElementMinKey
),
586 //---------------------------------------------------------------------------
587 // wxHIDJoystick::Get[XXX]
589 // Simple accessors so that the HID callback and the thread procedure
590 // can access members from wxHIDDevice (our parent here).
591 //---------------------------------------------------------------------------
592 IOHIDElementCookie
* wxHIDJoystick::GetCookies()
593 { return m_pCookies
; }
594 IOHIDQueueInterface
** wxHIDJoystick::GetQueue()
595 { return m_ppQueue
; }
597 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
601 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
603 //---------------------------------------------------------------------------
604 // wxJoystickThread Constructor
606 // Just initializes members
607 //---------------------------------------------------------------------------
608 wxJoystickThread::wxJoystickThread(wxHIDJoystick
* hid
, int joystick
)
610 m_joystick(joystick
),
611 m_lastposition(127,127),
616 memset(m_axe
, 0, sizeof(int) * wxJS_MAX_AXES
);
619 //---------------------------------------------------------------------------
620 // wxJoystickThread::Entry
624 // Runs a CFRunLoop for polling. Basically, it sets the HID queue to
625 // call wxJoystickThread::HIDCallback in the context of this thread
626 // when something changes on the device. It polls as long as the user
627 // wants, or a certain amount if the user wants to "block". Note that
628 // we don't actually block here since this is in a secondary thread.
629 //---------------------------------------------------------------------------
630 void* wxJoystickThread::Entry()
632 CFRunLoopSourceRef pRLSource
= NULL
;
634 if ((*m_hid
->GetQueue())->createAsyncEventSource(
635 m_hid
->GetQueue(), &pRLSource
) != kIOReturnSuccess
)
637 wxLogSysError(wxT("Couldn't create async event source"));
641 wxASSERT(pRLSource
!= NULL
);
643 //attach runloop source to main run loop in thread
644 CFRunLoopRef pRL
= CFRunLoopGetCurrent();
645 CFRunLoopAddSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
);
646 wxASSERT( CFRunLoopContainsSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
) );
649 if( (*m_hid
->GetQueue())->setEventCallout(m_hid
->GetQueue(),
650 wxJoystickThread::HIDCallback
, this, this) != kIOReturnSuccess
)
652 wxLogSysError(wxT("Could not set event callout for queue"));
656 if( (*m_hid
->GetQueue())->start(m_hid
->GetQueue()) != kIOReturnSuccess
)
658 wxLogSysError(wxT("Could not start queue"));
670 dTime
= 0.0001 * m_polling
;
672 dTime
= 0.0001 * 10; // check at least every 10 msec in "blocking" case
674 //true just "handles and returns" - false forces it to stay the time
677 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, dTime
, true);
680 HIDCallback(this, ret
, this, this);
685 wxASSERT( CFRunLoopContainsSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
) );
687 CFRunLoopRemoveSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
);
688 CFRelease(pRLSource
);
693 //---------------------------------------------------------------------------
694 // wxJoystickThread::HIDCallback (static)
696 // Callback for the native HID device when it recieves input.
698 // This is where the REAL dirty work gets done.
700 // 1) Loops through each event the queue has recieved
701 // 2) First, checks if the thread that is running the loop for
702 // the polling has ended - if so it breaks out
703 // 3) Next, it checks if there was an error getting this event from
704 // the HID queue, if there was, it logs an error and returns
705 // 4) Now it does the real dirty work by getting the button states
706 // from cookies 0-40 and axes positions/states from cookies 40-50
707 // in the native HID device by quering cookie values.
708 // 5) Sends the event to the polling window (if any)
709 // 6) Gets the next event and goes back to (1)
710 //---------------------------------------------------------------------------
711 /*static*/ void wxJoystickThread::HIDCallback(void* target
, IOReturn res
,
712 void* context
, void* sender
)
714 IOHIDEventStruct hidevent
;
715 AbsoluteTime bogustime
= {0,0};
717 wxJoystickThread
* pThis
= (wxJoystickThread
*) context
;
718 wxHIDJoystick
* m_hid
= pThis
->m_hid
;
720 //Get the "first" event from the queue
721 //bogustime tells it we don't care at what time to start
722 //where it gets the next from
723 ret
= (*m_hid
->GetQueue())->getNextEvent(m_hid
->GetQueue(),
724 &hidevent
, bogustime
, 0);
726 while (ret
!= kIOReturnUnderrun
)
728 if (pThis
->TestDestroy())
731 if(ret
!= kIOReturnSuccess
)
733 wxLogSysError(wxString::Format(wxT("wxJoystick Error:[%i]"), ret
));
737 wxJoystickEvent wxevent
;
739 //Find the cookie that changed
741 IOHIDElementCookie
* pCookies
= m_hid
->GetCookies();
744 if(hidevent
.elementCookie
== pCookies
[nIndex
])
754 wxLogSysError(wxString::Format(wxT("wxJoystick Out Of Bounds Error")));
759 //is the cookie a button?
764 pThis
->m_buttons
|= (1 << nIndex
);
765 wxevent
.SetEventType(wxEVT_JOY_BUTTON_DOWN
);
769 pThis
->m_buttons
&= ~(1 << nIndex
);
770 wxevent
.SetEventType(wxEVT_JOY_BUTTON_UP
);
773 wxevent
.SetButtonChange(nIndex
+1);
775 else if (nIndex
== wxJS_AXIS_X
)
777 pThis
->m_lastposition
.x
= hidevent
.value
;
778 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
779 pThis
->m_axe
[0] = hidevent
.value
;
781 else if (nIndex
== wxJS_AXIS_Y
)
783 pThis
->m_lastposition
.y
= hidevent
.value
;
784 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
785 pThis
->m_axe
[1] = hidevent
.value
;
787 else if (nIndex
== wxJS_AXIS_Z
)
789 wxevent
.SetEventType(wxEVT_JOY_ZMOVE
);
790 pThis
->m_axe
[2] = hidevent
.value
;
793 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
795 Nanoseconds timestamp
= AbsoluteToNanoseconds(hidevent
.timestamp
);
797 wxULongLong
llTime(timestamp
.hi
, timestamp
.lo
);
801 wxevent
.SetTimestamp(llTime
.GetValue());
802 wxevent
.SetJoystick(pThis
->m_joystick
);
803 wxevent
.SetButtonState(pThis
->m_buttons
);
804 wxevent
.SetPosition(pThis
->m_lastposition
);
805 wxevent
.SetZPosition(pThis
->m_axe
[2]);
806 wxevent
.SetEventObject(pThis
->m_catchwin
);
808 if (pThis
->m_catchwin
)
809 pThis
->m_catchwin
->AddPendingEvent(wxevent
);
811 ret
= (*m_hid
->GetQueue())->getNextEvent(m_hid
->GetQueue(),
812 &hidevent
, bogustime
, 0);
816 #endif // wxUSE_JOYSTICK && defined(__DARWIN__)