vrpn  07.33
Virtual Reality Peripheral Network
vrpn_Retrolink.C
Go to the documentation of this file.
1 // vrpn_Retrolink.C: VRPN driver for Retrolink Classic Controller devices
2 
3 #include <stdio.h> // for fprintf, stderr, NULL
4 #include <string.h> // for memset
5 #include <math.h> // for fabs
6 
7 #include "vrpn_Retrolink.h"
9 
10 #if defined(VRPN_USE_HID)
11 
12 static const double POLL_INTERVAL = 1e+6 / 30.0; // If we have not heard, ask.
13 
14 // USB vendor and product IDs for the models we support
15 static const vrpn_uint16 RETROLINK_VENDOR = 0x0079;
16 static const vrpn_uint16 RETROLINK_GAMECUBE = 0x0006;
17 
18 // A channel goes from -1 (value 0) to 1 (value 255), with
19 // an initial value between 0x80 and 0x83 for the one I developed
20 // with.
21 // XXX Consider adding a dead zone and putting it in the config file
22 static vrpn_float64 normalize_axis(const vrpn_uint8 value)
23 {
24  vrpn_float64 offset = static_cast<vrpn_float64>(value) - 128;
25  vrpn_float64 scaled = offset / 127;
26  if (scaled > 1) { scaled = 1; }
27  if (scaled < -1) { scaled = -1; }
28  return scaled;
29 }
30 
31 // Convert the 0-f nybble for the rocker switch into its angle
32 static void angle_and_buttons_from_rocker(const vrpn_uint8 value,
33  vrpn_float64 *angle,
34  bool *up, bool *right, bool *down, bool *left)
35 {
36  switch (value) {
37  case 0: *angle = 0; *up = true; *right = false; *down = false; *left = false; break;
38  case 1: *angle = 45; *up = true; *right = true; *down = false; *left = false; break;
39  case 2: *angle = 90; *up = false; *right = true; *down = false; *left = false; break;
40  case 3: *angle = 135; *up = false; *right = true; *down = true; *left = false; break;
41  case 4: *angle = 180; *up = false; *right = false; *down = true; *left = false; break;
42  case 5: *angle = 225; *up = false; *right = false; *down = true; *left = true; break;
43  case 6: *angle = 270; *up = false; *right = false; *down = false; *left = true; break;
44  case 7: *angle = 315; *up = true; *right = false; *down = false; *left = true; break;
45  default: *angle = -1; *up = false; *right = false; *down = false; *left = false; break;
46  }
47 }
48 
50  : _filter(filter)
51  , vrpn_HidInterface(filter)
52  , vrpn_BaseClass(name, c)
53 {
54  init_hid();
55 }
56 
58 {
59  delete _filter;
60 }
61 
63  // Get notifications when clients connect and disconnect
66 }
67 
68 void vrpn_Retrolink::on_data_received(size_t bytes, vrpn_uint8 *buffer)
69 {
70  decodePacket(bytes, buffer);
71 }
72 
74 {
75  vrpn_Retrolink *me = static_cast<vrpn_Retrolink *>(thisPtr);
76  return 0;
77 }
78 
80 {
81  vrpn_Retrolink *me = static_cast<vrpn_Retrolink *>(thisPtr);
82  return 0;
83 }
84 
86  : vrpn_Retrolink(_filter = new vrpn_HidProductAcceptor(RETROLINK_VENDOR, RETROLINK_GAMECUBE), name, c)
87  , vrpn_Button_Filter(name, c)
88  , vrpn_Analog(name, c)
89 {
92 
93  // Initialize the state of all the analogs, buttons
94  memset(buttons, 0, sizeof(buttons));
95  memset(lastbuttons, 0, sizeof(lastbuttons));
96  memset(channel, 0, sizeof(channel));
97 }
98 
100 {
101  update();
102  server_mainloop();
103  struct timeval current_time;
104  vrpn_gettimeofday(&current_time, NULL);
105  if (vrpn_TimevalDuration(current_time, _timestamp) > POLL_INTERVAL ) {
106  _timestamp = current_time;
107  report_changes();
108 
109  if (vrpn_Analog::num_channel > 0)
110  {
112  }
113  if (vrpn_Button::num_buttons > 0)
114  {
116  }
117  }
118 }
119 
120 void vrpn_Retrolink_GameCube::report(vrpn_uint32 class_of_service) {
121  if (vrpn_Analog::num_channel > 0)
122  {
124  }
125  if (vrpn_Button::num_buttons > 0)
126  {
128  }
129 
130  if (vrpn_Analog::num_channel > 0)
131  {
132  vrpn_Analog::report(class_of_service);
133  }
134  if (vrpn_Button::num_buttons > 0)
135  {
137  }
138 }
139 
140 void vrpn_Retrolink_GameCube::report_changes(vrpn_uint32 class_of_service) {
141  if (vrpn_Analog::num_channel > 0)
142  {
144  }
145  if (vrpn_Button::num_buttons > 0)
146  {
148  }
149 
150  if (vrpn_Analog::num_channel > 0)
151  {
152  vrpn_Analog::report(class_of_service);
153  }
154  if (vrpn_Button::num_buttons > 0)
155  {
157  }
158 }
159 
160 void vrpn_Retrolink_GameCube::decodePacket(size_t bytes, vrpn_uint8 *buffer)
161 {
162  /*
163  // Print the report so we can figure out what is going on.
164  for (size_t i = 0; i < bytes; i++) {
165  printf("%02x ", buffer[i]);
166  }
167  printf("\n");
168  return;
169  */
170 
171  // Because there is only one type of report, the initial "0" report-type
172  // byte is removed by the HIDAPI driver.
173  // Reports should be 8 bytes. They are encoded as follows:
174  // 0 = Left joystick X axis, goes from 0 (left) to 255 (right)
175  // 1 = Left joystick Y axis, goes from 0 (up) to 255 (down)
176  // 2 = Uncontrolled jibberish, must be from an unconnected channel
177  // 3 = Left joystick X axis, goes from 0 (left) to 255 (right)
178  // 4 = Left joystick Y axis, goes from 0 (left) to 255 (right)
179  // 5 upper nybble = bit 0 (Y), 1 (X), 2 (A), 3 (B)
180  // 5 lower nybble = rocker: 255 when nothing; 0 (up), 1 (UR), 2 (right), .. 7 (UL)
181  // 6 = bit # 0 (left trigger), 1 (right trigger), 2 (Z), 5 (Start/pause)
182  // 7 = always 0x20 (maybe unconnected buttons; probably safest to ignore)
183 
184  if (bytes == 8) {
185  // Figure out the joystick axes.
186  channel[0] = normalize_axis(buffer[0]);
187  channel[1] = normalize_axis(buffer[1]);
188  channel[2] = normalize_axis(buffer[3]);
189  channel[3] = normalize_axis(buffer[4]);
190 
191  // Figure out the buttons.
192  buttons[0] = (buffer[5] & (1 << 4)) != 0;
193  buttons[1] = (buffer[5] & (1 << 5)) != 0;
194  buttons[2] = (buffer[5] & (1 << 6)) != 0;
195  buttons[3] = (buffer[5] & (1 << 7)) != 0;
196  buttons[4] = (buffer[6] & (1 << 0)) != 0;
197  buttons[5] = (buffer[6] & (1 << 1)) != 0;
198  buttons[6] = (buffer[6] & (1 << 2)) != 0;
199  buttons[7] = (buffer[6] & (1 << 5)) != 0;
200 
201  // Figure out the rocker.
202  vrpn_uint8 rocker = buffer[5] & 0x0f;
203  vrpn_float64 angle;
204  bool up, right, down, left;
205  angle_and_buttons_from_rocker(rocker, &angle, &up, &right, &down, &left);
206  channel[4] = angle;
207  buttons[8] = up;
208  buttons[9] = right;
209  buttons[10] = down;
210  buttons[11] = left;
211  } else {
212  fprintf(stderr, "vrpn_Retrolink_GameCube: Found a corrupted report; # total bytes = %u\n", static_cast<unsigned>(bytes));
213  }
214 }
215 
216 // End of VRPN_USE_HID
217 #endif
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_got_connection
const char * vrpn_got_connection
Definition: vrpn_Connection.C:185
vrpn_TimevalDuration
unsigned long vrpn_TimevalDuration(struct timeval endT, struct timeval startT)
Return number of microseconds between startT and endT.
Definition: vrpn_Shared.C:129
vrpn_Button_Filter::report_changes
virtual void report_changes(void)
Definition: vrpn_Button.C:382
vrpn_Analog::channel
vrpn_float64 channel[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:38
vrpn_HidInterface
Definition: vrpn_HumanInterface.h:68
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::timestamp
struct timeval timestamp
Definition: vrpn_Analog.h:41
vrpn_HidInterface::update
virtual void update()
Polls the device buffers and causes on_data_received callbacks if appropriate You NEED to call this f...
Definition: vrpn_HumanInterface.C:140
vrpn_HidAcceptor
Definition: vrpn_HumanInterface.h:54
vrpn_Button::num_buttons
vrpn_int32 num_buttons
Definition: vrpn_Button.h:47
POLL_INTERVAL
#define POLL_INTERVAL
Definition: vrpn_IDEA.C:26
vrpn_Button::buttons
unsigned char buttons[vrpn_BUTTON_MAX_BUTTONS]
Definition: vrpn_Button.h:44
vrpn_BaseClassUnique::d_connection
vrpn_Connection * d_connection
Connection that this object talks to.
Definition: vrpn_BaseClass.h:224
vrpn_Connection::register_message_type
virtual vrpn_int32 register_message_type(const char *name)
Definition: vrpn_Connection.C:5074
vrpn_HANDLERPARAM
This structure is what is passed to a vrpn_Connection message callback.
Definition: vrpn_Connection.h:44
vrpn_HidProductAcceptor
Accepts any device with the given vendor and product IDs.
Definition: vrpn_HumanInterface.h:150
vrpn_Button::timestamp
struct timeval timestamp
Definition: vrpn_Button.h:48
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_Button::lastbuttons
unsigned char lastbuttons[vrpn_BUTTON_MAX_BUTTONS]
Definition: vrpn_Button.h:45
VRPN_SUPPRESS_EMPTY_OBJECT_WARNING
#define VRPN_SUPPRESS_EMPTY_OBJECT_WARNING()
Definition: vrpn_Configure.h:495
vrpn_BaseClassUnique::server_mainloop
void server_mainloop(void)
Handles functions that all servers should provide in their mainloop() (ping/pong, for example) Should...
Definition: vrpn_BaseClass.C:603
vrpn_BaseClass
Class from which all user-level (and other) classes that communicate with vrpn_Connections should der...
Definition: vrpn_BaseClass.h:313
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