/////////////////////////////////////////////////////////////////////////////
-// Name: joystick.cpp
+// Name: src/unix/joystick.cpp
// Purpose: wxJoystick class
// Author: Ported to Linux by Guilhem Lavaux
// Modified by:
// Created: 05/23/98
-// RCS-ID: $Id$
// Copyright: (c) Guilhem Lavaux
-// Licence: wxWindows licence
+// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
-#pragma implementation "joystick.h"
-#endif
-
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
-#include "wx/defs.h"
-
#if wxUSE_JOYSTICK
#include "wx/joystick.h"
+#ifndef WX_PRECOMP
+ #include "wx/event.h"
+ #include "wx/window.h"
+ #include "wx/log.h"
+#endif //WX_PRECOMP
+
+#include "wx/thread.h"
+
#include <linux/joystick.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
-#include "wx/event.h"
-#include "wx/window.h"
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+
+#include "wx/unix/private.h"
-#define JOYSTICK_AXE_MAX 32767
-#define JOYSTICK_AXE_MIN -32767
+enum {
+ wxJS_AXIS_X = 0,
+ wxJS_AXIS_Y,
+ wxJS_AXIS_Z,
+ wxJS_AXIS_RUDDER,
+ wxJS_AXIS_U,
+ wxJS_AXIS_V,
-IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject)
+ wxJS_AXIS_MAX = 32767,
+ wxJS_AXIS_MIN = -32767,
+ wxJS_MAX_AXES = 15,
+ wxJS_MAX_BUTTONS = sizeof(int) * 8
+};
-wxJoystick::wxJoystick(int joystick)
-{
- wxString dev_name;
- // Assume it's the same device name on all Linux systems ...
- dev_name.Printf( wxT("/dev/js%d"), (joystick == wxJOYSTICK1) ? 0 : 1); // FIXME Unicode?
- m_joystick = open(dev_name.fn_str(), O_RDWR);
- m_lastposition = wxPoint(-1, -1);
- for (int i=0;i<15;i++)
- m_axe[i] = 0;
- if (m_joystick != -1)
- Create();
-}
+IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject)
+
////////////////////////////////////////////////////////////////////////////
-// Background thread
+// Background thread for reading the joystick device
////////////////////////////////////////////////////////////////////////////
-void *wxJoystick::Entry(void)
-{
- struct js_event j_evt;
- wxJoystickEvent jwx_event;
- fd_set read_fds;
- struct timeval time_out = {0, 0};
-
- FD_ZERO(&read_fds);
- while (1) {
- TestDestroy();
-
- if (m_polling) {
- FD_SET(m_joystick, &read_fds);
- select(m_joystick+1, &read_fds, NULL, NULL, &time_out);
- if (FD_ISSET(m_joystick, &read_fds))
- read(m_joystick, &j_evt, sizeof(j_evt));
- else
- j_evt.type = 0;
- } else {
- read(m_joystick, &j_evt, sizeof(j_evt));
+
+class wxJoystickThread : public wxThread
+{
+public:
+ wxJoystickThread(int device, int joystick);
+ void* Entry();
+
+private:
+ void SendEvent(wxEventType type, long ts, int change = 0);
+ int m_device;
+ int m_joystick;
+ wxPoint m_lastposition;
+ int m_axe[wxJS_MAX_AXES];
+ int m_buttons;
+ wxWindow* m_catchwin;
+ int m_polling;
+ int m_threshold;
+
+ friend class wxJoystick;
+};
+
+
+wxJoystickThread::wxJoystickThread(int device, int joystick)
+ : m_device(device),
+ m_joystick(joystick),
+ m_lastposition(wxDefaultPosition),
+ m_buttons(0),
+ m_catchwin(NULL),
+ m_polling(0),
+ m_threshold(0)
+{
+ memset(m_axe, 0, sizeof(m_axe));
+}
+
+void wxJoystickThread::SendEvent(wxEventType type, long ts, int change)
+{
+ wxJoystickEvent jwx_event(type, m_buttons, m_joystick, change);
+
+ jwx_event.SetTimestamp(ts);
+ jwx_event.SetPosition(m_lastposition);
+ jwx_event.SetZPosition(m_axe[wxJS_AXIS_Z]);
+ jwx_event.SetEventObject(m_catchwin);
+
+ if (m_catchwin)
+ m_catchwin->GetEventHandler()->AddPendingEvent(jwx_event);
+}
+
+void* wxJoystickThread::Entry()
+{
+ struct js_event j_evt;
+ fd_set read_fds;
+ struct timeval time_out = {0, 0};
+
+ wxFD_ZERO(&read_fds);
+ while (true)
+ {
+ if (TestDestroy())
+ break;
+
+ // We use select when either polling or 'blocking' as even in the
+ // blocking case we need to check TestDestroy periodically
+ if (m_polling)
+ time_out.tv_usec = m_polling * 1000;
+ else
+ time_out.tv_usec = 10 * 1000; // check at least every 10 msec in blocking case
+
+ wxFD_SET(m_device, &read_fds);
+ select(m_device+1, &read_fds, NULL, NULL, &time_out);
+ if (wxFD_ISSET(m_device, &read_fds))
+ {
+ memset(&j_evt, 0, sizeof(j_evt));
+ read(m_device, &j_evt, sizeof(j_evt));
+
+ //printf("time: %d\t value: %d\t type: %d\t number: %d\n",
+ // j_evt.time, j_evt.value, j_evt.type, j_evt.number);
+
+ if ((j_evt.type & JS_EVENT_AXIS) && (j_evt.number < wxJS_MAX_AXES))
+ {
+ // Ignore invalid axis.
+ if ( j_evt.number >= wxJS_MAX_AXES )
+ {
+ wxLogDebug(wxS("Invalid axis index %d in joystick message."),
+ j_evt.number);
+ continue;
+ }
+
+ if ( (m_axe[j_evt.number] + m_threshold < j_evt.value)
+ || (m_axe[j_evt.number] - m_threshold > j_evt.value) )
+ {
+ m_axe[j_evt.number] = j_evt.value;
+
+ switch (j_evt.number)
+ {
+ case wxJS_AXIS_X:
+ m_lastposition.x = j_evt.value;
+ SendEvent(wxEVT_JOY_MOVE, j_evt.time);
+ break;
+ case wxJS_AXIS_Y:
+ m_lastposition.y = j_evt.value;
+ SendEvent(wxEVT_JOY_MOVE, j_evt.time);
+ break;
+ case wxJS_AXIS_Z:
+ SendEvent(wxEVT_JOY_ZMOVE, j_evt.time);
+ break;
+ default:
+ SendEvent(wxEVT_JOY_MOVE, j_evt.time);
+ // TODO: There should be a way to indicate that the event
+ // is for some other axes.
+ break;
+ }
+ }
+ }
+
+ if ( (j_evt.type & JS_EVENT_BUTTON) && (j_evt.number < wxJS_MAX_BUTTONS) )
+ {
+ if (j_evt.value)
+ {
+ m_buttons |= (1 << j_evt.number);
+ SendEvent(wxEVT_JOY_BUTTON_DOWN, j_evt.time, j_evt.number);
+ }
+ else
+ {
+ m_buttons &= ~(1 << j_evt.number);
+ SendEvent(wxEVT_JOY_BUTTON_UP, j_evt.time, j_evt.number);
+ }
+ }
+ }
}
- if ((j_evt.type & JS_EVENT_AXIS) == JS_EVENT_AXIS) {
- switch (j_evt.number) {
- case 1:
- m_lastposition.x = j_evt.value;
- jwx_event.SetEventType(wxEVT_JOY_MOVE);
- break;
- case 2:
- m_lastposition.y = j_evt.value;
- jwx_event.SetEventType(wxEVT_JOY_MOVE);
- break;
- case 3:
- m_axe[3] = j_evt.value;
- jwx_event.SetEventType(wxEVT_JOY_ZMOVE);
- break;
- default:
- m_axe[j_evt.number] = j_evt.value;
- jwx_event.SetEventType(wxEVT_JOY_MOVE);
- break;
- }
- jwx_event.SetPosition(m_lastposition);
- jwx_event.SetZPosition(m_axe[3]);
+ close(m_device);
+ return NULL;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+wxJoystick::wxJoystick(int joystick)
+ : m_device(-1),
+ m_joystick(joystick),
+ m_thread(NULL)
+{
+ wxString dev_name;
+
+ // old /dev structure
+ dev_name.Printf( wxT("/dev/js%d"), joystick);
+ m_device = open(dev_name.fn_str(), O_RDONLY);
+
+ // new /dev structure with "input" subdirectory
+ if (m_device == -1)
+ {
+ dev_name.Printf( wxT("/dev/input/js%d"), joystick);
+ m_device = open(dev_name.fn_str(), O_RDONLY);
}
- if ((j_evt.type & JS_EVENT_BUTTON) == JS_EVENT_BUTTON) {
- register int mask = 1 << j_evt.number;
- char button = m_buttons & mask;
-
- m_buttons &= ~mask;
- if (button) {
- jwx_event.SetEventType(wxEVT_JOY_BUTTON_UP);
- } else {
- jwx_event.SetEventType(wxEVT_JOY_BUTTON_DOWN);
- m_buttons |= mask;
- }
-
- jwx_event.SetButtonState(m_buttons);
- jwx_event.SetButtonChange(j_evt.number);
+
+ if (m_device != -1)
+ {
+ m_thread = new wxJoystickThread(m_device, m_joystick);
+ m_thread->Create();
+ m_thread->Run();
}
- }
- if (m_catchwin)
- m_catchwin->ProcessEvent(jwx_event);
- if (m_polling)
- usleep(m_polling*1000);
}
+
+wxJoystick::~wxJoystick()
+{
+ ReleaseCapture();
+ if (m_thread)
+ m_thread->Delete(); // It's detached so it will delete itself
+ m_device = -1;
+}
+
+
////////////////////////////////////////////////////////////////////////////
// State
////////////////////////////////////////////////////////////////////////////
-wxPoint wxJoystick::GetPosition(void) const
+wxPoint wxJoystick::GetPosition() const
+{
+ wxPoint pos(wxDefaultPosition);
+ if (m_thread) pos = m_thread->m_lastposition;
+ return pos;
+}
+
+int wxJoystick::GetPosition(unsigned axis) const
+{
+ if (m_thread && (axis < wxJS_MAX_AXES))
+ return m_thread->m_axe[axis];
+ return 0;
+}
+
+int wxJoystick::GetZPosition() const
{
- return m_lastposition;
+ if (m_thread)
+ return m_thread->m_axe[wxJS_AXIS_Z];
+ return 0;
}
-int wxJoystick::GetZPosition(void) const
+int wxJoystick::GetButtonState() const
{
- return m_axe[3];
+ if (m_thread)
+ return m_thread->m_buttons;
+ return 0;
}
-int wxJoystick::GetButtonState(void) const
+bool wxJoystick::GetButtonState(unsigned id) const
{
- return m_buttons;
+ if (m_thread && (id < wxJS_MAX_BUTTONS))
+ return (m_thread->m_buttons & (1 << id)) != 0;
+ return false;
}
-int wxJoystick::GetPOVPosition(void) const
+int wxJoystick::GetPOVPosition() const
{
- return -1;
+ return -1;
}
-int wxJoystick::GetPOVCTSPosition(void) const
+int wxJoystick::GetPOVCTSPosition() const
{
- return -1;
+ return -1;
}
-int wxJoystick::GetRudderPosition(void) const
+int wxJoystick::GetRudderPosition() const
{
- return m_axe[4];
+ if (m_thread)
+ return m_thread->m_axe[wxJS_AXIS_RUDDER];
+ return 0;
}
-int wxJoystick::GetUPosition(void) const
+int wxJoystick::GetUPosition() const
{
- return m_axe[5];
+ if (m_thread)
+ return m_thread->m_axe[wxJS_AXIS_U];
+ return 0;
}
-int wxJoystick::GetVPosition(void) const
+int wxJoystick::GetVPosition() const
{
- return m_axe[6];
+ if (m_thread)
+ return m_thread->m_axe[wxJS_AXIS_V];
+ return 0;
}
-int wxJoystick::GetMovementThreshold(void) const
+int wxJoystick::GetMovementThreshold() const
{
- return 0;
+ if (m_thread)
+ return m_thread->m_threshold;
+ return 0;
}
void wxJoystick::SetMovementThreshold(int threshold)
{
+ if (m_thread)
+ m_thread->m_threshold = threshold;
}
////////////////////////////////////////////////////////////////////////////
// Capabilities
////////////////////////////////////////////////////////////////////////////
-bool wxJoystick::IsOk(void) const
+bool wxJoystick::IsOk() const
{
- return (m_joystick != -1);
+ return (m_device != -1);
}
-int wxJoystick::GetNumberJoysticks(void) const
+int wxJoystick::GetNumberJoysticks()
{
- wxString dev_name;
- int fd, j;
+ wxString dev_name;
+ int fd, j;
+
+ for (j=0; j<4; j++) {
+ dev_name.Printf(wxT("/dev/js%d"), j);
+ fd = open(dev_name.fn_str(), O_RDONLY);
+ if (fd == -1)
+ break;
+ close(fd);
+ }
- for (j=0;j<2;j++) {
- dev_name.Printf(wxT("/dev/js%d"), j);
- fd = open(dev_name.fn_str(), O_RDONLY);
- if (fd == -1)
- return j;
- close(fd);
- }
- return j;
+ if (j == 0) {
+ for (j=0; j<4; j++) {
+ dev_name.Printf(wxT("/dev/input/js%d"), j);
+ fd = open(dev_name.fn_str(), O_RDONLY);
+ if (fd == -1)
+ return j;
+ close(fd);
+ }
+ }
+
+ return j;
}
-int wxJoystick::GetManufacturerId(void) const
+int wxJoystick::GetManufacturerId() const
{
- return 0;
+ return 0;
}
-int wxJoystick::GetProductId(void) const
+int wxJoystick::GetProductId() const
{
- return 0;
+ return 0;
}
-wxString wxJoystick::GetProductName(void) const
+wxString wxJoystick::GetProductName() const
{
- wxString dev_name;
- // 2002-08-20 johan@linkdata.se
- // Return the device name in lieu of a better one
- dev_name.Printf( wxT("/dev/js%d"), (m_joystick == wxJOYSTICK1) ? 0 : 1); // FIXME Unicode?
- return dev_name;
+ char name[128];
+
+ if (ioctl(m_device, JSIOCGNAME(sizeof(name)), name) < 0)
+ strcpy(name, "Unknown");
+ return wxString(name, wxConvLibc);
}
-int wxJoystick::GetXMin(void) const
+int wxJoystick::GetXMin() const
{
- return JOYSTICK_AXE_MAX;
+ return wxJS_AXIS_MIN;
}
-int wxJoystick::GetYMin(void) const
+int wxJoystick::GetYMin() const
{
- return JOYSTICK_AXE_MAX;
+ return wxJS_AXIS_MIN;
}
-int wxJoystick::GetZMin(void) const
+int wxJoystick::GetZMin() const
{
- return JOYSTICK_AXE_MAX;
+ return wxJS_AXIS_MIN;
}
-int wxJoystick::GetXMax(void) const
+int wxJoystick::GetXMax() const
{
- return JOYSTICK_AXE_MAX;
+ return wxJS_AXIS_MAX;
}
-int wxJoystick::GetYMax(void) const
+int wxJoystick::GetYMax() const
{
- return JOYSTICK_AXE_MAX;
+ return wxJS_AXIS_MAX;
}
-int wxJoystick::GetZMax(void) const
+int wxJoystick::GetZMax() const
{
- return JOYSTICK_AXE_MAX;
+ return wxJS_AXIS_MAX;
}
-int wxJoystick::GetNumberButtons(void) const
+int wxJoystick::GetNumberButtons() const
{
- int nb;
+ char nb=0;
- ioctl(m_joystick, JSIOCGBUTTONS, &nb);
+ if (m_device != -1)
+ ioctl(m_device, JSIOCGBUTTONS, &nb);
- return nb;
+ if ((int)nb > wxJS_MAX_BUTTONS)
+ nb = wxJS_MAX_BUTTONS;
+
+ return nb;
}
-int wxJoystick::GetNumberAxes(void) const
+int wxJoystick::GetNumberAxes() const
{
- int nb;
+ char nb=0;
+
+ if (m_device != -1)
+ ioctl(m_device, JSIOCGAXES, &nb);
- ioctl(m_joystick, JSIOCGAXES, &nb);
+ if ((int)nb > wxJS_MAX_AXES)
+ nb = wxJS_MAX_AXES;
- return nb;
+ return nb;
}
-int wxJoystick::GetMaxButtons(void) const
+int wxJoystick::GetMaxButtons() const
{
- return 15; // internal
+ return wxJS_MAX_BUTTONS; // internal
}
-int wxJoystick::GetMaxAxes(void) const
+int wxJoystick::GetMaxAxes() const
{
- return 15; // internal
+ return wxJS_MAX_AXES; // internal
}
-int wxJoystick::GetPollingMin(void) const
+int wxJoystick::GetPollingMin() const
{
- return -1;
+ return 10;
}
-int wxJoystick::GetPollingMax(void) const
+int wxJoystick::GetPollingMax() const
{
- return -1;
+ return 1000;
}
-int wxJoystick::GetRudderMin(void) const
+int wxJoystick::GetRudderMin() const
{
- return JOYSTICK_AXE_MIN;
+ return wxJS_AXIS_MIN;
}
-int wxJoystick::GetRudderMax(void) const
+int wxJoystick::GetRudderMax() const
{
- return JOYSTICK_AXE_MAX;
+ return wxJS_AXIS_MAX;
}
-int wxJoystick::GetUMin(void) const
+int wxJoystick::GetUMin() const
{
- return JOYSTICK_AXE_MIN;
+ return wxJS_AXIS_MIN;
}
-int wxJoystick::GetUMax(void) const
+int wxJoystick::GetUMax() const
{
- return JOYSTICK_AXE_MAX;
+ return wxJS_AXIS_MAX;
}
-int wxJoystick::GetVMin(void) const
+int wxJoystick::GetVMin() const
{
- return JOYSTICK_AXE_MIN;
+ return wxJS_AXIS_MIN;
}
-int wxJoystick::GetVMax(void) const
+int wxJoystick::GetVMax() const
{
- return JOYSTICK_AXE_MAX;
+ return wxJS_AXIS_MAX;
}
-bool wxJoystick::HasRudder(void) const
+bool wxJoystick::HasRudder() const
{
- return GetNumberAxes() >= 4;
+ return GetNumberAxes() >= wxJS_AXIS_RUDDER;
}
-bool wxJoystick::HasZ(void) const
+bool wxJoystick::HasZ() const
{
- return GetNumberAxes() >= 3;
+ return GetNumberAxes() >= wxJS_AXIS_Z;
}
-bool wxJoystick::HasU(void) const
+bool wxJoystick::HasU() const
{
- return GetNumberAxes() >= 5;
+ return GetNumberAxes() >= wxJS_AXIS_U;
}
-bool wxJoystick::HasV(void) const
+bool wxJoystick::HasV() const
{
- return GetNumberAxes() >= 6;
+ return GetNumberAxes() >= wxJS_AXIS_V;
}
-bool wxJoystick::HasPOV(void) const
+bool wxJoystick::HasPOV() const
{
- return FALSE;
+ return false;
}
-bool wxJoystick::HasPOV4Dir(void) const
+bool wxJoystick::HasPOV4Dir() const
{
- return FALSE;
+ return false;
}
-bool wxJoystick::HasPOVCTS(void) const
+bool wxJoystick::HasPOVCTS() const
{
- return FALSE;
+ return false;
}
////////////////////////////////////////////////////////////////////////////
bool wxJoystick::SetCapture(wxWindow* win, int pollingFreq)
{
- m_catchwin = win;
- m_polling = pollingFreq;
- return TRUE;
+ if (m_thread)
+ {
+ m_thread->m_catchwin = win;
+ m_thread->m_polling = pollingFreq;
+ return true;
+ }
+ return false;
}
-bool wxJoystick::ReleaseCapture(void)
+bool wxJoystick::ReleaseCapture()
{
- m_catchwin = NULL;
- m_polling = 0;
- return TRUE;
+ if (m_thread)
+ {
+ m_thread->m_catchwin = NULL;
+ m_thread->m_polling = 0;
+ return true;
+ }
+ return false;
}
#endif // wxUSE_JOYSTICK
-