X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/f6a9b35e1f57fce368c84657849b336e9e02d9a4..4cb1d3daa2e03b8c6481c9375d30451979170ba7:/src/mac/corefoundation/hidjoystick.cpp diff --git a/src/mac/corefoundation/hidjoystick.cpp b/src/mac/corefoundation/hidjoystick.cpp new file mode 100644 index 0000000000..eacceb0507 --- /dev/null +++ b/src/mac/corefoundation/hidjoystick.cpp @@ -0,0 +1,505 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: joystick.cpp +// Purpose: wxJoystick class +// Author: Ryan Norton +// Modified by: +// Created: 2/13/2005 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) +#pragma implementation "joystick.h" +#endif + +#include "wx/wxprec.h" + +#ifdef __DARWIN__ + +#if wxUSE_JOYSTICK + +#include "wx/event.h" +#include "wx/log.h" +#include "wx/joystick.h" +#include "wx/thread.h" +#include "wx/window.h" + +#include "wx/mac/corefoundation/hid.h" + +#include +#include +#include +#include + +enum { + wxJS_AXIS_X = 41, + wxJS_AXIS_Y, + wxJS_AXIS_Z, + wxJS_AXIS_RUDDER, + wxJS_AXIS_U, + wxJS_AXIS_V, + + wxJS_AXIS_MAX = 32767, + wxJS_AXIS_MIN = -32767 +}; + +class wxHIDJoystick : public wxHIDDevice +{ +public: + bool Create(int nWhich); + virtual void BuildCookies(wxCFArray& Array); + IOHIDElementCookie* GetCookies() {return m_pCookies;} + + IOHIDQueueInterface** GetQueue() {return m_ppQueue;} +}; + + +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 i, + nUsage, + nPage; + for (i = 0; i < Array.Count(); ++i) + { + 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 ); + 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 ); + } +}//end buildcookies + + + + +IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject) + + +//////////////////////////////////////////////////////////////////////////// +// Background thread for reading the joystick device +//////////////////////////////////////////////////////////////////////////// + +class wxJoystickThread : public wxThread +{ +public: + wxJoystickThread(wxHIDJoystick* hid, int joystick); + void* Entry(); + +private: + wxHIDJoystick* m_hid; + int m_joystick; + wxPoint m_lastposition; + int m_axe[15]; + int m_buttons; + wxWindow* m_catchwin; + int m_polling; + + friend class wxJoystick; +}; + + +wxJoystickThread::wxJoystickThread(wxHIDJoystick* hid, int joystick) + : m_hid(hid), + m_joystick(joystick), + m_lastposition(wxDefaultPosition), + 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() +{ + 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 ); + + double dTime; + IOHIDEventStruct hidevent; + AbsoluteTime bogustime = {0,0}; + IOReturn ret; + + 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, m_polling); + + while ( (ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(), + &hidevent, bogustime, 0)) != kIOReturnUnderrun ) + { + if (TestDestroy()) + break; + + wxJSASSERT(ret == kIOReturnSuccess); + wxJoystickEvent wxevent; + + int nIndex = 0; + IOHIDElementCookie* pCookies = m_hid->GetCookies(); + while(nIndex < 50) + { + if(hidevent.elementCookie == pCookies[nIndex]) + break; + } + wxASSERT(nIndex != 50); + + if (nIndex < 40) + { + if (hidevent.value) + { + m_buttons |= (1 << nIndex); + wxevent.SetEventType(wxEVT_JOY_BUTTON_DOWN); + } + else + { + m_buttons &= ~(1 << nIndex); + wxevent.SetEventType(wxEVT_JOY_BUTTON_UP); + } + + wxevent.SetButtonChange(nIndex); + } + else if (nIndex == wxJS_AXIS_X) + { + m_lastposition.x = hidevent.value; + wxevent.SetEventType(wxEVT_JOY_MOVE); + m_axe[nIndex - 39] = hidevent.value; + } + else if (nIndex == wxJS_AXIS_Y) + { + m_lastposition.y = hidevent.value; + wxevent.SetEventType(wxEVT_JOY_MOVE); + m_axe[nIndex - 39] = hidevent.value; + } + else if (nIndex == wxJS_AXIS_Z) + { + wxevent.SetEventType(wxEVT_JOY_ZMOVE); + m_axe[nIndex - 39] = 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(m_joystick); + wxevent.SetButtonState(m_buttons); + wxevent.SetPosition(m_lastposition); + wxevent.SetZPosition(m_axe[3]); + wxevent.SetEventObject(m_catchwin); + + if (m_catchwin) + m_catchwin->AddPendingEvent(wxevent); + } + } + return NULL; +} + + +//////////////////////////////////////////////////////////////////////////// + +wxJoystick::wxJoystick(int joystick) + : m_joystick(joystick), + m_thread(NULL) +{ + m_hid = new wxHIDJoystick(); + + if (m_hid->Create(m_joystick)) + { + m_thread = new wxJoystickThread(m_hid, m_joystick); + m_thread->Create(); + m_thread->Run(); + } + else + { + delete m_hid; + m_hid = NULL; + } +} + + +wxJoystick::~wxJoystick() +{ + ReleaseCapture(); + if (m_thread) + m_thread->Delete(); // It's detached so it will delete itself + + if (m_hid) + delete m_hid; +} + + +//////////////////////////////////////////////////////////////////////////// +// State +//////////////////////////////////////////////////////////////////////////// + +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::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 +{ + 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; +} + +int wxJoystick::GetMovementThreshold() const +{ return 0; } + +void wxJoystick::SetMovementThreshold(int threshold) +{ } + +//////////////////////////////////////////////////////////////////////////// +// Capabilities +//////////////////////////////////////////////////////////////////////////// + +bool wxJoystick::IsOk() const +{ return m_hid != NULL; } + +int wxJoystick::GetNumberJoysticks() const +{ return wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick) + + wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad); } + +int wxJoystick::GetManufacturerId() const +{ return 0; } + +int wxJoystick::GetProductId() const +{ return 0; } + +wxString wxJoystick::GetProductName() const +{ return wxT("unknown"); } + +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; } + +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; +} + +// +// internal +// +int wxJoystick::GetMaxButtons() const +{ return 15; } + +int wxJoystick::GetMaxAxes() const +{ return 10; } + +int wxJoystick::GetPollingMin() const +{ return 10; } + +int wxJoystick::GetPollingMax() const +{ return 1000; } + +int wxJoystick::GetRudderMin() const +{ return wxJS_AXIS_MIN; } + +int wxJoystick::GetRudderMax() const +{ return wxJS_AXIS_MAX; } + +int wxJoystick::GetUMin() const +{ return wxJS_AXIS_MIN; } + +int wxJoystick::GetUMax() const +{ return wxJS_AXIS_MAX; } + +int wxJoystick::GetVMin() const +{ return wxJS_AXIS_MIN; } + +int wxJoystick::GetVMax() const +{ return wxJS_AXIS_MAX; } + +bool wxJoystick::HasRudder() const +{ return m_hid->HasElement(wxJS_AXIS_RUDDER); } + +bool wxJoystick::HasZ() const +{ return m_hid->HasElement(wxJS_AXIS_Z); } + +bool wxJoystick::HasU() const +{ return m_hid->HasElement(wxJS_AXIS_U); } + +bool wxJoystick::HasV() const +{ return m_hid->HasElement(wxJS_AXIS_V); } + +bool wxJoystick::HasPOV() const +{ return false; } + +bool wxJoystick::HasPOV4Dir() const +{ return false; } + +bool wxJoystick::HasPOVCTS() const +{ return false; } + +//////////////////////////////////////////////////////////////////////////// +// Operations +//////////////////////////////////////////////////////////////////////////// + +bool wxJoystick::SetCapture(wxWindow* win, int pollingFreq) +{ + if (m_thread) + { + m_thread->m_catchwin = win; + m_thread->m_polling = pollingFreq; + return true; + } + return false; +} + +bool wxJoystick::ReleaseCapture() +{ + if (m_thread) + { + m_thread->m_catchwin = NULL; + m_thread->m_polling = 0; + return true; + } + return false; +} +#endif + //OSX + +#endif + // wxUSE_JOYSTICK +