vrpn 07.36
Virtual Reality Peripheral Network
Loading...
Searching...
No Matches
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
17typedef 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
25static const vrpn_uint16 vrpn_3DCONNEXION_VENDOR = 0x046d; //1133; // 3Dconnexion is made by Logitech
26static const vrpn_uint16 vrpn_SPACEMOUSEWIRELESS_VENDOR = 9583; // Made by a different vendor...
27static const vrpn_uint16 vrpn_3DCONNEXION_TRAVELER = 50723;
28static const vrpn_uint16 vrpn_3DCONNEXION_NAVIGATOR = 50726;
29static const vrpn_uint16 vrpn_3DCONNEXION_NAVIGATOR_FOR_NOTEBOOKS = 0xc628; // 50728;
30static const vrpn_uint16 vrpn_3DCONNEXION_SPACEEXPLORER = 0xc627; // 50727
31static const vrpn_uint16 vrpn_3DCONNEXION_SPACEMOUSE = 50691;
32static const vrpn_uint16 vrpn_3DCONNEXION_SPACEMOUSEPRO = 50731;
33static const vrpn_uint16 vrpn_3DCONNEXION_SPACEMOUSECOMPACT = 50741;
34static const vrpn_uint16 vrpn_3DCONNEXION_SPACEMOUSEWIRELESS = 50735;
35static const vrpn_uint16 vrpn_3DCONNEXION_SPACEMOUSEWIRELESS2 = 50734;
36static const vrpn_uint16 vrpn_3DCONNEXION_SPACEMOUSEPROWIRELESS = 0xC631;
37static const vrpn_uint16 vrpn_3DCONNEXION_SPACEBALL5000 = 0xc621; // 50721;
38static const vrpn_uint16 vrpn_3DCONNEXION_SPACEPILOT = 0xc625;
39static const vrpn_uint16 vrpn_3DCONNEXION_SPACEPILOTPRO = 0xc629;
40
42 const char *name, vrpn_Connection *c,
43 vrpn_uint16 vendor, vrpn_uint16 product)
44 : vrpn_Button_Filter(name, c)
45 , vrpn_Analog(name, c)
46#if defined(VRPN_USE_HID)
48#endif
49 , _filter(filter)
50{
53
54 // Initialize the state of all the analogs and buttons
55 memset(buttons, 0, sizeof(buttons));
56 memset(lastbuttons, 0, sizeof(lastbuttons));
57 memset(channel, 0, sizeof(channel));
58 memset(last, 0, sizeof(last));
59
60 // Initialize the timestamp.
62
63// There is a non-HID Linux-based driver for this device that has a capability
64// not implemented in the HID interface. It is implemented using the Event
65// interface.
66#if defined(VRPN_USING_3DCONNEXION_EVENT_IFACE)
67 // Use the Event interface to open devices looking for the one
68 // we want. Call the acceptor with all the devices we find
69 // until we get one that we want.
70 fd = -1;
71 FILE *f;
72 int i = 0;
73
74 // try to autodetect the device
75 char *fname = (char *)malloc(1000*sizeof(char));
76 while(i < 256) {
77 snprintf(fname, 1000, "/dev/input/event%d", i++);
78 f = fopen(fname, "r+b");
79 if(f) {
80 // We got an active device. Fill in its values and see if it
81 // is acceptable to the filter.
82 struct input_devinfo ID;
83 ioctl(fileno(f), EVIOCGID, &ID);
84 vrpn_HIDDEVINFO info;
85 info.product = ID.product;
86 info.vendor = ID.vendor;
87 if (_filter->accept(info)) {
88 fd = fileno(f);
89 set_led(1);
90 break;
91 } else {
92 fclose(f);
93 f = NULL;
94 }
95 }
96 }
97
98 if(!f) {
99 perror("Could not open the device");
100 exit(1);
101 }
102
103 fclose(f);
104 free(fname);
105
106 // turn the LED on
107 set_led(1);
108#else
109#ifndef VRPN_USE_HID
110 fprintf(stderr,"vrpn_3DConnexion::vrpn_3DConnexion(): No implementation compiled in "
111 "to open this device. Please recompile.\n");
112#endif
113#endif
114}
115
117{
118#if defined(VRPN_USING_3DCONNEXION_EVENT_IFACE)
119 set_led(0);
120#endif
121 try {
122 delete _filter;
123 } catch (...) {
124 fprintf(stderr, "vrpn_3DConnexion::~vrpn_3DConnexion(): delete failed\n");
125 return;
126 }
127}
128
129#if defined(VRPN_USE_HID)
130void vrpn_3DConnexion::on_data_received(size_t bytes, vrpn_uint8 *buffer)
131{
132 decodePacket(bytes, buffer);
133}
134#endif
135
137{
138#if defined(VRPN_USE_HID)
139 // Full reports are 7 bytes long.
140 // XXX If we get a 2-byte report mixed in, then something is going to get
141 // truncated.
142 update();
143#elif defined(VRPN_USING_3DCONNEXION_EVENT_IFACE)
144 struct timeval zerotime;
145 fd_set fdset;
146 struct input_event ev;
147 int i;
148
149 zerotime.tv_sec = 0;
150 zerotime.tv_usec = 0;
151
152 FD_ZERO(&fdset); /* clear fdset */
153 FD_SET(fd, &fdset); /* include fd in fdset */
154 int moreData = 0;
155 do {
156 vrpn_noint_select(fd + 1, &fdset, NULL, NULL, &zerotime);
157 moreData = 0;
158 if (FD_ISSET(fd, &fdset)) {
159 moreData = 1;
160 if (vrpn_noint_block_read(fd, reinterpret_cast<char*>(&ev), sizeof(struct input_event)) != sizeof(struct input_event)) {
161 send_text_message("Error reading from vrpn_3DConnexion", vrpn_Analog::timestamp, vrpn_TEXT_ERROR);
162 if (d_connection) { d_connection->send_pending_reports(); }
163 return;
164 }
165 switch (ev.type) {
166 case EV_KEY: // button movement
167 vrpn_gettimeofday((timeval *)&this->vrpn_Button::timestamp, NULL);
168 buttons[ev.code & 0x0ff] = ev.value;
169 break;
170
171 case EV_REL: // axis movement
172 case EV_ABS: // new kernels send more logical _ABS instead of _REL
173 vrpn_gettimeofday((timeval *)&this->vrpn_Analog::timestamp, NULL);
174 // Convert from short to int to avoid a short/double conversion
175 // bug in GCC 3.2.
176 i = ev.value;
177 channel[ev.code] = static_cast<double>(i)/400.0;
178 break;
179
180 default:
181 break;
182 }
183 }
185 } while (moreData == 1);
186#endif
187
190}
191
200
201void vrpn_3DConnexion::report(vrpn_uint32 class_of_service)
202{
205
206 vrpn_Analog::report(class_of_service);
208}
209
210#if defined(linux) && !defined(VRPN_USE_HID)
211int vrpn_3DConnexion::set_led(int led_state)
212{
213 struct input_event event;
214 int ret;
215
216 event.type = EV_LED;
217 event.code = LED_MISC;
218 event.value = led_state;
219
220 ret = write(fd, &event, sizeof(struct input_event));
221 if (ret < 0) {
222 perror ("setting led state failed");
223 }
224 return ret < static_cast<int>(sizeof(struct input_event));
225}
226#endif
227
228#if defined(VRPN_USE_HID)
229void vrpn_3DConnexion::decodePacket(size_t bytes, vrpn_uint8 *buffer)
230{
231 // Force 'small' buffers (ie button under linux - 3 bytes - and apple - 2 bytes - into 7 bytes
232 // so we get through the report loop once. XXX Problem: this is skipping 7 bytes per report
233 // regardless of how many bytes were in the report. This is going to get us into trouble for
234 // multi-report packets. Instead, we should go until we've parsed all characters and add the
235 // number of characters parsed each time rather than a constant 7 reports.
236 if(bytes<7) bytes=7;
237 if (bytes > 7) {
238 fprintf(stderr, "vrpn_3DConnexion::decodePacket(): Long packet (%d bytes), may mis-parse\n",
239 static_cast<int>(bytes));
240 }
241 // Decode all full reports.
242 // Full reports for all of the pro devices are 7 bytes long (the first
243 // byte is the report type, because this device has multiple ones the
244 // HIDAPI library leaves it in the report).
245 for (size_t i = 0; i < bytes / 7; i++) {
246 vrpn_uint8 *report = buffer + (i * 7);
247
248 // There are three types of reports. Parse whichever type
249 // this is.
250 char report_type = report[0];
251 vrpn_uint8 *bufptr = &report[1];
252 const float scale = 1.0f/400.0f;
253 switch (report_type) {
254 // Report types 1 and 2 come one after the other. Each seems
255 // to change when the puck is moved. It looks like each pair
256 // of values records a signed value for one channel; report
257 // type 1 is translation and report type 2 is rotation.
258 // The minimum and maximum values seem to vary somewhat.
259 // They all seem to be able to get over 400, so we scale
260 // by 400 and then clamp to (-1..1).
261 // The first byte is the low-order byte and the second is the
262 // high-order byte.
263 case 1:
264 channel[0] = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) * scale;
265 if (channel[0] < -1.0) { channel[0] = -1.0; }
266 if (channel[0] > 1.0) { channel[0] = 1.0; }
267 channel[1] = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) * scale;
268 if (channel[1] < -1.0) { channel[1] = -1.0; }
269 if (channel[1] > 1.0) { channel[1] = 1.0; }
270 channel[2] = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) * scale;
271 if (channel[2] < -1.0) { channel[2] = -1.0; }
272 if (channel[2] > 1.0) { channel[2] = 1.0; }
273 break;
274
275 case 2:
276 channel[3] = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) * scale;
277 if (channel[3] < -1.0) { channel[3] = -1.0; }
278 if (channel[3] > 1.0) { channel[3] = 1.0; }
279 channel[4] = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) * scale;
280 if (channel[4] < -1.0) { channel[4] = -1.0; }
281 if (channel[4] > 1.0) { channel[4] = 1.0; }
282 channel[5] = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) * scale;
283 if (channel[5] < -1.0) { channel[5] = -1.0; }
284 if (channel[5] > 1.0) { channel[5] = 1.0; }
285 break;
286
287 case 3: { // Button report
288 int btn;
289
290 // Button reports are encoded as bits in the first 2 bytes
291 // after the type. There can be more than one byte if there
292 // are more than 8 buttons such as on SpaceExplorer or SpaceBall5000.
293 // If 8 or less, we don't look at 2nd byte.
294 // SpaceExplorer buttons are (for example):
295 // Name Number
296 // 1 0
297 // 2 1
298 // T 2
299 // L 3
300 // R 4
301 // F 5
302 // ESC 6
303 // ALT 7
304 // SHIFT 8
305 // CTRL 9
306 // FIT 10
307 // PANEL 11
308 // + 12
309 // - 13
310 // 2D 14
311
312 for (btn = 0; btn < vrpn_Button::num_buttons; btn++) {
313 vrpn_uint8 *location, mask;
314 location = report + 1 + (btn / 8);
315 mask = 1 << (btn % 8);
316 buttons[btn] = ( (*location) & mask) != 0;
317 }
318 break;
319 }
320
321 default:
323 send_text_message("Unknown report type", _timestamp, vrpn_TEXT_WARNING);
324 }
325 // Report this event before parsing the next.
327 }
328}
329#endif
330
332 : vrpn_3DConnexion(new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_NAVIGATOR), 2, name, c, vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_NAVIGATOR)
333{
334}
335
337 : vrpn_3DConnexion(new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_NAVIGATOR_FOR_NOTEBOOKS), 2, name, c, vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_NAVIGATOR_FOR_NOTEBOOKS)
338{
339}
340
342 : vrpn_3DConnexion(new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_TRAVELER), 8, name, c, vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_TRAVELER)
343{
344}
345
347 : vrpn_3DConnexion(new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEMOUSE), 11, name, c, vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEMOUSE)
348{
349}
350
352 : vrpn_3DConnexion(new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEMOUSEPRO), 27, name, c, vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEMOUSEPRO)
353{ // 15 physical buttons are numbered: 0-2, 4-5, 8, 12-15, 22-26
354}
355
357 : vrpn_3DConnexion(new vrpn_HidProductAcceptor(vrpn_SPACEMOUSEWIRELESS_VENDOR, vrpn_3DCONNEXION_SPACEMOUSECOMPACT), 2, name, c, vrpn_SPACEMOUSEWIRELESS_VENDOR, vrpn_3DCONNEXION_SPACEMOUSECOMPACT)
358{
359}
360
362 : vrpn_3DConnexion(new vrpn_HidProductAcceptor(vrpn_SPACEMOUSEWIRELESS_VENDOR, vrpn_3DCONNEXION_SPACEMOUSEWIRELESS), 2, name, c, vrpn_SPACEMOUSEWIRELESS_VENDOR, vrpn_3DCONNEXION_SPACEMOUSEWIRELESS)
363{
364}
365
367 : vrpn_3DConnexion(new vrpn_HidProductAcceptor(vrpn_SPACEMOUSEWIRELESS_VENDOR, vrpn_3DCONNEXION_SPACEMOUSEWIRELESS2), 2, name, c, vrpn_SPACEMOUSEWIRELESS_VENDOR, vrpn_3DCONNEXION_SPACEMOUSEWIRELESS2)
368{
369}
370
372 : vrpn_3DConnexion(new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEEXPLORER), 15, name, c, vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEEXPLORER)
373{
374}
375
377 : vrpn_3DConnexion(new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEBALL5000), 12, name, c, vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEBALL5000)
378{
379}
380
382 : vrpn_3DConnexion(new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEPILOT), 21, name, c, vrpn_3DCONNEXION_VENDOR, vrpn_3DCONNEXION_SPACEPILOT)
383{
384}
385
388 : vrpn_3DConnexion(new vrpn_HidProductAcceptor(vrpn_3DCONNEXION_VENDOR,
389 vrpn_3DCONNEXION_SPACEPILOTPRO),
390 31, name, c, vrpn_3DCONNEXION_VENDOR,
391 vrpn_3DCONNEXION_SPACEPILOTPRO)
392{
393}
394
396 : vrpn_3DConnexion(new vrpn_HidProductAcceptor(vrpn_SPACEMOUSEWIRELESS_VENDOR, vrpn_3DCONNEXION_SPACEMOUSEPROWIRELESS), 32, name, c, vrpn_SPACEMOUSEWIRELESS_VENDOR, vrpn_3DCONNEXION_SPACEMOUSEPROWIRELESS)
397{
398}
399
400void vrpn_3DConnexion_SpaceMouseProWireless::decodePacket(size_t bytes, vrpn_uint8 *buffer)
401{
402 // under windows anyway, reports are always 13 bytes long
403 if ((bytes % 13) != 0) {
404 return;
405 }
406
407 for (size_t i = 0; i < bytes / 13; i++) {
408
409 vrpn_uint8 *report = buffer + (i * 13);
410 char report_type = report[0];
411 vrpn_uint8 *bufptr = &report[1];
412 const float scale = 1.0f / 350.f; // max value observed is 0x15e or 350 (signed)
413
414 switch (report_type) {
415 // Report type 1 includes both position and rotation on this device.
416 case 0x1:
417 {
418 for (int c = 0; c < 6; c++) {
419
420 channel[c] = vrpn_unbuffer_from_little_endian<vrpn_int16>(bufptr) * scale;
421
422 //clamp to sane range
423 if (channel[c] < -1.0) { channel[c] = -1.0; }
424 if (channel[c] > 1.0) { channel[c] = 1.0; }
425 }
426 break;
427 }
428
429 case 0x3: // Button report - hw only seems to have 15 buttons, but they aren't tightly packed
430 {
431 for (int btn = 0; btn < vrpn_Button::num_buttons; btn++) {
432 vrpn_uint8 *location = report + 1 + (btn / 8);
433 vrpn_uint8 mask = 1 << (btn % 8);
434 buttons[btn] = ((*location) & mask) != 0;
435 }
436 break;
437 }
438
439 case 0x17: // don't know what this is, seems to get sent at the end of some data after maybe a timeout?
440 break;
441
442 default:
444 send_text_message("Unknown report type", _timestamp,
446 }
447 // Report this event before parsing the next.
449 }
450}
451
vrpn_3DConnexion_Navigator_for_Notebooks(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_Navigator(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_SpaceBall5000(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_SpaceExplorer(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_SpaceMouseCompact(const char *name, vrpn_Connection *c=0)
void decodePacket(size_t bytes, vrpn_uint8 *buffer)
vrpn_3DConnexion_SpaceMouseProWireless(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_SpaceMousePro(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_SpaceMouseWireless2(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_SpaceMouseWireless(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_SpaceMouse(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_SpacePilotPro(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_SpacePilot(const char *name, vrpn_Connection *c=0)
vrpn_3DConnexion_Traveler(const char *name, vrpn_Connection *c=0)
vrpn_HidAcceptor * _filter
void report(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY)
struct timeval _timestamp
virtual void mainloop()
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
void on_data_received(size_t bytes, vrpn_uint8 *buffer)
Derived class reimplements this callback.
virtual ~vrpn_3DConnexion()
vrpn_3DConnexion(vrpn_HidAcceptor *filter, unsigned num_buttons, const char *name, vrpn_Connection *c=0, vrpn_uint16 vendor=0, vrpn_uint16 product=0)
virtual void decodePacket(size_t bytes, vrpn_uint8 *buffer)
vrpn_float64 last[vrpn_CHANNEL_MAX]
Definition vrpn_Analog.h:39
vrpn_float64 channel[vrpn_CHANNEL_MAX]
Definition vrpn_Analog.h:38
vrpn_Analog(const char *name, vrpn_Connection *c=NULL)
Definition vrpn_Analog.C:14
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
vrpn_Connection * d_connection
Connection that this object talks to.
void server_mainloop(void)
Handles functions that all servers should provide in their mainloop() (ping/pong, for example) Should...
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 void report_changes(void)
vrpn_Button_Filter(const char *, vrpn_Connection *c=NULL)
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.
vrpn_uint16 product() const
Returns USB product ID of connected device May not contain valid if an already-open device was provid...
vrpn_HidInterface(vrpn_HidAcceptor *acceptor, vrpn_uint16 vendor=0, vrpn_uint16 product=0, hid_device *device=NULL)
Constructor If we already have a HID device from some other source, it can be passed and we'll take o...
virtual void update()
Polls the device buffers and causes on_data_received callbacks if appropriate You NEED to call this f...
vrpn_uint16 vendor() const
Returns USB vendor ID of connected device May not contain valid if an already-open device was provide...
Accepts any device with the given vendor and product IDs.
All types of client/server/peer objects in VRPN should be derived from the vrpn_BaseClass type descri...
@ vrpn_TEXT_WARNING
@ vrpn_TEXT_ERROR
#define VRPN_USE_HID
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...
int vrpn_noint_block_read(int infile, char buffer[], size_t length)
#define EV_REL
#define EV_KEY
#define vrpn_gettimeofday