Files
wxWidgets/src/unix/joystick.cpp
Francesco Montorsi 004867dbc5 Change in wxWindow the access specifier of the wxEvtHandler event processing and queuing functions
from public to protected. Adapt wxWidgets code and wxWidgets samples to always use wxWindow::GetEventHandler()
when calling such functions on a wxWindow rather than directly using wxWindow::ProcessEvent, etc.
This enables correct event dispatching to the event handlers which have been pushed (with PushEventHandler) on the 
windows.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@58381 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2009-01-25 11:58:39 +00:00

529 lines
11 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// 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
/////////////////////////////////////////////////////////////////////////////
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#if wxUSE_JOYSTICK
#include "wx/joystick.h"
#ifndef WX_PRECOMP
#include "wx/event.h"
#include "wx/window.h"
#endif //WX_PRECOMP
#include "wx/thread.h"
#include <linux/joystick.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif
#include "wx/unix/private.h"
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 = 15,
wxJS_MAX_BUTTONS = sizeof(int) * 8
};
IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject)
////////////////////////////////////////////////////////////////////////////
// Background thread for reading the joystick device
////////////////////////////////////////////////////////////////////////////
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))
{
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);
}
}
}
}
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 (m_device != -1)
{
m_thread = new wxJoystickThread(m_device, m_joystick);
m_thread->Create();
m_thread->Run();
}
}
wxJoystick::~wxJoystick()
{
ReleaseCapture();
if (m_thread)
m_thread->Delete(); // It's detached so it will delete itself
m_device = -1;
}
////////////////////////////////////////////////////////////////////////////
// State
////////////////////////////////////////////////////////////////////////////
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
{
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;
}
bool wxJoystick::GetButtonState(unsigned id) const
{
if (m_thread && (id < wxJS_MAX_BUTTONS))
return (m_thread->m_buttons & (1 << id)) != 0;
return false;
}
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
{
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() const
{
return (m_device != -1);
}
int wxJoystick::GetNumberJoysticks()
{
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);
}
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() const
{
return 0;
}
int wxJoystick::GetProductId() const
{
return 0;
}
wxString wxJoystick::GetProductName() const
{
char name[128];
if (ioctl(m_device, JSIOCGNAME(sizeof(name)), name) < 0)
strcpy(name, "Unknown");
return wxString(name, wxConvLibc);
}
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
{
char nb=0;
if (m_device != -1)
ioctl(m_device, JSIOCGBUTTONS, &nb);
if ((int)nb > wxJS_MAX_BUTTONS)
nb = wxJS_MAX_BUTTONS;
return nb;
}
int wxJoystick::GetNumberAxes() const
{
char nb=0;
if (m_device != -1)
ioctl(m_device, JSIOCGAXES, &nb);
if ((int)nb > wxJS_MAX_AXES)
nb = wxJS_MAX_AXES;
return nb;
}
int wxJoystick::GetMaxButtons() const
{
return wxJS_MAX_BUTTONS; // internal
}
int wxJoystick::GetMaxAxes() const
{
return wxJS_MAX_AXES; // internal
}
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 GetNumberAxes() >= wxJS_AXIS_RUDDER;
}
bool wxJoystick::HasZ() const
{
return GetNumberAxes() >= wxJS_AXIS_Z;
}
bool wxJoystick::HasU() const
{
return GetNumberAxes() >= wxJS_AXIS_U;
}
bool wxJoystick::HasV() const
{
return GetNumberAxes() >= 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 // wxUSE_JOYSTICK