vrpn  07.33
Virtual Reality Peripheral Network
vrpn_3DConnexion.C
Go to the documentation of this file.
1 // vrpn_3DConnexion.C: VRPN driver for 3DConnexion
2 // Space Navigator, Space Traveler, Space Explorer, Space Mouse, Spaceball 5000
3 #include <string.h> // for memset
4 
5 #include "vrpn_3DConnexion.h"
6 #include "vrpn_BaseClass.h" // for ::vrpn_TEXT_WARNING
7 
8 // There is a non-HID Linux-based driver for this device that has a capability
9 // not implemented in the HID interface. It uses the input.h interface.
10 #if defined(linux) && !defined(VRPN_USE_HID)
11 #define VRPN_USING_3DCONNEXION_EVENT_IFACE
12 #include <linux/input.h>
13 #include <stdlib.h> // for malloc, free, etc
14 #include <unistd.h> // for write, etc
15 #endif
16 
17 typedef struct input_devinfo {
18  vrpn_uint16 bustype;
19  vrpn_uint16 vendor;
20  vrpn_uint16 product;
21  vrpn_uint16 version;
22 } XXX_should_have_been_in_system_includes;
23 
24 // USB vendor and product IDs for the models we support
25 static const vrpn_uint16 vrpn_3DCONNEXION_VENDOR = 0x046d; //1133; // 3Dconnexion is made by Logitech
26 static const vrpn_uint16 vrpn_SPACEMOUSEWIRELESS_VENDOR = 9583; // Made by a different vendor...
27 static const vrpn_uint16 vrpn_3DCONNEXION_TRAVELER = 50723;
28 static const vrpn_uint16 vrpn_3DCONNEXION_NAVIGATOR = 50726;
29 static const vrpn_uint16 vrpn_3DCONNEXION_NAVIGATOR_FOR_NOTEBOOKS = 0xc628; // 50728;
30 static const vrpn_uint16 vrpn_3DCONNEXION_SPACEEXPLORER = 0xc627; // 50727
31 static const vrpn_uint16 vrpn_3DCONNEXION_SPACEMOUSE = 50691;
32 static const vrpn_uint16 vrpn_3DCONNEXION_SPACEMOUSEPRO = 50731;
33 static const vrpn_uint16 vrpn_3DCONNEXION_SPACEMOUSEWIRELESS = 50735;
34 static const vrpn_uint16 vrpn_3DCONNEXION_SPACEBALL5000 = 0xc621; // 50721;
35 static const vrpn_uint16 vrpn_3DCONNEXION_SPACEPILOT = 0xc625;
36 
38  const char *name, vrpn_Connection *c)
39  : vrpn_Button_Filter(name, c)
40  , vrpn_Analog(name, c)
41 #if defined(VRPN_USE_HID)
42  , vrpn_HidInterface(filter)
43 #endif
44  , _filter(filter)
45 {
48 
49  // Initialize the state of all the analogs and buttons
50  memset(buttons, 0, sizeof(buttons));
51  memset(lastbuttons, 0, sizeof(lastbuttons));
52  memset(channel, 0, sizeof(channel));
53  memset(last, 0, sizeof(last));
54 
55 // There is a non-HID Linux-based driver for this device that has a capability
56 // not implemented in the HID interface. It is implemented using the Event
57 // interface.
58 #if defined(VRPN_USING_3DCONNEXION_EVENT_IFACE)
59  // Use the Event interface to open devices looking for the one
60  // we want. Call the acceptor with all the devices we find
61  // until we get one that we want.
62  fd = -1;
63  FILE *f;
64  int i = 0;
65 
66  // try to autodetect the device
67  char *fname = (char *)malloc(1000*sizeof(char));
68  while(i < 256) {
69  sprintf(fname, "/dev/input/event%d", i++);
70  f = fopen(fname, "r+b");
71  if(f) {
72  // We got an active device. Fill in its values and see if it
73  // is acceptable to the filter.
74  struct input_devinfo ID;
75  ioctl(fileno(f), EVIOCGID, &ID);
76  vrpn_HIDDEVINFO info;
77  info.product = ID.product;
78  info.vendor = ID.vendor;
79  if (_filter->accept(info)) {
80  fd = fileno(f);
81  set_led(1);
82  break;
83  } else {
84  fclose(f);
85  f = NULL;
86  }
87  }
88  }
89 
90  if(!f) {
91  perror("Could not open the device");
92  exit(1);
93  }
94 
95  fclose(f);
96  free(fname);
97 
98  // turn the LED on
99  set_led(1);
100 #endif
101 }
102 
104 {
105 #if defined(VRPN_USING_3DCONNEXION_EVENT_IFACE)
106  set_led(0);
107 #endif
108  delete _filter;
109 }
110 
111 #if defined(VRPN_USE_HID)
112 void vrpn_3DConnexion::on_data_received(size_t bytes, vrpn_uint8 *buffer)
113 {
114  decodePacket(bytes, buffer);
115 }
116 #endif
117 
119 {
120 #if defined(VRPN_USE_HID)
121  // Full reports are 7 bytes long.
122  // XXX If we get a 2-byte report mixed in, then something is going to get
123  // truncated.
124  update();
125 #elif defined(VRPN_USING_3DCONNEXION_EVENT_IFACE)
126  struct timeval zerotime;
127  fd_set fdset;
128  struct input_event ev;
129  int i;
130 
131  zerotime.tv_sec = 0;
132  zerotime.tv_usec = 0;
133 
134  FD_ZERO(&fdset); /* clear fdset */
135  FD_SET(fd, &fdset); /* include fd in fdset */
136  int moreData = 0;
137  do {
138  vrpn_noint_select(fd + 1, &fdset, NULL, NULL, &zerotime);
139  moreData = 0;
140  if (FD_ISSET(fd, &fdset)) {
141  moreData = 1;
142  if (vrpn_noint_block_read(fd, reinterpret_cast<char*>(&ev), sizeof(struct input_event)) != sizeof(struct input_event)) {
143  send_text_message("Error reading from vrpn_3DConnexion", vrpn_Analog::timestamp, vrpn_TEXT_ERROR);
145  return;
146  }
147  switch (ev.type) {
148  case EV_KEY: // button movement
149  vrpn_gettimeofday((timeval *)&this->vrpn_Button::timestamp, NULL);
150  buttons[ev.code & 0x0ff] = ev.value;
151  break;
152 
153  case EV_REL: // axis movement
154  case EV_ABS: // new kernels send more logical _ABS instead of _REL
155  vrpn_gettimeofday((timeval *)&this->vrpn_Analog::timestamp, NULL);
156  // Convert from short to int to avoid a short/double conversion
157  // bug in GCC 3.2.
158  i = ev.value;
159  channel[ev.code] = static_cast<double>(i)/400.0;
160  break;
161 
162  default:
163  break;
164  }
165  }
166  report_changes();
167  } while (moreData == 1);
168 #endif
169 
170  server_mainloop();
172 }
173 
174 void vrpn_3DConnexion::report_changes(vrpn_uint32 class_of_service)
175 {
178 
179  vrpn_Analog::report_changes(class_of_service);
181 }
182 
183 void vrpn_3DConnexion::report(vrpn_uint32 class_of_service)
184 {
187 
188  vrpn_Analog::report(class_of_service);
190 }
191 
192 #if defined(linux) && !defined(VRPN_USE_HID)
193 int vrpn_3DConnexion::set_led(int led_state)
194 {
195  struct input_event event;
196  int ret;
197 
198  event.type = EV_LED;
199  event.code = LED_MISC;
200  event.value = led_state;
201 
202  ret = write(fd, &event, sizeof(struct input_event));
203  if (ret < 0) {
204  perror ("setting led state failed");
205  }
206  return ret < static_cast<int>(sizeof(struct input_event));
207 }
208 #endif
209 
210 #if defined(VRPN_USE_HID)
211 void vrpn_3DConnexion::decodePacket(size_t bytes, vrpn_uint8 *buffer)
212 {
213  // Force 'small' buffers (ie button under linux - 3 bytes - and apple - 2 bytes - into 7 bytes
214  // so we get through the report loop once. XXX Problem: this is skipping 7 bytes per report
215  // regardless of how many bytes were in the report. This is going to get us into trouble for
216  // multi-report packets. Instead, we should go until we've parsed all characters and add the
217  // number of characters parsed each time rather than a constant 7 reports.
218  if(bytes<7) bytes=7;
219  if (bytes > 7) {
220  fprintf(stderr, "vrpn_3DConnexion::decodePacket(): Long packet (%d bytes), may mis-parse\n",
221  static_cast<int>(bytes));
222  }
223  // Decode all full reports.
224  // Full reports for all of the pro devices are 7 bytes long (the first
225  // byte is the report type, because this device has multiple ones the
226  // HIDAPI library leaves it in the report).
227  for (size_t i = 0; i < bytes / 7; i++) {
228  vrpn_uint8 *report = buffer + (i * 7);
229 
230  // There are three types of reports. Parse whichever type
231  // this is.
232  char report_type = report[0];
233  vrpn_uint8 *bufptr = &report[1];
234  const float scale = 1.0f/400.0f;
235  switch (report_type) {
236  // Report types 1 and 2 come one after the other. Each seems
237  // to change when the puck is moved. It looks like each pair
238  // of values records a signed value for one channel; report
239  // type 1 is translation and report type 2 is rotation.
240  // The minimum and maximum values seem to vary somewhat.
241  // They all seem to be able to get over 400, so we scale
242  // by 400 and then clamp to (-1..1).
243  // The first byte is the low-order byte and the second is the
244  // high-order byte.
245  case 1:
246  channel[0] = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) * scale;
247  if (channel[0] < -1.0) { channel[0] = -1.0; }
248  if (channel[0] > 1.0) { channel[0] = 1.0; }
249  channel[1] = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) * scale;
250  if (channel[1] < -1.0) { channel[1] = -1.0; }
251  if (channel[1] > 1.0) { channel[1] = 1.0; }
252  channel[2] = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) * scale;
253  if (channel[2] < -1.0) { channel[2] = -1.0; }
254  if (channel[2] > 1.0) { channel[2] = 1.0; }
255  break;
256 
257  case 2:
258  channel[3] = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) * scale;
259  if (channel[3] < -1.0) { channel[3] = -1.0; }
260  if (channel[3] > 1.0) { channel[3] = 1.0; }
261  channel[4] = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) * scale;
262  if (channel[4] < -1.0) { channel[4] = -1.0; }
263  if (channel[4] > 1.0) { channel[4] = 1.0; }
264  channel[5] = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) * scale;
265  if (channel[5] < -1.0) { channel[5] = -1.0; }
266  if (channel[5] > 1.0) { channel[5] = 1.0; }
267  break;
268 
269  case 3: { // Button report
270  int btn;
271 
272  // Button reports are encoded as bits in the first 2 bytes
273  // after the type. There can be more than one byte if there
274  // are more than 8 buttons such as on SpaceExplorer or SpaceBall5000.
275  // If 8 or less, we don't look at 2nd byte.
276  // SpaceExplorer buttons are (for example):
277  // Name Number
278  // 1 0
279  // 2 1
280  // T 2
281  // L 3
282  // R 4
283  // F 5
284  // ESC 6
285  // ALT 7
286  // SHIFT 8
287  // CTRL 9
288  // FIT 10
289  // PANEL 11
290  // + 12
291  // - 13
292  // 2D 14
293 
294  for (btn = 0; btn < vrpn_Button::num_buttons; btn++) {
295  vrpn_uint8 *location, mask;
296  location = report + 1 + (btn / 8);
297  mask = 1 << (btn % 8);
298  buttons[btn] = ( (*location) & mask) != 0;
299  }
300  break;
301  }
302 
303  default:
305  send_text_message("Unknown report type", _timestamp, vrpn_TEXT_WARNING);
306  }
307  // Report this event before parsing the next.
308  report_changes();
309  }
310 }
311 #endif
312 
314  : vrpn_3DConnexion(_filter = new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_NAVIGATOR), 2, name, c)
315 {
316 }
317 
319  : vrpn_3DConnexion(_filter = new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_NAVIGATOR_FOR_NOTEBOOKS), 2, name, c)
320 {
321 }
322 
324  : vrpn_3DConnexion(_filter = new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_TRAVELER), 8, name, c)
325 {
326 }
327 
329  : vrpn_3DConnexion(_filter = new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEMOUSE), 11, name, c)
330 {
331 }
332 
334 : vrpn_3DConnexion(_filter = new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEMOUSEPRO), 27, name, c)
335 { // 15 physical buttons are numbered: 0-2, 4-5, 8, 12-15, 22-26
336 }
337 
339  : vrpn_3DConnexion(_filter = new vrpn_HidProductAcceptor(vrpn_SPACEMOUSEWIRELESS_VENDOR, vrpn_3DCONNEXION_SPACEMOUSEWIRELESS), 2, name, c)
340 {
341 }
342 
344  : vrpn_3DConnexion(_filter = new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEEXPLORER), 15, name, c)
345 {
346 }
347 
349  : vrpn_3DConnexion(_filter = new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEBALL5000), 12, name, c)
350 {
351 }
352 
354  : vrpn_3DConnexion(_filter = new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEPILOT), 21, name, c)
355 {
356 }
vrpn_3DConnexion_SpaceMouseWireless(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_Navigator(const char *name, vrpn_Connection *c=0)
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
vrpn_3DConnexion_Navigator_for_Notebooks(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_SpaceBall5000(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_SpaceMouse(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_Traveler(const char *name, vrpn_Connection *c=0)
vrpn_int32 num_buttons
Definition: vrpn_Button.h:47
#define EV_REL
struct timeval _timestamp
vrpn_float64 channel[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:38
virtual int send_pending_reports(void)=0
send pending report, clear the buffer. This function was protected, now is public, so we can use it to send out intermediate results without calling mainloop
virtual void decodePacket(size_t bytes, vrpn_uint8 *buffer)
Accepts any device with the given vendor and product IDs.
virtual void mainloop()
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
int vrpn_noint_select(int width, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
This routine will perform like a normal select() call, but it will restart if it quit because of an i...
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
All types of client/server/peer objects in VRPN should be derived from the vrpn_BaseClass type descri...
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
vrpn_Connection * d_connection
Connection that this object talks to.
void report(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY)
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.
virtual bool accept(const vrpn_HIDDEVINFO &device)=0
struct timeval timestamp
Definition: vrpn_Button.h:48
int vrpn_noint_block_read(int infile, char buffer[], size_t length)
This routine will read in a block from the file descriptor.
#define vrpn_gettimeofday
Definition: vrpn_Shared.h:89
virtual void update()
Polls the device buffers and causes on_data_received callbacks if appropriate You NEED to call this f...
All button servers should derive from this class, which provides the ability to turn any of the butto...
Definition: vrpn_Button.h:65
virtual ~vrpn_3DConnexion()
unsigned char lastbuttons[vrpn_BUTTON_MAX_BUTTONS]
Definition: vrpn_Button.h:45
vrpn_3DConnexion_SpacePilot(const char *name, vrpn_Connection *c=0)
void on_data_received(size_t bytes, vrpn_uint8 *buffer)
Derived class reimplements this callback.
vrpn_3DConnexion_SpaceMousePro(const char *name, vrpn_Connection *c=0)
unsigned char buttons[vrpn_BUTTON_MAX_BUTTONS]
Definition: vrpn_Button.h:44
vrpn_3DConnexion_SpaceExplorer(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion(vrpn_HidAcceptor *filter, unsigned num_buttons, const char *name, vrpn_Connection *c=0)
struct timeval timestamp
Definition: vrpn_Analog.h:41
#define EV_KEY
vrpn_float64 last[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:39
vrpn_HidAcceptor * _filter