Merging r38898 through r38940 from trunk into soc-2011-tomato
authorSergey Sharybin <sergey.vfx@gmail.com>
Tue, 2 Aug 2011 17:32:19 +0000 (17:32 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Tue, 2 Aug 2011 17:32:19 +0000 (17:32 +0000)
70 files changed:
CMakeLists.txt
SConstruct
build_files/cmake/Modules/FindSpacenav.cmake [new file with mode: 0644]
build_files/cmake/macros.cmake
build_files/scons/config/darwin-config.py
build_files/scons/config/linux2-config.py
build_files/scons/config/win32-vc-config.py
build_files/scons/config/win64-vc-config.py
build_files/scons/tools/btools.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/modules/bpy/path.py
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_action.c
source/blender/makesrna/intern/rna_userdef.c
source/blender/makesrna/intern/rna_wm.c
source/blender/python/intern/bpy_props.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 e1e84ec..fba52ca 100644 (file)
@@ -182,6 +182,7 @@ option(WITH_LZMA          "Enable best LZMA compression, (used for pointcache)"
 option(WITH_LIBMV         "Enable libmv structure from motion library" ON)
 
 # Misc
+option(WITH_INPUT_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)
@@ -455,6 +456,19 @@ if(UNIX AND NOT APPLE)
                endif()
        endif()
 
+       if (WITH_INPUT_NDOF)
+               find_package(Spacenav)
+               if(NOT SPACENAV_FOUND)
+                       set(WITH_INPUT_NDOF OFF)
+               endif()
+
+               # use generic names within blenders buildsystem.
+               if(SPACENAV_FOUND)
+                       set(NDOF_INCLUDE_DIRS ${SPACENAV_INCLUDE_DIRS})
+                       set(NDOF_LIBRARIES ${SPACENAV_LIBRARIES})
+               endif()
+       endif()
+
        # OpenSuse needs lutil, ArchLinux not, for now keep, can avoid by using --as-needed
        set(PLATFORM_LINKLIBS "-lutil -lc -lm -lpthread -lstdc++")
 
@@ -1032,6 +1046,10 @@ elseif(APPLE)
                set(TIFF_LIBPATH ${TIFF}/lib)
        endif()
 
+       if (WITH_INPUT_NDOF)
+               # linker needs "-weak_framework 3DconnexionClient"
+       endif()
+
        set(EXETYPE MACOSX_BUNDLE)
 
        set(CMAKE_C_FLAGS_DEBUG "-fno-strict-aliasing -g")
@@ -1323,6 +1341,7 @@ if(FIRST_RUN)
        info_cfg_option(WITH_OPENCOLLADA)
        info_cfg_option(WITH_FFTW3)
        info_cfg_option(WITH_INTERNATIONAL)
+       info_cfg_option(WITH_INPUT_NDOF)
 
        info_cfg_text("Compiler Options:")
        info_cfg_option(WITH_BUILDINFO)
index 053f414..e27f8ea 100644 (file)
@@ -241,6 +241,7 @@ if 'blenderlite' in B.targets:
     target_env_defs['BF_BUILDINFO'] = False
     target_env_defs['BF_NO_ELBEEM'] = True
     target_env_defs['WITH_BF_PYTHON'] = False
+    target_env_defs['WITH_BF_3DMOUSE'] = False
     
     # Merge blenderlite, let command line to override
     for k,v in target_env_defs.iteritems():
diff --git a/build_files/cmake/Modules/FindSpacenav.cmake b/build_files/cmake/Modules/FindSpacenav.cmake
new file mode 100644 (file)
index 0000000..206f361
--- /dev/null
@@ -0,0 +1,70 @@
+# - Find Spacenav library
+# Find the native Spacenav includes and library
+# This module defines
+#  SPACENAV_INCLUDE_DIRS, where to find spnav.h, Set when
+#                        SPACENAV_INCLUDE_DIR is found.
+#  SPACENAV_LIBRARIES, libraries to link against to use Spacenav.
+#  SPACENAV_ROOT_DIR, The base directory to search for Spacenav.
+#                    This can also be an environment variable.
+#  SPACENAV_FOUND, If false, do not try to use Spacenav.
+#
+# also defined, but not for general use are
+#  SPACENAV_LIBRARY, where to find the Spacenav library.
+
+#=============================================================================
+# Copyright 2011 Blender Foundation.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+
+# If SPACENAV_ROOT_DIR was defined in the environment, use it.
+IF(NOT SPACENAV_ROOT_DIR AND NOT $ENV{SPACENAV_ROOT_DIR} STREQUAL "")
+  SET(SPACENAV_ROOT_DIR $ENV{SPACENAV_ROOT_DIR})
+ENDIF()
+
+SET(_spacenav_SEARCH_DIRS
+  ${SPACENAV_ROOT_DIR}
+  /usr/local
+  /sw # Fink
+  /opt/local # DarwinPorts
+  /opt/csw # Blastwave
+)
+
+FIND_PATH(SPACENAV_INCLUDE_DIR
+  NAMES
+    spnav.h
+  HINTS
+    ${_spacenav_SEARCH_DIRS}
+  PATH_SUFFIXES
+    include
+)
+
+FIND_LIBRARY(SPACENAV_LIBRARY
+  NAMES
+    spnav
+  HINTS
+    ${_spacenav_SEARCH_DIRS}
+  PATH_SUFFIXES
+    lib64 lib
+  )
+
+# handle the QUIETLY and REQUIRED arguments and set SPACENAV_FOUND to TRUE if 
+# all listed variables are TRUE
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(Spacenav DEFAULT_MSG
+    SPACENAV_LIBRARY SPACENAV_INCLUDE_DIR)
+
+IF(SPACENAV_FOUND)
+  SET(SPACENAV_LIBRARIES ${SPACENAV_LIBRARY})
+  SET(SPACENAV_INCLUDE_DIRS ${SPACENAV_INCLUDE_DIR})
+ENDIF(SPACENAV_FOUND)
+
+MARK_AS_ADVANCED(
+  SPACENAV_INCLUDE_DIR
+  SPACENAV_LIBRARY
+)
index 3430145..ed200ab 100644 (file)
@@ -314,6 +314,10 @@ macro(setup_liblinks
        if(WITH_MEM_JEMALLOC)
                target_link_libraries(${target} ${JEMALLOC_LIBRARIES})
        endif()
+       if(WITH_INPUT_NDOF)
+               target_link_libraries(${target} ${NDOF_LIBRARIES})
+       endif()
+
        if(WIN32 AND NOT UNIX)
                target_link_libraries(${target} ${PTHREADS_LIBRARIES})
        endif()
index 0c51476..4a4bc4a 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_3DMOUSE = 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_3DMOUSE:
+       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 328cd4c..6fb9090 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_3DMOUSE = True
+BF_3DMOUSE_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_3DMOUSE:
+       LLIBS = LLIBS + [BF_3DMOUSE_LIB];
 ##LOPTS = --dynamic
 ##DYNLDFLAGS = -shared $(LDFLAGS)
 
index 89b246c..5a91852 100644 (file)
@@ -149,6 +149,8 @@ BF_OPENCOLLADA_INC = '${BF_OPENCOLLADA}/include'
 BF_OPENCOLLADA_LIB = 'OpenCOLLADAStreamWriter OpenCOLLADASaxFrameworkLoader OpenCOLLADAFramework OpenCOLLADABaseUtils GeneratedSaxParser MathMLSolver xml2 pcre buffer ftoa UTF'
 BF_OPENCOLLADA_LIBPATH = '${BF_OPENCOLLADA}/lib'
 
+WITH_BF_3DMOUSE = True
+
 #Ray trace optimization
 WITH_BF_RAYOPTIMIZATION = True
 BF_RAYOPTIMIZATION_SSE_FLAGS = ['/arch:SSE']
index 67db1c4..6717f12 100644 (file)
@@ -153,6 +153,8 @@ BF_OPENCOLLADA_INC = '${BF_OPENCOLLADA}/include'
 BF_OPENCOLLADA_LIB = 'OpenCOLLADAStreamWriter OpenCOLLADASaxFrameworkLoader OpenCOLLADAFramework OpenCOLLADABaseUtils GeneratedSaxParser MathMLSolver xml2 pcre buffer ftoa UTF'
 BF_OPENCOLLADA_LIBPATH = '${BF_OPENCOLLADA}/lib'
 
+WITH_BF_3DMOUSE = True
+
 #Ray trace optimization
 WITH_BF_RAYOPTIMIZATION = True
 BF_RAYOPTIMIZATION_SSE_FLAGS = ['/arch:SSE','/arch:SSE2']
index 996d6a3..fbe29df 100644 (file)
@@ -136,7 +136,7 @@ def validate_arguments(args, bc):
             'BF_NO_ELBEEM',
             'WITH_BF_CXX_GUARDEDALLOC',
             'WITH_BF_JEMALLOC', 'WITH_BF_STATICJEMALLOC', 'BF_JEMALLOC', 'BF_JEMALLOC_INC', 'BF_JEMALLOC_LIBPATH', 'BF_JEMALLOC_LIB', 'BF_JEMALLOC_LIB_STATIC',
-            'BUILDBOT_BRANCH'
+            'BUILDBOT_BRANCH', 'WITH_BF_3DMOUSE'
             ]
     
     # Have options here that scons expects to be lists
@@ -437,6 +437,8 @@ def read_opts(env, cfg, args):
         (BoolVariable('WITH_BF_PLAYER', 'Build blenderplayer if true', False)),
         (BoolVariable('WITH_BF_NOBLENDER', 'Do not build blender if true', False)),
 
+        (BoolVariable('WITH_BF_3DMOUSE', 'Build blender with support of 3D mouses', False)),
+
         ('CFLAGS', 'C only flags', []),
         ('CCFLAGS', 'Generic C and C++ flags', []),
         ('CXXFLAGS', 'C++ only flags', []),
index ccd763e..2180ddb 100644 (file)
@@ -97,6 +97,12 @@ if(WITH_GHOST_DEBUG)
        add_definitions(-DWITH_GHOST_DEBUG)
 endif()
 
+if(WITH_INPUT_NDOF)
+       add_definitions(-DWITH_INPUT_NDOF)
+       list(APPEND INC_SYS
+               ${NDOF_INCLUDE_DIRS}
+       )
+endif()
 
 if(WITH_HEADLESS OR WITH_GHOST_SDL)
        if(WITH_HEADLESS)
@@ -158,12 +164,21 @@ elseif(APPLE)
                        intern/GHOST_SystemCocoa.mm
                        intern/GHOST_SystemPathsCocoa.mm
                        intern/GHOST_WindowCocoa.mm
-                       
+
                        intern/GHOST_DisplayManagerCocoa.h
                        intern/GHOST_SystemCocoa.h
                        intern/GHOST_SystemPathsCocoa.h
                        intern/GHOST_WindowCocoa.h
                )
+
+               if(WITH_INPUT_NDOF)
+                       list(APPEND SRC
+                               intern/GHOST_NDOFManagerCocoa.mm
+
+                               intern/GHOST_NDOFManagerCocoa.h
+                       )
+               endif()
+
        else()
                list(APPEND SRC
                        intern/GHOST_DisplayManagerCarbon.cpp
@@ -215,6 +230,14 @@ elseif(UNIX)
                )
        endif()
 
+       if(WITH_INPUT_NDOF)
+               list(APPEND SRC
+                       intern/GHOST_NDOFManagerX11.cpp
+
+                       intern/GHOST_NDOFManagerX11.h
+               )
+       endif()
+
 elseif(WIN32)
        if(MSVC)
                set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /WX")
@@ -238,6 +261,15 @@ elseif(WIN32)
                intern/GHOST_WindowWin32.h
                intern/GHOST_TaskbarWin32.h
        )
+
+       if(WITH_INPUT_NDOF)
+               list(APPEND SRC
+                       intern/GHOST_NDOFManagerWin32.cpp
+
+                       intern/GHOST_NDOFManagerWin32.h
+               )
+       endif()
+
 endif()
 
 blender_add_lib(bf_intern_ghost "${SRC}" "${INC}" "${INC_SYS}")
index 7583723..a315dfa 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 69e1007..015ae78 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 6a4da5c..f24ab00 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 c65ec58..761cbe5 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']
@@ -76,7 +76,22 @@ else:
 if env['BF_GHOST_DEBUG']:
     defs.append('WITH_GHOST_DEBUG')
 else:
-       sources.remove('intern' + os.sep + 'GHOST_EventPrinter.cpp')
+    sources.remove('intern' + os.sep + 'GHOST_EventPrinter.cpp')
+
+if env['WITH_BF_3DMOUSE']:
+    defs.append('WITH_INPUT_NDOF')
+else:
+    sources.remove('intern' + os.sep + 'GHOST_NDOFManager.cpp')
+    try:
+        if window_system in ('win32-vc', 'win32-mingw', 'cygwin', 'linuxcross', 'win64-vc'):
+            sources.remove('intern' + os.sep + 'GHOST_NDOFManagerWin32.cpp')
+        elif window_system=='darwin':
+            sources.remove('intern' + os.sep + 'GHOST_NDOFManagerCocoa.mm')
+        else:
+            sources.remove('intern' + os.sep + 'GHOST_NDOFManagerX11.cpp')
+    except ValueError:
+        pass
+
 
 if window_system in ('win32-vc', 'win32-mingw', 'cygwin', 'linuxcross', 'win64-vc'):
     incs = env['BF_WINTAB_INC'] + ' ' + incs
index 7ba8d7d..6332d72 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 30d9aa3..47f7489 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 2e77da4..99990a4 100644 (file)
@@ -33,6 +33,7 @@
  
 #include "GHOST_Debug.h"
 #include "GHOST_DropTargetWin32.h"
+#include <ShellApi.h>
 
 #ifdef GHOST_DEBUG
 // utility
index 0a553b6..980e9f9 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 1483555..86b8797 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 70861b0..394aff0 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 dae6cb5..5a0f14c 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * ***** BEGIN GPL LICENSE BLOCK *****
  *
  * This program is free software; you can redistribute it and/or
  * 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_Debug.h"
+#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));
+
+       #ifdef WITH_INPUT_NDOF
+       GHOST_PRINT("WITH_INPUT_NDOF is defined!");
+       #else
+       GHOST_PRINT("WITH_INPUT_NDOF is not defined.");
+       #endif
+       }
+
+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 c9e0937..5bdbe7a 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * ***** BEGIN GPL LICENSE BLOCK *****
  *
  * This program is free software; you can redistribute it and/or
  * 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..27397b7
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * $Id$
+ *
+ * ***** 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..07811c5
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * $Id$
+ *
+ * ***** 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..099b163
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ * 
+ * ***** 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..31f7e07
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * $Id$
+ *
+ * ***** 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..8043af2
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * $Id$
+ *
+ * ***** 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..175041b
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * $Id$
+ *
+ * ***** 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 cb3e97f..64c2c21 100644 (file)
 
 
 GHOST_System::GHOST_System()
-: m_displayManager(0), m_timerManager(0), m_windowManager(0), m_eventManager(0), m_ndofManager(0)
+    : m_displayManager(0),
+      m_timerManager(0),
+      m_windowManager(0),
+      m_eventManager(0)
+#ifdef WITH_INPUT_NDOF
+      , m_ndofManager(0)
+#endif
 {
 }
 
@@ -194,12 +200,17 @@ bool GHOST_System::getFullScreen(void)
 
 bool GHOST_System::dispatchEvents()
 {
-       bool handled;
-       if (m_eventManager) {
-               handled = m_eventManager->dispatchEvents();
+       bool handled = false;
+
+#ifdef WITH_INPUT_NDOF
+       // NDOF Motion event is sent only once per dispatch, so do it now:
+       if (m_ndofManager) {
+               handled |= m_ndofManager->sendMotionEvent();
        }
-       else {
-               handled = false;
+#endif
+
+       if (m_eventManager) {
+               handled |= m_eventManager->dispatchEvents();
        }
 
        m_timerManager->fireTimers(getMilliSeconds());
@@ -243,18 +254,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 +284,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) {
@@ -328,10 +321,12 @@ GHOST_TSuccess GHOST_System::exit()
                delete m_eventManager;
                m_eventManager = 0;
        }
+#ifdef WITH_INPUT_NDOF
        if (m_ndofManager) {
                delete m_ndofManager;
                m_ndofManager = 0;
        }
+#endif
        return GHOST_kSuccess;
 }
 
index b5c64bf..c1e7091 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
         ***************************************************************************************/
@@ -268,11 +249,13 @@ public:
         */
        virtual inline GHOST_WindowManager* getWindowManager() const;
 
+#ifdef WITH_INPUT_NDOF
        /**
         * Returns a pointer to our n-degree of freedeom manager.
         * @return A pointer to our n-degree of freedeom manager.
         */
        virtual inline GHOST_NDOFManager* getNDOFManager() const;
+#endif
 
        /**
         * Returns the state of all modifier keys.
@@ -337,8 +320,10 @@ protected:
        /** The event manager. */
        GHOST_EventManager* m_eventManager;
 
-    /** The N-degree of freedom device manager */
-    GHOST_NDOFManager* m_ndofManager;
+#ifdef WITH_INPUT_NDOF
+       /** The N-degree of freedom device manager */
+       GHOST_NDOFManager* m_ndofManager;
+#endif
        
        /** Prints all the events. */
 #ifdef GHOST_DEBUG
@@ -364,10 +349,12 @@ inline GHOST_WindowManager* GHOST_System::getWindowManager() const
        return m_windowManager;
 }
 
+#ifdef WITH_INPUT_NDOF
 inline GHOST_NDOFManager* GHOST_System::getNDOFManager() const
 {
        return m_ndofManager;
 }
+#endif
 
 #endif // _GHOST_SYSTEM_H_
 
index ce77735..d20aed6 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 5c88523..17f0f2d 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,11 @@ GHOST_TSuccess GHOST_SystemCocoa::init()
        
     GHOST_TSuccess success = GHOST_System::init();
     if (success) {
+
+#ifdef WITH_INPUT_NDOF
+               m_ndofManager = new GHOST_NDOFManagerCocoa(*this);
+#endif
+
                //ProcessSerialNumber psn;
                
                //Carbon stuff to move window & menu to foreground
@@ -1007,6 +1012,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 becccc2..523d119 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 67cc214..3de7bbf 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 95b3456..c985c6d 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));
+
+       // multi-axis mouse (SpaceNavigator, etc.)
+       devices[0].usUsagePage = 0x01;
+       devices[0].usUsage = 0x08;
 
-       return RegisterRawInputDevices(&device, 1, sizeof(device));
-};
+       // 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,10 @@ GHOST_SystemWin32::GHOST_SystemWin32()
        this->handleKeyboardChange();
        // Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32.
        OleInitialize(0);
+
+#ifdef WITH_INPUT_NDOF
+       m_ndofManager = new GHOST_NDOFManagerWin32(*this);
+#endif
 }
 
 GHOST_SystemWin32::~GHOST_SystemWin32()
@@ -245,6 +222,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 +363,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 +412,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;
                                }
-                               else
+                               break;
+                       case GHOST_kKeyLeftControl:
                                {
-                                       key = GHOST_kKeyUnknown;
+                                       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;
+               }
+               
+               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 +693,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 +728,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 +759,102 @@ void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO * minmax)
        minmax->ptMinTrackSize.y=240;
 }
 
+#ifdef WITH_INPUT_NDOF
+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;
+}
+#endif // WITH_INPUT_NDOF
+
 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 +871,38 @@ 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:
+#ifdef WITH_INPUT_NDOF
+                                               if (system->processNDOF(raw))
+                                                       eventHandled = true;
+#endif
+                                               break;
                                        }
-                                       break;
+                               break;
+                               }
                                ////////////////////////////////////////////////////////////////////////
                                // Keyboard events, ignored
                                ////////////////////////////////////////////////////////////////////////
@@ -840,9 +912,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 +1200,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 +1221,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 729ad56..c5dff27 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 dd296fa..105f71b 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,9 @@ init(
        GHOST_TSuccess success = GHOST_System::init();
 
        if (success) {
+#ifdef WITH_INPUT_NDOF
+               m_ndofManager = new GHOST_NDOFManagerX11(*this);
+#endif
                m_displayManager = new GHOST_DisplayManagerX11(this);
 
                if (m_displayManager) {
@@ -275,7 +264,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 +375,6 @@ lastEventTime(Time default_time) {
     return data.timestamp;
 }
 
-
-
        bool 
 GHOST_SystemX11::
 processEvents(
@@ -428,6 +415,13 @@ processEvents(
                if (generateWindowExposeEvents()) {
                        anyProcessed = true;
                }
+
+#ifdef WITH_INPUT_NDOF
+               if (dynamic_cast<GHOST_NDOFManagerX11*>(m_ndofManager)->processEvents()) {
+                       anyProcessed = true;
+               }
+#endif
+               
        } while (waitForEvent && !anyProcessed);
        
        return anyProcessed;
@@ -611,6 +605,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 +638,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 +696,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 +815,8 @@ GHOST_SystemX11::processEvent(XEvent *xe)
        }
 }
 
+#if 0 // obsolete SpaceNav code
+
        void *
 GHOST_SystemX11::
 prepareNdofInfo(volatile GHOST_TEventNDOFData *currentNdofValues)
@@ -846,6 +829,8 @@ prepareNdofInfo(volatile GHOST_TEventNDOFData *currentNdofValues)
        return (void*)&sNdofInfo;
 }
 
+#endif
+
        GHOST_TSuccess 
 GHOST_SystemX11::
 getModifierKeys(
index 746cd4e..845243f 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 ef9ebdf..eddff8b 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 4055c3a..70914d9 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 a01745f..284fef9 100644 (file)
@@ -136,7 +136,7 @@ def display_name(name):
 
 def display_name_from_filepath(name):
     """
-    Returns the path stripped of directort and extension,
+    Returns the path stripped of directory and extension,
     ensured to be utf8 compatible.
     """
     name = _os.path.splitext(basename(name))[0]
@@ -204,7 +204,7 @@ def resolve_ncase(path):
 
 def ensure_ext(filepath, ext, case_sensitive=False):
     """
-    Return the path with the extension added its its not alredy set.
+    Return the path with the extension added if it is not already set.
 
     :arg ext: The extension to check for.
     :type ext: string
index 576709c..1feb1ee 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()
 
@@ -881,7 +878,7 @@ class USERPREF_PT_addons(bpy.types.Panel):
         if not user_addon_paths:
             user_script_path = bpy.utils.user_script_path()
             if user_script_path is not None:
-                user_addon_paths.append(os.path.join(user_script_path(), "addons"))
+                user_addon_paths.append(os.path.join(user_script_path, "addons"))
             user_addon_paths.append(os.path.join(bpy.utils.resource_path('USER'), "scripts", "addons"))
 
         for path in user_addon_paths:
index 71a0ecb..e567750 100644 (file)
@@ -274,6 +274,8 @@ class InputKeyMapPanel:
             row.prop(kmi, "type", text="", full_event=True)
         elif map_type == 'MOUSE':
             row.prop(kmi, "type", text="", full_event=True)
+        elif map_type == 'NDOF':
+            row.prop(kmi, "type", text="", full_event=True)
         elif map_type == 'TWEAK':
             subrow = row.row()
             subrow.prop(kmi, "type", text="")
@@ -309,7 +311,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 001151a..a180da1 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 d21b042..17876c6 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 f4da734..28a54b2 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 22bfcb8..fd44ca3 100644 (file)
@@ -1647,6 +1647,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_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 7ddf5df..0987356 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 e9e77dd..399157d 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 d5515bd..6e84c1a 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 *UNUSED(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->tvec[0] / sima->zoom;
+       float pan_y = pan_sensitivity * dt * ndof->tvec[1] / 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->tvec[2];
+
+       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 2e9544f..afab4ed 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 81a02b8..d5f8bce 100644 (file)
@@ -676,6 +676,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)
 {
@@ -2661,6 +2759,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 d563c07..e6fd9e8 100644 (file)
@@ -928,6 +928,244 @@ 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_axis_angle(struct wmNDOFMotionData* ndof, float axis[3])
+{
+       return ndof->dt * normalize_v3_v3(axis, ndof->rvec);
+}
+
+void ndof_to_quat(struct wmNDOFMotionData* ndof, float q[4])
+{
+       float axis[3];
+       float angle;
+
+       angle= ndof_to_axis_angle(ndof, axis);
+       axis_angle_to_quat(q, axis, angle);
+}
+
+static int ndof_orbit_invoke(bContext *C, wmOperator *UNUSED(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 && !is_zero_v3(ndof->rvec);
+       
+               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->tvec[2]) {
+                       // 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->tvec[2];
+                       rv3d->dist += zoom_distance;
+               }
+       
+               if (rv3d->viewlock == RV3D_LOCKED) {
+                       /* rotation not allowed -- explore panning options instead */
+                       float pan_vec[3] = {ndof->tvec[0], ndof->tvec[1], 0.0f};
+                       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_axis_angle(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->rvec[0];
+                               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->rvec[1];
+                               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 *UNUSED(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->tvec[0],
+                                   vertical_sensitivity * ndof->tvec[1],
+                                   forward_sensitivity * ndof->tvec[2]
+                                  };
+
+               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 +3433,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 ed1ed5b..046037a 100644 (file)
@@ -29,6 +29,9 @@
 
 /* defines VIEW3D_OT_fly modal operator */
 
+//#define NDOF_FLY_DEBUG
+//#define NDOF_FLY_DRAW_TOOMUCH // is this needed for ndof? - commented so redraw doesnt thrash - campbell
+
 #include "DNA_anim_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_object_types.h"
@@ -106,7 +109,7 @@ void fly_modal_keymap(wmKeyConfig *keyconf)
        wmKeyMap *keymap= WM_modalkeymap_get(keyconf, "View3D Fly Modal");
 
        /* this function is called for each spacetype, only needs to add map once */
-       if(keymap) return;
+       if (keymap) return;
 
        keymap= WM_modalkeymap_add(keyconf, "View3D Fly Modal", modal_items);
 
@@ -143,7 +146,6 @@ void fly_modal_keymap(wmKeyConfig *keyconf)
 
        /* assign map to operators */
        WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly");
-
 }
 
 typedef struct FlyInfo {
@@ -158,7 +160,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,17 +261,21 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
        fly->ar = CTX_wm_region(C);
        fly->scene= CTX_data_scene(C);
 
-       if(fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->id.lib) {
+#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;
        }
 
-       if(fly->v3d->ob_centre) {
+       if (fly->v3d->ob_centre) {
                BKE_report(op->reports, RPT_ERROR, "Cannot fly when the view is locked to an object");
                return FALSE;
        }
 
-       if(fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->constraints.first) {
+       if (fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->constraints.first) {
                BKE_report(op->reports, RPT_ERROR, "Cannot fly an object with constraints");
                return FALSE;
        }
@@ -283,11 +291,15 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
        fly->grid= 1.0f;
        fly->use_precision= 0;
 
+#ifdef NDOF_FLY_DRAW_TOOMUCH
+       fly->redraw= 1;
+#endif
        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();
 
@@ -307,7 +319,7 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
        fly->dist_backup= fly->rv3d->dist;
        if (fly->rv3d->persp==RV3D_CAMOB) {
                Object *ob_back;
-               if((U.uiflag & USER_CAM_LOCK_NO_PARENT)==0 && (fly->root_parent=fly->v3d->camera->parent)) {
+               if ((U.uiflag & USER_CAM_LOCK_NO_PARENT)==0 && (fly->root_parent=fly->v3d->camera->parent)) {
                        while(fly->root_parent->parent)
                                fly->root_parent= fly->root_parent->parent;
                        ob_back= fly->root_parent;
@@ -325,12 +337,22 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
                negate_v3_v3(fly->rv3d->ofs, fly->v3d->camera->obmat[3]);
 
                fly->rv3d->dist=0.0;
-       } else {
+       }
+       else {
                /* 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 +360,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);
@@ -353,9 +374,13 @@ static int flyEnd(bContext *C, FlyInfo *fly)
 
        float upvec[3];
 
-       if(fly->state == FLY_RUNNING)
+       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);
@@ -366,14 +391,14 @@ static int flyEnd(bContext *C, FlyInfo *fly)
        /* Revert to original view? */
                if (fly->persp_backup==RV3D_CAMOB) { /* a camera view */
                        Object *ob_back;
-                       if(fly->root_parent)ob_back= fly->root_parent;
-                       else                            ob_back= fly->v3d->camera;
+                       ob_back= (fly->root_parent) ? fly->root_parent : fly->v3d->camera;
 
                        /* store the original camera loc and rot */
                        object_tfm_restore(ob_back, fly->obtfm);
 
                        DAG_id_tag_update(&ob_back->id, OB_RECALC_OB);
-               } else {
+               }
+               else {
                        /* Non Camera we need to reset the view back to the original location bacause the user canceled*/
                        copy_qt_qt(rv3d->viewquat, fly->rot_backup);
                        copy_v3_v3(rv3d->ofs, fly->ofs_backup);
@@ -398,10 +423,13 @@ static int flyEnd(bContext *C, FlyInfo *fly)
        rv3d->rflag &= ~RV3D_NAVIGATING;
 //XXX2.5       BIF_view3d_previewrender_signal(fly->sa, PR_DBASE|PR_DISPRECT); /* not working at the moment not sure why */
 
-       if(fly->obtfm)
+       if (fly->obtfm)
                MEM_freeN(fly->obtfm);
 
-       if(fly->state == FLY_CONFIRM) {
+       if (fly->ndof)
+               MEM_freeN(fly->ndof);
+
+       if (fly->state == FLY_CONFIRM) {
                MEM_freeN(fly);
                return OPERATOR_FINISHED;
        }
@@ -417,7 +445,52 @@ static void flyEvent(FlyInfo *fly, wmEvent *event)
        }
        else 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:
@@ -439,7 +512,9 @@ static void flyEvent(FlyInfo *fly, wmEvent *event)
                                /*Mouse wheel delays range from 0.5==slow to 0.01==fast*/
                                time_wheel = 1.0f + (10.0f - (20.0f * MIN2(time_wheel, 0.5f))); /* 0-0.5 -> 0-5.0 */
 
-                               if (fly->speed<0.0f) fly->speed= 0.0f;
+                               if (fly->speed < 0.0f) {
+                                       fly->speed= 0.0f;
+                               }
                                else {
                                        if (event->shift)
                                                fly->speed += fly->grid*time_wheel * 0.1f;
@@ -458,7 +533,9 @@ static void flyEvent(FlyInfo *fly, wmEvent *event)
                                fly->time_lastwheel = time_currwheel;
                                time_wheel = 1.0f + (10.0f - (20.0f * MIN2(time_wheel, 0.5f))); /* 0-0.5 -> 0-5.0 */
 
-                               if (fly->speed>0) fly->speed=0;
+                               if (fly->speed > 0.0f) {
+                                       fly->speed=0;
+                               }
                                else {
                                        if (event->shift)
                                                fly->speed-= fly->grid*time_wheel * 0.1f;
@@ -528,14 +605,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 +689,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 +707,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;
@@ -605,23 +741,25 @@ static int flyApply(bContext *C, FlyInfo *fly)
                 *
                 * the mouse moves isnt linear */
 
-               if(moffset[0]) {
+               if (moffset[0]) {
                        moffset[0] /= ar->winx - (xmargin*2);
                        moffset[0] *= fabsf(moffset[0]);
                }
 
-               if(moffset[1]) {
+               if (moffset[1]) {
                        moffset[1] /= ar->winy - (ymargin*2);
                        moffset[1] *= fabsf(moffset[1]);
                }
 
                /* Should we redraw? */
-               if(fly->speed != 0.0f || moffset[0] || moffset[1] || fly->zlock || fly->xlock || dvec[0] || dvec[1] || dvec[2] ) {
+               if (fly->speed != 0.0f || moffset[0] || moffset[1] || fly->zlock || fly->xlock || dvec[0] || dvec[1] || dvec[2] ) {
                        float dvec_tmp[3];
                        double time_current; /*time how fast it takes for us to redraw, this is so simple scenes dont fly too fast */
                        float time_redraw;
                        float time_redraw_clamped;
-
+#ifdef NDOF_FLY_DRAW_TOOMUCH
+                       fly->redraw= 1;
+#endif
                        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 */
@@ -648,8 +786,8 @@ static int flyApply(bContext *C, FlyInfo *fly)
 
                                mul_m3_v3(mat, dvec_tmp);
                                mul_v3_fl(dvec_tmp, time_redraw * 200.0f * fly->grid);
-
-                       else {
+                       }
+                       else {
                                float roll; /* similar to the angle between the camera's up and the Z-up, but its very rough so just roll*/
 
                                /* rotate about the X axis- look up/down */
@@ -670,27 +808,28 @@ static int flyApply(bContext *C, FlyInfo *fly)
                                if (moffset[0]) {
 
                                        /* if we're upside down invert the moffset */
-                                       upvec[0]=0;
-                                       upvec[1]=1;
-                                       upvec[2]=0;
+                                       upvec[0]= 0.0f;
+                                       upvec[1]= 1.0f;
+                                       upvec[2]= 0.0f;
                                        mul_m3_v3(mat, upvec);
 
-                                       if(upvec[2] < 0.0f)
+                                       if (upvec[2] < 0.0f)
                                                moffset[0]= -moffset[0];
 
                                        /* make the lock vectors */
                                        if (fly->zlock) {
-                                               upvec[0]=0;
-                                               upvec[1]=0;
-                                               upvec[2]=1;
-                                       } else {
-                                               upvec[0]=0;
-                                               upvec[1]=1;
-                                               upvec[2]=0;
+                                               upvec[0]= 0.0f;
+                                               upvec[1]= 0.0f;
+                                               upvec[2]= 1.0f;
+                                       }
+                                       else {
+                                               upvec[0]= 0.0f;
+                                               upvec[1]= 1.0f;
+                                               upvec[2]= 0.0f;
                                                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*/
@@ -698,25 +837,26 @@ static int flyApply(bContext *C, FlyInfo *fly)
                                }
 
                                if (fly->zlock==2) {
-                                       upvec[0]=1;
-                                       upvec[1]=0;
-                                       upvec[2]=0;
+                                       upvec[0]= 1.0f;
+                                       upvec[1]= 0.0f;
+                                       upvec[2]= 0.0f;
                                        mul_m3_v3(mat, upvec);
 
                                        /*make sure we have some z rolling*/
                                        if (fabsf(upvec[2]) > 0.00001f) {
-                                               roll= upvec[2]*5;
-                                               upvec[0]=0; /*rotate the view about this axis*/
-                                               upvec[1]=0;
-                                               upvec[2]=1;
+                                               roll= upvec[2] * 5.0f;
+                                               upvec[0]= 0.0f; /*rotate the view about this axis*/
+                                               upvec[1]= 0.0f;
+                                               upvec[2]= 1.0f;
 
                                                mul_m3_v3(mat, upvec);
                                                axis_angle_to_quat( tmp_quat, upvec, roll*time_redraw_clamped*fly->zlock_momentum * FLY_ZUP_CORRECT_FAC); /* Rotate about the relative up vec */
                                                mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
 
                                                fly->zlock_momentum += FLY_ZUP_CORRECT_ACCEL;
-                                       } else {
-                                               fly->zlock=1; /* dont check until the view rotates again */
+                                       }
+                                       else {
+                                               fly->zlock= 1; /* dont check until the view rotates again */
                                                fly->zlock_momentum= 0.0f;
                                        }
                                }
@@ -727,8 +867,8 @@ static int flyApply(bContext *C, FlyInfo *fly)
                                        upvec[2]=1;
                                        mul_m3_v3(mat, upvec);
                                        /*make sure we have some z rolling*/
-                                       if (fabs(upvec[2]) > 0.00001) {
-                                               roll= upvec[2] * -5;
+                                       if (fabs(upvec[2]) > 0.00001f) {
+                                               roll= upvec[2] * -5.0f;
 
                                                upvec[0]= 1.0f; /*rotate the view about this axis*/
                                                upvec[1]= 0.0f;
@@ -740,7 +880,8 @@ static int flyApply(bContext *C, FlyInfo *fly)
                                                mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
 
                                                fly->xlock_momentum += 0.05f;
-                                       } else {
+                                       }
+                                       else {
                                                fly->xlock=1; /* see above */
                                                fly->xlock_momentum= 0.0f;
                                        }
@@ -784,96 +925,164 @@ 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;
-                                       }
+                       if (rv3d->persp==RV3D_CAMOB)
+                               move_camera(C, rv3d, fly, (fly->xlock || fly->zlock || moffset[0] || moffset[1]), fly->speed);
 
-                                       copy_m4_m4(prev_view_mat, view_mat);
+               }
+               else {
+                       /* we're not redrawing but we need to update the time else the view will jump */
+                       fly->time_lastdraw= PIL_check_seconds_timer();
+               }
+               /* end drawing */
+               copy_v3_v3(fly->dvec_prev, dvec);
+       }
 
-                                       id_key= &fly->root_parent->id;
+       return OPERATOR_FINISHED;
+}
 
-                               }
-                               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;
-                               }
+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;
 
-                               /* 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);
-                               }
+/*     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->tvec[0],
+                       vertical_sensitivity * ndof->tvec[1],
+                       forward_sensitivity * ndof->tvec[2]
+                       };
+
+               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->tvec[1];
+               }
+
+               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 (!is_zero_v3(trans)) {
+                       // 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_axis_angle(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);
                        }
-               } else
-                       /*were not redrawing but we need to update the time else the view will jump */
-                       fly->time_lastdraw= PIL_check_seconds_timer();
-               /* end drawing */
-               copy_v3_v3(fly->dvec_prev, dvec);
+
+                       rv3d->view = RV3D_VIEW_USER;
+               }
+               else {
+                       shouldRotate = FALSE;
+               }
        }
 
-/* moved to flyEnd() */
+       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)
 {
        RegionView3D *rv3d= CTX_wm_region_view3d(C);
        FlyInfo *fly;
 
-       if(rv3d->viewlock)
+       if (rv3d->viewlock)
                return OPERATOR_CANCELLED;
 
        fly= MEM_callocN(sizeof(FlyInfo), "FlyOperation");
 
        op->customdata= fly;
 
-       if(initFlyInfo(C, fly, op, event)==FALSE) {
+       if (initFlyInfo(C, fly, op, event)==FALSE) {
                MEM_freeN(op->customdata);
                return OPERATOR_CANCELLED;
        }
@@ -908,21 +1117,28 @@ 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;
 
        exit_code = flyEnd(C, fly);
 
-       if(exit_code!=OPERATOR_RUNNING_MODAL)
+       if (exit_code!=OPERATOR_RUNNING_MODAL)
                do_draw= TRUE;
 
-       if(do_draw) {
-               if(rv3d->persp==RV3D_CAMOB) {
+       if (do_draw) {
+               if (rv3d->persp==RV3D_CAMOB) {
                        WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, fly_object);
                }
 
+               // puts("redraw!"); // too frequent, commented with NDOF_FLY_DRAW_TOOMUCH for now
                ED_region_tag_redraw(CTX_wm_region(C));
        }
 
index 629e8a5..68682d3 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_axis_angle(struct wmNDOFMotionData* ndof, float axis[3]);
 
 /* view3d_fly.c */
 void view3d_keymap(struct wmKeyConfig *keyconf);
index 05ef79a..e47cb1d 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 2523d77..f3bb779 100644 (file)
@@ -1040,9 +1040,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;
@@ -1051,43 +1053,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);
 
@@ -2933,10 +2898,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;
@@ -3208,8 +3169,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) {
@@ -3263,11 +3222,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;
@@ -3323,8 +3277,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)) {
@@ -3378,8 +3330,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;
 
@@ -3554,7 +3504,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)) {
@@ -3663,10 +3612,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;
@@ -3690,8 +3635,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)) {
@@ -3806,10 +3749,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;
@@ -3830,8 +3769,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);
@@ -5356,8 +5293,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;
@@ -5412,7 +5347,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);
        }
@@ -5972,54 +5906,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 7052226..e4ca1cf 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 a37eda8..f9b9874 100644 (file)
@@ -371,7 +371,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 */
@@ -382,6 +381,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];
@@ -390,7 +393,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 */
 
@@ -583,6 +586,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 2982f8f..468309a 100644 (file)
@@ -135,7 +135,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;
 
@@ -200,11 +204,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 7fdb96f..cfedee3 100644 (file)
@@ -191,8 +191,9 @@ static void rna_Action_active_pose_marker_index_range(PointerRNA *ptr, int *min,
 
 
 static void rna_Action_frame_range_get(PointerRNA *ptr,float *values)
-{
-       calc_action_range(ptr->id.data, values, values+1, 1);
+{      /* don't include modifiers because they too easily can have very large
+        * ranges: MINAFRAMEF to MAXFRAMEF. */
+       calc_action_range(ptr->id.data, values, values+1, FALSE);
 }
 
 
index 82ec4ac..7c260b4 100644 (file)
@@ -2820,17 +2820,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 73221c4..a046be5 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 0ba80bf..a0ad1ff 100644 (file)
@@ -326,6 +326,11 @@ static int bpy_prop_callback_assign(struct PropertyRNA *prop, PyObject *update_c
 "   :type description: string\n" \
 
 
+#define BPY_PROPDEF_UNIT_DOC \
+"   :arg unit: Enumerator in ['NONE', 'LENGTH', 'AREA', 'VOLUME', 'ROTATION', 'TIME', 'VELOCITY', 'ACCELERATION'].\n" \
+"   :type unit: string\n"      \
+
+
 #define BPY_PROPDEF_UPDATE_DOC \
 "   :arg update: function to be called when this value is modified,\n" \
 "      This function must take 2 values (self, context) and return None.\n" \
@@ -639,8 +644,7 @@ BPY_PROPDEF_DESC_DOC
 "   :type options: set\n"
 "   :arg subtype: Enumerator in ['UNSIGNED', 'PERCENTAGE', 'FACTOR', 'ANGLE', 'TIME', 'DISTANCE', 'NONE'].\n"
 "   :type subtype: string\n"
-"   :arg unit: Enumerator in ['NONE', 'LENGTH', 'AREA', 'VOLUME', 'ROTATION', 'TIME', 'VELOCITY', 'ACCELERATION'].\n"
-"   :type unit: string\n"
+BPY_PROPDEF_UNIT_DOC
 BPY_PROPDEF_UPDATE_DOC
 );
 static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
@@ -679,7 +683,7 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
                BPY_PROPDEF_SUBTYPE_CHECK(FloatProperty, property_flag_items, property_subtype_number_items)
 
                if(pyunit && RNA_enum_value_from_id(property_unit_items, pyunit, &unit)==0) {
-                       PyErr_Format(PyExc_TypeError, "FloatProperty(unit='%s'): invalid unit");
+                       PyErr_Format(PyExc_TypeError, "FloatProperty(unit='%s'): invalid unit", pyunit);
                        return NULL;
                }
 
@@ -716,6 +720,7 @@ BPY_PROPDEF_DESC_DOC
 "   :type options: set\n"
 "   :arg subtype: Enumerator in ['COLOR', 'TRANSLATION', 'DIRECTION', 'VELOCITY', 'ACCELERATION', 'MATRIX', 'EULER', 'QUATERNION', 'AXISANGLE', 'XYZ', 'COLOR_GAMMA', 'LAYER', 'NONE'].\n"
 "   :type subtype: string\n"
+BPY_PROPDEF_UNIT_DOC
 "   :arg size: Vector dimensions in [1, and " STRINGIFY(PYRNA_STACK_ARRAY) "].\n"
 "   :type size: int\n"
 BPY_PROPDEF_UPDATE_DOC
@@ -727,7 +732,7 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
        BPY_PROPDEF_HEAD(FloatVectorProperty)
 
        if(srna) {
-               static const char *kwlist[]= {"attr", "name", "description", "default", "min", "max", "soft_min", "soft_max", "step", "precision", "options", "subtype", "size", "update", NULL};
+               static const char *kwlist[]= {"attr", "name", "description", "default", "min", "max", "soft_min", "soft_max", "step", "precision", "options", "subtype", "unit", "size", "update", NULL};
                const char *id=NULL, *name="", *description="";
                int id_len;
                float min=-FLT_MAX, max=FLT_MAX, soft_min=-FLT_MAX, soft_max=FLT_MAX, step=3, def[PYRNA_STACK_ARRAY]={0.0f};
@@ -738,15 +743,17 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
                int opts=0;
                char *pysubtype= NULL;
                int subtype= PROP_NONE;
+               char *pyunit= NULL;
+               int unit= PROP_UNIT_NONE;
                PyObject *update_cb= NULL;
 
                if (!PyArg_ParseTupleAndKeywords(args, kw,
-                                                "s#|ssOfffffiO!siO:FloatVectorProperty",
+                                                "s#|ssOfffffiO!ssiO:FloatVectorProperty",
                                                 (char **)kwlist, &id, &id_len,
                                                 &name, &description, &pydef,
                                                 &min, &max, &soft_min, &soft_max,
                                                 &step, &precision, &PySet_Type,
-                                                &pyopts, &pysubtype, &size,
+                                                &pyopts, &pysubtype, &pyunit, &size,
                                                 &update_cb))
                {
                        return NULL;
@@ -754,6 +761,11 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
 
                BPY_PROPDEF_SUBTYPE_CHECK(FloatVectorProperty, property_flag_items, property_subtype_array_items)
 
+               if(pyunit && RNA_enum_value_from_id(property_unit_items, pyunit, &unit)==0) {
+                       PyErr_Format(PyExc_TypeError, "FloatVectorProperty(unit='%s'): invalid unit", pyunit);
+                       return NULL;
+               }
+
                if(size < 1 || size > PYRNA_STACK_ARRAY) {
                        PyErr_Format(PyExc_TypeError, "FloatVectorProperty(size=%d): size must be between 0 and " STRINGIFY(PYRNA_STACK_ARRAY), size);
                        return NULL;
@@ -766,7 +778,7 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
                        return NULL;
                }
 
-               prop= RNA_def_property(srna, id, PROP_FLOAT, subtype);
+               prop= RNA_def_property(srna, id, PROP_FLOAT, subtype | unit);
                RNA_def_property_array(prop, size);
                if(pydef) RNA_def_property_float_array_default(prop, def);
                RNA_def_property_range(prop, min, max);
index b34ccfd..a489845 100644 (file)
@@ -379,6 +379,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 tvec[3]; // translation
+       float rvec[3]; // 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 ce3830b..0abae2e 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->tvec[0]= s * ghost->tx;
+       data->tvec[1]= s * ghost->ty;
+       data->tvec[2]= s * ghost->tz;
+
+       data->rvec[0]= s * ghost->rx;
+       data->rvec[1]= s * ghost->ry;
+       data->rvec[2]= 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 4c280fe..e228295 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 7e18676..2c337e7 100644 (file)
@@ -3416,7 +3416,49 @@ 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;
+       }
+
+       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)
 {
@@ -3455,6 +3497,7 @@ void wm_operatortype_init(void)
        WM_operatortype_append(WM_OT_search_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
@@ -3676,11 +3719,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_menu(keymap, "VIEW3D_MT_ndof_settings", 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");
@@ -3724,6 +3768,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 9b1695b..7d60107 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 ee080e7..579f20c 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) **************** */