Add support for more than 4 joystick buttons under MSW
Use polling thread instead of relying on MM_JOYXXX events to allow receiving events from all the supported buttons. See https://github.com/wxWidgets/wxWidgets/pull/942 Closes #1142.
This commit is contained in:
		@@ -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).
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -37,8 +37,129 @@
 | 
			
		||||
 | 
			
		||||
#include <regstr.h>
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user