vrpn 07.35
Virtual Reality Peripheral Network
Loading...
Searching...
No Matches
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
5
6#if defined(_WIN32) && defined(VRPN_USE_WINDOWS_XINPUT)
7#include <xinput.h>
8
9vrpn_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
22 if (register_autodeleted_handler(request_m_id,
23 handle_request_message, this, d_sender_id)) {
24 fprintf(stderr, "vrpn_XInputGamepad: Can't register request-single handler\n");
25 return;
26 }
27
28 if (register_autodeleted_handler(request_channels_m_id,
29 handle_request_channels_message, this, d_sender_id)) {
30 fprintf(stderr, "vrpn_XInputGamepad: Can't register request-multiple handler\n");
31 return;
32 }
33
34 if (register_autodeleted_handler(
35 d_connection->register_message_type(vrpn_dropped_last_connection),
36 handle_last_connection_dropped, this)) {
37 fprintf(stderr, "vrpn_XInputGamepad: Can't register connections-dropped handler\n");
38 return;
39 }
40}
41
42vrpn_XInputGamepad::~vrpn_XInputGamepad() {
43}
44
45void 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
91vrpn_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
110vrpn_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
119vrpn_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
127void 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
150void 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
158void 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
167int VRPN_CALLBACK vrpn_XInputGamepad::handle_request_message(void *selfPtr,
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
202int VRPN_CALLBACK vrpn_XInputGamepad::handle_request_channels_message(void *selfPtr,
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
244int VRPN_CALLBACK vrpn_XInputGamepad::handle_last_connection_dropped(void *selfPtr,
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
struct timeval timestamp
Definition: vrpn_Analog.h:41
vrpn_int32 num_channel
Definition: vrpn_Analog.h:40
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
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
All button servers should derive from this class, which provides the ability to turn any of the butto...
Definition: vrpn_Button.h:66
vrpn_int32 num_buttons
Definition: vrpn_Button.h:48
struct timeval timestamp
Definition: vrpn_Button.h:49
virtual void report_changes(void)
Definition: vrpn_Button.C:423
Generic connection class not specific to the transport mechanism.
This structure is what is passed to a vrpn_Connection message callback.
const char * buffer
struct timeval msg_time
@ vrpn_TEXT_ERROR
#define VRPN_CALLBACK
const char * vrpn_dropped_last_connection
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:321
#define vrpn_gettimeofday
Definition: vrpn_Shared.h:99