Blender V2.61 - r43446

GHOST_NDOFManager.cpp

Go to the documentation of this file.
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 }