svn merge -r38814:38905 https://svn.blender.org/svnroot/bf-blender/trunk/blender . merwin-spacenav
authorMike Erwin <significant.bit@gmail.com>
Tue, 2 Aug 2011 01:01:56 +0000 (01:01 +0000)
committerMike Erwin <significant.bit@gmail.com>
Tue, 2 Aug 2011 01:01:56 +0000 (01:01 +0000)
62 files changed:
CMakeLists.txt
build_files/cmake/macros.cmake
build_files/scons/config/darwin-config.py
build_files/scons/config/linux2-config.py
intern/ghost/CMakeLists.txt
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_DisplayManagerWin32.cpp
intern/ghost/intern/GHOST_DropTargetWin32.cpp
intern/ghost/intern/GHOST_DropTargetWin32.h
intern/ghost/intern/GHOST_EventManager.cpp
intern/ghost/intern/GHOST_EventNDOF.h
intern/ghost/intern/GHOST_NDOFManager.cpp
intern/ghost/intern/GHOST_NDOFManager.h
intern/ghost/intern/GHOST_NDOFManagerCocoa.h [new file with mode: 0644]
intern/ghost/intern/GHOST_NDOFManagerCocoa.mm [new file with mode: 0644]
intern/ghost/intern/GHOST_NDOFManagerWin32.cpp [new file with mode: 0644]
intern/ghost/intern/GHOST_NDOFManagerWin32.h [new file with mode: 0644]
intern/ghost/intern/GHOST_NDOFManagerX11.cpp [new file with mode: 0644]
intern/ghost/intern/GHOST_NDOFManagerX11.h [new file with mode: 0644]
intern/ghost/intern/GHOST_System.cpp
intern/ghost/intern/GHOST_System.h
intern/ghost/intern/GHOST_SystemCocoa.h
intern/ghost/intern/GHOST_SystemCocoa.mm
intern/ghost/intern/GHOST_SystemPathsWin32.cpp
intern/ghost/intern/GHOST_SystemPathsWin32.h
intern/ghost/intern/GHOST_SystemWin32.cpp
intern/ghost/intern/GHOST_SystemWin32.h
intern/ghost/intern/GHOST_SystemX11.cpp
intern/ghost/intern/GHOST_SystemX11.h
intern/ghost/intern/GHOST_TaskbarWin32.h
intern/ghost/intern/GHOST_WindowWin32.h
release/scripts/startup/bl_ui/space_userpref.py
release/scripts/startup/bl_ui/space_userpref_keymap.py
release/scripts/startup/bl_ui/space_view3d.py
source/blender/blenkernel/BKE_global.h
source/blender/editors/gpencil/gpencil_paint.c
source/blender/editors/interface/resources.c
source/blender/editors/sculpt_paint/paint_stroke.c
source/blender/editors/space_image/image_intern.h
source/blender/editors/space_image/image_ops.c
source/blender/editors/space_image/space_image.c
source/blender/editors/space_view3d/view3d_draw.c
source/blender/editors/space_view3d/view3d_edit.c
source/blender/editors/space_view3d/view3d_fly.c
source/blender/editors/space_view3d/view3d_intern.h
source/blender/editors/space_view3d/view3d_ops.c
source/blender/editors/transform/transform.c
source/blender/editors/transform/transform_ops.c
source/blender/makesdna/DNA_userdef_types.h
source/blender/makesdna/DNA_view3d_types.h
source/blender/makesrna/intern/rna_userdef.c
source/blender/makesrna/intern/rna_wm.c
source/blender/windowmanager/WM_types.h
source/blender/windowmanager/intern/wm_event_system.c
source/blender/windowmanager/intern/wm_init_exit.c
source/blender/windowmanager/intern/wm_operators.c
source/blender/windowmanager/intern/wm_window.c
source/blender/windowmanager/wm_event_types.h

index 455c3070e8f32e0acf571102a8e1b98243889f82..d91d4a1349ddfd39b329ef50cb78a33048e227f3 100644 (file)
@@ -179,6 +179,7 @@ option(WITH_LZO           "Enable fast LZO compression (used for pointcache)" ON
 option(WITH_LZMA          "Enable best LZMA compression, (used for pointcache)" ON)
 
 # Misc
+option(WITH_NDOF "Enable NDOF input devices (SpaceNavigator and friends)" ON)
 option(WITH_RAYOPTIMIZATION    "Enable use of SIMD (SSE) optimizations for the raytracer" ON) 
 if(UNIX AND NOT APPLE)
        option(WITH_INSTALL_PORTABLE "Install redistributeable runtime, otherwise install into CMAKE_INSTALL_PREFIX" ON)
@@ -452,6 +453,15 @@ if(UNIX AND NOT APPLE)
                endif()
        endif()
 
+       if (WITH_NDOF)
+               if(CMAKE_SYSTEM_NAME MATCHES "Linux")
+                       set(NDOF /usr)
+                       set(NDOF_INC ${NDOF}/include)
+                       set(NDOF_LIBRARY spnav)
+                       set(NDOF_LIBPATH ${NDOF}/lib)
+               endif()
+       endif()
+
        # OpenSuse needs lutil, ArchLinux not, for now keep, can avoid by using --as-needed
        set(PLATFORM_LINKLIBS "-lutil -lc -lm -lpthread -lstdc++")
 
@@ -1029,6 +1039,11 @@ elseif(APPLE)
                set(TIFF_LIBPATH ${TIFF}/lib)
        endif()
 
+       if (WITH_NDOF)
+               # GHOST_NDOFManagerCocoa.mm needs "-fpascal-strings"
+               # linker needs "-weak_framework 3DconnexionClient"
+       endif()
+
        set(EXETYPE MACOSX_BUNDLE)
 
        set(CMAKE_C_FLAGS_DEBUG "-fno-strict-aliasing -g")
index 34301458a0654361a962b22e91e4e07f3a9a8263..3de9db6dac9cb3a706c34d30f6ebd06507272bb1 100644 (file)
@@ -193,6 +193,9 @@ macro(SETUP_LIBDIRS)
        if(WITH_MEM_JEMALLOC)
                link_directories(${JEMALLOC_LIBPATH})
        endif()
+       if(WITH_NDOF)
+               link_directories(${NDOF_LIBPATH})
+       endif()
 
        if(WIN32 AND NOT UNIX)
                link_directories(${PTHREADS_LIBPATH})
@@ -314,6 +317,10 @@ macro(setup_liblinks
        if(WITH_MEM_JEMALLOC)
                target_link_libraries(${target} ${JEMALLOC_LIBRARIES})
        endif()
+       if(WITH_NDOF)
+               target_link_libraries(${target} ${NDOF_LIBRARY})
+       endif()
+
        if(WIN32 AND NOT UNIX)
                target_link_libraries(${target} ${PTHREADS_LIBRARIES})
        endif()
index 0c51476a6d0913ece91b7618198dcd722fdcbbb5..ca99357330f15271852fd28e6b5c71ad7770df5a 100644 (file)
@@ -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 +296,9 @@ if WITH_BF_QUICKTIME == True:
        else:
                PLATFORM_LINKFLAGS = PLATFORM_LINKFLAGS+['-framework','QuickTime']
 
+if WITH_BF_SPACENAV:
+       PLATFORM_LINKFLAGS = PLATFORM_LINKFLAGS + ['-weak_framework','3DconnexionClient']
+
 #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']
 
index 328cd4cdb288fd55e3b1bfdd7ec25de63ed09c12..2f7bdace7d37dae6871ff6a353a54dd831120092 100644 (file)
@@ -192,6 +192,10 @@ WITH_BF_OPENMP = True
 WITH_BF_RAYOPTIMIZATION = True
 BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse','-pthread']
 
+#SpaceNavigator and friends
+WITH_BF_NDOF = True
+BF_NDOF_LIB = 'spnav'
+
 ##
 CC = 'gcc'
 CXX = 'g++'
@@ -223,6 +227,8 @@ CXX_WARN = ['-Wno-invalid-offsetof', '-Wno-sign-compare']
 ##FIX_STUBS_WARNINGS = -Wno-unused
 
 LLIBS = ['util', 'c', 'm', 'dl', 'pthread', 'stdc++']
+if WITH_BF_NDOF:
+       LLIBS = LLIBS + [BF_NDOF_LIB];
 ##LOPTS = --dynamic
 ##DYNLDFLAGS = -shared $(LDFLAGS)
 
index ccd763ef42ce74818c70f6950efbfe80e8ac448c..b69dff607e8890eb5222ebd6c6149df0dfa99d05 100644 (file)
@@ -158,11 +158,13 @@ elseif(APPLE)
                        intern/GHOST_SystemCocoa.mm
                        intern/GHOST_SystemPathsCocoa.mm
                        intern/GHOST_WindowCocoa.mm
-                       
+                       intern/GHOST_NDOFManagerCocoa.mm
+
                        intern/GHOST_DisplayManagerCocoa.h
                        intern/GHOST_SystemCocoa.h
                        intern/GHOST_SystemPathsCocoa.h
                        intern/GHOST_WindowCocoa.h
+                       intern/GHOST_NDOFManagerCocoa.h
                )
        else()
                list(APPEND SRC
@@ -197,11 +199,13 @@ elseif(UNIX)
                intern/GHOST_SystemX11.cpp
                intern/GHOST_SystemPathsX11.cpp
                intern/GHOST_WindowX11.cpp
+               intern/GHOST_NDOFManagerX11.cpp
 
                intern/GHOST_DisplayManagerX11.h
                intern/GHOST_SystemX11.h
                intern/GHOST_SystemPathsX11.h
                intern/GHOST_WindowX11.h
+               intern/GHOST_NDOFManagerX11.h
        )
 
        if(NOT WITH_INSTALL_PORTABLE)
@@ -230,6 +234,7 @@ elseif(WIN32)
                intern/GHOST_SystemPathsWin32.cpp
                intern/GHOST_WindowWin32.cpp
                intern/GHOST_DropTargetWin32.cpp
+               intern/GHOST_NDOFManagerWin32.cpp
 
                intern/GHOST_DisplayManagerWin32.h
                intern/GHOST_DropTargetWin32.h
@@ -237,6 +242,7 @@ elseif(WIN32)
                intern/GHOST_SystemPathsWin32.h
                intern/GHOST_WindowWin32.h
                intern/GHOST_TaskbarWin32.h
+               intern/GHOST_NDOFManagerWin32.h
        )
 endif()
 
index 75837239c4ac75fb30152750598a066aa98f4024..a315dfa85e9b6d0828c7da3d7090a78de2ec4867 100644 (file)
@@ -288,21 +288,6 @@ extern GHOST_TSuccess GHOST_SetProgressBar(GHOST_WindowHandle windowhandle, floa
  * @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
index 69e10070be57a87c87fbcc8848b0ae296b9eb465..015ae780bea7e74d70fde9e0e62f50ebedf6ea97 100644 (file)
@@ -298,22 +298,6 @@ public:
         */
        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
         ***************************************************************************************/
index 6a4da5c9d38cef1c2a3570e041c382ec628fcd0e..f24ab00acd35f21d15ab3c3d3b31896fa8fab5b3 100644 (file)
@@ -47,11 +47,6 @@ typedef unsigned short               GHOST_TUns16;
 typedef        int                                     GHOST_TInt32;
 typedef        unsigned int            GHOST_TUns32;
 
-#ifdef WIN32
-#define WM_BLND_NDOF_AXIS      WM_USER + 1
-#define WM_BLND_NDOF_BTN       WM_USER + 2
-#endif
-
 #if defined(WIN32) && !defined(FREE_WINDOWS)
 typedef __int64                                GHOST_TInt64;
 typedef unsigned __int64       GHOST_TUns64;
@@ -440,37 +435,33 @@ typedef struct {
        GHOST_TUns8 **strings;
 } GHOST_TStringArray;
 
+typedef enum {
+       GHOST_kNotStarted,
+       GHOST_kStarting,
+       GHOST_kInProgress,
+       GHOST_kFinishing,
+       GHOST_kFinished
+       } GHOST_TProgress;
 
-/* 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.
+       // These use blender standard view coordinates, with positive rotations being CCW about the axis.
+       float tx, ty, tz; // translation
+       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
+       GHOST_TProgress progress; // Starting, InProgress or Finishing (for modal handlers)
+} GHOST_TEventNDOFMotionData;
+
+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. */
index c65ec58a97b4412eb0fbb78317814317d3e0dacf..3dec748ce31100e574b9d693beb125241c178447 100644 (file)
@@ -11,7 +11,7 @@ if window_system == 'darwin':
     sources += env.Glob('intern/*.mm')
 
 
-pf = ['GHOST_DisplayManager', 'GHOST_System', 'GHOST_SystemPaths', 'GHOST_Window', 'GHOST_DropTarget']
+pf = ['GHOST_DisplayManager', 'GHOST_System', 'GHOST_SystemPaths', 'GHOST_Window', 'GHOST_DropTarget', 'GHOST_NDOFManager']
 defs=['_USE_MATH_DEFINES']
 
 incs = '. ../string #extern/glew/include #source/blender/imbuf #source/blender/makesdna ' + env['BF_OPENGL_INC']
index 7ba8d7db411c3d281a1bb9dce7ea47c8804d1165..6332d72a42f5f23f5dc16a82c7708226558f1b4f 100644 (file)
@@ -275,23 +275,6 @@ GHOST_TSuccess GHOST_EndProgressBar(GHOST_WindowHandle windowhandle)
 }
 
 
-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;
index 30d9aa31207faf3f1c84876b6df809536f80536c..47f748927ab5473958a6310b378c410016afd41a 100644 (file)
 #include "GHOST_DisplayManagerWin32.h"
 #include "GHOST_Debug.h"
 
-// We do not support multiple monitors at the moment
+#define _WIN32_WINNT 0x501 // require Windows XP or newer
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
+
+// We do not support multiple monitors at the moment
 #define COMPILE_MULTIMON_STUBS
 #ifndef FREE_WINDOWS
 #include <multimon.h>
index 2e77da42b310068ab4a88c196e81eb66e8e3f620..99990a46c2a1dd1495e6f41d524009e04da162da 100644 (file)
@@ -33,6 +33,7 @@
  
 #include "GHOST_Debug.h"
 #include "GHOST_DropTargetWin32.h"
+#include <ShellApi.h>
 
 #ifdef GHOST_DEBUG
 // utility
index 0a553b6701eb24265a7842b09ed988a40d51405b..980e9f9fe9b464199018c9c95b22a860137f0a2e 100644 (file)
@@ -33,7 +33,6 @@
 #ifndef _GHOST_DROP_TARGET_WIN32_H_
 #define _GHOST_DROP_TARGET_WIN32_H_
 
-#include <windows.h>
 #include <string.h>
 #include <GHOST_Types.h>
 #include "GHOST_WindowWin32.h"
index 1483555c362fefc4967a8ea8cf6e0ccffcd5a83b..86b8797303857e2ad0625987bd5a5deaf8ae02cd 100644 (file)
@@ -42,7 +42,7 @@
 #include "GHOST_EventManager.h"
 #include <algorithm>
 #include "GHOST_Debug.h"
-
+#include <stdio.h> // [mce] temp debug
 
 GHOST_EventManager::GHOST_EventManager()
 {
index 70861b08fc61931ca444dfb7220777b11ed460c7..394aff0493fc4edc17deeb5abc0d7ee7609df284 100644 (file)
  *
  * ***** END GPL LICENSE BLOCK *****
  */
-
-/** \file ghost/intern/GHOST_EventNDOF.h
- *  \ingroup GHOST
- */
-
  
 
 #ifndef _GHOST_EVENT_NDOF_H_
 
 #include "GHOST_Event.h"
 
-/**
- * N-degree of freedom device event.
- */
-class GHOST_EventNDOF : public GHOST_Event
-{
-public:
-       /**
-        * Constructor.
-        * @param msec          The time this event was generated.
-        * @param type          The type of this event.
-        * @param x                     The x-coordinate of the location the cursor was at at the time of the event.
-        * @param y                     The y-coordinate of the location the cursor was at at the time of the event.
-        */
-       GHOST_EventNDOF(GHOST_TUns64 msec, GHOST_TEventType type, GHOST_IWindow* window, 
-        GHOST_TEventNDOFData data)
-               : GHOST_Event(msec, type, window)
-       {
-               m_ndofEventData = data;
-               m_data = &m_ndofEventData;
-       }
 
-protected:
-       /** translation & rotation from the device. */
-       GHOST_TEventNDOFData m_ndofEventData;
-};
+class GHOST_EventNDOFMotion : public GHOST_Event
+       {
+       protected:
+               GHOST_TEventNDOFMotionData m_axisData;
+       
+       public:
+               GHOST_EventNDOFMotion(GHOST_TUns64 time, GHOST_IWindow* window)
+                       : GHOST_Event(time, GHOST_kEventNDOFMotion, window)
+                       {
+                       m_data = &m_axisData;
+                       }
+       };
+
+
+class GHOST_EventNDOFButton : public GHOST_Event
+       {
+       protected:
+               GHOST_TEventNDOFButtonData m_buttonData;
+       
+       public:
+               GHOST_EventNDOFButton(GHOST_TUns64 time, GHOST_IWindow* window)
+                       : GHOST_Event(time, GHOST_kEventNDOFButton, window)
+                       {
+                       m_data = &m_buttonData;
+                       }
+       };
 
 
 #endif // _GHOST_EVENT_NDOF_H_
-
index dae6cb5b5445bc89c26ff95f4b41fad49eb5ef04..bda30ce8d8326864c4aced35bfd71932acad93b2 100644 (file)
  * 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
  *
  * ***** END GPL LICENSE BLOCK *****
  */
 
-/** \file ghost/intern/GHOST_NDOFManager.cpp
- *  \ingroup GHOST
- */
+#include "GHOST_NDOFManager.h"
+#include "GHOST_EventNDOF.h"
+#include "GHOST_EventKey.h"
+#include "GHOST_WindowManager.h"
+#include <string.h> // for memory functions
+#include <stdio.h> // for error/info reporting
+#include <math.h>
 
+#ifdef DEBUG_NDOF_MOTION
+// printable version of each GHOST_TProgress value
+static const char* progress_string[] =
+       {"not started","starting","in progress","finishing","finished"};
+#endif
 
-#include <stdio.h> /* just for printf */
+#ifdef DEBUG_NDOF_BUTTONS
+static const char* ndof_button_names[] = {
+       // used internally, never sent
+       "NDOF_BUTTON_NONE",
+       // these two are available from any 3Dconnexion device
+       "NDOF_BUTTON_MENU",
+       "NDOF_BUTTON_FIT",
+       // standard views
+       "NDOF_BUTTON_TOP",
+       "NDOF_BUTTON_BOTTOM",
+       "NDOF_BUTTON_LEFT",
+       "NDOF_BUTTON_RIGHT",
+       "NDOF_BUTTON_FRONT",
+       "NDOF_BUTTON_BACK",
+       // more views
+       "NDOF_BUTTON_ISO1",
+       "NDOF_BUTTON_ISO2",
+       // 90 degree rotations
+       "NDOF_BUTTON_ROLL_CW",
+       "NDOF_BUTTON_ROLL_CCW",
+       "NDOF_BUTTON_SPIN_CW",
+       "NDOF_BUTTON_SPIN_CCW",
+       "NDOF_BUTTON_TILT_CW",
+       "NDOF_BUTTON_TILT_CCW",
+       // device control
+       "NDOF_BUTTON_ROTATE",
+       "NDOF_BUTTON_PANZOOM",
+       "NDOF_BUTTON_DOMINANT",
+       "NDOF_BUTTON_PLUS",
+       "NDOF_BUTTON_MINUS",
+       // general-purpose buttons
+       "NDOF_BUTTON_1",
+       "NDOF_BUTTON_2",
+       "NDOF_BUTTON_3",
+       "NDOF_BUTTON_4",
+       "NDOF_BUTTON_5",
+       "NDOF_BUTTON_6",
+       "NDOF_BUTTON_7",
+       "NDOF_BUTTON_8",
+       "NDOF_BUTTON_9",
+       "NDOF_BUTTON_10",
+       };
+#endif
 
-#include "GHOST_NDOFManager.h"
+static const NDOF_ButtonT SpaceNavigator_HID_map[] =
+       {
+       NDOF_BUTTON_MENU,
+       NDOF_BUTTON_FIT
+       };
 
+static const NDOF_ButtonT SpaceExplorer_HID_map[] =
+       {
+       NDOF_BUTTON_1,
+       NDOF_BUTTON_2,
+       NDOF_BUTTON_TOP,
+       NDOF_BUTTON_LEFT,
+       NDOF_BUTTON_RIGHT,
+       NDOF_BUTTON_FRONT,
+       NDOF_BUTTON_NONE, // esc key
+       NDOF_BUTTON_NONE, // alt key
+       NDOF_BUTTON_NONE, // shift key
+       NDOF_BUTTON_NONE, // ctrl key
+       NDOF_BUTTON_FIT,
+       NDOF_BUTTON_MENU,
+       NDOF_BUTTON_PLUS,
+       NDOF_BUTTON_MINUS,
+       NDOF_BUTTON_ROTATE
+       };
 
-// 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};
+static const NDOF_ButtonT SpacePilotPro_HID_map[] =
+       {
+       NDOF_BUTTON_MENU,
+       NDOF_BUTTON_FIT,
+       NDOF_BUTTON_TOP,
+       NDOF_BUTTON_LEFT,
+       NDOF_BUTTON_RIGHT,
+       NDOF_BUTTON_FRONT,
+       NDOF_BUTTON_BOTTOM,
+       NDOF_BUTTON_BACK,
+       NDOF_BUTTON_ROLL_CW,
+       NDOF_BUTTON_ROLL_CCW,
+       NDOF_BUTTON_ISO1,
+       NDOF_BUTTON_ISO2,
+       NDOF_BUTTON_1,
+       NDOF_BUTTON_2,
+       NDOF_BUTTON_3,
+       NDOF_BUTTON_4,
+       NDOF_BUTTON_5,
+       NDOF_BUTTON_6,
+       NDOF_BUTTON_7,
+       NDOF_BUTTON_8,
+       NDOF_BUTTON_9,
+       NDOF_BUTTON_10,
+       NDOF_BUTTON_NONE, // esc key
+       NDOF_BUTTON_NONE, // alt key
+       NDOF_BUTTON_NONE, // shift key
+       NDOF_BUTTON_NONE, // ctrl key
+       NDOF_BUTTON_ROTATE,
+       NDOF_BUTTON_PANZOOM,
+       NDOF_BUTTON_DOMINANT,
+       NDOF_BUTTON_PLUS,
+       NDOF_BUTTON_MINUS
+       };
 
-#if !defined(_WIN32) && !defined(__APPLE__)
-#include "GHOST_SystemX11.h"
-#endif
+static const NDOF_ButtonT SpacePilot_HID_map[] =
+// this is the older SpacePilot (sans Pro)
+// thanks to polosson for the info in this table
+       {
+       NDOF_BUTTON_1,
+       NDOF_BUTTON_2,
+       NDOF_BUTTON_3,
+       NDOF_BUTTON_4,
+       NDOF_BUTTON_5,
+       NDOF_BUTTON_6,
+       NDOF_BUTTON_TOP,
+       NDOF_BUTTON_LEFT,
+       NDOF_BUTTON_RIGHT,
+       NDOF_BUTTON_FRONT,
+       NDOF_BUTTON_NONE, // esc key
+       NDOF_BUTTON_NONE, // alt key
+       NDOF_BUTTON_NONE, // shift key
+       NDOF_BUTTON_NONE, // ctrl key
+       NDOF_BUTTON_FIT,
+       NDOF_BUTTON_MENU,
+       NDOF_BUTTON_PLUS,
+       NDOF_BUTTON_MINUS,
+       NDOF_BUTTON_DOMINANT,
+       NDOF_BUTTON_ROTATE,
+       NDOF_BUTTON_NONE // the CONFIG button -- what does it do?
+       };
 
-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);    
-               #elif defined(WITH_GHOST_SDL)
-                       /* do nothing */
-               #else
-                       GHOST_SystemX11 *sys;
-                       sys = static_cast<GHOST_SystemX11*>(GHOST_ISystem::getSystem());
-                       void *ndofInfo = sys->prepareNdofInfo(&currentNdofValues);
-                       m_DeviceHandle = ndofDeviceOpen(ndofInfo);
+GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System& sys)
+       : m_system(sys)
+       , m_deviceType(NDOF_UnknownDevice) // each platform has its own device detection code
+       , m_buttonCount(0)
+       , m_buttonMask(0)
+       , m_buttons(0)
+       , m_motionTime(0)
+       , m_prevMotionTime(0)
+       , m_motionState(GHOST_kNotStarted)
+       , m_motionEventPending(false)
+       , m_deadZone(0.f)
+       {
+       // 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));
+       }
+
+bool GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short product_id)
+       {
+       // default to NDOF_UnknownDevice so rogue button events will get discarded
+       // "mystery device" owners can help build a HID_map for their hardware
+
+       switch (vendor_id)
+               {
+               case 0x046D: // Logitech (3Dconnexion)
+                       switch (product_id)
+                               {
+                               // -- current devices --
+                               case 0xC626:
+                                       puts("ndof: using SpaceNavigator");
+                                       m_deviceType = NDOF_SpaceNavigator;
+                                       m_buttonCount = 2;
+                                       break;
+                               case 0xC628:
+                                       puts("ndof: using SpaceNavigator for Notebooks");
+                                       m_deviceType = NDOF_SpaceNavigator; // for Notebooks
+                                       m_buttonCount = 2;
+                                       break;
+                               case 0xC627:
+                                       puts("ndof: using SpaceExplorer");
+                                       m_deviceType = NDOF_SpaceExplorer;
+                                       m_buttonCount = 15;
+                                       break;
+                               case 0xC629:
+                                       puts("ndof: using SpacePilotPro");
+                                       m_deviceType = NDOF_SpacePilotPro;
+                                       m_buttonCount = 31;
+                                       break;
+
+                               // -- older devices --
+                               case 0xC625:
+                                       puts("ndof: using SpacePilot");
+                                       m_deviceType = NDOF_SpacePilot;
+                                       m_buttonCount = 21;
+                                       break;
+
+                               case 0xC623:
+                                       puts("ndof: SpaceTraveler not supported, please file a bug report");
+                                       m_buttonCount = 8;
+                                       break;
+
+                               default:
+                                       printf("ndof: unknown Logitech product %04hx\n", product_id);
+                               }
+                       break;
+               default:
+                       printf("ndof: unknown device %04hx:%04hx\n", vendor_id, product_id);
+               }
+
+       if (m_deviceType == NDOF_UnknownDevice)
+               return false;
+       else
+               {
+               m_buttonMask = ~(-1 << m_buttonCount);
+
+               #ifdef DEBUG_NDOF_BUTTONS
+               printf("ndof: %d buttons -> hex:%X\n", m_buttonCount, m_buttonMask);
                #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;
-}
+
+               return true;
+               }
+       }
+
+void GHOST_NDOFManager::updateTranslation(short t[3], GHOST_TUns64 time)
+       {
+       memcpy(m_translation, t, sizeof(m_translation));
+       m_motionTime = time;
+       m_motionEventPending = true;
+       }
+
+void GHOST_NDOFManager::updateRotation(short r[3], GHOST_TUns64 time)
+       {
+       memcpy(m_rotation, r, sizeof(m_rotation));
+       m_motionTime = time;
+       m_motionEventPending = true;
+       }
+
+void GHOST_NDOFManager::sendButtonEvent(NDOF_ButtonT button, bool press, GHOST_TUns64 time, GHOST_IWindow* window)
+       {
+       GHOST_EventNDOFButton* event = new GHOST_EventNDOFButton(time, window);
+       GHOST_TEventNDOFButtonData* data = (GHOST_TEventNDOFButtonData*) event->getData();
+
+       data->action = press ? GHOST_kPress : GHOST_kRelease;
+       data->button = button;
+
+       #ifdef DEBUG_NDOF_BUTTONS
+       printf("%s %s\n", ndof_button_names[button], press ? "pressed" : "released");
+       #endif
+
+       m_system.pushEvent(event);
+       }
+
+void GHOST_NDOFManager::sendKeyEvent(GHOST_TKey key, bool press, GHOST_TUns64 time, GHOST_IWindow* window)
+       {
+       GHOST_TEventType type = press ? GHOST_kEventKeyDown : GHOST_kEventKeyUp;
+       GHOST_EventKey* event = new GHOST_EventKey(time, type, window, key);
+
+       #ifdef DEBUG_NDOF_BUTTONS
+       printf("keyboard %s\n", press ? "down" : "up");
+       #endif
+
+       m_system.pushEvent(event);
+       }
+
+void GHOST_NDOFManager::updateButton(int button_number, bool press, GHOST_TUns64 time)
+       {
+       GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow();
+
+       #ifdef DEBUG_NDOF_BUTTONS
+       if (m_deviceType != NDOF_UnknownDevice)
+               printf("ndof: button %d -> ", button_number);
+       #endif
+
+       switch (m_deviceType)
+               {
+               case NDOF_SpaceNavigator:
+                       sendButtonEvent(SpaceNavigator_HID_map[button_number], press, time, window);
+                       break;
+               case NDOF_SpaceExplorer:
+                       switch (button_number)
+                               {
+                               case 6: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break;
+                               case 7: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break;
+                               case 8: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break;
+                               case 9: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break;
+                               default: sendButtonEvent(SpaceExplorer_HID_map[button_number], press, time, window);
+                               }
+                       break;
+               case NDOF_SpacePilotPro:
+                       switch (button_number)
+                               {
+                               case 22: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break;
+                               case 23: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break;
+                               case 24: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break;
+                               case 25: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break;
+                               default: sendButtonEvent(SpacePilotPro_HID_map[button_number], press, time, window);
+                               }
+                       break;
+               case NDOF_SpacePilot:
+                       switch (button_number)
+                               {
+                               case 10: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break;
+                               case 11: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break;
+                               case 12: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break;
+                               case 13: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break;
+                               case 20: puts("ndof: ignoring CONFIG button"); break;
+                               default: sendButtonEvent(SpacePilot_HID_map[button_number], press, time, window);
+                               }
+                       break;
+               case NDOF_UnknownDevice:
+                       printf("ndof: button %d on unknown device (ignoring)\n", button_number);
+               }
+
+       int mask = 1 << button_number;
+       if (press)
+               m_buttons |= mask; // set this button's bit
+       else
+               m_buttons &= ~mask; // clear this button's bit
+       }
+
+void GHOST_NDOFManager::updateButtons(int button_bits, GHOST_TUns64 time)
+       {
+       button_bits &= m_buttonMask; // discard any "garbage" bits
+
+       int diff = m_buttons ^ button_bits;
+
+       for (int button_number = 0; button_number < m_buttonCount; ++button_number)
+               {
+               int mask = 1 << button_number;
+
+               if (diff & mask)
+                       {
+                       bool press = button_bits & mask;
+                       updateButton(button_number, press, time);
+                       }
+               }
+       }
+
+void GHOST_NDOFManager::setDeadZone(float dz)
+       {
+       if (dz < 0.f)
+               // negative values don't make sense, so clamp at zero
+               dz = 0.f;
+       else if (dz > 0.5f)
+               // warn the rogue user/programmer, but allow it
+               printf("ndof: dead zone of %.2f is rather high...\n", dz);
+
+       m_deadZone = dz;
+
+       printf("ndof: dead zone set to %.2f\n", dz);
+       }
+
+static bool atHomePosition(GHOST_TEventNDOFMotionData* ndof)
+       {
+       #define HOME(foo) (ndof->foo == 0)
+       return HOME(tx) && HOME(ty) && HOME(tz) && HOME(rx) && HOME(ry) && HOME(rz);
+       }
+
+static bool nearHomePosition(GHOST_TEventNDOFMotionData* ndof, float threshold)
+       {
+       if (threshold == 0.f)
+               return atHomePosition(ndof);
+       else
+               {
+               #define HOME1(foo) (fabsf(ndof->foo) < threshold)
+               return HOME1(tx) && HOME1(ty) && HOME1(tz) && HOME1(rx) && HOME1(ry) && HOME1(rz);
+               }
+       }
+
+bool GHOST_NDOFManager::sendMotionEvent()
+       {
+       if (!m_motionEventPending)
+               return false;
+
+       m_motionEventPending = false; // any pending motion is handled right now
+
+       GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow();
+       if (window == NULL)
+               return false; // delivery will fail, so don't bother sending
+
+       GHOST_EventNDOFMotion* event = new GHOST_EventNDOFMotion(m_motionTime, window);
+       GHOST_TEventNDOFMotionData* data = (GHOST_TEventNDOFMotionData*) event->getData();
+
+       // scale axis values here to normalize them to around +/- 1
+       // they are scaled again for overall sensitivity in the WM based on user prefs
+
+       const float scale = 1.f / 350.f; // 3Dconnexion devices send +/- 350 usually
+
+       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
+
+       bool handMotion = !nearHomePosition(data, m_deadZone);
+
+       // determine what kind of motion event to send (Starting, InProgress, Finishing)
+       // and where that leaves this NDOF manager (NotStarted, InProgress, Finished)
+       switch (m_motionState)
+               {
+               case GHOST_kNotStarted:
+               case GHOST_kFinished:
+                       if (handMotion)
+                               {
+                               data->progress = GHOST_kStarting;
+                               m_motionState = GHOST_kInProgress;
+                               // prev motion time will be ancient, so just make up something reasonable
+                               data->dt = 0.0125f;
+                               }
+                       else
+                               {
+                               // send no event and keep current state
+                               delete event;
+                               return false;
+                               }
+                       break;
+               case GHOST_kInProgress:
+                       if (handMotion)
+                               {
+                               data->progress = GHOST_kInProgress;
+                               // keep InProgress state
+                               }
+                       else
+                               {
+                               data->progress = GHOST_kFinishing;
+                               m_motionState = GHOST_kFinished;
+                               }
+                       break;
+               }
+
+       #ifdef DEBUG_NDOF_MOTION
+       printf("ndof motion sent -- %s\n", progress_string[data->progress]);
+
+       // show details about this motion event
+       printf("    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);
+       #endif
+
+       m_system.pushEvent(event);
+
+       m_prevMotionTime = m_motionTime;
+
+       return true;
+       }
index c9e09370e0989a63eeb668d7c918924bca3c5ae4..ce0c3e96171cfad84ad76db57c6f00037edcc3ec 100644 (file)
  * 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
  *
  * ***** END GPL LICENSE BLOCK *****
  */
-
-/** \file ghost/intern/GHOST_NDOFManager.h
- *  \ingroup GHOST
- */
-
  
 #ifndef _GHOST_NDOFMANAGER_H_
 #define _GHOST_NDOFMANAGER_H_
 
 #include "GHOST_System.h"
-#include "GHOST_IWindow.h"
 
 
+// #define DEBUG_NDOF_MOTION
+#define DEBUG_NDOF_BUTTONS
+
+typedef enum {
+       NDOF_UnknownDevice, // <-- motion will work fine, buttons are ignored
+
+       // current devices
+       NDOF_SpaceNavigator,
+       NDOF_SpaceExplorer,
+       NDOF_SpacePilotPro,
+
+       // older devices
+       NDOF_SpacePilot
+
+       } NDOF_DeviceT;
+
+// NDOF device button event types
+typedef enum {
+       // used internally, never sent
+       NDOF_BUTTON_NONE,
+       // these two are available from any 3Dconnexion device
+       NDOF_BUTTON_MENU,
+       NDOF_BUTTON_FIT,
+       // standard views
+       NDOF_BUTTON_TOP,
+       NDOF_BUTTON_BOTTOM,
+       NDOF_BUTTON_LEFT,
+       NDOF_BUTTON_RIGHT,
+       NDOF_BUTTON_FRONT,
+       NDOF_BUTTON_BACK,
+       // more views
+       NDOF_BUTTON_ISO1,
+       NDOF_BUTTON_ISO2,
+       // 90 degree rotations
+       // these don't all correspond to physical buttons
+       NDOF_BUTTON_ROLL_CW,
+       NDOF_BUTTON_ROLL_CCW,
+       NDOF_BUTTON_SPIN_CW,
+       NDOF_BUTTON_SPIN_CCW,
+       NDOF_BUTTON_TILT_CW,
+       NDOF_BUTTON_TILT_CCW,
+       // device control
+       NDOF_BUTTON_ROTATE,
+       NDOF_BUTTON_PANZOOM,
+       NDOF_BUTTON_DOMINANT,
+       NDOF_BUTTON_PLUS,
+       NDOF_BUTTON_MINUS,
+       // general-purpose buttons
+       // users can assign functions via keymap editor
+       NDOF_BUTTON_1,
+       NDOF_BUTTON_2,
+       NDOF_BUTTON_3,
+       NDOF_BUTTON_4,
+       NDOF_BUTTON_5,
+       NDOF_BUTTON_6,
+       NDOF_BUTTON_7,
+       NDOF_BUTTON_8,
+       NDOF_BUTTON_9,
+       NDOF_BUTTON_10,
+
+       } NDOF_ButtonT;
 
 class GHOST_NDOFManager
 {
 public:
-       GHOST_NDOFManager();
-       virtual ~GHOST_NDOFManager();
-
-    int deviceOpen(GHOST_IWindow* window,
-        GHOST_NDOFLibraryInit_fp setNdofLibraryInit, 
-        GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
-        GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen);
-        
-    void GHOST_NDOFGetDatas(GHOST_TEventNDOFData &datas) const;
-        
-    bool available() const;
-    bool event_present() const;
+       GHOST_NDOFManager(GHOST_System&);
+
+       virtual ~GHOST_NDOFManager() {};
+
+       // whether multi-axis functionality is available (via the OS or driver)
+       // does not imply that a device is plugged in or being used
+       virtual bool available() = 0;
+
+       // each platform's device detection should call this
+       // use standard USB/HID identifiers
+       bool setDevice(unsigned short vendor_id, unsigned short product_id);
+
+       // filter out small/accidental/uncalibrated motions by
+       // setting up a "dead zone" around home position
+       // set to 0 to disable
+       // 0.1 is a safe and reasonable value
+       void setDeadZone(float);
+
+       // the latest raw axis data from the device
+       // NOTE: axis data should be in blender view coordinates
+       //       +X is to the right
+       //       +Y is up
+       //       +Z is out of the screen
+       //       for rotations, look from origin to each +axis
+       //       rotations are + when CCW, - when CW
+       // each platform is responsible for getting axis data into this form
+       // these values should not be scaled (just shuffled or flipped)
+       void updateTranslation(short t[3], GHOST_TUns64 time);
+       void updateRotation(short r[3], GHOST_TUns64 time);
+
+       // the latest raw button data from the device
+       // use HID button encoding (not NDOF_ButtonT)
+       void updateButton(int button_number, bool press, GHOST_TUns64 time);
+       void updateButtons(int button_bits, GHOST_TUns64 time);
+       // NDOFButton events are sent immediately
+
+       // processes and sends most recent raw data as an NDOFMotion event
+       // returns whether an event was sent
+       bool sendMotionEvent();
 
 protected:
-    void* m_DeviceHandle;
-};
+       GHOST_System& m_system;
 
+private:
+       void sendButtonEvent(NDOF_ButtonT, bool press, GHOST_TUns64 time, GHOST_IWindow*);
+       void sendKeyEvent(GHOST_TKey, bool press, GHOST_TUns64 time, GHOST_IWindow*);
+
+       NDOF_DeviceT m_deviceType;
+       int m_buttonCount;
+       int m_buttonMask;
+
+       short m_translation[3];
+       short m_rotation[3];
+       int m_buttons; // bit field
+
+       GHOST_TUns64 m_motionTime; // in milliseconds
+       GHOST_TUns64 m_prevMotionTime; // time of most recent Motion event sent
+
+       GHOST_TProgress m_motionState;
+       bool m_motionEventPending;
+       float m_deadZone; // discard motion with each component < this
+};
 
 #endif
diff --git a/intern/ghost/intern/GHOST_NDOFManagerCocoa.h b/intern/ghost/intern/GHOST_NDOFManagerCocoa.h
new file mode 100644 (file)
index 0000000..d8711e9
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * ***** 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 NDOF manager 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
diff --git a/intern/ghost/intern/GHOST_NDOFManagerCocoa.mm b/intern/ghost/intern/GHOST_NDOFManagerCocoa.mm
new file mode 100644 (file)
index 0000000..995f739
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * ***** 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 *****
+ */
+#include "GHOST_NDOFManagerCocoa.h"
+#include "GHOST_SystemCocoa.h"
+
+extern "C" {
+       #include <3DconnexionClient/ConnexionClientAPI.h>
+       #include <stdio.h>
+       }
+
+// static functions need to talk to these objects:
+static GHOST_SystemCocoa* ghost_system = NULL;
+static GHOST_NDOFManager* ndof_manager = NULL;
+
+// 3Dconnexion drivers before 10.x are "old"
+// not all buttons will work
+static bool has_old_driver = true;
+
+static void NDOF_DeviceAdded(io_connect_t connection)
+       {
+       printf("ndof: device added\n"); // change these: printf --> informational reports
+
+#if 0 // device preferences will be useful some day
+       ConnexionDevicePrefs p;
+       ConnexionGetCurrentDevicePrefs(kDevID_AnyDevice, &p);
+#endif
+
+       // determine exactly which device is plugged in
+       SInt32 result = 0;
+       ConnexionControl(kConnexionCtlGetDeviceID, 0, &result);
+       unsigned short vendorID = result >> 16;
+       unsigned short productID = result & 0xffff;
+
+       ndof_manager->setDevice(vendorID, productID);
+       }
+
+static void NDOF_DeviceRemoved(io_connect_t connection)
+       {
+       printf("ndof: device removed\n");
+       }
+
+static void NDOF_DeviceEvent(io_connect_t connection, natural_t messageType, void* messageArgument)
+       {
+       switch (messageType)
+               {
+               case kConnexionMsgDeviceState:
+                       {
+                       ConnexionDeviceState* s = (ConnexionDeviceState*)messageArgument;
+
+                       GHOST_TUns64 now = ghost_system->getMilliSeconds();
+
+                       switch (s->command)
+                               {
+                               case kConnexionCmdHandleAxis:
+                                       {
+                                       // convert to blender view coordinates
+                                       short t[3] = {s->axis[0], -(s->axis[2]), s->axis[1]};
+                                       short r[3] = {-(s->axis[3]), s->axis[5], -(s->axis[4])};
+
+                                       ndof_manager->updateTranslation(t, now);
+                                       ndof_manager->updateRotation(r, now);
+
+                                       ghost_system->notifyExternalEventProcessed();
+                                       break;
+                                       }
+                               case kConnexionCmdHandleButtons:
+                                       {
+                                       int button_bits = has_old_driver ? s->buttons8 : s->buttons;
+                                       ndof_manager->updateButtons(button_bits, now);
+                                       ghost_system->notifyExternalEventProcessed();
+                                       break;
+                                       }
+                               case kConnexionCmdAppSpecific:
+                                       printf("ndof: app-specific command, param = %hd, value = %d\n", s->param, s->value);
+                                       break;
+
+                               default:
+                                       printf("ndof: mystery device command %d\n", s->command);
+                               }
+                       break;
+                       }
+               case kConnexionMsgPrefsChanged:
+                       // printf("ndof: prefs changed\n"); // this includes app switches
+                       // TODO: look through updated prefs for things blender cares about
+                       break;
+               case kConnexionMsgCalibrateDevice:
+                       printf("ndof: calibrate\n"); // but what should blender do?
+                       break;
+               case kConnexionMsgDoMapping:
+                       // printf("ndof: driver did something\n");
+                       // sent when the driver itself consumes an NDOF event
+                       // and performs whatever action is set in user prefs
+                       // 3Dx header file says to ignore these
+                       break;
+               default:
+                       printf("ndof: mystery event %d\n", messageType);
+               }
+       }
+
+GHOST_NDOFManagerCocoa::GHOST_NDOFManagerCocoa(GHOST_System& sys)
+       : GHOST_NDOFManager(sys)
+       {
+       if (available())
+               {
+               // give static functions something to talk to:
+               ghost_system = dynamic_cast<GHOST_SystemCocoa*>(&sys);
+               ndof_manager = this;
+
+               OSErr error = InstallConnexionHandlers(NDOF_DeviceEvent, NDOF_DeviceAdded, NDOF_DeviceRemoved);
+               if (error)
+                       {
+                       printf("ndof: error %d while installing handlers\n", error);
+                       return;
+                       }
+
+               // Pascal string *and* a four-letter constant. How old-skool.
+               m_clientID = RegisterConnexionClient('blnd', (UInt8*) "\007blender",
+                       kConnexionClientModeTakeOver, kConnexionMaskAll);
+
+               // printf("ndof: client id = %d\n", m_clientID);
+
+               if (SetConnexionClientButtonMask != NULL)
+                       {
+                       has_old_driver = false;
+                       SetConnexionClientButtonMask(m_clientID, kConnexionMaskAllButtons);
+                       }
+               else
+                       printf("ndof: old 3Dx driver installed, some buttons may not work\n");
+               }
+       else
+               {
+               printf("ndof: 3Dx driver not found\n");
+               // This isn't a hard error, just means the user doesn't have a 3D mouse.
+               }
+       }
+
+GHOST_NDOFManagerCocoa::~GHOST_NDOFManagerCocoa()
+       {
+       UnregisterConnexionClient(m_clientID);
+       CleanupConnexionHandlers();
+       ghost_system = NULL;
+       ndof_manager = NULL;
+       }
+
+bool GHOST_NDOFManagerCocoa::available()
+       {
+       // extern OSErr InstallConnexionHandlers() __attribute__((weak_import));
+       // ^^ not needed since the entire framework is weak-linked
+       return InstallConnexionHandlers != NULL;
+       // this means that the driver is installed and dynamically linked to blender
+       }
diff --git a/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp b/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp
new file mode 100644 (file)
index 0000000..d7285d5
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * ***** 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 *****
+ */
+#include "GHOST_NDOFManagerWin32.h"
+
+
+GHOST_NDOFManagerWin32::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 GHOST_NDOFManagerWin32::available()
+       {
+       // always available since RawInput is built into Windows
+       return true;
+       }
diff --git a/intern/ghost/intern/GHOST_NDOFManagerWin32.h b/intern/ghost/intern/GHOST_NDOFManagerWin32.h
new file mode 100644 (file)
index 0000000..3802a6d
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * ***** 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&);
+       bool available();
+};
+
+
+#endif
diff --git a/intern/ghost/intern/GHOST_NDOFManagerX11.cpp b/intern/ghost/intern/GHOST_NDOFManagerX11.cpp
new file mode 100644 (file)
index 0000000..233a9b3
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * ***** 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 *****
+ */
+#include "GHOST_NDOFManagerX11.h"
+#include "GHOST_SystemX11.h"
+#include <spnav.h>
+#include <stdio.h>
+
+
+GHOST_NDOFManagerX11::GHOST_NDOFManagerX11(GHOST_System& sys)
+       : GHOST_NDOFManager(sys)
+       , m_available(false)
+       {
+       setDeadZone(0.1f); // how to calibrate on Linux? throw away slight motion!
+
+       if (spnav_open() != -1)
+               {
+               // determine exactly which device (if any) is plugged in
+
+               #define MAX_LINE_LENGTH 100
+
+               // look for USB devices with Logitech's vendor ID
+               FILE* command_output = popen("lsusb -d 046d:","r");
+               if (command_output)
+                       {
+                       char line[MAX_LINE_LENGTH] = {0};
+                       while (fgets(line, MAX_LINE_LENGTH, command_output))
+                               {
+                               unsigned short vendor_id = 0, product_id = 0;
+                               if (sscanf(line, "Bus %*d Device %*d: ID %hx:%hx", &vendor_id, &product_id) == 2)
+                                       if (setDevice(vendor_id, product_id))
+                                               {
+                                               m_available = true;
+                                               break; // stop looking once the first 3D mouse is found
+                                               }
+                               }
+                       pclose(command_output);
+                       }
+               }
+       else
+               {
+               printf("ndof: spacenavd not found\n");
+               // This isn't a hard error, just means the user doesn't have a 3D mouse.
+               }
+       }
+
+GHOST_NDOFManagerX11::~GHOST_NDOFManagerX11()
+       {
+       if (m_available)
+               spnav_close();
+       }
+
+bool GHOST_NDOFManagerX11::available()
+       {
+       return m_available;
+       }
+
+//bool GHOST_NDOFManagerX11::identifyDevice()
+//     {
+//     
+//     }
+
+bool GHOST_NDOFManagerX11::processEvents()
+       {
+       GHOST_TUns64 now = m_system.getMilliSeconds();
+
+       bool anyProcessed = false;
+       spnav_event e;
+       while (spnav_poll_event(&e))
+               {
+               switch (e.type)
+                       {
+                       case SPNAV_EVENT_MOTION:
+                               {
+                               // convert to blender view coords
+                               short t[3] = {e.motion.x, e.motion.y, -e.motion.z};
+                               short r[3] = {-e.motion.rx, -e.motion.ry, e.motion.rz};
+
+                               updateTranslation(t, now);
+                               updateRotation(r, now);
+                               break;
+                               }
+                       case SPNAV_EVENT_BUTTON:
+                               updateButton(e.button.bnum, e.button.press, now);
+                               break;
+                       }
+               anyProcessed = true;
+               }
+       return anyProcessed;
+       }
diff --git a/intern/ghost/intern/GHOST_NDOFManagerX11.h b/intern/ghost/intern/GHOST_NDOFManagerX11.h
new file mode 100644 (file)
index 0000000..5e1c9d9
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * ***** 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_NDOFMANAGERX11_H_
+#define _GHOST_NDOFMANAGERX11_H_
+
+#include "GHOST_NDOFManager.h"
+
+// Event capture is handled within the NDOF manager on Linux,
+// so there's no need for SystemX11 to look for them.
+
+class GHOST_NDOFManagerX11 : public GHOST_NDOFManager
+       {
+public:
+       GHOST_NDOFManagerX11(GHOST_System&);
+       ~GHOST_NDOFManagerX11();
+       bool available();
+       bool processEvents();
+
+private:
+//     bool identifyDevice();
+
+       bool m_available;
+       };
+
+#endif
+
index cb3e97fc5749b2f51b7132c94c3599de6cae5eb4..792adba7ce3bc17f0a6efc4775688e58f109e47e 100644 (file)
@@ -194,12 +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:
+       if (m_ndofManager) {
+               handled |= m_ndofManager->sendMotionEvent();
        }
-       else {
-               handled = false;
+
+       if (m_eventManager) {
+               handled |= m_eventManager->dispatchEvents();
        }
 
        m_timerManager->fireTimers(getMilliSeconds());
@@ -243,18 +246,6 @@ GHOST_TSuccess GHOST_System::pushEvent(GHOST_IEvent* event)
        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,12 +276,6 @@ 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) {
index b5c64bfceb672a5efd716d4337a9cbac2f22d428..f62c0984c8096ba2815a1f61ddace789762a4d16 100644 (file)
@@ -190,25 +190,6 @@ public:
         */
        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
         ***************************************************************************************/
index ce77735838934d088bff949686a3bc3fe8379d6c..d20aed63f42e92d673e2dc51d7a036fd6fc06ed6 100644 (file)
@@ -220,6 +220,11 @@ public:
      */
     GHOST_TSuccess handleApplicationBecomeActiveEvent();
 
+       /**
+        * External objects should call this when they send an event outside processEvents.
+        */
+       void notifyExternalEventProcessed();
+
        /**
         * @see GHOST_ISystem
         */
@@ -267,7 +272,7 @@ protected:
        /** Start time at initialization. */
        GHOST_TUns64 m_start_time;
        
-       /** 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 */
index 5c88523d4063220eb15501c4392024fa589272c7..8f2df4c396dac0e7d64ac1f0fb0e7112fdc4a6f0 100644 (file)
@@ -52,7 +52,7 @@
 #include "GHOST_TimerTask.h"
 #include "GHOST_WindowManager.h"
 #include "GHOST_WindowCocoa.h"
-#include "GHOST_NDOFManager.h"
+#include "GHOST_NDOFManagerCocoa.h"
 #include "AssertMacros.h"
 
 #pragma mark KeyMap, mouse converters
@@ -596,6 +596,9 @@ GHOST_TSuccess GHOST_SystemCocoa::init()
        
     GHOST_TSuccess success = GHOST_System::init();
     if (success) {
+
+               m_ndofManager = new GHOST_NDOFManagerCocoa(*this);
+
                //ProcessSerialNumber psn;
                
                //Carbon stuff to move window & menu to foreground
@@ -1007,6 +1010,11 @@ GHOST_TSuccess GHOST_SystemCocoa::handleApplicationBecomeActiveEvent()
        return GHOST_kSuccess;
 }
 
+void GHOST_SystemCocoa::notifyExternalEventProcessed()
+{
+       m_outsideLoopEventProcessed = true;
+}
+
 //Note: called from NSWindow delegate
 GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window)
 {
index becccc2c29f0764b380dfa4c3aa790cbe1ceee9f..523d119c7e74ec0ce87663d5eba89bfad3cee5c0 100644 (file)
 
 #include "GHOST_SystemPathsWin32.h"
 
-#define WIN32_LEAN_AND_MEAN
-#ifdef _WIN32_IE
-#undef _WIN32_IE
-#endif
+#ifndef _WIN32_IE
 #define _WIN32_IE 0x0501
-#include <windows.h>
+#endif
 #include <shlobj.h>
 
 #if defined(__MINGW32__) || defined(__CYGWIN__)
index 67cc2140e0e54301d4b556f688255e4f2f5279a5..3de7bbf934e74d1bc7c5e6bd4ff3b6b28f369366 100644 (file)
@@ -38,6 +38,8 @@
 #error WIN32 only!
 #endif // WIN32
 
+#define _WIN32_WINNT 0x501 // require Windows XP or newer
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
 #include "GHOST_SystemPaths.h"
index 95b3456ce3bca4ad45e9a2f507209f2979353c84..1b43fa92bb26771d0d0d2567e5ec367c43da22c6 100644 (file)
  * @date       May 7, 2001
  */
 
+#ifdef BF_GHOST_DEBUG
 #include <iostream>
-
-#ifdef FREE_WINDOWS
-#  define WINVER 0x0501 /* GetConsoleWindow() for MinGW */
 #endif
 
+#include <stdio.h> // [mce] temporary debug, remove soon!
+
 #include "GHOST_SystemWin32.h"
 #include "GHOST_EventDragnDrop.h"
 
-#define WIN32_LEAN_AND_MEAN
-#ifdef _WIN32_IE
-#undef _WIN32_IE
+#ifndef _WIN32_IE
+#define _WIN32_IE 0x0501 /* shipped before XP, so doesn't impose additional requirements */
 #endif
-#define _WIN32_IE 0x0501
-#include <windows.h>
 #include <shlobj.h>
 #include <tlhelp32.h>
 
 #endif
 #endif
 
-/*
- * According to the docs the mouse wheel message is supported from windows 98 
- * upwards. Leaving WINVER at default value, the WM_MOUSEWHEEL message and the 
- * wheel detent value are undefined.
- */
-#ifndef WM_MOUSEWHEEL
-#define WM_MOUSEWHEEL 0x020A
-#endif // WM_MOUSEWHEEL
-#ifndef WHEEL_DELTA
-#define WHEEL_DELTA 120        /* Value for rolling one detent, (old convention! MS changed it) */
-#endif // WHEEL_DELTA
-
-/* 
- * Defines for mouse buttons 4 and 5 aka xbutton1 and xbutton2.
- * MSDN: Declared in Winuser.h, include Windows.h 
- * This does not seem to work with MinGW so we define our own here.
- */
-#ifndef XBUTTON1
-#define XBUTTON1 0x0001
-#endif // XBUTTON1
-#ifndef XBUTTON2
-#define XBUTTON2 0x0002
-#endif // XBUTTON2
-#ifndef WM_XBUTTONUP
-#define WM_XBUTTONUP 524
-#endif // WM_XBUTTONUP
-#ifndef WM_XBUTTONDOWN
-#define WM_XBUTTONDOWN 523
-#endif // WM_XBUTTONDOWN
-
 #include "GHOST_Debug.h"
 #include "GHOST_DisplayManagerWin32.h"
 #include "GHOST_EventButton.h"
 #include "GHOST_EventCursor.h"
 #include "GHOST_EventKey.h"
 #include "GHOST_EventWheel.h"
-#include "GHOST_EventNDOF.h"
 #include "GHOST_TimerTask.h"
 #include "GHOST_TimerManager.h"
 #include "GHOST_WindowManager.h"
 #include "GHOST_WindowWin32.h"
-#include "GHOST_NDOFManager.h"
+#include "GHOST_NDOFManagerWin32.h"
 
 // Key code values not found in winuser.h
 #ifndef VK_MINUS
 #define VK_MEDIA_PLAY_PAUSE    0xB3
 #endif // VK_MEDIA_PLAY_PAUSE
 
-/*
-       Initiates WM_INPUT messages from keyboard
-       That way GHOST can retrieve true keys
-*/
-GHOST_TInt32 GHOST_SystemWin32::initKeyboardRawInput(void)
+static void initRawInput()
 {
-       RAWINPUTDEVICE device = {0};
-       device.usUsagePage      = 0x01; /* usUsagePage & usUsage for keyboard*/
-       device.usUsage          = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
+       RAWINPUTDEVICE devices[2];
+       memset(devices, 0, 2 * sizeof(RAWINPUTDEVICE));
 
-       return RegisterRawInputDevices(&device, 1, sizeof(device));
-};
+       // multi-axis mouse (SpaceNavigator, etc.)
+       devices[0].usUsagePage = 0x01;
+       devices[0].usUsage = 0x08;
+
+       // Initiates WM_INPUT messages from keyboard
+       // That way GHOST can retrieve true keys
+       devices[1].usUsagePage = 0x01;
+       devices[1].usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
+
+       if (RegisterRawInputDevices(devices, 2, sizeof(RAWINPUTDEVICE)))
+               puts("registered for RawInput (spacenav & keyboard)");
+       else
+               printf("could not register for RawInput: %d\n", (int)GetLastError());
+}
 
 GHOST_SystemWin32::GHOST_SystemWin32()
 : m_hasPerformanceCounter(false), m_freq(0), m_start(0)
@@ -187,6 +160,8 @@ GHOST_SystemWin32::GHOST_SystemWin32()
        this->handleKeyboardChange();
        // Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32.
        OleInitialize(0);
+
+       m_ndofManager = new GHOST_NDOFManagerWin32(*this);
 }
 
 GHOST_SystemWin32::~GHOST_SystemWin32()
@@ -245,6 +220,7 @@ GHOST_IWindow* GHOST_SystemWin32::createWindow(
                        // Store the pointer to the window
 //                     if (state != GHOST_kWindowStateFullScreen) {
                                m_windowManager->addWindow(window);
+                               m_windowManager->setActiveWindow(window);
 //                     }
                }
                else {
@@ -385,22 +361,15 @@ GHOST_TSuccess GHOST_SystemWin32::init()
        GHOST_TSuccess success = GHOST_System::init();
        
        /* Disable scaling on high DPI displays on Vista */
+       HMODULE
        user32 = ::LoadLibraryA("user32.dll");
        typedef BOOL (WINAPI * LPFNSETPROCESSDPIAWARE)();
        LPFNSETPROCESSDPIAWARE SetProcessDPIAware =
                (LPFNSETPROCESSDPIAWARE)GetProcAddress(user32, "SetProcessDPIAware");
        if (SetProcessDPIAware)
                SetProcessDPIAware();
-       #ifdef NEED_RAW_PROC
-               pRegisterRawInputDevices = (LPFNDLLRRID)GetProcAddress(user32, "RegisterRawInputDevices");
-               pGetRawInputData = (LPFNDLLGRID)GetProcAddress(user32, "GetRawInputData");
-       #else
-               FreeLibrary(user32);
-       #endif
-
-       /*      Initiates WM_INPUT messages from keyboard */
-       initKeyboardRawInput();
-
+       FreeLibrary(user32);
+       initRawInput();
 
        // Determine whether this system has a high frequency performance counter. */
        m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER*)&m_freq) == TRUE;
@@ -441,104 +410,84 @@ GHOST_TSuccess GHOST_SystemWin32::init()
 
 GHOST_TSuccess GHOST_SystemWin32::exit()
 {
-       #ifdef NEED_RAW_PROC
-       FreeLibrary(user32);
-       #endif
-
        return GHOST_System::exit();
 }
 
-GHOST_TKey GHOST_SystemWin32::hardKey(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam, int * keyDown, char * vk)
+GHOST_TKey GHOST_SystemWin32::hardKey(GHOST_IWindow *window, RAWINPUT const& raw, int * keyDown, char * vk)
 {
-       unsigned int size = 0;
-       char * data;
        GHOST_TKey key = GHOST_kKeyUnknown;
 
 
        if(!keyDown)
                return GHOST_kKeyUnknown;
 
-       GetRawInputData((HRAWINPUT)lParam, RID_INPUT, 0, &size, sizeof(RAWINPUTHEADER));
 
+       GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
 
-       if((data = (char*)malloc(size)) &&
-               GetRawInputData((HRAWINPUT)lParam, RID_INPUT, data, &size, sizeof(RAWINPUTHEADER)))
+       GHOST_ModifierKeys modifiers;
+       system->retrieveModifierKeys(modifiers);
+       
+       *keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK);
+       key = this->convertKey(window, raw.data.keyboard.VKey, raw.data.keyboard.MakeCode, (raw.data.keyboard.Flags&(RI_KEY_E1|RI_KEY_E0)));
+       
+       // extra handling of modifier keys: don't send repeats out from GHOST
+       if(key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt)
        {
-               RAWINPUT ri;
-               memcpy(&ri,data,(size < sizeof(ri)) ? size : sizeof(ri));
-
-               if (ri.header.dwType == RIM_TYPEKEYBOARD)
-               {
-                       GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
-                       
-                       GHOST_ModifierKeys modifiers;
-                       system->retrieveModifierKeys(modifiers);
-                       
-                       *keyDown = !(ri.data.keyboard.Flags & RI_KEY_BREAK);
-                       key = this->convertKey(window, ri.data.keyboard.VKey, ri.data.keyboard.MakeCode, (ri.data.keyboard.Flags&(RI_KEY_E1|RI_KEY_E0)));
-                       
-                       // extra handling of modifier keys: don't send repeats out from GHOST
-                       if(key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt)
-                       {
-                               bool changed = false;
-                               GHOST_TModifierKeyMask modifier;
-                               switch(key) {
-                                       case GHOST_kKeyLeftShift:
-                                               {
-                                                       changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != (bool)*keyDown);
-                                                       modifier = GHOST_kModifierKeyLeftShift;
-                                               }
-                                               break;
-                                       case GHOST_kKeyRightShift:
-                                               {
-                                                       changed = (modifiers.get(GHOST_kModifierKeyRightShift) != (bool)*keyDown);
-                                                       modifier = GHOST_kModifierKeyRightShift;
-                                               }
-                                               break;
-                                       case GHOST_kKeyLeftControl:
-                                               {
-                                                       changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != (bool)*keyDown);
-                                                       modifier = GHOST_kModifierKeyLeftControl;
-                                               }
-                                               break;
-                                       case GHOST_kKeyRightControl:
-                                               {
-                                                       changed = (modifiers.get(GHOST_kModifierKeyRightControl) != (bool)*keyDown);
-                                                       modifier = GHOST_kModifierKeyRightControl;
-                                               }
-                                               break;
-                                       case GHOST_kKeyLeftAlt:
-                                               {
-                                                       changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != (bool)*keyDown);
-                                                       modifier = GHOST_kModifierKeyLeftAlt;
-                                               }
-                                               break;
-                                       case GHOST_kKeyRightAlt:
-                                               {
-                                                       changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != (bool)*keyDown);
-                                                       modifier = GHOST_kModifierKeyRightAlt;
-                                               }
-                                               break;
-                                       default: break;
+               bool changed = false;
+               GHOST_TModifierKeyMask modifier;
+               switch(key) {
+                       case GHOST_kKeyLeftShift:
+                               {
+                                       changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != (bool)*keyDown);
+                                       modifier = GHOST_kModifierKeyLeftShift;
                                }
-                               
-                               if(changed)
+                               break;
+                       case GHOST_kKeyRightShift:
                                {
-                                       modifiers.set(modifier, (bool)*keyDown);
-                                       system->storeModifierKeys(modifiers);
+                                       changed = (modifiers.get(GHOST_kModifierKeyRightShift) != (bool)*keyDown);
+                                       modifier = GHOST_kModifierKeyRightShift;
+                               }
+                               break;
+                       case GHOST_kKeyLeftControl:
+                               {
+                                       changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != (bool)*keyDown);
+                                       modifier = GHOST_kModifierKeyLeftControl;
                                }
-                               else
+                               break;
+                       case GHOST_kKeyRightControl:
                                {
-                                       key = GHOST_kKeyUnknown;
+                                       changed = (modifiers.get(GHOST_kModifierKeyRightControl) != (bool)*keyDown);
+                                       modifier = GHOST_kModifierKeyRightControl;
                                }
-                       }
-                       
+                               break;
+                       case GHOST_kKeyLeftAlt:
+                               {
+                                       changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != (bool)*keyDown);
+                                       modifier = GHOST_kModifierKeyLeftAlt;
+                               }
+                               break;
+                       case GHOST_kKeyRightAlt:
+                               {
+                                       changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != (bool)*keyDown);
+                                       modifier = GHOST_kModifierKeyRightAlt;
+                               }
+                               break;
+                       default: break;
+               }
+               
+               if(changed)
+               {
+                       modifiers.set(modifier, (bool)*keyDown);
+                       system->storeModifierKeys(modifiers);
+               }
+               else
+               {
+                       key = GHOST_kKeyUnknown;
+               }
+       }
        
-                       if(vk) *vk = ri.data.keyboard.VKey;
-               };
 
-       };
-       free(data);
+       if(vk) *vk = raw.data.keyboard.VKey;
 
        return key;
 }
@@ -742,12 +691,12 @@ GHOST_EventWheel* GHOST_SystemWin32::processWheelEvent(GHOST_IWindow *window, WP
 }
 
 
-GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam)
+GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, RAWINPUT const& raw)
 {
        int keyDown=0;
        char vk;
        GHOST_SystemWin32 * system = (GHOST_SystemWin32 *)getSystem();
-       GHOST_TKey key = system->hardKey(window, wParam, lParam, &keyDown, &vk);
+       GHOST_TKey key = system->hardKey(window, raw, &keyDown, &vk);
        GHOST_EventKey* event;
        if (key != GHOST_kKeyUnknown) {
                char ascii = '\0';
@@ -777,7 +726,15 @@ GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, WPARAM
 
 GHOST_Event* GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_IWindow* window)
 {
-       return new GHOST_Event(getSystem()->getMilliSeconds(), type, window);
+       GHOST_System* system = (GHOST_System*)getSystem();
+
+       if (type == GHOST_kEventWindowActivate)
+               {
+               puts("activating window");
+               system->getWindowManager()->setActiveWindow(window);
+               }
+
+       return new GHOST_Event(system->getMilliSeconds(), type, window);
 }
 
 GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType, 
@@ -800,9 +757,101 @@ void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO * minmax)
        minmax->ptMinTrackSize.y=240;
 }
 
+bool GHOST_SystemWin32::processNDOF(RAWINPUT const& raw)
+{
+       bool eventSent = false;
+       GHOST_TUns64 now = getMilliSeconds();
+
+       static bool firstEvent = true;
+       if (firstEvent)
+               { // determine exactly which device is plugged in
+               RID_DEVICE_INFO info;
+               unsigned infoSize = sizeof(RID_DEVICE_INFO);
+               info.cbSize = infoSize;
+
+               GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICEINFO, &info, &infoSize);
+               if (info.dwType == RIM_TYPEHID)
+                       m_ndofManager->setDevice(info.hid.dwVendorId, info.hid.dwProductId);
+               else
+            puts("<!> not a HID device... mouse/kb perhaps?");
+
+               firstEvent = false;
+               }
+
+       // The NDOF manager sends button changes immediately, and *pretends* to
+       // send motion. Mark as 'sent' so motion will always get dispatched.
+       eventSent = true;
+
+#ifdef _MSC_VER
+    // using Microsoft compiler & header files
+    // they invented the RawInput API, so this version is (probably) correct
+       BYTE const* data = raw.data.hid.bRawData;
+       // struct RAWHID {
+       // DWORD dwSizeHid;
+       // DWORD dwCount;
+       // BYTE  bRawData[1];
+       // };
+#else
+       // MinGW's definition (below) doesn't agree, so we need a slight
+       // workaround until it's fixed
+       BYTE const* data = &raw.data.hid.bRawData;
+       // struct RAWHID {
+       // DWORD dwSizeHid;
+       // DWORD dwCount;
+       // BYTE bRawData; // <== isn't this s'posed to be a BYTE*?
+       // };
+#endif
+
+       BYTE packetType = data[0];
+       switch (packetType)
+               {
+               case 1: // translation
+                       {
+                       short* axis = (short*)(data + 1);
+                       short t[3] = {axis[0], -axis[2], axis[1]};
+                       m_ndofManager->updateTranslation(t, now);
+
+                       if (raw.data.hid.dwSizeHid == 13)
+                               { // this report also includes rotation
+                               short r[3] = {-axis[3], axis[5], -axis[4]};
+                               m_ndofManager->updateRotation(r, now);
+
+                               // I've never gotten one of these, has anyone else?
+                               puts("ndof: combined T + R");
+                               }
+                       break;
+                       }
+               case 2: // rotation
+                       {
+                       short* axis = (short*)(data + 1);
+                       short r[3] = {-axis[0], axis[2], -axis[1]};
+                       m_ndofManager->updateRotation(r, now);
+                       break;
+                       }
+               case 3: // buttons
+                       {
+                       #if 0
+                       // I'm getting garbage bits -- examine whole report:
+                       printf("ndof: HID report for buttons [");
+                       for (int i = 0; i < raw.data.hid.dwSizeHid; ++i)
+                               printf(" %02X", data[i]);
+                       printf(" ]\n");
+                       #endif
+
+                       int button_bits;
+                       memcpy(&button_bits, data + 1, sizeof(button_bits));
+                       m_ndofManager->updateButtons(button_bits, now);
+                       break;
+                       }
+               }
+       return eventSent;
+}
+
 LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
        GHOST_Event* event = 0;
+       bool eventHandled = false;
+
        LRESULT lResult = 0;
        GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
        GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized")
@@ -819,18 +868,36 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
                                // Keyboard events, processed
                                ////////////////////////////////////////////////////////////////////////
                                case WM_INPUT:
+                               {
                                        // check WM_INPUT from input sink when ghost window is not in the foreground
                                        if (wParam == RIM_INPUTSINK) {
                                                if (GetFocus() != hwnd) // WM_INPUT message not for this window
                                                        return 0;
-                                       } //else wPAram == RIM_INPUT
-                                       event = processKeyEvent(window, wParam, lParam);
-                                       if (!event) {
-                                               GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
-                                               GHOST_PRINT(msg)
-                                               GHOST_PRINT(" key ignored\n")
+                                       } //else wParam == RIM_INPUT
+
+                                       RAWINPUT raw;
+                                       RAWINPUT* raw_ptr = &raw;
+                                       UINT rawSize = sizeof(RAWINPUT);
+
+                                       GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_ptr, &rawSize, sizeof(RAWINPUTHEADER));
+
+                                       switch (raw.header.dwType)
+                                       {
+                                       case RIM_TYPEKEYBOARD:
+                                               event = processKeyEvent(window, raw);
+                                               if (!event) {
+                                                       GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
+                                                       GHOST_PRINT(msg)
+                                                       GHOST_PRINT(" key ignored\n")
+                                               }
+                                               break;
+                                       case RIM_TYPEHID:
+                                               if (system->processNDOF(raw))
+                                                       eventHandled = true;
+                                               break;
                                        }
-                                       break;
+                               break;
+                               }
                                ////////////////////////////////////////////////////////////////////////
                                // Keyboard events, ignored
                                ////////////////////////////////////////////////////////////////////////
@@ -840,9 +907,9 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
                                case WM_SYSKEYUP:
                                        /* These functions were replaced by WM_INPUT*/
                                case WM_CHAR:
-                                       /* The WM_CHAR message is posted to the window with the keyboard focus when 
-                                        * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR 
-                                        * contains the character code of the key that was pressed. 
+                                       /* The WM_CHAR message is posted to the window with the keyboard focus when
+                                        * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR
+                                        * contains the character code of the key that was pressed.
                                         */
                                case WM_DEADCHAR:
                                        /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
@@ -1128,28 +1195,6 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
                                         * In GHOST, we let DefWindowProc call the timer callback.
                                         */
                                        break;
-                               case WM_BLND_NDOF_AXIS:
-                                       {
-                                               GHOST_TEventNDOFData ndofdata;
-                                               system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
-                                               system->m_eventManager->
-                                                       pushEvent(new GHOST_EventNDOF(
-                                                               system->getMilliSeconds(), 
-                                                               GHOST_kEventNDOFMotion, 
-                                                               window, ndofdata));
-                                       }
-                                       break;
-                               case WM_BLND_NDOF_BTN:
-                                       {
-                                               GHOST_TEventNDOFData ndofdata;
-                                               system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
-                                               system->m_eventManager->
-                                                       pushEvent(new GHOST_EventNDOF(
-                                                               system->getMilliSeconds(), 
-                                                               GHOST_kEventNDOFButton, 
-                                                               window, ndofdata));
-                                       }
-                                       break;
                        }
                }
                else {
@@ -1171,10 +1216,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
 
        if (event) {
                system->pushEvent(event);
+               eventHandled = true;
        }
-       else {
+
+       if (!eventHandled)
                lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
-       }
+
        return lResult;
 }
 
index 729ad56d875a1db27515d88168e82a9ab2072036..c5dff27dace1b98206fb0e28c0d303ed09dd621e 100644 (file)
 #error WIN32 only!
 #endif // WIN32
 
+#define _WIN32_WINNT 0x501 // require Windows XP or newer
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
+#include <ole2.h> // for drag-n-drop
 
 #include "GHOST_System.h"
 
 #      define __int64 long long
 #endif
 
-#ifndef WM_INPUT
-#define WM_INPUT 0x00FF
-#endif 
-#ifndef RID_INPUT
-#define RID_INPUT 0x10000003
-#endif
-#ifndef RIM_INPUTSINK
-#define RIM_INPUTSINK 0x1
-#endif
-#ifndef RI_KEY_BREAK
-#define RI_KEY_BREAK 0x1
-#endif
-#ifndef RI_KEY_E0
-#define RI_KEY_E0 0x2
-#endif
-#ifndef RI_KEY_E1
-#define RI_KEY_E1 0x4
-#endif
-#ifndef RIM_TYPEMOUSE
-#define RIM_TYPEMOUSE          0x0
-#define RIM_TYPEKEYBOARD       0x1
-#define RIM_TYPEHID                    0x2
-
-typedef struct tagRAWINPUTDEVICE {
-       USHORT usUsagePage;
-       USHORT usUsage;
-       DWORD dwFlags;
-       HWND hwndTarget;
-} RAWINPUTDEVICE;
-
-
-
-typedef struct tagRAWINPUTHEADER {
-       DWORD dwType;
-       DWORD dwSize;
-       HANDLE hDevice;
-       WPARAM wParam;
-} RAWINPUTHEADER;
-
-typedef struct tagRAWMOUSE {
-       USHORT usFlags;
-       union {
-               ULONG ulButtons;
-               struct  {
-                       USHORT  usButtonFlags;
-                       USHORT  usButtonData;
-               };
-       };
-       ULONG   ulRawButtons;
-       LONG    lLastX;
-       LONG    lLastY;
-       ULONG   ulExtraInformation;
-} RAWMOUSE;
-
-typedef struct tagRAWKEYBOARD {
-       USHORT  MakeCode;
-       USHORT  Flags;
-       USHORT  Reserved;
-       USHORT  VKey;
-       UINT    Message;
-       ULONG   ExtraInformation;
-} RAWKEYBOARD;
-
-typedef struct tagRAWHID {
-       DWORD   dwSizeHid;
-       DWORD   dwCount;
-       BYTE    bRawData[1];
-} RAWHID;
-
-typedef struct tagRAWINPUT {
-       RAWINPUTHEADER header;
-       union {
-               RAWMOUSE        mouse;
-               RAWKEYBOARD keyboard;
-               RAWHID      hid;
-       } data;
-} RAWINPUT;
-
-DECLARE_HANDLE(HRAWINPUT);
-#endif
-
-#ifdef FREE_WINDOWS
-#define NEED_RAW_PROC
-typedef BOOL (WINAPI * LPFNDLLRRID)(RAWINPUTDEVICE*,UINT, UINT);
-
-typedef UINT (WINAPI * LPFNDLLGRID)(HRAWINPUT, UINT, LPVOID, PUINT, UINT);
-#define GetRawInputData(hRawInput, uiCommand, pData, pcbSize, cbSizeHeader) ((pGetRawInputData)?pGetRawInputData(hRawInput, uiCommand, pData, pcbSize, cbSizeHeader):(UINT)-1)
-#endif
-
 class GHOST_EventButton;
 class GHOST_EventCursor;
 class GHOST_EventKey;
@@ -314,14 +228,13 @@ protected:
 
        /**
         * Catches raw WIN32 key codes from WM_INPUT in the wndproc.
-        * @param window->      The window for this handling
-        * @param wParam        The wParam from the wndproc
-        * @param lParam        The lParam from the wndproc
+        * @param window        The window for this handling
+        * @param raw           RawInput structure with detailed info about the key event
         * @param keyDown       Pointer flag that specify if a key is down
         * @param vk            Pointer to virtual key
         * @return The GHOST key (GHOST_kKeyUnknown if no match).
         */
-       virtual GHOST_TKey hardKey(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam, int * keyDown, char * vk);
+       virtual GHOST_TKey hardKey(GHOST_IWindow *window, RAWINPUT const& raw, int * keyDown, char * vk);
 
        /**
         * Creates modifier key event(s) and updates the key data stored locally (m_modifierKeys).
@@ -362,10 +275,9 @@ protected:
         * In most cases this is a straightforward conversion of key codes.
         * For the modifier keys however, we want to distinguish left and right keys.
         * @param window        The window receiving the event (the active window).
-        * @param wParam        The wParam from the wndproc
-        * @param lParam        The lParam from the wndproc
+        * @param raw           RawInput structure with detailed info about the key event
         */
-       static GHOST_EventKey* processKeyEvent(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam);
+       static GHOST_EventKey* processKeyEvent(GHOST_IWindow *window, RAWINPUT const& raw);
 
        /**
         * Process special keys (VK_OEM_*), to see if current key layout
@@ -383,12 +295,22 @@ protected:
         * @return The event created.
         */
        static GHOST_Event* processWindowEvent(GHOST_TEventType type, GHOST_IWindow* window);
-       /** 
+
+       /**
         * Handles minimum window size.
         * @param minmax        The MINMAXINFO structure.
         */
        static void processMinMaxInfo(MINMAXINFO * minmax);
-       
+
+       /**
+        * Handles Motion and Button events from a SpaceNavigator or related device.
+        * Instead of returning an event object, this function communicates directly
+        * with the GHOST_NDOFManager.
+        * @param raw           RawInput structure with detailed info about the NDOF event
+        * @return Whether an event was generated and sent.
+        */
+       bool processNDOF(RAWINPUT const& raw);
+
        /**
         * Returns the local state of the modifier keys (from the message queue).
         * @param keys The state of the keys.
@@ -412,11 +334,6 @@ protected:
         */
        static LRESULT WINAPI s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
 
-       /**
-        * Initiates WM_INPUT messages from keyboard 
-        */
-       GHOST_TInt32 initKeyboardRawInput(void);
-
        /**
  * Toggles console
  * @action     0 - Hides
@@ -445,15 +362,6 @@ protected:
 
        /** Console status */
        int m_consoleStatus;
-
-       /** handle for user32.dll*/
-       HMODULE user32;
-       #ifdef NEED_RAW_PROC
-       /* pointer to RegisterRawInputDevices function */
-       LPFNDLLRRID pRegisterRawInputDevices;
-       /* pointer to GetRawInputData function */
-       LPFNDLLGRID pGetRawInputData;
-       #endif
 };
 
 inline void GHOST_SystemWin32::retrieveModifierKeys(GHOST_ModifierKeys& keys) const
@@ -487,4 +395,3 @@ inline void GHOST_SystemWin32::handleKeyboardChange(void)
        }
 }
 #endif // _GHOST_SYSTEM_WIN32_H_
-
index dd296fa979c5e3a78a00f80d48fffd026ca532cf..517c3a1ebac2c285b99740f264fcf03a1649f2f5 100644 (file)
@@ -42,8 +42,7 @@
 #include "GHOST_EventKey.h"
 #include "GHOST_EventButton.h"
 #include "GHOST_EventWheel.h"
-#include "GHOST_EventNDOF.h"
-#include "GHOST_NDOFManager.h"
+#include "GHOST_NDOFManagerX11.h"
 #include "GHOST_DisplayManagerX11.h"
 
 #include "GHOST_Debug.h"
 static GHOST_TKey
 convertXKey(KeySym key);
 
-typedef struct NDOFPlatformInfo {
-       Display *display;
-       Window window;
-       volatile GHOST_TEventNDOFData *currValues;
-       Atom cmdAtom;
-       Atom motionAtom;
-       Atom btnPressAtom;
-       Atom btnRelAtom;
-} NDOFPlatformInfo;
-
-static NDOFPlatformInfo sNdofInfo = {NULL, 0, NULL, 0, 0, 0, 0};
-
-
 //these are for copy and select copy
 static char *txt_cut_buffer= NULL;
 static char *txt_select_buffer= NULL;
@@ -181,6 +167,7 @@ init(
        GHOST_TSuccess success = GHOST_System::init();
 
        if (success) {
+               m_ndofManager = new GHOST_NDOFManagerX11(*this);
                m_displayManager = new GHOST_DisplayManagerX11(this);
 
                if (m_displayManager) {
@@ -275,7 +262,7 @@ createWindow(
                if (window->getValid()) {
                        // Store the pointer to the window 
                        m_windowManager->addWindow(window);
-                       
+                       m_windowManager->setActiveWindow(window);
                        pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
                }
                else {
@@ -386,8 +373,6 @@ lastEventTime(Time default_time) {
     return data.timestamp;
 }
 
-
-
        bool 
 GHOST_SystemX11::
 processEvents(
@@ -428,6 +413,11 @@ processEvents(
                if (generateWindowExposeEvents()) {
                        anyProcessed = true;
                }
+
+               if (dynamic_cast<GHOST_NDOFManagerX11*>(m_ndofManager)->processEvents()) {
+                       anyProcessed = true;
+               }
+               
        } while (waitForEvent && !anyProcessed);
        
        return anyProcessed;
@@ -611,6 +601,9 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                case FocusOut:
                {
                        XFocusChangeEvent &xfe = xe->xfocus;
+
+                       // TODO: make sure this is the correct place for activate/deactivate
+                       // printf("X: focus %s for window %d\n", xfe.type == FocusIn ? "in" : "out", (int) xfe.window);
                
                        // May have to look at the type of event and filter some
                        // out.
@@ -641,32 +634,8 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                                );
                        } else 
 #endif
-                       if (sNdofInfo.currValues) {
-                               static GHOST_TEventNDOFData data = {0,0,0,0,0,0,0,0,0,0,0};
-                               if (xcme.message_type == sNdofInfo.motionAtom)
-                               {
-                                       data.changed = 1;
-                                       data.delta = xcme.data.s[8] - data.time;
-                                       data.time = xcme.data.s[8];
-                                       data.tx = xcme.data.s[2] >> 2;
-                                       data.ty = xcme.data.s[3] >> 2;
-                                       data.tz = xcme.data.s[4] >> 2;
-                                       data.rx = xcme.data.s[5];
-                                       data.ry = xcme.data.s[6];
-                                       data.rz =-xcme.data.s[7];
-                                       g_event = new GHOST_EventNDOF(getMilliSeconds(),
-                                                                     GHOST_kEventNDOFMotion,
-                                                                     window, data);
-                               } else if (xcme.message_type == sNdofInfo.btnPressAtom) {
-                                       data.changed = 2;
-                                       data.delta = xcme.data.s[8] - data.time;
-                                       data.time = xcme.data.s[8];
-                                       data.buttons = xcme.data.s[2];
-                                       g_event = new GHOST_EventNDOF(getMilliSeconds(),
-                                                                     GHOST_kEventNDOFButton,
-                                                                     window, data);
-                               }
-                       } else if (((Atom)xcme.data.l[0]) == m_wm_take_focus) {
+
+                       if (((Atom)xcme.data.l[0]) == m_wm_take_focus) {
                                XWindowAttributes attr;
                                Window fwin;
                                int revert_to;
@@ -723,6 +692,14 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                                        xce.y_root
                                );
                        }
+
+                       // printf("X: %s window %d\n", xce.type == EnterNotify ? "entering" : "leaving", (int) xce.window);
+
+                       if (xce.type == EnterNotify)
+                               m_windowManager->setActiveWindow(window);
+                       else
+                               m_windowManager->setWindowInactive(window);
+
                        break;
                }
                case MapNotify:
@@ -834,6 +811,8 @@ GHOST_SystemX11::processEvent(XEvent *xe)
        }
 }
 
+#if 0 // obsolete SpaceNav code
+
        void *
 GHOST_SystemX11::
 prepareNdofInfo(volatile GHOST_TEventNDOFData *currentNdofValues)
@@ -846,6 +825,8 @@ prepareNdofInfo(volatile GHOST_TEventNDOFData *currentNdofValues)
        return (void*)&sNdofInfo;
 }
 
+#endif
+
        GHOST_TSuccess 
 GHOST_SystemX11::
 getModifierKeys(
index 746cd4ebdf4d749c6143e19d97d3d86196a68d90..845243f92e523a72bf575d49c026b666a14615ae 100644 (file)
@@ -203,11 +203,6 @@ public:
                return m_display;
        }       
 
-               void *
-       prepareNdofInfo(
-               volatile GHOST_TEventNDOFData *current_values
-       );
-
        /* Helped function for get data from the clipboard. */
        void getClipboard_xcout(XEvent evt, Atom sel, Atom target,
                         unsigned char **txt, unsigned long *len,
index ef9ebdf5860dad04869180e9ab1b2f67d2d1981b..eddff8bb91b2359115365f47eb65d3acafa363b8 100644 (file)
@@ -3,20 +3,16 @@
  */
 #ifndef GHOST_TASKBARWIN32_H_
 #define GHOST_TASKBARWIN32_H_
+
 #ifndef WIN32
 #error WIN32 only!
 #endif // WIN32
 
+#define _WIN32_WINNT 0x501 // require Windows XP or newer
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <shlobj.h>
 
-/* MinGW needs it */
-#ifdef FREE_WINDOWS
-#ifdef WINVER
-#undef WINVER
-#endif
-#define WINVER 0x0501
-#endif /* FREE_WINDOWS */
 
 // ITaskbarList, ITaskbarList2 and ITaskbarList3 might be missing, present here in that case.
 // Note, ITaskbarList3 is supported only since Windows 7, though. Check for that is done in
index 4055c3acf56ed1e4e99a38ac836bfec982e12d56..70914d9d2efd4072f22558c086aa7ee108299993 100644 (file)
 #endif // WIN32
 
 #include "GHOST_Window.h"
+#include "GHOST_TaskbarWin32.h"
 
-/* MinGW needs it */
-#ifdef FREE_WINDOWS
-#ifdef WINVER
-#undef WINVER
-#endif
-#define WINVER 0x0501
-#endif
-
-
-
+#define _WIN32_WINNT 0x501 // require Windows XP or newer
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-#include "GHOST_TaskbarWin32.h"
 
 
 #include <wintab.h>
index 576709c6072aca4346f2b00072f1652ce816c032..933579f5c1ea93dec5e2e1f47d86a99cea24c94a 100644 (file)
@@ -817,12 +817,9 @@ class USERPREF_PT_input(bpy.types.Panel, InputKeyMapPanel):
         #sub.prop(view, "wheel_scroll_lines", text="Scroll Lines")
 
         col.separator()
-        ''' not implemented yet
         sub = col.column()
         sub.label(text="NDOF Device:")
-        sub.prop(inputs, "ndof_pan_speed", text="Pan Speed")
-        sub.prop(inputs, "ndof_rotate_speed", text="Orbit Speed")
-        '''
+        sub.prop(inputs, "ndof_sensitivity", text="NDOF Sensitivity")
 
         row.separator()
 
index 85764c5530481807d0b46e6bd60ca900487df530..8faf1afab6313098edaa54d9ba4bc965870a1da8 100644 (file)
@@ -271,6 +271,8 @@ class InputKeyMapPanel:
             row.prop(kmi, "type", text="", full_event=True)
         elif map_type == 'MOUSE':
             row.prop(kmi, "type", text="", full_event=True)
+        if map_type == 'NDOF':
+            row.prop(kmi, "type", text="", full_event=True)
         elif map_type == 'TWEAK':
             subrow = row.row()
             subrow.prop(kmi, "type", text="")
@@ -306,7 +308,7 @@ class InputKeyMapPanel:
                 sub = split.column()
                 subrow = sub.row(align=True)
 
-                if map_type == 'KEYBOARD':
+                if map_type in ('KEYBOARD', 'NDOF'):
                     subrow.prop(kmi, "type", text="", event=True)
                     subrow.prop(kmi, "value", text="")
                 elif map_type == 'MOUSE':
index 083c330f61da3cb2b8bf072daed9419671fd18ef..f96a758e7cefa3e5ded37fc838c811bcf1975862 100644 (file)
@@ -349,6 +349,30 @@ class VIEW3D_MT_view_navigation(bpy.types.Menu):
         layout.operator("view3d.fly")
 
 
+class VIEW3D_MT_ndof_settings(bpy.types.Menu):
+    bl_label = "3D Mouse Settings"
+
+    def draw(self, context):
+        layout = self.layout
+        input_prefs = context.user_preferences.inputs
+
+        layout.separator()
+        layout.prop(input_prefs, "ndof_sensitivity")
+
+        if context.space_data.type == 'VIEW_3D':
+            layout.separator()
+            layout.prop(input_prefs, "ndof_show_guide")
+
+            layout.separator()
+            layout.label(text="orbit options")
+            layout.prop(input_prefs, "ndof_orbit_invert_axes")
+
+            layout.separator()
+            layout.label(text="fly options")
+            layout.prop(input_prefs, "ndof_fly_helicopter", icon='NDOF_FLY')
+            layout.prop(input_prefs, "ndof_lock_horizon", icon='NDOF_DOM')
+
+
 class VIEW3D_MT_view_align(bpy.types.Menu):
     bl_label = "Align View"
 
index d21b0428d7656eced4f31b47700db0174fd07330..17876c6ec9d3faea372b1ce8a89063bcad1f7601 100644 (file)
@@ -92,9 +92,6 @@ typedef struct Global {
     
        /* save the allowed windowstate of blender when using -W or -w */
        int windowstate;
-
-       /* ndof device found ? */
-       int ndofdevice;
 } Global;
 
 /* **************** GLOBAL ********************* */
@@ -174,5 +171,3 @@ extern Global G;
 #endif
        
 #endif
-
-
index f4da734473d9d9d1355f64cb2d8686072056cab9..28a54b20277396c13f464d12d46e0e46277794a5 100644 (file)
@@ -1617,6 +1617,18 @@ static int gpencil_draw_modal (bContext *C, wmOperator *op, wmEvent *event)
        tGPsdata *p= op->customdata;
        int estate = OPERATOR_PASS_THROUGH; /* default exit state - not handled, so let others have a share of the pie */
        
+       // if (event->type == NDOF_MOTION)
+       //      return OPERATOR_PASS_THROUGH;
+       // -------------------------------
+       // [mce] Not quite what I was looking
+       // for, but a good start! GP continues to
+       // draw on the screen while the 3D mouse
+       // moves the viewpoint. Problem is that
+       // the stroke is converted to 3D only after
+       // it is finished. This approach should work
+       // better in tools that immediately apply
+       // in 3D space.
+
        //printf("\tGP - handle modal event...\n");
        
        /* exit painting mode (and/or end current stroke) */
index 2b4003c7af055463b2f8285cddc0ac5992a8231c..16f3789ecb1757cd9c6c5ecdd91f2ef3347942fc 100644 (file)
@@ -1585,6 +1585,12 @@ void init_userdef_do_versions(void)
        if (U.anisotropic_filter <= 0)
                U.anisotropic_filter = 1;
 
+       if (U.ndof_sensitivity == 0.0f) {
+               U.ndof_sensitivity = 1.0f;
+               U.ndof_flag = NDOF_SHOW_GUIDE | NDOF_LOCK_HORIZON |
+                       NDOF_SHOULD_PAN | NDOF_SHOULD_ZOOM | NDOF_SHOULD_ROTATE;
+       }
+
        /* funny name, but it is GE stuff, moves userdef stuff to engine */
 // XXX space_set_commmandline_options();
        /* this timer uses U */
index 7ddf5dff000b62c375eab6f396111edc1d771057..09873566d4a91be076ce839fc21de5f57468c89e 100644 (file)
@@ -832,6 +832,13 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
        float mouse[2];
        int first= 0;
 
+       // let NDOF motion pass through to the 3D view so we can paint and rotate simultaneously!
+       // this isn't perfect... even when an extra MOUSEMOVE is spoofed, the stroke discards it
+       // since the 2D deltas are zero -- code in this file needs to be updated to use the
+       // post-NDOF_MOTION MOUSEMOVE
+       if (event->type == NDOF_MOTION)
+               return OPERATOR_PASS_THROUGH;
+
        if(!stroke->stroke_started) {
                stroke->last_mouse_position[0] = event->x;
                stroke->last_mouse_position[1] = event->y;
index e9e77ddf430d77f3defae1b0370439f57422f572..399157da85cf921d700c7fc9aca7699994389301 100644 (file)
@@ -73,6 +73,7 @@ void IMAGE_OT_view_zoom(struct wmOperatorType *ot);
 void IMAGE_OT_view_zoom_in(struct wmOperatorType *ot);
 void IMAGE_OT_view_zoom_out(struct wmOperatorType *ot);
 void IMAGE_OT_view_zoom_ratio(struct wmOperatorType *ot);
+void IMAGE_OT_view_ndof(struct wmOperatorType *ot);
 
 void IMAGE_OT_new(struct wmOperatorType *ot);
 void IMAGE_OT_open(struct wmOperatorType *ot);
index d5515bd1cf85ae10658a06da04c0e664e2170a57..204d5dfb1b120e22dc29cb7b0d166603eeff4df1 100644 (file)
@@ -437,6 +437,60 @@ void IMAGE_OT_view_zoom(wmOperatorType *ot)
                "Factor", "Zoom factor, values higher than 1.0 zoom in, lower values zoom out.", -FLT_MAX, FLT_MAX);
 }
 
+/********************** NDOF operator *********************/
+
+/* Combined pan/zoom from a 3D mouse device.
+ * Z zooms, XY pans
+ * "view" (not "paper") control -- user moves the viewpoint, not the image being viewed
+ * that explains the negative signs in the code below
+ */
+
+static int view_ndof_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       SpaceImage *sima= CTX_wm_space_image(C);
+       ARegion *ar= CTX_wm_region(C);
+
+       wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
+
+       float dt = ndof->dt > 0.25f ? 0.0125f : ndof->dt;
+       /* this is probably the first event for this motion, so set dt to something reasonable
+        * TODO: replace such guesswork with a flag or field from the NDOF manager
+        */
+
+       /* tune these until it feels right */
+       const float zoom_sensitivity = 0.5f;
+       const float pan_sensitivity = 300.f;
+
+       float pan_x = pan_sensitivity * dt * ndof->tx / sima->zoom;
+       float pan_y = pan_sensitivity * dt * ndof->ty / sima->zoom;
+
+       /* "mouse zoom" factor = 1 + (dx + dy) / 300
+        * what about "ndof zoom" factor? should behave like this:
+        * at rest -> factor = 1
+        * move forward -> factor > 1
+        * move backward -> factor < 1
+        */
+       float zoom_factor = 1.f + zoom_sensitivity * dt * -ndof->tz;
+
+       sima_zoom_set_factor(sima, ar, zoom_factor);
+       sima->xof += pan_x;
+       sima->yof += pan_y;
+
+       ED_region_tag_redraw(ar);       
+
+       return OPERATOR_FINISHED;
+}
+
+void IMAGE_OT_view_ndof(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "NDOF Pan/Zoom";
+       ot->idname= "IMAGE_OT_view_ndof";
+       
+       /* api callbacks */
+       ot->invoke= view_ndof_invoke;
+}
+
 /********************** view all operator *********************/
 
 /* Updates the fields of the View2D member of the SpaceImage struct.
index 2e9544f5d20f2b7c212d5ca8e93532122a69d0d0..afab4ede229b54e3935573069ce432815f577fc5 100644 (file)
@@ -469,6 +469,7 @@ static void image_operatortypes(void)
        WM_operatortype_append(IMAGE_OT_view_zoom_in);
        WM_operatortype_append(IMAGE_OT_view_zoom_out);
        WM_operatortype_append(IMAGE_OT_view_zoom_ratio);
+       WM_operatortype_append(IMAGE_OT_view_ndof);
 
        WM_operatortype_append(IMAGE_OT_new);
        WM_operatortype_append(IMAGE_OT_open);
@@ -518,6 +519,9 @@ static void image_keymap(struct wmKeyConfig *keyconf)
        WM_keymap_add_item(keymap, "IMAGE_OT_view_pan", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0);
        WM_keymap_add_item(keymap, "IMAGE_OT_view_pan", MOUSEPAN, 0, 0, 0);
 
+       WM_keymap_add_item(keymap, "IMAGE_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); // or view selected?
+       WM_keymap_add_item(keymap, "IMAGE_OT_view_ndof", NDOF_MOTION, 0, 0, 0);
+
        WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_in", WHEELINMOUSE, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_out", WHEELOUTMOUSE, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
index d2ff6eef09798234ffd8366455385d1f69b9a5bc..6e3f6549ba3c1268e3df8ca3751c8cd180332832 100644 (file)
@@ -675,6 +675,104 @@ static void draw_view_axis(RegionView3D *rv3d)
        glDisable(GL_BLEND);
 }
 
+/* draw center and axis of rotation for ongoing 3D mouse navigation */
+static void draw_rotation_guide(RegionView3D *rv3d)
+{
+       float o[3]; // center of rotation
+       float end[3]; // endpoints for drawing
+
+       float color[4] = {0.f ,0.4235f, 1.f, 1.f}; // bright blue so it matches device LEDs
+
+       negate_v3_v3(o, rv3d->ofs);
+
+       glEnable(GL_BLEND);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       glShadeModel(GL_SMOOTH);
+       glPointSize(5);
+       glEnable(GL_POINT_SMOOTH);
+       glDepthMask(0); // don't overwrite zbuf
+
+       if (rv3d->rot_angle != 0.f) {
+               // -- draw rotation axis --
+               float scaled_axis[3];
+               const float scale = rv3d->dist;
+               mul_v3_v3fl(scaled_axis, rv3d->rot_axis, scale);
+       
+               glBegin(GL_LINE_STRIP);
+                       color[3] = 0.f; // more transparent toward the ends
+                       glColor4fv(color);
+                       add_v3_v3v3(end, o, scaled_axis);
+                       glVertex3fv(end);
+       
+                       // color[3] = 0.2f + fabsf(rv3d->rot_angle); // modulate opacity with angle
+                       // ^^ neat idea, but angle is frame-rate dependent, so it's usually close to 0.2
+
+                       color[3] = 0.5f; // more opaque toward the center
+                       glColor4fv(color);
+                       glVertex3fv(o);
+       
+                       color[3] = 0.f;
+                       glColor4fv(color);
+                       sub_v3_v3v3(end, o, scaled_axis);
+                       glVertex3fv(end);
+               glEnd();
+               
+               // -- draw ring around rotation center --
+               {
+               #define ROT_AXIS_DETAIL 13
+               const float s = 0.05f * scale;
+               const float step = 2.f * M_PI / ROT_AXIS_DETAIL;
+               float angle;
+               int i;
+
+               float q[4]; // rotate ring so it's perpendicular to axis
+               const int upright = fabsf(rv3d->rot_axis[2]) >= 0.95f;
+               if (!upright)
+                       {
+                       const float up[3] = {0.f, 0.f, 1.f};
+                       float vis_angle, vis_axis[3];
+
+                       cross_v3_v3v3(vis_axis, up, rv3d->rot_axis);
+                       vis_angle = acosf(dot_v3v3(up, rv3d->rot_axis));
+                       axis_angle_to_quat(q, vis_axis, vis_angle);
+                       }
+
+               color[3] = 0.25f; // somewhat faint
+               glColor4fv(color);
+               glBegin(GL_LINE_LOOP);
+               for (i = 0, angle = 0.f; i < ROT_AXIS_DETAIL; ++i, angle += step)
+                       {
+                       float p[3] = { s * cosf(angle), s * sinf(angle), 0.f };
+
+                       if (!upright)
+                               mul_qt_v3(q, p);
+
+                       add_v3_v3(p, o);
+                       glVertex3fv(p);
+                       }
+               glEnd();
+               }
+
+               color[3] = 1.f; // solid dot
+       }
+       else
+               color[3] = 0.5f; // see-through dot
+
+       // -- draw rotation center --
+       glColor4fv(color);
+       glBegin(GL_POINTS);
+               glVertex3fv(o);
+       glEnd();
+
+       // find screen coordinates for rotation center, then draw pretty icon
+       // mul_m4_v3(rv3d->persinv, rot_center);
+       // UI_icon_draw(rot_center[0], rot_center[1], ICON_NDOF_TURN);
+       // ^^ just playing around, does not work
+
+       glDisable(GL_BLEND);
+       glDisable(GL_POINT_SMOOTH);
+       glDepthMask(1);
+}
 
 static void draw_view_icon(RegionView3D *rv3d)
 {
@@ -2618,6 +2716,10 @@ void view3d_main_area_draw(const bContext *C, ARegion *ar)
                BDR_drawSketch(C);
        }
 
+       if ((U.ndof_flag & NDOF_SHOW_GUIDE) && (rv3d->viewlock != RV3D_LOCKED) && (rv3d->persp != RV3D_CAMOB))
+               // TODO: draw something else (but not this) during fly mode
+               draw_rotation_guide(rv3d);
+
        ED_region_pixelspace(ar);
        
 //     retopo_paint_view_update(v3d);
index d563c07baf3a196c5b68d959b2c8df7c302eb0ba..94224698063c6d99b94126ef53bac01e38402f91 100644 (file)
@@ -928,6 +928,269 @@ void VIEW3D_OT_rotate(wmOperatorType *ot)
        ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
 }
 
+// NDOF utility functions
+// (should these functions live in this file?)
+float ndof_to_angle_axis(struct wmNDOFMotionData* ndof, float axis[3])
+       {
+       const float x = ndof->rx;
+       const float y = ndof->ry;
+       const float z = ndof->rz;
+
+       float angular_velocity = sqrtf(x*x + y*y + z*z);
+       float angle = ndof->dt * angular_velocity;
+
+       float scale = 1.f / angular_velocity;
+
+       // normalize 
+       axis[0] = scale * x;
+       axis[1] = scale * y;
+       axis[2] = scale * z;
+
+       return angle;
+       }
+
+void ndof_to_quat(struct wmNDOFMotionData* ndof, float q[4])
+       {
+       const float x = ndof->rx;
+       const float y = ndof->ry;
+       const float z = ndof->rz;
+
+       float angular_velocity = sqrtf(x*x + y*y + z*z);
+       float angle = ndof->dt * angular_velocity;
+
+       // combined scaling factor -- normalize axis while converting to quaternion
+       float scale = sin(0.5f * angle) / angular_velocity;
+
+       // convert axis-angle to quaternion
+       q[0] = cos(0.5f * angle);
+       q[1] = scale * x;
+       q[2] = scale * y;
+       q[3] = scale * z;
+       }
+
+static int ndof_orbit_invoke(bContext *C, wmOperator *op, wmEvent *event)
+// -- "orbit" navigation (trackball/turntable)
+// -- zooming
+// -- panning in rotationally-locked views
+{
+       RegionView3D* rv3d = CTX_wm_region_view3d(C);
+       wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
+
+       rv3d->rot_angle = 0.f; // off by default, until changed later this function
+
+       if (ndof->progress != P_FINISHING) {
+               const float dt = ndof->dt;
+       
+               // tune these until everything feels right
+               const float rot_sensitivity = 1.f;
+               const float zoom_sensitivity = 1.f;
+               const float pan_sensitivity = 1.f;
+       
+               // rather have bool, but...
+               int has_rotation = rv3d->viewlock != RV3D_LOCKED && (ndof->rx || ndof->ry || ndof->rz);
+       
+               float view_inv[4];
+               invert_qt_qt(view_inv, rv3d->viewquat);
+       
+               //#define DEBUG_NDOF_MOTION
+               #ifdef DEBUG_NDOF_MOTION
+               printf("ndof: T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f) dt=%.3f delivered to 3D view\n",
+                       ndof->tx, ndof->ty, ndof->tz, ndof->rx, ndof->ry, ndof->rz, ndof->dt);
+               #endif
+       
+               if (ndof->tz) {
+                       // Zoom!
+                       // velocity should be proportional to the linear velocity attained by rotational motion of same strength
+                       // [got that?]
+                       // proportional to arclength = radius * angle
+       
+                       float zoom_distance = zoom_sensitivity * rv3d->dist * dt * ndof->tz;
+                       rv3d->dist += zoom_distance;
+               }
+       
+               if (rv3d->viewlock == RV3D_LOCKED) {
+                       /* rotation not allowed -- explore panning options instead */
+                       float pan_vec[3] = {ndof->tx, ndof->ty, 0};
+                       mul_v3_fl(pan_vec, pan_sensitivity * rv3d->dist * dt);
+       
+                       /* transform motion from view to world coordinates */
+                       invert_qt_qt(view_inv, rv3d->viewquat);
+                       mul_qt_v3(view_inv, pan_vec);
+       
+                       /* move center of view opposite of hand motion (this is camera mode, not object mode) */
+                       sub_v3_v3(rv3d->ofs, pan_vec);
+               }
+       
+               if (has_rotation) {
+       
+                       const int invert = U.ndof_flag & NDOF_ORBIT_INVERT_AXES;
+       
+                       rv3d->view = RV3D_VIEW_USER;
+       
+                       if (U.flag & USER_TRACKBALL) {
+                               float rot[4];
+       #if 0   // -------------------------- Mike's nifty original version
+                               float view_inv_conj[4];
+       
+                               ndof_to_quat(ndof, rot);
+                               // mul_qt_fl(rot, rot_sensitivity);
+                               // ^^ no apparent effect
+       
+                               if (invert)
+                                       invert_qt(rot);
+       
+                               copy_qt_qt(view_inv_conj, view_inv);
+                               conjugate_qt(view_inv_conj);
+       
+                               // transform rotation from view to world coordinates
+                               mul_qt_qtqt(rot, view_inv, rot);
+                               mul_qt_qtqt(rot, rot, view_inv_conj);
+       #else   // ---------------------------------------- Mike's revised version
+                               float axis[3];
+                               float angle = rot_sensitivity * ndof_to_angle_axis(ndof, axis);
+       
+                               if (invert)
+                                       angle = -angle;
+       
+                               // transform rotation axis from view to world coordinates
+                               mul_qt_v3(view_inv, axis);
+       
+                               // update the onscreen doo-dad
+                               rv3d->rot_angle = angle;
+                               copy_v3_v3(rv3d->rot_axis, axis);
+       
+                               axis_angle_to_quat(rot, axis, angle);
+       #endif  // --------------------------------------------
+                               // apply rotation
+                               mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
+                       } else {
+                               /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */
+                               float angle, rot[4];
+                               float xvec[3] = {1,0,0};
+       
+                               /* Determine the direction of the x vector (for rotating up and down) */
+                               mul_qt_v3(view_inv, xvec);
+       
+                               /* Perform the up/down rotation */
+                               angle = rot_sensitivity * dt * ndof->rx;
+                               if (invert)
+                                       angle = -angle;
+                               rot[0] = cos(angle);
+                               mul_v3_v3fl(rot+1, xvec, sin(angle));
+                               mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
+       
+                               /* Perform the orbital rotation */
+                               angle = rot_sensitivity * dt * ndof->ry;
+                               if (invert)
+                                       angle = -angle;
+       
+                               // update the onscreen doo-dad
+                               rv3d->rot_angle = angle;
+                               rv3d->rot_axis[0] = 0;
+                               rv3d->rot_axis[1] = 0;
+                               rv3d->rot_axis[2] = 1;
+       
+                               rot[0] = cos(angle);
+                               rot[1] = rot[2] = 0.0;
+                               rot[3] = sin(angle);
+                               mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
+                       }
+               }
+       }
+
+       ED_region_tag_redraw(CTX_wm_region(C));
+
+       return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "NDOF Orbit View";
+       ot->description = "Explore every angle of an object using the 3D mouse.";
+       ot->idname = "VIEW3D_OT_ndof_orbit";
+
+       /* api callbacks */
+       ot->invoke = ndof_orbit_invoke;
+       ot->poll = ED_operator_view3d_active;
+
+       /* flags */
+       ot->flag = 0;
+}
+
+static int ndof_pan_invoke(bContext *C, wmOperator *op, wmEvent *event)
+// -- "pan" navigation
+// -- zoom or dolly?
+{
+       RegionView3D* rv3d = CTX_wm_region_view3d(C);
+       wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
+
+       rv3d->rot_angle = 0.f; // we're panning here! so erase any leftover rotation from other operators
+
+       if (ndof->progress != P_FINISHING) {
+               const float dt = ndof->dt;
+               float view_inv[4];
+#if 0 // ------------------------------------------- zoom with Z
+               // tune these until everything feels right
+               const float zoom_sensitivity = 1.f;
+               const float pan_sensitivity = 1.f;
+
+               float pan_vec[3] = {
+                       ndof->tx, ndof->ty, 0
+                       };
+
+               // "zoom in" or "translate"? depends on zoom mode in user settings?
+               if (ndof->tz) {
+                       float zoom_distance = zoom_sensitivity * rv3d->dist * dt * ndof->tz;
+                       rv3d->dist += zoom_distance;
+               }
+       
+               mul_v3_fl(pan_vec, pan_sensitivity * rv3d->dist * dt);
+#else // ------------------------------------------------------- dolly with Z
+               float speed = 10.f; // blender units per second
+               // ^^ this is ok for default cube scene, but should scale with.. something
+
+               // tune these until everything feels right
+               const float forward_sensitivity = 1.f;
+               const float vertical_sensitivity = 0.4f;
+               const float lateral_sensitivity = 0.6f;
+
+               float pan_vec[3] = {
+                       lateral_sensitivity * ndof->tx,
+                       vertical_sensitivity * ndof->ty,
+                       forward_sensitivity * ndof->tz
+                       };
+
+               mul_v3_fl(pan_vec, speed * dt);
+#endif
+               /* transform motion from view to world coordinates */
+               invert_qt_qt(view_inv, rv3d->viewquat);
+               mul_qt_v3(view_inv, pan_vec);
+
+               /* move center of view opposite of hand motion (this is camera mode, not object mode) */
+               sub_v3_v3(rv3d->ofs, pan_vec);
+       }
+
+       ED_region_tag_redraw(CTX_wm_region(C));
+
+       return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "NDOF Pan View";
+       ot->description = "Position your viewpoint with the 3D mouse.";
+       ot->idname = "VIEW3D_OT_ndof_pan";
+
+       /* api callbacks */
+       ot->invoke = ndof_pan_invoke;
+       ot->poll = ED_operator_view3d_active;
+
+       /* flags */
+       ot->flag = 0;
+}
+
 /* ************************ viewmove ******************************** */
 
 
@@ -3195,398 +3458,6 @@ int ED_view3d_autodist_depth_seg(struct ARegion *ar, const int mval_sta[2], cons
        return (*depth==FLT_MAX) ? 0:1;
 }
 
-/* ********************* NDOF ************************ */
-/* note: this code is confusing and unclear... (ton) */
-/* **************************************************** */
-
-// ndof scaling will be moved to user setting.
-// In the mean time this is just a place holder.
-
-// Note: scaling in the plugin and ghostwinlay.c
-// should be removed. With driver default setting,
-// each axis returns approx. +-200 max deflection.
-
-// The values I selected are based on the older
-// polling i/f. With event i/f, the sensistivity
-// can be increased for improved response from
-// small deflections of the device input.
-
-
-// lukep notes : i disagree on the range.
-// the normal 3Dconnection driver give +/-400
-// on defaut range in other applications
-// and up to +/- 1000 if set to maximum
-// because i remove the scaling by delta,
-// which was a bad idea as it depend of the system
-// speed and os, i changed the scaling values, but
-// those are still not ok
-
-#if 0
-static float ndof_axis_scale[6] = {
-       +0.01,  // Tx
-       +0.01,  // Tz
-       +0.01,  // Ty
-       +0.0015,        // Rx
-       +0.0015,        // Rz
-       +0.0015 // Ry
-};
-
-static void filterNDOFvalues(float *sbval)
-{
-       int i=0;
-       float max  = 0.0;
-
-       for (i =0; i<6;i++)
-               if (fabs(sbval[i]) > max)
-                       max = fabs(sbval[i]);
-       for (i =0; i<6;i++)
-               if (fabs(sbval[i]) != max )
-                       sbval[i]=0.0;
-}
-
-// statics for controlling rv3d->dist corrections.
-// viewmoveNDOF zeros and adjusts rv3d->ofs.
-// viewmove restores based on dz_flag state.
-
-int dz_flag = 0;
-float m_dist;
-
-void viewmoveNDOFfly(ARegion *ar, View3D *v3d, int UNUSED(mode))
-{
-       RegionView3D *rv3d= ar->regiondata;
-       int i;
-       float phi;
-       float dval[7];
-       // static fval[6] for low pass filter; device input vector is dval[6]
-       static float fval[6];
-       float tvec[3],rvec[3];
-       float q1[4];
-       float mat[3][3];
-       float upvec[3];
-
-
-       /*----------------------------------------------------
-        * sometimes this routine is called from headerbuttons
-        * viewmove needs to refresh the screen
-        */
-// XXX areawinset(ar->win);
-
-
-       // fetch the current state of the ndof device
-// XXX getndof(dval);
-
-       if (v3d->ndoffilter)
-               filterNDOFvalues(fval);
-
-       // Scale input values
-
-//     if(dval[6] == 0) return; // guard against divide by zero
-
-       for(i=0;i<6;i++) {
-
-               // user scaling
-               dval[i] = dval[i] * ndof_axis_scale[i];
-       }
-
-
-       // low pass filter with zero crossing reset
-
-       for(i=0;i<6;i++) {
-               if((dval[i] * fval[i]) >= 0)
-                       dval[i] = (fval[i] * 15 + dval[i]) / 16;
-               else
-                       fval[i] = 0;
-       }
-
-
-       // force perspective mode. This is a hack and is
-       // incomplete. It doesn't actually effect the view
-       // until the first draw and doesn't update the menu
-       // to reflect persp mode.
-
-       rv3d->persp = RV3D_PERSP;
-
-
-       // Correct the distance jump if rv3d->dist != 0
-
-       // This is due to a side effect of the original
-       // mouse view rotation code. The rotation point is
-       // set a distance in front of the viewport to
-       // make rotating with the mouse look better.
-       // The distance effect is written at a low level
-       // in the view management instead of the mouse
-       // view function. This means that all other view
-       // movement devices must subtract this from their
-       // view transformations.
-
-       if(rv3d->dist != 0.0) {
-               dz_flag = 1;
-               m_dist = rv3d->dist;
-               upvec[0] = upvec[1] = 0;
-               upvec[2] = rv3d->dist;
-               copy_m3_m4(mat, rv3d->viewinv);
-               mul_m3_v3(mat, upvec);
-               sub_v3_v3(rv3d->ofs, upvec);
-               rv3d->dist = 0.0;
-       }
-
-
-       // Apply rotation
-       // Rotations feel relatively faster than translations only in fly mode, so
-       // we have no choice but to fix that here (not in the plugins)
-       rvec[0] = -0.5 * dval[3];
-       rvec[1] = -0.5 * dval[4];
-       rvec[2] = -0.5 * dval[5];
-
-       // rotate device x and y by view z
-
-       copy_m3_m4(mat, rv3d->viewinv);
-       mat[2][2] = 0.0f;
-       mul_m3_v3(mat, rvec);
-
-       // rotate the view
-
-       phi = normalize_v3(rvec);
-       if(phi != 0) {
-               axis_angle_to_quat(q1,rvec,phi);
-               mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
-       }
-
-
-       // Apply translation
-
-       tvec[0] = dval[0];
-       tvec[1] = dval[1];
-       tvec[2] = -dval[2];
-
-       // the next three lines rotate the x and y translation coordinates
-       // by the current z axis angle
-
-       copy_m3_m4(mat, rv3d->viewinv);
-       mat[2][2] = 0.0f;
-       mul_m3_v3(mat, tvec);
-
-       // translate the view
-
-       sub_v3_v3(rv3d->ofs, tvec);
-
-
-       /*----------------------------------------------------
-        * refresh the screen XXX
-         */
-
-       // update render preview window
-
-// XXX BIF_view3d_previewrender_signal(ar, PR_DBASE|PR_DISPRECT);
-}
-
-void viewmoveNDOF(Scene *scene, ARegion *ar, View3D *v3d, int UNUSED(mode))
-{
-       RegionView3D *rv3d= ar->regiondata;
-       float fval[7];
-       float dvec[3];
-       float sbadjust = 1.0f;
-       float len;
-       short use_sel = 0;
-       Object *ob = OBACT;
-       float m[3][3];
-       float m_inv[3][3];
-       float xvec[3] = {1,0,0};
-       float yvec[3] = {0,-1,0};
-       float zvec[3] = {0,0,1};
-       float phi;
-       float q1[4];
-       float obofs[3];
-       float reverse;
-       //float diff[4];
-       float d, curareaX, curareaY;
-       float mat[3][3];
-       float upvec[3];
-
-       /* Sensitivity will control how fast the view rotates.  The value was
-        * obtained experimentally by tweaking until the author didn't get dizzy watching.
-        * Perhaps this should be a configurable user parameter.
-        */
-       float psens = 0.005f * (float) U.ndof_pan;   /* pan sensitivity */
-       float rsens = 0.005f * (float) U.ndof_rotate;  /* rotate sensitivity */
-       float zsens = 0.3f;   /* zoom sensitivity */
-
-       const float minZoom = -30.0f;
-       const float maxZoom = 300.0f;
-
-       //reset view type
-       rv3d->view = 0;
-//printf("passing here \n");
-//
-       if (scene->obedit==NULL && ob && !(ob->mode & OB_MODE_POSE)) {
-               use_sel = 1;
-       }
-
-       if((dz_flag)||rv3d->dist==0) {
-               dz_flag = 0;
-               rv3d->dist = m_dist;
-               upvec[0] = upvec[1] = 0;
-               upvec[2] = rv3d->dist;
-               copy_m3_m4(mat, rv3d->viewinv);
-               mul_m3_v3(mat, upvec);
-               add_v3_v3(rv3d->ofs, upvec);
-       }
-
-       /*----------------------------------------------------
-        * sometimes this routine is called from headerbuttons
-        * viewmove needs to refresh the screen
-        */
-// XXX areawinset(curarea->win);
-
-       /*----------------------------------------------------
-        * record how much time has passed. clamp at 10 Hz
-        * pretend the previous frame occurred at the clamped time
-        */
-//    now = PIL_check_seconds_timer();
- //   frametime = (now - prevTime);
- //   if (frametime > 0.1f){        /* if more than 1/10s */
- //       frametime = 1.0f/60.0;      /* clamp at 1/60s so no jumps when starting to move */
-//    }
-//    prevTime = now;
- //   sbadjust *= 60 * frametime;             /* normalize ndof device adjustments to 100Hz for framerate independence */
-
-       /* fetch the current state of the ndof device & enforce dominant mode if selected */
-// XXX    getndof(fval);
-       if (v3d->ndoffilter)
-               filterNDOFvalues(fval);
-
-
-       // put scaling back here, was previously in ghostwinlay
-       fval[0] = fval[0] * (1.0f/600.0f);
-       fval[1] = fval[1] * (1.0f/600.0f);
-       fval[2] = fval[2] * (1.0f/1100.0f);
-       fval[3] = fval[3] * 0.00005f;
-       fval[4] =-fval[4] * 0.00005f;
-       fval[5] = fval[5] * 0.00005f;
-       fval[6] = fval[6] / 1000000.0f;
-
-       // scale more if not in perspective mode
-       if (rv3d->persp == RV3D_ORTHO) {
-               fval[0] = fval[0] * 0.05f;
-               fval[1] = fval[1] * 0.05f;
-               fval[2] = fval[2] * 0.05f;
-               fval[3] = fval[3] * 0.9f;
-               fval[4] = fval[4] * 0.9f;
-               fval[5] = fval[5] * 0.9f;
-               zsens *= 8;
-       }
-
-       /* set object offset */
-       if (ob) {
-               obofs[0] = -ob->obmat[3][0];
-               obofs[1] = -ob->obmat[3][1];
-               obofs[2] = -ob->obmat[3][2];
-       }
-       else {
-               copy_v3_v3(obofs, rv3d->ofs);
-       }
-
-       /* calc an adjustment based on distance from camera
-          disabled per patch 14402 */
-        d = 1.0f;
-
-/*    if (ob) {
-               sub_v3_v3v3(diff, obofs, rv3d->ofs);
-               d = len_v3(diff);
-       }
-*/
-
-       reverse = (rv3d->persmat[2][1] < 0.0f) ? -1.0f : 1.0f;
-
-       /*----------------------------------------------------
-        * ndof device pan
-        */
-       psens *= 1.0f + d;
-       curareaX = sbadjust * psens * fval[0];
-       curareaY = sbadjust * psens * fval[1];
-       dvec[0] = curareaX * rv3d->persinv[0][0] + curareaY * rv3d->persinv[1][0];
-       dvec[1] = curareaX * rv3d->persinv[0][1] + curareaY * rv3d->persinv[1][1];
-       dvec[2] = curareaX * rv3d->persinv[0][2] + curareaY * rv3d->persinv[1][2];
-       add_v3_v3(rv3d->ofs, dvec);
-
-       /*----------------------------------------------------
-        * ndof device dolly
-        */
-       len = zsens * sbadjust * fval[2];
-
-       if (rv3d->persp==RV3D_CAMOB) {
-               if(rv3d->persp==RV3D_CAMOB) { /* This is stupid, please fix - TODO */
-                       rv3d->camzoom+= 10.0f * -len;
-               }
-               if (rv3d->camzoom < minZoom) rv3d->camzoom = minZoom;
-               else if (rv3d->camzoom > maxZoom) rv3d->camzoom = maxZoom;
-       }
-       else if ((rv3d->dist> 0.001*v3d->grid) && (rv3d->dist<10.0*v3d->far)) {
-               rv3d->dist*=(1.0 + len);
-       }
-
-
-       /*----------------------------------------------------
-        * ndof device turntable
-        * derived from the turntable code in viewmove
-        */
-
-       /* 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 compuated directly from the quaternion. */
-       mul_m3_v3(m_inv,xvec);
-       mul_m3_v3(m_inv,yvec);
-       mul_m3_v3(m_inv,zvec);
-
-       /* Perform the up/down rotation */
-       phi = sbadjust * rsens * /*0.5f * */ fval[3]; /* spin vertically half as fast as horizontally */
-       q1[0] = cos(phi);
-       mul_v3_v3fl(q1+1, xvec, sin(phi));
-       mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
-
-       if (use_sel) {
-               conjugate_qt(q1); /* conj == inv for unit quat */
-               sub_v3_v3(rv3d->ofs, obofs);
-               mul_qt_v3(q1, rv3d->ofs);
-               add_v3_v3(rv3d->ofs, obofs);
-       }
-
-       /* Perform the orbital rotation */
-       /* Perform the orbital rotation
-          If the seen Up axis is parallel to the zoom axis, rotation should be
-          achieved with a pure Roll motion (no Spin) on the device. When you start
-          to tilt, moving from Top to Side view, Spinning will increasingly become
-          more relevant while the Roll component will decrease. When a full
-          Side view is reached, rotations around the world's Up axis are achieved
-          with a pure Spin-only motion.  In other words the control of the spinning
-          around the world's Up axis should move from the device's Spin axis to the
-          device's Roll axis depending on the orientation of the world's Up axis
-          relative to the screen. */
-       //phi = sbadjust * rsens * reverse * fval[4];  /* spin the knob, y axis */
-       phi = sbadjust * rsens * (yvec[2] * fval[4] + zvec[2] * fval[5]);
-       q1[0] = cos(phi);
-       q1[1] = q1[2] = 0.0;
-       q1[3] = sin(phi);
-       mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
-
-       if (use_sel) {
-               conjugate_qt(q1);
-               sub_v3_v3(rv3d->ofs, obofs);
-               mul_qt_v3(q1, rv3d->ofs);
-               add_v3_v3(rv3d->ofs, obofs);
-       }
-
-       /*----------------------------------------------------
-        * refresh the screen
-        */
-// XXX    scrarea_do_windraw(curarea);
-}
-#endif // if 0, unused NDof code
-
-
 /* Gets the view trasnformation from a camera
 * currently dosnt take camzoom into account
 *
index ed1ed5b388182fad48e37c29b2f712eab52dee44..38d93ab59f36e51bdb22fc92a392d2b3b8d0152b 100644 (file)
@@ -29,6 +29,8 @@
 
 /* defines VIEW3D_OT_fly modal operator */
 
+//#define NDOF_FLY_DEBUG
+
 #include "DNA_anim_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_object_types.h"
@@ -143,7 +145,6 @@ void fly_modal_keymap(wmKeyConfig *keyconf)
 
        /* assign map to operators */
        WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly");
-
 }
 
 typedef struct FlyInfo {
@@ -158,7 +159,9 @@ typedef struct FlyInfo {
        short state;
        short use_precision;
        short redraw;
-       int mval[2];
+
+       int mval[2]; /* latest 2D mouse values */
+       wmNDOFMotionData* ndof; /* latest 3D mouse values */
 
        /* fly state state */
        float speed; /* the speed the view is moving per redraw */
@@ -257,6 +260,10 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
        fly->ar = CTX_wm_region(C);
        fly->scene= CTX_data_scene(C);
 
+       #ifdef NDOF_FLY_DEBUG
+       puts("\n-- fly begin --");
+       #endif
+
        if(fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->id.lib) {
                BKE_report(op->reports, RPT_ERROR, "Cannot fly a camera from an external library");
                return FALSE;
@@ -282,12 +289,14 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
        fly->zlock_momentum=0.0f;
        fly->grid= 1.0f;
        fly->use_precision= 0;
+       fly->redraw= 1;
 
        fly->dvec_prev[0]= fly->dvec_prev[1]= fly->dvec_prev[2]= 0.0f;
 
        fly->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
 
        VECCOPY2D(fly->mval, event->mval)
+       fly->ndof = NULL;
 
        fly->time_lastdraw= fly->time_lastwheel= PIL_check_seconds_timer();
 
@@ -329,8 +338,17 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
                /* perspective or ortho */
                if (fly->rv3d->persp==RV3D_ORTHO)
                        fly->rv3d->persp= RV3D_PERSP; /*if ortho projection, make perspective */
+
                copy_qt_qt(fly->rot_backup, fly->rv3d->viewquat);
                copy_v3_v3(fly->ofs_backup, fly->rv3d->ofs);
+
+               /* the dist defines a vector that is infront of the offset
+               to rotate the view about.
+               this is no good for fly mode because we
+               want to rotate about the viewers center.
+               but to correct the dist removal we must
+               alter offset so the view doesn't jump. */
+
                fly->rv3d->dist= 0.0f;
 
                upvec[2]= fly->dist_backup; /*x and y are 0*/
@@ -338,7 +356,6 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
                sub_v3_v3(fly->rv3d->ofs, upvec);
                /*Done with correcting for the dist*/
        }
-
        
        /* center the mouse, probably the UI mafia are against this but without its quite annoying */
        WM_cursor_warp(CTX_wm_window(C), fly->ar->winrct.xmin + fly->ar->winx/2, fly->ar->winrct.ymin + fly->ar->winy/2);
@@ -356,6 +373,10 @@ static int flyEnd(bContext *C, FlyInfo *fly)
        if(fly->state == FLY_RUNNING)
                return OPERATOR_RUNNING_MODAL;
 
+       #ifdef NDOF_FLY_DEBUG
+       puts("\n-- fly end --");
+       #endif
+
        WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), fly->timer);
 
        ED_region_draw_cb_exit(fly->ar->type, fly->draw_handle_pixel);
@@ -401,6 +422,9 @@ static int flyEnd(bContext *C, FlyInfo *fly)
        if(fly->obtfm)
                MEM_freeN(fly->obtfm);
 
+       if(fly->ndof)
+               MEM_freeN(fly->ndof);
+
        if(fly->state == FLY_CONFIRM) {
                MEM_freeN(fly);
                return OPERATOR_FINISHED;
@@ -412,12 +436,54 @@ static int flyEnd(bContext *C, FlyInfo *fly)
 
 static void flyEvent(FlyInfo *fly, wmEvent *event)
 {
-       if (event->type == TIMER && event->customdata == fly->timer) {
-               fly->redraw = 1;
-       }
-       else if (event->type == MOUSEMOVE) {
+       if (event->type == MOUSEMOVE) {
                VECCOPY2D(fly->mval, event->mval);
-       } /* handle modal keymap first */
+       }
+       else if (event->type == NDOF_MOTION) {
+               // do these automagically get delivered? yes.
+               // puts("ndof motion detected in fly mode!");
+               // static const char* tag_name = "3D mouse position";
+
+               wmNDOFMotionData* incoming_ndof = (wmNDOFMotionData*) event->customdata;
+               switch (incoming_ndof->progress)
+                       {
+                       case P_STARTING:
+                               // start keeping track of 3D mouse position
+                               #ifdef NDOF_FLY_DEBUG
+                               puts("start keeping track of 3D mouse position");
+                               #endif
+                               // fall through...
+                       case P_IN_PROGRESS:
+                               // update 3D mouse position
+                               #ifdef NDOF_FLY_DEBUG
+                               putchar('.'); fflush(stdout);
+                               #endif
+                               if (fly->ndof == NULL)
+                                       // fly->ndof = MEM_mallocN(sizeof(wmNDOFMotionData), tag_name);
+                                       fly->ndof = MEM_dupallocN(incoming_ndof);
+                                       // fly->ndof = malloc(sizeof(wmNDOFMotionData));
+                               else
+                                       memcpy(fly->ndof, incoming_ndof, sizeof(wmNDOFMotionData));
+                               break;
+                       case P_FINISHING:
+                               // stop keeping track of 3D mouse position
+                               #ifdef NDOF_FLY_DEBUG
+                               puts("stop keeping track of 3D mouse position");
+                               #endif
+                               if (fly->ndof)
+                                       {
+                                       MEM_freeN(fly->ndof);
+                                       // free(fly->ndof);
+                                       fly->ndof = NULL;
+                                       }
+                               /* update the time else the view will jump when 2D mouse/timer resume */
+                               fly->time_lastdraw= PIL_check_seconds_timer();
+                               break;
+                       default:
+                               ; // should always be one of the above 3
+                       }
+               }
+       /* handle modal keymap first */
        else if (event->type == EVT_MODAL_MAP) {
                switch (event->val) {
                        case FLY_MODAL_CANCEL:
@@ -528,14 +594,81 @@ static void flyEvent(FlyInfo *fly, wmEvent *event)
                        case FLY_MODAL_PRECISION_DISABLE:
                                fly->use_precision= FALSE;
                                break;
+               }
+       }
+}
+
+
+static void move_camera(bContext* C, RegionView3D* rv3d, FlyInfo* fly, int orientationChanged, int positionChanged)
+{
+       /* we are in camera view so apply the view ofs and quat to the view matrix and set the camera to the view */
 
+       View3D* v3d = fly->v3d;
+       Scene *scene= fly->scene;
+       ID *id_key;
+
+       /* transform the parent or the camera? */
+       if(fly->root_parent) {
+               Object *ob_update;
+
+               float view_mat[4][4];
+               float prev_view_mat[4][4];
+               float prev_view_imat[4][4];
+               float diff_mat[4][4];
+               float parent_mat[4][4];
+
+               ED_view3d_to_m4(prev_view_mat, fly->rv3d->ofs, fly->rv3d->viewquat, fly->rv3d->dist);
+               invert_m4_m4(prev_view_imat, prev_view_mat);
+               ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
+               mul_m4_m4m4(diff_mat, prev_view_imat, view_mat);
+               mul_m4_m4m4(parent_mat, fly->root_parent->obmat, diff_mat);
+               object_apply_mat4(fly->root_parent, parent_mat, TRUE, FALSE);
+
+               // where_is_object(scene, fly->root_parent);
+
+               ob_update= v3d->camera->parent;
+               while(ob_update) {
+                       DAG_id_tag_update(&ob_update->id, OB_RECALC_OB);
+                       ob_update= ob_update->parent;
                }
+
+               id_key= &fly->root_parent->id;
+       }
+       else {
+               float view_mat[4][4];
+               ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
+               object_apply_mat4(v3d->camera, view_mat, TRUE, FALSE);
+               id_key= &v3d->camera->id;
+       }
+
+       /* record the motion */
+       if (autokeyframe_cfra_can_key(scene, id_key)) {
+               ListBase dsources = {NULL, NULL};
+               
+               /* add datasource override for the camera object */
+               ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL); 
+               
+               /* insert keyframes 
+                *      1) on the first frame
+                *      2) on each subsequent frame
+                *              TODO: need to check in future that frame changed before doing this 
+                */
+               if (orientationChanged) {
+                       KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Rotation");
+                       ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
+               }
+               if (positionChanged) {
+                       KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Location");
+                       ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
+               }
+               
+               /* free temp data */
+               BLI_freelistN(&dsources);
        }
 }
 
 static int flyApply(bContext *C, FlyInfo *fly)
 {
-
 #define FLY_ROTATE_FAC 2.5f /* more is faster */
 #define FLY_ZUP_CORRECT_FAC 0.1f /* amount to correct per step */
 #define FLY_ZUP_CORRECT_ACCEL 0.05f /* increase upright momentum each step */
@@ -545,11 +678,7 @@ static int flyApply(bContext *C, FlyInfo *fly)
        a fly loop where the user can move move the view as if they are flying
        */
        RegionView3D *rv3d= fly->rv3d;
-       View3D *v3d = fly->v3d;
        ARegion *ar = fly->ar;
-       Scene *scene= fly->scene;
-
-       float prev_view_mat[4][4];
 
        float mat[3][3], /* 3x3 copy of the view matrix so we can move allong the view axis */
        dvec[3]={0,0,0}, /* this is the direction thast added to the view offset per redraw */
@@ -567,15 +696,11 @@ static int flyApply(bContext *C, FlyInfo *fly)
        unsigned char
        apply_rotation= 1; /* if the user presses shift they can look about without movinf the direction there looking*/
 
-       if(fly->root_parent)
-               ED_view3d_to_m4(prev_view_mat, fly->rv3d->ofs, fly->rv3d->viewquat, fly->rv3d->dist);
+       #ifdef NDOF_FLY_DEBUG
+       static unsigned int iteration = 1;
+       printf("fly timer %d\n", iteration++);
+       #endif
 
-       /* the dist defines a vector that is infront of the offset
-       to rotate the view about.
-       this is no good for fly mode because we
-       want to rotate about the viewers center.
-       but to correct the dist removal we must
-       alter offset so the view doesn't jump. */
 
        xmargin= ar->winx/20.0f;
        ymargin= ar->winy/20.0f;
@@ -622,6 +747,8 @@ static int flyApply(bContext *C, FlyInfo *fly)
                        float time_redraw;
                        float time_redraw_clamped;
 
+                       fly->redraw= 1;
+
                        time_current= PIL_check_seconds_timer();
                        time_redraw= (float)(time_current - fly->time_lastdraw);
                        time_redraw_clamped= MIN2(0.05f, time_redraw); /* clamt the redraw time to avoid jitter in roll correction */
@@ -690,7 +817,7 @@ static int flyApply(bContext *C, FlyInfo *fly)
                                                mul_m3_v3(mat, upvec);
                                        }
 
-                                       axis_angle_to_quat( tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC); /* Rotate about the relative up vec */
+                                       axis_angle_to_quat(tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC); /* Rotate about the relative up vec */
                                        mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
 
                                        if (fly->xlock) fly->xlock = 2;/*check for rotation*/
@@ -784,69 +911,9 @@ static int flyApply(bContext *C, FlyInfo *fly)
                                ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X off/Z off,  Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
 #endif
 
-                       /* we are in camera view so apply the view ofs and quat to the view matrix and set the camera to the view */
-                       if (rv3d->persp==RV3D_CAMOB) {
-                               ID *id_key;
-                               /* transform the parent or the camera? */
-                               if(fly->root_parent) {
-                                       Object *ob_update;
-
-                                       float view_mat[4][4];
-                                       float prev_view_imat[4][4];
-                                       float diff_mat[4][4];
-                                       float parent_mat[4][4];
-
-                                       invert_m4_m4(prev_view_imat, prev_view_mat);
-                                       ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
-                                       mul_m4_m4m4(diff_mat, prev_view_imat, view_mat);
-                                       mul_m4_m4m4(parent_mat, fly->root_parent->obmat, diff_mat);
-                                       object_apply_mat4(fly->root_parent, parent_mat, TRUE, FALSE);
-
-                                       // where_is_object(scene, fly->root_parent);
-
-                                       ob_update= v3d->camera->parent;
-                                       while(ob_update) {
-                                               DAG_id_tag_update(&ob_update->id, OB_RECALC_OB);
-                                               ob_update= ob_update->parent;
-                                       }
-
-                                       copy_m4_m4(prev_view_mat, view_mat);
-
-                                       id_key= &fly->root_parent->id;
-
-                               }
-                               else {
-                                       float view_mat[4][4];
-                                       ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
-                                       object_apply_mat4(v3d->camera, view_mat, TRUE, FALSE);
-                                       id_key= &v3d->camera->id;
-                               }
+                       if (rv3d->persp==RV3D_CAMOB)
+                               move_camera(C, rv3d, fly, (fly->xlock || fly->zlock || moffset[0] || moffset[1]), fly->speed);
 
-                               /* record the motion */
-                               if (autokeyframe_cfra_can_key(scene, id_key)) {
-                                       ListBase dsources = {NULL, NULL};
-                                       
-                                       /* add datasource override for the camera object */
-                                       ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL); 
-                                       
-                                       /* insert keyframes 
-                                        *      1) on the first frame
-                                        *      2) on each subsequent frame
-                                        *              TODO: need to check in future that frame changed before doing this 
-                                        */
-                                       if (fly->xlock || fly->zlock || moffset[0] || moffset[1]) {
-                                               KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Rotation");
-                                               ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
-                                       }
-                                       if (fly->speed) {
-                                               KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Location");
-                                               ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
-                                       }
-                                       
-                                       /* free temp data */
-                                       BLI_freelistN(&dsources);
-                               }
-                       }
                } else
                        /*were not redrawing but we need to update the time else the view will jump */
                        fly->time_lastdraw= PIL_check_seconds_timer();
@@ -854,11 +921,141 @@ static int flyApply(bContext *C, FlyInfo *fly)
                copy_v3_v3(fly->dvec_prev, dvec);
        }
 
-/* moved to flyEnd() */
-
        return OPERATOR_FINISHED;
 }
 
+static int flyApply_ndof(bContext *C, FlyInfo *fly)
+{
+       // shorthand for oft-used variables
+       wmNDOFMotionData* ndof = fly->ndof;
+       const float dt = ndof->dt;
+       RegionView3D* rv3d = fly->rv3d;
+       const int flag = U.ndof_flag;
+
+//     int shouldRotate = (flag & NDOF_SHOULD_ROTATE) && (fly->pan_view == FALSE),
+//         shouldTranslate = (flag & (NDOF_SHOULD_PAN | NDOF_SHOULD_ZOOM));
+
+       int shouldRotate = (fly->pan_view == FALSE),
+           shouldTranslate = TRUE;
+
+       float view_inv[4];
+       invert_qt_qt(view_inv, rv3d->viewquat);
+
+       rv3d->rot_angle = 0.f; // disable onscreen rotation doo-dad
+
+       if (shouldTranslate)
+               {
+               const float forward_sensitivity = 1.f;
+               const float vertical_sensitivity = 0.4f;
+               const float lateral_sensitivity = 0.6f;
+
+               float speed = 10.f; // blender units per second
+               // ^^ this is ok for default cube scene, but should scale with.. something
+
+               float trans[3] = {
+                       lateral_sensitivity * ndof->tx,
+                       vertical_sensitivity * ndof->ty,
+                       forward_sensitivity * ndof->tz
+                       };
+
+               if (fly->use_precision)
+                       speed *= 0.2f;
+
+               mul_v3_fl(trans, speed * dt);
+
+               // transform motion from view to world coordinates
+               mul_qt_v3(view_inv, trans);
+
+               if (flag & NDOF_FLY_HELICOPTER)
+                       {
+                       // replace world z component with device y (yes it makes sense)
+                       trans[2] = speed * dt * vertical_sensitivity * ndof->ty;
+                       }
+
+               if (rv3d->persp==RV3D_CAMOB) {
+                       // respect camera position locks
+                       Object *lock_ob= fly->root_parent ? fly->root_parent : fly->v3d->camera;
+                       if (lock_ob->protectflag & OB_LOCK_LOCX) trans[0] = 0.f;
+                       if (lock_ob->protectflag & OB_LOCK_LOCY) trans[1] = 0.f;
+                       if (lock_ob->protectflag & OB_LOCK_LOCZ) trans[2] = 0.f;
+               }
+
+               if (trans[0] || trans[1] || trans[2])
+                       {
+                       // move center of view opposite of hand motion (this is camera mode, not object mode)
+                       sub_v3_v3(rv3d->ofs, trans);
+                       shouldTranslate = TRUE;
+                       }
+               else
+                       shouldTranslate = FALSE;
+               }
+
+       if (shouldRotate)
+               {
+               const float turn_sensitivity = 1.f;
+
+               float rotation[4];
+               float axis[3];
+               float angle = turn_sensitivity * ndof_to_angle_axis(ndof, axis);
+
+               if (fabsf(angle) > 0.0001f)
+                       {
+                       shouldRotate = TRUE;
+
+                       if (fly->use_precision)
+                               angle *= 0.2f;
+
+                       // transform rotation axis from view to world coordinates
+                       mul_qt_v3(view_inv, axis);
+
+                       // apply rotation to view
+                       axis_angle_to_quat(rotation, axis, angle);
+                       mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
+
+                       if (flag & NDOF_LOCK_HORIZON)
+                               // force an upright viewpoint
+                               // TODO: make this less... sudden
+                               {
+                               float view_horizon[3] = {1.f, 0.f, 0.f}; // view +x
+                               float view_direction[3] = {0.f, 0.f, -1.f}; // view -z (into screen)
+
+                               // find new inverse since viewquat has changed
+                               invert_qt_qt(view_inv, rv3d->viewquat);
+                               // could apply reverse rotation to existing view_inv to save a few cycles
+
+                               // transform view vectors to world coordinates
+                               mul_qt_v3(view_inv, view_horizon);
+                               mul_qt_v3(view_inv, view_direction);
+
+                               // find difference between view & world horizons
+                               // true horizon lives in world xy plane, so look only at difference in z
+                               angle = -asinf(view_horizon[2]);
+
+                               #ifdef NDOF_FLY_DEBUG
+                               printf("lock horizon: adjusting %.1f degrees\n\n", RAD2DEG(angle));
+                               #endif
+
+                               // rotate view so view horizon = world horizon
+                               axis_angle_to_quat(rotation, view_direction, angle);
+                               mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
+                               }
+
+                       rv3d->view = RV3D_VIEW_USER;
+                       }
+               else
+                       shouldRotate = FALSE;
+               }
+
+       if (shouldTranslate || shouldRotate)
+               {
+               fly->redraw = TRUE;
+
+               if (rv3d->persp==RV3D_CAMOB)
+                       move_camera(C, rv3d, fly, shouldRotate, shouldTranslate);
+               }
+
+       return OPERATOR_FINISHED;
+}
 
 
 static int fly_invoke(bContext *C, wmOperator *op, wmEvent *event)
@@ -908,7 +1105,12 @@ static int fly_modal(bContext *C, wmOperator *op, wmEvent *event)
 
        flyEvent(fly, event);
 
-       if(event->type==TIMER && event->customdata == fly->timer)
+       if (fly->ndof) // 3D mouse overrules [2D mouse + timer]
+               {
+               if (event->type==NDOF_MOTION)
+                       flyApply_ndof(C, fly);
+               }
+       else if (event->type==TIMER && event->customdata == fly->timer)
                flyApply(C, fly);
 
        do_draw |= fly->redraw;
@@ -923,6 +1125,7 @@ static int fly_modal(bContext *C, wmOperator *op, wmEvent *event)
                        WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, fly_object);
                }
 
+               // puts("redraw!"); // too frequent, fix tomorrow.
                ED_region_tag_redraw(CTX_wm_region(C));
        }
 
index d3886d48873278e6c2879705a35796ebe7a94cc1..c4207b0ce25fa554c3a3a892fed9463b9309fbe3 100644 (file)
@@ -51,6 +51,7 @@ struct ARegionType;
 struct bPoseChannel;
 struct bAnimVizSettings;
 struct bMotionPath;
+struct wmNDOFMotionData;
 
 #define BL_NEAR_CLIP 0.001
 
@@ -72,6 +73,8 @@ 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_orbit(struct wmOperatorType *ot);
+void VIEW3D_OT_ndof_pan(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);
@@ -91,6 +94,8 @@ void VIEW3D_OT_zoom_border(struct wmOperatorType *ot);
 void VIEW3D_OT_drawtype(struct wmOperatorType *ot);
 
 void view3d_boxview_copy(ScrArea *sa, ARegion *ar);
+void ndof_to_quat(struct wmNDOFMotionData* ndof, float q[4]);
+float ndof_to_angle_axis(struct wmNDOFMotionData* ndof, float axis[3]);
 
 /* view3d_fly.c */
 void view3d_keymap(struct wmKeyConfig *keyconf);
index 05ef79a9f29a48a31330f77987301ceb2a3548e2..e47cb1db753922803817d27c30631463a88b39a1 100644 (file)
@@ -64,6 +64,8 @@ void view3d_operatortypes(void)
        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_orbit);
+       WM_operatortype_append(VIEW3D_OT_ndof_pan);
        WM_operatortype_append(VIEW3D_OT_view_all);
        WM_operatortype_append(VIEW3D_OT_viewnumpad);
        WM_operatortype_append(VIEW3D_OT_view_orbit);
@@ -161,6 +163,17 @@ void view3d_keymap(wmKeyConfig *keyconf)
        RNA_boolean_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_all", HOMEKEY, KM_PRESS, 0, 0)->ptr, "center", 0); /* only without camera view */
        RNA_boolean_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_all", CKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "center", 1);
 
+       /* 3D mouse */
+       WM_keymap_add_item(keymap, "VIEW3D_OT_ndof_orbit", NDOF_MOTION, 0, 0, 0);
+       WM_keymap_add_item(keymap, "VIEW3D_OT_ndof_pan", NDOF_MOTION, 0, KM_SHIFT, 0);
+       WM_keymap_add_item(keymap, "VIEW3D_OT_view_selected", NDOF_BUTTON_FIT, KM_PRESS, 0, 0);
+       RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_FRONT, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_FRONT);
+       RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_BACK, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_BACK);
+       RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_LEFT, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_LEFT);
+       RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_RIGHT, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_RIGHT);
+       RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_TOP, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_TOP);
+       RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_BOTTOM, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_BOTTOM);
+
        /* numpad view hotkeys*/
        RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", PAD0, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_CAMERA);
        RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", PAD1, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_FRONT);
@@ -210,6 +223,17 @@ void view3d_keymap(wmKeyConfig *keyconf)
        RNA_enum_set(kmi->ptr, "type", RV3D_VIEW_BOTTOM);
        RNA_boolean_set(kmi->ptr, "align_active", TRUE);
 
+       /* 3D mouse align */
+       kmi= WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_FRONT, KM_PRESS, KM_SHIFT, 0);
+       RNA_enum_set(kmi->ptr, "type", RV3D_VIEW_FRONT);
+       RNA_boolean_set(kmi->ptr, "align_active", TRUE);
+       kmi= WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_RIGHT, KM_PRESS, KM_SHIFT, 0);
+       RNA_enum_set(kmi->ptr, "type", RV3D_VIEW_RIGHT);
+       RNA_boolean_set(kmi->ptr, "align_active", TRUE);
+       kmi= WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_TOP, KM_PRESS, KM_SHIFT, 0);
+       RNA_enum_set(kmi->ptr, "type", RV3D_VIEW_TOP);
+       RNA_boolean_set(kmi->ptr, "align_active", TRUE);
+
        WM_keymap_add_item(keymap, "VIEW3D_OT_localview", PADSLASHKEY, KM_PRESS, 0, 0);
        
        /* layers, shift + alt are properties set in invoke() */
index 59e9e681e2b83bca3949a83d17cc7d8d7f8ec0ff..92ac8471172f0d61f665e28daf2ccf0e1b7ede5e 100644 (file)
@@ -1006,9 +1006,11 @@ int transformEvent(TransInfo *t, wmEvent *event)
                        else view_editmove(event->type);
                        t->redraw= 1;
                        break;
-//             case NDOFMOTION:
-//            viewmoveNDOF(1);
-  //         break;
+#if 0
+               case NDOF_MOTION:
+                       // should have been caught by tranform_modal
+                       return OPERATOR_PASS_THROUGH;
+#endif
                default:
                        handled = 0;
                        break;
@@ -1017,43 +1019,6 @@ int transformEvent(TransInfo *t, wmEvent *event)
                // Numerical input events
                t->redraw |= handleNumInput(&(t->num), event);
 
-               // NDof input events
-               switch(handleNDofInput(&(t->ndof), event))
-               {
-                       case NDOF_CONFIRM:
-                               if ((t->options & CTX_NDOF) == 0)
-                               {
-                                       /* Confirm on normal transform only */
-                                       t->state = TRANS_CONFIRM;
-                               }
-                               break;
-                       case NDOF_CANCEL:
-                               if (t->options & CTX_NDOF)
-                               {
-                                       /* Cancel on pure NDOF transform */
-                                       t->state = TRANS_CANCEL;
-                               }
-                               else
-                               {
-                                       /* Otherwise, just redraw, NDof input was cancelled */
-                                       t->redraw |= TREDRAW_HARD;
-                               }
-                               break;
-                       case NDOF_NOMOVE:
-                               if (t->options & CTX_NDOF)
-                               {
-                                       /* Confirm on pure NDOF transform */
-                                       t->state = TRANS_CONFIRM;
-                               }
-                               break;
-                       case NDOF_REFRESH:
-                               t->redraw |= TREDRAW_HARD;
-                               break;
-                       default:
-                               handled = 0;
-                               break;
-               }
-
                // Snapping events
                t->redraw |= handleSnapping(t, event);
 
@@ -2886,10 +2851,6 @@ void initRotation(TransInfo *t)
        setInputPostFct(&t->mouse, postInputRotation);
        initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
        
-       t->ndof.axis = 16;
-       /* Scale down and flip input for rotation */
-       t->ndof.factor[0] = -0.2f;
-       
        t->idx_max = 0;
        t->num.idx_max = 0;
        t->snap[0] = 0.0f;
@@ -3161,8 +3122,6 @@ int Rotation(TransInfo *t, const int UNUSED(mval[2]))
 
        final = t->values[0];
        
-       applyNDofInput(&t->ndof, &final);
-       
        snapGrid(t, &final);
        
        if ((t->con.mode & CON_APPLY) && t->con.applyRot) {
@@ -3216,11 +3175,6 @@ void initTrackball(TransInfo *t)
 
        initMouseInputMode(t, &t->mouse, INPUT_TRACKBALL);
 
-       t->ndof.axis = 40;
-       /* Scale down input for rotation */
-       t->ndof.factor[0] = 0.2f;
-       t->ndof.factor[1] = 0.2f;
-
        t->idx_max = 1;
        t->num.idx_max = 1;
        t->snap[0] = 0.0f;
@@ -3276,8 +3230,6 @@ int Trackball(TransInfo *t, const int UNUSED(mval[2]))
        phi[0] = t->values[0];
        phi[1] = t->values[1];
 
-       applyNDofInput(&t->ndof, phi);
-
        snapGrid(t, phi);
 
        if (hasNumInput(&t->num)) {
@@ -3331,8 +3283,6 @@ void initTranslation(TransInfo *t)
        t->num.flag = 0;
        t->num.idx_max = t->idx_max;
 
-       t->ndof.axis = (t->flag & T_2D_EDIT)? 1|2: 1|2|4;
-
        if(t->spacetype == SPACE_VIEW3D) {
                RegionView3D *rv3d = t->ar->regiondata;
 
@@ -3507,7 +3457,6 @@ int Translation(TransInfo *t, const int UNUSED(mval[2]))
                headerTranslation(t, pvec, str);
        }
        else {
-               applyNDofInput(&t->ndof, t->values);
                snapGrid(t, t->values);
                applyNumInput(&t->num, t->values);
                if (hasNumInput(&t->num)) {
@@ -3616,10 +3565,6 @@ void initTilt(TransInfo *t)
 
        initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
 
-       t->ndof.axis = 16;
-       /* Scale down and flip input for rotation */
-       t->ndof.factor[0] = -0.2f;
-
        t->idx_max = 0;
        t->num.idx_max = 0;
        t->snap[0] = 0.0f;
@@ -3643,8 +3588,6 @@ int Tilt(TransInfo *t, const int UNUSED(mval[2]))
 
        final = t->values[0];
 
-       applyNDofInput(&t->ndof, &final);
-
        snapGrid(t, &final);
 
        if (hasNumInput(&t->num)) {
@@ -3759,10 +3702,6 @@ void initPushPull(TransInfo *t)
 
        initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE);
 
-       t->ndof.axis = 4;
-       /* Flip direction */
-       t->ndof.factor[0] = -1.0f;
-
        t->idx_max = 0;
        t->num.idx_max = 0;
        t->snap[0] = 0.0f;
@@ -3783,8 +3722,6 @@ int PushPull(TransInfo *t, const int UNUSED(mval[2]))
 
        distance = t->values[0];
 
-       applyNDofInput(&t->ndof, &distance);
-
        snapGrid(t, &distance);
 
        applyNumInput(&t->num, &distance);
@@ -5309,8 +5246,6 @@ void initSeqSlide(TransInfo *t)
        t->num.flag = 0;
        t->num.idx_max = t->idx_max;
 
-       t->ndof.axis = 1|2;
-
        t->snap[0] = 0.0f;
        t->snap[1] = floor(t->scene->r.frs_sec / t->scene->r.frs_sec_base);
        t->snap[2] = 10.0f;
@@ -5365,7 +5300,6 @@ int SeqSlide(TransInfo *t, const int UNUSED(mval[2]))
                VECCOPY(t->values, tvec);
        }
        else {
-               applyNDofInput(&t->ndof, t->values);
                snapGrid(t, t->values);
                applyNumInput(&t->num, t->values);
        }
@@ -5925,54 +5859,3 @@ void BIF_TransformSetUndo(char *UNUSED(str))
        // TRANSFORM_FIX_ME
        //Trans.undostr= str;
 }
-
-
-#if 0 // TRANSFORM_FIX_ME
-static void NDofTransform(void)
-{
-       float fval[7];
-       float maxval = 50.0f; // also serves as threshold
-       int axis = -1;
-       int mode = 0;
-       int i;
-
-       getndof(fval);
-
-       for(i = 0; i < 6; i++)
-       {
-               float val = fabs(fval[i]);
-               if (val > maxval)
-               {
-                       axis = i;
-                       maxval = val;
-               }
-       }
-
-       switch(axis)
-       {
-               case -1:
-                       /* No proper axis found */
-                       break;
-               case 0:
-               case 1:
-               case 2:
-                       mode = TFM_TRANSLATION;
-                       break;
-               case 4:
-                       mode = TFM_ROTATION;
-                       break;
-               case 3:
-               case 5:
-                       mode = TFM_TRACKBALL;
-                       break;
-               default:
-                       printf("ndof: what we are doing here ?");
-       }
-
-       if (mode != 0)
-       {
-               initTransform(mode, CTX_NDOF);
-               Transform();
-       }
-}
-#endif
index 2d0c1ac281803a50034e8647089f78b7f5be4588..a779982099ecaae8dd57e84b37a258cbd3bceb2c 100644 (file)
@@ -360,6 +360,12 @@ static int transform_modal(bContext *C, wmOperator *op, wmEvent *event)
 
        TransInfo *t = op->customdata;
 
+       if (event->type == NDOF_MOTION)
+               {
+               // puts("transform_modal: passing through NDOF_MOTION");
+               return OPERATOR_PASS_THROUGH;
+               }
+
        /* XXX insert keys are called here, and require context */
        t->context= C;
        exit_code = transformEvent(t, event);
index ae57cf3f80b03ec5a80e4798803a76fc108b5b9e..12f8cd656a0454373fa7d2698328c85f26554c55 100644 (file)
@@ -365,7 +365,6 @@ typedef struct UserDef {
        short recent_files;             /* maximum number of recently used files to remember  */
        short smooth_viewtx;    /* miliseconds to spend spinning the view */
        short glreslimit;
-       short ndof_pan, ndof_rotate;
        short curssize;
        short color_picker_type;
        short ipo_new;                  /* interpolation mode for newly added F-Curves */
@@ -376,6 +375,10 @@ typedef struct UserDef {
        
        short widget_unit;              /* defaults to 20 for 72 DPI setting */
        short anisotropic_filter;
+       /*short pad[3];                 */
+
+       float ndof_sensitivity; /* overall sensitivity of 3D mouse */
+       int ndof_flag;                  /* flags for 3D mouse */
 
        char versemaster[160];
        char verseuser[160];
@@ -384,7 +387,7 @@ typedef struct UserDef {
        short autokey_mode;             /* autokeying mode */
        short autokey_flag;             /* flags for autokeying */
        
-       short text_render, pad9;                /*options for text rendering*/
+       short text_render, pad9[3];             /*options for text rendering*/
 
        struct ColorBand coba_weight;   /* from texture.h */
 
@@ -577,6 +580,28 @@ extern UserDef U; /* from blenkernel blender.c */
 #define TH_OLDSKOOL    3
 #define TH_SHADED      4
 
+/* ndof_flag (3D mouse options) */
+#define NDOF_SHOW_GUIDE     (1 << 0)
+#define NDOF_FLY_HELICOPTER (1 << 1)
+#define NDOF_LOCK_HORIZON   (1 << 2)
+/* the following might not need to be saved between sessions,
+   but they do need to live somewhere accessible... */
+#define NDOF_SHOULD_PAN     (1 << 3)
+#define NDOF_SHOULD_ZOOM    (1 << 4)
+#define NDOF_SHOULD_ROTATE  (1 << 5)
+/* orbit navigation modes
+   only two options, so it's sort of a hyrbrid bool/enum
+   if ((U.ndof_flag & NDOF_ORBIT_MODE) == NDOF_OM_OBJECT)... */
+/*
+#define NDOF_ORBIT_MODE     (1 << 6)
+#define NDOF_OM_TARGETCAMERA 0
+#define NDOF_OM_OBJECT      NDOF_ORBIT_MODE
+*/
+/* actually... users probably don't care about what the mode
+   is called, just that it feels right */
+#define NDOF_ORBIT_INVERT_AXES (1 << 6)
+
+
 #ifdef __cplusplus
 }
 #endif
index 7379493003d2fd90f8dab84c082775a75c108865..89b8bad28069db22865f0ab0144ffe91147423e5 100644 (file)
@@ -130,7 +130,11 @@ typedef struct RegionView3D {
        
        float twangle[3];
 
-       float padf;
+       /* active rotation from NDOF or elsewhere */
+       float rot_angle;
+       float rot_axis[3];
+       
+       char pad2[4];
 
 } RegionView3D;
 
@@ -190,11 +194,10 @@ typedef struct View3D {
        /* drawflags, denoting state */
        short zbuf, transp, xray;
 
-       char ndofmode;                  /* mode of transform for 6DOF devices -1 not found, 0 normal, 1 fly, 2 ob transform */
-       char ndoffilter;                /* filter for 6DOF devices 0 normal, 1 dominant */
-       
+       char pad3[2];
+
        void *properties_storage;       /* Nkey panel stores stuff here (runtime only!) */
-       
+
        /* XXX depricated? */
        struct bGPdata *gpd;            /* Grease-Pencil Data (annotation layers) */
 
index 4d1e1f175f2ca2df3b57c13d579a33bfec7eb2d1..7a9193571fdd0de74486f0738bb4f6bdf066a45a 100644 (file)
@@ -2737,17 +2737,30 @@ static void rna_def_userdef_input(BlenderRNA *brna)
        RNA_def_property_int_sdna(prop, NULL, "dragthreshold");
        RNA_def_property_range(prop, 3, 40);
        RNA_def_property_ui_text(prop, "Drag Threshold", "Amount of pixels you have to drag before dragging UI items happens");
-       
-       prop= RNA_def_property(srna, "ndof_pan_speed", PROP_INT, PROP_NONE);
-       RNA_def_property_int_sdna(prop, NULL, "ndof_pan");
-       RNA_def_property_range(prop, 0, 200);
-       RNA_def_property_ui_text(prop, "NDof Pan Speed", "The overall panning speed of an NDOF device, as percent of standard");
 
-       prop= RNA_def_property(srna, "ndof_rotate_speed", PROP_INT, PROP_NONE);
-       RNA_def_property_int_sdna(prop, NULL, "ndof_rotate");
-       RNA_def_property_range(prop, 0, 200);
-       RNA_def_property_ui_text(prop, "NDof Rotation Speed", "The overall rotation speed of an NDOF device, as percent of standard");
-       
+       /* 3D mouse settings */
+       prop= RNA_def_property(srna, "ndof_sensitivity", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_range(prop, 0.25f, 4.0f);
+       RNA_def_property_ui_text(prop, "Sensitivity", "Overall sensitivity of the 3D Mouse");
+
+       prop= RNA_def_property(srna, "ndof_show_guide", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "ndof_flag", NDOF_SHOW_GUIDE);
+       RNA_def_property_ui_text(prop, "Show Navigation Guide", "Display the center and axis during rotation");
+       /* TODO: update description when fly-mode visuals are in place  ("projected position in fly mode")*/
+
+       prop= RNA_def_property(srna, "ndof_orbit_invert_axes", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "ndof_flag", NDOF_ORBIT_INVERT_AXES);
+       RNA_def_property_ui_text(prop, "Invert Axes", "Toggle between moving the viewpoint or moving the scene being viewed");
+
+       prop= RNA_def_property(srna, "ndof_lock_horizon", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "ndof_flag", NDOF_LOCK_HORIZON);
+       RNA_def_property_ui_text(prop, "Lock Horizon", "Keep horizon level while flying with 3D Mouse");
+
+       prop= RNA_def_property(srna, "ndof_fly_helicopter", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "ndof_flag", NDOF_FLY_HELICOPTER);
+       RNA_def_property_ui_text(prop, "Helicopter Mode", "Device up/down directly controls your Z position");
+
+
        prop= RNA_def_property(srna, "mouse_double_click_time", PROP_INT, PROP_NONE);
        RNA_def_property_int_sdna(prop, NULL, "dbl_click_time");
        RNA_def_property_range(prop, 1, 1000);
index 73221c47c90c3efa535744ad571bed647aaea2d2..a046be59ab512739bbb29c6f63f0ed85a249d9fa 100644 (file)
@@ -106,6 +106,46 @@ EnumPropertyItem event_timer_type_items[]= {
        {TIMER2, "TIMER2", 0, "Timer 2", ""},
        {0, NULL, 0, NULL, NULL}};
 
+EnumPropertyItem event_ndof_type_items[]= {
+       /* buttons on all 3dconnexion devices */
+       {NDOF_BUTTON_MENU, "NDOF_BUTTON_MENU", 0, "Menu", ""},
+       {NDOF_BUTTON_FIT, "NDOF_BUTTON_FIT", 0, "Fit", ""},
+       /* view buttons */
+       {NDOF_BUTTON_TOP, "NDOF_BUTTON_TOP", 0, "Top", ""},
+       {NDOF_BUTTON_BOTTOM, "NDOF_BUTTON_BOTTOM", 0, "Bottom", ""},
+       {NDOF_BUTTON_LEFT, "NDOF_BUTTON_LEFT", 0, "Left", ""},
+       {NDOF_BUTTON_RIGHT, "NDOF_BUTTON_RIGHT", 0, "Right", ""},
+       {NDOF_BUTTON_FRONT, "NDOF_BUTTON_FRONT", 0, "Front", ""},
+       {NDOF_BUTTON_BACK, "NDOF_BUTTON_BACK", 0, "Back", ""},
+       /* more views */
+       {NDOF_BUTTON_ISO1, "NDOF_BUTTON_ISO1", 0, "ISO 1", ""},
+       {NDOF_BUTTON_ISO2, "NDOF_BUTTON_ISO2", 0, "ISO 2", ""},
+       /* 90 degree rotations */
+       {NDOF_BUTTON_ROLL_CW, "NDOF_BUTTON_ROLL_CW", 0, "Roll CW", ""},
+       {NDOF_BUTTON_ROLL_CCW, "NDOF_BUTTON_ROLL_CCW", 0, "Roll CCW", ""},
+       {NDOF_BUTTON_SPIN_CW, "NDOF_BUTTON_SPIN_CW", 0, "Spin CW", ""},
+       {NDOF_BUTTON_SPIN_CCW, "NDOF_BUTTON_SPIN_CCW", 0, "Spin CCW", ""},
+       {NDOF_BUTTON_TILT_CW, "NDOF_BUTTON_TILT_CW", 0, "Tilt CW", ""},
+       {NDOF_BUTTON_TILT_CCW, "NDOF_BUTTON_TILT_CCW", 0, "Tilt CCW", ""},
+       /* device control */
+       {NDOF_BUTTON_ROTATE, "NDOF_BUTTON_ROTATE", 0, "Rotate", ""},
+       {NDOF_BUTTON_PANZOOM, "NDOF_BUTTON_PANZOOM", 0, "Pan/Zoom", ""},
+       {NDOF_BUTTON_DOMINANT, "NDOF_BUTTON_DOMINANT", 0, "Dominant", ""},
+       {NDOF_BUTTON_PLUS, "NDOF_BUTTON_PLUS", 0, "Plus", ""},
+       {NDOF_BUTTON_MINUS, "NDOF_BUTTON_MINUS", 0, "Minus", ""},
+       /* general-purpose buttons */
+       {NDOF_BUTTON_1, "NDOF_BUTTON_1", 0, "Button 1", ""},
+       {NDOF_BUTTON_2, "NDOF_BUTTON_2", 0, "Button 2", ""},
+       {NDOF_BUTTON_3, "NDOF_BUTTON_3", 0, "Button 3", ""},
+       {NDOF_BUTTON_4, "NDOF_BUTTON_4", 0, "Button 4", ""},
+       {NDOF_BUTTON_5, "NDOF_BUTTON_5", 0, "Button 5", ""},
+       {NDOF_BUTTON_6, "NDOF_BUTTON_6", 0, "Button 6", ""},
+       {NDOF_BUTTON_7, "NDOF_BUTTON_7", 0, "Button 7", ""},
+       {NDOF_BUTTON_8, "NDOF_BUTTON_8", 0, "Button 8", ""},
+       {NDOF_BUTTON_9, "NDOF_BUTTON_9", 0, "Button 9", ""},
+       {NDOF_BUTTON_10, "NDOF_BUTTON_10", 0, "Button 10", ""},
+       {0, NULL, 0, NULL, NULL}};
+
 /* not returned: CAPSLOCKKEY, UNKNOWNKEY */
 EnumPropertyItem event_type_items[] = {
 
@@ -256,6 +296,44 @@ EnumPropertyItem event_type_items[] = {
        {TIMER0, "TIMER0", 0, "Timer 0", ""},
        {TIMER1, "TIMER1", 0, "Timer 1", ""},
        {TIMER2, "TIMER2", 0, "Timer 2", ""},
+       {0, "", 0, NULL, NULL},
+       /* buttons on all 3dconnexion devices */
+       {NDOF_BUTTON_MENU, "NDOF_BUTTON_MENU", 0, "Menu", ""},
+       {NDOF_BUTTON_FIT, "NDOF_BUTTON_FIT", 0, "Fit", ""},
+       /* view buttons */
+       {NDOF_BUTTON_TOP, "NDOF_BUTTON_TOP", 0, "Top", ""},
+       {NDOF_BUTTON_BOTTOM, "NDOF_BUTTON_BOTTOM", 0, "Bottom", ""},
+       {NDOF_BUTTON_LEFT, "NDOF_BUTTON_LEFT", 0, "Left", ""},
+       {NDOF_BUTTON_RIGHT, "NDOF_BUTTON_RIGHT", 0, "Right", ""},
+       {NDOF_BUTTON_FRONT, "NDOF_BUTTON_FRONT", 0, "Front", ""},
+       {NDOF_BUTTON_BACK, "NDOF_BUTTON_BACK", 0, "Back", ""},
+       /* more views */
+       {NDOF_BUTTON_ISO1, "NDOF_BUTTON_ISO1", 0, "ISO 1", ""},
+       {NDOF_BUTTON_ISO2, "NDOF_BUTTON_ISO2", 0, "ISO 2", ""},
+       /* 90 degree rotations */
+       {NDOF_BUTTON_ROLL_CW, "NDOF_BUTTON_ROLL_CW", 0, "Roll CW", ""},
+       {NDOF_BUTTON_ROLL_CCW, "NDOF_BUTTON_ROLL_CCW", 0, "Roll CCW", ""},
+       {NDOF_BUTTON_SPIN_CW, "NDOF_BUTTON_SPIN_CW", 0, "Spin CW", ""},
+       {NDOF_BUTTON_SPIN_CCW, "NDOF_BUTTON_SPIN_CCW", 0, "Spin CCW", ""},
+       {NDOF_BUTTON_TILT_CW, "NDOF_BUTTON_TILT_CW", 0, "Tilt CW", ""},
+       {NDOF_BUTTON_TILT_CCW, "NDOF_BUTTON_TILT_CCW", 0, "Tilt CCW", ""},
+       /* device control */
+       {NDOF_BUTTON_ROTATE, "NDOF_BUTTON_ROTATE", 0, "Rotate", ""},
+       {NDOF_BUTTON_PANZOOM, "NDOF_BUTTON_PANZOOM", 0, "Pan/Zoom", ""},
+       {NDOF_BUTTON_DOMINANT, "NDOF_BUTTON_DOMINANT", 0, "Dominant", ""},
+       {NDOF_BUTTON_PLUS, "NDOF_BUTTON_PLUS", 0, "Plus", ""},
+       {NDOF_BUTTON_MINUS, "NDOF_BUTTON_MINUS", 0, "Minus", ""},
+       /* general-purpose buttons */
+       {NDOF_BUTTON_1, "NDOF_BUTTON_1", 0, "Button 1", ""},
+       {NDOF_BUTTON_2, "NDOF_BUTTON_2", 0, "Button 2", ""},
+       {NDOF_BUTTON_3, "NDOF_BUTTON_3", 0, "Button 3", ""},
+       {NDOF_BUTTON_4, "NDOF_BUTTON_4", 0, "Button 4", ""},
+       {NDOF_BUTTON_5, "NDOF_BUTTON_5", 0, "Button 5", ""},
+       {NDOF_BUTTON_6, "NDOF_BUTTON_6", 0, "Button 6", ""},
+       {NDOF_BUTTON_7, "NDOF_BUTTON_7", 0, "Button 7", ""},
+       {NDOF_BUTTON_8, "NDOF_BUTTON_8", 0, "Button 8", ""},
+       {NDOF_BUTTON_9, "NDOF_BUTTON_9", 0, "Button 9", ""},
+       {NDOF_BUTTON_10, "NDOF_BUTTON_10", 0, "Button 10", ""},
        {0, NULL, 0, NULL, NULL}};      
 
 EnumPropertyItem keymap_propvalue_items[] = {
@@ -303,6 +381,7 @@ EnumPropertyItem wm_report_items[] = {
 #define KMI_TYPE_TWEAK         2
 #define KMI_TYPE_TEXTINPUT     3
 #define KMI_TYPE_TIMER         4
+#define KMI_TYPE_NDOF          5
 
 #ifdef RNA_RUNTIME
 
@@ -433,6 +512,7 @@ static int rna_wmKeyMapItem_map_type_get(PointerRNA *ptr)
        if(ISKEYBOARD(kmi->type)) return KMI_TYPE_KEYBOARD;
        if(ISTWEAK(kmi->type)) return KMI_TYPE_TWEAK;
        if(ISMOUSE(kmi->type)) return KMI_TYPE_MOUSE;
+       if(ISNDOF(kmi->type)) return KMI_TYPE_NDOF;
        if(kmi->type == KM_TEXTINPUT) return KMI_TYPE_TEXTINPUT;
        return KMI_TYPE_KEYBOARD;
 }
@@ -464,6 +544,10 @@ static void rna_wmKeyMapItem_map_type_set(PointerRNA *ptr, int value)
                        kmi->type= TIMER;
                        kmi->val= KM_NOTHING;
                        break;
+               case KMI_TYPE_NDOF:
+                       kmi->type = NDOF_BUTTON_MENU;
+                       kmi->val = KM_NOTHING;
+                       break;
                }
        }
 }
@@ -475,6 +559,7 @@ static EnumPropertyItem *rna_KeyMapItem_type_itemf(bContext *UNUSED(C), PointerR
        if(map_type == KMI_TYPE_MOUSE) return event_mouse_type_items;
        if(map_type == KMI_TYPE_TWEAK) return event_tweak_type_items;
        if(map_type == KMI_TYPE_TIMER) return event_timer_type_items;
+       if(map_type == KMI_TYPE_NDOF) return event_ndof_type_items;
        else return event_type_items;
 }
 
@@ -482,7 +567,7 @@ static EnumPropertyItem *rna_KeyMapItem_value_itemf(bContext *UNUSED(C), Pointer
 {
        int map_type= rna_wmKeyMapItem_map_type_get(ptr);
 
-       if(map_type == KMI_TYPE_MOUSE || map_type == KMI_TYPE_KEYBOARD) return event_keymouse_value_items;
+       if(map_type == KMI_TYPE_MOUSE || map_type == KMI_TYPE_KEYBOARD || map_type == KMI_TYPE_NDOF) return event_keymouse_value_items;
        if(map_type == KMI_TYPE_TWEAK) return event_tweak_value_items;
        else return event_value_items;
 }
@@ -1653,6 +1738,7 @@ static void rna_def_keyconfig(BlenderRNA *brna)
                {KMI_TYPE_KEYBOARD, "KEYBOARD", 0, "Keyboard", ""},
                {KMI_TYPE_TWEAK, "TWEAK", 0, "Tweak", ""},
                {KMI_TYPE_MOUSE, "MOUSE", 0, "Mouse", ""},
+               {KMI_TYPE_NDOF, "NDOF", 0, "NDOF", ""},
                {KMI_TYPE_TEXTINPUT, "TEXTINPUT", 0, "Text Input", ""},
                {KMI_TYPE_TIMER, "TIMER", 0, "Timer", ""},
                {0, NULL, 0, NULL, NULL}};
index 49bd3ede37d54212538a85b684c137993f14cee8..7476410ec19c93a72b03ba8ea0b495c78344c40a 100644 (file)
@@ -377,6 +377,26 @@ typedef struct wmTabletData {
        float Ytilt;            /* as above */
 } wmTabletData;
 
+typedef enum { // motion progress, for modal handlers
+       P_NOT_STARTED,
+       P_STARTING,    // <--
+       P_IN_PROGRESS, // <-- only these are sent for NDOF motion
+       P_FINISHING,   // <--
+       P_FINISHED
+       } wmProgress;
+
+typedef struct wmNDOFMotionData {
+       /* awfully similar to GHOST_TEventNDOFMotionData... */
+       // Each component normally ranges from -1 to +1, but can exceed that.
+       // These use blender standard view coordinates, with positive rotations being CCW about the axis.
+       float tx, ty, tz; // translation
+       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
+       wmProgress progress; // is this the first event, the last, or one of many in between?
+} wmNDOFMotionData;
+
 typedef struct wmTimer {
        struct wmTimer *next, *prev;
        
index ce3830b059cb3c6e724c0c00988b0f9c803c2275..322cd3b564200e20aee89a6807d1e4f01f3f7698 100644 (file)
@@ -1815,7 +1815,10 @@ void wm_event_do_handlers(bContext *C)
                                        /* for regions having custom cursors */
                                        wm_paintcursor_test(C, event);
                                }
-                               
+                               else if (event->type==NDOF_MOTION) {
+                                       win->addmousemove = TRUE;
+                               }
+
                                for(sa= win->screen->areabase.first; sa; sa= sa->next) {
                                        if(wm_event_inside_i(event, &sa->totrct)) {
                                                CTX_wm_area_set(C, sa);
@@ -1879,7 +1882,10 @@ void wm_event_do_handlers(bContext *C)
                                if(doit && win->screen && win->screen->subwinactive != win->screen->mainwin) {
                                        win->eventstate->prevx= event->x;
                                        win->eventstate->prevy= event->y;
+                                       //printf("win->eventstate->prev = %d %d\n", event->x, event->y);
                                }
+                               else
+                                       ;//printf("not setting prev to %d %d\n", event->x, event->y);
                        }
                        
                        /* store last event for this window */
@@ -1922,6 +1928,7 @@ void wm_event_do_handlers(bContext *C)
                /* only add mousemove when queue was read entirely */
                if(win->addmousemove && win->eventstate) {
                        wmEvent tevent= *(win->eventstate);
+                       //printf("adding MOUSEMOVE %d %d\n", tevent.x, tevent.y);
                        tevent.type= MOUSEMOVE;
                        tevent.prevx= tevent.x;
                        tevent.prevy= tevent.y;
@@ -2309,6 +2316,30 @@ static void update_tablet_data(wmWindow *win, wmEvent *event)
        } 
 }
 
+/* adds customdata to event */
+static void attach_ndof_data(wmEvent* event, const GHOST_TEventNDOFMotionData* ghost)
+{
+       wmNDOFMotionData* data = MEM_mallocN(sizeof(wmNDOFMotionData), "customdata NDOF");
+
+       const float s = U.ndof_sensitivity;
+
+       data->tx = s * ghost->tx;
+       data->ty = s * ghost->ty;
+       data->tz = s * ghost->tz;
+
+       data->rx = s * ghost->rx;
+       data->ry = s * ghost->ry;
+       data->rz = s * ghost->rz;
+
+       data->dt = ghost->dt;
+
+       data->progress = (wmProgress) ghost->progress;
+
+       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)
 {
@@ -2355,7 +2386,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
 {
        wmWindow *owin;
        wmEvent event, *evt= win->eventstate;
-       
+
        /* initialize and copy state (only mouse x y and modifiers) */
        event= *evt;
        
@@ -2384,6 +2415,8 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
 
                                update_tablet_data(win, &event);
                                wm_event_add(win, &event);
+
+                               //printf("sending MOUSEMOVE %d %d\n", event.x, event.y);
                                
                                /* also add to other window if event is there, this makes overdraws disappear nicely */
                                /* it remaps mousecoord to other window in event */
@@ -2557,6 +2590,38 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
                        break;
                }
 
+               case GHOST_kEventNDOFMotion: {
+                       event.type = NDOF_MOTION;
+                       attach_ndof_data(&event, customdata);
+                       wm_event_add(win, &event);
+
+                       //printf("sending NDOF_MOTION, prev = %d %d\n", event.x, event.y);
+
+                       break;
+               }
+
+               case GHOST_kEventNDOFButton: {
+                       GHOST_TEventNDOFButtonData* e = customdata;
+
+                       event.type = NDOF_BUTTON_NONE + e->button;
+
+                       switch (e->action) {
+                               case GHOST_kPress:
+                                       event.val = KM_PRESS;
+                                       break;
+                               case GHOST_kRelease:
+                                       event.val = KM_RELEASE;
+                                       break;
+                               }
+
+                       event.custom = 0;
+                       event.customdata = NULL;
+
+                       wm_event_add(win, &event);
+
+                       break;
+               }
+
                case GHOST_kEventUnknown:
                case GHOST_kNumEventTypes:
                        break;
index 4c280fe43411f12d08c96aebe65bfe73408e29dc..e22829577f4a8ab95980d4bf0cda4aa6177ff2ca 100644 (file)
@@ -183,8 +183,6 @@ void WM_init(bContext *C, int argc, const char **argv)
                
        ED_preview_init_dbase();
        
-       G.ndofdevice = -1;      /* XXX bad initializer, needs set otherwise buttons show! */
-       
        WM_read_history();
 
        /* allow a path of "", this is what happens when making a new file */
index 29afdb570ea49733cd8778120b74c90b457f402a..d813fd913ab29561e7d21db8046682baba9a64c0 100644 (file)
@@ -1411,6 +1411,22 @@ static void WM_OT_search_menu(wmOperatorType *ot)
        ot->poll= wm_search_menu_poll;
 }
 
+static int wm_ndof_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
+{
+       uiPupMenuInvoke(C,"VIEW3D_MT_ndof_settings");
+
+       return OPERATOR_FINISHED; // <-- correct?
+       return OPERATOR_CANCELLED; // <-- correct?
+}
+
+static void WM_OT_ndof_menu(wmOperatorType *ot)
+{
+       ot->name = "NDOF Menu";
+       ot->idname = "WM_OT_ndof_menu";
+       
+       ot->invoke = wm_ndof_menu_invoke;
+}
+
 static int wm_call_menu_exec(bContext *C, wmOperator *op)
 {
        char idname[BKE_ST_MAXNAME];
@@ -3416,7 +3432,55 @@ static void WM_OT_memory_statistics(wmOperatorType *ot)
 }
 
 /* ******************************************************* */
+
+static int wm_ndof_sensitivity_exec(bContext *UNUSED(C), wmOperator *op)
+{
+       const float min = 0.25f, max = 4.f; // TODO: get these from RNA property
+       float change;
+       float sensitivity = U.ndof_sensitivity;
+
+       if(RNA_boolean_get(op->ptr, "fast"))
+               change = 0.5f; // 50% change
+       else
+               change = 0.1f; // 10%
+
+       if(RNA_boolean_get(op->ptr, "decrease"))
+               {
+               sensitivity -= sensitivity * change; 
+               if (sensitivity < min)
+                       sensitivity = min;
+               }
+       else
+               {
+               sensitivity += sensitivity * change; 
+               if (sensitivity > max)
+                       sensitivity = max;
+               }
+
+       if (sensitivity != U.ndof_sensitivity)
+               {
+               U.ndof_sensitivity = sensitivity;
+               printf("new sensitivity: %f\n", U.ndof_sensitivity);
+               }
+       else
+               printf("same sensitivity: %f\n", U.ndof_sensitivity);
+
+       return OPERATOR_FINISHED;
+}
+
+static void WM_OT_ndof_sensitivity_change(wmOperatorType *ot)
+{
+       ot->name= "Change NDOF sensitivity";
+       ot->idname= "WM_OT_ndof_sensitivity_change";
+       ot->description="Change NDOF sensitivity";
+       
+       ot->exec= wm_ndof_sensitivity_exec;
+
+       RNA_def_boolean(ot->srna, "decrease", 1, "Decrease NDOF sensitivity", "If true then action decreases NDOF sensitivity instead of increasing");
+       RNA_def_boolean(ot->srna, "fast", 0, "Fast NDOF sensitivity change", "If true then sensitivity changes 50%, otherwise 10%");
+} 
+
+/* ******************************************************* */
 /* called on initialize WM_exit() */
 void wm_operatortype_free(void)
 {
@@ -3453,8 +3517,10 @@ void wm_operatortype_init(void)
        WM_operatortype_append(WM_OT_debug_menu);
        WM_operatortype_append(WM_OT_splash);
        WM_operatortype_append(WM_OT_search_menu);
+       WM_operatortype_append(WM_OT_ndof_menu);
        WM_operatortype_append(WM_OT_call_menu);
        WM_operatortype_append(WM_OT_radial_control);
+       WM_operatortype_append(WM_OT_ndof_sensitivity_change);
 #if defined(WIN32)
        WM_operatortype_append(WM_OT_console_toggle);
 #endif
@@ -3674,11 +3740,12 @@ void wm_window_keymap(wmKeyConfig *keyconf)
        /* debug/testing */
        WM_keymap_verify_item(keymap, "WM_OT_redraw_timer", TKEY, KM_PRESS, KM_ALT|KM_CTRL, 0);
        WM_keymap_verify_item(keymap, "WM_OT_debug_menu", DKEY, KM_PRESS, KM_ALT|KM_CTRL, 0);
-       WM_keymap_verify_item(keymap, "WM_OT_search_menu", SPACEKEY, KM_PRESS, 0, 0);
-       
-       /* Space switching */
 
+       /* menus that can be accessed anywhere in blender */
+       WM_keymap_verify_item(keymap, "WM_OT_search_menu", SPACEKEY, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "WM_OT_ndof_menu", NDOF_BUTTON_MENU, KM_PRESS, 0, 0);
 
+       /* Space switching */
        kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", F2KEY, KM_PRESS, KM_SHIFT, 0); /* new in 2.5x, was DXF export */
        RNA_string_set(kmi->ptr, "data_path", "area.type");
        RNA_string_set(kmi->ptr, "value", "LOGIC_EDITOR");
@@ -3722,6 +3789,23 @@ void wm_window_keymap(wmKeyConfig *keyconf)
        kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", F12KEY, KM_PRESS, KM_SHIFT, 0);
        RNA_string_set(kmi->ptr, "data_path", "area.type");
        RNA_string_set(kmi->ptr, "value", "DOPESHEET_EDITOR");
+       
+       /* ndof speed */
+       kmi= WM_keymap_add_item(keymap, "WM_OT_ndof_sensitivity_change", NDOF_BUTTON_PLUS, KM_PRESS, 0, 0);
+       RNA_boolean_set(kmi->ptr, "decrease", FALSE);
+       RNA_boolean_set(kmi->ptr, "fast", FALSE);
+
+       kmi= WM_keymap_add_item(keymap, "WM_OT_ndof_sensitivity_change", NDOF_BUTTON_MINUS, KM_PRESS, 0, 0);
+       RNA_boolean_set(kmi->ptr, "decrease", TRUE);
+       RNA_boolean_set(kmi->ptr, "fast", FALSE);
+
+       kmi= WM_keymap_add_item(keymap, "WM_OT_ndof_sensitivity_change", NDOF_BUTTON_PLUS, KM_PRESS, KM_SHIFT, 0);
+       RNA_boolean_set(kmi->ptr, "decrease", FALSE);
+       RNA_boolean_set(kmi->ptr, "fast", TRUE);
+
+       kmi= WM_keymap_add_item(keymap, "WM_OT_ndof_sensitivity_change", NDOF_BUTTON_MINUS, KM_PRESS, KM_SHIFT, 0);
+       RNA_boolean_set(kmi->ptr, "decrease", TRUE);
+       RNA_boolean_set(kmi->ptr, "fast", TRUE);
 
        gesture_circle_modal_keymap(keyconf);
        gesture_border_modal_keymap(keyconf);
index 9b1695be67a066d4b22e1c409cfca2b7e1abd1df..7d6010786d2e7d81689dc1ae1ac68965b64e7e85 100644 (file)
@@ -621,12 +621,12 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr private)
                if (!ghostwin) {
                        // XXX - should be checked, why are we getting an event here, and
                        //      what is it?
-                       
+                       puts("<!> event has no window");
                        return 1;
                } else if (!GHOST_ValidWindow(g_system, ghostwin)) {
                        // XXX - should be checked, why are we getting an event here, and
                        //      what is it?
-                       
+                       puts("<!> event has invalid window");                   
                        return 1;
                } else {
                        win= GHOST_GetWindowUserData(ghostwin);
index ee080e7c0aadefa83be00f168e84e6e8f7591602..579f20ca6053e66d374b37d5d879970681def8cb 100644 (file)
@@ -45,6 +45,7 @@
 #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 */
 #define EVT_TABLET_NONE                0
 #define INBETWEEN_MOUSEMOVE    17
 
 
+/* NDOF (from SpaceNavigator & friends)
+   These should be kept in sync with GHOST_NDOFManager.h
+   Ordering matters, exact values do not. */
+
+#define NDOF_MOTION 400
+
+enum {
+       // used internally, never sent
+       NDOF_BUTTON_NONE = NDOF_MOTION,
+       // these two are available from any 3Dconnexion device
+       NDOF_BUTTON_MENU,
+       NDOF_BUTTON_FIT,
+       // standard views
+       NDOF_BUTTON_TOP,
+       NDOF_BUTTON_BOTTOM,
+       NDOF_BUTTON_LEFT,
+       NDOF_BUTTON_RIGHT,
+       NDOF_BUTTON_FRONT,
+       NDOF_BUTTON_BACK,
+       // more views
+       NDOF_BUTTON_ISO1,
+       NDOF_BUTTON_ISO2,
+       // 90 degree rotations
+       NDOF_BUTTON_ROLL_CW,
+       NDOF_BUTTON_ROLL_CCW,
+       NDOF_BUTTON_SPIN_CW,
+       NDOF_BUTTON_SPIN_CCW,
+       NDOF_BUTTON_TILT_CW,
+       NDOF_BUTTON_TILT_CCW,
+       // device control
+       NDOF_BUTTON_ROTATE,
+       NDOF_BUTTON_PANZOOM,
+       NDOF_BUTTON_DOMINANT,
+       NDOF_BUTTON_PLUS,
+       NDOF_BUTTON_MINUS,
+       // general-purpose buttons
+       NDOF_BUTTON_1,
+       NDOF_BUTTON_2,
+       NDOF_BUTTON_3,
+       NDOF_BUTTON_4,
+       NDOF_BUTTON_5,
+       NDOF_BUTTON_6,
+       NDOF_BUTTON_7,
+       NDOF_BUTTON_8,
+       NDOF_BUTTON_9,
+       NDOF_BUTTON_10,
+       NDOF_LAST
+       };
+
+
 /* SYSTEM : 0x01xx */
 #define        INPUTCHANGE             0x0103  /* input connected or disconnected */
 #define WINDEACTIVATE  0x0104  /* window is deactivated, focus lost */
        /* test whether the event is tweak event */
 #define ISTWEAK(event) (event >= EVT_TWEAK_L && event <= EVT_GESTURE)
 
+       /* test whether the event is a NDOF event */
+#define ISNDOF(event)  (event >= NDOF_MOTION && event < NDOF_LAST)
+
 /* test whether event type is acceptable as hotkey, excluding modifiers */
-#define ISHOTKEY(event)        ((ISKEYBOARD(event) || ISMOUSE(event)) && event!=ESCKEY && !(event>=LEFTCTRLKEY && event<=LEFTSHIFTKEY) && !(event>=UNKNOWNKEY && event<=GRLESSKEY))
+#define ISHOTKEY(event)        ((ISKEYBOARD(event) || ISMOUSE(event) || ISNDOF(event)) && event!=ESCKEY && !(event>=LEFTCTRLKEY && event<=LEFTSHIFTKEY) && !(event>=UNKNOWNKEY && event<=GRLESSKEY))
 
 /* **************** BLENDER GESTURE EVENTS (0x5000) **************** */