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