vrpn 07.35
Virtual Reality Peripheral Network
Loading...
Searching...
No Matches
vrpn_BaseClass.C
Go to the documentation of this file.
1// vrpn_BaseClass.C
2
3#include <stddef.h> // for size_t
4#include <stdio.h> // for fprintf, NULL, stderr, etc
5#include <string.h> // for strcmp, strlen
6
7#include "vrpn_BaseClass.h"
8#include "vrpn_Shared.h" // for timeval, vrpn_buffer, etc
9
10//#define VERBOSE
11
15static vrpn_TextPrinter vrpn_System_TextPrinter_instance;
16// SWIG does not like this statement
17#ifndef SWIG
18vrpn_TextPrinter &vrpn_System_TextPrinter = vrpn_System_TextPrinter_instance;
19#endif
20
22 : d_first_watched_object(NULL)
23 , d_ostream(stdout)
24 , d_severity_to_print(vrpn_TEXT_WARNING)
25 , d_level_to_print(0)
26{
27}
28
31{
33/* XXX No longer removes these. We get into trouble with the
34 system-defined vrpn_System_TextPrinter destructor because it
35 may run after the vrpn_ConnectionManager destructor has run,
36 which (if we have some undeleted objects) will leave objects
37 that don't have a NULL connection pointer, but whose pointers
38 point to already-deleted connections. This causes a crash. */
39/* XXX Did the above change to making the vrpn_System_TextPrinter
40 static remove the race condition, so that it is safe to put
41 this back in? */
42#if 0
43
44 vrpn_TextPrinter_Watch_Entry *victim, *next;
45 vrpn_BaseClass *obj;
46
48 while (victim != NULL) {
49 next = victim->next;
50 obj = victim->obj;
51
52 // Guard against the case where the object has set its connection pointer
53 // to NULL, which is how some objects notify themselves that they are
54 // broken.
55 if (obj->connectionPtr()) {
57 }
58 try {
59 delete victim;
60 } catch (...) {
61 fprintf(stderr, "vrpn_TextPrinter::~vrpn_TextPrinter: delete failed\n");
62 return;
63 }
64 victim = next;
65 }
66#endif // XXX
67}
68
79{
82
83 // Make sure we have an actual object.
84 if (o == NULL) {
85 fprintf(stderr,
86 "vrpn_TextPrinter::add_object(): NULL pointer passed\n");
87 return -1;
88 }
89
90#ifdef VERBOSE
91 printf("vrpn_TextPrinter: adding object %s\n", o->d_servicename);
92#endif
93
94 // If the object is already in the list, we are done. It is considered the
95 // same
96 // object if it has the same connection and the same service name.
98 while (victim != NULL) {
99 if ((o->d_connection == victim->obj->d_connection) &&
100 (strcmp(o->d_servicename, victim->obj->d_servicename) == 0)) {
101 return 0;
102 }
103 victim = victim->next;
104 }
105
106 // Add the object to the beginning of the list.
107 try { victim = new vrpn_TextPrinter_Watch_Entry; }
108 catch (...) {
109 fprintf(stderr, "vrpn_TextPrinter::add_object(): out of memory\n");
110 return -1;
111 }
112 victim->obj = o;
113 victim->me = this;
115 d_first_watched_object = victim;
116
117 // Register a callback for the object
119 text_message_handler, victim,
120 o->d_sender_id) != 0) {
121 fprintf(stderr,
122 "vrpn_TextPrinter::add_object(): Can't register callback\n");
124 try {
125 delete victim;
126 } catch (...) {
127 fprintf(stderr, "vrpn_TextPrinter::add_object: delete failed\n");
128 return -1;
129 }
130 return -1;
131 }
132
133 return 0;
134}
135
142{
144 vrpn_TextPrinter_Watch_Entry *victim, **snitch;
145
146#ifdef VERBOSE
147 printf("vrpn_TextPrinter: removing object %s\n", o->d_servicename);
148#endif
149
150 // Make sure we have an actual object.
151 if (o == NULL) {
152 fprintf(stderr,
153 "vrpn_TextPrinter::remove_object(): NULL pointer passed\n");
154 return;
155 }
156
157 // Find the entry in the list (if it is there).
158 // Starts this pointing at the first watched object, so it will
159 // update that object if it is the one who pointed us at the one
160 // to be deleted.
161 snitch = &d_first_watched_object;
162 victim = *snitch;
163 while ((victim != NULL) &&
164 ((o->d_connection != victim->obj->d_connection) ||
165 (strcmp(o->d_servicename, victim->obj->d_servicename) != 0))) {
166
167 snitch = &((*snitch)->next);
168 victim = victim->next;
169 }
170
171 // If the object is on the list, unregister its callback and delete it.
172 if (victim != NULL) {
173 // Unregister the callback for the object, unless its d_connetion
174 // pointer
175 // is NULL (which is a convention used by devices to indicate that they
176 // are broken, so we need to guard against it here).
177 if (o->d_connection) {
180 o->d_sender_id) != 0) {
181 fprintf(stderr, "vrpn_TextPrinter::remove_object(): Can't "
182 "unregister callback\n");
183 }
184 }
185
186 // Remove the entry from the list
187 *snitch = victim->next;
188 try {
189 delete victim;
190 } catch (...) {
191 fprintf(stderr, "vrpn_TextPrinter::remove_object: delete failed\n");
192 return;
193 }
194
195 // We're done.
196 return;
197 }
198
199 // Object not in the list, so we're done.
200 return;
201}
202
211{
214 vrpn_TextPrinter *me = entry->me;
215 vrpn_BaseClass *obj = entry->obj;
216 vrpn_TEXT_SEVERITY severity;
217 vrpn_uint32 level;
218 char message[vrpn_MAX_TEXT_LEN];
220
221#ifdef VERBOSE
222 printf("vrpn_TextPrinter: text handler called\n");
223#endif
224
225 // Make sure we have a valid ostream.
226 if (me->d_ostream == NULL) {
227 return 0;
228 };
229
230 // Decode the message
232 message, &severity, &level, p.buffer) != 0) {
233 fprintf(
234 stderr,
235 "vrpn_TextPrinter::text_message_handler(): Can't decode message\n");
236 return -1;
237 }
238
239 // If the severity and level criteria pass, then print the annotated
240 // message.
241 // The printing is "VRPN", then one of Message/Warning/Error, then the level
242 // of the
243 // text, "from" the name of the service sending the message, and then the
244 // message.
245 if ((severity > me->d_severity_to_print) ||
246 ((severity == me->d_severity_to_print) &&
247 (level >= me->d_level_to_print))) {
248
249 fprintf(me->d_ostream, "VRPN ");
250
251 switch (severity) {
252 case vrpn_TEXT_NORMAL:
253 fprintf(me->d_ostream, "Message\n");
254 break;
256 fprintf(me->d_ostream, "Warning\n");
257 break;
258 case vrpn_TEXT_ERROR:
259 fprintf(me->d_ostream, "Error\n");
260 break;
261 default:
262 fprintf(me->d_ostream, "UNKNOWN SEVERITY\n");
263 break;
264 }
265
266 fprintf(me->d_ostream, " (%d) from %s: %s\n", level,
267 obj->d_connection->sender_name(p.sender), message);
268 }
269 return 0;
270}
271
275 vrpn_uint32 level)
276{
278 d_severity_to_print = severity;
279 d_level_to_print = level;
280}
281
285{
287 d_ostream = o;
288}
289
312{
313 // Has a constructor on this BaseClassUnique been called before?
314 // Note that this might also be true if it was called once before but
315 // failed.
316 bool firstTimeCalled = (d_connection == NULL);
317
318 if (firstTimeCalled) {
319 // Get the connection for this object established. If the user passed in
320 // a NULL connection object, then we determine the connection from the
321 // name of the object itself (for example, Tracker0@mumble.cs.unc.edu
322 // will make a connection to the machine mumble on the standard VRPN
323 // port).
324 //
325 // The vrpn_BaseClassUnique destructor handles telling the connection we
326 // are no longer referring to it. Since we only add the reference once
327 // here (when d_connection is NULL), it is okay for the unique
328 // destructor to remove the reference.
329 if (c) { // using existing connection.
330 d_connection = c;
332 } else {
333 // This will implicitly add the reference to the connection.
335 }
336
337 // Get the part of the name for this device that does not include the
338 // connection.
339 // The vrpn_BaseClassUnique destructor handles the deletion of the
340 // space.
342 }
343}
344
346{
347 // Remove us from the list of objects with messages to be printed
349}
350
359{
360 // In the case of multiple inheritance from this base class, the rest of
361 // the code in this function will be executed each time init is called.
362
363 if (d_connection) {
364 // Register the sender and types
365 // that this device type uses. If one of these fails, set the
366 // connection
367 // for this object to NULL to indicate failure, and print an error
368 // message.
369 if (register_senders() || register_types()) {
370 fprintf(stderr, "vrpn_BaseClassUnique: Can't register IDs\n");
371 d_connection = NULL;
372 return -1;
373 }
374
375 // Register the text and ping/pong types, which will be available to all
376 // classes for use.
378 d_connection->register_message_type("vrpn_Base text_message");
379 if (d_text_message_id == -1) {
380 fprintf(stderr,
381 "vrpn_BaseClassUnique: Can't register Text type ID\n");
382 d_connection = NULL;
383 return -1;
384 }
386 d_connection->register_message_type("vrpn_Base ping_message");
387 if (d_ping_message_id == -1) {
388 fprintf(stderr,
389 "vrpn_BaseClassUnique: Can't register ping type ID\n");
390 d_connection = NULL;
391 return -1;
392 }
394 d_connection->register_message_type("vrpn_Base pong_message");
395 if (d_pong_message_id == -1) {
396 fprintf(stderr,
397 "vrpn_BaseClassUnique: Can't register pong type ID\n");
398 d_connection = NULL;
399 return -1;
400 }
401
402 // Sign us up with the standard print function.
404 return 0;
405 }
406 else {
407 // Error if we don't have a connection.
408 return -1;
409 }
410}
411
423{
424 if (d_connection == NULL) {
425 return -1;
426 }
428 if (d_sender_id == -1) {
429 return -1;
430 }
431 else {
432 return 0;
433 }
434}
435
437 : shutup(false)
438 , // don't suppress the "No response from server" messages
439 d_connection(NULL)
440 , d_servicename(NULL)
441 , d_sender_id(-1)
442 , d_num_autodeletions(0)
443 , d_first_mainloop(1)
444 , d_unanswered_ping(0)
445 , d_flatline(0)
446{
447 // Initialize variables
448 d_time_first_ping.tv_sec = d_time_first_ping.tv_usec = 0;
449}
450
456{
457 int i;
458
459 // Unregister all of the handlers that were to be autodeleted,
460 // if we have a connection.
461 if (d_connection != NULL) {
462 for (i = 0; i < d_num_autodeletions; i++) {
464 d_handler_autodeletion_record[i].type,
465 d_handler_autodeletion_record[i].handler,
466 d_handler_autodeletion_record[i].userdata,
467 d_handler_autodeletion_record[i].sender);
468 }
469 d_num_autodeletions = 0;
470 }
471
472 // notify the connection that this object is no longer using it.
473 // This was added in the vrpn_BaseClass constructor for exactly one of the
474 // objects that are sharing this unique destructor.
475 if (d_connection != NULL) {
477 }
478
479 // Delete the space allocated in the constructor for the servicename
480 // Because this destructor may be called multiple times, set the pointer
481 // to NULL after deleting it, so it won't get deleted again.
482 if (d_servicename != NULL) {
483 try {
484 delete[] d_servicename;
485 } catch (...) {
486 fprintf(stderr, "vrpn_BaseClassUnique::~vrpn_BaseClassUnique: delete failed\n");
487 return;
488 }
489 d_servicename = NULL;
490 }
491}
492
505 vrpn_int32 type, vrpn_MESSAGEHANDLER handler, void *userdata,
506 vrpn_int32 sender)
507{
508 // Make sure we have a Connection
509 if (d_connection == NULL) {
510 fprintf(stderr, "vrpn_BaseClassUnique::register_autodeleted_handler: "
511 "No vrpn_Connection.\n");
512 return -1;
513 }
514
515 // Make sure we have an empy entry to fill in.
516 if (d_num_autodeletions >= vrpn_MAX_BCADRS) {
517 fprintf(stderr,
518 "vrpn_BaseClassUnique::register_autodeleted_handler: "
519 "Too many handlers registered. Increase vrpn_MAX_BCADRS "
520 "and recompile VRPN. Please report to vrpn@cs.unc.edu.\n");
521 return -1;
522 }
523
524 // Fill in the values so we know what to delete, and bump the count
525 d_handler_autodeletion_record[d_num_autodeletions].handler = handler;
526 d_handler_autodeletion_record[d_num_autodeletions].sender = sender;
527 d_handler_autodeletion_record[d_num_autodeletions].type = type;
528 d_handler_autodeletion_record[d_num_autodeletions].userdata = userdata;
529 d_num_autodeletions++;
530
531 // Call the register command.
533}
534
536 char *buf, vrpn_TEXT_SEVERITY severity, vrpn_uint32 level, const char *msg)
537{
538 char *bufptr = buf;
539 int buflen = 2 * sizeof(vrpn_int32) + vrpn_MAX_TEXT_LEN;
540 vrpn_uint32 severity_as_uint = severity;
541
542 // Send the type, level and string message
543 vrpn_buffer(&bufptr, &buflen, severity_as_uint);
544 vrpn_buffer(&bufptr, &buflen, level);
545 vrpn_buffer(&bufptr, &buflen, msg, -1); // -1 means "pack until NULL"
546
547 return 0;
548}
549
551 char *msg, vrpn_TEXT_SEVERITY *severity, vrpn_uint32 *level,
552 const char *buf)
553{
554 const char *bufptr = buf;
555 vrpn_uint32 severity_as_uint;
556
557 // Read the type, level and message
558 vrpn_unbuffer(&bufptr, &severity_as_uint);
559 *severity = (vrpn_TEXT_SEVERITY)(severity_as_uint);
560 vrpn_unbuffer(&bufptr, level);
561 // Negative length means "unpack until NULL"
562 if (vrpn_unbuffer(&bufptr, msg, -(int)(vrpn_MAX_TEXT_LEN)) != 0) {
563 return -1;
564 }
565
566 return 0;
567}
568
570 struct timeval timestamp,
572 vrpn_uint32 level)
573{
574 char buffer[2 * sizeof(vrpn_int32) + vrpn_MAX_TEXT_LEN];
575 size_t len = strlen(msg) + 1; // +1 is for the NULL terminator
576
577 if (len > vrpn_MAX_TEXT_LEN) {
578 fprintf(stderr, "vrpn_BaseClassUnique::send_message: Attempt to encode "
579 "string that is too long\n");
580 return -1;
581 }
582
583 // send type, level and message
584
585 encode_text_message_to_buffer(buffer, type, level, msg);
586 if (d_connection) {
587 d_connection->pack_message(sizeof(buffer), timestamp, d_text_message_id,
588 d_sender_id, buffer,
590 }
591
592 return 0;
593}
594
605{
606 // Set up to handle pong message. This should be sent whenever there is
607 // a ping request from a client.
608
609 if (d_first_mainloop && (d_connection != NULL)) {
612 d_first_mainloop = 0;
613 }
614}
615
639{
640 struct timeval now;
641 struct timeval diff;
642
643 // The first time through, set up a callback handler for the pong message so
644 // that we
645 // know when we are getting them. Also set up a handler for the system
646 // dropped-connection
647 // message so that we can initiate a ping cycle when that happens. Also,
648 // we'll initiate
649 // the ping cycle here.
650
651 if (d_first_mainloop && (d_connection != NULL)) {
652
653 // Set up handlers for the pong message and for the system
654 // connection-drop message
659 handle_connection_dropped, this);
660
661 // Initiate a ping cycle;
662 initiate_ping_cycle();
663
664 // No longer first time through mainloop.
665 d_first_mainloop = 0;
666 }
667
668 // If we are in the middle of a ping cycle...
669 // Check if we've heard, if it has been long enough since we gave a warning
670 // or error (>= 1 sec).
671 // If it has been three seconds or more since we sent our first ping,
672 // start giving warnings. If it has been ten seconds or more since we got
673 // one,
674 // switch to errors. New ping requests go out each second.
675
676 if (d_unanswered_ping) {
677
678 vrpn_gettimeofday(&now, NULL);
679 diff = vrpn_TimevalDiff(now, d_time_last_warned);
681
682 if (diff.tv_sec >= 1) {
683
684 // Send a new ping, since it has been a second since the last one
687
688 // Send another warning or error, and say if we're flatlined (10+
689 // seconds)
690 d_time_last_warned = now;
691 if (!shutup) {
692 diff = vrpn_TimevalDiff(now, d_time_first_ping);
694 if (diff.tv_sec >= 10) {
696 "No response from server for >= 10 seconds", now,
697 vrpn_TEXT_ERROR, diff.tv_sec);
698 d_flatline = 1;
699 }
700 else if (diff.tv_sec >= 3) {
702 "No response from server for >= 3 seconds", now,
703 vrpn_TEXT_WARNING, diff.tv_sec);
704 }
705 }
706 }
707 }
708}
709
710void vrpn_BaseClassUnique::initiate_ping_cycle(void)
711{
712 // Record when we sent the ping and say that we haven't gotten an answer
713 vrpn_gettimeofday(&d_time_first_ping, NULL);
714 d_connection->pack_message(0, d_time_first_ping, d_ping_message_id,
716 d_unanswered_ping = 1;
717
718 // We didn't send a warning about this one yet...
719 d_time_last_warned.tv_sec = d_time_last_warned.tv_usec = 0;
720}
721
729int vrpn_BaseClassUnique::handle_pong(void *userdata, vrpn_HANDLERPARAM p)
730{
732
733 me->d_unanswered_ping = 0;
734
735 // If we were flatlined, report that things are okay again.
736 if (me->d_flatline) {
737 me->send_text_message("Server connection re-established!", p.msg_time,
739 me->d_flatline = 0;
740 }
741
742 return 0;
743}
744
753int vrpn_BaseClassUnique::handle_ping(void *userdata, vrpn_HANDLERPARAM)
754{
756 struct timeval now;
757
758 vrpn_gettimeofday(&now, NULL);
759 if (me->d_connection != NULL) {
761 me->d_sender_id, NULL,
763 }
764
765 return 0;
766}
767
774int vrpn_BaseClassUnique::handle_connection_dropped(void *userdata,
776{
778 struct timeval now;
779
780 if (me->d_unanswered_ping != 0) {
781 return 0;
782 }
783
784 vrpn_gettimeofday(&now, NULL);
785 if (me->d_connection != NULL) {
786 me->initiate_ping_cycle();
787 }
788
789 return 0;
790}
An RAII lock/guard class for vrpn_Semaphore.
INTERNAL class to hold members that there should only be one copy of even when a class inherits from ...
static int decode_text_message_from_buffer(char *msg, vrpn_TEXT_SEVERITY *severity, vrpn_uint32 *level, const char *buf)
Decodes the body of the text message from a buffer from the connection.
vrpn_Connection * connectionPtr()
Returns a pointer to the connection this object is using.
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.
vrpn_Connection * d_connection
Connection that this object talks to.
void client_mainloop(void)
Handles functions that all clients should provide in their mainloop() (warning of no server,...
vrpn_MESSAGEHANDLER handler
vrpn_int32 d_pong_message_id
Server telling that it is there.
vrpn_int32 d_sender_id
Sender ID registered with the connection.
void server_mainloop(void)
Handles functions that all servers should provide in their mainloop() (ping/pong, for example) Should...
vrpn_int32 d_text_message_id
ID for text messages.
virtual ~vrpn_BaseClassUnique()
Unregister all of the message handlers that were to be autodeleted.
int send_text_message(const char *msg, struct timeval timestamp, vrpn_TEXT_SEVERITY type=vrpn_TEXT_NORMAL, vrpn_uint32 level=0)
Sends a NULL-terminated text message from the device d_sender_id.
static int encode_text_message_to_buffer(char *buf, vrpn_TEXT_SEVERITY severity, vrpn_uint32 level, const char *msg)
Encodes the body of the text message into a buffer, preparing for sending.
vrpn_int32 d_ping_message_id
Ask the server if they are there.
char * d_servicename
Name of this device, not including the connection part.
Class from which all user-level (and other) classes that communicate with vrpn_Connections should der...
virtual int register_senders(void)
Register the sender for this device (by default, the name of the device). Return 0 on success,...
virtual int register_types(void)=0
Register the types of messages this device sends/receives. Return 0 on success, -1 on fail.
virtual ~vrpn_BaseClass()
vrpn_BaseClass(const char *name, vrpn_Connection *c=NULL)
Names the device and assigns or opens connection, calls registration methods.
virtual int init(void)
Initialize things that the constructor can't. Returns 0 on success, -1 on failure.
Generic connection class not specific to the transport mechanism.
void addReference()
Counting references to this connection.
virtual const char * sender_name(vrpn_int32 sender)
Returns the name of the specified sender/type, or NULL if the parameter is invalid....
virtual vrpn_int32 register_message_type(const char *name)
virtual int pack_message(vrpn_uint32 len, struct timeval time, vrpn_int32 type, vrpn_int32 sender, const char *buffer, vrpn_uint32 class_of_service)
Pack a message that will be sent the next time mainloop() is called. Turn off the RELIABLE flag if yo...
virtual vrpn_int32 register_sender(const char *name)
Get a token to use for the string name of the sender or type. Remember to check for -1 meaning failur...
virtual int unregister_handler(vrpn_int32 type, vrpn_MESSAGEHANDLER handler, void *userdata, vrpn_int32 sender=vrpn_ANY_SENDER)
virtual int register_handler(vrpn_int32 type, vrpn_MESSAGEHANDLER handler, void *userdata, vrpn_int32 sender=vrpn_ANY_SENDER)
Set up (or remove) a handler for a message of a given type. Optionally, specify which sender to handl...
Structure to hold the objects that are being watched.
vrpn_TextPrinter_Watch_Entry * next
Pointer to the next one in the list.
vrpn_BaseClass * obj
Object being watched.
vrpn_TextPrinter * me
Pointer to this, because used in a static function.
Class that handles text/warning/error printing for all objects in the system.
FILE * d_ostream
Output stream to use.
static int VRPN_CALLBACK text_message_handler(void *userdata, vrpn_HANDLERPARAM p)
Handles the text messages that come from the connections for objects we are watching.
vrpn_TEXT_SEVERITY d_severity_to_print
Minimum severity to print.
vrpn_Semaphore d_semaphore
Mutex to ensure thread safety;.
void set_ostream_to_use(FILE *o)
Change the ostream that will be used to print messages. Setting a NULL ostream results in no printing...
~vrpn_TextPrinter()
Deletes any callbacks that are still registered.
void remove_object(vrpn_BaseClass *o)
Remove an object from the list of watched objects (multiple deletions of the object will not cause an...
vrpn_TextPrinter_Watch_Entry * d_first_watched_object
Head of list of objects being watched.
int add_object(vrpn_BaseClass *o)
Adds an object to the list of watched objects (multiple registration of the same object will result i...
void set_min_level_to_print(vrpn_TEXT_SEVERITY severity, vrpn_uint32 level=0)
Change the level of printing for the object (sets the minimum level to print). Default is Warnings an...
vrpn_uint32 d_level_to_print
Minimum level to print.
This structure is what is passed to a vrpn_Connection message callback.
const char * buffer
struct timeval msg_time
vrpn_TextPrinter & vrpn_System_TextPrinter
All types of client/server/peer objects in VRPN should be derived from the vrpn_BaseClass type descri...
vrpn_TEXT_SEVERITY
Since the sending of text messages has been pulled into the base class (so that every object can send...
@ vrpn_TEXT_NORMAL
@ vrpn_TEXT_WARNING
@ vrpn_TEXT_ERROR
const unsigned vrpn_MAX_TEXT_LEN
const int vrpn_MAX_BCADRS
Internal value for number of BaseClass addresses.
VRPN_API vrpn_TextPrinter & vrpn_System_TextPrinter
char * vrpn_copy_service_name(const char *fullname)
const char * vrpn_dropped_connection
vrpn_Connection * vrpn_get_connection_by_name(const char *cname, const char *local_in_logfile_name, const char *local_out_logfile_name, const char *remote_in_logfile_name, const char *remote_out_logfile_name, const char *NIC_IPaddress, bool force_connection)
Create a client connection of arbitrary type (VRPN UDP/TCP, TCP, File, Loopback, MPI).
const vrpn_uint32 vrpn_CONNECTION_RELIABLE
Classes of service for messages, specify multiple by ORing them together Priority of satisfying these...
int(VRPN_CALLBACK * vrpn_MESSAGEHANDLER)(void *userdata, vrpn_HANDLERPARAM p)
Type of a message handler for vrpn_Connection messages.
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.
VRPN_API int vrpn_buffer(char **insertPt, vrpn_int32 *buflen, const timeval t)
Utility routine for placing a timeval struct into a buffer that is to be sent as a message.
timeval vrpn_TimevalDiff(const timeval &tv1, const timeval &tv2)
timeval vrpn_TimevalNormalize(const timeval &in_tv)
Definition vrpn_Shared.C:43
#define vrpn_gettimeofday
Definition vrpn_Shared.h:99