]> git.saurik.com Git - wxWidgets.git/blobdiff - src/mac/corefoundation/hidjoystick.cpp
correction to last commit: don't test unsetenv() return value, it's void under Darwin
[wxWidgets.git] / src / mac / corefoundation / hidjoystick.cpp
index 0216bf347c3c3c5969692de2a7c4bb317405f2b1..12d5b86ba92b0c8c80b7b672232d23f508db2282 100644 (file)
 /////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////
-// Name:        joystick.cpp
+// 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
 // Purpose:     wxJoystick class
 // Author:      Ryan Norton
 // Modified by:
 // Created:     2/13/2005
 // RCS-ID:      $Id$
 // Copyright:   (c) Ryan Norton
-// Licence:       wxWindows licence
+// Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
 /////////////////////////////////////////////////////////////////////////////
 
+//===========================================================================
+//  DECLARATIONS
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Pre-compiled header stuff
+//---------------------------------------------------------------------------
+
+// For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
 #include "wx/wxprec.h"
 
-#ifdef __DARWIN__
+//---------------------------------------------------------------------------
+// Guard
+//---------------------------------------------------------------------------
+
+//we only support HID on OSX (DARWIN), since it requires DARWIN...
+#if wxUSE_JOYSTICK && defined(__DARWIN__) && wxUSE_THREADS
+
+//---------------------------------------------------------------------------
+// Includes
+//---------------------------------------------------------------------------
 
 
-#if wxUSE_JOYSTICK
+#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/event.h"
-#include "wx/log.h"
-#include "wx/joystick.h"
-#include "wx/thread.h"
-#include "wx/window.h"
+#include "wx/joystick.h"    //...
+#include "wx/thread.h"      //wxThread for polling thread/ wxCriticalSection
 
 
-#include "wx/mac/corefoundation/hid.h"
+//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>
 
 #include <CoreServices/CoreServices.h>
 #include <mach/mach.h>
 #include <mach/mach_time.h>
 #include <unistd.h>
 
-enum {
+//---------------------------------------------------------------------------
+// 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,
     wxJS_AXIS_X = 40,
     wxJS_AXIS_Y,
     wxJS_AXIS_Z,
     wxJS_AXIS_RUDDER,
     wxJS_AXIS_U,
     wxJS_AXIS_V,
-
-    wxJS_AXIS_MAX = 255, //32767,
-    wxJS_AXIS_MIN = 0, //-32767
 };
 
 };
 
+//---------------------------------------------------------------------------
+// wxHIDJoystick
+//---------------------------------------------------------------------------
 class wxHIDJoystick : public wxHIDDevice
 {
 public:
 class wxHIDJoystick : public wxHIDDevice
 {
 public:
-       bool Create(int nWhich);
-       virtual void BuildCookies(wxCFArray& Array);
-       void MakeCookies(wxCFArray& Array);
-    IOHIDElementCookie* GetCookies() {return m_pCookies;}
-    IOHIDQueueInterface** GetQueue() {return m_ppQueue;}
-    
-    friend class wxJoystick;
-};
+    wxHIDJoystick();
+    virtual ~wxHIDJoystick();
 
 
+    bool Create(int nWhich);
+    virtual void BuildCookies(CFArrayRef Array);
+    void MakeCookies(CFArrayRef Array);
+    IOHIDElementCookie* GetCookies();
+    IOHIDQueueInterface** GetQueue();
 
 
-bool wxHIDJoystick::Create(int nWhich)
-{
-    int nJoysticks = GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
-    
-    if (nWhich <= nJoysticks)
-        return wxHIDDevice::Create(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
-    else
-        nWhich -= nJoysticks;
-    
-    int nGamePads = GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
-    
-    if (nWhich <= nGamePads)
-        return wxHIDDevice::Create(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
-    else
-        return false;
-}
-
-void wxHIDJoystick::BuildCookies(wxCFArray& Array)
-{
-       Array = CFDictionaryGetValue((CFDictionaryRef)Array[0], CFSTR(kIOHIDElementKey));
-       InitCookies(50, true);
+    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;
 
 
-    memset(m_pCookies, 0, sizeof(*m_pCookies) * 50);
-    MakeCookies(Array);
-    
-//    for(int i = 0; i < 50; ++i)
-//        wxPrintf(wxT("\nVAL #%i:[%i]"), i, m_pCookies[i]);
-}//end buildcookies
-
-void wxHIDJoystick::MakeCookies(wxCFArray& Array)
-{
-       int i,
-               nUsage,
-        nPage;
-       for (i = 0; i < Array.Count(); ++i)
-       {
-        const void* ref = CFDictionaryGetValue((CFDictionaryRef)Array[i], CFSTR(kIOHIDElementKey));
-
-//        wxPrintf(wxT("ELM\n"));
-        if (ref  != NULL)
-        {
-            wxCFArray newarray(ref);
-            MakeCookies(newarray);
-        }
-        else
-        {
-            CFNumberGetValue(
-                       (CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) Array[i], CFSTR(kIOHIDElementUsageKey)), 
-                               kCFNumberLongType, &nUsage);
-                       
-            CFNumberGetValue(
-                       (CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) Array[i], CFSTR(kIOHIDElementUsagePageKey)), 
-                               kCFNumberLongType, &nPage);
-
-            if (nPage == kHIDPage_Button && nUsage <= 40)
-                AddCookieInQueue(Array[i], nUsage-1 );
-            else if (nPage == kHIDPage_GenericDesktop)
-            {
-                switch(nUsage)
-                {
-                    case kHIDUsage_GD_X:
-                        AddCookieInQueue(Array[i], wxJS_AXIS_X);
-                        break;                    
-                    case kHIDUsage_GD_Y:
-                        AddCookieInQueue(Array[i], wxJS_AXIS_Y);
-                        break;
-                    case kHIDUsage_GD_Z:
-                        AddCookieInQueue(Array[i], wxJS_AXIS_Z);
-                        break;
-                    default:
-                        break;
-                }
-            }
-            else if (nPage == kHIDPage_Simulation && nUsage == kHIDUsage_Sim_Rudder)
-                AddCookieInQueue(Array[i], wxJS_AXIS_RUDDER );
-        }
-       }
-}
-
-
-
-IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject)
-
-
-////////////////////////////////////////////////////////////////////////////
-// Background thread for reading the joystick device
-////////////////////////////////////////////////////////////////////////////
+    friend class wxJoystick;
+};
 
 
+//---------------------------------------------------------------------------
+// wxJoystickThread
+//---------------------------------------------------------------------------
 class wxJoystickThread : public wxThread
 {
 public:
     wxJoystickThread(wxHIDJoystick* hid, int joystick);
     void* Entry();
 class wxJoystickThread : public wxThread
 {
 public:
     wxJoystickThread(wxHIDJoystick* hid, int joystick);
     void* Entry();
+    static void HIDCallback(void* target, IOReturn res, void* context, void* sender);
 
 
-    static void HIDCallback(void* target, IOReturn res, void* context, void* sender)
-    {
-        IOHIDEventStruct hidevent;
-        AbsoluteTime bogustime = {0,0};
-        IOReturn ret;
-        wxJoystickThread* pThis = (wxJoystickThread*) context;
-        wxHIDJoystick* m_hid = pThis->m_hid;
-        
-//        wxMutexGuiEnter();
-        ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(), 
-                        &hidevent, bogustime, 0);
-  //      wxMutexGuiLeave(); 
-        while (    ret != kIOReturnUnderrun )
-        {
-            if (pThis->TestDestroy())
-                break;
-
-//            wxPrintf(wxT("ENTER\n"));
-            if(ret != kIOReturnSuccess)
-            {
-                wxLogSysError(wxString::Format(wxT("wxJoystick Error:[%i]"), ret));
-                return;
-            }  
-                
-            wxJoystickEvent wxevent;
-            
-            int nIndex = 0;
-            IOHIDElementCookie* pCookies = m_hid->GetCookies();
-            while(nIndex < 50)
-            {
-                if(hidevent.elementCookie == pCookies[nIndex])
-                    break;
-                    
-                ++nIndex;
-            } 
-            if(nIndex == 50)
-            {
-                wxLogSysError(wxString::Format(wxT("wxJoystick Out Of Bounds Error")));
-                break;
-            }  
-            
-            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);
-
-//            wxPrintf(wxT("SEND\n"));
-
-            if (pThis->m_catchwin)
-                pThis->m_catchwin->AddPendingEvent(wxevent);            
-
-         //   wxMutexGuiEnter();
-            ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(), 
-                        &hidevent, bogustime, 0);
-           // wxMutexGuiLeave(); 
-        }
-    }
-    
 private:
     wxHIDJoystick*       m_hid;
     int       m_joystick;
     wxPoint   m_lastposition;
 private:
     wxHIDJoystick*       m_hid;
     int       m_joystick;
     wxPoint   m_lastposition;
-    int       m_axe[15];
+    int       m_axe[wxJS_MAX_AXES];
     int       m_buttons;
     wxWindow* m_catchwin;
     int       m_polling;
     int       m_buttons;
     wxWindow* m_catchwin;
     int       m_polling;
@@ -260,70 +111,45 @@ private:
     friend class wxJoystick;
 };
 
     friend class wxJoystick;
 };
 
+//===========================================================================
+//  IMPLEMENTATION
+//===========================================================================
 
 
-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)
-{
-    for (int i=0; i<15; i++)
-        m_axe[i] = 0;
-}
-
-
-#      define wxJSVERIFY(arg)  if(!(arg)) {wxLogSysError(wxT(#arg)); return NULL;}
-#      define wxJSASSERT(arg)  wxJSVERIFY(arg)
-
-void* wxJoystickThread::Entry()
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// wxGetIntFromCFDictionary
+//
+// Helper function that gets a integer from a dictionary key
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+void wxGetIntFromCFDictionary(CFTypeRef cfDict, CFStringRef key, int* pOut)
 {
 {
-    CFRunLoopSourceRef pRLSource = NULL;
-
-    wxJSVERIFY( (*m_hid->GetQueue())->createAsyncEventSource(m_hid->GetQueue(), &pRLSource) 
-                        == kIOReturnSuccess );
-    wxJSASSERT(pRLSource != NULL);
-
-    //attach runloop source to main run loop in thread
-    CFRunLoopRef pRL = CFRunLoopGetCurrent();  
-    CFRunLoopAddSource(pRL, pRLSource, kCFRunLoopDefaultMode);
-      
-    wxJSVERIFY( (*m_hid->GetQueue())->start(m_hid->GetQueue()) == kIOReturnSuccess ); 
-    wxJSVERIFY( (*m_hid->GetQueue())->setEventCallout(m_hid->GetQueue(), &wxJoystickThread::HIDCallback, this, this) == kIOReturnSuccess ); 
-    
-    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
-
-        CFRunLoopRunInMode(kCFRunLoopDefaultMode, dTime, true);        
-    }
-    
-    wxJSASSERT( CFRunLoopContainsSource(pRL, pRLSource, kCFRunLoopDefaultMode) );
-    CFRunLoopRemoveSource(pRL, pRLSource, kCFRunLoopDefaultMode);
-    CFRelease(pRLSource);
-
-    return NULL;
+        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();
 
 wxJoystick::wxJoystick(int joystick)
     : m_joystick(joystick),
       m_thread(NULL)
 {
     m_hid = new wxHIDJoystick();
 
-    if (m_hid->Create(m_joystick))
+    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 = new wxJoystickThread(m_hid, m_joystick);
         m_thread->Create();
@@ -336,63 +162,52 @@ wxJoystick::wxJoystick(int joystick)
     }
 }
 
     }
 }
 
-
+//---------------------------------------------------------------------------
+// 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
 wxJoystick::~wxJoystick()
 {
     ReleaseCapture();
     if (m_thread)
         m_thread->Delete();  // It's detached so it will delete itself
-        
+
     if (m_hid)
         delete m_hid;
 }
 
     if (m_hid)
         delete m_hid;
 }
 
-
-////////////////////////////////////////////////////////////////////////////
-// State
-////////////////////////////////////////////////////////////////////////////
-
+//---------------------------------------------------------------------------
+// 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;
 }
 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::GetZPosition() const
 {
     if (m_thread)
         return m_thread->m_axe[wxJS_AXIS_Z];
     return 0;
 }
-
-int wxJoystick::GetButtonState() const
-{
-    if (m_thread)
-        return m_thread->m_buttons;
-    return 0;
-}
-
-int wxJoystick::GetPOVPosition() const
-{      return -1;                              }
-
-int wxJoystick::GetPOVCTSPosition() const
-{      return -1;                              }
-
 int wxJoystick::GetRudderPosition() const
 int wxJoystick::GetRudderPosition() const
-{      
+{
     if (m_thread)
         return m_thread->m_axe[wxJS_AXIS_RUDDER];
     return 0;
 }
     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::GetUPosition() const
 {
     if (m_thread)
         return m_thread->m_axe[wxJS_AXIS_U];
     return 0;
 }
-
 int wxJoystick::GetVPosition() const
 {
     if (m_thread)
 int wxJoystick::GetVPosition() const
 {
     if (m_thread)
@@ -400,158 +215,689 @@ int wxJoystick::GetVPosition() const
     return 0;
 }
 
     return 0;
 }
 
-int wxJoystick::GetMovementThreshold() const
-{      return 0;                               }
-
-void wxJoystick::SetMovementThreshold(int threshold)
-{                                                      }
-
-////////////////////////////////////////////////////////////////////////////
-// Capabilities
-////////////////////////////////////////////////////////////////////////////
+//---------------------------------------------------------------------------
+// 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
 bool wxJoystick::IsOk() const
-{      return m_hid != NULL;   }
+{
+    return m_hid != NULL;
+}
 
 
-int wxJoystick::GetNumberJoysticks() const
-{    return wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick) +
-            wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);   }
-    
+//---------------------------------------------------------------------------
+// wxJoystick::Get[XXX](Id/Name)
+//
+// Simple accessors to the native HID implementation
+//---------------------------------------------------------------------------
 int wxJoystick::GetManufacturerId() const
 int wxJoystick::GetManufacturerId() const
-{      return m_hid->m_nManufacturerId;                                }
+{
+    return m_hid->m_nManufacturerId;
+}
 
 int wxJoystick::GetProductId() const
 
 int wxJoystick::GetProductId() const
-{      return m_hid->m_nProductId;                             }
+{
+    return m_hid->m_nProductId;
+}
 
 wxString wxJoystick::GetProductName() const
 
 wxString wxJoystick::GetProductName() const
-{      return m_hid->m_szProductName;                          }
-
-int wxJoystick::GetXMin() const
-{      return wxJS_AXIS_MIN;   }
-
-int wxJoystick::GetYMin() const
-{      return wxJS_AXIS_MIN;   }
-
-int wxJoystick::GetZMin() const
-{      return wxJS_AXIS_MIN;   }
-
-int wxJoystick::GetXMax() const
-{      return wxJS_AXIS_MAX;   }
-
-int wxJoystick::GetYMax() const
-{      return wxJS_AXIS_MAX;   }
-
-int wxJoystick::GetZMax() const
-{      return wxJS_AXIS_MAX;   }
+{
+    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;
 int wxJoystick::GetNumberButtons() const
 {
     int nCount = 0;
-    
+
     for(int nIndex = 0; nIndex < 40; ++nIndex)
     {
         if(m_hid->HasElement(nIndex))
             ++nCount;
     }
     for(int nIndex = 0; nIndex < 40; ++nIndex)
     {
         if(m_hid->HasElement(nIndex))
             ++nCount;
     }
-    
+
     return nCount;
 }
     return nCount;
 }
-
 int wxJoystick::GetNumberAxes() const
 {
     int nCount = 0;
 int wxJoystick::GetNumberAxes() const
 {
     int nCount = 0;
-    
+
     for(int nIndex = 40; nIndex < 50; ++nIndex)
     {
         if(m_hid->HasElement(nIndex))
             ++nCount;
     }
     for(int nIndex = 40; nIndex < 50; ++nIndex)
     {
         if(m_hid->HasElement(nIndex))
             ++nCount;
     }
-    
+
     return nCount;
 }
 
     return nCount;
 }
 
+//---------------------------------------------------------------------------
+// wxJoystick::GetNumberJoysticks
 //
 //
-// internal
+// 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
 //
 //
-int wxJoystick::GetMaxButtons() const
-{      return 15;      }
+// 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;
+}
 
 
-int wxJoystick::GetMaxAxes() const
-{      return 10;      }
+//---------------------------------------------------------------------------
+// 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;
+}
 
 
-int wxJoystick::GetPollingMin() const
-{      return 10;      }
+//---------------------------------------------------------------------------
+// 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::GetPollingMax() const
-{      return 1000;    }
+int wxJoystick::GetYMin() const
+{
+    return m_hid->m_nYMin;
+}
 
 
-int wxJoystick::GetRudderMin() const
-{      return wxJS_AXIS_MIN;   }
+int wxJoystick::GetZMin() const
+{
+    return m_hid->m_nZMin;
+}
 
 
-int wxJoystick::GetRudderMax() const
-{      return wxJS_AXIS_MAX;   }
+int wxJoystick::GetRudderMin() const
+{
+    return m_hid->m_nRudderMin;
+}
 
 int wxJoystick::GetUMin() const
 
 int wxJoystick::GetUMin() const
-{      return wxJS_AXIS_MIN;   }
-
-int wxJoystick::GetUMax() const
-{      return wxJS_AXIS_MAX;   }
+{
+    return m_hid->m_nUMin;
+}
 
 int wxJoystick::GetVMin() const
 
 int wxJoystick::GetVMin() const
-{      return wxJS_AXIS_MIN;   }
+{
+    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
 
 int wxJoystick::GetVMax() const
-{      return wxJS_AXIS_MAX;   }
+{
+    return m_hid->m_nVMax;
+}
 
 
-bool wxJoystick::HasRudder() const
-{      return m_hid->HasElement(wxJS_AXIS_RUDDER);     }
+//---------------------------------------------------------------------------
+// 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
 bool wxJoystick::HasZ() const
-{      return m_hid->HasElement(wxJS_AXIS_Z);  }
+{
+    return m_hid->HasElement(wxJS_AXIS_Z);
+}
+
+bool wxJoystick::HasRudder() const
+{
+    return m_hid->HasElement(wxJS_AXIS_RUDDER);
+}
 
 bool wxJoystick::HasU() const
 
 bool wxJoystick::HasU() const
-{      return m_hid->HasElement(wxJS_AXIS_U);  }
+{
+    return m_hid->HasElement(wxJS_AXIS_U);
+}
 
 bool wxJoystick::HasV() const
 
 bool wxJoystick::HasV() const
-{      return m_hid->HasElement(wxJS_AXIS_V);  }
+{
+    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 threshold)
+{
+}
 
 bool wxJoystick::HasPOV() const
 
 bool wxJoystick::HasPOV() const
-{      return false;   }
+{
+    return false;
+}
 
 bool wxJoystick::HasPOV4Dir() const
 
 bool wxJoystick::HasPOV4Dir() const
-{      return false;   }
+{
+    return false;
+}
 
 bool wxJoystick::HasPOVCTS() const
 
 bool wxJoystick::HasPOVCTS() const
-{      return false;   }
+{
+    return false;
+}
 
 
-////////////////////////////////////////////////////////////////////////////
-// Operations
-////////////////////////////////////////////////////////////////////////////
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+//
+// wxHIDJoystick
+//
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 
-bool wxJoystick::SetCapture(wxWindow* win, int pollingFreq)
+//---------------------------------------------------------------------------
+// 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)
 {
 {
-    if (m_thread)
-    {
-        m_thread->m_catchwin = win;
-        m_thread->m_polling = pollingFreq;
-        return true;
-    }
+}
+
+//---------------------------------------------------------------------------
+// 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;
 }
 
     return false;
 }
 
-bool wxJoystick::ReleaseCapture()
+//---------------------------------------------------------------------------
+// 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)
 {
 {
-    if (m_thread)
+    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)
     {
     {
-        m_thread->m_catchwin = NULL;
-        m_thread->m_polling = 0;
-        return true;
+        const void* ref = CFDictionaryGetValue(
+                (CFDictionaryRef)CFArrayGetValueAtIndex(Array, i),
+                CFSTR(kIOHIDElementKey)
+                                              );
+
+        if (ref != NULL)
+        {
+            MakeCookies((CFArrayRef) ref);
     }
     }
-    return false;
+        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
 #endif
-    //OSX 
-    
+    }
+
+    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* target, IOReturn res,
+                                              void* context, void* 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
 #endif
-    // wxUSE_JOYSTICK
 
 
+        //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 && defined(__DARWIN__)