]> git.saurik.com Git - wxWidgets.git/blobdiff - src/mac/core/hidjoystick.cpp
renaming
[wxWidgets.git] / src / mac / core / hidjoystick.cpp
diff --git a/src/mac/core/hidjoystick.cpp b/src/mac/core/hidjoystick.cpp
new file mode 100644 (file)
index 0000000..637546c
--- /dev/null
@@ -0,0 +1,905 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        src/mac/corefoundation/joystick.cpp
+// Purpose:     wxJoystick class
+// Author:      Ryan Norton
+// Modified by:
+// Created:     2/13/2005
+// RCS-ID:      $Id$
+// Copyright:   (c) Ryan Norton
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+//===========================================================================
+//  DECLARATIONS
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Pre-compiled header stuff
+//---------------------------------------------------------------------------
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+//---------------------------------------------------------------------------
+// Guard
+//---------------------------------------------------------------------------
+
+//we only support HID on OSX (DARWIN), since it requires DARWIN...
+#if wxUSE_JOYSTICK && wxUSE_THREADS
+
+//---------------------------------------------------------------------------
+// Includes
+//---------------------------------------------------------------------------
+
+#ifndef WX_PRECOMP
+    #include "wx/log.h"
+    #include "wx/event.h"   //joystick wxEvents
+    #include "wx/window.h"  //for wxWindow to "capture" joystick
+#endif
+
+#include "wx/joystick.h"    //...
+#include "wx/thread.h"      //wxThread for polling thread/ wxCriticalSection
+
+//private headers
+#include "wx/mac/corefoundation/hid.h" //private mac hid stuff
+
+//mac headers
+#include <CoreServices/CoreServices.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <unistd.h>
+
+//---------------------------------------------------------------------------
+// Definitions/Enumerations
+//---------------------------------------------------------------------------
+
+#define wxJS_MAX_AXES       10 /*max number of axes*/
+#define wxJS_MAX_BUTTONS    40 /*max number of buttons*/
+
+enum
+{
+    //These are positions within the cookie array
+    //in wxHIDJoystick that the cookies that store the axis' are
+    wxJS_AXIS_X = 40,
+    wxJS_AXIS_Y,
+    wxJS_AXIS_Z,
+    wxJS_AXIS_RUDDER,
+    wxJS_AXIS_U,
+    wxJS_AXIS_V,
+};
+
+//---------------------------------------------------------------------------
+// wxHIDJoystick
+//---------------------------------------------------------------------------
+class wxHIDJoystick : public wxHIDDevice
+{
+public:
+    wxHIDJoystick();
+    virtual ~wxHIDJoystick();
+
+    bool Create(int nWhich);
+    virtual void BuildCookies(CFArrayRef Array);
+    void MakeCookies(CFArrayRef Array);
+    IOHIDElementCookie* GetCookies();
+    IOHIDQueueInterface** GetQueue();
+
+    int  m_nXMax, m_nYMax, m_nZMax, m_nRudderMax, m_nUMax, m_nVMax,
+         m_nXMin, m_nYMin, m_nZMin, m_nRudderMin, m_nUMin, m_nVMin;
+
+    friend class wxJoystick;
+};
+
+//---------------------------------------------------------------------------
+// wxJoystickThread
+//---------------------------------------------------------------------------
+class wxJoystickThread : public wxThread
+{
+public:
+    wxJoystickThread(wxHIDJoystick* hid, int joystick);
+    void* Entry();
+    static void HIDCallback(void* target, IOReturn res, void* context, void* sender);
+
+private:
+    wxHIDJoystick*       m_hid;
+    int       m_joystick;
+    wxPoint   m_lastposition;
+    int       m_axe[wxJS_MAX_AXES];
+    int       m_buttons;
+    wxWindow* m_catchwin;
+    int       m_polling;
+
+    friend class wxJoystick;
+};
+
+//===========================================================================
+//  IMPLEMENTATION
+//===========================================================================
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// wxGetIntFromCFDictionary
+//
+// Helper function that gets a integer from a dictionary key
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+void wxGetIntFromCFDictionary(CFTypeRef cfDict, CFStringRef key, int* pOut)
+{
+        CFNumberGetValue(
+          (CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) cfDict,
+                                              key),
+                        kCFNumberIntType, pOut);
+}
+
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+//
+// wxJoystick
+//
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject)
+
+//---------------------------------------------------------------------------
+// wxJoystick Constructor
+//
+// 1) Initializes member variables
+// 2) Attempts to create the native HID joystick implementation - if none
+//    could be found (no joysticks, etc.) then it sets it to NULL
+//---------------------------------------------------------------------------
+wxJoystick::wxJoystick(int joystick)
+    : m_joystick(joystick),
+      m_thread(NULL)
+{
+    m_hid = new wxHIDJoystick();
+
+    if (m_hid->Create(m_joystick+1)) //wxHIDDevice is 1-based while this is 0
+    {
+        m_thread = new wxJoystickThread(m_hid, m_joystick);
+        m_thread->Create();
+        m_thread->Run();
+    }
+    else
+    {
+        delete m_hid;
+        m_hid = NULL;
+    }
+}
+
+//---------------------------------------------------------------------------
+// wxJoystick Destructor
+//
+// Releases the capture of the thread, deletes it, and deletes
+// the native implementation.
+//---------------------------------------------------------------------------
+wxJoystick::~wxJoystick()
+{
+    ReleaseCapture();
+    if (m_thread)
+        m_thread->Delete();  // It's detached so it will delete itself
+
+    if (m_hid)
+        delete m_hid;
+}
+
+//---------------------------------------------------------------------------
+// wxJoystick::Get[XXX]Position
+//
+// Returns the value of an axis that was polled from the thread. In the
+// case of GetPosition returns the X and Y values in a wxPoint
+//---------------------------------------------------------------------------
+wxPoint wxJoystick::GetPosition() const
+{
+    wxPoint pos(wxDefaultPosition);
+    if (m_thread) pos = m_thread->m_lastposition;
+    return pos;
+}
+int wxJoystick::GetZPosition() const
+{
+    if (m_thread)
+        return m_thread->m_axe[wxJS_AXIS_Z];
+    return 0;
+}
+int wxJoystick::GetRudderPosition() const
+{
+    if (m_thread)
+        return m_thread->m_axe[wxJS_AXIS_RUDDER];
+    return 0;
+}
+int wxJoystick::GetUPosition() const
+{
+    if (m_thread)
+        return m_thread->m_axe[wxJS_AXIS_U];
+    return 0;
+}
+int wxJoystick::GetVPosition() const
+{
+    if (m_thread)
+        return m_thread->m_axe[wxJS_AXIS_V];
+    return 0;
+}
+
+//---------------------------------------------------------------------------
+// wxJoystick::GetButtonState
+//
+// Returns the state of the buttons in a bitmask as dictated by the
+// wx manual (the real work takes place in the thread, as always)
+//---------------------------------------------------------------------------
+int wxJoystick::GetButtonState() const
+{
+    if (m_thread)
+        return m_thread->m_buttons;
+    return 0;
+}
+
+//---------------------------------------------------------------------------
+// wxJoystick::IsOk
+//
+// Returns whether the joystick initialized successfully - in this case
+// if the native implementation doesn't exist (in constructor)
+//---------------------------------------------------------------------------
+bool wxJoystick::IsOk() const
+{
+    return m_hid != NULL;
+}
+
+//---------------------------------------------------------------------------
+// wxJoystick::Get[XXX](Id/Name)
+//
+// Simple accessors to the native HID implementation
+//---------------------------------------------------------------------------
+int wxJoystick::GetManufacturerId() const
+{
+    return m_hid->m_nManufacturerId;
+}
+
+int wxJoystick::GetProductId() const
+{
+    return m_hid->m_nProductId;
+}
+
+wxString wxJoystick::GetProductName() const
+{
+    return m_hid->m_szProductName;
+}
+
+//---------------------------------------------------------------------------
+// wxJoystick::GetNumberButtons
+// wxJoystick::GetNumberAxes
+//
+// Queries the joystick for an active number of buttons/axes.
+//
+// In the native HID implementation, the cookies:
+// 0-40     are the buttons of the joystick
+// 40-50    are the axes of the joystick
+//
+// These just query the native HID implementation as above.
+//---------------------------------------------------------------------------
+int wxJoystick::GetNumberButtons() const
+{
+    int nCount = 0;
+
+    for(int nIndex = 0; nIndex < 40; ++nIndex)
+    {
+        if(m_hid->HasElement(nIndex))
+            ++nCount;
+    }
+
+    return nCount;
+}
+int wxJoystick::GetNumberAxes() const
+{
+    int nCount = 0;
+
+    for(int nIndex = 40; nIndex < 50; ++nIndex)
+    {
+        if(m_hid->HasElement(nIndex))
+            ++nCount;
+    }
+
+    return nCount;
+}
+
+//---------------------------------------------------------------------------
+// wxJoystick::GetNumberJoysticks
+//
+// Gets the number of joysticks on the system. In HID that
+// is all devices with the kHIDUsage_GD_Joystick or kHIDUsage_GD_GamePad
+// identifiers.
+//---------------------------------------------------------------------------
+int wxJoystick::GetNumberJoysticks()
+{
+    return
+        wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick) +
+        wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
+}
+
+//---------------------------------------------------------------------------
+// wxJoystick::SetCapture
+//
+// Stops sending events from the thread to the window set in
+// SetCapture and stops polling the joystick
+//---------------------------------------------------------------------------
+bool wxJoystick::SetCapture(wxWindow* win, int pollingFreq)
+{
+    if (m_thread)
+    {
+        m_thread->m_catchwin = win;
+        m_thread->m_polling = pollingFreq;
+        return true;
+    }
+    return false;
+}
+
+//---------------------------------------------------------------------------
+// wxJoystick::ReleaseCapture
+//
+// Stops sending events from the thread to the window set in
+// SetCapture and stops polling the joystick
+//---------------------------------------------------------------------------
+bool wxJoystick::ReleaseCapture()
+{
+    if (m_thread)
+    {
+        m_thread->m_catchwin = NULL;
+        m_thread->m_polling = 0;
+        return true;
+    }
+    return false;
+}
+
+//---------------------------------------------------------------------------
+// wxJoystick::Get[XXX]
+//
+// Gets the minimum and maximum values for each axis, returning 0 if the
+// axis doesn't exist.
+//---------------------------------------------------------------------------
+int wxJoystick::GetXMin() const
+{
+    return m_hid->m_nXMin;
+}
+
+int wxJoystick::GetYMin() const
+{
+    return m_hid->m_nYMin;
+}
+
+int wxJoystick::GetZMin() const
+{
+    return m_hid->m_nZMin;
+}
+
+int wxJoystick::GetRudderMin() const
+{
+    return m_hid->m_nRudderMin;
+}
+
+int wxJoystick::GetUMin() const
+{
+    return m_hid->m_nUMin;
+}
+
+int wxJoystick::GetVMin() const
+{
+    return m_hid->m_nVMin;
+}
+
+int wxJoystick::GetXMax() const
+{
+    return m_hid->m_nXMax;
+}
+
+int wxJoystick::GetYMax() const
+{
+    return m_hid->m_nYMax;
+}
+
+int wxJoystick::GetZMax() const
+{
+    return m_hid->m_nZMax;
+}
+
+int wxJoystick::GetRudderMax() const
+{
+    return m_hid->m_nRudderMax;
+}
+
+int wxJoystick::GetUMax() const
+{
+    return m_hid->m_nUMax;
+}
+
+int wxJoystick::GetVMax() const
+{
+    return m_hid->m_nVMax;
+}
+
+//---------------------------------------------------------------------------
+// wxJoystick::Get[XXX]
+//
+// Min/Max values for buttons, axes, etc.. Polling in this case is just
+// what the linux port has.
+//---------------------------------------------------------------------------
+int wxJoystick::GetMaxButtons() const
+{
+    return wxJS_MAX_BUTTONS;
+}
+
+int wxJoystick::GetMaxAxes() const
+{
+    return wxJS_MAX_AXES;
+}
+
+int wxJoystick::GetPollingMin() const
+{
+    return 10;
+}
+
+int wxJoystick::GetPollingMax() const
+{
+    return 1000;
+}
+
+//---------------------------------------------------------------------------
+// wxJoystick::Has[XXX]
+//
+// Just queries the native hid implementation if the cookie was found
+// when enumerating the cookies of the joystick device
+//---------------------------------------------------------------------------
+bool wxJoystick::HasZ() const
+{
+    return m_hid->HasElement(wxJS_AXIS_Z);
+}
+
+bool wxJoystick::HasRudder() const
+{
+    return m_hid->HasElement(wxJS_AXIS_RUDDER);
+}
+
+bool wxJoystick::HasU() const
+{
+    return m_hid->HasElement(wxJS_AXIS_U);
+}
+
+bool wxJoystick::HasV() const
+{
+    return m_hid->HasElement(wxJS_AXIS_V);
+}
+
+//---------------------------------------------------------------------------
+// UNSUPPORTED
+//---------------------------------------------------------------------------
+int wxJoystick::GetPOVPosition() const
+{
+    return -1;
+}
+
+int wxJoystick::GetPOVCTSPosition() const
+{
+    return -1;
+}
+
+int wxJoystick::GetMovementThreshold() const
+{
+    return 0;
+}
+
+void wxJoystick::SetMovementThreshold(int WXUNUSED(threshold))
+{
+}
+
+bool wxJoystick::HasPOV() const
+{
+    return false;
+}
+
+bool wxJoystick::HasPOV4Dir() const
+{
+    return false;
+}
+
+bool wxJoystick::HasPOVCTS() const
+{
+    return false;
+}
+
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+//
+// wxHIDJoystick
+//
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+//---------------------------------------------------------------------------
+// wxHIDJoystick ctor
+//
+// Initializes the min/max members
+//---------------------------------------------------------------------------
+wxHIDJoystick::wxHIDJoystick() :
+ m_nXMax(0), m_nYMax(0), m_nZMax(0), m_nRudderMax(0), m_nUMax(0), m_nVMax(0),
+ m_nXMin(0), m_nYMin(0), m_nZMin(0), m_nRudderMin(0), m_nUMin(0), m_nVMin(0)
+{
+}
+
+//---------------------------------------------------------------------------
+// wxHIDJoystick dtor
+//
+// Nothing...
+//---------------------------------------------------------------------------
+wxHIDJoystick::~wxHIDJoystick()
+{
+}
+
+//---------------------------------------------------------------------------
+// wxHIDJoystick::Create
+//
+// Creates the native HID device (joysticks are of either
+// kHIDUsage_GD_Joystick or kHIDUsage_GD_GamePad)
+//---------------------------------------------------------------------------
+bool wxHIDJoystick::Create(int nWhich)
+{
+    int nJoysticks = GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
+
+    if (nWhich <= nJoysticks)
+        return wxHIDDevice::Create(kHIDPage_GenericDesktop,
+                                   kHIDUsage_GD_Joystick,
+                                   nWhich);
+    else
+        nWhich -= nJoysticks;
+
+    int nGamePads = GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
+
+    if (nWhich <= nGamePads)
+        return wxHIDDevice::Create(kHIDPage_GenericDesktop,
+                                   kHIDUsage_GD_GamePad,
+                                   nWhich);
+    else
+    return false;
+}
+
+//---------------------------------------------------------------------------
+// wxHIDJoystick::BuildCookies
+// wxHIDJoystick::MakeCookies
+//
+// Sets up the cookies for the HID device (called from Create) - as
+// mentioned 0-40 are the buttons and 40-50 are the axes.
+//
+// MakeCookies is just a recursive function for each array within
+// BuildCookies.
+//---------------------------------------------------------------------------
+void wxHIDJoystick::BuildCookies(CFArrayRef Array)
+{
+    InitCookies(50, true);
+
+    //
+    // I wasted two hours of my life on this line :(
+    // accidently removed it during some source cleaning...
+    //
+    MakeCookies(Array);
+
+    //paranoid debugging stuff
+#if 0
+    for(int i = 0; i < 50; ++i)
+        wxPrintf(wxT("\nVAL #%i:[%i]"), i, m_pCookies[i]);
+#endif
+}//end buildcookies
+
+void wxHIDJoystick::MakeCookies(CFArrayRef Array)
+{
+    int i, nUsage, nPage;
+
+    for (i = 0; i < CFArrayGetCount(Array); ++i)
+    {
+        const void* ref = CFDictionaryGetValue(
+                (CFDictionaryRef)CFArrayGetValueAtIndex(Array, i),
+                CFSTR(kIOHIDElementKey)
+                                              );
+
+        if (ref != NULL)
+        {
+            MakeCookies((CFArrayRef) ref);
+    }
+        else
+        {
+            CFNumberGetValue(
+                (CFNumberRef)
+                    CFDictionaryGetValue(
+                        (CFDictionaryRef) CFArrayGetValueAtIndex(Array, i),
+                        CFSTR(kIOHIDElementUsageKey)
+                                        ),
+                kCFNumberIntType,
+                &nUsage    );
+
+            CFNumberGetValue(
+                (CFNumberRef)
+                    CFDictionaryGetValue(
+                        (CFDictionaryRef) CFArrayGetValueAtIndex(Array, i),
+                        CFSTR(kIOHIDElementUsagePageKey)
+                                        ),
+                kCFNumberIntType,
+                &nPage     );
+
+#if 0
+            wxLogSysError(wxT("[%i][%i]"), nUsage, nPage);
+#endif
+            if (nPage == kHIDPage_Button && nUsage <= 40)
+                AddCookieInQueue(CFArrayGetValueAtIndex(Array, i), nUsage-1 );
+            else if (nPage == kHIDPage_GenericDesktop)
+            {
+                //axis...
+                switch(nUsage)
+                {
+                    case kHIDUsage_GD_X:
+                        AddCookieInQueue(CFArrayGetValueAtIndex(Array, i), wxJS_AXIS_X);
+                        wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i),
+                                                 CFSTR(kIOHIDElementMaxKey),
+                                                 &m_nXMax);
+                        wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i),
+                                                 CFSTR(kIOHIDElementMinKey),
+                                                 &m_nXMin);
+                        break;
+                    case kHIDUsage_GD_Y:
+                        AddCookieInQueue(CFArrayGetValueAtIndex(Array, i), wxJS_AXIS_Y);
+                        wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i),
+                                                 CFSTR(kIOHIDElementMaxKey),
+                                                 &m_nYMax);
+                        wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i),
+                                                 CFSTR(kIOHIDElementMinKey),
+                                                 &m_nYMin);
+                        break;
+                    case kHIDUsage_GD_Z:
+                        AddCookieInQueue(CFArrayGetValueAtIndex(Array, i), wxJS_AXIS_Z);
+                        wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i),
+                                                 CFSTR(kIOHIDElementMaxKey),
+                                                 &m_nZMax);
+                        wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i),
+                                                 CFSTR(kIOHIDElementMinKey),
+                                                 &m_nZMin);
+                        break;
+                    default:
+                        break;
+                }
+            }
+            else if (nPage == kHIDPage_Simulation && nUsage == kHIDUsage_Sim_Rudder)
+            {
+                //rudder...
+                AddCookieInQueue(CFArrayGetValueAtIndex(Array, i), wxJS_AXIS_RUDDER );
+                wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i),
+                                         CFSTR(kIOHIDElementMaxKey),
+                                         &m_nRudderMax);
+                wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i),
+                                         CFSTR(kIOHIDElementMinKey),
+                                         &m_nRudderMin);
+            }
+        }
+    }
+}
+
+//---------------------------------------------------------------------------
+// wxHIDJoystick::Get[XXX]
+//
+// Simple accessors so that the HID callback and the thread procedure
+// can access members from wxHIDDevice (our parent here).
+//---------------------------------------------------------------------------
+IOHIDElementCookie* wxHIDJoystick::GetCookies()
+{   return m_pCookies;  }
+IOHIDQueueInterface** wxHIDJoystick::GetQueue()
+{   return m_ppQueue;   }
+
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+//
+// wxJoystickThread
+//
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+//---------------------------------------------------------------------------
+// wxJoystickThread Constructor
+//
+// Just initializes members
+//---------------------------------------------------------------------------
+wxJoystickThread::wxJoystickThread(wxHIDJoystick* hid, int joystick)
+    : m_hid(hid),
+      m_joystick(joystick),
+      m_lastposition(127,127),
+      m_buttons(0),
+      m_catchwin(NULL),
+      m_polling(0)
+{
+    memset(m_axe, 0, sizeof(int) * wxJS_MAX_AXES);
+}
+
+//---------------------------------------------------------------------------
+// wxJoystickThread::Entry
+//
+// Thread procedure
+//
+// Runs a CFRunLoop for polling. Basically, it sets the HID queue to
+// call wxJoystickThread::HIDCallback in the context of this thread
+// when something changes on the device. It polls as long as the user
+// wants, or a certain amount if the user wants to "block". Note that
+// we don't actually block here since this is in a secondary thread.
+//---------------------------------------------------------------------------
+void* wxJoystickThread::Entry()
+{
+    CFRunLoopSourceRef pRLSource = NULL;
+
+    if ((*m_hid->GetQueue())->createAsyncEventSource(
+                    m_hid->GetQueue(), &pRLSource) != kIOReturnSuccess )
+    {
+        wxLogSysError(wxT("Couldn't create async event source"));
+        return NULL;
+    }
+
+    wxASSERT(pRLSource != NULL);
+
+    //attach runloop source to main run loop in thread
+    CFRunLoopRef pRL = CFRunLoopGetCurrent();
+    CFRunLoopAddSource(pRL, pRLSource, kCFRunLoopDefaultMode);
+    wxASSERT( CFRunLoopContainsSource(pRL, pRLSource, kCFRunLoopDefaultMode) );
+
+
+    if( (*m_hid->GetQueue())->setEventCallout(m_hid->GetQueue(),
+          wxJoystickThread::HIDCallback, this, this) != kIOReturnSuccess )
+    {
+        wxLogSysError(wxT("Could not set event callout for queue"));
+        return NULL;
+    }
+
+    if( (*m_hid->GetQueue())->start(m_hid->GetQueue()) != kIOReturnSuccess )
+    {
+        wxLogSysError(wxT("Could not start queue"));
+        return NULL;
+    }
+
+    double dTime;
+
+    while(true)
+    {
+        if (TestDestroy())
+            break;
+
+        if (m_polling)
+            dTime = 0.0001 * m_polling;
+        else
+            dTime = 0.0001 * 10;  // check at least every 10 msec in "blocking" case
+
+        //true just "handles and returns" - false forces it to stay the time
+        //amount
+#if 1
+        CFRunLoopRunInMode(kCFRunLoopDefaultMode, dTime, true);
+#else
+        IOReturn ret = NULL;
+        HIDCallback(this, ret, this, this);
+        Sleep(3000);
+#endif
+    }
+
+    wxASSERT( CFRunLoopContainsSource(pRL, pRLSource, kCFRunLoopDefaultMode) );
+
+    CFRunLoopRemoveSource(pRL, pRLSource, kCFRunLoopDefaultMode);
+    CFRelease(pRLSource);
+
+    return NULL;
+}
+
+//---------------------------------------------------------------------------
+// wxJoystickThread::HIDCallback (static)
+//
+// Callback for the native HID device when it recieves input.
+//
+// This is where the REAL dirty work gets done.
+//
+// 1) Loops through each event the queue has recieved
+// 2) First, checks if the thread that is running the loop for
+//    the polling has ended - if so it breaks out
+// 3) Next, it checks if there was an error getting this event from
+//    the HID queue, if there was, it logs an error and returns
+// 4) Now it does the real dirty work by getting the button states
+//    from cookies 0-40 and axes positions/states from cookies 40-50
+//    in the native HID device by quering cookie values.
+// 5) Sends the event to the polling window (if any)
+// 6) Gets the next event and goes back to (1)
+//---------------------------------------------------------------------------
+/*static*/ void wxJoystickThread::HIDCallback(void* WXUNUSED(target),
+                                              IOReturn WXUNUSED(res),
+                                              void* context,
+                                              void* WXUNUSED(sender))
+{
+    IOHIDEventStruct hidevent;
+    AbsoluteTime bogustime = {0,0};
+    IOReturn ret;
+    wxJoystickThread* pThis = (wxJoystickThread*) context;
+    wxHIDJoystick* m_hid = pThis->m_hid;
+
+    //Get the "first" event from the queue
+    //bogustime tells it we don't care at what time to start
+    //where it gets the next from
+    ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(),
+                    &hidevent, bogustime, 0);
+
+    while (ret != kIOReturnUnderrun)
+    {
+        if (pThis->TestDestroy())
+            break;
+
+        if(ret != kIOReturnSuccess)
+        {
+            wxLogSysError(wxString::Format(wxT("wxJoystick Error:[%i]"), ret));
+            return;
+        }
+
+        wxJoystickEvent wxevent;
+
+        //Find the cookie that changed
+        int nIndex = 0;
+        IOHIDElementCookie* pCookies = m_hid->GetCookies();
+        while(nIndex < 50)
+        {
+            if(hidevent.elementCookie == pCookies[nIndex])
+                break;
+
+            ++nIndex;
+        }
+
+        //debugging stuff
+#if 0
+        if(nIndex == 50)
+        {
+            wxLogSysError(wxString::Format(wxT("wxJoystick Out Of Bounds Error")));
+            break;
+        }
+#endif
+
+        //is the cookie a button?
+        if (nIndex < 40)
+        {
+            if (hidevent.value)
+            {
+                pThis->m_buttons |= (1 << nIndex);
+                wxevent.SetEventType(wxEVT_JOY_BUTTON_DOWN);
+            }
+            else
+            {
+                pThis->m_buttons &= ~(1 << nIndex);
+                wxevent.SetEventType(wxEVT_JOY_BUTTON_UP);
+            }
+
+            wxevent.SetButtonChange(nIndex+1);
+        }
+        else if (nIndex == wxJS_AXIS_X)
+        {
+            pThis->m_lastposition.x = hidevent.value;
+            wxevent.SetEventType(wxEVT_JOY_MOVE);
+            pThis->m_axe[0] = hidevent.value;
+        }
+        else if (nIndex == wxJS_AXIS_Y)
+        {
+            pThis->m_lastposition.y = hidevent.value;
+            wxevent.SetEventType(wxEVT_JOY_MOVE);
+            pThis->m_axe[1] = hidevent.value;
+        }
+        else if (nIndex == wxJS_AXIS_Z)
+        {
+            wxevent.SetEventType(wxEVT_JOY_ZMOVE);
+            pThis->m_axe[2] = hidevent.value;
+        }
+        else
+            wxevent.SetEventType(wxEVT_JOY_MOVE);
+
+        Nanoseconds timestamp = AbsoluteToNanoseconds(hidevent.timestamp);
+
+        wxULongLong llTime(timestamp.hi, timestamp.lo);
+
+        llTime /= 1000000;
+
+        wxevent.SetTimestamp(llTime.GetValue());
+        wxevent.SetJoystick(pThis->m_joystick);
+        wxevent.SetButtonState(pThis->m_buttons);
+        wxevent.SetPosition(pThis->m_lastposition);
+        wxevent.SetZPosition(pThis->m_axe[2]);
+        wxevent.SetEventObject(pThis->m_catchwin);
+
+        if (pThis->m_catchwin)
+            pThis->m_catchwin->AddPendingEvent(wxevent);
+
+        ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(),
+                    &hidevent, bogustime, 0);
+    }
+}
+
+#endif // wxUSE_JOYSTICK