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