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:
Vadim Zeitlin 2018-09-25 01:01:20 +02:00
parent 7872b9fd38
commit a02ed536e6
4 changed files with 152 additions and 113 deletions

View File

@ -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).

View File

@ -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
////////////////////////////////////////////////////////////////////////////
@ -85,6 +88,7 @@ public:
protected:
int m_joystick;
wxJoystickThread* m_thread;
};
#endif

View File

@ -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

View File

@ -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