migrated NDOF code from soc-2010-merwin, SpaceNavigator now works on Mac blender
authorMike Erwin <significant.bit@gmail.com>
Wed, 8 Jun 2011 21:18:03 +0000 (21:18 +0000)
committerMike Erwin <significant.bit@gmail.com>
Wed, 8 Jun 2011 21:18:03 +0000 (21:18 +0000)
20 files changed:
1  2 
build_files/scons/config/darwin-config.py
intern/ghost/GHOST_C-api.h
intern/ghost/GHOST_ISystem.h
intern/ghost/GHOST_Types.h
intern/ghost/SConscript
intern/ghost/intern/GHOST_C-api.cpp
intern/ghost/intern/GHOST_NDOFManager.cpp
intern/ghost/intern/GHOST_NDOFManagerCocoa.h
intern/ghost/intern/GHOST_NDOFManagerCocoa.mm
intern/ghost/intern/GHOST_NDOFManagerWin32.h
intern/ghost/intern/GHOST_System.cpp
intern/ghost/intern/GHOST_System.h
intern/ghost/intern/GHOST_SystemCocoa.h
intern/ghost/intern/GHOST_SystemCocoa.mm
source/blender/editors/space_view3d/view3d_edit.c
source/blender/editors/space_view3d/view3d_intern.h
source/blender/editors/space_view3d/view3d_ops.c
source/blender/windowmanager/WM_types.h
source/blender/windowmanager/intern/wm_event_system.c
source/blender/windowmanager/wm_event_types.h

@@@ -264,7 -264,7 +264,9 @@@ if MACOSX_ARCHITECTURE == 'i386'
      BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse']
  elif MACOSX_ARCHITECTURE == 'x86_64':
      BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse','-msse2']
--    
++
++# SpaceNavigator and related 3D mice
++WITH_BF_SPACENAV = True
  
  #############################################################################
  ###################  various compile settings and flags    ##################
@@@ -294,6 -294,6 +296,10 @@@ if WITH_BF_QUICKTIME == True
        else:
                PLATFORM_LINKFLAGS = PLATFORM_LINKFLAGS+['-framework','QuickTime']
  
++if WITH_BF_SPACENAV:
++      PLATFORM_LINKFLAGS = PLATFORM_LINKFLAGS + ['-weak_framework','3DconnexionClient']
++      CXXFLAGS = CXXFLAGS + ['-fpascal-strings'] # they use an old-skool Mac programming style
++
  #note to build succesfully on 10.3.9 SDK you need to patch  10.3.9 by adding the SystemStubs.a lib from 10.4
  LLIBS = ['stdc++', 'SystemStubs']
  
@@@ -288,22 -280,8 +288,7 @@@ extern GHOST_TSuccess GHOST_SetProgress
   * @param windowhandle The handle to the window
   */
  extern GHOST_TSuccess GHOST_EndProgressBar(GHOST_WindowHandle windowhandle);
-       
-       
- /***************************************************************************************
-  ** N-degree of freedom device management functionality
-  ***************************************************************************************/
-  
- /**
- * Open N-degree of freedom devices
-  */
- extern int GHOST_OpenNDOF(GHOST_SystemHandle systemhandle, 
-                            GHOST_WindowHandle windowhandle,
-                           GHOST_NDOFLibraryInit_fp setNdofLibraryInit, 
-                           GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
-                           GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen
-                           );
  
 -
  /***************************************************************************************
   ** Cursor management functionality
   ***************************************************************************************/
@@@ -291,29 -296,7 +291,13 @@@ public
         */
        virtual GHOST_TSuccess addEventConsumer(GHOST_IEventConsumer* consumer) = 0;
  
 +      /**
 +       * Removes the given event consumer to our list.
 +       * @param consumer The event consumer to remove.
 +       * @return Indication of success.
 +       */
 +      virtual GHOST_TSuccess removeEventConsumer(GHOST_IEventConsumer* consumer) = 0;
  
-        /***************************************************************************************
-        ** N-degree of freedom device management functionality
-        ***************************************************************************************/
-    /**
-     * Starts the N-degree of freedom device manager
-     */
-    virtual int openNDOF(GHOST_IWindow*,
-        GHOST_NDOFLibraryInit_fp setNdofLibraryInit, 
-        GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
-        GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen
-        // original patch only
-       // GHOST_NDOFEventHandler_fp setNdofEventHandler
-        ) = 0;
        /***************************************************************************************
         ** Cursor management functionality
         ***************************************************************************************/
@@@ -439,37 -421,23 +439,23 @@@ typedef struct 
        GHOST_TUns8 **strings;
  } GHOST_TStringArray;
  
- /* original patch used floats, but the driver return ints and uns. We will calibrate in view, no sense on doing conversions twice */
- /* as all USB device controls are likely to use ints, this is also more future proof */
- //typedef struct {
- //   /** N-degree of freedom device data */
- //   float tx, ty, tz;   /** -x left, +y up, +z forward */
- //   float rx, ry, rz;
- //   float dt;
- //} GHOST_TEventNDOFData;
+ typedef struct {
 -   /** N-degree of freedom device data v3 [GSoC 2010]*/
 -   /* Each component normally ranges from -1 to +1, but can exceed that. */
 -   float tx, ty, tz; /* translation: -x left, +y forward, -z up */
 -   float rx, ry, rz; /* rotation:
 -      axis = (rx,ry,rz).normalized
 -      amount = (rx,ry,rz).magnitude [in revolutions, 1.0 = 360 deg] */
 -   float dt; // time since previous NDOF Motion event (or zero if this is the first)
 -
++      /** N-degree of freedom device data v3 [GSoC 2010]*/
++      /* Each component normally ranges from -1 to +1, but can exceed that. */
++      float tx, ty, tz; /* translation: -x left, +y forward, -z up */
++      float rx, ry, rz; /* rotation:
++              axis = (rx,ry,rz).normalized
++              amount = (rx,ry,rz).magnitude [in revolutions, 1.0 = 360 deg] */
++      float dt; // time since previous NDOF Motion event (or zero if this is the first)
+ } GHOST_TEventNDOFMotionData;
 -typedef enum { GHOST_kPress, GHOST_kRelease } GHOST_TButtonAction; // good for mouse or other buttons too, hmmm?
++typedef enum { GHOST_kPress, GHOST_kRelease } GHOST_TButtonAction;
++      // good for mouse or other buttons too, hmmm?
  
  typedef struct {
-    /** N-degree of freedom device data v2*/
-    int changed;
-    GHOST_TUns64 client;
-    GHOST_TUns64 address;
-    GHOST_TInt16 tx, ty, tz;   /** -x left, +y up, +z forward */
-    GHOST_TInt16 rx, ry, rz;
-    GHOST_TInt16 buttons;
-    GHOST_TUns64 time;
-    GHOST_TUns64 delta;
- } GHOST_TEventNDOFData;
- typedef int     (*GHOST_NDOFLibraryInit_fp)(void);
- typedef void    (*GHOST_NDOFLibraryShutdown_fp)(void* deviceHandle);
- typedef void*   (*GHOST_NDOFDeviceOpen_fp)(void* platformData);
- // original patch windows callback. In mac os X version the callback is internal to the plug-in and post an event to main thead.
- // not necessary faster, but better integration with other events. 
- //typedef int     (*GHOST_NDOFEventHandler_fp)(float* result7, void* deviceHandle, unsigned int message, unsigned int* wParam, unsigned long* lParam);
- //typedef void     (*GHOST_NDOFCallBack_fp)(GHOST_TEventNDOFDataV2 *VolDatas);
+       GHOST_TButtonAction action;
+       short button;
+ } GHOST_TEventNDOFButtonData;
  
  typedef struct {
        /** The key code. */
@@@ -8,57 -8,54 +8,57 @@@ window_system = env['OURPLATFORM'
  
  sources = env.Glob('intern/*.cpp')
  if window_system == 'darwin':
 -      sources += env.Glob('intern/*.mm')
 +    sources += env.Glob('intern/*.mm')
  
  
- pf = ['GHOST_DisplayManager', 'GHOST_System', 'GHOST_SystemPaths', 'GHOST_Window', 'GHOST_DropTarget']
 -pf = ['GHOST_DisplayManager', 'GHOST_System', 'GHOST_Window', 'GHOST_DropTarget','GHOST_TabletManager']
++pf = ['GHOST_DisplayManager', 'GHOST_System', 'GHOST_SystemPaths', 'GHOST_Window', 'GHOST_DropTarget', 'GHOST_NDOFManager']
  defs=['_USE_MATH_DEFINES']
  
 -if window_system in ('linux2', 'openbsd3', 'sunos5', 'freebsd6', 'irix6', 'aix4', 'aix5'):
 -      for f in pf:
 -              try:
 -                      sources.remove('intern' + os.sep + f + 'Win32.cpp')
 -                      sources.remove('intern' + os.sep + f + 'Carbon.cpp')
 -              except ValueError:
 -                      pass
 +if window_system in ('linux2', 'openbsd3', 'sunos5', 'freebsd7', 'freebsd8', 'freebsd9', 'irix6', 'aix4', 'aix5'):
 +    for f in pf:
 +        try:
 +            sources.remove('intern' + os.sep + f + 'Win32.cpp')
 +            sources.remove('intern' + os.sep + f + 'Carbon.cpp')
 +        except ValueError:
 +            pass
 +    defs += ['PREFIX=\\"/usr/local/\\"']  # XXX, make an option
 +    defs += ['WITH_X11_XINPUT']  # XXX, make an option
 +
  elif window_system in ('win32-vc', 'win32-mingw', 'cygwin', 'linuxcross', 'win64-vc'):
 -      for f in pf:
 -              try:
 -                      sources.remove('intern' + os.sep + f + 'X11.cpp')
 -                      sources.remove('intern' + os.sep + f + 'Carbon.cpp')
 -              except ValueError:
 -                      pass
 +    for f in pf:
 +        try:
 +            sources.remove('intern' + os.sep + f + 'X11.cpp')
 +            sources.remove('intern' + os.sep + f + 'Carbon.cpp')
 +        except ValueError:
 +            pass
  elif window_system == 'darwin':
 -      if env['WITH_GHOST_COCOA']:
 -              if env['WITH_BF_QUICKTIME']:
 -                      defs.append('WITH_QUICKTIME')
 -              if env['USE_QTKIT']:
 -                      defs.append('USE_QTKIT')
 -              for f in pf:
 -                      try:
 -                              sources.remove('intern' + os.sep + f + 'Win32.cpp')
 -                              sources.remove('intern' + os.sep + f + 'X11.cpp')
 -                              sources.remove('intern' + os.sep + f + 'Carbon.cpp')
 -                      except ValueError:
 -                              pass
 -      else:
 -              for f in pf:
 -                      try:
 -                              sources.remove('intern' + os.sep + f + 'Win32.cpp')
 -                              sources.remove('intern' + os.sep + f + 'X11.cpp')
 -                              sources.remove('intern' + os.sep + f + 'Cocoa.mm')
 -                      except ValueError:
 -                              pass
 +    if env['WITH_GHOST_COCOA']:
 +        if env['WITH_BF_QUICKTIME']:
 +            defs.append('WITH_QUICKTIME')
 +        if env['USE_QTKIT']:
 +            defs.append('USE_QTKIT')
 +        for f in pf:
 +            try:
 +                sources.remove('intern' + os.sep + f + 'Win32.cpp')
 +                sources.remove('intern' + os.sep + f + 'X11.cpp')
 +                sources.remove('intern' + os.sep + f + 'Carbon.cpp')
 +            except ValueError:
 +                pass
 +    else:
 +        for f in pf:
 +            try:
 +                sources.remove('intern' + os.sep + f + 'Win32.cpp')
 +                sources.remove('intern' + os.sep + f + 'X11.cpp')
 +                sources.remove('intern' + os.sep + f + 'Cocoa.mm')
 +            except ValueError:
 +                pass
  
  else:
 -      print "Unknown window system specified."
 -      Exit()
 +    print "Unknown window system specified."
 +    Exit()
  
  if env['BF_GHOST_DEBUG']:
 -      defs.append('BF_GHOST_DEBUG')
 +    defs.append('BF_GHOST_DEBUG')
  
  incs = '. ../string #extern/glew/include #source/blender/imbuf #source/blender/makesdna ' + env['BF_OPENGL_INC']
  if window_system in ('win32-vc', 'win32-mingw', 'cygwin', 'linuxcross', 'win64-vc'):
@@@ -275,23 -263,7 +275,6 @@@ GHOST_TSuccess GHOST_EndProgressBar(GHO
  }
  
  
- int GHOST_OpenNDOF(GHOST_SystemHandle systemhandle, GHOST_WindowHandle windowhandle,
-    GHOST_NDOFLibraryInit_fp setNdofLibraryInit, 
-     GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
-     GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen)
-   //original patch only
-   /*  GHOST_NDOFEventHandler_fp setNdofEventHandler)*/
- {
-       GHOST_ISystem* system = (GHOST_ISystem*) systemhandle;
-     return system->openNDOF((GHOST_IWindow*) windowhandle,
-         setNdofLibraryInit, setNdofLibraryShutdown, setNdofDeviceOpen);
- //    original patch
- //        setNdofLibraryInit, setNdofLibraryShutdown, setNdofDeviceOpen, setNdofEventHandler);
- }
--
  GHOST_TStandardCursor GHOST_GetCursorShape(GHOST_WindowHandle windowhandle)
  {
        GHOST_IWindow* window = (GHOST_IWindow*) windowhandle;
   * along with this program; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
   *
-  * Contributor(s): none yet.
 - * Contributor(s): Mike Erwin, July 2010.
++ * Contributor(s):
++ *    Mike Erwin
   *
   * ***** END GPL LICENSE BLOCK *****
   */
  
- /** \file ghost/intern/GHOST_NDOFManager.cpp
-  *  \ingroup GHOST
-  */
+ #include "GHOST_NDOFManager.h"
+ #include "GHOST_EventNDOF.h"
+ #include "GHOST_WindowManager.h"
+ #include <string.h> // for memory functions
+ #include <stdio.h> // for debug tracing
+ GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System& sys)
+       : m_system(sys)
+       , m_buttons(0)
+       , m_motionTime(1000) // one full second (operators should filter out such large time deltas)
+       , m_prevMotionTime(0)
+       , m_atRest(true)
+       {
+       // to avoid the rare situation where one triple is updated and
+       // the other is not, initialize them both here:
+       memset(m_translation, 0, sizeof(m_translation));
+       memset(m_rotation, 0, sizeof(m_rotation));
+       }
+ void GHOST_NDOFManager::updateTranslation(short t[3], GHOST_TUns64 time)
+       {
+       memcpy(m_translation, t, sizeof(m_translation));
+       m_motionTime = time;
+       m_atRest = false;
+       }
+ void GHOST_NDOFManager::updateRotation(short r[3], GHOST_TUns64 time)
+       {
+       memcpy(m_rotation, r, sizeof(m_rotation));
+       m_motionTime = time;
+       m_atRest = false;
+       }
+ void GHOST_NDOFManager::updateButtons(unsigned short buttons, GHOST_TUns64 time)
+       {
+       GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow();
+       unsigned short diff = m_buttons ^ buttons;
+       for (int i = 0; i < 16; ++i)
+               {
+               unsigned short mask = 1 << i;
+               if (diff & mask)
+                       {
+                       GHOST_EventNDOFButton* event = new GHOST_EventNDOFButton(time, window);
+                       GHOST_TEventNDOFButtonData* data = (GHOST_TEventNDOFButtonData*) event->getData();
+                       
+                       data->action = (buttons & mask) ? GHOST_kPress : GHOST_kRelease;
+                       data->button = i + 1;
  
+                       // printf("sending button %d %s\n", data->button, (data->action == GHOST_kPress) ? "pressed" : "released");
  
- #include <stdio.h> /* just for printf */
+                       m_system.pushEvent(event);
+                       }
+               }
  
- #include "GHOST_NDOFManager.h"
+       m_buttons = buttons;
+       }
  
+ bool GHOST_NDOFManager::sendMotionEvent()
+       {
+       if (m_atRest)
+               return false;
  
- // the variable is outside the class because it must be accessed from plugin
- static volatile GHOST_TEventNDOFData currentNdofValues = {0,0,0,0,0,0,0,0,0,0,0};
- #if !defined(_WIN32) && !defined(__APPLE__)
- #include "GHOST_SystemX11.h"
- #endif
- namespace
- {
-     GHOST_NDOFLibraryInit_fp ndofLibraryInit = 0;
-     GHOST_NDOFLibraryShutdown_fp ndofLibraryShutdown = 0;
-     GHOST_NDOFDeviceOpen_fp ndofDeviceOpen = 0;
- }
- GHOST_NDOFManager::GHOST_NDOFManager()
- {
-     m_DeviceHandle = 0;
-     // discover the API from the plugin
-     ndofLibraryInit = 0;
-     ndofLibraryShutdown = 0;
-     ndofDeviceOpen = 0;
- }
- GHOST_NDOFManager::~GHOST_NDOFManager()
- {
-     if (ndofLibraryShutdown)
-         ndofLibraryShutdown(m_DeviceHandle);
-     m_DeviceHandle = 0;
- }
- int
- GHOST_NDOFManager::deviceOpen(GHOST_IWindow* window,
-         GHOST_NDOFLibraryInit_fp setNdofLibraryInit, 
-         GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
-         GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen)
- {
-       int Pid;
-       
-     ndofLibraryInit = setNdofLibraryInit;
-     ndofLibraryShutdown = setNdofLibraryShutdown;
-     ndofDeviceOpen = setNdofDeviceOpen;
-     if (ndofLibraryInit  && ndofDeviceOpen)
-     {
-       Pid= ndofLibraryInit();
- #if 0
-               printf("%i client \n", Pid);
- #endif
-               #if defined(WITH_HEADLESS)
-                       /* do nothing */
-               #elif defined(_WIN32) || defined(__APPLE__)
-                       m_DeviceHandle = ndofDeviceOpen((void *)&currentNdofValues);    
-               #else
-                       GHOST_SystemX11 *sys;
-                       sys = static_cast<GHOST_SystemX11*>(GHOST_ISystem::getSystem());
-                       void *ndofInfo = sys->prepareNdofInfo(&currentNdofValues);
-                       m_DeviceHandle = ndofDeviceOpen(ndofInfo);
-               #endif
-                return (Pid > 0) ? 0 : 1;
-                       
-       } else
-               return 1;
- }
- bool 
- GHOST_NDOFManager::available() const
- { 
-     return m_DeviceHandle != 0; 
- }
- bool 
- GHOST_NDOFManager::event_present() const
- { 
-     if( currentNdofValues.changed >0) {
-               printf("time %llu but%u x%i y%i z%i rx%i ry%i rz%i \n"  ,                       
-                               currentNdofValues.time,         currentNdofValues.buttons,
-                               currentNdofValues.tx,currentNdofValues.ty,currentNdofValues.tz,
-                               currentNdofValues.rx,currentNdofValues.ry,currentNdofValues.rz);
-       return true;
-       }else
-       return false;
- }
- void        GHOST_NDOFManager::GHOST_NDOFGetDatas(GHOST_TEventNDOFData &datas) const
- {
-       datas.tx = currentNdofValues.tx;
-       datas.ty = currentNdofValues.ty;
-       datas.tz = currentNdofValues.tz;
-       datas.rx = currentNdofValues.rx;
-       datas.ry = currentNdofValues.ry;
-       datas.rz = currentNdofValues.rz;
-       datas.buttons = currentNdofValues.buttons;
-       datas.client = currentNdofValues.client;
-       datas.address = currentNdofValues.address;
-       datas.time = currentNdofValues.time;
-       datas.delta = currentNdofValues.delta;
- }
+       GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow();
+       GHOST_EventNDOFMotion* event = new GHOST_EventNDOFMotion(m_motionTime, window);
+       GHOST_TEventNDOFMotionData* data = (GHOST_TEventNDOFMotionData*) event->getData();
+       const float scale = 1.f / 350.f; // SpaceNavigator sends +/- 350 usually
+       // 350 according to their developer's guide; others recommend 500 as comfortable
+       // possible future enhancement
+       // scale *= m_sensitivity;
+       data->tx = -scale * m_translation[0];
+       data->ty = scale * m_translation[1];
+       data->tz = scale * m_translation[2];
+       data->rx = scale * m_rotation[0];
+       data->ry = scale * m_rotation[1];
+       data->rz = scale * m_rotation[2];
+       data->dt = 0.001f * (m_motionTime - m_prevMotionTime); // in seconds
+       m_prevMotionTime = m_motionTime;
 -//    printf("sending T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f) dt=%.3f\n",
 -//            data->tx, data->ty, data->tz, data->rx, data->ry, data->rz, data->dt);
++      // printf("sending T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f) dt=%.3f\n",
++      //      data->tx, data->ty, data->tz, data->rx, data->ry, data->rz, data->dt);
+       m_system.pushEvent(event);
+       // 'at rest' test goes at the end so that the first 'rest' event gets sent
+       m_atRest = m_rotation[0] == 0 && m_rotation[1] == 0 && m_rotation[2] == 0 &&
+               m_translation[0] == 0 && m_translation[1] == 0 && m_translation[2] == 0;
+       return true;
+       }
index 0000000,4f2a227..efe840b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,47 +1,48 @@@
 - * Contributor(s): none yet.
+ /*
+  * ***** BEGIN GPL LICENSE BLOCK *****
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * as published by the Free Software Foundation; either version 2
+  * of the License, or (at your option) any later version. 
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software Foundation,
+  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  *
++ * Contributor(s):
++ *   Mike Erwin
+  *
+  * ***** END GPL LICENSE BLOCK *****
+  */
+  
+ #ifndef _GHOST_NDOFMANAGERCOCOA_H_
+ #define _GHOST_NDOFMANAGERCOCOA_H_
+ #include "GHOST_NDOFManager.h"
+ // Event capture is handled within the NDOFManager on Macintosh,
+ // so there's no need for SystemCocoa to look for them.
+ class GHOST_NDOFManagerCocoa : public GHOST_NDOFManager
+ {
+ public:
+       GHOST_NDOFManagerCocoa(GHOST_System&);
+       ~GHOST_NDOFManagerCocoa();
+       // whether multi-axis functionality is available (via the OS or driver)
+       // does not imply that a device is plugged in or being used
+       bool available();
+ private:
+       unsigned short m_clientID;
+ };
+ #endif
index 0000000,b29cbb6..75a9655
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,117 +1,121 @@@
 - * Contributor(s): none yet.
+ /*
+  * ***** BEGIN GPL LICENSE BLOCK *****
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * as published by the Free Software Foundation; either version 2
+  * of the License, or (at your option) any later version. 
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software Foundation,
+  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  *
 -      printf("SpaceNav added\n");
++ * Contributor(s):
++ *   Mike Erwin
+  *
+  * ***** END GPL LICENSE BLOCK *****
+  */
+  
+ #include "GHOST_NDOFManagerCocoa.h"
+ #include "GHOST_SystemCocoa.h"
+ extern "C" {
+       #include <3DconnexionClient/ConnexionClientAPI.h>
+       #include <stdio.h>
+       }
+ static void SpaceNavAdded(io_connect_t connection)
+       {
 -static void SpaceNavEvent(io_connect_t connection, natural_t messageType, void *messageArgument)
++      printf("SpaceNav added\n"); // change these: printf --> informational reports
+       }
+ static void SpaceNavRemoved(io_connect_t connection)
+       {
+       printf("SpaceNav removed\n");
+       }
 -//                                    manager->updateTranslation(s->axis, s->time);
++static void SpaceNavEvent(io_connect_t connection, natural_t messageType, void* messageArgument)
+       {
+       GHOST_SystemCocoa* system = (GHOST_SystemCocoa*) GHOST_ISystem::getSystem();
+       GHOST_NDOFManager* manager = system->getNDOFManager();
+       switch (messageType)
+               {
+               case kConnexionMsgDeviceState:
+                       {
+                       ConnexionDeviceState* s = (ConnexionDeviceState*)messageArgument;
+                       GHOST_TUns64 now = system->getMilliSeconds();
+                       switch (s->command)
+                               {
+                               case kConnexionCmdHandleAxis:
 -              m_clientID = RegisterConnexionClient('blnd', (UInt8*) "\pBlender",
+                                       manager->updateTranslation(s->axis, now);
+                                       manager->updateRotation(s->axis + 3, now);
+                                       system->notifyExternalEventProcessed();
+                                       break;
+                               case kConnexionCmdHandleButtons:
+                                       manager->updateButtons(s->buttons, now);
+                                       system->notifyExternalEventProcessed();
+                                       break;
++
++                              default:
++                                      printf("device state command %d\n", s->command);
+                               }
+                       break;
+                       }
+               case kConnexionMsgPrefsChanged:
+                       printf("prefs changed\n"); // this includes app switches
+                       break;
+               case kConnexionMsgDoAction:
+                       printf("do action\n"); // no idea what this means
+                       // 'calibrate device' in System Prefs sends this
++                      // 3Dx header file says to ignore these
+                       break;
+               default:
+                       printf("<!> mystery event\n");
+               }
+       }
+ GHOST_NDOFManagerCocoa::GHOST_NDOFManagerCocoa(GHOST_System& sys)
+       : GHOST_NDOFManager(sys)
+       {
+       if (available())
+               {
+               OSErr error = InstallConnexionHandlers(SpaceNavEvent, SpaceNavAdded, SpaceNavRemoved);
+               if (error)
+                       {
+                       printf("<!> error = %d\n", error);
+                       return;
+                       }
+               // Pascal string *and* a four-letter constant. How old-skool.
++              m_clientID = RegisterConnexionClient('blnd', (UInt8*) "\pblender",
+                       kConnexionClientModeTakeOver, kConnexionMaskAll);
+               printf("client id = %d\n", m_clientID);
+               }
+       else
+               {
+               printf("<!> SpaceNav driver not found\n");
+               // This isn't a hard error, just means the user doesn't have a SpaceNavigator.
+               }
+       }
+ GHOST_NDOFManagerCocoa::~GHOST_NDOFManagerCocoa()
+       {
+       UnregisterConnexionClient(m_clientID);
+       CleanupConnexionHandlers();
+       }
+ bool GHOST_NDOFManagerCocoa::available()
+       {
+ //    extern OSErr InstallConnexionHandlers() __attribute__((weak_import));
+ // ^-- not needed since the entire framework is weak-linked
+       return InstallConnexionHandlers != NULL;
+       }
index 0000000,a09808d..0570eef
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,46 +1,47 @@@
 - * Contributor(s): none yet.
+ /*
+  * ***** BEGIN GPL LICENSE BLOCK *****
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * as published by the Free Software Foundation; either version 2
+  * of the License, or (at your option) any later version. 
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software Foundation,
+  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  *
++ * Contributor(s):
++ *   Mike Erwin
+  *
+  * ***** END GPL LICENSE BLOCK *****
+  */
+  
+ #ifndef _GHOST_NDOFMANAGERWIN32_H_
+ #define _GHOST_NDOFMANAGERWIN32_H_
+ #include "GHOST_NDOFManager.h"
+ class GHOST_NDOFManagerWin32 : public GHOST_NDOFManager
+ {
+ public:
+       GHOST_NDOFManagerWin32(GHOST_System& sys)
+               : GHOST_NDOFManager(sys)
+               {}
+       // whether multi-axis functionality is available (via the OS or driver)
+       // does not imply that a device is plugged in or being used
+       bool available()
+               {
+               // always available since RawInput is built into Windows
+               return true;
+               }
+ };
+ #endif
@@@ -194,12 -195,16 +194,15 @@@ bool GHOST_System::getFullScreen(void
  
  bool GHOST_System::dispatchEvents()
  {
-       bool handled;
-       if (m_eventManager) {
-               handled = m_eventManager->dispatchEvents();
+       bool handled = false;
+       // NDOF Motion event is sent only once per dispatch, so do it now:
 -      handled |= m_ndofManager->sendMotionEvent();
++      if (m_ndofManager) {
++              handled |= m_ndofManager->sendMotionEvent();
 +      }
-       else {
-               handled = false;
+       if (m_eventManager) {
 -              if (m_input_fidelity_hint == LO_FI)
 -                      handled |= m_eventManager->dispatchEvents_lo_fi();
 -              else
 -                      handled |= m_eventManager->dispatchEvents();
++              handled |= m_eventManager->dispatchEvents();
        }
  
        m_timerManager->fireTimers(getMilliSeconds());
@@@ -243,18 -237,7 +246,6 @@@ GHOST_TSuccess GHOST_System::pushEvent(
        return success;
  }
  
- int GHOST_System::openNDOF(GHOST_IWindow* w,
-               GHOST_NDOFLibraryInit_fp setNdofLibraryInit, 
-               GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
-               GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen)
- {
-  return m_ndofManager->deviceOpen(w,
-               setNdofLibraryInit, 
-               setNdofLibraryShutdown,
-               setNdofDeviceOpen);
- }
--
  GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKeyMask mask, bool& isDown) const
  {
        GHOST_ModifierKeys keys;
@@@ -285,13 -273,7 +276,7 @@@ GHOST_TSuccess GHOST_System::init(
        m_timerManager = new GHOST_TimerManager ();
        m_windowManager = new GHOST_WindowManager ();
        m_eventManager = new GHOST_EventManager ();
-       m_ndofManager = new GHOST_NDOFManager();
--
- #if 0
-       if(m_ndofManager)
-               printf("ndof manager \n");
- #endif
 +      
  #ifdef GHOST_DEBUG
        if (m_eventManager) {
                m_eventPrinter = new GHOST_EventPrinter();
@@@ -183,32 -183,7 +183,13 @@@ public
         */
        virtual GHOST_TSuccess addEventConsumer(GHOST_IEventConsumer* consumer);
  
 +      /**
 +       * Remove the given event consumer to our list.
 +       * @param consumer The event consumer to remove.
 +       * @return Indication of success.
 +       */
 +      virtual GHOST_TSuccess removeEventConsumer(GHOST_IEventConsumer* consumer);
  
-       /***************************************************************************************
-        ** N-degree of freedom devcice management functionality
-        ***************************************************************************************/
-       /** Inherited from GHOST_ISystem
-      *  Opens the N-degree of freedom device manager
-        * return 0 if device found, 1 otherwise
-      */
-     virtual int openNDOF(GHOST_IWindow* w,        
-         GHOST_NDOFLibraryInit_fp setNdofLibraryInit, 
-         GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
-         GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen);
-         
- // original patch only        
- //        GHOST_NDOFEventHandler_fp setNdofEventHandler);
        /***************************************************************************************
         ** Cursor management functionality
         ***************************************************************************************/
@@@ -202,38 -198,57 +202,43 @@@ public
  
        /**
         * Returns Clipboard data
 -       * @param selection  Indicate which buffer to return
 -       * @return           Returns the selected buffer
 +       * @param selection             Indicate which buffer to return
 +       * @return                              Returns the selected buffer
         */
 -      GHOST_TUns8* getClipboard(bool selection) const;
 -
 +      virtual GHOST_TUns8* getClipboard(bool selection) const;
 +      
        /**
         * Puts buffer to system clipboard
 -       * @param buffer     The buffer to be copied
 -       * @param selection  Indicates which buffer to copy too, only used on X11
 -       */
 -      void putClipboard(GHOST_TInt8 *buffer, bool selection) const;
 -
 -      /**
 -       * Determine the base dir in which shared resources are located. It will first try to use
 -       * "unpack and run" path, then look for properly installed path, not including versioning.
 -       * @return Unsigned char string pointing to system dir (eg /usr/share/blender/).
 +       * @param buffer        The buffer to be copied
 +       * @param selection     Indicates which buffer to copy too, only used on X11
         */
 -      const GHOST_TUns8* getSystemDir() const;
 +      virtual void putClipboard(GHOST_TInt8 *buffer, bool selection) const;
  
        /**
 -       * Determine the base dir in which user configuration is stored, not including versioning.
 -       * If needed, it will create the base directory.
 -       * @return Unsigned char string pointing to user dir (eg ~/.blender/).
 -       */
 -      const GHOST_TUns8* getUserDir() const;
 -
 -      /**
 -       * Determine the directory of the current binary
 -       * @return Unsigned char string pointing to the binary dir
 -       */
 -      const GHOST_TUns8* getBinaryDir() const;
 -
 -      /**
 -       * Handles a window event. Called by GHOST_WindowCocoa window delegate
 -       * @param eventType  The type of window event
 -       * @param window     The window on which the event occurred
 -       * @return           Indication whether the event was handled.
 -       */
 -      GHOST_TSuccess handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window);
 -
 +     * Handles a window event. Called by GHOST_WindowCocoa window delegate
 +     * @param eventType The type of window event
 +       * @param window The window on which the event occurred
 +     * @return Indication whether the event was handled. 
 +     */
 +    GHOST_TSuccess handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window);
 +      
        /**
 -       * Handles the Cocoa event telling the application has become active (again)
 -       * @return  Indication whether the event was handled. 
 -       */
 -      GHOST_TSuccess handleApplicationBecomeActiveEvent();
 +     * Handles the Cocoa event telling the application has become active (again)
 +     * @return Indication whether the event was handled. 
 +     */
 +    GHOST_TSuccess handleApplicationBecomeActiveEvent();
  
+       /**
+        * External objects should call this when they send an event outside processEvents.
+        */
+       void notifyExternalEventProcessed();
 +      /**
 +       * @see GHOST_ISystem
 +       */
 +      int toggleConsole(int action) { return 0; }
 +      
 +      
  protected:
        /**
         * Initializes the system.
  
        /** Start time at initialization. */
        GHOST_TUns64 m_start_time;
 -      double m_start_time_2;
 -
 +      
-       /** Event has been processed directly by Cocoa and has sent a ghost event to be dispatched */
+       /** Event has been processed directly by Cocoa (or NDOF manager) and has sent a ghost event to be dispatched */
        bool m_outsideLoopEventProcessed;
 -
 +      
        /** Raised window is not yet known by the window manager, so delay application become active event handling */
        bool m_needDelayedApplicationBecomeActiveEventProcessing;
 -
 +      
        /** Mouse buttons state */
        GHOST_TUns32 m_pressedMouseButtons;
 +      
 +    /** State of the modifiers. */
 +    GHOST_TUns32 m_modifierMask;
  
 -      /** State of the modifiers. */
 -      GHOST_TUns32 m_modifierMask;
 -
 -      /** Ignores window size messages (when window is dragged). */
 -      bool m_ignoreWindowSizedMessages;   
 -
 +    /** Ignores window size messages (when window is dragged). */
 +    bool m_ignoreWindowSizedMessages;   
 +      
        /** Stores the mouse cursor delta due to setting a new cursor position
         * Needed because cocoa event delta cursor move takes setCursorPosition changes too.
         */
@@@ -593,21 -592,14 +593,24 @@@ GHOST_SystemCocoa::~GHOST_SystemCocoa(
  
  GHOST_TSuccess GHOST_SystemCocoa::init()
  {
 -      GHOST_TSuccess success = GHOST_System::init();
 -      if (success) {
 +      
 +    GHOST_TSuccess success = GHOST_System::init();
 +    if (success) {
+               m_ndofManager = new GHOST_NDOFManagerCocoa(*this);
++
 +              //ProcessSerialNumber psn;
                
 +              //Carbon stuff to move window & menu to foreground
 +              /*if (!GetCurrentProcess(&psn)) {
 +                      TransformProcessType(&psn, kProcessTransformToForegroundApplication);
 +                      SetFrontProcess(&psn);
 +              }*/
 +              
 +              NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                if (NSApp == nil) {
                        [NSApplication sharedApplication];
 -
 +                      
                        if ([NSApp mainMenu] == nil) {
                                NSMenu *mainMenubar = [[NSMenu alloc] init];
                                NSMenuItem *menuItem;
@@@ -929,6 -813,256 +929,257 @@@ void VIEW3D_OT_rotate(wmOperatorType *o
        ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
  }
  
 -#if 0 // my version
+ // returns angular velocity (0..1), fills axis of rotation
+ // (shouldn't live in this file!)
+ float ndof_to_angle_axis(const float ndof[3], float axis[3])
+       {
+       const float x = ndof[0];
+       const float y = ndof[1];
+       const float z = ndof[2];
+       float angular_velocity = sqrtf(x*x + y*y + z*z);
+       float scale = 1.f / angular_velocity;
+       // normalize 
+       axis[0] = scale * x;
+       axis[1] = scale * y;
+       axis[2] = scale * z;
+       return angular_velocity;
+       }
 -#endif
++// Mike's version
+ static int viewndof_invoke(bContext *C, wmOperator *op, wmEvent *event)
+ {
+       wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
+       float dt = ndof->dt;
+       RegionView3D* rv3d = CTX_wm_region_view3d(C);
+       if (dt > 0.25f)
+               /* this is probably the first event for this motion, so set dt to something reasonable */
+               dt = 0.0125f;
+       /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */
+       float phi, q1[4];
+       float m[3][3];
+       float m_inv[3][3];
+       float xvec[3] = {1,0,0};
+       const float sensitivity = 0.035;
+       /* Get the 3x3 matrix and its inverse from the quaternion */
+       quat_to_mat3(m,rv3d->viewquat);
+       invert_m3_m3(m_inv,m);
+       /* Determine the direction of the x vector (for rotating up and down) */
+       /* This can likely be computed directly from the quaternion. */
+       mul_m3_v3(m_inv,xvec);
+       /* Perform the up/down rotation */
+       phi = sensitivity * -ndof->rx;
+       q1[0] = cos(phi);
+       mul_v3_v3fl(q1+1, xvec, sin(phi));
+       mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
+       /* Perform the orbital rotation */
+       phi = sensitivity * ndof->rz;
+       q1[0] = cos(phi);
+       q1[1] = q1[2] = 0.0;
+       q1[3] = sin(phi);
+       mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
+       ED_region_tag_redraw(CTX_wm_region(C));
+       return OPERATOR_FINISHED;
+       }
+ // Tom's version
++#if 0 
+ static int viewndof_invoke(bContext *C, wmOperator *op, wmEvent *event)
+ {
+       wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
+       
+       float phi, q1[4];
+       float m[3][3];
+       float m_inv[3][3];
+       float xvec[3] = {1,0,0};
+       float yvec[3] = {0,1,0};
+       float vec[3];
+       float mat[3][3];
+       const float rotaSensitivity = 0.007;
+       const float tranSensitivity = 0.120;
+       
+       ARegion *ar= CTX_wm_region(C);
+       RegionView3D *rv3d = CTX_wm_region_view3d(C);
+       View3D *v3d = CTX_wm_view3d(C);
+       
+       float dt = ndof->dt;
+       
+       if (dt > 0.25f) {
+               /* this is probably the first event for this motion, so set dt to something reasonable */
+               dt = 0.0125f;
+       }
+       /* Get the 3x3 matrix and its inverse from the quaternion */
+       quat_to_mat3(m,rv3d->viewquat);
+       invert_m3_m3(m_inv,m);
+       
+       /* Determine the direction of the x vector (for rotating up and down) */
+       /* This can likely be computed directly from the quaternion. */
+       mul_m3_v3(m_inv,xvec);
+       
+       //if(rv3d->persp=!= RV3D_PERSP)  //Camera control not supported yet
+       /* Lock fixed views out of using rotation controls */
+       if(rv3d->view!=RV3D_VIEW_FRONT && rv3d->view!=RV3D_VIEW_BACK)
+               if(rv3d->view!=RV3D_VIEW_TOP && rv3d->view!=RV3D_VIEW_BOTTOM)
+                       if(rv3d->view!=RV3D_VIEW_RIGHT && rv3d->view!=RV3D_VIEW_LEFT) {
+                               // Perform the up/down rotation                                 
+                               phi = (rotaSensitivity+dt) * -ndof->rx;
+                               q1[0] = cos(phi);
+                               mul_v3_v3fl(q1+1, xvec, sin(phi));
+                               mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
+                               // Perform the left/right rotation 
+                               mul_m3_v3(m_inv,yvec);
+                               phi = (rotaSensitivity+dt) * ndof->ry;
+                               q1[0] = cos(phi);
+                               mul_v3_v3fl(q1+1, yvec, sin(phi));
+                               mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
+                               // Perform the orbital rotation
+                               phi = (rotaSensitivity+dt) * ndof->rz;
+                               q1[0] = cos(phi);
+                               q1[1] = q1[2] = 0.0;
+                               q1[3] = sin(phi);
+                               mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
+                       }
+       // Perform Pan translation      
+       vec[0]= (tranSensitivity+dt) * ndof->tx;
+       vec[1]= (tranSensitivity+dt) * ndof->tz;
+       //vec[2]= 0.0f;//tranSensitivity * ndof->ty;
+       //window_to_3d_delta(ar, vec, -ndof->tx, -ndof->tz); // experimented a little instead of above 
+       copy_m3_m4(mat, rv3d->viewinv);
+       mat[2][2] = 0.0f;
+       mul_m3_v3(mat, vec);
+       // translate the view
+       add_v3_v3(rv3d->ofs, vec);
+       // Perform Zoom translation     
+       if (ndof->ty!=0.0f){ // TODO - need to add limits to prevent flipping past gridlines 
+               rv3d->dist += (tranSensitivity+dt)* ndof->ty;
+               // printf("dist %5.3f view %d grid %f\n",rv3d->dist,rv3d->view,v3d->grid);
+       }
+       //printf("Trans tx:%5.2f ty:%5.2f tz:%5.2f \n",ndof->tx, ndof->ty, ndof->tz);
+       ED_region_tag_redraw(ar);
+       
+       return OPERATOR_FINISHED;
+ }
++#endif
+ #if 0
+ static int viewndof_invoke_1st_try(bContext *C, wmOperator *op, wmEvent *event)
+ {
+       wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
+       float dt = ndof->dt;
+       RegionView3D *rv3d= CTX_wm_region_view3d(C);
+       if (dt > 0.25f)
+               /* this is probably the first event for this motion, so set dt to something reasonable */
+               dt = 0.0125f;
+       /* very simple for now, move viewpoint along world axes */
+       rv3d->ofs[0] += dt * ndof->tx;
+       rv3d->ofs[1] += dt * ndof->ty;
+       rv3d->ofs[2] += dt * ndof->tz;
+ //    request_depth_update(CTX_wm_region_view3d(C)); /* need this? */
+       ED_region_tag_redraw(CTX_wm_region(C));
+       return OPERATOR_FINISHED;
+ }
+ static int viewndof_invoke_2nd_try(bContext *C, wmOperator *op, wmEvent *event)
+ {
+       wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
+       float dt = ndof->dt;
+       RegionView3D* rv3d = CTX_wm_region_view3d(C);
+       if (dt > 0.25f)
+               /* this is probably the first event for this motion, so set dt to something reasonable */
+               dt = 0.0125f;
+       float axis[3];
+       float angle = ndof_to_angle_axis(&(ndof->rx), axis);
+       float eyeball_q[4];// = {0.f};
+ //    float* eyeball_v = eyeball_q + 1;
+       axis_angle_to_quat(eyeball_q, axis, angle);
+       float eye_conj[4];
+       copy_qt_qt(eye_conj, eyeball_q);
+       conjugate_qt(eye_conj);
+ //    float mat[3][3];
+ //    quat_to_mat3(mat, rv3d->viewquat);
+ /*
+       eyeball_v[0] = dt * ndof->tx;
+       eyeball_v[1] = dt * ndof->ty;
+       eyeball_v[2] = dt * ndof->tz;
+ */
+ //    mul_m3_v3(mat, eyeball_vector);
+ //    mul_qt_v3(rv3d->viewquat, eyeball_vector);
+       // doesn't this transform v?
+       // v' = (q)(v)(~q)
+       float view_q[4];
+       copy_qt_qt(view_q, rv3d->viewquat);
+ //    float q_conj[4];
+ //    copy_qt_qt(q_conj, q);
+ //    conjugate_qt(q_conj);
+       mul_qt_qtqt(view_q, eyeball_q, view_q);
+       mul_qt_qtqt(view_q, view_q, eye_conj);
+ //    mul_qt_qtqt(eyeball_q, q, eyeball_q);
+ //    mul_qt_qtqt(eyeball_q, eyeball_q, q_conj);
+ //    add_v3_v3(rv3d->ofs, eyeball_v);
+       copy_qt_qt(rv3d->viewquat, view_q);
+       ED_region_tag_redraw(CTX_wm_region(C));
+       return OPERATOR_FINISHED;
+ }
+ #endif
+ void VIEW3D_OT_ndof(struct wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Navigate view";
+       ot->description = "Navigate the view using a 3D mouse.";
+       ot->idname = "VIEW3D_OT_ndof";
+       /* api callbacks */
+       ot->invoke = viewndof_invoke;
+       ot->poll = ED_operator_view3d_active;
+       /* flags */
+       ot->flag = 0;
+ }
  /* ************************ viewmove ******************************** */
  
  
@@@ -68,10 -68,10 +68,11 @@@ void view3d_operatortypes(void)
  
  /* view3d_edit.c */
  void VIEW3D_OT_zoom(struct wmOperatorType *ot);
 +void VIEW3D_OT_dolly(struct wmOperatorType *ot);
 +void VIEW3D_OT_zoom_camera_1_to_1(struct wmOperatorType *ot);
  void VIEW3D_OT_move(struct wmOperatorType *ot);
  void VIEW3D_OT_rotate(struct wmOperatorType *ot);
+ void VIEW3D_OT_ndof(struct wmOperatorType *ot);
  void VIEW3D_OT_view_all(struct wmOperatorType *ot);
  void VIEW3D_OT_viewnumpad(struct wmOperatorType *ot);
  void VIEW3D_OT_view_selected(struct wmOperatorType *ot);
@@@ -62,8 -62,7 +62,9 @@@ void view3d_operatortypes(void
        WM_operatortype_append(VIEW3D_OT_rotate);
        WM_operatortype_append(VIEW3D_OT_move);
        WM_operatortype_append(VIEW3D_OT_zoom);
 +      WM_operatortype_append(VIEW3D_OT_zoom_camera_1_to_1);
 +      WM_operatortype_append(VIEW3D_OT_dolly);
+       WM_operatortype_append(VIEW3D_OT_ndof);
        WM_operatortype_append(VIEW3D_OT_view_all);
        WM_operatortype_append(VIEW3D_OT_viewnumpad);
        WM_operatortype_append(VIEW3D_OT_view_orbit);
@@@ -377,6 -354,6 +377,19 @@@ typedef struct wmTabletData 
        float Ytilt;            /* as above */
  } wmTabletData;
  
++typedef struct {
++      /* awfully similar to GHOST_TEventNDOFMotionData... */
++
++   /* Each component normally ranges from -1 to +1, but can exceed that. */
++
++   float tx, ty, tz; /* translation: -x left, +y forward, -z up */
++   float rx, ry, rz; /* rotation:
++      axis = (rx,ry,rz).normalized
++      amount = (rx,ry,rz).magnitude [in revolutions, 1.0 = 360 deg] */
++   
++   float dt; // time since previous NDOF Motion event (or zero if this is the first)
++} wmNDOFMotionData;
++
  typedef struct wmTimer {
        struct wmTimer *next, *prev;
        
@@@ -2290,25 -2063,45 +2290,45 @@@ static int convert_key(GHOST_TKey key
  }
  
  /* adds customdata to event */
 -static void attach_tablet_data(wmEvent* event, const GHOST_TabletData* ghost)
 +static void update_tablet_data(wmWindow *win, wmEvent *event)
  {
 -      if (ghost->Active != GHOST_kTabletModeNone)
 -              {
 -              wmTabletData* data = MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
 -      
 -              data->Active = ghost->Active;
 -              data->Pressure = ghost->Pressure;
 -              data->Xtilt = ghost->Xtilt;
 -              data->Ytilt = ghost->Ytilt;
 +      const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
        
 -              event->custom = EVT_DATA_TABLET;
 -              event->customdata = data;
 -              event->customdatafree = 1;
 -
 -              // printf("+ pressure = %.2f   tilt = %.2f %.2f\n", data->Pressure, data->Xtilt, data->Ytilt);
 -              }
 +      /* if there's tablet data from an active tablet device then add it */
 +      if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
 +              struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
 +              
 +              wmtab->Active = (int)td->Active;
 +              wmtab->Pressure = td->Pressure;
 +              wmtab->Xtilt = td->Xtilt;
 +              wmtab->Ytilt = td->Ytilt;
 +              
 +              event->custom= EVT_DATA_TABLET;
 +              event->customdata= wmtab;
 +              event->customdatafree= 1;
 +      } 
  }
  
+ /* adds customdata to event */
+ static void attach_ndof_data(wmEvent* event, const GHOST_TEventNDOFMotionData* ghost)
+ {
+       wmNDOFMotionData* data = MEM_mallocN(sizeof(wmNDOFMotionData), "customdata NDOF");
+       data->tx = ghost->tx;
+       data->ty = ghost->ty;
+       data->tz = ghost->tz;
+       data->rx = ghost->rx;
+       data->ry = ghost->ry;
+       data->rz = ghost->rz;
+       data->dt = ghost->dt;
+       event->custom = EVT_DATA_NDOF_MOTION;
+       event->customdata = data;
+       event->customdatafree = 1;
+ }
  /* imperfect but probably usable... draw/enable drags to other windows */
  static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *evt)
  {
  #define WM_EVENT_TYPES_H
  
  /* customdata type */
 -#define EVT_DATA_TABLET      1
 -#define EVT_DATA_GESTURE     2
 -#define EVT_DATA_TIMER       3
 -#define EVT_DATA_LISTBASE    4
 +#define EVT_DATA_TABLET               1
 +#define EVT_DATA_GESTURE      2
 +#define EVT_DATA_TIMER                3
 +#define EVT_DATA_LISTBASE     4
+ #define EVT_DATA_NDOF_MOTION 5
  
  /* tablet active, matches GHOST_TTabletMode */
 -/* [mce] also matches my own TabletTool.type */
  #define EVT_TABLET_NONE               0
  #define EVT_TABLET_STYLUS     1
  #define EVT_TABLET_ERASER     2
  
 -/* [mce] what are these for? */
 -#define MOUSEX                0x004
 -#define MOUSEY                0x005
 +#define MOUSEX                4
 +#define MOUSEY                5
  
  /* MOUSE : 0x00x */
 -#define LEFTMOUSE             0x001
 -#define MIDDLEMOUSE           0x002
 -#define RIGHTMOUSE            0x003
 -#define MOUSEMOVE             0x004
 +#define LEFTMOUSE             1
 +#define MIDDLEMOUSE           2
 +#define RIGHTMOUSE            3
 +#define MOUSEMOVE             4
                /* only use if you want user option switch possible */
 -#define ACTIONMOUSE           0x005
 -#define SELECTMOUSE           0x006
 +#define ACTIONMOUSE           5
 +#define SELECTMOUSE           6
                /* Extra mouse buttons */
 -#define BUTTON4MOUSE  0x007
 -#define BUTTON5MOUSE  0x008
 +#define BUTTON4MOUSE  7
 +#define BUTTON5MOUSE  8
                /* Extra trackpad gestures */
 -#define MOUSEPAN              0x00e
 -#define MOUSEZOOM             0x00f
 -#define MOUSEROTATE           0x010
 +#define MOUSEPAN              14
 +#define MOUSEZOOM             15
 +#define MOUSEROTATE           16
                /* defaults from ghost */
 -#define WHEELUPMOUSE  0x00a
 -#define WHEELDOWNMOUSE        0x00b
 +#define WHEELUPMOUSE  10
 +#define WHEELDOWNMOUSE        11
                /* mapped with userdef */
 -#define WHEELINMOUSE  0x00c
 -#define WHEELOUTMOUSE 0x00d
 -#define INBETWEEN_MOUSEMOVE   0x011
 +#define WHEELINMOUSE  12
 +#define WHEELOUTMOUSE 13
 +#define INBETWEEN_MOUSEMOVE   17
  
 -      NDOF_BUTTON_NONE = NDOF_MOTION, /* never sent, used during translation */
+ /* NDOF (from SpaceNavigator & friends) */
+ #define NDOF_MOTION 0x12
+ enum {
 -      NDOF_BUTTON2/*,
 -      NDOF_BUTTON3,
++      NDOF_BUTTON_NONE = NDOF_MOTION, /* never sent, used internally */
+       NDOF_BUTTON1,
++      NDOF_BUTTON2/*, the following buttons will be supported soon...
++      NDOF_BUTTON3,   and possibly get meaningful names
+       NDOF_BUTTON4,
+       NDOF_BUTTON5,
+       NDOF_BUTTON6,
+       NDOF_BUTTON7,
+       NDOF_BUTTON8,
+       NDOF_BUTTON9,
+       NDOF_BUTTON10,
+       NDOF_BUTTON11,
+       NDOF_BUTTON12,
+       NDOF_BUTTON13,
+       NDOF_BUTTON14,
+       NDOF_BUTTON15,
+       NDOF_BUTTON16*/
+       };
 +
  /* SYSTEM : 0x01xx */
  #define       INPUTCHANGE             0x0103  /* input connected or disconnected */
  #define WINDEACTIVATE 0x0104  /* window is deactivated, focus lost */