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