svn merge -r37276:38555 https://svn.blender.org/svnroot/bf-blender/trunk/blender .
authorNathan Letwory <nathan@letworyinteractive.com>
Thu, 21 Jul 2011 09:40:59 +0000 (09:40 +0000)
committerNathan Letwory <nathan@letworyinteractive.com>
Thu, 21 Jul 2011 09:40:59 +0000 (09:40 +0000)
50 files changed:
CMakeLists.txt
build_files/scons/config/darwin-config.py
build_files/scons/config/linux2-config.py
intern/ghost/CMakeLists.txt
intern/ghost/GHOST_C-api.h
intern/ghost/GHOST_ISystem.h
intern/ghost/GHOST_Types.h
intern/ghost/SConscript
intern/ghost/intern/GHOST_C-api.cpp
intern/ghost/intern/GHOST_DisplayManagerWin32.cpp
intern/ghost/intern/GHOST_DropTargetWin32.cpp
intern/ghost/intern/GHOST_DropTargetWin32.h
intern/ghost/intern/GHOST_EventManager.cpp
intern/ghost/intern/GHOST_EventNDOF.h
intern/ghost/intern/GHOST_NDOFManager.cpp
intern/ghost/intern/GHOST_NDOFManager.h
intern/ghost/intern/GHOST_NDOFManagerCocoa.h [new file with mode: 0644]
intern/ghost/intern/GHOST_NDOFManagerCocoa.mm [new file with mode: 0644]
intern/ghost/intern/GHOST_NDOFManagerWin32.cpp [new file with mode: 0644]
intern/ghost/intern/GHOST_NDOFManagerWin32.h [new file with mode: 0644]
intern/ghost/intern/GHOST_NDOFManagerX11.cpp [new file with mode: 0644]
intern/ghost/intern/GHOST_NDOFManagerX11.h [new file with mode: 0644]
intern/ghost/intern/GHOST_System.cpp
intern/ghost/intern/GHOST_System.h
intern/ghost/intern/GHOST_SystemCocoa.h
intern/ghost/intern/GHOST_SystemCocoa.mm
intern/ghost/intern/GHOST_SystemPathsWin32.cpp
intern/ghost/intern/GHOST_SystemPathsWin32.h
intern/ghost/intern/GHOST_SystemWin32.cpp
intern/ghost/intern/GHOST_SystemWin32.h
intern/ghost/intern/GHOST_SystemX11.cpp
intern/ghost/intern/GHOST_SystemX11.h
intern/ghost/intern/GHOST_TaskbarWin32.h
intern/ghost/intern/GHOST_WindowWin32.h
release/scripts/startup/bl_ui/space_view3d.py
source/blender/editors/object/object_bake.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_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/makesdna/DNA_userdef_types.h
source/blender/makesrna/intern/rna_userdef.c
source/blender/windowmanager/WM_types.h
source/blender/windowmanager/intern/wm_event_system.c
source/blender/windowmanager/intern/wm_operators.c
source/blender/windowmanager/intern/wm_window.c
source/blender/windowmanager/wm_event_types.h

index ebf7aa8a6c065bf7821955d8a192eaf68c1618ff..cb59b35f406643457c785035aea9d1bdc621c5e1 100644 (file)
@@ -178,6 +178,7 @@ option(WITH_LZO           "Enable fast LZO compression (used for pointcache)" ON
 option(WITH_LZMA          "Enable best LZMA compression, (used for pointcache)" ON)
 
 # Misc
+option(WITH_NDOF "Enable NDOF input devices (SpaceNavigator and friends)" ON)
 option(WITH_RAYOPTIMIZATION    "Enable use of SIMD (SSE) optimizations for the raytracer" ON) 
 if(UNIX AND NOT APPLE)
        option(WITH_INSTALL_PORTABLE "Install redistributeable runtime, otherwise install into CMAKE_INSTALL_PREFIX" ON)
@@ -451,6 +452,15 @@ if(UNIX AND NOT APPLE)
                endif()
        endif()
 
+       if (WITH_NDOF)
+               if(CMAKE_SYSTEM_NAME MATCHES "Linux")
+                       set(NDOF /usr)
+                       set(NDOF_INC ${NDOF}/include)
+                       set(NDOF_LIB spnav)
+                       set(NDOF_LIBPATH ${FFTW3}/lib)
+               endif()
+       endif()
+
        # OpenSuse needs lutil, ArchLinux not, for now keep, can avoid by using --as-needed
        set(PLATFORM_LINKLIBS "-lutil -lc -lm -lpthread -lstdc++")
 
@@ -1028,6 +1038,11 @@ elseif(APPLE)
                set(TIFF_LIBPATH ${TIFF}/lib)
        endif()
 
+       if (WITH_NDOF)
+               # GHOST_NDOFManagerCocoa.mm needs "-fpascal-strings"
+               # linker needs "-weak_framework 3DconnexionClient"
+       endif()
+
        set(EXETYPE MACOSX_BUNDLE)
 
        set(CMAKE_C_FLAGS_DEBUG "-fno-strict-aliasing -g")
index 0c51476a6d0913ece91b7618198dcd722fdcbbb5..ca99357330f15271852fd28e6b5c71ad7770df5a 100644 (file)
@@ -264,7 +264,9 @@ if MACOSX_ARCHITECTURE == 'i386':
     BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse']
 elif MACOSX_ARCHITECTURE == 'x86_64':
     BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse','-msse2']
-    
+
+# SpaceNavigator and related 3D mice
+WITH_BF_SPACENAV = True
 
 #############################################################################
 ###################  various compile settings and flags    ##################
@@ -294,6 +296,9 @@ if WITH_BF_QUICKTIME == True:
        else:
                PLATFORM_LINKFLAGS = PLATFORM_LINKFLAGS+['-framework','QuickTime']
 
+if WITH_BF_SPACENAV:
+       PLATFORM_LINKFLAGS = PLATFORM_LINKFLAGS + ['-weak_framework','3DconnexionClient']
+
 #note to build succesfully on 10.3.9 SDK you need to patch  10.3.9 by adding the SystemStubs.a lib from 10.4
 LLIBS = ['stdc++', 'SystemStubs']
 
index 328cd4cdb288fd55e3b1bfdd7ec25de63ed09c12..2f7bdace7d37dae6871ff6a353a54dd831120092 100644 (file)
@@ -192,6 +192,10 @@ WITH_BF_OPENMP = True
 WITH_BF_RAYOPTIMIZATION = True
 BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse','-pthread']
 
+#SpaceNavigator and friends
+WITH_BF_NDOF = True
+BF_NDOF_LIB = 'spnav'
+
 ##
 CC = 'gcc'
 CXX = 'g++'
@@ -223,6 +227,8 @@ CXX_WARN = ['-Wno-invalid-offsetof', '-Wno-sign-compare']
 ##FIX_STUBS_WARNINGS = -Wno-unused
 
 LLIBS = ['util', 'c', 'm', 'dl', 'pthread', 'stdc++']
+if WITH_BF_NDOF:
+       LLIBS = LLIBS + [BF_NDOF_LIB];
 ##LOPTS = --dynamic
 ##DYNLDFLAGS = -shared $(LDFLAGS)
 
index ccd763ef42ce74818c70f6950efbfe80e8ac448c..b69dff607e8890eb5222ebd6c6149df0dfa99d05 100644 (file)
@@ -158,11 +158,13 @@ elseif(APPLE)
                        intern/GHOST_SystemCocoa.mm
                        intern/GHOST_SystemPathsCocoa.mm
                        intern/GHOST_WindowCocoa.mm
-                       
+                       intern/GHOST_NDOFManagerCocoa.mm
+
                        intern/GHOST_DisplayManagerCocoa.h
                        intern/GHOST_SystemCocoa.h
                        intern/GHOST_SystemPathsCocoa.h
                        intern/GHOST_WindowCocoa.h
+                       intern/GHOST_NDOFManagerCocoa.h
                )
        else()
                list(APPEND SRC
@@ -197,11 +199,13 @@ elseif(UNIX)
                intern/GHOST_SystemX11.cpp
                intern/GHOST_SystemPathsX11.cpp
                intern/GHOST_WindowX11.cpp
+               intern/GHOST_NDOFManagerX11.cpp
 
                intern/GHOST_DisplayManagerX11.h
                intern/GHOST_SystemX11.h
                intern/GHOST_SystemPathsX11.h
                intern/GHOST_WindowX11.h
+               intern/GHOST_NDOFManagerX11.h
        )
 
        if(NOT WITH_INSTALL_PORTABLE)
@@ -230,6 +234,7 @@ elseif(WIN32)
                intern/GHOST_SystemPathsWin32.cpp
                intern/GHOST_WindowWin32.cpp
                intern/GHOST_DropTargetWin32.cpp
+               intern/GHOST_NDOFManagerWin32.cpp
 
                intern/GHOST_DisplayManagerWin32.h
                intern/GHOST_DropTargetWin32.h
@@ -237,6 +242,7 @@ elseif(WIN32)
                intern/GHOST_SystemPathsWin32.h
                intern/GHOST_WindowWin32.h
                intern/GHOST_TaskbarWin32.h
+               intern/GHOST_NDOFManagerWin32.h
        )
 endif()
 
index 75837239c4ac75fb30152750598a066aa98f4024..a315dfa85e9b6d0828c7da3d7090a78de2ec4867 100644 (file)
@@ -288,21 +288,6 @@ extern GHOST_TSuccess GHOST_SetProgressBar(GHOST_WindowHandle windowhandle, floa
  * @param windowhandle The handle to the window
  */
 extern GHOST_TSuccess GHOST_EndProgressBar(GHOST_WindowHandle windowhandle);
-       
-       
-/***************************************************************************************
- ** N-degree of freedom device management functionality
- ***************************************************************************************/
-/**
-* Open N-degree of freedom devices
- */
-extern int GHOST_OpenNDOF(GHOST_SystemHandle systemhandle, 
-                           GHOST_WindowHandle windowhandle,
-                          GHOST_NDOFLibraryInit_fp setNdofLibraryInit, 
-                          GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
-                          GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen
-                          );
 
 /***************************************************************************************
  ** Cursor management functionality
index 69e10070be57a87c87fbcc8848b0ae296b9eb465..015ae780bea7e74d70fde9e0e62f50ebedf6ea97 100644 (file)
@@ -298,22 +298,6 @@ public:
         */
        virtual GHOST_TSuccess removeEventConsumer(GHOST_IEventConsumer* consumer) = 0;
 
-        /***************************************************************************************
-        ** N-degree of freedom device management functionality
-        ***************************************************************************************/
-
-   /**
-    * Starts the N-degree of freedom device manager
-    */
-   virtual int openNDOF(GHOST_IWindow*,
-       GHOST_NDOFLibraryInit_fp setNdofLibraryInit, 
-       GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
-       GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen
-       // original patch only
-      // GHOST_NDOFEventHandler_fp setNdofEventHandler
-       ) = 0;
-
-
        /***************************************************************************************
         ** Cursor management functionality
         ***************************************************************************************/
index 6a4da5c9d38cef1c2a3570e041c382ec628fcd0e..f24ab00acd35f21d15ab3c3d3b31896fa8fab5b3 100644 (file)
@@ -47,11 +47,6 @@ typedef unsigned short               GHOST_TUns16;
 typedef        int                                     GHOST_TInt32;
 typedef        unsigned int            GHOST_TUns32;
 
-#ifdef WIN32
-#define WM_BLND_NDOF_AXIS      WM_USER + 1
-#define WM_BLND_NDOF_BTN       WM_USER + 2
-#endif
-
 #if defined(WIN32) && !defined(FREE_WINDOWS)
 typedef __int64                                GHOST_TInt64;
 typedef unsigned __int64       GHOST_TUns64;
@@ -440,37 +435,33 @@ typedef struct {
        GHOST_TUns8 **strings;
 } GHOST_TStringArray;
 
+typedef enum {
+       GHOST_kNotStarted,
+       GHOST_kStarting,
+       GHOST_kInProgress,
+       GHOST_kFinishing,
+       GHOST_kFinished
+       } GHOST_TProgress;
 
-/* original patch used floats, but the driver return ints and uns. We will calibrate in view, no sense on doing conversions twice */
-/* as all USB device controls are likely to use ints, this is also more future proof */
-//typedef struct {
-//   /** N-degree of freedom device data */
-//   float tx, ty, tz;   /** -x left, +y up, +z forward */
-//   float rx, ry, rz;
-//   float dt;
-//} GHOST_TEventNDOFData;
+typedef struct {
+       /** N-degree of freedom device data v3 [GSoC 2010] */
+       // Each component normally ranges from -1 to +1, but can exceed that.
+       // These use blender standard view coordinates, with positive rotations being CCW about the axis.
+       float tx, ty, tz; // translation
+       float rx, ry, rz; // rotation:
+               // axis = (rx,ry,rz).normalized
+               // amount = (rx,ry,rz).magnitude [in revolutions, 1.0 = 360 deg]
+       float dt; // time since previous NDOF Motion event
+       GHOST_TProgress progress; // Starting, InProgress or Finishing (for modal handlers)
+} GHOST_TEventNDOFMotionData;
+
+typedef enum { GHOST_kPress, GHOST_kRelease } GHOST_TButtonAction;
+       // good for mouse or other buttons too, hmmm?
 
 typedef struct {
-   /** N-degree of freedom device data v2*/
-   int changed;
-   GHOST_TUns64 client;
-   GHOST_TUns64 address;
-   GHOST_TInt16 tx, ty, tz;   /** -x left, +y up, +z forward */
-   GHOST_TInt16 rx, ry, rz;
-   GHOST_TInt16 buttons;
-   GHOST_TUns64 time;
-   GHOST_TUns64 delta;
-} GHOST_TEventNDOFData;
-
-typedef int     (*GHOST_NDOFLibraryInit_fp)(void);
-typedef void    (*GHOST_NDOFLibraryShutdown_fp)(void* deviceHandle);
-typedef void*   (*GHOST_NDOFDeviceOpen_fp)(void* platformData);
-
-// original patch windows callback. In mac os X version the callback is internal to the plug-in and post an event to main thead.
-// not necessary faster, but better integration with other events. 
-
-//typedef int     (*GHOST_NDOFEventHandler_fp)(float* result7, void* deviceHandle, unsigned int message, unsigned int* wParam, unsigned long* lParam);
-//typedef void     (*GHOST_NDOFCallBack_fp)(GHOST_TEventNDOFDataV2 *VolDatas);
+       GHOST_TButtonAction action;
+       short button;
+} GHOST_TEventNDOFButtonData;
 
 typedef struct {
        /** The key code. */
index c65ec58a97b4412eb0fbb78317814317d3e0dacf..3dec748ce31100e574b9d693beb125241c178447 100644 (file)
@@ -11,7 +11,7 @@ if window_system == 'darwin':
     sources += env.Glob('intern/*.mm')
 
 
-pf = ['GHOST_DisplayManager', 'GHOST_System', 'GHOST_SystemPaths', 'GHOST_Window', 'GHOST_DropTarget']
+pf = ['GHOST_DisplayManager', 'GHOST_System', 'GHOST_SystemPaths', 'GHOST_Window', 'GHOST_DropTarget', 'GHOST_NDOFManager']
 defs=['_USE_MATH_DEFINES']
 
 incs = '. ../string #extern/glew/include #source/blender/imbuf #source/blender/makesdna ' + env['BF_OPENGL_INC']
index 7ba8d7db411c3d281a1bb9dce7ea47c8804d1165..6332d72a42f5f23f5dc16a82c7708226558f1b4f 100644 (file)
@@ -275,23 +275,6 @@ GHOST_TSuccess GHOST_EndProgressBar(GHOST_WindowHandle windowhandle)
 }
 
 
-int GHOST_OpenNDOF(GHOST_SystemHandle systemhandle, GHOST_WindowHandle windowhandle,
-   GHOST_NDOFLibraryInit_fp setNdofLibraryInit, 
-    GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
-    GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen)
-  //original patch only
-  /*  GHOST_NDOFEventHandler_fp setNdofEventHandler)*/
-{
-       GHOST_ISystem* system = (GHOST_ISystem*) systemhandle;
-
-    return system->openNDOF((GHOST_IWindow*) windowhandle,
-        setNdofLibraryInit, setNdofLibraryShutdown, setNdofDeviceOpen);
-//     original patch
-//        setNdofLibraryInit, setNdofLibraryShutdown, setNdofDeviceOpen, setNdofEventHandler);
-}
-
-
-
 GHOST_TStandardCursor GHOST_GetCursorShape(GHOST_WindowHandle windowhandle)
 {
        GHOST_IWindow* window = (GHOST_IWindow*) windowhandle;
index 30d9aa31207faf3f1c84876b6df809536f80536c..47f748927ab5473958a6310b378c410016afd41a 100644 (file)
 #include "GHOST_DisplayManagerWin32.h"
 #include "GHOST_Debug.h"
 
-// We do not support multiple monitors at the moment
+#define _WIN32_WINNT 0x501 // require Windows XP or newer
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
+
+// We do not support multiple monitors at the moment
 #define COMPILE_MULTIMON_STUBS
 #ifndef FREE_WINDOWS
 #include <multimon.h>
index 2e77da42b310068ab4a88c196e81eb66e8e3f620..99990a46c2a1dd1495e6f41d524009e04da162da 100644 (file)
@@ -33,6 +33,7 @@
  
 #include "GHOST_Debug.h"
 #include "GHOST_DropTargetWin32.h"
+#include <ShellApi.h>
 
 #ifdef GHOST_DEBUG
 // utility
index 0a553b6701eb24265a7842b09ed988a40d51405b..980e9f9fe9b464199018c9c95b22a860137f0a2e 100644 (file)
@@ -33,7 +33,6 @@
 #ifndef _GHOST_DROP_TARGET_WIN32_H_
 #define _GHOST_DROP_TARGET_WIN32_H_
 
-#include <windows.h>
 #include <string.h>
 #include <GHOST_Types.h>
 #include "GHOST_WindowWin32.h"
index 1483555c362fefc4967a8ea8cf6e0ccffcd5a83b..86b8797303857e2ad0625987bd5a5deaf8ae02cd 100644 (file)
@@ -42,7 +42,7 @@
 #include "GHOST_EventManager.h"
 #include <algorithm>
 #include "GHOST_Debug.h"
-
+#include <stdio.h> // [mce] temp debug
 
 GHOST_EventManager::GHOST_EventManager()
 {
index 70861b08fc61931ca444dfb7220777b11ed460c7..394aff0493fc4edc17deeb5abc0d7ee7609df284 100644 (file)
  *
  * ***** END GPL LICENSE BLOCK *****
  */
-
-/** \file ghost/intern/GHOST_EventNDOF.h
- *  \ingroup GHOST
- */
-
  
 
 #ifndef _GHOST_EVENT_NDOF_H_
 
 #include "GHOST_Event.h"
 
-/**
- * N-degree of freedom device event.
- */
-class GHOST_EventNDOF : public GHOST_Event
-{
-public:
-       /**
-        * Constructor.
-        * @param msec          The time this event was generated.
-        * @param type          The type of this event.
-        * @param x                     The x-coordinate of the location the cursor was at at the time of the event.
-        * @param y                     The y-coordinate of the location the cursor was at at the time of the event.
-        */
-       GHOST_EventNDOF(GHOST_TUns64 msec, GHOST_TEventType type, GHOST_IWindow* window, 
-        GHOST_TEventNDOFData data)
-               : GHOST_Event(msec, type, window)
-       {
-               m_ndofEventData = data;
-               m_data = &m_ndofEventData;
-       }
 
-protected:
-       /** translation & rotation from the device. */
-       GHOST_TEventNDOFData m_ndofEventData;
-};
+class GHOST_EventNDOFMotion : public GHOST_Event
+       {
+       protected:
+               GHOST_TEventNDOFMotionData m_axisData;
+       
+       public:
+               GHOST_EventNDOFMotion(GHOST_TUns64 time, GHOST_IWindow* window)
+                       : GHOST_Event(time, GHOST_kEventNDOFMotion, window)
+                       {
+                       m_data = &m_axisData;
+                       }
+       };
+
+
+class GHOST_EventNDOFButton : public GHOST_Event
+       {
+       protected:
+               GHOST_TEventNDOFButtonData m_buttonData;
+       
+       public:
+               GHOST_EventNDOFButton(GHOST_TUns64 time, GHOST_IWindow* window)
+                       : GHOST_Event(time, GHOST_kEventNDOFButton, window)
+                       {
+                       m_data = &m_buttonData;
+                       }
+       };
 
 
 #endif // _GHOST_EVENT_NDOF_H_
-
index dae6cb5b5445bc89c26ff95f4b41fad49eb5ef04..06c792128cd8accf4f22b4eecc06d433b73e7c75 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
- * Contributor(s): none yet.
+ * Contributor(s):
+ *   Mike Erwin
  *
  * ***** END GPL LICENSE BLOCK *****
  */
 
-/** \file ghost/intern/GHOST_NDOFManager.cpp
- *  \ingroup GHOST
- */
+#include "GHOST_NDOFManager.h"
+#include "GHOST_EventNDOF.h"
+#include "GHOST_EventKey.h"
+#include "GHOST_WindowManager.h"
+#include <string.h> // for memory functions
+#include <stdio.h> // for error/info reporting
+#include <math.h>
 
+#ifdef DEBUG_NDOF_MOTION
+// printable version of each GHOST_TProgress value
+static const char* progress_string[] =
+       {"not started","starting","in progress","finishing","finished"};
+#endif
 
-#include <stdio.h> /* just for printf */
+#ifdef DEBUG_NDOF_BUTTONS
+static const char* ndof_button_names[] = {
+       // used internally, never sent
+       "NDOF_BUTTON_NONE",
+       // these two are available from any 3Dconnexion device
+       "NDOF_BUTTON_MENU",
+       "NDOF_BUTTON_FIT",
+       // standard views
+       "NDOF_BUTTON_TOP",
+       "NDOF_BUTTON_BOTTOM",
+       "NDOF_BUTTON_LEFT",
+       "NDOF_BUTTON_RIGHT",
+       "NDOF_BUTTON_FRONT",
+       "NDOF_BUTTON_BACK",
+       // more views
+       "NDOF_BUTTON_ISO1",
+       "NDOF_BUTTON_ISO2",
+       // 90 degree rotations
+       "NDOF_BUTTON_ROLL_CW",
+       "NDOF_BUTTON_ROLL_CCW",
+       "NDOF_BUTTON_SPIN_CW",
+       "NDOF_BUTTON_SPIN_CCW",
+       "NDOF_BUTTON_TILT_CW",
+       "NDOF_BUTTON_TILT_CCW",
+       // device control
+       "NDOF_BUTTON_ROTATE",
+       "NDOF_BUTTON_PANZOOM",
+       "NDOF_BUTTON_DOMINANT",
+       "NDOF_BUTTON_PLUS",
+       "NDOF_BUTTON_MINUS",
+       // general-purpose buttons
+       "NDOF_BUTTON_1",
+       "NDOF_BUTTON_2",
+       "NDOF_BUTTON_3",
+       "NDOF_BUTTON_4",
+       "NDOF_BUTTON_5",
+       "NDOF_BUTTON_6",
+       "NDOF_BUTTON_7",
+       "NDOF_BUTTON_8",
+       "NDOF_BUTTON_9",
+       "NDOF_BUTTON_10",
+       };
+#endif
 
-#include "GHOST_NDOFManager.h"
+static const NDOF_ButtonT SpaceNavigator_HID_map[] =
+       {
+       NDOF_BUTTON_MENU,
+       NDOF_BUTTON_FIT
+       };
 
+static const NDOF_ButtonT SpaceExplorer_HID_map[] =
+       {
+       NDOF_BUTTON_1,
+       NDOF_BUTTON_2,
+       NDOF_BUTTON_TOP,
+       NDOF_BUTTON_LEFT,
+       NDOF_BUTTON_RIGHT,
+       NDOF_BUTTON_FRONT,
+       NDOF_BUTTON_NONE, // esc key
+       NDOF_BUTTON_NONE, // alt key
+       NDOF_BUTTON_NONE, // shift key
+       NDOF_BUTTON_NONE, // ctrl key
+       NDOF_BUTTON_FIT,
+       NDOF_BUTTON_MENU,
+       NDOF_BUTTON_PLUS,
+       NDOF_BUTTON_MINUS,
+       NDOF_BUTTON_ROTATE
+       };
 
-// the variable is outside the class because it must be accessed from plugin
-static volatile GHOST_TEventNDOFData currentNdofValues = {0,0,0,0,0,0,0,0,0,0,0};
+static const NDOF_ButtonT SpacePilotPro_HID_map[] =
+       {
+       NDOF_BUTTON_MENU,
+       NDOF_BUTTON_FIT,
+       NDOF_BUTTON_TOP,
+       NDOF_BUTTON_LEFT,
+       NDOF_BUTTON_RIGHT,
+       NDOF_BUTTON_FRONT,
+       NDOF_BUTTON_BOTTOM,
+       NDOF_BUTTON_BACK,
+       NDOF_BUTTON_ROLL_CW,
+       NDOF_BUTTON_ROLL_CCW,
+       NDOF_BUTTON_ISO1,
+       NDOF_BUTTON_ISO2,
+       NDOF_BUTTON_1,
+       NDOF_BUTTON_2,
+       NDOF_BUTTON_3,
+       NDOF_BUTTON_4,
+       NDOF_BUTTON_5,
+       NDOF_BUTTON_6,
+       NDOF_BUTTON_7,
+       NDOF_BUTTON_8,
+       NDOF_BUTTON_9,
+       NDOF_BUTTON_10,
+       NDOF_BUTTON_NONE, // esc key
+       NDOF_BUTTON_NONE, // alt key
+       NDOF_BUTTON_NONE, // shift key
+       NDOF_BUTTON_NONE, // ctrl key
+       NDOF_BUTTON_ROTATE,
+       NDOF_BUTTON_PANZOOM,
+       NDOF_BUTTON_DOMINANT,
+       NDOF_BUTTON_PLUS,
+       NDOF_BUTTON_MINUS
+       };
 
-#if !defined(_WIN32) && !defined(__APPLE__)
-#include "GHOST_SystemX11.h"
-#endif
+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));
+       }
 
-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);
-               #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;
-}
+void GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short product_id)
+       {
+       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 --
+                               // keep unknown device type so rogue button events will get discarded
+                               // "mystery device" owners can help build another HID_map for their hardware
+                               case 0xC623:
+                                       puts("ndof: SpaceTraveler not supported, please file a bug report");
+                                       m_buttonCount = 8;
+                                       break;
+                               case 0xC625:
+                                       puts("ndof: SpacePilot not supported, please file a bug report");
+                                       m_buttonCount = 21;
+                                       break;
+
+                               default: printf("ndof: unknown Logitech product %04hx\n", product_id);
+                               }
+                       break;
+               default:
+                       printf("ndof: unknown device %04hx:%04hx\n", vendor_id, product_id);
+               }
+
+       m_buttonMask = ~(-1 << m_buttonCount);
+
+       #ifdef DEBUG_NDOF_BUTTONS
+       printf("ndof: %d buttons -> hex:%X\n", m_buttonCount, m_buttonMask);
+       #endif
+       }
+
+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_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();
+
+       const float scale = 1.f / 350.f; // 3Dconnexion devices send +/- 350 usually
+
+       // probable future enhancement
+       // scale *= U.ndof_sensitivity;
+
+       data->tx = scale * m_translation[0];
+       data->ty = scale * m_translation[1];
+       data->tz = scale * m_translation[2];
+
+       data->rx = scale * m_rotation[0];
+       data->ry = scale * m_rotation[1];
+       data->rz = scale * m_rotation[2];
+
+       data->dt = 0.001f * (m_motionTime - m_prevMotionTime); // in seconds
+
+       bool handMotion = !nearHomePosition(data, m_deadZone);
+
+       // determine what kind of motion event to send (Starting, InProgress, Finishing)
+       // and where that leaves this NDOF manager (NotStarted, InProgress, Finished)
+       switch (m_motionState)
+               {
+               case GHOST_kNotStarted:
+               case GHOST_kFinished:
+                       if (handMotion)
+                               {
+                               data->progress = GHOST_kStarting;
+                               m_motionState = GHOST_kInProgress;
+                               // prev motion time will be ancient, so just make up something reasonable
+                               data->dt = 0.0125f;
+                               }
+                       else
+                               {
+                               // send no event and keep current state
+                               delete event;
+                               return false;
+                               }
+                       break;
+               case GHOST_kInProgress:
+                       if (handMotion)
+                               {
+                               data->progress = GHOST_kInProgress;
+                               // keep InProgress state
+                               }
+                       else
+                               {
+                               data->progress = GHOST_kFinishing;
+                               m_motionState = GHOST_kFinished;
+                               }
+                       break;
+               }
+
+       #ifdef DEBUG_NDOF_MOTION
+       printf("ndof motion sent -- %s\n", progress_string[data->progress]);
+
+       // show details about this motion event
+       printf("    T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f) dt=%.3f\n",
+               data->tx, data->ty, data->tz,
+               data->rx, data->ry, data->rz,
+               data->dt);
+       #endif
+
+       m_system.pushEvent(event);
+
+       m_prevMotionTime = m_motionTime;
+
+       return true;
+       }
index c9e09370e0989a63eeb668d7c918924bca3c5ae4..9c67daa44125d116938f17b10235a1cb9daa8fa7 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
- * Contributor(s): none yet.
+ * Contributor(s):
+ *   Mike Erwin
  *
  * ***** END GPL LICENSE BLOCK *****
  */
-
-/** \file ghost/intern/GHOST_NDOFManager.h
- *  \ingroup GHOST
- */
-
  
 #ifndef _GHOST_NDOFMANAGER_H_
 #define _GHOST_NDOFMANAGER_H_
 
 #include "GHOST_System.h"
-#include "GHOST_IWindow.h"
 
 
+// #define DEBUG_NDOF_MOTION
+#define DEBUG_NDOF_BUTTONS
+
+typedef enum {
+       NDOF_UnknownDevice, // <-- motion will work fine, buttons are ignored
+       NDOF_SpaceNavigator,
+       NDOF_SpaceExplorer,
+       NDOF_SpacePilotPro
+       } 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
+       // TODO: expose these to keymap editor so users can assign functions
+       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
+       void setDevice(unsigned short vendor_id, unsigned short product_id);
+
+       // filter out small/accidental/uncalibrated motions by
+       // setting up a "dead zone" around home position
+       // set to 0 to disable
+       // 0.1 is a safe and reasonable value
+       void setDeadZone(float);
+
+       // the latest raw axis data from the device
+       // NOTE: axis data should be in blender view coordinates
+       //       +X is to the right
+       //       +Y is up
+       //       +Z is out of the screen
+       //       for rotations, look from origin to each +axis
+       //       rotations are + when CCW, - when CW
+       // each platform is responsible for getting axis data into this form
+       // these values should not be scaled (just shuffled or flipped)
+       void updateTranslation(short t[3], GHOST_TUns64 time);
+       void updateRotation(short r[3], GHOST_TUns64 time);
+
+       // the latest raw button data from the device
+       // use HID button encoding (not NDOF_ButtonT)
+       void updateButton(int button_number, bool press, GHOST_TUns64 time);
+       void updateButtons(int button_bits, GHOST_TUns64 time);
+       // NDOFButton events are sent immediately
+
+       // processes and sends most recent raw data as an NDOFMotion event
+       // returns whether an event was sent
+       bool sendMotionEvent();
 
 protected:
-    void* m_DeviceHandle;
-};
+       GHOST_System& m_system;
+
+private:
+       void sendButtonEvent(NDOF_ButtonT, bool press, GHOST_TUns64 time, GHOST_IWindow*);
+       void sendKeyEvent(GHOST_TKey, bool press, GHOST_TUns64 time, GHOST_IWindow*);
 
+       NDOF_DeviceT m_deviceType;
+       int m_buttonCount;
+       int m_buttonMask;
+
+       short m_translation[3];
+       short m_rotation[3];
+       int m_buttons; // bit field
+
+       GHOST_TUns64 m_motionTime; // in milliseconds
+       GHOST_TUns64 m_prevMotionTime; // time of most recent Motion event sent
+
+       GHOST_TProgress m_motionState;
+       bool m_motionEventPending;
+       float m_deadZone; // discard motion with each component < this
+};
 
 #endif
diff --git a/intern/ghost/intern/GHOST_NDOFManagerCocoa.h b/intern/ghost/intern/GHOST_NDOFManagerCocoa.h
new file mode 100644 (file)
index 0000000..d8711e9
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version. 
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s):
+ *   Mike Erwin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef _GHOST_NDOFMANAGERCOCOA_H_
+#define _GHOST_NDOFMANAGERCOCOA_H_
+
+#include "GHOST_NDOFManager.h"
+
+// Event capture is handled within the NDOF manager on Macintosh,
+// so there's no need for SystemCocoa to look for them.
+
+class GHOST_NDOFManagerCocoa : public GHOST_NDOFManager
+{
+public:
+       GHOST_NDOFManagerCocoa(GHOST_System&);
+
+       ~GHOST_NDOFManagerCocoa();
+
+       // whether multi-axis functionality is available (via the OS or driver)
+       // does not imply that a device is plugged in or being used
+       bool available();
+
+private:
+       unsigned short m_clientID;
+};
+
+
+#endif
diff --git a/intern/ghost/intern/GHOST_NDOFManagerCocoa.mm b/intern/ghost/intern/GHOST_NDOFManagerCocoa.mm
new file mode 100644 (file)
index 0000000..1c8730a
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * ***** 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 soon
+       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:
+                                       {
+                                       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
+                       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;
+       }
diff --git a/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp b/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp
new file mode 100644 (file)
index 0000000..d7285d5
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version. 
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s):
+ *   Mike Erwin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#include "GHOST_NDOFManagerWin32.h"
+
+
+GHOST_NDOFManagerWin32::GHOST_NDOFManagerWin32(GHOST_System& sys)
+       : GHOST_NDOFManager(sys)
+       {}
+
+// whether multi-axis functionality is available (via the OS or driver)
+// does not imply that a device is plugged in or being used
+bool GHOST_NDOFManagerWin32::available()
+       {
+       // always available since RawInput is built into Windows
+       return true;
+       }
diff --git a/intern/ghost/intern/GHOST_NDOFManagerWin32.h b/intern/ghost/intern/GHOST_NDOFManagerWin32.h
new file mode 100644 (file)
index 0000000..3802a6d
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version. 
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s):
+ *   Mike Erwin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef _GHOST_NDOFMANAGERWIN32_H_
+#define _GHOST_NDOFMANAGERWIN32_H_
+
+#include "GHOST_NDOFManager.h"
+
+
+class GHOST_NDOFManagerWin32 : public GHOST_NDOFManager
+{
+public:
+       GHOST_NDOFManagerWin32(GHOST_System&);
+       bool available();
+};
+
+
+#endif
diff --git a/intern/ghost/intern/GHOST_NDOFManagerX11.cpp b/intern/ghost/intern/GHOST_NDOFManagerX11.cpp
new file mode 100644 (file)
index 0000000..a823c3c
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * ***** 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)
+       {
+       if (spnav_open() != -1)
+               {
+               m_available = true;
+
+               setDeadZone(0.1f); // how to calibrate on Linux? throw away slight motion!
+
+               // determine exactly which device 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)
+                                       setDevice(vendor_id, product_id);
+                               }
+                       pclose(command_output);
+                       }
+               }
+       else
+               {
+               m_available = false;
+               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::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:
+                               {
+// "natural" device 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};
+
+// blender world coords
+//                             short t[3] = {e.motion.x, e.motion.z, e.motion.y};
+//                             short r[3] = {-e.motion.rx, -e.motion.rz, -e.motion.ry};
+
+// 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..def311d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * ***** 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 m_available;
+       };
+
+#endif
+
index cb3e97fc5749b2f51b7132c94c3599de6cae5eb4..792adba7ce3bc17f0a6efc4775688e58f109e47e 100644 (file)
@@ -194,12 +194,15 @@ bool GHOST_System::getFullScreen(void)
 
 bool GHOST_System::dispatchEvents()
 {
-       bool handled;
-       if (m_eventManager) {
-               handled = m_eventManager->dispatchEvents();
+       bool handled = false;
+
+       // NDOF Motion event is sent only once per dispatch, so do it now:
+       if (m_ndofManager) {
+               handled |= m_ndofManager->sendMotionEvent();
        }
-       else {
-               handled = false;
+
+       if (m_eventManager) {
+               handled |= m_eventManager->dispatchEvents();
        }
 
        m_timerManager->fireTimers(getMilliSeconds());
@@ -243,18 +246,6 @@ GHOST_TSuccess GHOST_System::pushEvent(GHOST_IEvent* event)
        return success;
 }
 
-int GHOST_System::openNDOF(GHOST_IWindow* w,
-               GHOST_NDOFLibraryInit_fp setNdofLibraryInit, 
-               GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
-               GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen)
-{
- return m_ndofManager->deviceOpen(w,
-               setNdofLibraryInit, 
-               setNdofLibraryShutdown,
-               setNdofDeviceOpen);
-}
-
-
 GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKeyMask mask, bool& isDown) const
 {
        GHOST_ModifierKeys keys;
@@ -285,12 +276,6 @@ GHOST_TSuccess GHOST_System::init()
        m_timerManager = new GHOST_TimerManager ();
        m_windowManager = new GHOST_WindowManager ();
        m_eventManager = new GHOST_EventManager ();
-       m_ndofManager = new GHOST_NDOFManager();
-
-#if 0
-       if(m_ndofManager)
-               printf("ndof manager \n");
-#endif
        
 #ifdef GHOST_DEBUG
        if (m_eventManager) {
index b5c64bfceb672a5efd716d4337a9cbac2f22d428..f62c0984c8096ba2815a1f61ddace789762a4d16 100644 (file)
@@ -190,25 +190,6 @@ public:
         */
        virtual GHOST_TSuccess removeEventConsumer(GHOST_IEventConsumer* consumer);
 
-       /***************************************************************************************
-        ** N-degree of freedom devcice management functionality
-        ***************************************************************************************/
-
-       /** Inherited from GHOST_ISystem
-     *  Opens the N-degree of freedom device manager
-        * return 0 if device found, 1 otherwise
-     */
-    virtual int openNDOF(GHOST_IWindow* w,        
-        GHOST_NDOFLibraryInit_fp setNdofLibraryInit, 
-        GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
-        GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen);
-        
-// original patch only        
-//        GHOST_NDOFEventHandler_fp setNdofEventHandler);
-
-
-
-
        /***************************************************************************************
         ** Cursor management functionality
         ***************************************************************************************/
index ce77735838934d088bff949686a3bc3fe8379d6c..d20aed63f42e92d673e2dc51d7a036fd6fc06ed6 100644 (file)
@@ -220,6 +220,11 @@ public:
      */
     GHOST_TSuccess handleApplicationBecomeActiveEvent();
 
+       /**
+        * External objects should call this when they send an event outside processEvents.
+        */
+       void notifyExternalEventProcessed();
+
        /**
         * @see GHOST_ISystem
         */
@@ -267,7 +272,7 @@ protected:
        /** Start time at initialization. */
        GHOST_TUns64 m_start_time;
        
-       /** Event has been processed directly by Cocoa and has sent a ghost event to be dispatched */
+       /** Event has been processed directly by Cocoa (or NDOF manager) and has sent a ghost event to be dispatched */
        bool m_outsideLoopEventProcessed;
        
        /** Raised window is not yet known by the window manager, so delay application become active event handling */
index bb3d6e3aee331257e563f01162a1c68b43348faf..5f27eab9d8c98284bc102330a57ba7128e8a4965 100644 (file)
@@ -52,7 +52,7 @@
 #include "GHOST_TimerTask.h"
 #include "GHOST_WindowManager.h"
 #include "GHOST_WindowCocoa.h"
-#include "GHOST_NDOFManager.h"
+#include "GHOST_NDOFManagerCocoa.h"
 #include "AssertMacros.h"
 
 #pragma mark KeyMap, mouse converters
@@ -596,6 +596,9 @@ GHOST_TSuccess GHOST_SystemCocoa::init()
        
     GHOST_TSuccess success = GHOST_System::init();
     if (success) {
+
+               m_ndofManager = new GHOST_NDOFManagerCocoa(*this);
+
                //ProcessSerialNumber psn;
                
                //Carbon stuff to move window & menu to foreground
@@ -1007,6 +1010,11 @@ GHOST_TSuccess GHOST_SystemCocoa::handleApplicationBecomeActiveEvent()
        return GHOST_kSuccess;
 }
 
+void GHOST_SystemCocoa::notifyExternalEventProcessed()
+{
+       m_outsideLoopEventProcessed = true;
+}
+
 //Note: called from NSWindow delegate
 GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window)
 {
index becccc2c29f0764b380dfa4c3aa790cbe1ceee9f..ae376bc37a5140023ab41a017acfc8b9de8921df 100644 (file)
 
 #include "GHOST_SystemPathsWin32.h"
 
-#define WIN32_LEAN_AND_MEAN
-#ifdef _WIN32_IE
-#undef _WIN32_IE
-#endif
 #define _WIN32_IE 0x0501
-#include <windows.h>
 #include <shlobj.h>
 
 #if defined(__MINGW32__) || defined(__CYGWIN__)
index 67cc2140e0e54301d4b556f688255e4f2f5279a5..3de7bbf934e74d1bc7c5e6bd4ff3b6b28f369366 100644 (file)
@@ -38,6 +38,8 @@
 #error WIN32 only!
 #endif // WIN32
 
+#define _WIN32_WINNT 0x501 // require Windows XP or newer
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
 #include "GHOST_SystemPaths.h"
index ace6cc0e0cff67a9ae27425ecd753d0080e1b865..d5dd77b7c831e75a3836fd860c46cc3af0f65eca 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
-#endif
-#define _WIN32_IE 0x0501
-#include <windows.h>
+#define _WIN32_IE 0x0501 /* shipped before XP, so doesn't impose additional requirements */
 #include <shlobj.h>
 
 // win64 doesn't define GWL_USERDATA
 #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)
@@ -186,6 +157,8 @@ GHOST_SystemWin32::GHOST_SystemWin32()
        this->handleKeyboardChange();
        // Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32.
        OleInitialize(0);
+
+       m_ndofManager = new GHOST_NDOFManagerWin32(*this);
 }
 
 GHOST_SystemWin32::~GHOST_SystemWin32()
@@ -244,6 +217,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 {
@@ -384,22 +358,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;
@@ -440,104 +407,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;
 }
@@ -741,12 +688,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';
@@ -776,7 +723,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, 
@@ -799,9 +754,101 @@ void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO * minmax)
        minmax->ptMinTrackSize.y=240;
 }
 
+bool GHOST_SystemWin32::processNDOF(RAWINPUT const& raw)
+{
+       bool eventSent = false;
+       GHOST_TUns64 now = getMilliSeconds();
+
+       static bool firstEvent = true;
+       if (firstEvent)
+               { // determine exactly which device is plugged in
+               RID_DEVICE_INFO info;
+               unsigned infoSize = sizeof(RID_DEVICE_INFO);
+               info.cbSize = infoSize;
+
+               GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICEINFO, &info, &infoSize);
+               if (info.dwType == RIM_TYPEHID)
+                       m_ndofManager->setDevice(info.hid.dwVendorId, info.hid.dwProductId);
+               else
+            puts("<!> not a HID device... mouse/kb perhaps?");
+
+               firstEvent = false;
+               }
+
+       // The NDOF manager sends button changes immediately, and *pretends* to
+       // send motion. Mark as 'sent' so motion will always get dispatched.
+       eventSent = true;
+
+#ifdef _MSC_VER
+    // using Microsoft compiler & header files
+    // they invented the RawInput API, so this version is (probably) correct
+       BYTE const* data = raw.data.hid.bRawData;
+       // struct RAWHID {
+       // DWORD dwSizeHid;
+       // DWORD dwCount;
+       // BYTE  bRawData[1];
+       // };
+#else
+       // MinGW's definition (below) doesn't agree, so we need a slight
+       // workaround until it's fixed
+       BYTE const* data = &raw.data.hid.bRawData;
+       // struct RAWHID {
+       // DWORD dwSizeHid;
+       // DWORD dwCount;
+       // BYTE bRawData; // <== isn't this s'posed to be a BYTE*?
+       // };
+#endif
+
+       BYTE packetType = data[0];
+       switch (packetType)
+               {
+               case 1: // translation
+                       {
+                       short* axis = (short*)(data + 1);
+                       short t[3] = {axis[0], -axis[2], axis[1]};
+                       m_ndofManager->updateTranslation(t, now);
+
+                       if (raw.data.hid.dwSizeHid == 13)
+                               { // this report also includes rotation
+                               short r[3] = {-axis[3], axis[5], -axis[4]};
+                               m_ndofManager->updateRotation(r, now);
+
+                               // I've never gotten one of these, has anyone else?
+                               puts("ndof: combined T + R");
+                               }
+                       break;
+                       }
+               case 2: // rotation
+                       {
+                       short* axis = (short*)(data + 1);
+                       short r[3] = {-axis[0], axis[2], -axis[1]};
+                       m_ndofManager->updateRotation(r, now);
+                       break;
+                       }
+               case 3: // buttons
+                       {
+                       #if 0
+                       // I'm getting garbage bits -- examine whole report:
+                       printf("ndof: HID report for buttons [");
+                       for (int i = 0; i < raw.data.hid.dwSizeHid; ++i)
+                               printf(" %02X", data[i]);
+                       printf(" ]\n");
+                       #endif
+
+                       int button_bits;
+                       memcpy(&button_bits, data + 1, sizeof(button_bits));
+                       m_ndofManager->updateButtons(button_bits, now);
+                       break;
+                       }
+               }
+       return eventSent;
+}
+
 LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
        GHOST_Event* event = 0;
+       bool eventHandled = false;
+
        LRESULT lResult = 0;
        GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
        GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized")
@@ -818,18 +865,36 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
                                // Keyboard events, processed
                                ////////////////////////////////////////////////////////////////////////
                                case WM_INPUT:
+                               {
                                        // check WM_INPUT from input sink when ghost window is not in the foreground
                                        if (wParam == RIM_INPUTSINK) {
                                                if (GetFocus() != hwnd) // WM_INPUT message not for this window
                                                        return 0;
-                                       } //else wPAram == RIM_INPUT
-                                       event = processKeyEvent(window, wParam, lParam);
-                                       if (!event) {
-                                               GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
-                                               GHOST_PRINT(msg)
-                                               GHOST_PRINT(" key ignored\n")
+                                       } //else wParam == RIM_INPUT
+
+                                       RAWINPUT raw;
+                                       RAWINPUT* raw_ptr = &raw;
+                                       UINT rawSize = sizeof(RAWINPUT);
+
+                                       GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_ptr, &rawSize, sizeof(RAWINPUTHEADER));
+
+                                       switch (raw.header.dwType)
+                                       {
+                                       case RIM_TYPEKEYBOARD:
+                                               event = processKeyEvent(window, raw);
+                                               if (!event) {
+                                                       GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
+                                                       GHOST_PRINT(msg)
+                                                       GHOST_PRINT(" key ignored\n")
+                                               }
+                                               break;
+                                       case RIM_TYPEHID:
+                                               if (system->processNDOF(raw))
+                                                       eventHandled = true;
+                                               break;
                                        }
-                                       break;
+                               break;
+                               }
                                ////////////////////////////////////////////////////////////////////////
                                // Keyboard events, ignored
                                ////////////////////////////////////////////////////////////////////////
@@ -839,9 +904,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
@@ -1122,28 +1187,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 {
@@ -1165,10 +1208,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
 
        if (event) {
                system->pushEvent(event);
+               eventHandled = true;
        }
-       else {
+
+       if (!eventHandled)
                lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
-       }
+
        return lResult;
 }
 
index 729ad56d875a1db27515d88168e82a9ab2072036..c5dff27dace1b98206fb0e28c0d303ed09dd621e 100644 (file)
 #error WIN32 only!
 #endif // WIN32
 
+#define _WIN32_WINNT 0x501 // require Windows XP or newer
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
+#include <ole2.h> // for drag-n-drop
 
 #include "GHOST_System.h"
 
 #      define __int64 long long
 #endif
 
-#ifndef WM_INPUT
-#define WM_INPUT 0x00FF
-#endif 
-#ifndef RID_INPUT
-#define RID_INPUT 0x10000003
-#endif
-#ifndef RIM_INPUTSINK
-#define RIM_INPUTSINK 0x1
-#endif
-#ifndef RI_KEY_BREAK
-#define RI_KEY_BREAK 0x1
-#endif
-#ifndef RI_KEY_E0
-#define RI_KEY_E0 0x2
-#endif
-#ifndef RI_KEY_E1
-#define RI_KEY_E1 0x4
-#endif
-#ifndef RIM_TYPEMOUSE
-#define RIM_TYPEMOUSE          0x0
-#define RIM_TYPEKEYBOARD       0x1
-#define RIM_TYPEHID                    0x2
-
-typedef struct tagRAWINPUTDEVICE {
-       USHORT usUsagePage;
-       USHORT usUsage;
-       DWORD dwFlags;
-       HWND hwndTarget;
-} RAWINPUTDEVICE;
-
-
-
-typedef struct tagRAWINPUTHEADER {
-       DWORD dwType;
-       DWORD dwSize;
-       HANDLE hDevice;
-       WPARAM wParam;
-} RAWINPUTHEADER;
-
-typedef struct tagRAWMOUSE {
-       USHORT usFlags;
-       union {
-               ULONG ulButtons;
-               struct  {
-                       USHORT  usButtonFlags;
-                       USHORT  usButtonData;
-               };
-       };
-       ULONG   ulRawButtons;
-       LONG    lLastX;
-       LONG    lLastY;
-       ULONG   ulExtraInformation;
-} RAWMOUSE;
-
-typedef struct tagRAWKEYBOARD {
-       USHORT  MakeCode;
-       USHORT  Flags;
-       USHORT  Reserved;
-       USHORT  VKey;
-       UINT    Message;
-       ULONG   ExtraInformation;
-} RAWKEYBOARD;
-
-typedef struct tagRAWHID {
-       DWORD   dwSizeHid;
-       DWORD   dwCount;
-       BYTE    bRawData[1];
-} RAWHID;
-
-typedef struct tagRAWINPUT {
-       RAWINPUTHEADER header;
-       union {
-               RAWMOUSE        mouse;
-               RAWKEYBOARD keyboard;
-               RAWHID      hid;
-       } data;
-} RAWINPUT;
-
-DECLARE_HANDLE(HRAWINPUT);
-#endif
-
-#ifdef FREE_WINDOWS
-#define NEED_RAW_PROC
-typedef BOOL (WINAPI * LPFNDLLRRID)(RAWINPUTDEVICE*,UINT, UINT);
-
-typedef UINT (WINAPI * LPFNDLLGRID)(HRAWINPUT, UINT, LPVOID, PUINT, UINT);
-#define GetRawInputData(hRawInput, uiCommand, pData, pcbSize, cbSizeHeader) ((pGetRawInputData)?pGetRawInputData(hRawInput, uiCommand, pData, pcbSize, cbSizeHeader):(UINT)-1)
-#endif
-
 class GHOST_EventButton;
 class GHOST_EventCursor;
 class GHOST_EventKey;
@@ -314,14 +228,13 @@ protected:
 
        /**
         * Catches raw WIN32 key codes from WM_INPUT in the wndproc.
-        * @param window->      The window for this handling
-        * @param wParam        The wParam from the wndproc
-        * @param lParam        The lParam from the wndproc
+        * @param window        The window for this handling
+        * @param raw           RawInput structure with detailed info about the key event
         * @param keyDown       Pointer flag that specify if a key is down
         * @param vk            Pointer to virtual key
         * @return The GHOST key (GHOST_kKeyUnknown if no match).
         */
-       virtual GHOST_TKey hardKey(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam, int * keyDown, char * vk);
+       virtual GHOST_TKey hardKey(GHOST_IWindow *window, RAWINPUT const& raw, int * keyDown, char * vk);
 
        /**
         * Creates modifier key event(s) and updates the key data stored locally (m_modifierKeys).
@@ -362,10 +275,9 @@ protected:
         * In most cases this is a straightforward conversion of key codes.
         * For the modifier keys however, we want to distinguish left and right keys.
         * @param window        The window receiving the event (the active window).
-        * @param wParam        The wParam from the wndproc
-        * @param lParam        The lParam from the wndproc
+        * @param raw           RawInput structure with detailed info about the key event
         */
-       static GHOST_EventKey* processKeyEvent(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam);
+       static GHOST_EventKey* processKeyEvent(GHOST_IWindow *window, RAWINPUT const& raw);
 
        /**
         * Process special keys (VK_OEM_*), to see if current key layout
@@ -383,12 +295,22 @@ protected:
         * @return The event created.
         */
        static GHOST_Event* processWindowEvent(GHOST_TEventType type, GHOST_IWindow* window);
-       /** 
+
+       /**
         * Handles minimum window size.
         * @param minmax        The MINMAXINFO structure.
         */
        static void processMinMaxInfo(MINMAXINFO * minmax);
-       
+
+       /**
+        * Handles Motion and Button events from a SpaceNavigator or related device.
+        * Instead of returning an event object, this function communicates directly
+        * with the GHOST_NDOFManager.
+        * @param raw           RawInput structure with detailed info about the NDOF event
+        * @return Whether an event was generated and sent.
+        */
+       bool processNDOF(RAWINPUT const& raw);
+
        /**
         * Returns the local state of the modifier keys (from the message queue).
         * @param keys The state of the keys.
@@ -412,11 +334,6 @@ protected:
         */
        static LRESULT WINAPI s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
 
-       /**
-        * Initiates WM_INPUT messages from keyboard 
-        */
-       GHOST_TInt32 initKeyboardRawInput(void);
-
        /**
  * Toggles console
  * @action     0 - Hides
@@ -445,15 +362,6 @@ protected:
 
        /** Console status */
        int m_consoleStatus;
-
-       /** handle for user32.dll*/
-       HMODULE user32;
-       #ifdef NEED_RAW_PROC
-       /* pointer to RegisterRawInputDevices function */
-       LPFNDLLRRID pRegisterRawInputDevices;
-       /* pointer to GetRawInputData function */
-       LPFNDLLGRID pGetRawInputData;
-       #endif
 };
 
 inline void GHOST_SystemWin32::retrieveModifierKeys(GHOST_ModifierKeys& keys) const
@@ -487,4 +395,3 @@ inline void GHOST_SystemWin32::handleKeyboardChange(void)
        }
 }
 #endif // _GHOST_SYSTEM_WIN32_H_
-
index dd296fa979c5e3a78a00f80d48fffd026ca532cf..ef182a36539cb43422515ebd82ec8fbbd10ec915 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"
 #include <stdio.h> // for fprintf only
 #include <cstdlib> // for exit
 
-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 +164,7 @@ init(
        GHOST_TSuccess success = GHOST_System::init();
 
        if (success) {
+               m_ndofManager = new GHOST_NDOFManagerX11(*this);
                m_displayManager = new GHOST_DisplayManagerX11(this);
 
                if (m_displayManager) {
@@ -275,7 +259,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 +370,6 @@ lastEventTime(Time default_time) {
     return data.timestamp;
 }
 
-
-
        bool 
 GHOST_SystemX11::
 processEvents(
@@ -428,6 +410,11 @@ processEvents(
                if (generateWindowExposeEvents()) {
                        anyProcessed = true;
                }
+
+               if (dynamic_cast<GHOST_NDOFManagerX11*>(m_ndofManager)->processEvents()) {
+                       anyProcessed = true;
+               }
+               
        } while (waitForEvent && !anyProcessed);
        
        return anyProcessed;
@@ -611,6 +598,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 +631,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 +689,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 +808,8 @@ GHOST_SystemX11::processEvent(XEvent *xe)
        }
 }
 
+#if 0 // obsolete SpaceNav code
+
        void *
 GHOST_SystemX11::
 prepareNdofInfo(volatile GHOST_TEventNDOFData *currentNdofValues)
@@ -846,6 +822,8 @@ prepareNdofInfo(volatile GHOST_TEventNDOFData *currentNdofValues)
        return (void*)&sNdofInfo;
 }
 
+#endif
+
        GHOST_TSuccess 
 GHOST_SystemX11::
 getModifierKeys(
index 746cd4ebdf4d749c6143e19d97d3d86196a68d90..845243f92e523a72bf575d49c026b666a14615ae 100644 (file)
@@ -203,11 +203,6 @@ public:
                return m_display;
        }       
 
-               void *
-       prepareNdofInfo(
-               volatile GHOST_TEventNDOFData *current_values
-       );
-
        /* Helped function for get data from the clipboard. */
        void getClipboard_xcout(XEvent evt, Atom sel, Atom target,
                         unsigned char **txt, unsigned long *len,
index ef9ebdf5860dad04869180e9ab1b2f67d2d1981b..eddff8bb91b2359115365f47eb65d3acafa363b8 100644 (file)
@@ -3,20 +3,16 @@
  */
 #ifndef GHOST_TASKBARWIN32_H_
 #define GHOST_TASKBARWIN32_H_
+
 #ifndef WIN32
 #error WIN32 only!
 #endif // WIN32
 
+#define _WIN32_WINNT 0x501 // require Windows XP or newer
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <shlobj.h>
 
-/* MinGW needs it */
-#ifdef FREE_WINDOWS
-#ifdef WINVER
-#undef WINVER
-#endif
-#define WINVER 0x0501
-#endif /* FREE_WINDOWS */
 
 // ITaskbarList, ITaskbarList2 and ITaskbarList3 might be missing, present here in that case.
 // Note, ITaskbarList3 is supported only since Windows 7, though. Check for that is done in
index 4055c3acf56ed1e4e99a38ac836bfec982e12d56..70914d9d2efd4072f22558c086aa7ee108299993 100644 (file)
 #endif // WIN32
 
 #include "GHOST_Window.h"
+#include "GHOST_TaskbarWin32.h"
 
-/* MinGW needs it */
-#ifdef FREE_WINDOWS
-#ifdef WINVER
-#undef WINVER
-#endif
-#define WINVER 0x0501
-#endif
-
-
-
+#define _WIN32_WINNT 0x501 // require Windows XP or newer
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-#include "GHOST_TaskbarWin32.h"
 
 
 #include <wintab.h>
index aef6accb4e1bff3011260bf21313f4ce3ebe0786..e1d31b533591a2411ec6300de6a4a6d971ae0948 100644 (file)
@@ -354,6 +354,25 @@ 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.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 bdd911d68ee2ae8f473fefef62793e4a1effb9e6..679e4e58017f6c03a0d9b78456242f7521ab5d4e 100644 (file)
@@ -860,6 +860,10 @@ static void finish_images(MultiresBakeRender *bkr)
                RE_bake_ibuf_filter(ibuf, (char *)ibuf->userdata, bkr->bake_filter);
 
                ibuf->userflags|= IB_BITMAPDIRTY;
+
+               if(ibuf->rect_float)
+                       ibuf->userflags|= IB_RECT_INVALID;
+
                if(ibuf->mipmap[0]) {
                        ibuf->userflags|= IB_MIPMAP_INVALID;
                        imb_freemipmapImBuf(ibuf);
index e9e77ddf430d77f3defae1b0370439f57422f572..399157da85cf921d700c7fc9aca7699994389301 100644 (file)
@@ -73,6 +73,7 @@ void IMAGE_OT_view_zoom(struct wmOperatorType *ot);
 void IMAGE_OT_view_zoom_in(struct wmOperatorType *ot);
 void IMAGE_OT_view_zoom_out(struct wmOperatorType *ot);
 void IMAGE_OT_view_zoom_ratio(struct wmOperatorType *ot);
+void IMAGE_OT_view_ndof(struct wmOperatorType *ot);
 
 void IMAGE_OT_new(struct wmOperatorType *ot);
 void IMAGE_OT_open(struct wmOperatorType *ot);
index 10b8cb238aa2217a22445c2c49545c42f6f4ef51..77fb129e2781a8fa7ee273dd76476fe3848b6a77 100644 (file)
@@ -437,6 +437,60 @@ void IMAGE_OT_view_zoom(wmOperatorType *ot)
                "Factor", "Zoom factor, values higher than 1.0 zoom in, lower values zoom out.", -FLT_MAX, FLT_MAX);
 }
 
+/********************** NDOF operator *********************/
+
+/* Combined pan/zoom from a 3D mouse device.
+ * Z zooms, XY pans
+ * "view" (not "paper") control -- user moves the viewpoint, not the image being viewed
+ * that explains the negative signs in the code below
+ */
+
+static int view_ndof_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       SpaceImage *sima= CTX_wm_space_image(C);
+       ARegion *ar= CTX_wm_region(C);
+
+       wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
+
+       float dt = ndof->dt > 0.25f ? 0.0125f : ndof->dt;
+       /* this is probably the first event for this motion, so set dt to something reasonable
+        * TODO: replace such guesswork with a flag or field from the NDOF manager
+        */
+
+       /* tune these until it feels right */
+       const float zoom_sensitivity = 0.5f;
+       const float pan_sensitivity = 300.f;
+
+       float pan_x = pan_sensitivity * dt * ndof->tx / sima->zoom;
+       float pan_y = pan_sensitivity * dt * ndof->ty / sima->zoom;
+
+       /* "mouse zoom" factor = 1 + (dx + dy) / 300
+        * what about "ndof zoom" factor? should behave like this:
+        * at rest -> factor = 1
+        * move forward -> factor > 1
+        * move backward -> factor < 1
+        */
+       float zoom_factor = 1.f + zoom_sensitivity * dt * -ndof->tz;
+
+       sima_zoom_set_factor(sima, ar, zoom_factor);
+       sima->xof += pan_x;
+       sima->yof += pan_y;
+
+       ED_region_tag_redraw(ar);       
+
+       return OPERATOR_FINISHED;
+}
+
+void IMAGE_OT_view_ndof(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "NDOF Pan/Zoom";
+       ot->idname= "IMAGE_OT_view_ndof";
+       
+       /* api callbacks */
+       ot->invoke= view_ndof_invoke;
+}
+
 /********************** view all operator *********************/
 
 /* Updates the fields of the View2D member of the SpaceImage struct.
index 2e9544f5d20f2b7c212d5ca8e93532122a69d0d0..afab4ede229b54e3935573069ce432815f577fc5 100644 (file)
@@ -469,6 +469,7 @@ static void image_operatortypes(void)
        WM_operatortype_append(IMAGE_OT_view_zoom_in);
        WM_operatortype_append(IMAGE_OT_view_zoom_out);
        WM_operatortype_append(IMAGE_OT_view_zoom_ratio);
+       WM_operatortype_append(IMAGE_OT_view_ndof);
 
        WM_operatortype_append(IMAGE_OT_new);
        WM_operatortype_append(IMAGE_OT_open);
@@ -518,6 +519,9 @@ static void image_keymap(struct wmKeyConfig *keyconf)
        WM_keymap_add_item(keymap, "IMAGE_OT_view_pan", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0);
        WM_keymap_add_item(keymap, "IMAGE_OT_view_pan", MOUSEPAN, 0, 0, 0);
 
+       WM_keymap_add_item(keymap, "IMAGE_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); // or view selected?
+       WM_keymap_add_item(keymap, "IMAGE_OT_view_ndof", NDOF_MOTION, 0, 0, 0);
+
        WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_in", WHEELINMOUSE, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_out", WHEELOUTMOUSE, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
index 9ff73767b4c4753cd6d5aa89f7661ab69d43dcbd..0c07df9fd013f6fff49a6928add95fe6af5050ee 100644 (file)
@@ -929,6 +929,163 @@ void VIEW3D_OT_rotate(wmOperatorType *ot)
        ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
 }
 
+// NDOF utility functions
+// (should these functions live in this file?)
+float ndof_to_angle_axis(struct wmNDOFMotionData* ndof, float axis[3])
+       {
+       const float x = ndof->rx;
+       const float y = ndof->ry;
+       const float z = ndof->rz;
+
+       float angular_velocity = sqrtf(x*x + y*y + z*z);
+       float angle = ndof->dt * angular_velocity;
+
+       float scale = 1.f / angular_velocity;
+
+       // normalize 
+       axis[0] = scale * x;
+       axis[1] = scale * y;
+       axis[2] = scale * z;
+
+       return angle;
+       }
+
+void ndof_to_quat(struct wmNDOFMotionData* ndof, float q[4])
+       {
+       const float x = ndof->rx;
+       const float y = ndof->ry;
+       const float z = ndof->rz;
+
+       float angular_velocity = sqrtf(x*x + y*y + z*z);
+       float angle = ndof->dt * angular_velocity;
+
+       // combined scaling factor -- normalize axis while converting to quaternion
+       float scale = sin(0.5f * angle) / angular_velocity;
+
+       // convert axis-angle to quaternion
+       q[0] = cos(0.5f * angle);
+       q[1] = scale * x;
+       q[2] = scale * y;
+       q[3] = scale * z;
+       }
+
+static int ndof_orbit_invoke(bContext *C, wmOperator *op, wmEvent *event)
+// -- "orbit" navigation (trackball/turntable)
+// -- zooming
+// -- panning in rotationally-locked views
+{
+       RegionView3D* rv3d = CTX_wm_region_view3d(C);
+       wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
+
+       const float dt = ndof->dt;
+
+       // tune these until everything feels right
+       const float rot_sensitivity = 1.f;
+       const float zoom_sensitivity = 1.f;
+       const float pan_sensitivity = 1.f;
+
+       // rather have bool, but...
+       int has_rotation = rv3d->viewlock != RV3D_LOCKED && (ndof->rx || ndof->ry || ndof->rz);
+
+       //#define DEBUG_NDOF_MOTION
+       #ifdef DEBUG_NDOF_MOTION
+       printf("ndof: T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f) dt=%.3f delivered to 3D view\n",
+               ndof->tx, ndof->ty, ndof->tz, ndof->rx, ndof->ry, ndof->rz, ndof->dt);
+       #endif
+
+       if (ndof->tz) {
+               // Zoom!
+               // velocity should be proportional to the linear velocity attained by rotational motion of same strength
+               // [got that?]
+               // proportional to arclength = radius * angle
+
+               float zoom_distance = zoom_sensitivity * rv3d->dist * dt * ndof->tz;
+               rv3d->dist += zoom_distance;
+       }
+
+       if (rv3d->viewlock == RV3D_LOCKED) {
+               /* rotation not allowed -- explore panning options instead */
+               float view_inv[4];
+               float pan_vec[3] = {ndof->tx, ndof->ty, 0};
+               mul_v3_fl(pan_vec, pan_sensitivity * rv3d->dist * dt);
+
+               /* transform motion from view to world coordinates */
+               invert_qt_qt(view_inv, rv3d->viewquat);
+               mul_qt_v3(view_inv, pan_vec);
+
+               /* move center of view opposite of hand motion (this is camera mode, not object mode) */
+               sub_v3_v3(rv3d->ofs, pan_vec);
+       }
+
+       if (has_rotation) {
+
+               rv3d->view = RV3D_VIEW_USER;
+
+               if (U.flag & USER_TRACKBALL) {
+
+                       float rot[4];
+                       float view_inv[4], view_inv_conj[4];
+
+                       ndof_to_quat(ndof, rot);
+                       // scale by rot_sensitivity?
+                       // mul_qt_fl(rot, rot_sensitivity);
+
+                       invert_qt_qt(view_inv, rv3d->viewquat);
+                       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);
+
+                       // 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) */
+                       float view_inv[4];
+                       invert_qt_qt(view_inv, rv3d->viewquat);
+                       mul_qt_v3(view_inv, xvec);
+
+                       /* Perform the up/down rotation */
+                       angle = rot_sensitivity * dt * ndof->rx;
+                       rot[0] = cos(angle);
+                       mul_v3_v3fl(rot+1, xvec, sin(angle));
+                       mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
+
+                       /* Perform the orbital rotation */
+                       angle = rot_sensitivity * dt * ndof->ry;
+                       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(struct wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Navigate view";
+       ot->description = "Navigate the view using a 3D mouse.";
+       ot->idname = "VIEW3D_OT_ndof";
+
+       /* api callbacks */
+       ot->invoke = ndof_orbit_invoke;
+       ot->poll = ED_operator_view3d_active;
+
+       /* flags */
+       ot->flag = 0;
+}
+
 /* ************************ viewmove ******************************** */
 
 
@@ -3196,398 +3353,6 @@ int ED_view3d_autodist_depth_seg(struct ARegion *ar, const int mval_sta[2], cons
        return (*depth==FLT_MAX) ? 0:1;
 }
 
-/* ********************* NDOF ************************ */
-/* note: this code is confusing and unclear... (ton) */
-/* **************************************************** */
-
-// ndof scaling will be moved to user setting.
-// In the mean time this is just a place holder.
-
-// Note: scaling in the plugin and ghostwinlay.c
-// should be removed. With driver default setting,
-// each axis returns approx. +-200 max deflection.
-
-// The values I selected are based on the older
-// polling i/f. With event i/f, the sensistivity
-// can be increased for improved response from
-// small deflections of the device input.
-
-
-// lukep notes : i disagree on the range.
-// the normal 3Dconnection driver give +/-400
-// on defaut range in other applications
-// and up to +/- 1000 if set to maximum
-// because i remove the scaling by delta,
-// which was a bad idea as it depend of the system
-// speed and os, i changed the scaling values, but
-// those are still not ok
-
-#if 0
-static float ndof_axis_scale[6] = {
-       +0.01,  // Tx
-       +0.01,  // Tz
-       +0.01,  // Ty
-       +0.0015,        // Rx
-       +0.0015,        // Rz
-       +0.0015 // Ry
-};
-
-static void filterNDOFvalues(float *sbval)
-{
-       int i=0;
-       float max  = 0.0;
-
-       for (i =0; i<6;i++)
-               if (fabs(sbval[i]) > max)
-                       max = fabs(sbval[i]);
-       for (i =0; i<6;i++)
-               if (fabs(sbval[i]) != max )
-                       sbval[i]=0.0;
-}
-
-// statics for controlling rv3d->dist corrections.
-// viewmoveNDOF zeros and adjusts rv3d->ofs.
-// viewmove restores based on dz_flag state.
-
-int dz_flag = 0;
-float m_dist;
-
-void viewmoveNDOFfly(ARegion *ar, View3D *v3d, int UNUSED(mode))
-{
-       RegionView3D *rv3d= ar->regiondata;
-       int i;
-       float phi;
-       float dval[7];
-       // static fval[6] for low pass filter; device input vector is dval[6]
-       static float fval[6];
-       float tvec[3],rvec[3];
-       float q1[4];
-       float mat[3][3];
-       float upvec[3];
-
-
-       /*----------------------------------------------------
-        * sometimes this routine is called from headerbuttons
-        * viewmove needs to refresh the screen
-        */
-// XXX areawinset(ar->win);
-
-
-       // fetch the current state of the ndof device
-// XXX getndof(dval);
-
-       if (v3d->ndoffilter)
-               filterNDOFvalues(fval);
-
-       // Scale input values
-
-//     if(dval[6] == 0) return; // guard against divide by zero
-
-       for(i=0;i<6;i++) {
-
-               // user scaling
-               dval[i] = dval[i] * ndof_axis_scale[i];
-       }
-
-
-       // low pass filter with zero crossing reset
-
-       for(i=0;i<6;i++) {
-               if((dval[i] * fval[i]) >= 0)
-                       dval[i] = (fval[i] * 15 + dval[i]) / 16;
-               else
-                       fval[i] = 0;
-       }
-
-
-       // force perspective mode. This is a hack and is
-       // incomplete. It doesn't actually effect the view
-       // until the first draw and doesn't update the menu
-       // to reflect persp mode.
-
-       rv3d->persp = RV3D_PERSP;
-
-
-       // Correct the distance jump if rv3d->dist != 0
-
-       // This is due to a side effect of the original
-       // mouse view rotation code. The rotation point is
-       // set a distance in front of the viewport to
-       // make rotating with the mouse look better.
-       // The distance effect is written at a low level
-       // in the view management instead of the mouse
-       // view function. This means that all other view
-       // movement devices must subtract this from their
-       // view transformations.
-
-       if(rv3d->dist != 0.0) {
-               dz_flag = 1;
-               m_dist = rv3d->dist;
-               upvec[0] = upvec[1] = 0;
-               upvec[2] = rv3d->dist;
-               copy_m3_m4(mat, rv3d->viewinv);
-               mul_m3_v3(mat, upvec);
-               sub_v3_v3(rv3d->ofs, upvec);
-               rv3d->dist = 0.0;
-       }
-
-
-       // Apply rotation
-       // Rotations feel relatively faster than translations only in fly mode, so
-       // we have no choice but to fix that here (not in the plugins)
-       rvec[0] = -0.5 * dval[3];
-       rvec[1] = -0.5 * dval[4];
-       rvec[2] = -0.5 * dval[5];
-
-       // rotate device x and y by view z
-
-       copy_m3_m4(mat, rv3d->viewinv);
-       mat[2][2] = 0.0f;
-       mul_m3_v3(mat, rvec);
-
-       // rotate the view
-
-       phi = normalize_v3(rvec);
-       if(phi != 0) {
-               axis_angle_to_quat(q1,rvec,phi);
-               mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
-       }
-
-
-       // Apply translation
-
-       tvec[0] = dval[0];
-       tvec[1] = dval[1];
-       tvec[2] = -dval[2];
-
-       // the next three lines rotate the x and y translation coordinates
-       // by the current z axis angle
-
-       copy_m3_m4(mat, rv3d->viewinv);
-       mat[2][2] = 0.0f;
-       mul_m3_v3(mat, tvec);
-
-       // translate the view
-
-       sub_v3_v3(rv3d->ofs, tvec);
-
-
-       /*----------------------------------------------------
-        * refresh the screen XXX
-         */
-
-       // update render preview window
-
-// XXX BIF_view3d_previewrender_signal(ar, PR_DBASE|PR_DISPRECT);
-}
-
-void viewmoveNDOF(Scene *scene, ARegion *ar, View3D *v3d, int UNUSED(mode))
-{
-       RegionView3D *rv3d= ar->regiondata;
-       float fval[7];
-       float dvec[3];
-       float sbadjust = 1.0f;
-       float len;
-       short use_sel = 0;
-       Object *ob = OBACT;
-       float m[3][3];
-       float m_inv[3][3];
-       float xvec[3] = {1,0,0};
-       float yvec[3] = {0,-1,0};
-       float zvec[3] = {0,0,1};
-       float phi;
-       float q1[4];
-       float obofs[3];
-       float reverse;
-       //float diff[4];
-       float d, curareaX, curareaY;
-       float mat[3][3];
-       float upvec[3];
-
-       /* Sensitivity will control how fast the view rotates.  The value was
-        * obtained experimentally by tweaking until the author didn't get dizzy watching.
-        * Perhaps this should be a configurable user parameter.
-        */
-       float psens = 0.005f * (float) U.ndof_pan;   /* pan sensitivity */
-       float rsens = 0.005f * (float) U.ndof_rotate;  /* rotate sensitivity */
-       float zsens = 0.3f;   /* zoom sensitivity */
-
-       const float minZoom = -30.0f;
-       const float maxZoom = 300.0f;
-
-       //reset view type
-       rv3d->view = 0;
-//printf("passing here \n");
-//
-       if (scene->obedit==NULL && ob && !(ob->mode & OB_MODE_POSE)) {
-               use_sel = 1;
-       }
-
-       if((dz_flag)||rv3d->dist==0) {
-               dz_flag = 0;
-               rv3d->dist = m_dist;
-               upvec[0] = upvec[1] = 0;
-               upvec[2] = rv3d->dist;
-               copy_m3_m4(mat, rv3d->viewinv);
-               mul_m3_v3(mat, upvec);
-               add_v3_v3(rv3d->ofs, upvec);
-       }
-
-       /*----------------------------------------------------
-        * sometimes this routine is called from headerbuttons
-        * viewmove needs to refresh the screen
-        */
-// XXX areawinset(curarea->win);
-
-       /*----------------------------------------------------
-        * record how much time has passed. clamp at 10 Hz
-        * pretend the previous frame occurred at the clamped time
-        */
-//    now = PIL_check_seconds_timer();
- //   frametime = (now - prevTime);
- //   if (frametime > 0.1f){        /* if more than 1/10s */
- //       frametime = 1.0f/60.0;      /* clamp at 1/60s so no jumps when starting to move */
-//    }
-//    prevTime = now;
- //   sbadjust *= 60 * frametime;             /* normalize ndof device adjustments to 100Hz for framerate independence */
-
-       /* fetch the current state of the ndof device & enforce dominant mode if selected */
-// XXX    getndof(fval);
-       if (v3d->ndoffilter)
-               filterNDOFvalues(fval);
-
-
-       // put scaling back here, was previously in ghostwinlay
-       fval[0] = fval[0] * (1.0f/600.0f);
-       fval[1] = fval[1] * (1.0f/600.0f);
-       fval[2] = fval[2] * (1.0f/1100.0f);
-       fval[3] = fval[3] * 0.00005f;
-       fval[4] =-fval[4] * 0.00005f;
-       fval[5] = fval[5] * 0.00005f;
-       fval[6] = fval[6] / 1000000.0f;
-
-       // scale more if not in perspective mode
-       if (rv3d->persp == RV3D_ORTHO) {
-               fval[0] = fval[0] * 0.05f;
-               fval[1] = fval[1] * 0.05f;
-               fval[2] = fval[2] * 0.05f;
-               fval[3] = fval[3] * 0.9f;
-               fval[4] = fval[4] * 0.9f;
-               fval[5] = fval[5] * 0.9f;
-               zsens *= 8;
-       }
-
-       /* set object offset */
-       if (ob) {
-               obofs[0] = -ob->obmat[3][0];
-               obofs[1] = -ob->obmat[3][1];
-               obofs[2] = -ob->obmat[3][2];
-       }
-       else {
-               copy_v3_v3(obofs, rv3d->ofs);
-       }
-
-       /* calc an adjustment based on distance from camera
-          disabled per patch 14402 */
-        d = 1.0f;
-
-/*    if (ob) {
-               sub_v3_v3v3(diff, obofs, rv3d->ofs);
-               d = len_v3(diff);
-       }
-*/
-
-       reverse = (rv3d->persmat[2][1] < 0.0f) ? -1.0f : 1.0f;
-
-       /*----------------------------------------------------
-        * ndof device pan
-        */
-       psens *= 1.0f + d;
-       curareaX = sbadjust * psens * fval[0];
-       curareaY = sbadjust * psens * fval[1];
-       dvec[0] = curareaX * rv3d->persinv[0][0] + curareaY * rv3d->persinv[1][0];
-       dvec[1] = curareaX * rv3d->persinv[0][1] + curareaY * rv3d->persinv[1][1];
-       dvec[2] = curareaX * rv3d->persinv[0][2] + curareaY * rv3d->persinv[1][2];
-       add_v3_v3(rv3d->ofs, dvec);
-
-       /*----------------------------------------------------
-        * ndof device dolly
-        */
-       len = zsens * sbadjust * fval[2];
-
-       if (rv3d->persp==RV3D_CAMOB) {
-               if(rv3d->persp==RV3D_CAMOB) { /* This is stupid, please fix - TODO */
-                       rv3d->camzoom+= 10.0f * -len;
-               }
-               if (rv3d->camzoom < minZoom) rv3d->camzoom = minZoom;
-               else if (rv3d->camzoom > maxZoom) rv3d->camzoom = maxZoom;
-       }
-       else if ((rv3d->dist> 0.001*v3d->grid) && (rv3d->dist<10.0*v3d->far)) {
-               rv3d->dist*=(1.0 + len);
-       }
-
-
-       /*----------------------------------------------------
-        * ndof device turntable
-        * derived from the turntable code in viewmove
-        */
-
-       /* Get the 3x3 matrix and its inverse from the quaternion */
-       quat_to_mat3( m,rv3d->viewquat);
-       invert_m3_m3(m_inv,m);
-
-       /* Determine the direction of the x vector (for rotating up and down) */
-       /* This can likely be compuated directly from the quaternion. */
-       mul_m3_v3(m_inv,xvec);
-       mul_m3_v3(m_inv,yvec);
-       mul_m3_v3(m_inv,zvec);
-
-       /* Perform the up/down rotation */
-       phi = sbadjust * rsens * /*0.5f * */ fval[3]; /* spin vertically half as fast as horizontally */
-       q1[0] = cos(phi);
-       mul_v3_v3fl(q1+1, xvec, sin(phi));
-       mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
-
-       if (use_sel) {
-               conjugate_qt(q1); /* conj == inv for unit quat */
-               sub_v3_v3(rv3d->ofs, obofs);
-               mul_qt_v3(q1, rv3d->ofs);
-               add_v3_v3(rv3d->ofs, obofs);
-       }
-
-       /* Perform the orbital rotation */
-       /* Perform the orbital rotation
-          If the seen Up axis is parallel to the zoom axis, rotation should be
-          achieved with a pure Roll motion (no Spin) on the device. When you start
-          to tilt, moving from Top to Side view, Spinning will increasingly become
-          more relevant while the Roll component will decrease. When a full
-          Side view is reached, rotations around the world's Up axis are achieved
-          with a pure Spin-only motion.  In other words the control of the spinning
-          around the world's Up axis should move from the device's Spin axis to the
-          device's Roll axis depending on the orientation of the world's Up axis
-          relative to the screen. */
-       //phi = sbadjust * rsens * reverse * fval[4];  /* spin the knob, y axis */
-       phi = sbadjust * rsens * (yvec[2] * fval[4] + zvec[2] * fval[5]);
-       q1[0] = cos(phi);
-       q1[1] = q1[2] = 0.0;
-       q1[3] = sin(phi);
-       mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
-
-       if (use_sel) {
-               conjugate_qt(q1);
-               sub_v3_v3(rv3d->ofs, obofs);
-               mul_qt_v3(q1, rv3d->ofs);
-               add_v3_v3(rv3d->ofs, obofs);
-       }
-
-       /*----------------------------------------------------
-        * refresh the screen
-        */
-// XXX    scrarea_do_windraw(curarea);
-}
-#endif // if 0, unused NDof code
-
-
 /* Gets the view trasnformation from a camera
 * currently dosnt take camzoom into account
 *
index ed1ed5b388182fad48e37c29b2f712eab52dee44..6955aefcb9e81b656cf4972067079b934b2cc762 100644 (file)
@@ -29,6 +29,8 @@
 
 /* defines VIEW3D_OT_fly modal operator */
 
+// #define NDOF_FLY_DEBUG
+
 #include "DNA_anim_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_object_types.h"
@@ -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,6 +261,10 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
        fly->ar = CTX_wm_region(C);
        fly->scene= CTX_data_scene(C);
 
+       #ifdef NDOF_FLY_DEBUG
+       puts("\n-- fly begin --");
+       #endif
+
        if(fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->id.lib) {
                BKE_report(op->reports, RPT_ERROR, "Cannot fly a camera from an external library");
                return FALSE;
@@ -282,12 +290,14 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
        fly->zlock_momentum=0.0f;
        fly->grid= 1.0f;
        fly->use_precision= 0;
+       fly->redraw= 1;
 
        fly->dvec_prev[0]= fly->dvec_prev[1]= fly->dvec_prev[2]= 0.0f;
 
        fly->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
 
        VECCOPY2D(fly->mval, event->mval)
+       fly->ndof = NULL;
 
        fly->time_lastdraw= fly->time_lastwheel= PIL_check_seconds_timer();
 
@@ -329,8 +339,17 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
                /* perspective or ortho */
                if (fly->rv3d->persp==RV3D_ORTHO)
                        fly->rv3d->persp= RV3D_PERSP; /*if ortho projection, make perspective */
+
                copy_qt_qt(fly->rot_backup, fly->rv3d->viewquat);
                copy_v3_v3(fly->ofs_backup, fly->rv3d->ofs);
+
+               /* the dist defines a vector that is infront of the offset
+               to rotate the view about.
+               this is no good for fly mode because we
+               want to rotate about the viewers center.
+               but to correct the dist removal we must
+               alter offset so the view doesn't jump. */
+
                fly->rv3d->dist= 0.0f;
 
                upvec[2]= fly->dist_backup; /*x and y are 0*/
@@ -338,7 +357,6 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
                sub_v3_v3(fly->rv3d->ofs, upvec);
                /*Done with correcting for the dist*/
        }
-
        
        /* center the mouse, probably the UI mafia are against this but without its quite annoying */
        WM_cursor_warp(CTX_wm_window(C), fly->ar->winrct.xmin + fly->ar->winx/2, fly->ar->winrct.ymin + fly->ar->winy/2);
@@ -356,6 +374,10 @@ static int flyEnd(bContext *C, FlyInfo *fly)
        if(fly->state == FLY_RUNNING)
                return OPERATOR_RUNNING_MODAL;
 
+       #ifdef NDOF_FLY_DEBUG
+       puts("\n-- fly end --");
+       #endif
+
        WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), fly->timer);
 
        ED_region_draw_cb_exit(fly->ar->type, fly->draw_handle_pixel);
@@ -401,6 +423,9 @@ static int flyEnd(bContext *C, FlyInfo *fly)
        if(fly->obtfm)
                MEM_freeN(fly->obtfm);
 
+       if(fly->ndof)
+               MEM_freeN(fly->ndof);
+
        if(fly->state == FLY_CONFIRM) {
                MEM_freeN(fly);
                return OPERATOR_FINISHED;
@@ -412,12 +437,54 @@ static int flyEnd(bContext *C, FlyInfo *fly)
 
 static void flyEvent(FlyInfo *fly, wmEvent *event)
 {
-       if (event->type == TIMER && event->customdata == fly->timer) {
-               fly->redraw = 1;
-       }
-       else if (event->type == MOUSEMOVE) {
+       if (event->type == MOUSEMOVE) {
                VECCOPY2D(fly->mval, event->mval);
-       } /* handle modal keymap first */
+       }
+       else if (event->type == NDOF_MOTION) {
+               // do these automagically get delivered? yes.
+               // puts("ndof motion detected in fly mode!");
+               // static const char* tag_name = "3D mouse position";
+
+               wmNDOFMotionData* incoming_ndof = (wmNDOFMotionData*) event->customdata;
+               switch (incoming_ndof->progress)
+                       {
+                       case P_STARTING:
+                               // start keeping track of 3D mouse position
+                               #ifdef NDOF_FLY_DEBUG
+                               puts("start keeping track of 3D mouse position");
+                               #endif
+                               // fall through...
+                       case P_IN_PROGRESS:
+                               // update 3D mouse position
+                               #ifdef NDOF_FLY_DEBUG
+                               putchar('.'); fflush(stdout);
+                               #endif
+                               if (fly->ndof == NULL)
+                                       // fly->ndof = MEM_mallocN(sizeof(wmNDOFMotionData), tag_name);
+                                       fly->ndof = MEM_dupallocN(incoming_ndof);
+                                       // fly->ndof = malloc(sizeof(wmNDOFMotionData));
+                               else
+                                       memcpy(fly->ndof, incoming_ndof, sizeof(wmNDOFMotionData));
+                               break;
+                       case P_FINISHING:
+                               // stop keeping track of 3D mouse position
+                               #ifdef NDOF_FLY_DEBUG
+                               puts("stop keeping track of 3D mouse position");
+                               #endif
+                               if (fly->ndof)
+                                       {
+                                       MEM_freeN(fly->ndof);
+                                       // free(fly->ndof);
+                                       fly->ndof = NULL;
+                                       }
+                               /* update the time else the view will jump when 2D mouse/timer resume */
+                               fly->time_lastdraw= PIL_check_seconds_timer();
+                               break;
+                       default:
+                               ; // should always be one of the above 3
+                       }
+               }
+       /* handle modal keymap first */
        else if (event->type == EVT_MODAL_MAP) {
                switch (event->val) {
                        case FLY_MODAL_CANCEL:
@@ -528,7 +595,6 @@ static void flyEvent(FlyInfo *fly, wmEvent *event)
                        case FLY_MODAL_PRECISION_DISABLE:
                                fly->use_precision= FALSE;
                                break;
-
                }
        }
 }
@@ -567,16 +633,14 @@ 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*/
 
+       #ifdef NDOF_FLY_DEBUG
+       static unsigned int iteration = 1;
+       printf("fly timer %d\n", iteration++);
+       #endif
+
        if(fly->root_parent)
                ED_view3d_to_m4(prev_view_mat, fly->rv3d->ofs, fly->rv3d->viewquat, fly->rv3d->dist);
 
-       /* the dist defines a vector that is infront of the offset
-       to rotate the view about.
-       this is no good for fly mode because we
-       want to rotate about the viewers center.
-       but to correct the dist removal we must
-       alter offset so the view doesn't jump. */
-
        xmargin= ar->winx/20.0f;
        ymargin= ar->winy/20.0f;
 
@@ -622,6 +686,8 @@ static int flyApply(bContext *C, FlyInfo *fly)
                        float time_redraw;
                        float time_redraw_clamped;
 
+                       fly->redraw= 1;
+
                        time_current= PIL_check_seconds_timer();
                        time_redraw= (float)(time_current - fly->time_lastdraw);
                        time_redraw_clamped= MIN2(0.05f, time_redraw); /* clamt the redraw time to avoid jitter in roll correction */
@@ -854,11 +920,103 @@ static int flyApply(bContext *C, FlyInfo *fly)
                copy_v3_v3(fly->dvec_prev, dvec);
        }
 
-/* moved to flyEnd() */
-
        return OPERATOR_FINISHED;
 }
 
+static int flyApply_ndof(bContext *C, FlyInfo *fly)
+{
+       // shorthand for oft-used variables
+       wmNDOFMotionData* ndof = fly->ndof;
+       const float dt = ndof->dt;
+       RegionView3D* rv3d = fly->rv3d;
+
+       const int shouldRotate = 1, shouldTranslate = 1;
+
+       float view_inv[4];
+       invert_qt_qt(view_inv, rv3d->viewquat);
+
+       if (shouldTranslate)
+               {
+               const float forward_sensitivity = 1.f;
+               const float vertical_sensitivity = 0.4f;
+               const float lateral_sensitivity = 0.6f;
+
+               float speed = 10.f; // blender units per second
+               // ^^ this is ok for default cube scene, but should scale with.. something
+
+               float trans[3] = {
+                       lateral_sensitivity * ndof->tx,
+                       vertical_sensitivity * ndof->ty,
+                       forward_sensitivity * ndof->tz
+                       };
+
+               mul_v3_fl(trans, speed * dt);
+
+               // transform motion from view to world coordinates
+               mul_qt_v3(view_inv, trans);
+
+               if (U.ndof_flag & NDOF_FLY_HELICOPTER)
+                       // could also use RNA to get a simple boolean value
+                       {
+                       // replace world z component with device y (yes it makes sense)
+                       trans[2] = speed * dt * vertical_sensitivity * ndof->ty;
+                       }
+
+               // move center of view opposite of hand motion (this is camera mode, not object mode)
+               sub_v3_v3(rv3d->ofs, trans);
+
+               fly->redraw = 1;
+               }
+
+       if (shouldRotate)
+               {
+               const float turn_sensitivity = 1.f;
+
+               float rotation[4];
+               float axis[3];
+               float angle = turn_sensitivity * ndof_to_angle_axis(ndof, axis);
+
+               // 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 (U.ndof_flag & NDOF_LOCK_HORIZON)
+                       // force an upright viewpoint
+                       // TODO: make this less... sudden
+                       {
+                       float view_horizon[3] = {1, 0, 0}; // view +x
+                       float view_direction[3] = {0, 0, -1}; // view -z (into screen)
+
+                       // find new inverse since viewquat has changed
+                       invert_qt_qt(view_inv, rv3d->viewquat);
+
+                       // transform view vectors to world coordinates
+                       mul_qt_v3(view_inv, view_horizon);
+                       mul_qt_v3(view_inv, view_direction);
+
+                       // find difference between view & world horizons
+                       // true horizon lives in world xy plane, so look only at difference in z
+                       angle = -asinf(view_horizon[2]);
+
+                       #ifdef NDOF_FLY_DEBUG
+                       printf("lock horizon: adjusting %.1f degrees\n\n", RAD2DEG(angle));
+                       #endif
+
+                       // rotate view so view horizon = world horizon
+                       axis_angle_to_quat(rotation, view_direction, angle);
+                       mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);          
+                       }
+
+               rv3d->view = RV3D_VIEW_USER;
+
+               fly->redraw = 1;
+               }
+
+       return OPERATOR_FINISHED;
+}
 
 
 static int fly_invoke(bContext *C, wmOperator *op, wmEvent *event)
@@ -908,7 +1066,12 @@ static int fly_modal(bContext *C, wmOperator *op, wmEvent *event)
 
        flyEvent(fly, event);
 
-       if(event->type==TIMER && event->customdata == fly->timer)
+       if (fly->ndof) // 3D mouse overrules [2D mouse + timer]
+               {
+               if (event->type==NDOF_MOTION)
+                       flyApply_ndof(C, fly);
+               }
+       else if (event->type==TIMER && event->customdata == fly->timer)
                flyApply(C, fly);
 
        do_draw |= fly->redraw;
index d3886d48873278e6c2879705a35796ebe7a94cc1..28e0e48f22052d89696c75753e0127ababc36dfa 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,7 @@ void VIEW3D_OT_dolly(struct wmOperatorType *ot);
 void VIEW3D_OT_zoom_camera_1_to_1(struct wmOperatorType *ot);
 void VIEW3D_OT_move(struct wmOperatorType *ot);
 void VIEW3D_OT_rotate(struct wmOperatorType *ot);
+void VIEW3D_OT_ndof(struct wmOperatorType *ot);
 void VIEW3D_OT_view_all(struct wmOperatorType *ot);
 void VIEW3D_OT_viewnumpad(struct wmOperatorType *ot);
 void VIEW3D_OT_view_selected(struct wmOperatorType *ot);
@@ -91,6 +93,8 @@ void VIEW3D_OT_zoom_border(struct wmOperatorType *ot);
 void VIEW3D_OT_drawtype(struct wmOperatorType *ot);
 
 void view3d_boxview_copy(ScrArea *sa, ARegion *ar);
+void ndof_to_quat(struct wmNDOFMotionData* ndof, float q[4]);
+float ndof_to_angle_axis(struct wmNDOFMotionData* ndof, float axis[3]);
 
 /* view3d_fly.c */
 void view3d_keymap(struct wmKeyConfig *keyconf);
index 05ef79a9f29a48a31330f77987301ceb2a3548e2..2dd560a6408297300babd5a033d7be0da182001c 100644 (file)
@@ -64,6 +64,7 @@ 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);
        WM_operatortype_append(VIEW3D_OT_view_all);
        WM_operatortype_append(VIEW3D_OT_viewnumpad);
        WM_operatortype_append(VIEW3D_OT_view_orbit);
@@ -161,6 +162,16 @@ 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", NDOF_MOTION, 0, 0, 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);
index ae57cf3f80b03ec5a80e4798803a76fc108b5b9e..a94c10a7aa08cd7957923684bba4b2823bea4184 100644 (file)
@@ -365,7 +365,7 @@ 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 ndof_pan, ndof_rotate; */
        short curssize;
        short color_picker_type;
        short ipo_new;                  /* interpolation mode for newly added F-Curves */
@@ -376,6 +376,10 @@ typedef struct UserDef {
        
        short widget_unit;              /* defaults to 20 for 72 DPI setting */
        short anisotropic_filter;
+       /*short pad[3];                 */
+
+       float ndof_sensitivity; /* overall sensitivity of 3D mouse */
+       int ndof_flag;                  /* flags for 3D mouse */
 
        char versemaster[160];
        char verseuser[160];
@@ -384,7 +388,7 @@ typedef struct UserDef {
        short autokey_mode;             /* autokeying mode */
        short autokey_flag;             /* flags for autokeying */
        
-       short text_render, pad9;                /*options for text rendering*/
+       short text_render, pad9[3];             /*options for text rendering*/
 
        struct ColorBand coba_weight;   /* from texture.h */
 
@@ -577,6 +581,17 @@ 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)
+*/
+
 #ifdef __cplusplus
 }
 #endif
index 4d1e1f175f2ca2df3b57c13d579a33bfec7eb2d1..664cb7732f9544392660fb4f5ecb610a55a0f307 100644 (file)
@@ -2737,7 +2737,9 @@ 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");
-       
+
+       /* 3D mouse settings */
+/*     
        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);
@@ -2747,7 +2749,24 @@ static void rna_def_userdef_input(BlenderRNA *brna)
        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");
-       
+*/
+       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, "3D Mouse Sensitivity", "Baseline 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 3D Mouse Guide", "Visualize the center and axis of rotation (or projected position in fly mode)");
+
+       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 Fly Mode", "");
+
+
        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 49bd3ede37d54212538a85b684c137993f14cee8..7476410ec19c93a72b03ba8ea0b495c78344c40a 100644 (file)
@@ -377,6 +377,26 @@ typedef struct wmTabletData {
        float Ytilt;            /* as above */
 } wmTabletData;
 
+typedef enum { // motion progress, for modal handlers
+       P_NOT_STARTED,
+       P_STARTING,    // <--
+       P_IN_PROGRESS, // <-- only these are sent for NDOF motion
+       P_FINISHING,   // <--
+       P_FINISHED
+       } wmProgress;
+
+typedef struct wmNDOFMotionData {
+       /* awfully similar to GHOST_TEventNDOFMotionData... */
+       // Each component normally ranges from -1 to +1, but can exceed that.
+       // These use blender standard view coordinates, with positive rotations being CCW about the axis.
+       float tx, ty, tz; // translation
+       float rx, ry, rz; // rotation:
+               // axis = (rx,ry,rz).normalized
+               // amount = (rx,ry,rz).magnitude [in revolutions, 1.0 = 360 deg]
+       float dt; // time since previous NDOF Motion event
+       wmProgress progress; // is this the first event, the last, or one of many in between?
+} wmNDOFMotionData;
+
 typedef struct wmTimer {
        struct wmTimer *next, *prev;
        
index ce3830b059cb3c6e724c0c00988b0f9c803c2275..26d72906ece5050a1c3d7a0fad547cf16339d718 100644 (file)
@@ -2309,6 +2309,28 @@ 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");
+
+       data->tx = ghost->tx;
+       data->ty = ghost->ty;
+       data->tz = ghost->tz;
+
+       data->rx = ghost->rx;
+       data->ry = ghost->ry;
+       data->rz = ghost->rz;
+
+       data->dt = ghost->dt;
+
+       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 +2377,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;
        
@@ -2557,6 +2579,36 @@ 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);
+
+                       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 29afdb570ea49733cd8778120b74c90b457f402a..32d2adffb01cb82f128cf1beb2ccbd696d96314e 100644 (file)
@@ -1411,6 +1411,113 @@ static void WM_OT_search_menu(wmOperatorType *ot)
        ot->poll= wm_search_menu_poll;
 }
 
+// BEGIN ndof menu -- experimental!
+
+#if 0
+static uiBlock* wm_block_ndof_menu_1st(bContext* C, ARegion* ar, void* UNUSED(arg_op))
+{
+       uiBlock* block;
+       uiBut* but;
+
+       block = uiBeginBlock(C, ar, "ndof_popup_menu", UI_EMBOSS);
+       uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
+//     uiBlockSetDirection(block, UI_DOWN);
+//     uiBlockBeginAlign(block);
+
+       // uiItemBooleanO(block->curlayout, "enable pan/zoom", ICON_NDOF_TRANS, "toggle_ndof_pan_zoom_enabled", "ndof_pan_zoom_enabled", 1);
+       // uiBlock is used as an opaque type in this file, so can't use members...
+
+       int foo = 333;
+       uiDefButI(block, TOG, 0, "foo", 10, 10, 9*UI_UNIT_X, UI_UNIT_Y, &foo, 0.f, 1.f, 0.1f, 0.9f, "15%");
+       // uiDefBut(block, TOG, 0, "enable pan/zoom", 0, 0, 10, 10, NULL, 0.f, 1.f, 0.f, 1.f, "don't talk to strangers");
+
+//     uiBlockEndAlign(block);
+//     uiBoundsBlock(block, 6);
+       uiEndBlock(C, block);
+
+       return block;
+}
+
+static int wm_ndof_menu_poll(bContext *C)
+{
+       if(CTX_wm_window(C)==NULL)
+               return 0;
+
+       // if menu is already pulled up, another button press should dismiss it
+       // not sure if that behavior should go here or elsewhere...
+
+       puts("ndof: menu poll");
+       return 1;
+}
+
+static int wm_ndof_menu_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+       puts("ndof: menu exec");
+       return OPERATOR_FINISHED;       
+}
+#endif
+
+static int wm_ndof_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
+{
+       uiPupMenuInvoke(C,"VIEW3D_MT_ndof_settings");
+
+       return OPERATOR_CANCELLED; // <-- correct?
+
+/*
+//     uiPupMenuNotice(C, "Hello!"); // <-- this works
+//     uiPupBlock(C, wm_block_ndof_menu, op); // <-- no luck!
+//     ui_popup_menu_create(C, NULL, NULL, NULL, NULL, "Hello!"); // <-- this works
+
+       uiPopupMenu* pup = uiPupMenuBegin(C,"3D mouse settings",ICON_NDOF_TURN);
+       uiLayout* layout = uiPupMenuLayout(pup);
+
+       uiItemS(layout); // separator
+       uiItemFloatO(layout, "sensitivity", 0, 0, "ndof_sensitivity", 1.f);
+       // do I have to look specifically in "UserPreferences" for ndof_sensitivity property?
+
+       // trial & error -- ok, mostly error
+//     uiItemBooleanO(layout, "enable pan/zoom", ICON_NDOF_TRANS, "ndof_toggle_pan_zoom_enabled", "ndof_pan_zoom_enabled", 1);
+//     uiItemBooleanO(layout, "enable rotation", ICON_NDOF_TURN, "ndof_toggle_rotation_enabled", "ndof_rotation_enabled", 1);
+//     uiItemV(layout,"sensitivity",ICON_NDOF_TRANS, 1);
+
+       printf("ndof: menu invoked in ");
+
+       switch (CTX_wm_area(C)->spacetype) // diff spaces can have diff 3d mouse options
+               {
+               case SPACE_VIEW3D:
+                       puts("3D area");
+                       uiItemS(layout);
+                       uiItemL(layout, "3D navigation mode", 0);
+                       uiItemBooleanO(layout, "helicopter", ICON_NDOF_FLY, 0, "ndof_fly_helicopter", 1);
+                       uiItemBooleanO(layout, "lock horizon", ICON_NDOF_DOM, 0, "ndof_lock_horizon", 1);
+                       break;
+               case SPACE_IMAGE:
+                       puts("image area");
+                       break;
+               default:
+                       puts("some iNDOFferent area");
+               }
+
+       //uiBlock* block = uiLayoutGetBlock(layout);
+       //int foo = 1;
+       //uiDefButI(block, TOG, 0, "foo", 10, 10, 9*UI_UNIT_X, UI_UNIT_Y, &foo, 0.f, 1.f, 0.1f, 0.9f, "15%");
+
+       uiPupMenuEnd(C,pup);
+*/
+}
+
+static void WM_OT_ndof_menu(wmOperatorType *ot)
+{
+       puts("ndof: registering menu operator");
+
+       ot->name = "NDOF Menu";
+       ot->idname = "WM_OT_ndof_menu";
+       
+       ot->invoke = wm_ndof_menu_invoke;
+}
+
+// END ndof menu
+
 static int wm_call_menu_exec(bContext *C, wmOperator *op)
 {
        char idname[BKE_ST_MAXNAME];
@@ -3453,6 +3560,7 @@ void wm_operatortype_init(void)
        WM_operatortype_append(WM_OT_debug_menu);
        WM_operatortype_append(WM_OT_splash);
        WM_operatortype_append(WM_OT_search_menu);
+       WM_operatortype_append(WM_OT_ndof_menu);
        WM_operatortype_append(WM_OT_call_menu);
        WM_operatortype_append(WM_OT_radial_control);
 #if defined(WIN32)
@@ -3675,7 +3783,9 @@ void wm_window_keymap(wmKeyConfig *keyconf)
        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);
-       
+
+       WM_keymap_add_item(keymap, "WM_OT_ndof_menu", NDOF_BUTTON_MENU, KM_PRESS, 0, 0);
+
        /* Space switching */
 
 
index 9b1695be67a066d4b22e1c409cfca2b7e1abd1df..7d6010786d2e7d81689dc1ae1ac68965b64e7e85 100644 (file)
@@ -621,12 +621,12 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr private)
                if (!ghostwin) {
                        // XXX - should be checked, why are we getting an event here, and
                        //      what is it?
-                       
+                       puts("<!> event has no window");
                        return 1;
                } else if (!GHOST_ValidWindow(g_system, ghostwin)) {
                        // XXX - should be checked, why are we getting an event here, and
                        //      what is it?
-                       
+                       puts("<!> event has invalid window");                   
                        return 1;
                } else {
                        win= GHOST_GetWindowUserData(ghostwin);
index ee080e7c0aadefa83be00f168e84e6e8f7591602..210dd902b99ec4fa8873ca09f415c0960dbac13e 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,
+       };
+
+
 /* SYSTEM : 0x01xx */
 #define        INPUTCHANGE             0x0103  /* input connected or disconnected */
 #define WINDEACTIVATE  0x0104  /* window is deactivated, focus lost */