diff --git a/docs/changes.txt b/docs/changes.txt index cbf1c9cf12..3d97b8f6db 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -141,6 +141,7 @@ wxMSW: - Add experimental support for Windows 10/ARM64 platform (Simon Rozman). - Fix hang after clearing wxTAB_TRAVERSAL style on a window with children. - Fix handling of AUX2 mouse button events (Timon Rozmanrylz). +- Implement support for more than 4 joystick buttons (Mick Phillips). - Fix saving/restoring window position for maximized windows. - Fix stack corruption when using wxStackWalker (srfisk). - Fix positioning windows at positions >= SHORT_MAX (Cătălin Răceanu). diff --git a/include/wx/msw/joystick.h b/include/wx/msw/joystick.h index 09c8bcb7c8..bd5fdbf5b4 100644 --- a/include/wx/msw/joystick.h +++ b/include/wx/msw/joystick.h @@ -13,6 +13,8 @@ #include "wx/event.h" +class wxJoystickThread; + class WXDLLIMPEXP_ADV wxJoystick: public wxObject { wxDECLARE_DYNAMIC_CLASS(wxJoystick); @@ -22,6 +24,7 @@ public: */ wxJoystick(int joystick = wxJOYSTICK1); + virtual ~wxJoystick(); // Attributes //////////////////////////////////////////////////////////////////////////// @@ -84,7 +87,8 @@ public: bool ReleaseCapture(void); protected: - int m_joystick; + int m_joystick; + wxJoystickThread* m_thread; }; #endif diff --git a/src/msw/joystick.cpp b/src/msw/joystick.cpp index b85db51640..3290460ab6 100644 --- a/src/msw/joystick.cpp +++ b/src/msw/joystick.cpp @@ -37,8 +37,129 @@ #include +// Use optimised count trailing zeros where available. +static int wxCtz(unsigned x) +{ + wxCHECK_MSG(x > 0, 0, "Undefined for x == 0."); +#ifdef __GNUC__ + return __builtin_ctz(x); +#else + int n; + n = 1; + if ((x & 0x0000FFFF) == 0) {n = n +16; x = x >>16;} + if ((x & 0x000000FF) == 0) {n = n + 8; x = x >> 8;} + if ((x & 0x0000000F) == 0) {n = n + 4; x = x >> 4;} + if ((x & 0x00000003) == 0) {n = n + 2; x = x >> 2;} + return n - (x & 1); +#endif +} + + +enum { + wxJS_AXIS_X = 0, + wxJS_AXIS_Y, + wxJS_AXIS_Z, + wxJS_AXIS_RUDDER, + wxJS_AXIS_U, + wxJS_AXIS_V, + + wxJS_AXIS_MAX = 32767, + wxJS_AXIS_MIN = -32767, + wxJS_MAX_AXES = 6, // WinMM supports up to 6 axes. + wxJS_MAX_BUTTONS = 32, // WinMM supports up to 32 buttons. +}; + wxIMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject); +//////////////////////////////////////////////////////////////////////////// +// Background thread for reading the joystick device +//////////////////////////////////////////////////////////////////////////// + +class wxJoystickThread : public wxThread +{ +public: + explicit wxJoystickThread(int joystick); + void* Entry() wxOVERRIDE; + void SetPolling(wxWindow* win, int pollingFreq) + { + m_catchwin = win; + m_polling = pollingFreq; + }; + + +private: + void SendEvent(wxEventType type, long ts, int change = 0); + int m_joystick; + UINT m_buttons; + wxWindow* m_catchwin; + int m_polling; + JOYINFO m_joyInfo; + JOYINFO m_lastJoyInfo; +}; + + +wxJoystickThread::wxJoystickThread(int joystick) + : m_joystick(joystick), + m_buttons(0), + m_catchwin(NULL), + m_polling(0), + m_joyInfo(), + m_lastJoyInfo() +{ +} + +void wxJoystickThread::SendEvent(wxEventType type, long ts, int change) +{ + wxJoystickEvent joystickEvent(type, (int)m_buttons, m_joystick, change); + + joystickEvent.SetTimestamp(ts); + joystickEvent.SetPosition(wxPoint( (int)m_joyInfo.wXpos, (int)m_joyInfo.wYpos) ); + joystickEvent.SetZPosition( (int)m_joyInfo.wZpos ); + joystickEvent.SetEventObject(m_catchwin); + + if (m_catchwin) + m_catchwin->GetEventHandler()->ProcessThreadEvent(joystickEvent); +} + +void* wxJoystickThread::Entry() +{ + joyGetPos(m_joystick, &m_lastJoyInfo); + + while (!TestDestroy()) + { + Sleep(m_polling); + long ts = GetTickCount(); + + joyGetPos(m_joystick, &m_joyInfo); + m_buttons = m_joyInfo.wButtons; + UINT delta = m_buttons ^ m_lastJoyInfo.wButtons; + UINT deltaUp = delta & !m_buttons; + UINT deltaDown = delta & m_buttons; + + // Use count trailing zeros to determine which button changed. + // Was using JOYINFOEX.dwButtons, because the docs state this is + // "Current button number that is pressed.", but it turns out + // it is the *total* number of buttons pressed. + if (deltaUp) + SendEvent(wxEVT_JOY_BUTTON_UP, ts, wxCtz(deltaUp)+1); + if (deltaDown) + SendEvent(wxEVT_JOY_BUTTON_DOWN, ts, wxCtz(deltaDown)+1); + + if ((m_joyInfo.wXpos != m_lastJoyInfo.wXpos) || + (m_joyInfo.wYpos != m_lastJoyInfo.wYpos) || + (m_joyInfo.wZpos != m_lastJoyInfo.wZpos) ) + { + SendEvent(wxEVT_JOY_MOVE, ts); + } + + m_lastJoyInfo = m_joyInfo; + } + + return NULL; +} + + + // Attributes //////////////////////////////////////////////////////////////////////////// @@ -62,6 +183,8 @@ wxJoystick::wxJoystick(int joystick) { /* Found the one we want, store actual OS id and return */ m_joystick = i; + m_thread = new wxJoystickThread(m_joystick); + m_thread->Run(); return; } joystick --; @@ -73,6 +196,15 @@ wxJoystick::wxJoystick(int joystick) return; } + +wxJoystick::~wxJoystick() +{ + ReleaseCapture(); + if (m_thread) + m_thread->Delete(); // It's detached so it will delete itself +} + + wxPoint wxJoystick::GetPosition() const { JOYINFO joyInfo; @@ -256,7 +388,7 @@ int wxJoystick::GetMovementThreshold() const MMRESULT res = joyGetThreshold(m_joystick, & thresh); if (res == JOYERR_NOERROR ) { - return thresh; + return (int)thresh; } else return 0; @@ -264,8 +396,7 @@ int wxJoystick::GetMovementThreshold() const void wxJoystick::SetMovementThreshold(int threshold) { - UINT thresh = threshold; - joySetThreshold(m_joystick, thresh); + joySetThreshold(m_joystick, (UINT)threshold); } // Capabilities @@ -578,26 +709,28 @@ bool wxJoystick::HasPOVCTS() const return ((joyCaps.wCaps & JOYCAPS_POVCTS) == JOYCAPS_POVCTS); } +//////////////////////////////////////////////////////////////////////////// // Operations //////////////////////////////////////////////////////////////////////////// bool wxJoystick::SetCapture(wxWindow* win, int pollingFreq) { -#ifdef __WXMSW__ - BOOL changed = (pollingFreq == 0); - MMRESULT res = joySetCapture((HWND) win->GetHWND(), m_joystick, pollingFreq, changed); - return (res == JOYERR_NOERROR); -#else - wxUnusedVar(win); - wxUnusedVar(pollingFreq); + if (m_thread) + { + m_thread->SetPolling(win, pollingFreq); + return true; + } return false; -#endif } bool wxJoystick::ReleaseCapture() { - MMRESULT res = joyReleaseCapture(m_joystick); - return (res == JOYERR_NOERROR); + if (m_thread) + { + m_thread->SetPolling(NULL, 0); + return true; + } + return false; } #endif // wxUSE_JOYSTICK diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 52dbb45e08..83add1d1d8 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -3133,19 +3133,6 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result, } break; - case MM_JOY1MOVE: - case MM_JOY2MOVE: - case MM_JOY1ZMOVE: - case MM_JOY2ZMOVE: - case MM_JOY1BUTTONDOWN: - case MM_JOY2BUTTONDOWN: - case MM_JOY1BUTTONUP: - case MM_JOY2BUTTONUP: - processed = HandleJoystickEvent(message, - LOWORD(lParam), - HIWORD(lParam), - wParam); - break; case WM_COMMAND: { @@ -6162,92 +6149,6 @@ bool wxWindowMSW::HandleClipboardEvent(WXUINT nMsg) return HandleWindowEvent(evt); } -// --------------------------------------------------------------------------- -// joystick -// --------------------------------------------------------------------------- - -bool wxWindowMSW::HandleJoystickEvent(WXUINT msg, int x, int y, WXUINT flags) -{ - int change = 0; - if ( flags & JOY_BUTTON1CHG ) - change = wxJOY_BUTTON1; - if ( flags & JOY_BUTTON2CHG ) - change = wxJOY_BUTTON2; - if ( flags & JOY_BUTTON3CHG ) - change = wxJOY_BUTTON3; - if ( flags & JOY_BUTTON4CHG ) - change = wxJOY_BUTTON4; - - int buttons = 0; - if ( flags & JOY_BUTTON1 ) - buttons |= wxJOY_BUTTON1; - if ( flags & JOY_BUTTON2 ) - buttons |= wxJOY_BUTTON2; - if ( flags & JOY_BUTTON3 ) - buttons |= wxJOY_BUTTON3; - if ( flags & JOY_BUTTON4 ) - buttons |= wxJOY_BUTTON4; - - // the event ids aren't consecutive so we can't use table based lookup - int joystick; - wxEventType eventType; - switch ( msg ) - { - case MM_JOY1MOVE: - joystick = 1; - eventType = wxEVT_JOY_MOVE; - break; - - case MM_JOY2MOVE: - joystick = 2; - eventType = wxEVT_JOY_MOVE; - break; - - case MM_JOY1ZMOVE: - joystick = 1; - eventType = wxEVT_JOY_ZMOVE; - break; - - case MM_JOY2ZMOVE: - joystick = 2; - eventType = wxEVT_JOY_ZMOVE; - break; - - case MM_JOY1BUTTONDOWN: - joystick = 1; - eventType = wxEVT_JOY_BUTTON_DOWN; - break; - - case MM_JOY2BUTTONDOWN: - joystick = 2; - eventType = wxEVT_JOY_BUTTON_DOWN; - break; - - case MM_JOY1BUTTONUP: - joystick = 1; - eventType = wxEVT_JOY_BUTTON_UP; - break; - - case MM_JOY2BUTTONUP: - joystick = 2; - eventType = wxEVT_JOY_BUTTON_UP; - break; - - default: - wxFAIL_MSG(wxT("no such joystick event")); - - return false; - } - - wxJoystickEvent event(eventType, buttons, joystick, change); - if ( eventType == wxEVT_JOY_ZMOVE ) - event.SetZPosition(x); - else - event.SetPosition(wxPoint(x, y)); - event.SetEventObject(this); - - return HandleWindowEvent(event); -} // --------------------------------------------------------------------------- // scrolling