Skip to content
Snippets Groups Projects
vrpn_Microsoft_Controller_Raw.C 25.04 KiB
// vrpn_Microsoft_Controller_Raw.C: VRPN driver for Microsoft Controller Raw devices

#include <stdio.h>                      // for fprintf, stderr, NULL
#include <string.h>                     // for memset
#include <math.h>                       // for sqrt and fabs

#include "vrpn_Microsoft_Controller_Raw.h"

VRPN_SUPPRESS_EMPTY_OBJECT_WARNING()

#if defined(VRPN_USE_HID)

static const double POLL_INTERVAL = 1e+6 / 30.0;		// If we have not heard, ask.

#define MS_GAMEPAD_LEFT_THUMB_DEADZONE 7849
#define MS_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
#define MS_GAMEPAD_TRIGGER_THRESHOLD 30

//////////////////////////////////////////////////////////////////////////
// helpers
//////////////////////////////////////////////////////////////////////////
static vrpn_float64 normalize_dpad(unsigned char up, unsigned char right, unsigned char down, unsigned char left)
{
	int x = 0;
	int y = 0;
	if (right)
	{
		x += 1;
	}
	if (left)
	{
		x -= 1;
	}
	if (up)
	{
		y += 1;
	}
	if (down)
	{
		y -= 1;
	}
	size_t index = ((x + 1) * 3) + (y + 1);
	vrpn_float64 angles[] = {225, 270, 315, 180, -1, 0, 135, 90, 45};
	return (angles[index]);
}

static void normalize_axis(const unsigned int value, const short deadzone, const vrpn_float64 scale, vrpn_float64& channel, int wordSize = 16)
{
	channel = (static_cast<float>(value) - (float) (1 << (wordSize - 1)));
	if (fabs(channel) < (deadzone * 3 / 4))
	{
		channel = 0.0f;
	}
	else
	{
		channel /= (float) (1 << (wordSize - 1));
	}
	channel *= scale;
	if (channel < -1.0) { channel = -1.0; }
	if (channel > 1.0) { channel = 1.0; }
}

static void normalize_axes(const unsigned int x, const unsigned int y, const short deadzone, const vrpn_float64 scale, vrpn_float64& channelX, vrpn_float64& channelY, int wordSize = 16)
{
#ifdef FUTURE
	// adapted from: http://msdn.microsoft.com/en-us/library/windows/desktop/ee417001%28v=vs.85%29.aspx
	// determine how far the controller is pushed
	float magnitude = (float) sqrt((double) ((x * x) + (y * y)));

	// determine the direction the controller is pushed
	float normalizedX = ((magnitude > 0.0f) ? (x / magnitude) : 0.0f);
	float normalizedY = ((magnitude > 0.0f) ? (y / magnitude) : 0.0f);

	float normalizedMagnitude = 0.0f;

	// check if the controller is outside a circular dead zone
	if (magnitude > deadzone)
	{
		// clip the magnitude at its expected maximum value
		if (magnitude > 32767)
		{
			magnitude = 32767;
		}

		// adjust magnitude relative to the end of the dead zone
		magnitude -= deadzone;

		// optionally normalize the magnitude with respect to its
		// expected range giving a magnitude value of 0.0 to 1.0
		normalizedMagnitude = magnitude / (32767.0f - deadzone);
	}
	else
	{	// if the controller is in the deadzone zero out the magnitude
		magnitude = 0.0f;
		normalizedMagnitude = 0.0f;
	}
#else
	normalize_axis(x, deadzone, scale, channelX, wordSize);
	normalize_axis(y, deadzone, scale, channelY, wordSize);
#endif // FUTURE
}

static vrpn_float64 normalize_trigger(unsigned int trigger)
{
	// Filter out low-intensity signals
	int value = trigger - 0x80;
	return ((fabs(static_cast<double>(value)) < MS_GAMEPAD_TRIGGER_THRESHOLD) ? 0.0f : (value * 2.0f / 255.0f));
}

//////////////////////////////////////////////////////////////////////////
// Common base class
//////////////////////////////////////////////////////////////////////////
vrpn_Microsoft_Controller_Raw::vrpn_Microsoft_Controller_Raw(vrpn_HidAcceptor *filter, const char *name, vrpn_Connection *c)
  : _filter(filter)
  , vrpn_HidInterface(filter)
  , vrpn_BaseClass(name, c)
{
	init_hid();
}

vrpn_Microsoft_Controller_Raw::~vrpn_Microsoft_Controller_Raw(void)
{
  delete _filter;
}

void vrpn_Microsoft_Controller_Raw::init_hid() {
	// Get notifications when clients connect and disconnect
	register_autodeleted_handler(d_connection->register_message_type(vrpn_dropped_last_connection), on_last_disconnect, this);
	register_autodeleted_handler(d_connection->register_message_type(vrpn_got_connection), on_connect, this);
}

void vrpn_Microsoft_Controller_Raw::on_data_received(size_t bytes, vrpn_uint8 *buffer)
{
  decodePacket(bytes, buffer);
}

int vrpn_Microsoft_Controller_Raw::on_last_disconnect(void *thisPtr, vrpn_HANDLERPARAM /*p*/)
{
	vrpn_Microsoft_Controller_Raw *me = static_cast<vrpn_Microsoft_Controller_Raw *>(thisPtr);
	return 0;
}

int vrpn_Microsoft_Controller_Raw::on_connect(void *thisPtr, vrpn_HANDLERPARAM /*p*/)
{
	vrpn_Microsoft_Controller_Raw *me = static_cast<vrpn_Microsoft_Controller_Raw *>(thisPtr);
	return 0;
}

//////////////////////////////////////////////////////////////////////////
// SideWinder Precision 2 Joystick
//////////////////////////////////////////////////////////////////////////
vrpn_Microsoft_SideWinder_Precision_2::vrpn_Microsoft_SideWinder_Precision_2(const char *name, vrpn_Connection *c) :
	vrpn_Microsoft_Controller_Raw(_filter = new vrpn_HidProductAcceptor(MICROSOFT_VENDOR, SIDEWINDER_PRECISION_2), name, c),
	vrpn_Button_Filter(name, c), vrpn_Analog(name, c), vrpn_Dial(name, c)
{
	vrpn_Analog::num_channel = 5;
	vrpn_Dial::num_dials = 0;
	vrpn_Button::num_buttons = 12;

	// Initialize the state of all the analogs, buttons, and dials
	memset(buttons, 0, sizeof(buttons));
	memset(lastbuttons, 0, sizeof(lastbuttons));
	memset(channel, 0, sizeof(channel));
	memset(last, 0, sizeof(last));
}

void vrpn_Microsoft_SideWinder_Precision_2::mainloop(void)
{
	update();
	server_mainloop();
	struct timeval current_time;
	vrpn_gettimeofday(&current_time, NULL);
	if (vrpn_TimevalDuration(current_time, _timestamp) > POLL_INTERVAL)
	{
		_timestamp = current_time;
		report_changes();

		vrpn_Analog::server_mainloop();
		vrpn_Button::server_mainloop();
		if (vrpn_Dial::num_dials > 0)
		{
			vrpn_Dial::server_mainloop();
		}
	}
}

void vrpn_Microsoft_SideWinder_Precision_2::report(vrpn_uint32 class_of_service)
{
	vrpn_Analog::timestamp = _timestamp;
	vrpn_Button::timestamp = _timestamp;
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::timestamp = _timestamp;
	}

	vrpn_Analog::report_changes(class_of_service);
	vrpn_Button::report_changes();
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::report();
	}
}

void vrpn_Microsoft_SideWinder_Precision_2::report_changes(vrpn_uint32 class_of_service)
{
	vrpn_Analog::timestamp = _timestamp;
	vrpn_Button::timestamp = _timestamp;
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::timestamp = _timestamp;
	}

	vrpn_Analog::report(class_of_service);
	vrpn_Button::report_changes();
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::report();
	}
}

void vrpn_Microsoft_SideWinder_Precision_2::decodePacket(size_t bytes, vrpn_uint8 *buffer)
{
	// SideWinder Precision 2 joystick

	// Decode all full reports, each of which is 40 bytes long.
		// Because there is only one type of report, the initial "0" report-type
		// byte is removed by the HIDAPI driver.
	/*
	Byte   : Bit 7   6   5   4   3   2   1   0 | 7   6   5   4   3   2   1   0
	[0]:	X-axis (left=00, right=ff)
	[1]:	Y-axis (up=00, down=ff)
	[2]:	Z-rotate (left=00, right=ff)
	[3]:	Slider (up=00, down=ff)
	[4]:	buttons (bit flags: none=0x00, "1" (trigger)=0x01, "2"=0x02, "3"=0x04, ..., "8"=0x80
	[5]:	POV Hat high nibble (none=0x80, N=0x00, NE=0x10, ... NW=0x80)
	*/
	// XXX Check to see that this works with HIDAPI, there may be two smaller reports.
	if (bytes == 6)
	{
		normalize_axes(buffer[0], buffer[1], 0x08, 1.0f, channel[0], channel[1], 8);
		normalize_axis(buffer[2], 0x08, 1.0f, channel[2], 8);
		normalize_axis(buffer[3], 0x08, 1.0f, channel[3], 8);

		vrpn_uint8 value, mask;
		value = buffer[4];
		for (int btn = 0; btn < 8; btn++)
		{
			mask = static_cast<vrpn_uint8>(1 << (btn % 8));
			buttons[btn] = ((value & mask) != 0);
		}

		// Point of View Hat
		buttons[8] = buttons[9] = buttons[10] = buttons[11] = 0;
		switch (buffer[5] >> 4)
		{
		case 0:		// up
			buttons[8] = true;
			break;
		case 1:
			buttons[8] = buttons[9] = true;
			break;
		case 2:		// right
			buttons[9] = true;
			break;
		case 3:
			buttons[9] = buttons[10] = true;
			break;
		case 4:		// down
			buttons[10] = true;
			break;
		case 5:
			buttons[10] = buttons[11] = true;
			break;
		case 6:		// left
			buttons[11] = true;
			break;
		case 7:
			buttons[11] = buttons[8] = true;
			break;
		case 8:
		default:
			// nothing to do
			break;
		}
		channel[4] = normalize_dpad(buttons[8], buttons[9], buttons[10], buttons[11]);
	}
	else
	{
		fprintf(stderr, "vrpn_Microsoft_SideWinder_Precision_2: Found a corrupted report; # total bytes = %u\n", static_cast<unsigned>(bytes));
	}
}

//////////////////////////////////////////////////////////////////////////
// SideWinder Joystick
//////////////////////////////////////////////////////////////////////////
vrpn_Microsoft_SideWinder::vrpn_Microsoft_SideWinder(const char *name, vrpn_Connection *c) :
	vrpn_Microsoft_Controller_Raw(_filter = new vrpn_HidProductAcceptor(MICROSOFT_VENDOR, SIDEWINDER), name, c),
	vrpn_Button_Filter(name, c), vrpn_Analog(name, c), vrpn_Dial(name, c)
{
	vrpn_Analog::num_channel = 3;
	vrpn_Dial::num_dials = 0;
	vrpn_Button::num_buttons = 8;

	// Initialize the state of all the analogs, buttons, and dials
	memset(buttons, 0, sizeof(buttons));
	memset(lastbuttons, 0, sizeof(lastbuttons));
	memset(channel, 0, sizeof(channel));
	memset(last, 0, sizeof(last));
}

void vrpn_Microsoft_SideWinder::mainloop(void)
{
	update();
	server_mainloop();
	struct timeval current_time;
	vrpn_gettimeofday(&current_time, NULL);
	if (vrpn_TimevalDuration(current_time, _timestamp) > POLL_INTERVAL)
	{
		_timestamp = current_time;
		report_changes();

		vrpn_Analog::server_mainloop();
		vrpn_Button::server_mainloop();
		if (vrpn_Dial::num_dials > 0)
		{
			vrpn_Dial::server_mainloop();
		}
	}
}

void vrpn_Microsoft_SideWinder::report(vrpn_uint32 class_of_service)
{
	vrpn_Analog::timestamp = _timestamp;
	vrpn_Button::timestamp = _timestamp;
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::timestamp = _timestamp;
	}

	vrpn_Analog::report_changes(class_of_service);
	vrpn_Button::report_changes();
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::report();
	}
}

void vrpn_Microsoft_SideWinder::report_changes(vrpn_uint32 class_of_service)
{
	vrpn_Analog::timestamp = _timestamp;
	vrpn_Button::timestamp = _timestamp;
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::timestamp = _timestamp;
	}

	vrpn_Analog::report(class_of_service);
	vrpn_Button::report_changes();
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::report();
	}
}

void vrpn_Microsoft_SideWinder::decodePacket(size_t bytes, vrpn_uint8 *buffer)
{
	// SideWinder Precision 2 joystick

	// Decode all full reports, each of which is 40 bytes long.
		// Because there is only one type of report, the initial "0" report-type
		// byte is removed by the HIDAPI driver.
	/*
	Byte   : Bit 7   6   5   4   3   2   1   0 | 7   6   5   4   3   2   1   0
	[0]:	X-axis (left=00, right=ff)
	[1]:	Y-axis (up=00, down=ff)
	[2]:	slider (up/fwd=00, down/back=ff)
	[3]:	buttons (bit flags: none=0x00, "1" (trigger)=0x01, "2"=0x02, "3"=0x04, ..., "8"=0x80)
	*/
	// XXX Check to see that this works with HIDAPI, there may be two smaller reports.
	if (bytes == 4)
	{
		normalize_axes(buffer[0], buffer[1], 0x08, 1.0f, channel[0], channel[1], 8);
		normalize_axis(buffer[2], 0x08, 1.0f, channel[2], 8);

		vrpn_uint8 value, mask;
		value = buffer[3];
		for (int btn = 0; btn < 8; btn++)
		{
			mask = static_cast<vrpn_uint8>(1 << (btn % 8));
			buttons[btn] = ((value & mask) != 0);
		}
	}
	else
	{
		fprintf(stderr, "vrpn_Microsoft_SideWinder: Found a corrupted report; # total bytes = %u\n", static_cast<unsigned>(bytes));
	}
}

//////////////////////////////////////////////////////////////////////////
// Xbox S
//////////////////////////////////////////////////////////////////////////
vrpn_Microsoft_Controller_Raw_Xbox_S::vrpn_Microsoft_Controller_Raw_Xbox_S(const char *name, vrpn_Connection *c)
  : vrpn_Microsoft_Controller_Raw(_filter = new vrpn_HidProductAcceptor(MICROSOFT_VENDOR, XBOX_S), name, c)
  , vrpn_Button_Filter(name, c)
  , vrpn_Analog(name, c)
  , vrpn_Dial(name, c)
{
  vrpn_Analog::num_channel = 5;
  vrpn_Dial::num_dials = 0;
  vrpn_Button::num_buttons = 16;

  // Initialize the state of all the analogs, buttons, and dials
  _lastDial = 0;
  memset(buttons, 0, sizeof(buttons));
  memset(lastbuttons, 0, sizeof(lastbuttons));
  memset(channel, 0, sizeof(channel));
  memset(last, 0, sizeof(last));
}

void vrpn_Microsoft_Controller_Raw_Xbox_S::mainloop()
{
	update();
	server_mainloop();
	struct timeval current_time;
	vrpn_gettimeofday(&current_time, NULL);
	if (vrpn_TimevalDuration(current_time, _timestamp) > POLL_INTERVAL ) {
		_timestamp = current_time;
		report_changes();

		vrpn_Analog::server_mainloop();
		vrpn_Button::server_mainloop();
		if (vrpn_Dial::num_dials > 0)
		{
			vrpn_Dial::server_mainloop();
		}
	}
}

void vrpn_Microsoft_Controller_Raw_Xbox_S::report(vrpn_uint32 class_of_service) {
	vrpn_Analog::timestamp = _timestamp;
	vrpn_Button::timestamp = _timestamp;
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::timestamp = _timestamp;
	}

	vrpn_Analog::report_changes(class_of_service);
	vrpn_Button::report_changes();
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::report();
	}
}

void vrpn_Microsoft_Controller_Raw_Xbox_S::report_changes(vrpn_uint32 class_of_service) {
	vrpn_Analog::timestamp = _timestamp;
	vrpn_Button::timestamp = _timestamp;
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::timestamp = _timestamp;
	}

	vrpn_Analog::report_changes(class_of_service);
	vrpn_Button::report_changes();
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::report();
	}
}

void vrpn_Microsoft_Controller_Raw_Xbox_S::decodePacket(size_t bytes, vrpn_uint8 *buffer) {
	// Xbox Controller S requires adapter to be made/purchased to connect USB to computer.
	// Also, it may require a driver to be installed such as XBCD_Installer_0.2.7.exe (Windows).

	// Decode all full reports, each of which is 40 bytes long.
        // Because there is only one type of report, the initial "0" report-type
        // byte is removed by the HIDAPI driver.
	/*
	Byte   : Bit 7   6   5   4   3   2   1   0 | 7   6   5   4   3   2   1   0
	[0]    :   |           Report ID           |
	[1]    :   |B8 |B7 |B6 |B5 |B4 |B3 |B2 |B1 |
	[2]    :   |B16|B15|B14|B13|B12|B11|B10|B9 |
	[3]    :   |B24|B23|B22|B21|B20|B19|B18|B17|   <------- no such buttons exist and data does not change
	[4-5]  :   |                            X Axis                             |
	[6-7]  :   |                            Y Axis                             |
	[8-9]  :   |                            Z Axis                             |
	[10-11] :  |                            RX Axis                            |
	[12-13]:   |                            RY Axis                            |
	[14-15]:   |                            RZ Axis                            |
	[16-17]:   |                          Slider Axis                          |
	[18]   :   |     EMPTY     |      POV      |
	[19]   :   |       Current MapMatrix       |
	--- Original data from Xbox controller ---
	[20]   :   |             0x00              |
	[21]   :   |      0x14(Size of report)     |
	[22]   :   |RSp|LSp|Bk |St |Rgt|Lft|Dn |Up |   <------- other buttons
	[23]   :   |             0x00              |
	[24]   :   |           Button A            |
	[25]   :   |           Button B            |
	[26]   :   |           Button X            |
	[27]   :   |           Button Y            |
	[28]   :   |         Button Black          |
	[29]   :   |         Button White          |
	[30]   :   |         Left Trigger          |
	[31]   :   |         Right Trigger         |
	[32-33]:   |                         Left-Stick X                          |
	[34-35]:   |                         Left-Stick Y                          |
	[36-37]:   |                         Right-Stick X                         |
	[38-39]:   |                         Right-Stick Y                         |
	*/
	// XXX Check to see that this works with HIDAPI, there may be two smaller reports.
	if (bytes == 40) {
		if (buffer[0] == 1) {

			// Report joystick axes as analogs
			vrpn_uint16 x, y;
			vrpn_uint8 *bufptr;
#ifdef OLD_DATA
			//	left joy left/right: Left 27, center (normal) 81 (calc 7e), right D5
			bufptr = &buffer[32];
			x = vrpn_unbuffer<vrpn_int16>(bufptr);
			//	left joy up/down: Up 34, center (normal) 81 (calc 81), down CF
			bufptr = &buffer[34];
			y = vrpn_unbuffer<vrpn_int16>(bufptr);
			normalize_axes(x, y, MS_GAMEPAD_LEFT_THUMB_DEADZONE, 1.0f, channel[0], channel[1]);
			//	right joy up/down: Up 32, (center calc 7f), down CC
			bufptr = &buffer[36];
			x = vrpn_unbuffer<vrpn_int16>(bufptr);
			//	right joy left/right: Left 2B, center (normal) 82 (calc 84), right DC
			bufptr = &buffer[38];
			y = vrpn_unbuffer<vrpn_int16>(bufptr);
			normalize_axes(x, y, MS_GAMEPAD_RIGHT_THUMB_DEADZONE, 1.0f, channel[2], channel[3]);
#else
			vrpn_int16 temp;
			bufptr = &buffer[4];
			temp = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) / 2;
			x = temp + (1 << 15);
			x *= 2;
			bufptr = &buffer[6];
			temp = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) / 2;
			y = temp + (1 << 15);
			y *= 2;
			normalize_axes(x, y, MS_GAMEPAD_LEFT_THUMB_DEADZONE, 1.0f, channel[0], channel[1]);
			bufptr = &buffer[10];
			temp = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) / 2;
			x = temp + (1 << 15);
			bufptr = &buffer[12];
			temp = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) / 2;
			y = temp + (1 << 15);
			normalize_axes(x, y, MS_GAMEPAD_LEFT_THUMB_DEADZONE, 2.0f, channel[2], channel[3]);
#endif // OLD_DATA

#ifdef OLD_DATA
			//	button #0: 24 A
			buttons[0] = (buffer[24] != 0);
			//	button #1: 25 B
			buttons[1] = (buffer[25] != 0);
			//	button #2: 26 X
			buttons[2] = (buffer[26] != 0);
			//	button #3: 27 Y
			buttons[3] = (buffer[27] != 0);
			//	button #4: 28 Black
			buttons[4] = (buffer[28] != 0);
			//	button #5: 29 White
			buttons[5] = (buffer[29] != 0);
			//	button #6: 40 Start
			buttons[6] = ((buffer[22] & 0x10) != 0);
			//	button #7: 80 Back
			buttons[7] = ((buffer[22] & 0x20) != 0);
			//	button #8:  left joy
			buttons[8] = ((buffer[22] & 0x40) != 0);
			//	button #9:  right joy
			buttons[9] = ((buffer[22] & 0x80) != 0);
			//	button #10:30 left trigger
			buttons[10] = (buffer[30] != 0);
			//	button #11:31 right trigger
			buttons[11] = (buffer[31] != 0);
#else
			vrpn_uint8 value, mask;
			value = buffer[1];
			for (int btn = 0; btn < 8; btn++)
			{
				mask = static_cast<vrpn_uint8>(1 << (btn % 8));
				buttons[btn] = ((value & mask) != 0);
			}
			value = buffer[2];
			for (int btn = 0; btn < 4; btn++)
			{
				mask = static_cast<vrpn_uint8>(1 << (btn % 8));
				buttons[8 + btn] = ((value & mask) != 0);
			}
#endif // OLD_DATA

			// Point of View Hat
#ifdef OLD_DATA
			buttons[12] = ((buffer[22] & 0x01) != 0);	// Up
			buttons[14] = ((buffer[22] & 0x02) != 0);	// Down
			buttons[15] = ((buffer[22] & 0x04) != 0);	// Left
			buttons[13] = ((buffer[22] & 0x08) != 0);	// Right
#else
			// Point of View Hat
			buttons[12] = buttons[13] = buttons[14] = buttons[15] = 0;
			switch (buffer[18] & 0x0f)
			{
			case 0:		// up
				buttons[12] = true;
				break;
			case 1:
				buttons[12] = buttons[13] = true;
				break;
			case 2:		// right
				buttons[13] = true;
				break;
			case 3:
				buttons[13] = buttons[14] = true;
				break;
			case 4:		// down
				buttons[14] = true;
				break;
			case 5:
				buttons[14] = buttons[15] = true;
				break;
			case 6:		// left
				buttons[15] = true;
				break;
			case 7:
				buttons[15] = buttons[12] = true;
				break;
			case 8:
			default:
				// nothing to do
				break;
			}
#endif // OLD_DATA
			channel[4] = normalize_dpad(buttons[12], buttons[13], buttons[14], buttons[15]);
		} else {
			fprintf(stderr, "vrpn_Microsoft_Controller_Raw_Xbox_S: Unknown report = %u\n", static_cast<unsigned>(buffer[0]));
		}
	} else {
		fprintf(stderr, "vrpn_Microsoft_Controller_Raw_Xbox_S: Found a corrupted report; # total bytes = %u\n", static_cast<unsigned>(bytes));
	}
}

//////////////////////////////////////////////////////////////////////////
// Xbox 360
//////////////////////////////////////////////////////////////////////////
vrpn_Microsoft_Controller_Raw_Xbox_360::vrpn_Microsoft_Controller_Raw_Xbox_360(const char *name, vrpn_Connection *c, vrpn_uint16 vendorId /*=MICROSOFT_VENDOR*/, vrpn_uint16 productId /*=XBOX_360*/)
: vrpn_Microsoft_Controller_Raw(_filter = new vrpn_HidProductAcceptor(vendorId, productId), name, c)
, vrpn_Button_Filter(name, c)
, vrpn_Analog(name, c)
, vrpn_Dial(name, c)
{
	vrpn_Analog::num_channel = 6;
	vrpn_Dial::num_dials = 0;
	vrpn_Button::num_buttons = 18;

	// Initialize the state of all the analogs, buttons, and dials
	_lastDial = 0;
	memset(buttons, 0, sizeof(buttons));
	memset(lastbuttons, 0, sizeof(lastbuttons));
	memset(channel, 0, sizeof(channel));
	memset(last, 0, sizeof(last));
}

void vrpn_Microsoft_Controller_Raw_Xbox_360::mainloop()
{
	update();
	server_mainloop();
	struct timeval current_time;
	vrpn_gettimeofday(&current_time, NULL);
	if (vrpn_TimevalDuration(current_time, _timestamp) > POLL_INTERVAL ) {
		_timestamp = current_time;
		report_changes();

		vrpn_Analog::server_mainloop();
		vrpn_Button::server_mainloop();
		if (vrpn_Dial::num_dials > 0)
		{
			vrpn_Dial::server_mainloop();
		}
	}
}

void vrpn_Microsoft_Controller_Raw_Xbox_360::report(vrpn_uint32 class_of_service) {
	vrpn_Analog::timestamp = _timestamp;
	vrpn_Button::timestamp = _timestamp;
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::timestamp = _timestamp;
	}

	vrpn_Analog::report_changes(class_of_service);
	vrpn_Button::report_changes();
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::report();
	}
}

void vrpn_Microsoft_Controller_Raw_Xbox_360::report_changes(vrpn_uint32 class_of_service) {
	vrpn_Analog::timestamp = _timestamp;
	vrpn_Button::timestamp = _timestamp;
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::timestamp = _timestamp;
	}

	vrpn_Analog::report(class_of_service);
	vrpn_Button::report_changes();
	if (vrpn_Dial::num_dials > 0)
	{
		vrpn_Dial::report();
	}
}

void vrpn_Microsoft_Controller_Raw_Xbox_360::decodePacket(size_t bytes, vrpn_uint8 *buffer) {
	// Decode all full reports, each of which is 14 bytes long.
        // Because there is only one type of report, the initial "0" report-type
        // byte is removed by the HIDAPI driver.
	/*
	0: left-X
	1: "
	2: left-Y
	3: "
	4: right-X
	5: "
	6: right-Y
	7: "
	8: 00/80: not sure with left & right triggers
	9: 80 normal, ...ff=left trigger, ...00=right trigger
	10: Button bit flags: 01=A, ..., 80=start
	11: Button bit flags: 01=left joy, 02=right joy, 04=hat up add 04 every 45 degrees right
	12: varies with everything
	13: "
	??: not sure where Xbox button is
	*/
	// XXX Check to see that this works with HIDAPI, there may be two smaller reports.
	if (bytes == 14) {
		if (true) {
			vrpn_uint8 *bufptr;

			// Report joystick axes as analogs
			//	left joy left/right
			vrpn_uint16 x, y;
			bufptr = &buffer[0];
			x = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr);
			//	left joy up/down
			bufptr = &buffer[2];
			y = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr);
			normalize_axes(x, y, MS_GAMEPAD_LEFT_THUMB_DEADZONE, 1.0f, channel[0], channel[1]);
			//	right joy left/right
			bufptr = &buffer[4];
			x = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr);
			//	right joy up/down
			bufptr = &buffer[6];
			y = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr);
			normalize_axes(x, y, MS_GAMEPAD_RIGHT_THUMB_DEADZONE, 1.0f, channel[2], channel[3]);

			// triggers: left goes positive, right goes negative
			channel[4] = normalize_trigger(buffer[9]);

			vrpn_uint8 value;
			value = buffer[10];
			for (int btn = 0; btn < 8; btn++) {
				/*
				1: A
				2: B
				3: X
				4: Y
				5: left bump
				6: right bump
				7: back
				8: start
				*/
				vrpn_uint8 mask = static_cast<vrpn_uint8>(1 << (btn % 8));
				buttons[btn] = ((value & mask) != 0);
			}
			value = buffer[11];
			for (int btn = 0; btn < 2; btn++) {
				/*
				9: left joy
				10: right joy
				*/
				vrpn_uint8 mask = static_cast<vrpn_uint8>(1 << (btn % 8));
				buttons[8 + btn] = ((value & mask) != 0);
			}
			value &= 0x3c;	// remove joystick buttons and isolate just the "Point of View Hat"
			value >>= 2;
			/*
			11: 04 (>>2=1, >>3=0) hat up (0)
				08 (>>2=2, >>3=1) up-right (45)
			12: 0c (>>2=3, >>3=1) hat right (90)
				10 (>>2=4, >>3=2) right-down (135)
			13: 14 (>>2=5, >>3=2) hat down (180)
				18 (>>2=6, >>3=3) down-left (225)
			14: 1c (>>2=7, >>3=3) hat left (270)
				20 (>>2=8, >>3=4) left-up (315)
			*/
			buttons[10] = buttons[11] = buttons[12] = buttons[13] = false;
			if (value != 0)
			{
				int lowerBtn = (10 + (value >> 3)) & 0x03;
				switch (value)
				{
				case 1:
					buttons[10] = true;
					break;
				case 2:
					buttons[10] = buttons[11] = true;
					break;
				case 3:
					buttons[11] = true;
					break;
				case 4:
					buttons[11] = buttons[12] = true;
					break;
				case 5:
					buttons[12] = true;
					break;
				case 6:
					buttons[12] = buttons[13] = true;
					break;
				case 7:
					buttons[13] = true;
					break;
				case 8:
					buttons[13] = buttons[10] = true;
					break;
				}
			}
			channel[5] = normalize_dpad(buttons[10], buttons[11], buttons[12], buttons[13]);
		} else {
			fprintf(stderr, "vrpn_Microsoft_Controller_Raw_Xbox_360: Unknown report = %u\n", static_cast<unsigned>(buffer[0]));
		}
	} else {
		fprintf(stderr, "vrpn_Microsoft_Controller_Raw_Xbox_360: Found a corrupted report; # total bytes = %u\n", static_cast<unsigned>(bytes));
	}
}

// End of VRPN_USE_HID
#endif