vrpn  07.33
Virtual Reality Peripheral Network
vrpn_Spaceball.C
Go to the documentation of this file.
1 // vrpn_Spaceball.C
2 // This is a driver for the 3DConnexions (was Labtec/Spacetec.)
3 // http://www.3dconnexions.com/
4 // Spaceball 6DOF motion controller, with 2, 9, or 12 buttons and
5 // a ball that translational and rotational forces can be
6 // applied to. It uses an RS-232 interface and can be communicated
7 // with using either a raw serial protocol (done in this driver),
8 // or by using the X-Windows or Microsoft Windows drivers supplied
9 // by the hardware vendor.
10 //
11 // This driver was written by John E. Stone, based on his previous Spaceball
12 // driver implementations. The VRPN version of this code has only been
13 // directly tested with the Spaceball 2003 version of the device so far,
14 // though the original standalone libsball code it is based on is known
15 // to work with the 3003 and 4000 FLX devices, so the VRPN driver should too.
16 // If you have questions about this code in general, please email the author:
17 // johns@megapixel.com j.stone@acm.org johns@ks.uiuc.edu
18 // http://www.megapixel.com/
19 //
20 // Guidance for the VRPN version of this driver was obtained by
21 // basing it on the vrpn_Magellan code, where possible.
22 //
23 // Comments about this code:
24 // Spots in the code marked with XXX are either of particular interest
25 // as they could be improved, or because they are a VRPN-interpretation of
26 // functionality written in libsball that was changed for VRPN.
27 // In particular, it might be useful for VRPN to know whether or not it is
28 // talking to a Spaceball 2003, 3003, or 4000, but this implementation does
29 // not set any status bits in VRPN to make such information available.
30 // Should the "lefty" mode of the 4000 be reported as a button?
31 // At present, none of this state is exposed to VRPN, though it might
32 // be useful in some cases. Also, the VRPN code hides the differences
33 // between the 2003, 3003, and 4000 button handling, where libsball
34 // gives them separate types of button status. Either interpretation
35 // is useful, depending on what the application people want, but since
36 // VRPN is supposed to be very general purpose, I chose to make this
37 // driver hide as much of the device specific stuff as possible.
38 // The main issues between the devices are the actual number of buttons
39 // on the devices, and the interpretation of the "rezero" button,
40 // the interpretation of the "pick" button, and the interpretation of
41 // the "lefty" mode on the Spaceball 4000. These can easily be
42 // changed to better suit VRPN's needs however if some other behavior
43 // is preferable.
44 //
45 // The current button mapping in VRPN (only verified on 2003 so far)
46 // is as follows:
47 //
48 // Spaceball Model
49 // VRPN Button 1003 2003 3003 4000
50 // 1 1 L 1
51 // 2 2 R 2
52 // 3 3 3
53 // 4 4 4
54 // 5 5 5
55 // 6 6 6
56 // 7 7 7
57 // 8 8/Zero Zero 8
58 // 9 Pick 9
59 // 10 10 ("aka A?")
60 // 11 11 ("aka B?")
61 // 12 12 ("aka C?")
62 //
63 // Enjoy,
64 // John E. Stone
65 // johns@megapixel.com
66 // j.stone@acm.org
67 // johns@ks.uiuc.edu
68 //
69 
70 #include <stdio.h> // for fprintf, stderr
71 #include <string.h> // for NULL, strlen
72 
73 #include "vrpn_Serial.h" // for vrpn_flush_input_buffer, etc
74 #include "vrpn_Shared.h" // for timeval, vrpn_SleepMsecs, etc
75 #include "vrpn_Spaceball.h"
76 
77 // turn on for debugging code, leave off otherwise
78 #undef VERBOSE
79 
80 #if defined(VERBOSE)
81 #include <ctype.h> // for isprint()
82 
83 #define DEBUG 1
84 #endif
85 
86 // Defines the modes in which the box can find itself.
87 #define STATUS_RESETTING (-1) // Resetting the device
88 #define STATUS_SYNCING (0) // Looking for the first char of report
89 #define STATUS_READING (1) // Looking for the rest of the report
90 #define MAX_TIME_INTERVAL (2000000) // max time between reports (usec)
91 
92 // This creates a vrpn_Spaceball and sets it to reset mode. It opens
93 // the serial device using the code in the vrpn_Serial_Analog constructor.
95  const char * port, int baud):
96  vrpn_Serial_Analog(name, c, port, baud),
97  vrpn_Button_Filter(name, c),
98  _numbuttons(12),
99  _numchannels(6),
100  null_radius(8)
101 {
102  // Set the parameters in the parent classes
105 
106  // Set the status of the buttons and analogs to 0 to start
107  clear_values();
108 
109  // Set the mode to reset
111 }
112 
114 {
115  int i;
116 
117  for (i = 0; i < _numbuttons; i++) {
119  }
120  for (i = 0; i < _numchannels; i++) {
122  }
123 }
124 
125 
126 
127 
128 
129 // This routine will reset the Spaceball, zeroing the position,
130 // mode and making the device beep.
132 
133  /* Spaceball initialization string. Newer documentation suggests */
134  /* eliminating several init settings, leaving them at defaults. */
135  const char *reset_str = "CB\rNT\rFTp\rFRp\rP@r@r\rMSSV\rZ\rBcCcC\r";
136 
137  // Set the values back to zero for all buttons, analogs and encoders
138  clear_values();
139 
140  /* Reset some state variables back to zero */
141  bufpos = 0;
142  packtype = 0;
143  packlen = 0;
144  escapedchar = 0;
145  erroroccured = 0;
146  resetoccured = 0;
147  spaceball4000 = 0; /* re-determine which type it is */
148  leftymode4000 = 0; /* re-determine if its in lefty mode */
149  null_radius = 8; // The NULL radius is now set to 8
150 
151  // Send commands to the device to cause it to reset and beep.
153  vrpn_write_slowly(serial_fd, (unsigned char *)reset_str, strlen(reset_str), 5);
154 
155  // We're now waiting for a response from the box
157 
158  vrpn_gettimeofday(&timestamp, NULL); // Set watchdog now
159  return 0;
160 }
161 
162 
163 
164 // This function will read characters until it has a full report, then
165 // put that report into the time, analog, or button fields and call
166 // the report methods on these. The time stored is that of
167 // the first character received as part of the report.
168 // Reports start with different characters, and the length of the report
169 // depends on what the first character of the report is. We switch based
170 // on the first character of the report to see how many more to expect and
171 // to see how to handle the report.
172 // Returns 1 if there is a complete report found, 0 otherwise. This is
173 // so that the calling routine can know to check again at the end of complete
174 // reports to see if there is more than one report buffered up.
175 
177 {
178  unsigned char rawbuf[1024]; // raw unprocessed incoming characters
179  int i, num, packs;
180 #if defined(DEBUG)
181  int j;
182 #endif
183  packs = 0; /* no packs received yet */
184 
185  // read up to 1023 unprocessed characters from the serial device at once
186  num = vrpn_read_available_characters(serial_fd, rawbuf, 1023);
187 
188  // if we receive 1 or more chars, we will see if this completes any
189  // pending packets we're trying to process.
190  if (num > 0) {
191  for (i=0; i<num; i++) {
192  /* process potentially occurring escaped character sequences */
193  if (rawbuf[i] == '^') {
194  if (!escapedchar) {
195  escapedchar = 1;
196  continue; /* eat the escape character from buffer */
197  }
198  }
199 
200  /* if in escaped mode, we convert the escaped char to final form */
201  if (escapedchar) {
202  escapedchar = 0; /* we're back out of escape mode after this */
203 
204  switch (rawbuf[i]) {
205  case '^': /* leave char in buffer unchanged */
206  break;
207 
208  case 'Q':
209  case 'S':
210  case 'M':
211  rawbuf[i] &= 0x1F; /* convert character to unescaped form */
212  break;
213 
214  default:
215 #if defined(DEBUG)
216  printf("\nGot a bad escape sequence! 0x%02x", rawbuf[i]);
217  if (isprint(rawbuf[i]))
218  printf(" (%c)", rawbuf[i]);
219  else
220  printf(" (unprintable)");
221  printf("\n");
222 #endif
223  break;
224  }
225  }
226 
227 
228  /* figure out what kind of packet we received */
229  if (bufpos == 0) {
230  status = STATUS_SYNCING; /* update our status */
231 
232  switch(rawbuf[i]) {
233  case 'D': /* Displacement packet */
234  packtype = 'D';
235  packlen = 16; /* D packets are 15 bytes long */
236  break;
237 
238  case 'K': /* Button/Key packet */
239  packtype = 'K';
240  packlen = 4; /* K packets are 3 bytes long */
241  break;
242 
243  case '.': /* Spaceball 4000 FLX "advanced" button press event */
244  packtype = '.';
245  packlen = 4; /* . packets are 3 bytes long */
246  break;
247 
248  case 'C': /* Communications mode packet */
249  packtype = 'C';
250  packlen = 4;
251  break;
252 
253  case 'F': /* Spaceball sensitization mode packet */
254  packtype = 'F';
255  packlen = 4;
256  break;
257 
258  case 'M': /* Movement mode packet */
259  packtype = 'M';
260  packlen = 5;
261  break;
262 
263  case 'N': /* Null region packet */
264  packtype = 'N';
265  packlen = 3;
266  break;
267 
268  case 'P': /* Update rate packet */
269  packtype = 'P';
270  packlen = 6;
271  break;
272 
273  case '\v': /* XON at poweron */
274  packtype = '\v';
275  packlen = 1;
276  break;
277 
278  case '\n': /* carriage return at poweron */
279  case '\r': /* carriage return at poweron */
280  packtype = '\r';
281  packlen = 1;
282  break;
283 
284  case '@': /* Spaceball Hard/Soft Reset packet */
285  resetoccured=1;
286  packtype = '@';
287  packlen = 62; /* Resets aren't longer than 62 chars */
288  break;
289 
290  case 'E': /* Error packet */
291  packtype = 'E';
292  packlen = 8; /* E packets are up to 7 bytes long */
293  break;
294 
295  case 'Z': /* Zero packet (Spaceball 2003/3003/4000 FLX) */
296  packtype = 'Z';
297  packlen = 14; /* Z packets are hardware dependent */
298  break;
299 
300  default: /* Unknown packet! */
301 #if defined(DEBUG)
302  printf("\nUnknown packet (1) [%d]: 0x%02x \n ", i, rawbuf[i]);
303  printf(" char: ");
304  if (isprint(rawbuf[i]))
305  printf("%c", rawbuf[i]);
306  else
307  printf(" (unprintable)");
308  printf("\n");
309 #endif
310  continue;
311  }
312  }
313 
314 
315  buf[bufpos] = rawbuf[i]; /* copy processed chars into long-term buffer */
316  bufpos++; /* go to next buffer slot */
317 
318  /* Reset packet processing */
319  if (packtype == '@') {
320  if (rawbuf[i] != '\r')
321  continue;
322  else
323  packlen = bufpos;
324  }
325 
326  /* Error packet processing */
327  if (packtype == 'E') {
328  if (rawbuf[i] != '\r')
329  continue;
330  else
331  packlen = bufpos;
332  } else if (bufpos != packlen)
333  continue;
334 
335  status = STATUS_READING; // ready to process event packet
336  vrpn_gettimeofday(&timestamp, NULL); // set timestamp of this event
337 
338  switch (packtype) {
339  case 'D': /* ball displacement event */
340  {
341  int nextchar, chan;
342 
343  /* number of 1/16ths of milliseconds since last */
344  /* ball displacement packet */
345 
346  nextchar = 1; // this is where the timer data is, if we want it.
347  nextchar = 3; // Skip the zeroeth character (the command)
348  for (chan = 0; chan < _numchannels; chan++) {
349  long intval;
350  intval = (buf[nextchar++]) << 8;
351  intval |= (buf[nextchar++]);
352  intval = (intval << 16) >> 16;
353 
354  // If the absolute value of the integer is <= the NULL
355  // radius, it should be set to zero.
356  if ( (intval <= null_radius) && (intval >= - null_radius) ) {
357  intval = 0;
358  }
359 
360  // maximum possible values per axis are +/- 32768, although the
361  // largest value I've ever observed among several devices
362  // is only 20,000. For now, we'll divide by 32768, and if this is
363  // too insensitive, we can do something better later.
364  double realval = intval / 32768.0;
365  channel[chan] = realval;
366  // printf("XXX Channel[%d] = %f %d \n", z, realval, intval);
367  }
368  channel[2] = -channel[2]; // Negate Z translation
369  channel[5] = -channel[5]; // Negate Z rotation
370  }
371  break;
372 
373  case 'K': /* button press event */
374  /* Spaceball 2003A, 2003B, 2003 FLX, 3003 FLX, 4000 FLX */
375  /* button packet. (4000 only for backwards compatibility) */
376  /* The lowest 5 bits of the first byte are buttons 5-9 */
377  /* Button '8' on a Spaceball 2003 is the rezero button */
378  /* The lowest 4 bits of the second byte are buttons 1-4 */
379  /* For Spaceball 2003, we'll map the buttons 1-7 normally */
380  /* skip 8, as its a hardware "rezero button" on that device */
381  /* and call the "pick" button "8". */
382  /* On the Spaceball 3003, the "right" button also triggers */
383  /* the "pick" bit. We OR the 2003/3003 rezero bits together */
384 
385  /* if we have found a Spaceball 4000, then we ignore the 'K' */
386  /* packets entirely, and only use the '.' packets. */
387  if (spaceball4000)
388  break;
389 
390 // XXX my original libsball button decoding code, for reference
391 // buttons =
392 // ((buf[1] & 0x10) << 3) | /* 2003 pick button is "8" */
393 // ((buf[1] & 0x20) << 9) | /* 3003 rezero button */
394 // ((buf[1] & 0x08) << 11) | /* 2003 rezero button */
395 // ((buf[1] & 0x07) << 4) | /* 5,6,7 (2003/4000) */
396 // ((buf[2] & 0x30) << 8) | /* 3003 Left/Right buttons */
397 // ((buf[2] & 0x0F)); /* 1,2,3,4 (2003/4000) */
398 
399  // Mapping the buttons is tricky since different models
400  // of the Spaceball have different button sets.
401  // 2003 has 9 buttons (rezero included)
402  // 3003 has 3 buttons (rezero included)
403  // 4000 has 12 buttons, and can be in "lefty" or "righty" mode.
404  // We'll skip reporting lefty/righty mode for now though.
405 
406  // Spaceball 2003/4000 buttons 1-4 mapped to slots 0-3
407  // Spaceball 3003 L/R buttons are mapped as slots 0/1
408  buttons[0] = static_cast<unsigned char>(((buf[2] & 0x01) != 0) | ((buf[2] & 0x10) != 0));
409  buttons[1] = static_cast<unsigned char>(((buf[2] & 0x02) != 0) | ((buf[2] & 0x20) != 0));
410  buttons[2] = static_cast<unsigned char>(((buf[2] & 0x04) != 0));
411  buttons[3] = static_cast<unsigned char>(((buf[2] & 0x08) != 0));
412 
413  // Spaceball 2003/4000 buttons 5,6,7 mapped to slots 4-6
414  buttons[4] = static_cast<unsigned char>(((buf[1] & 0x01) != 0));
415  buttons[5] = static_cast<unsigned char>(((buf[1] & 0x02) != 0));
416  buttons[6] = static_cast<unsigned char>(((buf[1] & 0x04) != 0));
417 
418  // Spaceball 2003/3003 rezero buttons are mapped to slot 7
419  // The rezero button's function is sometimes hard-wired,
420  // and may or may not be used by applications, this is up
421  // in the air still. For now, I'll map it to slot 7 which
422  // keeps the numbering in a sane order on the 2003 device anyway.
423  buttons[7] = static_cast<unsigned char>(((buf[1] & 0x20) != 0) | ((buf[1] & 0x08) != 0));
424 
425  // Spaceball 2003 pick button mapped to slot 8
426  // Note: the pick button is the button embedded into the front
427  // surface of the control sphere.
428  buttons[8] = static_cast<unsigned char>(((buf[1] & 0x10) != 0));
429  break;
430 
431 
432  case '.': /* button press event (4000) */
433  /* Spaceball 4000 FLX "expanded" button packet, with 12 buttons */
434 
435  /* extra packet validity check, since we use this packet type */
436  /* to override the 'K' button packets, and determine if its a */
437  /* Spaceball 4000 or not... */
438  if (buf[3] != '\r') {
439  break; /* if not terminated with a '\r', probably garbage */
440  }
441 
442  /* if we got a valid '.' packet, this must be a Spaceball 4000 */
443 #if defined(DEBUG)
444  if (!spaceball4000)
445  printf("\nDetected a Spaceball 4000 FLX\n");
446 #endif
447  spaceball4000 = 1; /* Must be talking to a Spaceball 4000 */
448 
449 // XXX my original libsball button decoding code, for reference
450 // buttons =
451 // (((~buf[1]) & 0x20) << 10) | /* "left handed" mode */
452 // ((buf[1] & 0x1F) << 7) | /* 8,9,10,11,12 */
453 // ((buf[2] & 0x3F) ) | /* 1,2,3,4,5,6 */
454 // ((buf[2] & 0x80) >> 1); /* 7 */
455 
456  /* Spaceball 4000 series "expanded" button press event */
457  /* includes data for 12 buttons, and left/right orientation */
458  buttons[0] = ((buf[2] & 0x01) != 0); // SB 4000 button 1
459  buttons[1] = ((buf[2] & 0x02) != 0); // SB 4000 button 2
460  buttons[2] = ((buf[2] & 0x04) != 0); // SB 4000 button 3
461  buttons[3] = ((buf[2] & 0x08) != 0); // SB 4000 button 4
462  buttons[4] = ((buf[2] & 0x10) != 0); // SB 4000 button 5
463  buttons[5] = ((buf[2] & 0x20) != 0); // SB 4000 button 6
464  buttons[6] = ((buf[2] & 0x80) != 0); // SB 4000 button 7
465 
466  buttons[7] = ((buf[1] & 0x01) != 0); // SB 4000 button 8
467  buttons[8] = ((buf[1] & 0x02) != 0); // SB 4000 button 9
468  buttons[9] = ((buf[1] & 0x04) != 0); // SB 4000 button 10
469  buttons[10] = ((buf[1] & 0x08) != 0); // SB 4000 button 11
470  buttons[11] = ((buf[1] & 0x10) != 0); // SB 4000 button 12
471 
472  // XXX lefty/righty mode handling goes here if we wish to
473  // represent it as a "button" etc..
474  //buttons[??] = ((~buf[1]) & 0x20) != 0) // SB 4000 "lefty" mode bit
475 
476 #if defined(DEBUG)
477  if (leftymode4000 != ((buf[1] & 0x20) == 0))
478  printf("\nSpaceball 4000 mode changed to: %s\n",
479  (((buf[1] & 0x20) == 0) ? "left handed" : "right handed"));
480 #endif
481  /* set "lefty" orientation mode if "lefty bit" is _clear_ */
482  if ((buf[1] & 0x20) == 0)
483  leftymode4000 = 1; /* left handed mode */
484  else
485  leftymode4000 = 0; /* right handed mode */
486  break;
487 
488  case 'C': /* Communications mode packet */
489  case 'F': /* Spaceball sensitization packet */
490  case 'P': /* Spaceball update rate packet */
491  case 'M': /* Spaceball movement mode packet */
492  case 'N': /* Null region packet */
493  case '\r': /* carriage return at poweron */
494  case '\v': /* XON at poweron */
495  /* eat and ignore these packets */
496  break;
497 
498  case '@': /* Reset packet */
499 #if defined(DEBUG)
500  printf("Spaceball reset: ");
501  for (j=0; j<packlen; j++) {
502  if (isprint(buf[j]))
503  printf("%c", buf[j]);
504  }
505  printf("\n");
506 #endif
507  /* if we get a reset packet, we have to re-initialize */
508  /* the device, and assume that its completely schizophrenic */
509  /* at this moment, we must reset it again at this point */
510  resetoccured=1;
511  reset(); // was sball_hwreset()
512  break;
513 
514 
515  case 'E': /* Error packet, hardware/software problem */
516  erroroccured++;
517 #if defined(DEBUG)
518  printf("\nSpaceball Error!! ");
519  printf("Error code: ");
520  for (j=0; j<packlen; j++) {
521  printf(" 0x%02x ", buf[j]);
522  }
523  printf("\n");
524 #endif
525  break;
526 
527  case 'Z': /* Zero packet (Spaceball 2003/3003/4000 FLX) */
528  /* We just ignore these... */
529  break;
530 
531  default:
532 #if defined(DEBUG)
533  printf("Unknown packet (2): 0x%02x\n", packtype);
534  printf(" char: ");
535  if (isprint(packtype))
536  printf("%c", packtype);
537  else
538  printf(" (unprintable)");
539  printf("\n");
540 #endif
541  break;
542  }
543 
544  /* reset */
545  bufpos = 0;
546  packtype = 0;
547  packlen = 1;
548  packs++;
549  }
550  }
551 
552  report_changes(); // Report updates to VRPN
553 
554  if (packs > 0)
555  return 1; // got at least 1 full report
556  else
557  return 0; // didn't get any full reports
558 }
559 
560 void vrpn_Spaceball::report_changes(vrpn_uint32 class_of_service)
561 {
564 
565  vrpn_Analog::report_changes(class_of_service);
567 }
568 
569 void vrpn_Spaceball::report(vrpn_uint32 class_of_service)
570 {
573 
574  vrpn_Analog::report(class_of_service);
576 }
577 
578 // This routine is called each time through the server's main loop. It will
579 // take a course of action depending on the current status of the Spaceball,
580 // either trying to reset it or trying to get a reading from it.
582 {
583  server_mainloop();
584 
585  switch(status) {
586  case STATUS_RESETTING:
587  reset();
588  break;
589 
590  case STATUS_SYNCING:
591  case STATUS_READING:
592  // Keep getting reports until all full reports are read.
593  while (get_report()) {};
594  break;
595 
596  default:
597  fprintf(stderr,"vrpn_Spaceball: Unknown mode (internal error)\n");
598  break;
599  }
600 }
601 
602 
virtual int reset(void)
Set device back to starting config.
void server_mainloop(void)
Handles functions that all servers should provide in their mainloop() (ping/pong, for example) Should...
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
#define STATUS_RESETTING
#define STATUS_READING
int erroroccured
A device error has occurred.
int escapedchar
We&#39;re processing an escaped char.
int packtype
What kind of packet we are decoding.
vrpn_int32 num_buttons
Definition: vrpn_Button.h:47
int vrpn_flush_input_buffer(int comm)
Throw out any characters within the input buffer.
Definition: vrpn_Serial.C:435
vrpn_Spaceball(const char *name, vrpn_Connection *c, const char *port, int baud)
vrpn_Serial: Pulls all the serial port routines into one file to make porting to new operating system...
struct timeval timestamp
Time of the last report from the device.
vrpn_float64 channel[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:38
int _numchannels
How many analog channels to open.
int null_radius
range where no motion should be reported
Generic connection class not specific to the transport mechanism.
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 mainloop()
Called once through each main loop iteration to handle updates.
int vrpn_write_slowly(int comm, const unsigned char *buffer, size_t bytes, int millisec_delay)
Definition: vrpn_Serial.C:670
virtual void report_changes(void)
Definition: vrpn_Button.C:382
virtual void report_changes(void)
Definition: vrpn_Button.C:422
vrpn_int32 num_channel
Definition: vrpn_Analog.h:40
int vrpn_read_available_characters(int comm, unsigned char *buffer, size_t bytes)
Definition: vrpn_Serial.C:512
unsigned char buf[512]
Buffer of characters in report,.
int bufpos
Current char pos in buffer.
#define STATUS_SYNCING
int _numbuttons
How many buttons to open.
int resetoccured
A reset event has occurred.
struct timeval timestamp
Definition: vrpn_Button.h:48
virtual void clear_values(void)
Set all buttons, analogs and encoders back to 0.
#define vrpn_gettimeofday
Definition: vrpn_Shared.h:89
int spaceball4000
We found a Spaceball 4000.
All button servers should derive from this class, which provides the ability to turn any of the butto...
Definition: vrpn_Button.h:65
unsigned char lastbuttons[vrpn_BUTTON_MAX_BUTTONS]
Definition: vrpn_Button.h:45
int leftymode4000
Spaceball 4000 is in lefty mode.
virtual int get_report(void)
Try to read reports from the device. Returns 1 if a complete report received, 0 otherwise. Sets status to current mode.
unsigned char buttons[vrpn_BUTTON_MAX_BUTTONS]
Definition: vrpn_Button.h:44
int packlen
Expected packet length.
virtual void report(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY)
send report whether or not changed
struct timeval timestamp
Definition: vrpn_Analog.h:41
vrpn_float64 last[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:39