5c2ca658fdb86d48ddd105aabb43041eca7ff9e9
[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::updateTranslation(short t[3], GHOST_TUns64 time)
150         {
151         memcpy(m_translation, t, sizeof(m_translation));
152         m_motionTime = time;
153         m_atRest = false;
154         }
155
156 void GHOST_NDOFManager::updateRotation(short r[3], GHOST_TUns64 time)
157         {
158         memcpy(m_rotation, r, sizeof(m_rotation));
159         m_motionTime = time;
160         m_atRest = false;
161         }
162
163 void GHOST_NDOFManager::sendButtonEvent(NDOF_ButtonT button, bool press, GHOST_TUns64 time, GHOST_IWindow* window)
164         {
165         GHOST_EventNDOFButton* event = new GHOST_EventNDOFButton(time, window);
166         GHOST_TEventNDOFButtonData* data = (GHOST_TEventNDOFButtonData*) event->getData();
167
168         data->action = press ? GHOST_kPress : GHOST_kRelease;
169         data->button = button;
170
171         #ifdef DEBUG_NDOF_BUTTONS
172         printf("%s %s\n", ndof_button_names[button], press ? "pressed" : "released");
173         #endif
174
175         m_system.pushEvent(event);
176         }
177
178 void GHOST_NDOFManager::sendKeyEvent(GHOST_TKey key, bool press, GHOST_TUns64 time, GHOST_IWindow* window)
179         {
180         GHOST_TEventType type = press ? GHOST_kEventKeyDown : GHOST_kEventKeyUp;
181         GHOST_EventKey* event = new GHOST_EventKey(time, type, window, key);
182
183         #ifdef DEBUG_NDOF_BUTTONS
184         printf("keyboard %s\n", press ? "down" : "up");
185         #endif
186
187         m_system.pushEvent(event);
188         }
189
190 void GHOST_NDOFManager::updateButton(int button_number, bool press, GHOST_TUns64 time)
191         {
192         GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow();
193
194         #ifdef DEBUG_NDOF_BUTTONS
195         printf("button %d -> ", button_number);
196         #endif
197
198         switch (m_deviceType)
199                 {
200                 case NDOF_SpaceNavigator:
201                         sendButtonEvent(SpaceNavigator_HID_map[button_number], press, time, window);
202                         break;
203                 case NDOF_SpaceExplorer:
204                         switch (button_number)
205                                 {
206                                 case 6: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break;
207                                 case 7: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break;
208                                 case 8: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break;
209                                 case 9: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break;
210                                 default: sendButtonEvent(SpaceExplorer_HID_map[button_number], press, time, window);
211                                 }
212                         break;
213                 case NDOF_SpacePilotPro:
214                         switch (button_number)
215                                 {
216                                 case 22: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break;
217                                 case 23: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break;
218                                 case 24: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break;
219                                 case 25: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break;
220                                 default: sendButtonEvent(SpacePilotPro_HID_map[button_number], press, time, window);
221                                 }
222                         break;
223                 case NDOF_UnknownDevice:
224                         printf("button %d on unknown device (not sent)\n", button_number);
225                 }
226
227         int mask = 1 << button_number;
228         if (press)
229                 m_buttons |= mask; // set this button's bit
230         else
231                 m_buttons &= ~mask; // clear this button's bit
232         }
233
234 void GHOST_NDOFManager::updateButtons(int button_bits, GHOST_TUns64 time)
235         {
236         int diff = m_buttons ^ button_bits;
237
238         for (int button_number = 0; button_number <= 31; ++button_number)
239                 {
240                 int mask = 1 << button_number;
241
242                 if (diff & mask)
243                         {
244                         bool press = button_bits & mask;
245                         updateButton(button_number, press, time);
246                         }
247                 }
248         }
249
250 bool GHOST_NDOFManager::sendMotionEvent()
251         {
252         if (m_atRest)
253                 return false;
254
255         GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow();
256
257         GHOST_EventNDOFMotion* event = new GHOST_EventNDOFMotion(m_motionTime, window);
258         GHOST_TEventNDOFMotionData* data = (GHOST_TEventNDOFMotionData*) event->getData();
259
260         const float scale = 1.f / 350.f; // SpaceNavigator sends +/- 350 usually
261         // 350 according to their developer's guide; others recommend 500 as comfortable
262
263         // possible future enhancement
264         // scale *= m_sensitivity;
265
266         data->tx = -scale * m_translation[0];
267         data->ty = scale * m_translation[1];
268         data->tz = scale * m_translation[2];
269
270         data->rx = scale * m_rotation[0];
271         data->ry = scale * m_rotation[1];
272         data->rz = scale * m_rotation[2];
273
274         data->dt = 0.001f * (m_motionTime - m_prevMotionTime); // in seconds
275
276         m_prevMotionTime = m_motionTime;
277
278         #ifdef DEBUG_NDOF_MOTION
279         printf("sending T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f) dt=%.3f\n",
280                 data->tx, data->ty, data->tz, data->rx, data->ry, data->rz, data->dt);
281         #endif
282
283         m_system.pushEvent(event);
284
285         // 'at rest' test goes at the end so that the first 'rest' event gets sent
286         m_atRest = m_rotation[0] == 0 && m_rotation[1] == 0 && m_rotation[2] == 0 &&
287                 m_translation[0] == 0 && m_translation[1] == 0 && m_translation[2] == 0;
288         // this needs to be aware of calibration -- 0.01 0.01 0.03 might be 'rest'
289
290         return true;
291         }