ndof device detection now visible in log
[blender.git] / intern / ghost / intern / GHOST_NDOFManager.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s):
19  *   Mike Erwin
20  *
21  * ***** END GPL LICENSE BLOCK *****
22  */
23
24 #include "GHOST_NDOFManager.h"
25 #include "GHOST_EventNDOF.h"
26 #include "GHOST_EventKey.h"
27 #include "GHOST_WindowManager.h"
28 #include <string.h> // for memory functions
29 #include <stdio.h> // for error/info reporting
30
31 #ifdef DEBUG_NDOF_BUTTONS
32 static const char* ndof_button_names[] = {
33         // used internally, never sent
34         "NDOF_BUTTON_NONE",
35         // these two are available from any 3Dconnexion device
36         "NDOF_BUTTON_MENU",
37         "NDOF_BUTTON_FIT",
38         // standard views
39         "NDOF_BUTTON_TOP",
40         "NDOF_BUTTON_BOTTOM",
41         "NDOF_BUTTON_LEFT",
42         "NDOF_BUTTON_RIGHT",
43         "NDOF_BUTTON_FRONT",
44         "NDOF_BUTTON_BACK",
45         // more views
46         "NDOF_BUTTON_ISO1",
47         "NDOF_BUTTON_ISO2",
48         // 90 degree rotations
49         "NDOF_BUTTON_ROLL_CW",
50         "NDOF_BUTTON_ROLL_CCW",
51         "NDOF_BUTTON_SPIN_CW",
52         "NDOF_BUTTON_SPIN_CCW",
53         "NDOF_BUTTON_TILT_CW",
54         "NDOF_BUTTON_TILT_CCW",
55         // device control
56         "NDOF_BUTTON_ROTATE",
57         "NDOF_BUTTON_PANZOOM",
58         "NDOF_BUTTON_DOMINANT",
59         "NDOF_BUTTON_PLUS",
60         "NDOF_BUTTON_MINUS",
61         // general-purpose buttons
62         "NDOF_BUTTON_1",
63         "NDOF_BUTTON_2",
64         "NDOF_BUTTON_3",
65         "NDOF_BUTTON_4",
66         "NDOF_BUTTON_5",
67         "NDOF_BUTTON_6",
68         "NDOF_BUTTON_7",
69         "NDOF_BUTTON_8",
70         "NDOF_BUTTON_9",
71         "NDOF_BUTTON_10",
72         };
73 #endif
74
75 static const NDOF_ButtonT SpaceNavigator_HID_map[] =
76         {
77         NDOF_BUTTON_MENU,
78         NDOF_BUTTON_FIT
79         };
80
81 static const NDOF_ButtonT SpaceExplorer_HID_map[] =
82         {
83         NDOF_BUTTON_1,
84         NDOF_BUTTON_2,
85         NDOF_BUTTON_TOP,
86         NDOF_BUTTON_LEFT,
87         NDOF_BUTTON_RIGHT,
88         NDOF_BUTTON_FRONT,
89         NDOF_BUTTON_NONE, // esc key
90         NDOF_BUTTON_NONE, // alt key
91         NDOF_BUTTON_NONE, // shift key
92         NDOF_BUTTON_NONE, // ctrl key
93         NDOF_BUTTON_FIT,
94         NDOF_BUTTON_MENU,
95         NDOF_BUTTON_PLUS,
96         NDOF_BUTTON_MINUS,
97         NDOF_BUTTON_ROTATE
98         };
99
100 static const NDOF_ButtonT SpacePilotPro_HID_map[] =
101         {
102         NDOF_BUTTON_MENU,
103         NDOF_BUTTON_FIT,
104         NDOF_BUTTON_TOP,
105         NDOF_BUTTON_LEFT,
106         NDOF_BUTTON_RIGHT,
107         NDOF_BUTTON_FRONT,
108         NDOF_BUTTON_BOTTOM,
109         NDOF_BUTTON_BACK,
110         NDOF_BUTTON_ROLL_CW,
111         NDOF_BUTTON_ROLL_CCW,
112         NDOF_BUTTON_ISO1,
113         NDOF_BUTTON_ISO2,
114         NDOF_BUTTON_1,
115         NDOF_BUTTON_2,
116         NDOF_BUTTON_3,
117         NDOF_BUTTON_4,
118         NDOF_BUTTON_5,
119         NDOF_BUTTON_6,
120         NDOF_BUTTON_7,
121         NDOF_BUTTON_8,
122         NDOF_BUTTON_9,
123         NDOF_BUTTON_10,
124         NDOF_BUTTON_NONE, // esc key
125         NDOF_BUTTON_NONE, // alt key
126         NDOF_BUTTON_NONE, // shift key
127         NDOF_BUTTON_NONE, // ctrl key
128         NDOF_BUTTON_ROTATE,
129         NDOF_BUTTON_PANZOOM,
130         NDOF_BUTTON_DOMINANT,
131         NDOF_BUTTON_PLUS,
132         NDOF_BUTTON_MINUS
133         };
134
135 GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System& sys)
136         : m_system(sys)
137         , m_deviceType(NDOF_UnknownDevice) // each platform has its own device detection code
138         , m_buttonCount(0)
139         , m_buttonMask(0)
140         , m_buttons(0)
141         , m_motionTime(1000) // one full second (operators should filter out such large time deltas)
142         , m_prevMotionTime(0)
143         , m_atRest(true)
144         {
145         // to avoid the rare situation where one triple is updated and
146         // the other is not, initialize them both here:
147         memset(m_translation, 0, sizeof(m_translation));
148         memset(m_rotation, 0, sizeof(m_rotation));
149         }
150
151 void GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short product_id)
152         {
153         switch (vendor_id)
154                 {
155                 case 0x046D: // Logitech (3Dconnexion)
156                         switch (product_id)
157                                 {
158                                 // -- current devices --
159                                 case 0xC626:
160                                         puts("ndof: using SpaceNavigator");
161                                         m_deviceType = NDOF_SpaceNavigator;
162                                         m_buttonCount = 2;
163                                         break;
164                                 case 0xC628:
165                                         puts("ndof: using SpaceNavigator for Notebooks");
166                                         m_deviceType = NDOF_SpaceNavigator; // for Notebooks
167                                         m_buttonCount = 2;
168                                         break;
169                                 case 0xC627:
170                                         puts("ndof: using SpaceExplorer");
171                                         m_deviceType = NDOF_SpaceExplorer;
172                                         m_buttonCount = 15;
173                                         break;
174                                 case 0xC629:
175                                         puts("ndof: using SpacePilotPro");
176                                         m_deviceType = NDOF_SpacePilotPro;
177                                         m_buttonCount = 31;
178                                         break;
179
180                                 // -- older devices --
181                                 case 0xC623: puts("ndof: SpaceTraveler not supported, please file a bug report"); break;
182                                         // no buttons?
183                                 case 0xC625: puts("ndof: SpacePilot not supported, please file a bug report"); break;
184                                         // 21 buttons
185
186                                 default: printf("ndof: unknown Logitech product %04hx\n", product_id);
187                                 }
188                         break;
189                 default:
190                         printf("ndof: unknown vendor %04hx\n", vendor_id);
191                 }
192
193         m_buttonMask = ~(-1 << m_buttonCount);
194
195         #ifdef DEBUG_NDOF_BUTTONS
196         printf("ndof: %d buttons -> hex:%X\n", m_buttonCount, m_buttonMask);
197         #endif
198         }
199
200 void GHOST_NDOFManager::updateTranslation(short t[3], GHOST_TUns64 time)
201         {
202         memcpy(m_translation, t, sizeof(m_translation));
203         m_motionTime = time;
204         m_atRest = false;
205         }
206
207 void GHOST_NDOFManager::updateRotation(short r[3], GHOST_TUns64 time)
208         {
209         memcpy(m_rotation, r, sizeof(m_rotation));
210         m_motionTime = time;
211         m_atRest = false;
212         }
213
214 void GHOST_NDOFManager::sendButtonEvent(NDOF_ButtonT button, bool press, GHOST_TUns64 time, GHOST_IWindow* window)
215         {
216         GHOST_EventNDOFButton* event = new GHOST_EventNDOFButton(time, window);
217         GHOST_TEventNDOFButtonData* data = (GHOST_TEventNDOFButtonData*) event->getData();
218
219         data->action = press ? GHOST_kPress : GHOST_kRelease;
220         data->button = button;
221
222         #ifdef DEBUG_NDOF_BUTTONS
223         printf("%s %s\n", ndof_button_names[button], press ? "pressed" : "released");
224         #endif
225
226         m_system.pushEvent(event);
227         }
228
229 void GHOST_NDOFManager::sendKeyEvent(GHOST_TKey key, bool press, GHOST_TUns64 time, GHOST_IWindow* window)
230         {
231         GHOST_TEventType type = press ? GHOST_kEventKeyDown : GHOST_kEventKeyUp;
232         GHOST_EventKey* event = new GHOST_EventKey(time, type, window, key);
233
234         #ifdef DEBUG_NDOF_BUTTONS
235         printf("keyboard %s\n", press ? "down" : "up");
236         #endif
237
238         m_system.pushEvent(event);
239         }
240
241 void GHOST_NDOFManager::updateButton(int button_number, bool press, GHOST_TUns64 time)
242         {
243         GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow();
244
245         #ifdef DEBUG_NDOF_BUTTONS
246         if (m_deviceType != NDOF_UnknownDevice)
247                 printf("ndof: button %d -> ", button_number);
248         #endif
249
250         switch (m_deviceType)
251                 {
252                 case NDOF_SpaceNavigator:
253                         sendButtonEvent(SpaceNavigator_HID_map[button_number], press, time, window);
254                         break;
255                 case NDOF_SpaceExplorer:
256                         switch (button_number)
257                                 {
258                                 case 6: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break;
259                                 case 7: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break;
260                                 case 8: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break;
261                                 case 9: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break;
262                                 default: sendButtonEvent(SpaceExplorer_HID_map[button_number], press, time, window);
263                                 }
264                         break;
265                 case NDOF_SpacePilotPro:
266                         switch (button_number)
267                                 {
268                                 case 22: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break;
269                                 case 23: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break;
270                                 case 24: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break;
271                                 case 25: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break;
272                                 default: sendButtonEvent(SpacePilotPro_HID_map[button_number], press, time, window);
273                                 }
274                         break;
275                 case NDOF_UnknownDevice:
276                         printf("ndof: button %d on unknown device (ignoring)\n", button_number);
277                 }
278
279         int mask = 1 << button_number;
280         if (press)
281                 m_buttons |= mask; // set this button's bit
282         else
283                 m_buttons &= ~mask; // clear this button's bit
284         }
285
286 void GHOST_NDOFManager::updateButtons(int button_bits, GHOST_TUns64 time)
287         {
288         button_bits &= m_buttonMask; // discard any "garbage" bits
289
290         int diff = m_buttons ^ button_bits;
291
292         for (int button_number = 0; button_number <= 31; ++button_number)
293                 {
294                 int mask = 1 << button_number;
295
296                 if (diff & mask)
297                         {
298                         bool press = button_bits & mask;
299                         updateButton(button_number, press, time);
300                         }
301                 }
302         }
303
304 bool GHOST_NDOFManager::sendMotionEvent()
305         {
306         if (m_atRest)
307                 return false;
308
309         GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow();
310
311         GHOST_EventNDOFMotion* event = new GHOST_EventNDOFMotion(m_motionTime, window);
312         GHOST_TEventNDOFMotionData* data = (GHOST_TEventNDOFMotionData*) event->getData();
313
314         const float scale = 1.f / 350.f; // 3Dconnexion devices send +/- 350 usually
315
316         // possible future enhancement
317         // scale *= m_sensitivity;
318
319         data->tx = scale * m_translation[0];
320         data->ty = scale * m_translation[1];
321         data->tz = scale * m_translation[2];
322
323         data->rx = scale * m_rotation[0];
324         data->ry = scale * m_rotation[1];
325         data->rz = scale * m_rotation[2];
326
327         data->dt = 0.001f * (m_motionTime - m_prevMotionTime); // in seconds
328
329         m_prevMotionTime = m_motionTime;
330
331         #ifdef DEBUG_NDOF_MOTION
332         printf("ndof: T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f) dt=%.3f\n",
333                 data->tx, data->ty, data->tz, data->rx, data->ry, data->rz, data->dt);
334         #endif
335
336         m_system.pushEvent(event);
337
338         // 'at rest' test goes at the end so that the first 'rest' event gets sent
339         m_atRest = m_rotation[0] == 0 && m_rotation[1] == 0 && m_rotation[2] == 0 &&
340                 m_translation[0] == 0 && m_translation[1] == 0 && m_translation[2] == 0;
341         // this needs to be aware of calibration -- 0.01 0.01 0.03 might be 'rest'
342
343         return true;
344         }