|
Blender
V2.59
|
00001 /* 00002 * $Id: GHOST_NDOFManager.cpp 39153 2011-08-07 16:44:10Z merwin $ 00003 * 00004 * ***** BEGIN GPL LICENSE BLOCK ***** 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU General Public License 00008 * as published by the Free Software Foundation; either version 2 00009 * of the License, or (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software Foundation, 00018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 * 00020 * Contributor(s): 00021 * Mike Erwin 00022 * 00023 * ***** END GPL LICENSE BLOCK ***** 00024 */ 00025 00026 #include "GHOST_Debug.h" 00027 #include "GHOST_NDOFManager.h" 00028 #include "GHOST_EventNDOF.h" 00029 #include "GHOST_EventKey.h" 00030 #include "GHOST_WindowManager.h" 00031 #include <string.h> // for memory functions 00032 #include <stdio.h> // for error/info reporting 00033 #include <math.h> 00034 00035 #ifdef DEBUG_NDOF_MOTION 00036 // printable version of each GHOST_TProgress value 00037 static const char* progress_string[] = 00038 {"not started","starting","in progress","finishing","finished"}; 00039 #endif 00040 00041 #ifdef DEBUG_NDOF_BUTTONS 00042 static const char* ndof_button_names[] = { 00043 // used internally, never sent 00044 "NDOF_BUTTON_NONE", 00045 // these two are available from any 3Dconnexion device 00046 "NDOF_BUTTON_MENU", 00047 "NDOF_BUTTON_FIT", 00048 // standard views 00049 "NDOF_BUTTON_TOP", 00050 "NDOF_BUTTON_BOTTOM", 00051 "NDOF_BUTTON_LEFT", 00052 "NDOF_BUTTON_RIGHT", 00053 "NDOF_BUTTON_FRONT", 00054 "NDOF_BUTTON_BACK", 00055 // more views 00056 "NDOF_BUTTON_ISO1", 00057 "NDOF_BUTTON_ISO2", 00058 // 90 degree rotations 00059 "NDOF_BUTTON_ROLL_CW", 00060 "NDOF_BUTTON_ROLL_CCW", 00061 "NDOF_BUTTON_SPIN_CW", 00062 "NDOF_BUTTON_SPIN_CCW", 00063 "NDOF_BUTTON_TILT_CW", 00064 "NDOF_BUTTON_TILT_CCW", 00065 // device control 00066 "NDOF_BUTTON_ROTATE", 00067 "NDOF_BUTTON_PANZOOM", 00068 "NDOF_BUTTON_DOMINANT", 00069 "NDOF_BUTTON_PLUS", 00070 "NDOF_BUTTON_MINUS", 00071 // general-purpose buttons 00072 "NDOF_BUTTON_1", 00073 "NDOF_BUTTON_2", 00074 "NDOF_BUTTON_3", 00075 "NDOF_BUTTON_4", 00076 "NDOF_BUTTON_5", 00077 "NDOF_BUTTON_6", 00078 "NDOF_BUTTON_7", 00079 "NDOF_BUTTON_8", 00080 "NDOF_BUTTON_9", 00081 "NDOF_BUTTON_10", 00082 }; 00083 #endif 00084 00085 static const NDOF_ButtonT SpaceNavigator_HID_map[] = { 00086 NDOF_BUTTON_MENU, 00087 NDOF_BUTTON_FIT 00088 }; 00089 00090 static const NDOF_ButtonT SpaceExplorer_HID_map[] = { 00091 NDOF_BUTTON_1, 00092 NDOF_BUTTON_2, 00093 NDOF_BUTTON_TOP, 00094 NDOF_BUTTON_LEFT, 00095 NDOF_BUTTON_RIGHT, 00096 NDOF_BUTTON_FRONT, 00097 NDOF_BUTTON_NONE, // esc key 00098 NDOF_BUTTON_NONE, // alt key 00099 NDOF_BUTTON_NONE, // shift key 00100 NDOF_BUTTON_NONE, // ctrl key 00101 NDOF_BUTTON_FIT, 00102 NDOF_BUTTON_MENU, 00103 NDOF_BUTTON_PLUS, 00104 NDOF_BUTTON_MINUS, 00105 NDOF_BUTTON_ROTATE 00106 }; 00107 00108 static const NDOF_ButtonT SpacePilotPro_HID_map[] = { 00109 NDOF_BUTTON_MENU, 00110 NDOF_BUTTON_FIT, 00111 NDOF_BUTTON_TOP, 00112 NDOF_BUTTON_LEFT, 00113 NDOF_BUTTON_RIGHT, 00114 NDOF_BUTTON_FRONT, 00115 NDOF_BUTTON_BOTTOM, 00116 NDOF_BUTTON_BACK, 00117 NDOF_BUTTON_ROLL_CW, 00118 NDOF_BUTTON_ROLL_CCW, 00119 NDOF_BUTTON_ISO1, 00120 NDOF_BUTTON_ISO2, 00121 NDOF_BUTTON_1, 00122 NDOF_BUTTON_2, 00123 NDOF_BUTTON_3, 00124 NDOF_BUTTON_4, 00125 NDOF_BUTTON_5, 00126 NDOF_BUTTON_6, 00127 NDOF_BUTTON_7, 00128 NDOF_BUTTON_8, 00129 NDOF_BUTTON_9, 00130 NDOF_BUTTON_10, 00131 NDOF_BUTTON_NONE, // esc key 00132 NDOF_BUTTON_NONE, // alt key 00133 NDOF_BUTTON_NONE, // shift key 00134 NDOF_BUTTON_NONE, // ctrl key 00135 NDOF_BUTTON_ROTATE, 00136 NDOF_BUTTON_PANZOOM, 00137 NDOF_BUTTON_DOMINANT, 00138 NDOF_BUTTON_PLUS, 00139 NDOF_BUTTON_MINUS 00140 }; 00141 00142 /* this is the older SpacePilot (sans Pro) 00143 * thanks to polosson for the info in this table */ 00144 static const NDOF_ButtonT SpacePilot_HID_map[] = { 00145 NDOF_BUTTON_1, 00146 NDOF_BUTTON_2, 00147 NDOF_BUTTON_3, 00148 NDOF_BUTTON_4, 00149 NDOF_BUTTON_5, 00150 NDOF_BUTTON_6, 00151 NDOF_BUTTON_TOP, 00152 NDOF_BUTTON_LEFT, 00153 NDOF_BUTTON_RIGHT, 00154 NDOF_BUTTON_FRONT, 00155 NDOF_BUTTON_NONE, // esc key 00156 NDOF_BUTTON_NONE, // alt key 00157 NDOF_BUTTON_NONE, // shift key 00158 NDOF_BUTTON_NONE, // ctrl key 00159 NDOF_BUTTON_FIT, 00160 NDOF_BUTTON_MENU, 00161 NDOF_BUTTON_PLUS, 00162 NDOF_BUTTON_MINUS, 00163 NDOF_BUTTON_DOMINANT, 00164 NDOF_BUTTON_ROTATE, 00165 NDOF_BUTTON_NONE // the CONFIG button -- what does it do? 00166 }; 00167 00168 GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System& sys) 00169 : m_system(sys) 00170 , m_deviceType(NDOF_UnknownDevice) // each platform has its own device detection code 00171 , m_buttonCount(0) 00172 , m_buttonMask(0) 00173 , m_buttons(0) 00174 , m_motionTime(0) 00175 , m_prevMotionTime(0) 00176 , m_motionState(GHOST_kNotStarted) 00177 , m_motionEventPending(false) 00178 , m_deadZone(0.f) 00179 { 00180 // to avoid the rare situation where one triple is updated and 00181 // the other is not, initialize them both here: 00182 memset(m_translation, 0, sizeof(m_translation)); 00183 memset(m_rotation, 0, sizeof(m_rotation)); 00184 } 00185 00186 bool GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short product_id) 00187 { 00188 // default to NDOF_UnknownDevice so rogue button events will get discarded 00189 // "mystery device" owners can help build a HID_map for their hardware 00190 00191 switch (vendor_id) { 00192 case 0x046D: // Logitech (3Dconnexion) 00193 switch (product_id) { 00194 // -- current devices -- 00195 case 0xC626: 00196 puts("ndof: using SpaceNavigator"); 00197 m_deviceType = NDOF_SpaceNavigator; 00198 m_buttonCount = 2; 00199 break; 00200 case 0xC628: 00201 puts("ndof: using SpaceNavigator for Notebooks"); 00202 m_deviceType = NDOF_SpaceNavigator; // for Notebooks 00203 m_buttonCount = 2; 00204 break; 00205 case 0xC627: 00206 puts("ndof: using SpaceExplorer"); 00207 m_deviceType = NDOF_SpaceExplorer; 00208 m_buttonCount = 15; 00209 break; 00210 case 0xC629: 00211 puts("ndof: using SpacePilotPro"); 00212 m_deviceType = NDOF_SpacePilotPro; 00213 m_buttonCount = 31; 00214 break; 00215 00216 // -- older devices -- 00217 case 0xC625: 00218 puts("ndof: using SpacePilot"); 00219 m_deviceType = NDOF_SpacePilot; 00220 m_buttonCount = 21; 00221 break; 00222 00223 case 0xC623: 00224 puts("ndof: SpaceTraveler not supported, please file a bug report"); 00225 m_buttonCount = 8; 00226 break; 00227 00228 default: 00229 printf("ndof: unknown Logitech product %04hx\n", product_id); 00230 } 00231 break; 00232 default: 00233 printf("ndof: unknown device %04hx:%04hx\n", vendor_id, product_id); 00234 } 00235 00236 if (m_deviceType == NDOF_UnknownDevice) { 00237 return false; 00238 } 00239 else { 00240 m_buttonMask = ~(-1 << m_buttonCount); 00241 00242 #ifdef DEBUG_NDOF_BUTTONS 00243 printf("ndof: %d buttons -> hex:%X\n", m_buttonCount, m_buttonMask); 00244 #endif 00245 00246 return true; 00247 } 00248 } 00249 00250 void GHOST_NDOFManager::updateTranslation(short t[3], GHOST_TUns64 time) 00251 { 00252 memcpy(m_translation, t, sizeof(m_translation)); 00253 m_motionTime = time; 00254 m_motionEventPending = true; 00255 } 00256 00257 void GHOST_NDOFManager::updateRotation(short r[3], GHOST_TUns64 time) 00258 { 00259 memcpy(m_rotation, r, sizeof(m_rotation)); 00260 m_motionTime = time; 00261 m_motionEventPending = true; 00262 } 00263 00264 void GHOST_NDOFManager::sendButtonEvent(NDOF_ButtonT button, bool press, GHOST_TUns64 time, GHOST_IWindow* window) 00265 { 00266 GHOST_EventNDOFButton* event = new GHOST_EventNDOFButton(time, window); 00267 GHOST_TEventNDOFButtonData* data = (GHOST_TEventNDOFButtonData*) event->getData(); 00268 00269 data->action = press ? GHOST_kPress : GHOST_kRelease; 00270 data->button = button; 00271 00272 #ifdef DEBUG_NDOF_BUTTONS 00273 printf("%s %s\n", ndof_button_names[button], press ? "pressed" : "released"); 00274 #endif 00275 00276 m_system.pushEvent(event); 00277 } 00278 00279 void GHOST_NDOFManager::sendKeyEvent(GHOST_TKey key, bool press, GHOST_TUns64 time, GHOST_IWindow* window) 00280 { 00281 GHOST_TEventType type = press ? GHOST_kEventKeyDown : GHOST_kEventKeyUp; 00282 GHOST_EventKey* event = new GHOST_EventKey(time, type, window, key); 00283 00284 #ifdef DEBUG_NDOF_BUTTONS 00285 printf("keyboard %s\n", press ? "down" : "up"); 00286 #endif 00287 00288 m_system.pushEvent(event); 00289 } 00290 00291 void GHOST_NDOFManager::updateButton(int button_number, bool press, GHOST_TUns64 time) 00292 { 00293 GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow(); 00294 00295 #ifdef DEBUG_NDOF_BUTTONS 00296 if (m_deviceType != NDOF_UnknownDevice) 00297 printf("ndof: button %d -> ", button_number); 00298 #endif 00299 00300 switch (m_deviceType) { 00301 case NDOF_SpaceNavigator: 00302 sendButtonEvent(SpaceNavigator_HID_map[button_number], press, time, window); 00303 break; 00304 case NDOF_SpaceExplorer: 00305 switch (button_number) { 00306 case 6: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break; 00307 case 7: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break; 00308 case 8: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break; 00309 case 9: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break; 00310 default: sendButtonEvent(SpaceExplorer_HID_map[button_number], press, time, window); 00311 } 00312 break; 00313 case NDOF_SpacePilotPro: 00314 switch (button_number) { 00315 case 22: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break; 00316 case 23: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break; 00317 case 24: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break; 00318 case 25: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break; 00319 default: sendButtonEvent(SpacePilotPro_HID_map[button_number], press, time, window); 00320 } 00321 break; 00322 case NDOF_SpacePilot: 00323 switch (button_number) { 00324 case 10: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break; 00325 case 11: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break; 00326 case 12: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break; 00327 case 13: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break; 00328 case 20: puts("ndof: ignoring CONFIG button"); break; 00329 default: sendButtonEvent(SpacePilot_HID_map[button_number], press, time, window); 00330 } 00331 break; 00332 case NDOF_UnknownDevice: 00333 printf("ndof: button %d on unknown device (ignoring)\n", button_number); 00334 } 00335 00336 int mask = 1 << button_number; 00337 if (press) { 00338 m_buttons |= mask; // set this button's bit 00339 } 00340 else { 00341 m_buttons &= ~mask; // clear this button's bit 00342 } 00343 } 00344 00345 void GHOST_NDOFManager::updateButtons(int button_bits, GHOST_TUns64 time) 00346 { 00347 button_bits &= m_buttonMask; // discard any "garbage" bits 00348 00349 int diff = m_buttons ^ button_bits; 00350 00351 for (int button_number = 0; button_number < m_buttonCount; ++button_number) { 00352 int mask = 1 << button_number; 00353 00354 if (diff & mask) { 00355 bool press = button_bits & mask; 00356 updateButton(button_number, press, time); 00357 } 00358 } 00359 } 00360 00361 void GHOST_NDOFManager::setDeadZone(float dz) 00362 { 00363 if (dz < 0.f) { 00364 // negative values don't make sense, so clamp at zero 00365 dz = 0.f; 00366 } 00367 else if (dz > 0.5f) { 00368 // warn the rogue user/programmer, but allow it 00369 printf("ndof: dead zone of %.2f is rather high...\n", dz); 00370 } 00371 m_deadZone = dz; 00372 00373 printf("ndof: dead zone set to %.2f\n", dz); 00374 } 00375 00376 static bool atHomePosition(GHOST_TEventNDOFMotionData* ndof) 00377 { 00378 #define HOME(foo) (ndof->foo == 0.f) 00379 return HOME(tx) && HOME(ty) && HOME(tz) && HOME(rx) && HOME(ry) && HOME(rz); 00380 #undef HOME 00381 } 00382 00383 static bool nearHomePosition(GHOST_TEventNDOFMotionData* ndof, float threshold) 00384 { 00385 if (threshold == 0.f) { 00386 return atHomePosition(ndof); 00387 } 00388 else { 00389 #define HOME(foo) (fabsf(ndof->foo) < threshold) 00390 return HOME(tx) && HOME(ty) && HOME(tz) && HOME(rx) && HOME(ry) && HOME(rz); 00391 #undef HOME 00392 } 00393 } 00394 00395 bool GHOST_NDOFManager::sendMotionEvent() 00396 { 00397 if (!m_motionEventPending) 00398 return false; 00399 00400 m_motionEventPending = false; // any pending motion is handled right now 00401 00402 GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow(); 00403 00404 if (window == NULL) { 00405 return false; // delivery will fail, so don't bother sending 00406 } 00407 00408 GHOST_EventNDOFMotion* event = new GHOST_EventNDOFMotion(m_motionTime, window); 00409 GHOST_TEventNDOFMotionData* data = (GHOST_TEventNDOFMotionData*) event->getData(); 00410 00411 // scale axis values here to normalize them to around +/- 1 00412 // they are scaled again for overall sensitivity in the WM based on user prefs 00413 00414 const float scale = 1.f / 350.f; // 3Dconnexion devices send +/- 350 usually 00415 00416 data->tx = scale * m_translation[0]; 00417 data->ty = scale * m_translation[1]; 00418 data->tz = scale * m_translation[2]; 00419 00420 data->rx = scale * m_rotation[0]; 00421 data->ry = scale * m_rotation[1]; 00422 data->rz = scale * m_rotation[2]; 00423 00424 data->dt = 0.001f * (m_motionTime - m_prevMotionTime); // in seconds 00425 00426 bool weHaveMotion = !nearHomePosition(data, m_deadZone); 00427 00428 // determine what kind of motion event to send (Starting, InProgress, Finishing) 00429 // and where that leaves this NDOF manager (NotStarted, InProgress, Finished) 00430 switch (m_motionState) { 00431 case GHOST_kNotStarted: 00432 case GHOST_kFinished: 00433 if (weHaveMotion) { 00434 data->progress = GHOST_kStarting; 00435 m_motionState = GHOST_kInProgress; 00436 // prev motion time will be ancient, so just make up a reasonable time delta 00437 data->dt = 0.0125f; 00438 } 00439 else { 00440 // send no event and keep current state 00441 delete event; 00442 return false; 00443 } 00444 break; 00445 case GHOST_kInProgress: 00446 if (weHaveMotion) { 00447 data->progress = GHOST_kInProgress; 00448 // remain 'InProgress' 00449 } 00450 else { 00451 data->progress = GHOST_kFinishing; 00452 m_motionState = GHOST_kFinished; 00453 } 00454 break; 00455 default: 00456 ; // will always be one of the above 00457 } 00458 00459 #ifdef DEBUG_NDOF_MOTION 00460 printf("ndof motion sent -- %s\n", progress_string[data->progress]); 00461 00462 // show details about this motion event 00463 printf(" T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f) dt=%.3f\n", 00464 data->tx, data->ty, data->tz, 00465 data->rx, data->ry, data->rz, 00466 data->dt); 00467 #endif 00468 00469 m_system.pushEvent(event); 00470 00471 m_prevMotionTime = m_motionTime; 00472 00473 return true; 00474 }