vrpn  07.33
Virtual Reality Peripheral Network
vrpn_DirectXRumblePad.C
Go to the documentation of this file.
1 // vrpn_DirectXRumblePad.C: This is another driver for force-feedback
2 // joysticks under Windows such as the Logitech Cordless RumblePad.
3 //
4 // Note that if you ARE using a Logitech gamepad, it is almost a
5 // certainty that this code will not work without Logitech's drivers
6 // installed. Without them you will not be able to find any available
7 // force feedback effects, though polling joystick axes and the POV hat
8 // should work fine.
9 //
10 // Chris VanderKnyff, revised July 2007
11 
12 #include "vrpn_DirectXRumblePad.h"
13 
14 #if defined(_WIN32) && defined(VRPN_USE_DIRECTINPUT)
15 #include <stdio.h>
16 #include <time.h> // for time
17 #include <algorithm> // for min
18 using std::min;
19 
20 // Hacks to maintain VC++6 compatibility while preserving 64-bitness
21 // Also cleans up the annoying (and in this case meaningless) warnings under Win32
22 #ifdef _WIN64
23 # if !defined(GetWindowLongPtr) || !defined(SetWindowLongPtr)
24 # error 64-bit compilation requires an SDK that supports LONG_PTRs.
25 # endif
26 #else
27 # ifndef LONG_PTR
28 # define LONG_PTR LONG
29 # endif
30 # ifdef GetWindowLongPtr
31 # undef GetWindowLongPtr
32 # undef GWLP_USERDATA
33 # endif
34 # ifdef SetWindowLongPtr
35 # undef SetWindowLongPtr
36 # endif
37 # define GetWindowLongPtr GetWindowLong
38 # define SetWindowLongPtr SetWindowLong
39 # define GWLP_USERDATA GWL_USERDATA
40 #endif
41 #ifndef HWND_MESSAGE
42 # define HWND_MESSAGE ((HWND) -3)
43 #endif
44 
45 // __ImageBase allows us to get the HINSTANCE for the specific component
46 // we've been compiled into. This is actually the correct way for static
47 // libraries, because GetModuleHandle(NULL) returns the HINSTANCE for the
48 // hosting EXE. In the case where VRPN is a DLL itself or has been linked
49 // into a DLL, GetModuleHandle(NULL) is the wrong process's HINSTANCE.
50 //
51 // By using the pseudo-variable __ImageBase provided by the Visual C++
52 // compiler, we know exactly what our proper HINSTANCE is.
53 //
54 // More info: http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
55 EXTERN_C IMAGE_DOS_HEADER __ImageBase;
56 #define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
57 
58 namespace {
59  const char *CLASS_NAME = "vrpn_DirectXRumblePad"; // Win32 window class name
60  const int MSG_POLL = WM_APP + 101; // app-specific "polling" message
61  const int MSG_SETMAGNITUDE = WM_APP + 102; // app-specific "set magnitude" message
62  const int POLL_SUCCESS = 1701; // private value to indicate polling success
63 
64  // Quick-and-dirty GUID comparison function
65  // Arguments: Two Win32 GUID structures
66  // Returns: true iff the two GUIDs are identical
67  inline bool guid_equals(const GUID &a, const GUID &b) {
68  return !memcmp(&a, &b, sizeof(GUID));
69  }
70 }
71 
72 // Device constructor.
73 // Parameters:
74 // - name: VRPN name to assign to this server
75 // - c: VRPN connection this device should be attached to
76 // - device_guid: If provided, the specific DirectInput device GUID we want to use
77 // If not provided (or the null GUID), we'll bind to the first available joystick
78 vrpn_DirectXRumblePad::vrpn_DirectXRumblePad(const char *name, vrpn_Connection *c,
79  GUID device_guid)
80  : vrpn_Analog(name, c)
81  , vrpn_Button_Filter(name, c)
82  , vrpn_Analog_Output(name, c)
83  , _target_device(device_guid)
84  , _wnd(NULL)
85  , _thread(INVALID_HANDLE_VALUE)
86  , _directInput(NULL)
87  , _effect(NULL)
88  , _gamepad(NULL)
89 {
90  last_error = time(NULL);
91 
95 
96  // Register a handler for the request channel change message
97  if (register_autodeleted_handler(request_m_id,
98  handle_request_message, this, d_sender_id)) {
99  FAIL("vrpn_DirectXRumblePad: can't register change channel request handler");
100  return;
101  }
102 
103  // Register a handler for the request channels change message
104  if (register_autodeleted_handler(request_channels_m_id,
105  handle_request_channels_message, this, d_sender_id)) {
106  FAIL("vrpn_DirectXRumblePad: can't register change channels request handler");
107  return;
108  }
109 
110  // Register a handler for the no-one's-connected-now message
113  handle_last_connection_dropped, this)) {
114  FAIL("Can't register self-destruct handler");
115  return;
116  }
117 
118  _thread = CreateThread(NULL, 0, thread_proc, this, 0, NULL);
119  if (_thread == INVALID_HANDLE_VALUE) {
120  FAIL("Unable to create thread.");
121  return;
122  }
123 }
124 
125 // Device destructor
126 vrpn_DirectXRumblePad::~vrpn_DirectXRumblePad() {
127  // Tell the thread window to close itself
128  if (_wnd) {
129  SendMessage(_wnd, WM_CLOSE, 0, 0);
130  }
131 
132  // Wait for the worker thread to terminate
133  // Spinlocks are suboptimal, but I know no easy way to "join" to a thread's termination.
134  if (_thread != INVALID_HANDLE_VALUE) {
135  DWORD code;
136  do {
137  GetExitCodeThread(_thread, &code);
138  } while (code == STILL_ACTIVE);
139  }
140 }
141 
142 // joystick_enum_cb: Callback function for joystick enumeration
143 // Tries to find the desired device GUID if provided.
144 // Otherwise it will match on the first joystick enumerated.
145 BOOL CALLBACK vrpn_DirectXRumblePad::joystick_enum_cb(LPCDIDEVICEINSTANCE lpddi, LPVOID ref) {
146  vrpn_DirectXRumblePad *me = reinterpret_cast<vrpn_DirectXRumblePad *>(ref);
147 
148  if (guid_equals(me->_target_device, GUID_NULL)
149  || guid_equals(me->_target_device, lpddi->guidInstance)) {
150  if (SUCCEEDED(me->_directInput->CreateDevice(lpddi->guidInstance, &me->_gamepad, 0))) {
151  return DIENUM_STOP;
152  }
153  else {
154  me->FAIL("Unable to create device instance! Attempting to find another one.");
155  return DIENUM_CONTINUE;
156  }
157  }
158  return DIENUM_CONTINUE;
159 }
160 
161 // Main thread procedure. Registers a window class, creates a worker window
162 // (so we have an HWND for DirectInput), and runs a message pump.
163 // We move all this onto another thread to prevent the host application
164 // from accidentally being flagged as a GUI thread. Enough DirectX things
165 // have thread affinity that it's safer to isolate things this way.
166 // All userland APIs are just wrappers around SendMessage to make sure all
167 // DirectX calls are executed in the context of the proper thread.
168 //
169 // LPVOID parameter is the generic "user data" passed to CreateThread API.
170 // In this case, it's our C++ this pointer--everything is static in the other thread.
171 // Multiple vrpn_DirectXRumblePads simply create multiple threads with their own local storage.
172 DWORD CALLBACK vrpn_DirectXRumblePad::thread_proc(LPVOID ref) {
173  // Retrieve C++ this pointer
174  vrpn_DirectXRumblePad *me = reinterpret_cast<vrpn_DirectXRumblePad *>(ref);
175 
176  // Register a simple window class
177  WNDCLASS wc = {0};
178  wc.lpfnWndProc = window_proc;
179  wc.cbWndExtra = sizeof(vrpn_DirectXRumblePad *);
180  wc.hInstance = HINST_THISCOMPONENT;
181  wc.lpszClassName = CLASS_NAME;
182 
183  if (!RegisterClass(&wc)) {
184  me->FAIL("Unable to register class.");
185  return 1;
186  }
187 
188  // Create our window
189  me->_wnd = CreateWindow(CLASS_NAME, "VRPN Worker Thread", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, ref);
190 
191  if (!me->_wnd) {
192  me->FAIL("Unable to create window.");
193  return 1;
194  }
195 
196  // Fire up DirectInput
197  if (FAILED(DirectInput8Create(HINST_THISCOMPONENT, DIRECTINPUT_VERSION,
198  IID_IDirectInput8, (void**)&me->_directInput, NULL))) {
199  me->FAIL("Unable to connect DirectInput.");
200  return 1;
201  }
202 
203  // Enumerate force-feedback joysticks
204  if (FAILED(me->_directInput->EnumDevices(DI8DEVCLASS_GAMECTRL,
205  joystick_enum_cb, me, DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK))) {
206  me->FAIL("Unable to enumerate joysticks.");
207  return 1;
208  }
209 
210  // Make sure we found something
211  if (!me->_gamepad) {
212  me->FAIL("No compatible joystick found!");
213  return 1;
214  }
215 
216  // Set data format for retrieving axis values
217  if (FAILED(me->_gamepad->SetDataFormat(&c_dfDIJoystick2))) {
218  me->FAIL("Unable to set data format.");
219  return 1;
220  }
221 
222  // Grab exclusive access to the joystick
223  if (FAILED(me->_gamepad->SetCooperativeLevel(me->_wnd, DISCL_EXCLUSIVE | DISCL_BACKGROUND))) {
224  me->FAIL("Unable to set cooperative level.");
225  return 1;
226  }
227 
228  // Find all the axes and set their ranges to [-1000, 1000]
229  if (FAILED(me->_gamepad->EnumObjects(axis_enum_cb, me->_gamepad, DIDFT_AXIS))) {
230  me->FAIL("Unable to enumerate axes.");
231  return 1;
232  }
233 
234  // Load the force effect onto the joystick
235  if (FAILED(me->init_force())) {
236  me->FAIL("Unable to initialize forces.");
237  return 1;
238  }
239 
240  // Acquire the joystick for polling
241  if (FAILED(me->_gamepad->Acquire())) {
242  me->FAIL("Unable to acquire joystick.");
243  return 1;
244  }
245 
246  // Start the main message loop
247  BOOL ret;
248  MSG msg;
249  while ((ret = GetMessage(&msg, me->_wnd, 0, 0)) != 0) {
250  if (ret == -1) {
251  me->FAIL("GetMessage() threw an error.");
252  return 1;
253  }
254  else {
255  TranslateMessage(&msg);
256  DispatchMessage(&msg);
257  }
258  }
259 
260  // Clean up after ourselves
261  if (me->_effect)
262  me->_effect->Release();
263  if (me->_gamepad)
264  me->_gamepad->Release();
265  if (me->_directInput)
266  me->_directInput->Release();
267 
268  return 0;
269 }
270 
271 // Window procedure for the worker thread's helper window
272 // Standard WindowProc semantics for a message-only window
273 LRESULT CALLBACK vrpn_DirectXRumblePad::window_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
274  // Retrieve "this" pointer
275  // Guaranteed to be garbage until WM_CREATE finishes, but
276  // we don't actually use this value until WM_CREATE writes a valid one
277  vrpn_DirectXRumblePad *me = reinterpret_cast<vrpn_DirectXRumblePad *>
278  (GetWindowLongPtr(hwnd, GWLP_USERDATA));
279 
280  switch (msg) {
281  // Window is being created; store "this" pointer for future retrieval
282  case WM_CREATE: {
283  CREATESTRUCT *s = reinterpret_cast<CREATESTRUCT *>(lp);
284  me = reinterpret_cast<vrpn_DirectXRumblePad *>(s->lpCreateParams);
285  SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(me));
286  return 0;
287  }
288 
289  // Something (most likely ~vrpn_DirectXRumblePad) wants to close the window
290  // Go ahead and signal shutdown
291  case WM_CLOSE:
292  DestroyWindow(hwnd);
293  PostQuitMessage(0);
294  break;
295 
296  // Main thread wants to poll the joystick. Do so.
297  case MSG_POLL:
298  if (FAILED(me->_gamepad->Poll())) {
299  // Keep trying to acquire the joystick if necessary
300  do {
301  me->_gamepad->Acquire();
302  } while (me->_gamepad->Poll() == DIERR_INPUTLOST);
303  }
304  // Read data from the joystick
305  if (SUCCEEDED(me->_gamepad->GetDeviceState(sizeof(DIJOYSTATE2), reinterpret_cast<DIJOYSTATE2 *>(lp)))) {
306  return POLL_SUCCESS; // Make sure main thread knows we're running here
307  }
308  else {
309  me->FAIL("GetDeviceState() returned error result.");
310  break;
311  }
312 
313  // Main thread wants to change rumble magnitude. Do so.
314  case MSG_SETMAGNITUDE: {
315  float mag = *reinterpret_cast<float*>(&wp);
316 
317  if (me->_effect)
318  me->_effect->Stop();
319 
320  if (mag > 0) {
321  HRESULT hr;
322 
323  me->_diPeriodic.dwMagnitude = (DWORD) (DI_FFNOMINALMAX * mag);
324  hr = me->_effect->SetParameters(&me->_diEffect, DIEP_TYPESPECIFICPARAMS);
325  hr = me->_effect->Download();
326  hr = me->_effect->Start(1, 0);
327  hr = hr;
328  }
329  break;
330  }
331 
332  // Everything not explicitly handled goes to DefWindowProc as per usual
333  default:
334  return DefWindowProc(hwnd, msg, wp, lp);
335  }
336 
337  return 0;
338 }
339 
340 // Axis enumeration callback
341 // For each joystick axis we can find, tell it to report values in [-1000, 1000]
342 BOOL CALLBACK vrpn_DirectXRumblePad::axis_enum_cb(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID ref) {
343  LPDIRECTINPUTDEVICE8 gamepad = reinterpret_cast<LPDIRECTINPUTDEVICE8>(ref);
344 
345  DIPROPRANGE prop;
346  prop.diph.dwSize = sizeof(DIPROPRANGE);
347  prop.diph.dwHeaderSize = sizeof(DIPROPHEADER);
348  prop.diph.dwHow = DIPH_BYID;
349  prop.diph.dwObj = lpddoi->dwType;
350  prop.lMin = -1000;
351  prop.lMax = 1000;
352 
353  if (FAILED(gamepad->SetProperty(DIPROP_RANGE, &prop.diph))) {
354  return DIENUM_STOP;
355  }
356 
357  return DIENUM_CONTINUE;
358 }
359 
360 // VRPN main loop
361 // Poll the joystick, update the axes, and let the VRPN change notifications fire
362 void vrpn_DirectXRumblePad::mainloop() {
363  DIJOYSTATE2 js;
364 
365  server_mainloop();
366 
367  if (SendMessage(_wnd, MSG_POLL, 0, reinterpret_cast<LPARAM>(&js)) != POLL_SUCCESS) {
368  if ((time(NULL) - last_error) > 2) {
369  struct timeval now;
370 
371  time(&last_error);
372  vrpn_gettimeofday(&now, NULL);
373  send_text_message("Cannot talk to joystick", now, vrpn_TEXT_ERROR);
374  }
375  return;
376  }
377 
378  channel[0] = js.lX / 1000.0;
379  channel[1] = js.lY / 1000.0;
380  channel[2] = js.lZ / 1000.0;
381 
382  channel[3] = js.lRx / 1000.0;
383  channel[4] = js.lRy / 1000.0;
384  channel[5] = js.lRz / 1000.0;
385 
386  channel[6] = js.rglSlider[0] / 1000.0;
387  channel[7] = js.rglSlider[1] / 1000.0;
388 
389  int i;
390  for (i = 0; i < 4; i++) {
391  long v = (long) js.rgdwPOV[i];
392  channel[8+i] = (v == -1) ? -1 : (v / 100.0);
393  }
394 
395  for (i = 0; i < vrpn_Analog::num_channel/*min(128,vrpn_BUTTON_MAX_BUTTONS)*/; i++) {
396  buttons[i] = ( (js.rgbButtons[i] & 0x80) != 0);
397  }
398 
399  // Send any changes out over the connection.
400  vrpn_gettimeofday(&_timestamp, NULL);
401  report_changes();
402 }
403 
404 void vrpn_DirectXRumblePad::report(vrpn_uint32 class_of_service) {
405  vrpn_Analog::timestamp = _timestamp;
406  vrpn_Button::timestamp = _timestamp;
407 
408  vrpn_Analog::report(class_of_service);
410 }
411 
412 void vrpn_DirectXRumblePad::report_changes(vrpn_uint32 class_of_service) {
413  vrpn_Analog::timestamp = _timestamp;
414  vrpn_Button::timestamp = _timestamp;
415 
416  vrpn_Analog::report_changes(class_of_service);
418 }
419 
420 /* static */
421 int vrpn_DirectXRumblePad::handle_request_message(void *userdata,
423 {
424  const char* bufptr = p.buffer;
425  vrpn_int32 chan_num;
426  vrpn_int32 pad;
427  vrpn_float64 value;
428  vrpn_DirectXRumblePad* me = (vrpn_DirectXRumblePad*)userdata;
429 
430  // Read the parameters from the buffer
431  vrpn_unbuffer(&bufptr, &chan_num);
432  vrpn_unbuffer(&bufptr, &pad);
433  vrpn_unbuffer(&bufptr, &value);
434 
435  // Set the appropriate value, if the channel number is in the
436  // range of the ones we have.
437  if ( (chan_num < 0) || (chan_num >= me->o_num_channel) ) {
438  fprintf(stderr,"vrpn_Analog_Output_Server::handle_request_message(): Index out of bounds\n");
439  char msg[1024];
440  sprintf( msg, "Error: (handle_request_message): channel %d is not active. Squelching.", chan_num );
441  me->send_text_message( msg, p.msg_time, vrpn_TEXT_ERROR );
442  return 0;
443  }
444  me->o_channel[chan_num] = value;
445 
446  float mag = static_cast<float>(value);
447  mag = (mag < 0) ? 0 : (mag > 1) ? 1 : mag;
448  SendMessage(me->_wnd, MSG_SETMAGNITUDE, *reinterpret_cast<WPARAM*>(&mag), 0);
449 
450  return 0;
451 }
452 
453 /* static */
454 int vrpn_DirectXRumblePad::handle_request_channels_message(void* userdata,
456 {
457  const char* bufptr = p.buffer;
458  vrpn_int32 num;
459  vrpn_int32 pad;
460  vrpn_DirectXRumblePad* me = (vrpn_DirectXRumblePad*)userdata;
461 
462  // Read the values from the buffer
463  vrpn_unbuffer(&bufptr, &num);
464  vrpn_unbuffer(&bufptr, &pad);
465  if (num > me->o_num_channel)
466  {
467  char msg[1024];
468  sprintf( msg, "Error: (handle_request_channels_message): channels above %d not active; "
469  "bad request up to channel %d. Squelching.", me->o_num_channel, num );
470  me->send_text_message( msg, p.msg_time, vrpn_TEXT_ERROR );
471  num = me->o_num_channel;
472  }
473  if (num < 0)
474  {
475  char msg[1024];
476  sprintf( msg, "Error: (handle_request_channels_message): invalid channel %d. Squelching.", num );
477  me->send_text_message( msg, p.msg_time, vrpn_TEXT_ERROR );
478  return 0;
479  }
480 
481  // Pull only channel 0 from the buffer, no matter how many values we received.
482  vrpn_float64 value;
483  vrpn_unbuffer(&bufptr, &value);
484  float mag = static_cast<float>(value);
485  mag = (mag < 0) ? 0 : (mag > 1) ? 1 : mag;
486  SendMessage(me->_wnd, MSG_SETMAGNITUDE, *reinterpret_cast<WPARAM*>(&mag), 0);
487 
488  return 0;
489 }
490 
491 int VRPN_CALLBACK vrpn_DirectXRumblePad::handle_last_connection_dropped(void *selfPtr, vrpn_HANDLERPARAM data) {
492  // Kill force feedback if no one is connected
493  SendMessage(((vrpn_DirectXRumblePad *)selfPtr)->_wnd, MSG_SETMAGNITUDE, 0, 0);
494  return 0;
495 }
496 
497 // Initializes the joystick with a force feedback effect
498 HRESULT vrpn_DirectXRumblePad::init_force() {
499  HRESULT hr;
500 
501  // Turn off autocentering
502  DIPROPDWORD prop;
503  prop.diph.dwSize = sizeof(prop);
504  prop.diph.dwHeaderSize = sizeof(DIPROPHEADER);
505  prop.diph.dwObj = 0;
506  prop.diph.dwHow = DIPH_DEVICE;
507  prop.dwData = DIPROPAUTOCENTER_OFF;
508 
509  hr = _gamepad->SetProperty(DIPROP_AUTOCENTER, &prop.diph);
510  if (FAILED(hr))
511  return hr;
512 
513  // Create the actual effect
514  DWORD dwAxes[2] = {DIJOFS_X, DIJOFS_Y};
515  LONG lDirection[2] = {0, 0};
516 
517  _diPeriodic.dwMagnitude = DI_FFNOMINALMAX;
518  _diPeriodic.lOffset = 0;
519  _diPeriodic.dwPhase = 0;
520  _diPeriodic.dwPeriod = static_cast<DWORD>(0.05 * DI_SECONDS);
521 
522  _diEffect.dwSize = sizeof(DIEFFECT);
523  _diEffect.dwFlags = DIEFF_POLAR | DIEFF_OBJECTOFFSETS;
524  _diEffect.dwDuration = INFINITE;
525  _diEffect.dwSamplePeriod = 0;
526  _diEffect.dwGain = DI_FFNOMINALMAX;
527  _diEffect.dwTriggerButton = DIEB_NOTRIGGER;
528  _diEffect.dwTriggerRepeatInterval = 0;
529  _diEffect.cAxes = 2;
530  _diEffect.rgdwAxes = dwAxes;
531  _diEffect.rglDirection = &lDirection[0];
532  _diEffect.lpEnvelope = NULL;
533  _diEffect.cbTypeSpecificParams = sizeof(_diPeriodic);
534  _diEffect.lpvTypeSpecificParams = &_diPeriodic;
535  _diEffect.dwStartDelay = 0;
536 
537  hr = _gamepad->CreateEffect(GUID_Square, &_diEffect, &_effect, NULL);
538 
539  return hr;
540 }
541 
542 
543 #endif // _WIN32 and VRPN_USE_DIRECTINPUT
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
min
#define min(x, y)
Definition: vrpn_WiiMote.C:47
vrpn_BUTTON_MAX_BUTTONS
const int vrpn_BUTTON_MAX_BUTTONS
Definition: vrpn_Button.h:12
DIRECTINPUT_VERSION
#define DIRECTINPUT_VERSION
Definition: vrpn_Configure.h:501
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_Output::o_num_channel
vrpn_int32 o_num_channel
Definition: vrpn_Analog_Output.h:37
vrpn_Analog::timestamp
struct timeval timestamp
Definition: vrpn_Analog.h:41
vrpn_Analog_Output
Definition: vrpn_Analog_Output.h:26
vrpn_Button::num_buttons
vrpn_int32 num_buttons
Definition: vrpn_Button.h:47
vrpn_DirectXRumblePad.h
vrpn_unbuffer
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:312
vrpn_BaseClassUnique::d_connection
vrpn_Connection * d_connection
Connection that this object talks to.
Definition: vrpn_BaseClass.h:224
vrpn_HANDLERPARAM::buffer
const char * buffer
Definition: vrpn_Connection.h:49
vrpn_Connection::register_message_type
virtual vrpn_int32 register_message_type(const char *name)
Definition: vrpn_Connection.C:5074
vrpn_TEXT_ERROR
Definition: vrpn_BaseClass.h:103
vrpn_HANDLERPARAM
This structure is what is passed to a vrpn_Connection message callback.
Definition: vrpn_Connection.h:44
vrpn_BaseClassUnique::d_sender_id
vrpn_int32 d_sender_id
Sender ID registered with the connection.
Definition: vrpn_BaseClass.h:228
vrpn_Button::timestamp
struct timeval timestamp
Definition: vrpn_Button.h:48
vrpn_HANDLERPARAM::msg_time
struct timeval msg_time
Definition: vrpn_Connection.h:47
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_CALLBACK
#define VRPN_CALLBACK
Definition: vrpn_Configure.h:647
vrpn_Analog::report_changes
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
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