finished Mac NDOF device detection, moved core device ID to base NDOFManager, clarifi...
[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 debug tracing
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 needs its own device detection code
138         , m_buttons(0)
139         , m_motionTime(1000) // one full second (operators should filter out such large time deltas)
140         , m_prevMotionTime(0)
141         , m_atRest(true)
142         {
143         // to avoid the rare situation where one triple is updated and
144         // the other is not, initialize them both here:
145         memset(m_translation, 0, sizeof(m_translation));
146         memset(m_rotation, 0, sizeof(m_rotation));
147         }
148
149 void GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short product_id)
150         {
151         switch (vendor_id)
152                 {
153                 case 0x046d: // Logitech (3Dconnexion)
154                         switch (product_id)
155                                 {
156                                 case 0xc626: m_deviceType = NDOF_SpaceNavigator; break;
157                                 case 0xc627: m_deviceType = NDOF_SpaceExplorer; break;
158                                 case 0xc629: m_deviceType = NDOF_SpacePilotPro; break;
159                                 default: printf("ndof: unknown Logitech product %04hx\n", product_id);
160                                 }
161                         break;
162                 default:
163                         printf("ndof: unknown vendor %04hx\n", vendor_id);
164                 }
165         }
166
167 void GHOST_NDOFManager::updateTranslation(short t[3], GHOST_TUns64 time)
168         {
169         memcpy(m_translation, t, sizeof(m_translation));
170         m_motionTime = time;
171         m_atRest = false;
172         }
173
174 void GHOST_NDOFManager::updateRotation(short r[3], GHOST_TUns64 time)
175         {
176         memcpy(m_rotation, r, sizeof(m_rotation));
177         m_motionTime = time;
178         m_atRest = false;
179         }
180
181 void GHOST_NDOFManager::sendButtonEvent(NDOF_ButtonT button, bool press, GHOST_TUns64 time, GHOST_IWindow* window)
182         {
183         GHOST_EventNDOFButton* event = new GHOST_EventNDOFButton(time, window);
184         GHOST_TEventNDOFButtonData* data = (GHOST_TEventNDOFButtonData*) event->getData();
185
186         data->action = press ? GHOST_kPress : GHOST_kRelease;
187         data->button = button;
188
189         #ifdef DEBUG_NDOF_BUTTONS
190         printf("%s %s\n", ndof_button_names[button], press ? "pressed" : "released");
191         #endif
192
193         m_system.pushEvent(event);
194         }
195
196 void GHOST_NDOFManager::sendKeyEvent(GHOST_TKey key, bool press, GHOST_TUns64 time, GHOST_IWindow* window)
197         {
198         GHOST_TEventType type = press ? GHOST_kEventKeyDown : GHOST_kEventKeyUp;
199         GHOST_EventKey* event = new GHOST_EventKey(time, type, window, key);
200
201         #ifdef DEBUG_NDOF_BUTTONS
202         printf("keyboard %s\n", press ? "down" : "up");
203         #endif
204
205         m_system.pushEvent(event);
206         }
207
208 void GHOST_NDOFManager::updateButton(int button_number, bool press, GHOST_TUns64 time)
209         {
210         GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow();
211
212         #ifdef DEBUG_NDOF_BUTTONS
213         printf("ndof: button %d -> ", button_number);
214         #endif
215
216         switch (m_deviceType)
217                 {
218                 case NDOF_SpaceNavigator:
219                         sendButtonEvent(SpaceNavigator_HID_map[button_number], press, time, window);
220                         break;
221                 case NDOF_SpaceExplorer:
222                         switch (button_number)
223                                 {
224                                 case 6: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break;
225                                 case 7: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break;
226                                 case 8: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break;
227                                 case 9: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break;
228                                 default: sendButtonEvent(SpaceExplorer_HID_map[button_number], press, time, window);
229                                 }
230                         break;
231                 case NDOF_SpacePilotPro:
232                         switch (button_number)
233                                 {
234                                 case 22: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break;
235                                 case 23: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break;
236                                 case 24: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break;
237                                 case 25: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break;
238                                 default: sendButtonEvent(SpacePilotPro_HID_map[button_number], press, time, window);
239                                 }
240                         break;
241                 case NDOF_UnknownDevice:
242                         printf("ndof: button %d on unknown device (ignoring)\n", button_number);
243                 }
244
245         int mask = 1 << button_number;
246         if (press)
247                 m_buttons |= mask; // set this button's bit
248         else
249                 m_buttons &= ~mask; // clear this button's bit
250         }
251
252 void GHOST_NDOFManager::updateButtons(int button_bits, GHOST_TUns64 time)
253         {
254         int diff = m_buttons ^ button_bits;
255
256         for (int button_number = 0; button_number <= 31; ++button_number)
257                 {
258                 int mask = 1 << button_number;
259
260                 if (diff & mask)
261                         {
262                         bool press = button_bits & mask;
263                         updateButton(button_number, press, time);
264                         }
265                 }
266         }
267
268 bool GHOST_NDOFManager::sendMotionEvent()
269         {
270         if (m_atRest)
271                 return false;
272
273         GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow();
274
275         GHOST_EventNDOFMotion* event = new GHOST_EventNDOFMotion(m_motionTime, window);
276         GHOST_TEventNDOFMotionData* data = (GHOST_TEventNDOFMotionData*) event->getData();
277
278         const float scale = 1.f / 350.f; // SpaceNavigator sends +/- 350 usually
279         // 350 according to their developer's guide; others recommend 500 as comfortable
280
281         // possible future enhancement
282         // scale *= m_sensitivity;
283
284         data->tx = -scale * m_translation[0];
285         data->ty = scale * m_translation[1];
286         data->tz = scale * m_translation[2];
287
288         data->rx = scale * m_rotation[0];
289         data->ry = scale * m_rotation[1];
290         data->rz = scale * m_rotation[2];
291
292         data->dt = 0.001f * (m_motionTime - m_prevMotionTime); // in seconds
293
294         m_prevMotionTime = m_motionTime;
295
296         #ifdef DEBUG_NDOF_MOTION
297         printf("ndof: T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f) dt=%.3f\n",
298                 data->tx, data->ty, data->tz, data->rx, data->ry, data->rz, data->dt);
299         #endif
300
301         m_system.pushEvent(event);
302
303         // 'at rest' test goes at the end so that the first 'rest' event gets sent
304         m_atRest = m_rotation[0] == 0 && m_rotation[1] == 0 && m_rotation[2] == 0 &&
305                 m_translation[0] == 0 && m_translation[1] == 0 && m_translation[2] == 0;
306         // this needs to be aware of calibration -- 0.01 0.01 0.03 might be 'rest'
307
308         return true;
309         }