vrpn  07.33
Virtual Reality Peripheral Network
vrpn_XInputGamepad.C
Go to the documentation of this file.
1 // vrpn_XInputGamepad.C: Gamepad driver for devices using XInput
2 // such as (read: primarily) the Microsoft Xbox 360 controller.
3 
4 #include "vrpn_XInputGamepad.h"
5 
6 #if defined(_WIN32) && defined(VRPN_USE_WINDOWS_XINPUT)
7 #include <xinput.h>
8 
9 vrpn_XInputGamepad::vrpn_XInputGamepad(const char *name, vrpn_Connection *c, unsigned int controllerIndex):
10  vrpn_Analog(name, c),
11  vrpn_Button_Filter(name, c),
12  vrpn_Analog_Output(name, c),
13  _controllerIndex(controllerIndex)
14 {
18 
19  _motorSpeed[0] = 0;
20  _motorSpeed[1] = 0;
21 
24  fprintf(stderr, "vrpn_XInputGamepad: Can't register request-single handler\n");
25  return;
26  }
27 
30  fprintf(stderr, "vrpn_XInputGamepad: Can't register request-multiple handler\n");
31  return;
32  }
33 
37  fprintf(stderr, "vrpn_XInputGamepad: Can't register connections-dropped handler\n");
38  return;
39  }
40 }
41 
42 vrpn_XInputGamepad::~vrpn_XInputGamepad() {
43 }
44 
45 void vrpn_XInputGamepad::mainloop() {
46  XINPUT_STATE state;
47  DWORD rv;
48 
49  server_mainloop();
50  if ((rv = XInputGetState(_controllerIndex, &state)) != ERROR_SUCCESS) {
51  char errMsg[256];
52  struct timeval now;
53 
54  if (rv == ERROR_DEVICE_NOT_CONNECTED)
55  sprintf(errMsg, "XInput device %u not connected", _controllerIndex);
56  else
57  sprintf(errMsg, "XInput device %u returned Windows error code %u",
58  _controllerIndex, rv);
59 
60  vrpn_gettimeofday(&now, NULL);
61  send_text_message(errMsg, now, vrpn_TEXT_ERROR);
62  return;
63  }
64 
65  // Set device state in VRPN_Analog
66  channel[0] = normalize_axis(state.Gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
67  channel[1] = normalize_axis(state.Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
68  channel[2] = normalize_axis(state.Gamepad.sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
69  channel[3] = normalize_axis(state.Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
70  channel[4] = normalize_dpad(state.Gamepad.wButtons);
71  channel[5] = normalize_trigger(state.Gamepad.bLeftTrigger);
72  channel[6] = normalize_trigger(state.Gamepad.bRightTrigger);
73 
74  // Set device state in VRPN_Button
75  // Buttons are listed in DirectInput ordering
76  buttons[0] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_A) != 0;
77  buttons[1] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_B) != 0;
78  buttons[2] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_X) != 0;
79  buttons[3] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) != 0;
80  buttons[4] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) != 0;
81  buttons[5] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) != 0;
82  buttons[6] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) != 0;
83  buttons[7] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) != 0;
84  buttons[8] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) != 0;
85  buttons[9] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) != 0;
86 
87  vrpn_gettimeofday(&_timestamp, NULL);
88  report_changes();
89 }
90 
91 vrpn_float64 vrpn_XInputGamepad::normalize_dpad(WORD buttons) const {
92  int x = 0;
93  int y = 0;
94 
95  if (buttons & XINPUT_GAMEPAD_DPAD_RIGHT)
96  x += 1;
97  if (buttons & XINPUT_GAMEPAD_DPAD_LEFT)
98  x -= 1;
99  if (buttons & XINPUT_GAMEPAD_DPAD_UP)
100  y += 1;
101  if (buttons & XINPUT_GAMEPAD_DPAD_DOWN)
102  y -= 1;
103 
104  size_t index = (x + 1) * 3 + (y + 1);
105  vrpn_float64 angles[] = {225, 270, 315, 180, -1, 0, 135, 90, 45};
106  return angles[index];
107 }
108 
109 
110 vrpn_float64 vrpn_XInputGamepad::normalize_axis(SHORT axis, SHORT deadzone) const {
111  // Filter out areas near the center
112  if (axis > -deadzone && axis < deadzone)
113  return 0;
114 
115  // Note ranges are asymmetric (-32768 to 32767)
116  return axis / ((axis < 0) ? 32768.0 : 32767.0);
117 }
118 
119 vrpn_float64 vrpn_XInputGamepad::normalize_trigger(BYTE trigger) const {
120  // Filter out low-intensity signals
121  if (trigger < XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
122  return 0;
123 
124  return trigger / 255.0;
125 }
126 
127 void vrpn_XInputGamepad::update_vibration() {
128  XINPUT_VIBRATION vibration;
129 
130  vibration.wLeftMotorSpeed = _motorSpeed[0];
131  vibration.wRightMotorSpeed = _motorSpeed[1];
132 
133  DWORD rv = XInputSetState(_controllerIndex, &vibration);
134  if (rv != ERROR_SUCCESS) {
135  char errMsg[256];
136  struct timeval now;
137 
138  if (rv == ERROR_DEVICE_NOT_CONNECTED)
139  sprintf(errMsg, "XInput device %u not connected", _controllerIndex);
140  else
141  sprintf(errMsg, "XInput device %u returned Windows error code %u",
142  _controllerIndex, rv);
143 
144  vrpn_gettimeofday(&now, NULL);
145  send_text_message(errMsg, now, vrpn_TEXT_ERROR);
146  return;
147  }
148 }
149 
150 void vrpn_XInputGamepad::report(vrpn_uint32 class_of_service) {
151  vrpn_Analog::timestamp = _timestamp;
152  vrpn_Button::timestamp = _timestamp;
153 
154  vrpn_Analog::report(class_of_service);
156 }
157 
158 void vrpn_XInputGamepad::report_changes(vrpn_uint32 class_of_service) {
159  vrpn_Analog::timestamp = _timestamp;
160  vrpn_Button::timestamp = _timestamp;
161 
162  vrpn_Analog::report_changes(class_of_service);
164 }
165 
166 // Static callback
167 int VRPN_CALLBACK vrpn_XInputGamepad::handle_request_message(void *selfPtr,
168  vrpn_HANDLERPARAM data)
169 {
170  const char *bufptr = data.buffer;
171  vrpn_int32 chan_num;
172  vrpn_int32 pad;
173  vrpn_float64 value;
174  vrpn_XInputGamepad *me = (vrpn_XInputGamepad *) selfPtr;
175 
176  // Read the parameters from the buffer
177  vrpn_unbuffer(&bufptr, &chan_num);
178  vrpn_unbuffer(&bufptr, &pad);
179  vrpn_unbuffer(&bufptr, &value);
180 
181  // Set the appropriate value, if the channel number is in the
182  // range of the ones we have.
183  if ( (chan_num < 0) || (chan_num >= me->o_num_channel) ) {
184  fprintf(stderr,"vrpn_Analog_Output_Server::handle_request_message(): Index out of bounds\n");
185  char msg[1024];
186  sprintf( msg, "Error: (handle_request_message): channel %d is not active. Squelching.", chan_num );
187  me->send_text_message( msg, data.msg_time, vrpn_TEXT_ERROR );
188  return 0;
189  }
190  me->o_channel[chan_num] = value;
191 
192  float magnitude = static_cast<float>(value);
193  magnitude = (magnitude < 0) ? 0 : (magnitude > 1) ? 1 : magnitude;
194 
195  me->_motorSpeed[chan_num] = static_cast<WORD>(magnitude * 65535);
196  me->update_vibration();
197 
198  return 0;
199 }
200 
201 // Static callback
202 int VRPN_CALLBACK vrpn_XInputGamepad::handle_request_channels_message(void *selfPtr,
203  vrpn_HANDLERPARAM data)
204 {
205  const char *bufptr = data.buffer;
206  vrpn_int32 chan_num;
207  vrpn_int32 pad;
208  vrpn_XInputGamepad *me = (vrpn_XInputGamepad *) selfPtr;
209  int i;
210 
211  // Read the parameters from the buffer
212  vrpn_unbuffer(&bufptr, &chan_num);
213  vrpn_unbuffer(&bufptr, &pad);
214 
215  if (chan_num > me->o_num_channel) {
216  char msg[1024];
217  sprintf( msg, "Error: (handle_request_channels_message): channels above %d not active; "
218  "bad request up to channel %d. Squelching.", me->o_num_channel, chan_num );
219  me->send_text_message( msg, data.msg_time, vrpn_TEXT_ERROR );
220  chan_num = me->o_num_channel;
221  }
222  if (chan_num < 0) {
223  char msg[1024];
224  sprintf( msg, "Error: (handle_request_channels_message): invalid channel %d. Squelching.", chan_num );
225  me->send_text_message( msg, data.msg_time, vrpn_TEXT_ERROR );
226  return 0;
227  }
228  for (i = 0; i < chan_num; i++) {
229  vrpn_float64 value;
230  vrpn_unbuffer(&bufptr, &value);
231 
232  float magnitude = static_cast<float>(value);
233  magnitude = (magnitude < 0) ? 0 : (magnitude > 1) ? 1 : magnitude;
234 
235  me->_motorSpeed[chan_num] = static_cast<WORD>(magnitude * 65535);
236  }
237 
238  me->update_vibration();
239 
240  return 0;
241 }
242 
243 // Static callback
244 int VRPN_CALLBACK vrpn_XInputGamepad::handle_last_connection_dropped(void *selfPtr,
245  vrpn_HANDLERPARAM data)
246 {
247  vrpn_XInputGamepad *me = static_cast<vrpn_XInputGamepad *>(selfPtr);
248 
249  // Kill force feedback if no one is connected
250  me->_motorSpeed[0] = 0;
251  me->_motorSpeed[1] = 0;
252  me->update_vibration();
253 
254  return 0;
255 }
256 
257 #endif // _WIN32 && VRPN_USE_DIRECTINPUT
258 
vrpn_BaseClassUnique::register_autodeleted_handler
int register_autodeleted_handler(vrpn_int32 type, vrpn_MESSAGEHANDLER handler, void *userdata, vrpn_int32 sender=vrpn_ANY_SENDER)
Registers a handler with the connection, and remembers to delete at destruction.
Definition: vrpn_BaseClass.C:503
vrpn_Button::report_changes
virtual void report_changes(void)
Definition: vrpn_Button.C:422
vrpn_dropped_last_connection
const char * vrpn_dropped_last_connection
Definition: vrpn_Connection.C:187
vrpn_Analog::report
virtual void report(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY, const struct timeval time=vrpn_ANALOG_NOW)
Send a report whether something has changed or not (for servers) Optionally, tell what time to stamp ...
Definition: vrpn_Analog.C:94
vrpn_Analog
Definition: vrpn_Analog.h:28
vrpn_Analog_Output::o_num_channel
vrpn_int32 o_num_channel
Definition: vrpn_Analog_Output.h:37
vrpn_Analog::timestamp
struct timeval timestamp
Definition: vrpn_Analog.h:41
vrpn_Analog_Output
Definition: vrpn_Analog_Output.h:26
vrpn_Button::num_buttons
vrpn_int32 num_buttons
Definition: vrpn_Button.h:47
vrpn_unbuffer
VRPN_API int vrpn_unbuffer(const char **buffer, timeval *t)
Utility routine for taking a struct timeval from a buffer that was sent as a message.
Definition: vrpn_Shared.C:312
vrpn_BaseClassUnique::d_connection
vrpn_Connection * d_connection
Connection that this object talks to.
Definition: vrpn_BaseClass.h:224
vrpn_HANDLERPARAM::buffer
const char * buffer
Definition: vrpn_Connection.h:49
vrpn_Connection::register_message_type
virtual vrpn_int32 register_message_type(const char *name)
Definition: vrpn_Connection.C:5074
vrpn_TEXT_ERROR
Definition: vrpn_BaseClass.h:103
vrpn_XInputGamepad.h
vrpn_HANDLERPARAM
This structure is what is passed to a vrpn_Connection message callback.
Definition: vrpn_Connection.h:44
vrpn_BaseClassUnique::d_sender_id
vrpn_int32 d_sender_id
Sender ID registered with the connection.
Definition: vrpn_BaseClass.h:228
vrpn_Button::timestamp
struct timeval timestamp
Definition: vrpn_Button.h:48
vrpn_WiiMote::handle_request_channels_message
static int VRPN_CALLBACK handle_request_channels_message(void *userdata, vrpn_HANDLERPARAM p)
Definition: vrpn_WiiMote.C:669
vrpn_HANDLERPARAM::msg_time
struct timeval msg_time
Definition: vrpn_Connection.h:47
vrpn_Connection
Generic connection class not specific to the transport mechanism.
Definition: vrpn_Connection.h:510
vrpn_Analog::num_channel
vrpn_int32 num_channel
Definition: vrpn_Analog.h:40
vrpn_gettimeofday
#define vrpn_gettimeofday
Definition: vrpn_Shared.h:89
vrpn_WiiMote::handle_last_connection_dropped
static int VRPN_CALLBACK handle_last_connection_dropped(void *selfPtr, vrpn_HANDLERPARAM data)
Definition: vrpn_WiiMote.C:705
VRPN_CALLBACK
#define VRPN_CALLBACK
Definition: vrpn_Configure.h:647
vrpn_WiiMote::handle_request_message
static int VRPN_CALLBACK handle_request_message(void *userdata, vrpn_HANDLERPARAM p)
Definition: vrpn_WiiMote.C:625
vrpn_Analog::report_changes
virtual void report_changes(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY, const struct timeval time=vrpn_ANALOG_NOW)
Send a report only if something has changed (for servers) Optionally, tell what time to stamp the val...
Definition: vrpn_Analog.C:71
vrpn_Analog_Output::request_channels_m_id
vrpn_int32 request_channels_m_id
Definition: vrpn_Analog_Output.h:40
vrpn_Analog_Output::request_m_id
vrpn_int32 request_m_id
Definition: vrpn_Analog_Output.h:39
vrpn_Button_Filter
All button servers should derive from this class, which provides the ability to turn any of the butto...
Definition: vrpn_Button.h:65