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