0031632: Draw Harness - handle 3d mouse Raw HID input on Windows
authorkgv <kgv@opencascade.com>
Fri, 26 Jun 2020 06:58:50 +0000 (09:58 +0300)
committerbugmaster <bugmaster@opencascade.com>
Fri, 3 Jul 2020 08:03:05 +0000 (11:03 +0300)
WNT_HIDSpaceMouse - added auxiliary class for processing 3d mouse Raw HID input.
AIS_ViewController::Update3dMouse() - added default processor for 3d mouse input event.
ViewerTest now redirects WM_INPUT to AIS_ViewController::Update3dMouse().
Aspect_VKey enumeration has been extended by 3D view buttons.
WNT_Window::RegisterRawInputDevices() has beend added as a small wrapper
over WinAPI RegisterRawInputDevices() for common HID input devices.

AIS_ViewCube now stores animation duration within AIS_AnimationCamera instead of dedicated duplicating class property.

15 files changed:
adm/cmake/occt_toolkit.cmake
adm/genproj.tcl
src/AIS/AIS_ViewController.cxx
src/AIS/AIS_ViewController.hxx
src/AIS/AIS_ViewCube.cxx
src/AIS/AIS_ViewCube.hxx
src/Aspect/Aspect_VKey.hxx
src/Aspect/Aspect_VKeySet.hxx
src/ViewerTest/ViewerTest_EventManager.cxx
src/ViewerTest/ViewerTest_ViewerCommands.cxx
src/WNT/FILES
src/WNT/WNT_HIDSpaceMouse.cxx [new file with mode: 0644]
src/WNT/WNT_HIDSpaceMouse.hxx [new file with mode: 0644]
src/WNT/WNT_Window.cxx
src/WNT/WNT_Window.hxx

index f59b163..38c39ae 100644 (file)
@@ -57,117 +57,110 @@ foreach (OCCT_PACKAGE ${USED_PACKAGES})
     set (OCCT_PACKAGE_NAME "${OCCT_PACKAGE}")
   endif()
 
-  # TKService contains platform-dependent packages: Xw and WNT
-  if ((WIN32 AND "${OCCT_PACKAGE}" STREQUAL "Xw") OR (NOT WIN32 AND "${OCCT_PACKAGE}" STREQUAL "WNT"))
-    # do nothing
-  else()
-
-    if (WIN32)
-      list (APPEND PRECOMPILED_DEFS "-D__${OCCT_PACKAGE_NAME}_DLL")
-    endif()
-
-    set (SOURCE_FILES)
-    set (HEADER_FILES)
+  if (WIN32)
+    list (APPEND PRECOMPILED_DEFS "-D__${OCCT_PACKAGE_NAME}_DLL")
+  endif()
 
-    # Generate Flex and Bison files
-    if (${BUILD_YACCLEX})
+  set (SOURCE_FILES)
+  set (HEADER_FILES)
 
-      # flex files
-      OCCT_ORIGIN_AND_PATCHED_FILES ("${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}" "*[.]lex" SOURCE_FILES_FLEX)
-      list (LENGTH SOURCE_FILES_FLEX SOURCE_FILES_FLEX_LEN)
+  # Generate Flex and Bison files
+  if (${BUILD_YACCLEX})
+    # flex files
+    OCCT_ORIGIN_AND_PATCHED_FILES ("${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}" "*[.]lex" SOURCE_FILES_FLEX)
+    list (LENGTH SOURCE_FILES_FLEX SOURCE_FILES_FLEX_LEN)
 
-      # bison files
-      OCCT_ORIGIN_AND_PATCHED_FILES ("${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}" "*[.]yacc" SOURCE_FILES_BISON)
-      list (LENGTH SOURCE_FILES_BISON SOURCE_FILES_BISON_LEN)
+    # bison files
+    OCCT_ORIGIN_AND_PATCHED_FILES ("${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}" "*[.]yacc" SOURCE_FILES_BISON)
+    list (LENGTH SOURCE_FILES_BISON SOURCE_FILES_BISON_LEN)
 
-      if (${SOURCE_FILES_FLEX_LEN} EQUAL ${SOURCE_FILES_BISON_LEN} AND NOT ${SOURCE_FILES_FLEX_LEN} EQUAL 0)
+    if (${SOURCE_FILES_FLEX_LEN} EQUAL ${SOURCE_FILES_BISON_LEN} AND NOT ${SOURCE_FILES_FLEX_LEN} EQUAL 0)
       
-        list (SORT SOURCE_FILES_FLEX)
-        list (SORT SOURCE_FILES_BISON)
+      list (SORT SOURCE_FILES_FLEX)
+      list (SORT SOURCE_FILES_BISON)
 
-        math (EXPR SOURCE_FILES_FLEX_LEN "${SOURCE_FILES_FLEX_LEN} - 1")
-        foreach (FLEX_FILE_INDEX RANGE ${SOURCE_FILES_FLEX_LEN})
+      math (EXPR SOURCE_FILES_FLEX_LEN "${SOURCE_FILES_FLEX_LEN} - 1")
+      foreach (FLEX_FILE_INDEX RANGE ${SOURCE_FILES_FLEX_LEN})
 
-          list (GET SOURCE_FILES_FLEX ${FLEX_FILE_INDEX} CURRENT_FLEX_FILE)
-          get_filename_component (CURRENT_FLEX_FILE_NAME ${CURRENT_FLEX_FILE} NAME_WE)
+        list (GET SOURCE_FILES_FLEX ${FLEX_FILE_INDEX} CURRENT_FLEX_FILE)
+        get_filename_component (CURRENT_FLEX_FILE_NAME ${CURRENT_FLEX_FILE} NAME_WE)
 
-          list (GET SOURCE_FILES_BISON ${FLEX_FILE_INDEX} CURRENT_BISON_FILE)
-          get_filename_component (CURRENT_BISON_FILE_NAME ${CURRENT_BISON_FILE} NAME_WE)
+        list (GET SOURCE_FILES_BISON ${FLEX_FILE_INDEX} CURRENT_BISON_FILE)
+        get_filename_component (CURRENT_BISON_FILE_NAME ${CURRENT_BISON_FILE} NAME_WE)
           
-          string (COMPARE EQUAL ${CURRENT_FLEX_FILE_NAME} ${CURRENT_BISON_FILE_NAME} ARE_FILES_EQUAL)
-
-          if (EXISTS "${CURRENT_FLEX_FILE}" AND EXISTS "${CURRENT_BISON_FILE}" AND ${ARE_FILES_EQUAL})
-            set (BISON_OUTPUT_FILE ${CURRENT_BISON_FILE_NAME}.tab.c)
-            set (FLEX_OUTPUT_FILE lex.${CURRENT_FLEX_FILE_NAME}.c)
-            BISON_TARGET (Parser_${CURRENT_BISON_FILE_NAME} ${CURRENT_BISON_FILE} ${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${BISON_OUTPUT_FILE} COMPILE_FLAGS "-p ${CURRENT_BISON_FILE_NAME}")
-            FLEX_TARGET  (Scanner_${CURRENT_FLEX_FILE_NAME} ${CURRENT_FLEX_FILE} ${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${FLEX_OUTPUT_FILE} COMPILE_FLAGS "-P${CURRENT_FLEX_FILE_NAME}")
-            ADD_FLEX_BISON_DEPENDENCY (Scanner_${CURRENT_FLEX_FILE_NAME} Parser_${CURRENT_BISON_FILE_NAME})
+        string (COMPARE EQUAL ${CURRENT_FLEX_FILE_NAME} ${CURRENT_BISON_FILE_NAME} ARE_FILES_EQUAL)
+
+        if (EXISTS "${CURRENT_FLEX_FILE}" AND EXISTS "${CURRENT_BISON_FILE}" AND ${ARE_FILES_EQUAL})
+          set (BISON_OUTPUT_FILE ${CURRENT_BISON_FILE_NAME}.tab.c)
+          set (FLEX_OUTPUT_FILE lex.${CURRENT_FLEX_FILE_NAME}.c)
+          BISON_TARGET (Parser_${CURRENT_BISON_FILE_NAME} ${CURRENT_BISON_FILE} ${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${BISON_OUTPUT_FILE} COMPILE_FLAGS "-p ${CURRENT_BISON_FILE_NAME}")
+          FLEX_TARGET  (Scanner_${CURRENT_FLEX_FILE_NAME} ${CURRENT_FLEX_FILE} ${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${FLEX_OUTPUT_FILE} COMPILE_FLAGS "-P${CURRENT_FLEX_FILE_NAME}")
+          ADD_FLEX_BISON_DEPENDENCY (Scanner_${CURRENT_FLEX_FILE_NAME} Parser_${CURRENT_BISON_FILE_NAME})
            
-            list (APPEND SOURCE_FILES ${BISON_OUTPUT_FILE} ${FLEX_OUTPUT_FILE})
-          endif()
-        endforeach()
-      endif()
+          list (APPEND SOURCE_FILES ${BISON_OUTPUT_FILE} ${FLEX_OUTPUT_FILE})
+        endif()
+      endforeach()
     endif()
+  endif()
 
-    # header files
-    if (BUILD_PATCH AND EXISTS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES")
-      file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" HEADER_FILES_M   REGEX ".+[.]h")
-      file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" HEADER_FILES_LXX REGEX ".+[.]lxx")
-      file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" HEADER_FILES_GXX REGEX ".+[.]gxx")
+  # header files
+  if (BUILD_PATCH AND EXISTS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES")
+    file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" HEADER_FILES_M   REGEX ".+[.]h")
+    file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" HEADER_FILES_LXX REGEX ".+[.]lxx")
+    file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" HEADER_FILES_GXX REGEX ".+[.]gxx")
 
-      file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" SOURCE_FILES_C REGEX ".+[.]c")
-      if(APPLE)
-        file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" SOURCE_FILES_M REGEX ".+[.]mm")
-      endif()
-    else()
-      file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES"     HEADER_FILES_M   REGEX ".+[.]h")
-      file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES"     HEADER_FILES_LXX REGEX ".+[.]lxx")
-      file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES"     HEADER_FILES_GXX REGEX ".+[.]gxx")
+    file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" SOURCE_FILES_C REGEX ".+[.]c")
+    if(APPLE)
+      file (STRINGS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES" SOURCE_FILES_M REGEX ".+[.]mm")
+    endif()
+  else()
+    file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES"     HEADER_FILES_M   REGEX ".+[.]h")
+    file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES"     HEADER_FILES_LXX REGEX ".+[.]lxx")
+    file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES"     HEADER_FILES_GXX REGEX ".+[.]gxx")
 
-      file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES"     SOURCE_FILES_C REGEX ".+[.]c")
-      if(APPLE)
-        file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES"   SOURCE_FILES_M REGEX ".+[.]mm")
-      endif()
+    file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES"     SOURCE_FILES_C REGEX ".+[.]c")
+    if(APPLE)
+      file (STRINGS "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/FILES"   SOURCE_FILES_M REGEX ".+[.]mm")
     endif()
+  endif()
     
-    list (APPEND HEADER_FILES ${HEADER_FILES_M} ${HEADER_FILES_LXX} ${SOURCE_FILES_GXX})
-    list (APPEND SOURCE_FILES ${SOURCE_FILES_C})
-    if(APPLE)
-      list (APPEND SOURCE_FILES ${SOURCE_FILES_M})
+  list (APPEND HEADER_FILES ${HEADER_FILES_M} ${HEADER_FILES_LXX} ${SOURCE_FILES_GXX})
+  list (APPEND SOURCE_FILES ${SOURCE_FILES_C})
+  if(APPLE)
+    list (APPEND SOURCE_FILES ${SOURCE_FILES_M})
+  endif()
+
+  foreach(HEADER_FILE ${HEADER_FILES})
+    if (BUILD_PATCH AND EXISTS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}")
+      message (STATUS "Info: consider patched file: ${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}")
+      list (APPEND USED_INCFILES "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}")
+      SOURCE_GROUP ("Header Files\\${OCCT_PACKAGE_NAME}" FILES "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}")
+    else()
+      list (APPEND USED_INCFILES "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}")
+      SOURCE_GROUP ("Header Files\\${OCCT_PACKAGE_NAME}" FILES "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}")
     endif()
+  endforeach()
 
-    foreach(HEADER_FILE ${HEADER_FILES})
-      if (BUILD_PATCH AND EXISTS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}")
-        message (STATUS "Info: consider patched file: ${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}")
-        list (APPEND USED_INCFILES "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}")
-        SOURCE_GROUP ("Header Files\\${OCCT_PACKAGE_NAME}" FILES "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}")
-      else()
-        list (APPEND USED_INCFILES "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}")
-        SOURCE_GROUP ("Header Files\\${OCCT_PACKAGE_NAME}" FILES "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${HEADER_FILE}")
-      endif()
-    endforeach()
-
-    foreach(SOURCE_FILE ${SOURCE_FILES})
-      if (BUILD_PATCH AND EXISTS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}")
-        message (STATUS "Info: consider patched file: ${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}")
-        list (APPEND USED_SRCFILES "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}")
-        SOURCE_GROUP ("Source Files\\${OCCT_PACKAGE_NAME}" FILES "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}")
-      else()
-        list (APPEND USED_SRCFILES "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}")
-        SOURCE_GROUP ("Source Files\\${OCCT_PACKAGE_NAME}" FILES "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}")
-      endif()
-    endforeach()
+  foreach(SOURCE_FILE ${SOURCE_FILES})
+    if (BUILD_PATCH AND EXISTS "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}")
+      message (STATUS "Info: consider patched file: ${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}")
+      list (APPEND USED_SRCFILES "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}")
+      SOURCE_GROUP ("Source Files\\${OCCT_PACKAGE_NAME}" FILES "${BUILD_PATCH}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}")
+    else()
+      list (APPEND USED_SRCFILES "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}")
+      SOURCE_GROUP ("Source Files\\${OCCT_PACKAGE_NAME}" FILES "${CMAKE_SOURCE_DIR}/${RELATIVE_SOURCES_DIR}/${OCCT_PACKAGE}/${SOURCE_FILE}")
+    endif()
+  endforeach()
 
-    if (USE_QT)
-      FIND_AND_INSTALL_QT_RESOURCES (${OCCT_PACKAGE} RESOURCE_FILES)
-      #message("Qt Resource files are: ${QT_RESOURCE_FILES} in ${OCCT_PACKAGE}")
-    endif(USE_QT)
+  if (USE_QT)
+    FIND_AND_INSTALL_QT_RESOURCES (${OCCT_PACKAGE} RESOURCE_FILES)
+    #message("Qt Resource files are: ${QT_RESOURCE_FILES} in ${OCCT_PACKAGE}")
+  endif(USE_QT)
 
-    #message("Resource files are: ${RESOURCE_FILES} in ${OCCT_PACKAGE}")
-    foreach(RESOURCE_FILE ${RESOURCE_FILES})
-      SOURCE_GROUP ("Resource Files\\${OCCT_PACKAGE_NAME}" FILES "${RESOURCE_FILE}")
-    endforeach()
-  endif()
+  #message("Resource files are: ${RESOURCE_FILES} in ${OCCT_PACKAGE}")
+  foreach(RESOURCE_FILE ${RESOURCE_FILES})
+    SOURCE_GROUP ("Resource Files\\${OCCT_PACKAGE_NAME}" FILES "${RESOURCE_FILE}")
+  endforeach()
 endforeach()
 string (REGEX REPLACE ";" " " PRECOMPILED_DEFS "${PRECOMPILED_DEFS}")
 
index 501bf1c..8a077ae 100644 (file)
@@ -1596,11 +1596,6 @@ proc osutils:tk:units { tkloc theSrcDir } {
   return $l
 }
 
-proc osutils:justwnt { listloc } {
-  set goaway [list Xw]
-  return [osutils:juststation $goaway $listloc]
-}
-
 # remove from listloc OpenCascade units indesirables on NT
 proc osutils:juststation {goaway listloc} {
   global path
@@ -2267,17 +2262,6 @@ proc wokUtils:FILES:mkdir { d } {
     }
 }
 
-# remove from listloc OpenCascade units indesirables on Unix
-proc osutils:justunix { listloc } {
-  if { "$::tcl_platform(os)" == "Darwin" } {
-    set goaway [list Xw WNT]
-  } else {
-    set goaway [list WNT]
-  }
-  return [osutils:juststation $goaway $listloc]
-}
-
-
 ####### CODEBLOCK ###################################################################
 # Function to generate Code Blocks workspace and project files
 proc OS:MKCBP { theOutDir theModules theAllSolution thePlatform theCmpl } {
@@ -2333,13 +2317,8 @@ proc osutils:cbptk { theCmpl theOutDir theToolKit thePlatform} {
     set listloc $theToolKit
   }
 
-  if { $thePlatform == "wnt" || $thePlatform == "uwp" } {
-    set resultloc [osutils:justwnt  $listloc]
-  } else {
-    set resultloc [osutils:justunix $listloc]
-  }
   if [array exists written] { unset written }
-  foreach fxlo $resultloc {
+  foreach fxlo $listloc {
     set xlo       $fxlo
     set aSrcFiles [osutils:tk:cxxfiles $xlo $thePlatform "src"]
     foreach aSrcFile [lsort $aSrcFiles] {
@@ -2928,9 +2907,8 @@ proc osutils:xcdtk:sources {theToolKit theTargetType theSrcFileRefSection theGro
   upvar $theIncPaths          anIncPaths
 
   set listloc [osutils:tk:units $theToolKit "src"]
-  set resultloc [osutils:justunix $listloc]
   set aBuildFileSection ""
-  set aPackages [lsort -nocase $resultloc]
+  set aPackages [lsort -nocase $listloc]
   if { "$theTargetType" == "executable" } {
     set aPackages [list "$theToolKit"]
   }
index 736f0ac..17a5425 100644 (file)
@@ -28,6 +28,7 @@
 #include <Message_Messenger.hxx>
 #include <gp_Quaternion.hxx>
 #include <V3d_View.hxx>
+#include <WNT_HIDSpaceMouse.hxx>
 
 // =======================================================================
 // function : AIS_ViewController
@@ -98,9 +99,17 @@ AIS_ViewController::AIS_ViewController()
   myUpdateStartPointRot  (true),
   myUpdateStartPointZRot (true),
   //
+  my3dMouseNoRotate  (false, false, false),
+  my3dMouseToReverse (true,  false, false),
+  my3dMouseAccelTrans  (2.0f),
+  my3dMouseAccelRotate (4.0f),
+  my3dMouseIsQuadric   (true),
+  //
   myPanPnt3d (Precision::Infinite(), 0.0, 0.0)
 {
+  memset(my3dMouseButtonState, 0, sizeof(my3dMouseButtonState));
   myEventTimer.Start();
+  myViewAnimation->SetOwnDuration (0.5);
 
   myAnchorPointPrs1 = new AIS_Point (new Geom_CartesianPoint (0.0, 0.0, 0.0));
   myAnchorPointPrs1->SetZLayer (Graphic3d_ZLayerId_Top);
@@ -1093,6 +1102,112 @@ void AIS_ViewController::UpdateTouchPoint (Standard_Size theId,
 }
 
 // =======================================================================
+// function : Update3dMouse
+// purpose  :
+// =======================================================================
+bool AIS_ViewController::Update3dMouse (const WNT_HIDSpaceMouse& theEvent)
+{
+  bool toUpdate = false;
+  toUpdate = update3dMouseTranslation (theEvent) || toUpdate;
+  toUpdate = update3dMouseRotation (theEvent) || toUpdate;
+  toUpdate = update3dMouseKeys (theEvent) || toUpdate;
+  return toUpdate;
+}
+
+// =======================================================================
+// function : update3dMouseTranslation
+// purpose  :
+// =======================================================================
+bool AIS_ViewController::update3dMouseTranslation (const WNT_HIDSpaceMouse& theEvent)
+{
+  if (!theEvent.IsTranslation())
+  {
+    return false;
+  }
+
+  bool isIdle = true;
+  const double aTimeStamp = EventTime();
+  const Graphic3d_Vec3d aTrans = theEvent.Translation (isIdle, my3dMouseIsQuadric) * my3dMouseAccelTrans;
+  myKeys.KeyFromAxis (Aspect_VKey_NavSlideLeft, Aspect_VKey_NavSlideRight, aTimeStamp, aTrans.x());
+  myKeys.KeyFromAxis (Aspect_VKey_NavForward,   Aspect_VKey_NavBackward,   aTimeStamp, aTrans.y());
+  myKeys.KeyFromAxis (Aspect_VKey_NavSlideUp,   Aspect_VKey_NavSlideDown,  aTimeStamp, aTrans.z());
+  return true;
+}
+
+// =======================================================================
+// function : update3dMouseRotation
+// purpose  :
+// =======================================================================
+bool AIS_ViewController::update3dMouseRotation (const WNT_HIDSpaceMouse& theEvent)
+{
+  if (!theEvent.IsRotation()
+   || !myToAllowRotation)
+  {
+    return false;
+  }
+
+  bool isIdle = true, toUpdate = false;
+  const double aTimeStamp = EventTime();
+  const Graphic3d_Vec3d aRot3 = theEvent.Rotation (isIdle, my3dMouseIsQuadric) * my3dMouseAccelRotate;
+  if (!my3dMouseNoRotate.x())
+  {
+    KeyFromAxis (Aspect_VKey_NavLookUp,   Aspect_VKey_NavLookDown,  aTimeStamp, !my3dMouseToReverse.x() ? aRot3.x() : -aRot3.x());
+    toUpdate = true;
+  }
+  if (!my3dMouseNoRotate.y())
+  {
+    KeyFromAxis (Aspect_VKey_NavRollCW,   Aspect_VKey_NavRollCCW,   aTimeStamp, !my3dMouseToReverse.y() ? aRot3.y() : -aRot3.y());
+    toUpdate = true;
+  }
+  if (!my3dMouseNoRotate.z())
+  {
+    KeyFromAxis (Aspect_VKey_NavLookLeft, Aspect_VKey_NavLookRight, aTimeStamp, !my3dMouseToReverse.z() ? aRot3.z() : -aRot3.z());
+    toUpdate = true;
+  }
+  return toUpdate;
+}
+
+// =======================================================================
+// function : update3dMouseKeys
+// purpose  :
+// =======================================================================
+bool AIS_ViewController::update3dMouseKeys (const WNT_HIDSpaceMouse& theEvent)
+{
+  bool toUpdate = false;
+  const double aTimeStamp = EventTime();
+  if (theEvent.IsKeyState())
+  {
+    const uint32_t aKeyState = theEvent.KeyState();
+    for (unsigned short aKeyBit = 0; aKeyBit < 32; ++aKeyBit)
+    {
+      const bool isPressed  = (aKeyState & (1 << aKeyBit)) != 0;
+      const bool isReleased = my3dMouseButtonState[aKeyBit] && !isPressed;
+      //const bool isRepeated = my3dMouseButtonState[aKeyBit] &&  isPressed;
+      my3dMouseButtonState[aKeyBit] = isPressed;
+      if (!isReleased && !isPressed)
+      {
+        continue;
+      }
+
+      const Aspect_VKey aVKey = theEvent.HidToSpaceKey (aKeyBit);
+      if (aVKey != Aspect_VKey_UNKNOWN)
+      {
+        toUpdate = true;
+        if (isPressed)
+        {
+          KeyDown (aVKey, aTimeStamp);
+        }
+        else
+        {
+          KeyUp (aVKey, aTimeStamp);
+        }
+      }
+    }
+  }
+  return toUpdate;
+}
+
+// =======================================================================
 // function : SetNavigationMode
 // purpose  :
 // =======================================================================
@@ -1756,6 +1871,135 @@ gp_Pnt AIS_ViewController::GravityPoint (const Handle(AIS_InteractiveContext)& t
 }
 
 // =======================================================================
+// function : FitAllAuto
+// purpose  :
+// =======================================================================
+void AIS_ViewController::FitAllAuto (const Handle(AIS_InteractiveContext)& theCtx,
+                                     const Handle(V3d_View)& theView)
+{
+  const Bnd_Box aBoxSel = theCtx->BoundingBoxOfSelection();
+  const double aFitMargin = 0.01;
+  if (aBoxSel.IsVoid())
+  {
+    theView->FitAll (aFitMargin, false);
+    return;
+  }
+
+  // fit all algorithm is not 100% stable - so compute some precision to compare equal camera values
+  const double  aFitTol = (aBoxSel.CornerMax().XYZ() - aBoxSel.CornerMin().XYZ()).Modulus() * 0.000001;
+  const Bnd_Box aBoxAll = theView->View()->MinMaxValues();
+
+  const Handle(Graphic3d_Camera)& aCam = theView->Camera();
+  Handle(Graphic3d_Camera) aCameraSel = new Graphic3d_Camera (aCam);
+  Handle(Graphic3d_Camera) aCameraAll = new Graphic3d_Camera (aCam);
+  theView->FitMinMax (aCameraSel, aBoxSel, aFitMargin);
+  theView->FitMinMax (aCameraAll, aBoxAll, aFitMargin);
+  if (aCameraSel->Center().IsEqual (aCam->Center(),     aFitTol)
+   && Abs (aCameraSel->Scale()    - aCam->Scale())    < aFitTol
+   && Abs (aCameraSel->Distance() - aCam->Distance()) < aFitTol)
+  {
+    // fit all entire view on second FitALL request
+    aCam->Copy (aCameraAll);
+  }
+  else
+  {
+    aCam->Copy (aCameraSel);
+  }
+}
+
+// =======================================================================
+// function : handleViewOrientationKeys
+// purpose  :
+// =======================================================================
+void AIS_ViewController::handleViewOrientationKeys (const Handle(AIS_InteractiveContext)& theCtx,
+                                                    const Handle(V3d_View)& theView)
+{
+  if (myNavigationMode == AIS_NavigationMode_FirstPersonWalk)
+  {
+    return;
+  }
+
+  Handle(Graphic3d_Camera) aCameraBack;
+  struct ViewKeyAction
+  {
+    Aspect_VKey Key;
+    V3d_TypeOfOrientation Orientation;
+  };
+  static const ViewKeyAction THE_VIEW_KEYS[] =
+  {
+    { Aspect_VKey_ViewTop,          V3d_TypeOfOrientation_Zup_Top },
+    { Aspect_VKey_ViewBottom,       V3d_TypeOfOrientation_Zup_Bottom },
+    { Aspect_VKey_ViewLeft,         V3d_TypeOfOrientation_Zup_Left },
+    { Aspect_VKey_ViewRight,        V3d_TypeOfOrientation_Zup_Right },
+    { Aspect_VKey_ViewFront,        V3d_TypeOfOrientation_Zup_Front },
+    { Aspect_VKey_ViewBack,         V3d_TypeOfOrientation_Zup_Back },
+    { Aspect_VKey_ViewAxoLeftProj,  V3d_TypeOfOrientation_Zup_AxoLeft },
+    { Aspect_VKey_ViewAxoRightProj, V3d_TypeOfOrientation_Zup_AxoRight },
+    { Aspect_VKey_ViewRoll90CW,     (V3d_TypeOfOrientation )-1},
+    { Aspect_VKey_ViewRoll90CCW,    (V3d_TypeOfOrientation )-1},
+    { Aspect_VKey_ViewFitAll,       (V3d_TypeOfOrientation )-1}
+  };
+  {
+    Standard_Mutex::Sentry aLock (myKeys.Mutex());
+    const size_t aNbKeys = sizeof(THE_VIEW_KEYS) / sizeof(*THE_VIEW_KEYS);
+    const double anEventTime = EventTime();
+    for (size_t aKeyIter = 0; aKeyIter < aNbKeys; ++aKeyIter)
+    {
+      const ViewKeyAction& aKeyAction = THE_VIEW_KEYS[aKeyIter];
+      if (!myKeys.IsKeyDown (aKeyAction.Key))
+      {
+        continue;
+      }
+
+      myKeys.KeyUp (aKeyAction.Key, anEventTime);
+      if (aCameraBack.IsNull())
+      {
+        aCameraBack = theView->Camera();
+        theView->SetCamera (new Graphic3d_Camera (aCameraBack));
+      }
+      if (aKeyAction.Orientation != (V3d_TypeOfOrientation )-1)
+      {
+        theView->SetProj (aKeyAction.Orientation);
+        FitAllAuto (theCtx, theView);
+      }
+      else if (aKeyAction.Key == Aspect_VKey_ViewRoll90CW)
+      {
+        const double aTwist = theView->Twist() + M_PI / 2.0;
+        theView->SetTwist (aTwist);
+      }
+      else if (aKeyAction.Key == Aspect_VKey_ViewRoll90CCW)
+      {
+        const double aTwist = theView->Twist() - M_PI / 2.0;
+        theView->SetTwist (aTwist);
+      }
+      else if (aKeyAction.Key == Aspect_VKey_ViewFitAll)
+      {
+        FitAllAuto (theCtx, theView);
+      }
+    }
+  }
+
+  if (aCameraBack.IsNull())
+  {
+    return;
+  }
+
+  Handle(Graphic3d_Camera) aCameraNew = theView->Camera();
+  theView->SetCamera (aCameraBack);
+  const Graphic3d_Mat4d anOrientMat1 = aCameraBack->OrientationMatrix();
+  const Graphic3d_Mat4d anOrientMat2 = aCameraNew ->OrientationMatrix();
+  if (anOrientMat1 != anOrientMat2)
+  {
+    const Handle(AIS_AnimationCamera)& aCamAnim = myViewAnimation;
+    aCamAnim->SetView (theView);
+    aCamAnim->SetStartPts (0.0);
+    aCamAnim->SetCameraStart (new Graphic3d_Camera (aCameraBack));
+    aCamAnim->SetCameraEnd   (new Graphic3d_Camera (aCameraNew));
+    aCamAnim->StartTimer (0.0, 1.0, true, false);
+  }
+}
+
+// =======================================================================
 // function : handleNavigationKeys
 // purpose  :
 // =======================================================================
@@ -2763,7 +3007,7 @@ void AIS_ViewController::handleViewRedraw (const Handle(AIS_InteractiveContext)&
   {
     const Handle(V3d_View)& aView = aViewIter.Value();
     if (aView->IsInvalidated()
-     || myToAskNextFrame)
+     || (myToAskNextFrame && aView == theView))
     {
       if (aView->ComputedMode())
       {
@@ -3056,6 +3300,7 @@ void AIS_ViewController::HandleViewEvents (const Handle(AIS_InteractiveContext)&
 {
   const bool wasImmediateUpdate = theView->SetImmediateUpdate (false);
 
+  handleViewOrientationKeys (theCtx, theView);
   const AIS_WalkDelta aWalk = handleNavigationKeys (theCtx, theView);
   handleXRInput (theCtx, theView, aWalk);
   if (theView->View()->IsActiveXR())
index eaa12fb..11a6b60 100644 (file)
@@ -41,6 +41,7 @@ class AIS_RubberBand;
 class AIS_XRTrackedDevice;
 class Graphic3d_Camera;
 class V3d_View;
+class WNT_HIDSpaceMouse;
 
 //! Auxiliary structure for handling viewer events between GUI and Rendering threads.
 //!
@@ -428,6 +429,50 @@ public: //! @name multi-touch input
   Standard_EXPORT virtual void UpdateTouchPoint (Standard_Size theId,
                                                  const Graphic3d_Vec2d& thePnt);
 
+public: //! @name 3d mouse input
+
+  //! Return acceleration ratio for translation event; 2.0 by default.
+  float Get3dMouseTranslationScale() const { return my3dMouseAccelTrans; }
+
+  //! Set acceleration ratio for translation event.
+  void Set3dMouseTranslationScale (float theScale) { my3dMouseAccelTrans = theScale; }
+
+  //! Return acceleration ratio for rotation event; 4.0 by default.
+  float Get3dMouseRotationScale() const { return my3dMouseAccelRotate; }
+
+  //! Set acceleration ratio for rotation event.
+  void Set3dMouseRotationScale (float theScale) { my3dMouseAccelRotate = theScale; }
+
+  //! Return quadric acceleration flag; TRUE by default.
+  bool To3dMousePreciseInput() const { return my3dMouseIsQuadric; }
+
+  //! Set quadric acceleration flag.
+  void Set3dMousePreciseInput (bool theIsQuadric) { my3dMouseIsQuadric = theIsQuadric; }
+
+  //! Return 3d mouse rotation axes (tilt/roll/spin) ignore flag; (FALSE, FALSE, FALSE) by default.
+  const NCollection_Vec3<bool>& Get3dMouseIsNoRotate() const { return my3dMouseNoRotate; }
+
+  //! Return 3d mouse rotation axes (tilt/roll/spin) ignore flag; (FALSE, FALSE, FALSE) by default.
+  NCollection_Vec3<bool>& Change3dMouseIsNoRotate() { return my3dMouseNoRotate; }
+
+  //! Return 3d mouse rotation axes (tilt/roll/spin) reverse flag; (TRUE, FALSE, FALSE) by default.
+  const NCollection_Vec3<bool>& Get3dMouseToReverse() const { return my3dMouseToReverse; }
+
+  //! Return 3d mouse rotation axes (tilt/roll/spin) reverse flag; (TRUE, FALSE, FALSE) by default.
+  NCollection_Vec3<bool>& Change3dMouseToReverse() { return my3dMouseToReverse; }
+
+  //! Process 3d mouse input event (redirects to translation, rotation and keys).
+  Standard_EXPORT virtual bool Update3dMouse (const WNT_HIDSpaceMouse& theEvent);
+
+  //! Process 3d mouse input translation event.
+  Standard_EXPORT virtual bool update3dMouseTranslation (const WNT_HIDSpaceMouse& theEvent);
+
+  //! Process 3d mouse input rotation event.
+  Standard_EXPORT virtual bool update3dMouseRotation (const WNT_HIDSpaceMouse& theEvent);
+
+  //! Process 3d mouse input keys event.
+  Standard_EXPORT virtual bool update3dMouseKeys (const WNT_HIDSpaceMouse& theEvent);
+
 public:
 
   //! Return event time (e.g. current time).
@@ -488,14 +533,25 @@ public:
   Standard_EXPORT virtual gp_Pnt GravityPoint (const Handle(AIS_InteractiveContext)& theCtx,
                                                const Handle(V3d_View)& theView);
 
+  //! Modify view camera to fit all objects.
+  //! Default implementation fits either all visible and all selected objects (swapped on each call).
+  Standard_EXPORT virtual void FitAllAuto (const Handle(AIS_InteractiveContext)& theCtx,
+                                           const Handle(V3d_View)& theView);
+
 public:
 
-  //! Perform navigation.
+  //! Handle hot-keys defining new camera orientation (Aspect_VKey_ViewTop and similar keys).
+  //! Default implementation starts an animated transaction from the current to the target camera orientation, when specific action key was pressed.
+  //! This method is expected to be called from rendering thread.
+  Standard_EXPORT virtual void handleViewOrientationKeys (const Handle(AIS_InteractiveContext)& theCtx,
+                                                          const Handle(V3d_View)& theView);
+
+  //! Perform navigation (Aspect_VKey_NavForward and similar keys).
   //! This method is expected to be called from rendering thread.
   Standard_EXPORT virtual AIS_WalkDelta handleNavigationKeys (const Handle(AIS_InteractiveContext)& theCtx,
                                                               const Handle(V3d_View)& theView);
 
-  //! Perform camera actions.
+  //! Perform immediate camera actions (rotate/zoom/pan) on gesture progress.
   //! This method is expected to be called from rendering thread.
   Standard_EXPORT virtual void handleCameraActions (const Handle(AIS_InteractiveContext)& theCtx,
                                                     const Handle(V3d_View)& theView,
@@ -742,6 +798,15 @@ protected: //! @name multi-touch input variables
   Standard_Boolean    myUpdateStartPointRot;      //!< flag indicating that new gravity point should be picked for starting rotation   gesture
   Standard_Boolean    myUpdateStartPointZRot;     //!< flag indicating that new gravity point should be picked for starting Z-rotation gesture
 
+protected: //! @name 3d mouse input variables
+
+  bool                   my3dMouseButtonState[32];//!< cached button state
+  NCollection_Vec3<bool> my3dMouseNoRotate;       //!< ignore  3d mouse rotation axes
+  NCollection_Vec3<bool> my3dMouseToReverse;      //!< reverse 3d mouse rotation axes
+  float                  my3dMouseAccelTrans;     //!< acceleration ratio for translation event
+  float                  my3dMouseAccelRotate;    //!< acceleration ratio for rotation event
+  bool                   my3dMouseIsQuadric;      //!< quadric acceleration
+
 protected: //! @name rotation/panning transient state variables
 
   Handle(AIS_Point)   myAnchorPointPrs1;          //!< anchor point presentation (Graphic3d_ZLayerId_Top)
index dd643a7..a8c7cef 100644 (file)
@@ -149,12 +149,12 @@ AIS_ViewCube::AIS_ViewCube()
   myViewAnimation (new AIS_AnimationCamera ("AIS_ViewCube", Handle(V3d_View)())),
   myStartState(new Graphic3d_Camera()),
   myEndState  (new Graphic3d_Camera()),
-  myDuration (0.5),
   myToAutoStartAnim (true),
   myIsFixedAnimation (true),
   myToFitSelected (true),
   myToResetCameraUp (false)
 {
+  myViewAnimation->SetOwnDuration (0.5);
   myInfiniteState = true;
   myIsMutable = true;
   myDrawer->SetZLayer (Graphic3d_ZLayerId_Topmost);
@@ -825,6 +825,24 @@ void AIS_ViewCube::ComputeSelection (const Handle(SelectMgr_Selection)& theSelec
 }
 
 //=======================================================================
+//function : Duration
+//purpose  :
+//=======================================================================
+Standard_Real AIS_ViewCube::Duration() const
+{
+  return myViewAnimation->OwnDuration();
+}
+
+//=======================================================================
+//function : SetDuration
+//purpose  :
+//=======================================================================
+void AIS_ViewCube::SetDuration (Standard_Real theDurationSec)
+{
+  myViewAnimation->SetOwnDuration (theDurationSec);
+}
+
+//=======================================================================
 //function : HasAnimation
 //purpose  :
 //=======================================================================
@@ -913,7 +931,6 @@ void AIS_ViewCube::StartAnimation (const Handle(AIS_ViewCubeOwner)& theOwner)
   myViewAnimation->SetView (aView);
   myViewAnimation->SetCameraStart (myStartState);
   myViewAnimation->SetCameraEnd   (myEndState);
-  myViewAnimation->SetOwnDuration (myDuration);
   myViewAnimation->StartTimer (0.0, 1.0, true, false);
 }
 
@@ -924,7 +941,7 @@ void AIS_ViewCube::StartAnimation (const Handle(AIS_ViewCubeOwner)& theOwner)
 Standard_Boolean AIS_ViewCube::updateAnimation()
 {
   const Standard_Real aPts = myViewAnimation->UpdateTimer();
-  if (aPts >= myDuration)
+  if (aPts >= myViewAnimation->OwnDuration())
   {
     myViewAnimation->Stop();
     onAnimationFinished();
index 757f645..4667d19 100644 (file)
@@ -465,11 +465,11 @@ public:
 public: //! @name animation methods
 
   //! Return duration of animation in seconds; 0.5 sec by default
-  Standard_Real Duration() const { return myDuration; }
+  Standard_EXPORT Standard_Real Duration() const;
 
   //! Set duration of animation.
   //! @param theValue [in] input value of duration in seconds
-  void SetDuration (Standard_Real theValue) { myDuration = theValue; }
+  Standard_EXPORT void SetDuration (Standard_Real theValue);
 
   //! Return TRUE if new camera Up direction should be always set to default value for a new camera Direction; FALSE by default.
   //! When this flag is FALSE, the new camera Up will be set as current Up orthogonalized to the new camera Direction,
@@ -675,7 +675,6 @@ protected: //! @name Animation options
   Handle(AIS_AnimationCamera)   myViewAnimation;     //!< Camera animation object
   Handle(Graphic3d_Camera)      myStartState;        //!< Start state of view camera
   Handle(Graphic3d_Camera)      myEndState;          //!< End state of view camera
-  Standard_Real                 myDuration;          //!< Duration of animation. By default it is half a second
   Standard_Boolean              myToAutoStartAnim;   //!< start animation automatically on click
   Standard_Boolean              myIsFixedAnimation;  //!< fixed-loop animation
   Standard_Boolean              myToFitSelected;     //!< fit selected or fit entire scene
index ff0def5..4b55efa 100644 (file)
@@ -139,6 +139,20 @@ enum Aspect_VKeyBasic
   Aspect_VKey_BrowserFavorites,
   Aspect_VKey_BrowserHome,
 
+  // 3d view keys
+  Aspect_VKey_ViewTop,
+  Aspect_VKey_ViewBottom,
+  Aspect_VKey_ViewLeft,
+  Aspect_VKey_ViewRight,
+  Aspect_VKey_ViewFront,
+  Aspect_VKey_ViewBack,
+  Aspect_VKey_ViewAxoLeftProj,
+  Aspect_VKey_ViewAxoRightProj,
+  Aspect_VKey_ViewFitAll,
+  Aspect_VKey_ViewRoll90CW,
+  Aspect_VKey_ViewRoll90CCW,
+  Aspect_VKey_ViewSwitchRotate,
+
   // modifier keys, @sa Aspect_VKey_ModifiersLower and Aspect_VKey_ModifiersUpper below
   Aspect_VKey_Shift,
   Aspect_VKey_Control,
index bc7b312..7872792 100644 (file)
@@ -65,6 +65,11 @@ public:
     return myKeys[theKey].Status == KeyStatus_Pressed;
   }
 
+  //! Return mutex for thread-safe updates.
+  //! All operations in class implicitly locks this mutex,
+  //! so this method could be used only for batch processing of keys.
+  Standard_Mutex& Mutex() { return myLock; }
+
 public:
 
   //! Reset the key state into unpressed state.
index 2f732e8..a1e5837 100644 (file)
@@ -35,6 +35,7 @@ IMPLEMENT_STANDARD_RTTIEXT(ViewerTest_EventManager,Standard_Transient)
 const Handle(AIS_AnimationCamera)& ViewerTest_EventManager::GlobalViewAnimation()
 {
   static Handle(AIS_AnimationCamera) THE_CAMERA_ANIM = new AIS_AnimationCamera ("ViewerTest_EventManager_ViewAnimation", Handle(V3d_View)());
+  THE_CAMERA_ANIM->SetOwnDuration (0.5);
   return THE_CAMERA_ANIM;
 }
 
index 498ec55..b6cf50d 100644 (file)
@@ -55,6 +55,7 @@
 #include <Message_ProgressSentry.hxx>
 #include <NCollection_DataMap.hxx>
 #include <NCollection_List.hxx>
+#include <NCollection_LocalArray.hxx>
 #include <NCollection_Vector.hxx>
 #include <OSD.hxx>
 #include <OSD_Timer.hxx>
@@ -89,6 +90,7 @@
 #if defined(_WIN32)
   #include <WNT_WClass.hxx>
   #include <WNT_Window.hxx>
+  #include <WNT_HIDSpaceMouse.hxx>
 #elif defined(__APPLE__) && !defined(MACOSX_USE_GLX)
   #include <Cocoa_Window.hxx>
 #else
@@ -1831,6 +1833,7 @@ TCollection_AsciiString ViewerTest::ViewerInit (const Standard_Integer thePxLeft
                                     aPxLeft, aPxTop,
                                     aPxWidth, aPxHeight,
                                     Quantity_NOC_BLACK);
+  VT_GetWindow()->RegisterRawInputDevices (WNT_Window::RawInputMask_SpaceMouse);
 #elif defined(__APPLE__) && !defined(MACOSX_USE_GLX)
   VT_GetWindow() = new Cocoa_Window (aTitle.ToCString(),
                                      aPxLeft, aPxTop,
@@ -3258,6 +3261,40 @@ static LRESULT WINAPI ViewerWindowProc (HWND theWinHandle,
       ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), aView, true);
       break;
     }
+    case WM_INPUT:
+    {
+      UINT aSize = 0;
+      ::GetRawInputData ((HRAWINPUT )lParam, RID_INPUT, NULL, &aSize, sizeof(RAWINPUTHEADER));
+      NCollection_LocalArray<BYTE> aRawData (aSize);
+      if (aSize == 0 || ::GetRawInputData ((HRAWINPUT )lParam, RID_INPUT, aRawData, &aSize, sizeof(RAWINPUTHEADER)) != aSize)
+      {
+        break;
+      }
+
+      const RAWINPUT* aRawInput = (RAWINPUT* )(BYTE* )aRawData;
+      if (aRawInput->header.dwType != RIM_TYPEHID)
+      {
+        break;
+      }
+
+      RID_DEVICE_INFO aDevInfo;
+      aDevInfo.cbSize = sizeof(RID_DEVICE_INFO);
+      UINT aDevInfoSize = sizeof(RID_DEVICE_INFO);
+      if (::GetRawInputDeviceInfoW (aRawInput->header.hDevice, RIDI_DEVICEINFO, &aDevInfo, &aDevInfoSize) != sizeof(RID_DEVICE_INFO)
+        || (aDevInfo.hid.dwVendorId != WNT_HIDSpaceMouse::VENDOR_ID_LOGITECH
+         && aDevInfo.hid.dwVendorId != WNT_HIDSpaceMouse::VENDOR_ID_3DCONNEXION))
+      {
+        break;
+      }
+
+      WNT_HIDSpaceMouse aSpaceData (aDevInfo.hid.dwProductId, aRawInput->data.hid.bRawData, aRawInput->data.hid.dwSizeHid);
+      if (ViewerTest::CurrentEventManager()->Update3dMouse (aSpaceData)
+      && !VT_GetWindow().IsNull())
+      {
+        VT_GetWindow()->InvalidateContent();
+      }
+      break;
+    }
     default:
     {
       return DefWindowProcW (theWinHandle, theMsg, wParam, lParam);
index d604a60..0d1163e 100755 (executable)
@@ -1,5 +1,7 @@
 WNT_ClassDefinitionError.hxx
 WNT_Dword.hxx
+WNT_HIDSpaceMouse.cxx
+WNT_HIDSpaceMouse.hxx
 WNT_OrientationType.hxx
 WNT_WClass.cxx
 WNT_WClass.hxx
diff --git a/src/WNT/WNT_HIDSpaceMouse.cxx b/src/WNT/WNT_HIDSpaceMouse.cxx
new file mode 100644 (file)
index 0000000..74937a1
--- /dev/null
@@ -0,0 +1,320 @@
+// Copyright (c) 2019-2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <WNT_HIDSpaceMouse.hxx>
+
+namespace
+{
+  //! Enumeration of known Space Mouse models.
+  enum SpacePid
+  {
+    // VENDOR_ID_LOGITECH
+    SpacePid_SpaceMouse             = 0xC603,
+    SpacePid_CADMan                 = 0xC605,
+    SpacePid_SpaceMouseClassic      = 0xC606,
+    SpacePid_SpaceBall5000          = 0xC621,
+    SpacePid_SpaceTraveler          = 0xC623,
+    SpacePid_SpacePilot             = 0xC625,
+    SpacePid_SpaceNavigator         = 0xC626, //!< has only 2 "menu" buttons (second one is treated as SpaceVKey_Fit)
+    SpacePid_SpaceExplorer          = 0xC627, //!< 15 buttons
+    SpacePid_NavigatorForNotebooks  = 0xC628, //!< has only 2 "menu" buttons (second one is treated as SpaceVKey_Fit)
+    SpacePid_SpacePilotPro          = 0xC629, //!< 31 buttons
+    SpacePid_SpaceMousePro          = 0xC62B, //!< has only 15 buttons, but codes range from 0 to 26
+    // VENDOR_ID_3DCONNEXION
+    SpacePid_SpaceMouseWireless1    = 0xC62E, //!< [plugged in] has only  2 buttons
+    SpacePid_SpaceMouseWireless2    = 0xC62F, //!< [wireless]   has only  2 buttons
+    SpacePid_SpaceMouseProWireless1 = 0xC631, //!< [plugged in] has only 15 buttons
+    SpacePid_SpaceMouseProWireless2 = 0xC632, //!< [wireless]   has only 15 buttons
+    SpacePid_SpaceMouseEnterprise   = 0xC633, //!< 31 buttons
+    SpacePid_SpaceMouseCompact      = 0xC635
+  };
+
+  //! Enumeration of known keys available on various Space Mouse models.
+  enum SpaceVKey
+  {
+    SpaceVKey_INVALID = 0,
+    SpaceVKey_Menu = 1, SpaceVKey_Fit,
+    SpaceVKey_Top, SpaceVKey_Left, SpaceVKey_Right, SpaceVKey_Front, SpaceVKey_Bottom, SpaceVKey_Back,
+    SpaceVKey_RollCW, SpaceVKey_RollCCW,
+    SpaceVKey_ISO1, SpaceVKey_ISO2,
+    SpaceVKey_1, SpaceVKey_2, SpaceVKey_3, SpaceVKey_4, SpaceVKey_5, SpaceVKey_6, SpaceVKey_7, SpaceVKey_8, SpaceVKey_9, SpaceVKey_10,
+    SpaceVKey_Esc, SpaceVKey_Alt, SpaceVKey_Shift, SpaceVKey_Ctrl,
+    SpaceVKey_Rotate, SpaceVKey_PanZoom, SpaceVKey_Dominant,
+    SpaceVKey_Plus, SpaceVKey_Minus,
+  };
+
+  //! The raw value range on tested device is [-350; 350].
+  enum { THE_RAW_RANGE_350 = 350 };
+
+  //! Convert key state bit into virtual key.
+  static SpaceVKey hidToSpaceKey (unsigned long theProductId,
+                                  unsigned short theKeyBit)
+  {
+    static const SpaceVKey THE_PILOT_KEYS[] =
+    {
+      SpaceVKey_1, SpaceVKey_2, SpaceVKey_3, SpaceVKey_4, SpaceVKey_5, SpaceVKey_6,
+      SpaceVKey_Top, SpaceVKey_Left, SpaceVKey_Right, SpaceVKey_Front,
+      SpaceVKey_Esc, SpaceVKey_Alt, SpaceVKey_Shift, SpaceVKey_Ctrl,
+      SpaceVKey_Fit, SpaceVKey_Menu,
+      SpaceVKey_Plus, SpaceVKey_Minus,
+      SpaceVKey_Dominant, SpaceVKey_Rotate
+    };
+    const int THE_NB_PILOT_KEYS = sizeof(THE_PILOT_KEYS) / sizeof(SpaceVKey);
+
+    static const SpaceVKey THE_EXPLORER_KEYS[] =
+    {
+      SpaceVKey_1, SpaceVKey_2,
+      SpaceVKey_Top, SpaceVKey_Left, SpaceVKey_Right, SpaceVKey_Front,
+      SpaceVKey_Esc, SpaceVKey_Alt,  SpaceVKey_Shift, SpaceVKey_Ctrl,
+      SpaceVKey_Fit, SpaceVKey_Menu,
+      SpaceVKey_Plus, SpaceVKey_Minus,
+      SpaceVKey_Rotate
+    };
+    const int THE_NB_EXPLORER_KEYS = sizeof(THE_EXPLORER_KEYS) / sizeof(SpaceVKey);
+
+    // shared by latest 3Dconnexion hardware
+    static const SpaceVKey THE_SPACEMOUSEPRO_KEYS[] =
+    {
+      SpaceVKey_Menu, SpaceVKey_Fit,
+      SpaceVKey_Top, SpaceVKey_Left, SpaceVKey_Right, SpaceVKey_Front, SpaceVKey_Bottom, SpaceVKey_Back,
+      SpaceVKey_RollCW, SpaceVKey_RollCCW, SpaceVKey_ISO1, SpaceVKey_ISO2,
+      SpaceVKey_1, SpaceVKey_2, SpaceVKey_3, SpaceVKey_4,
+      SpaceVKey_5, SpaceVKey_6, SpaceVKey_7, SpaceVKey_8, SpaceVKey_9, SpaceVKey_10,
+      SpaceVKey_Esc, SpaceVKey_Alt, SpaceVKey_Shift, SpaceVKey_Ctrl,
+      SpaceVKey_Rotate,
+      SpaceVKey_PanZoom, SpaceVKey_Dominant, SpaceVKey_Plus, SpaceVKey_Minus
+    };
+    const int THE_NB_SPACEMOUSEPRO_KEYS = sizeof(THE_SPACEMOUSEPRO_KEYS) / sizeof(SpaceVKey);
+
+    switch (theProductId)
+    {
+      case SpacePid_SpacePilot:
+        return theKeyBit < THE_NB_PILOT_KEYS ? THE_PILOT_KEYS[theKeyBit] : SpaceVKey_INVALID;
+      case SpacePid_SpaceExplorer:
+        return theKeyBit < THE_NB_EXPLORER_KEYS ? THE_EXPLORER_KEYS[theKeyBit] : SpaceVKey_INVALID;
+      case SpacePid_SpaceNavigator:
+      case SpacePid_NavigatorForNotebooks:
+      case SpacePid_SpacePilotPro:
+      case SpacePid_SpaceMousePro:
+      case SpacePid_SpaceMouseWireless1:
+      case SpacePid_SpaceMouseWireless2:
+      case SpacePid_SpaceMouseProWireless1:
+      case SpacePid_SpaceMouseProWireless2:
+      case SpacePid_SpaceMouseEnterprise:
+      case SpacePid_SpaceMouseCompact:
+        return theKeyBit < THE_NB_SPACEMOUSEPRO_KEYS ? THE_SPACEMOUSEPRO_KEYS[theKeyBit] : SpaceVKey_INVALID;
+    }
+    return SpaceVKey_INVALID;
+  }
+
+}
+
+// =======================================================================
+// function : WNT_HIDSpaceMouse
+// purpose  :
+// =======================================================================
+WNT_HIDSpaceMouse::WNT_HIDSpaceMouse (unsigned long theProductId,
+                                      const Standard_Byte* theData,
+                                      Standard_Size theSize)
+: myData (theData),
+  mySize (theSize),
+  myProductId (theProductId),
+  myValueRange (THE_RAW_RANGE_350)
+{
+  //
+}
+
+// =======================================================================
+// function : IsKnownProduct
+// purpose  :
+// =======================================================================
+bool WNT_HIDSpaceMouse::IsKnownProduct (unsigned long theProductId)
+{
+  switch (theProductId)
+  {
+    case SpacePid_SpacePilot:
+    case SpacePid_SpaceExplorer:
+    case SpacePid_SpaceNavigator:
+    case SpacePid_NavigatorForNotebooks:
+    case SpacePid_SpacePilotPro:
+    case SpacePid_SpaceMousePro:
+    case SpacePid_SpaceMouseWireless1:
+    case SpacePid_SpaceMouseWireless2:
+    case SpacePid_SpaceMouseProWireless1:
+    case SpacePid_SpaceMouseProWireless2:
+    case SpacePid_SpaceMouseEnterprise:
+    case SpacePid_SpaceMouseCompact:
+      return true;
+  }
+  return false;
+}
+
+// =======================================================================
+// function : Translation
+// purpose  :
+// =======================================================================
+Graphic3d_Vec3d WNT_HIDSpaceMouse::Translation (bool& theIsIdle,
+                                                bool theIsQuadric) const
+{
+  theIsIdle = true;
+  return myData[0] == SpaceRawInput_Translation
+      && (mySize == 7 || mySize == 13)
+       ? fromRawVec3 (theIsIdle, myData + 1, true, theIsQuadric)
+       : Graphic3d_Vec3d();
+}
+
+// =======================================================================
+// function : Rotation
+// purpose  :
+// =======================================================================
+Graphic3d_Vec3d WNT_HIDSpaceMouse::Rotation (bool& theIsIdle,
+                                             bool theIsQuadric) const
+{
+  theIsIdle = true;
+  if (myData[0] == SpaceRawInput_Rotation && mySize == 7)
+  {
+    return fromRawVec3 (theIsIdle, myData + 1, false, theIsQuadric);
+  }
+  else if (myData[0] == SpaceRawInput_Translation && mySize == 13)
+  {
+    return fromRawVec3 (theIsIdle, myData + 7, false, theIsQuadric);
+  }
+  return Graphic3d_Vec3d();
+}
+
+// =======================================================================
+// function : fromRawVec3
+// purpose  :
+// =======================================================================
+Graphic3d_Vec3d WNT_HIDSpaceMouse::fromRawVec3 (bool& theIsIdle,
+                                                const Standard_Byte* theData,
+                                                bool theIsTrans,
+                                                bool theIsQuadric) const
+{
+  theIsIdle = true;
+  const NCollection_Vec3<int16_t>& aRaw16 = *reinterpret_cast<const NCollection_Vec3<int16_t>*>(theData);
+  Graphic3d_Vec3d aVec (aRaw16.x(), aRaw16.y(), aRaw16.z());
+  if (theIsTrans)
+  {
+    static const int16_t THE_MIN_RAW_TRANS   = 4;
+    static const int16_t THE_MIN_RAW_TRANS_Z = 8;
+    for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
+    {
+      if (aRaw16[aCompIter] > -THE_MIN_RAW_TRANS && aRaw16[aCompIter] < THE_MIN_RAW_TRANS)
+      {
+        aVec[aCompIter] = 0.0;
+      }
+      else
+      {
+        theIsIdle = false;
+      }
+    }
+    if (aRaw16.z() > -THE_MIN_RAW_TRANS_Z && aRaw16.z() < THE_MIN_RAW_TRANS_Z)
+    {
+      aVec.z() = 0.0;
+    }
+  }
+  else
+  {
+    for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
+    {
+      if (aRaw16[aCompIter] != 0)
+      {
+        theIsIdle = false;
+        break;
+      }
+    }
+  }
+
+  // determine raw value range
+  for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
+  {
+    if (aRaw16[aCompIter] > myValueRange
+    || -aRaw16[aCompIter] > myValueRange)
+    {
+      myValueRange = 32767; // SHRT_MAX
+      break;
+    }
+  }
+
+  if (!theIsQuadric)
+  {
+    return aVec / double(myValueRange);
+  }
+
+  for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
+  {
+    aVec[aCompIter] =  aRaw16[aCompIter] > 0
+                    ?  aVec[aCompIter] * aVec[aCompIter]
+                    : -aVec[aCompIter] * aVec[aCompIter];
+  }
+  return aVec / (double(myValueRange) * double(myValueRange));
+}
+
+// =======================================================================
+// function : HidToSpaceKey
+// purpose  :
+// =======================================================================
+Aspect_VKey WNT_HIDSpaceMouse::HidToSpaceKey (unsigned short theKeyBit) const
+{
+  const SpaceVKey aKey = hidToSpaceKey (myProductId, theKeyBit);
+  switch (aKey)
+  {
+    case SpaceVKey_1:
+    case SpaceVKey_2:
+    case SpaceVKey_3:
+    case SpaceVKey_4:
+    case SpaceVKey_5:
+    case SpaceVKey_6:
+    case SpaceVKey_7:
+    case SpaceVKey_8:
+    case SpaceVKey_9:
+    case SpaceVKey_10:
+      return (int(aKey) - int(SpaceVKey_1)) + Aspect_VKey_1;
+    case SpaceVKey_Esc:
+      return Aspect_VKey_Escape;
+    case SpaceVKey_Shift:
+      return Aspect_VKey_Shift;
+    case SpaceVKey_Alt:
+      return Aspect_VKey_Alt;
+    case SpaceVKey_Ctrl:
+      return Aspect_VKey_Control;
+    case SpaceVKey_Top:
+      return Aspect_VKey_ViewTop;
+    case SpaceVKey_Bottom:
+      return Aspect_VKey_ViewBottom;
+    case SpaceVKey_Left:
+      return Aspect_VKey_ViewLeft;
+    case SpaceVKey_Right:
+      return Aspect_VKey_ViewRight;
+    case SpaceVKey_Front:
+      return Aspect_VKey_ViewFront;
+    case SpaceVKey_Back:
+      return Aspect_VKey_ViewBack;
+    case SpaceVKey_ISO1:
+      return Aspect_VKey_ViewAxoLeftProj;
+    case SpaceVKey_ISO2:
+      return Aspect_VKey_ViewAxoRightProj;
+    case SpaceVKey_Fit:
+      return Aspect_VKey_ViewFitAll;
+    case SpaceVKey_RollCW:
+      return Aspect_VKey_ViewRoll90CW;
+    case SpaceVKey_RollCCW:
+      return Aspect_VKey_ViewRoll90CCW;
+    case SpaceVKey_Rotate:
+      return Aspect_VKey_ViewSwitchRotate;
+    default:
+      break;
+  }
+  return Aspect_VKey_UNKNOWN;
+}
diff --git a/src/WNT/WNT_HIDSpaceMouse.hxx b/src/WNT/WNT_HIDSpaceMouse.hxx
new file mode 100644 (file)
index 0000000..ea6a8d8
--- /dev/null
@@ -0,0 +1,167 @@
+// Copyright (c) 2019-2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _WNT_HIDSpaceMouse_Header
+#define _WNT_HIDSpaceMouse_Header
+
+#include <Aspect_VKey.hxx>
+#include <Graphic3d_Vec.hxx>
+
+//! Wrapper over Space Mouse data chunk within WM_INPUT event (known also as Raw Input in WinAPI).
+//! This class predefines specific list of supported devices, which does not depend on 3rdparty library provided by mouse vendor.
+//! Supported input chunks:
+//! - Rotation (3 directions);
+//! - Translation (3 directions);
+//! - Pressed buttons.
+//!
+//! To use the class, register Raw Input device:
+//! @code
+//!  Handle(WNT_Window) theWindow;
+//!  RAWINPUTDEVICE aRawInDevList[1];
+//!  RAWINPUTDEVICE& aRawSpace = aRawInDevList[0];
+//!  aRawSpace.usUsagePage = HID_USAGE_PAGE_GENERIC;
+//!  aRawSpace.usUsage     = HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER;
+//!  aRawSpace.dwFlags     = 0; // RIDEV_DEVNOTIFY
+//!  aRawSpace.hwndTarget  = (HWND )theWindow->NativeHandle();
+//!  if (!::RegisterRawInputDevices (aRawInDevList, 1, sizeof(aRawInDevList[0]))) { Error; }
+//! @endcode
+//!
+//! Then handle WM_INPUT events within window message loop.
+//! @code
+//!  AIS_ViewController theViewCtrl;
+//!  case WM_INPUT:
+//!  {
+//!    UINT aSize = 0;
+//!    ::GetRawInputData ((HRAWINPUT )theLParam, RID_INPUT, NULL, &aSize, sizeof(RAWINPUTHEADER));
+//!    NCollection_LocalArray<BYTE> aRawData (aSize); // receive Raw Input for any device and process known devices
+//!    if (aSize == 0 || ::GetRawInputData ((HRAWINPUT )theLParam, RID_INPUT, aRawData, &aSize, sizeof(RAWINPUTHEADER)) != aSize)
+//!    {
+//!      break;
+//!    }
+//!    const RAWINPUT* aRawInput = (RAWINPUT* )(BYTE* )aRawData;
+//!    if (aRawInput->header.dwType != RIM_TYPEHID)
+//!    {
+//!      break;
+//!    }
+//!
+//!    RID_DEVICE_INFO aDevInfo; aDevInfo.cbSize = sizeof(RID_DEVICE_INFO);
+//!    UINT aDevInfoSize = sizeof(RID_DEVICE_INFO);
+//!    if (::GetRawInputDeviceInfoW (aRawInput->header.hDevice, RIDI_DEVICEINFO, &aDevInfo, &aDevInfoSize) != sizeof(RID_DEVICE_INFO)
+//!     || (aDevInfo.hid.dwVendorId != WNT_HIDSpaceMouse::VENDOR_ID_LOGITECH
+//!      && aDevInfo.hid.dwVendorId != WNT_HIDSpaceMouse::VENDOR_ID_3DCONNEXION))
+//!    {
+//!      break;
+//!    }
+//!
+//!    Aspect_VKeySet& aKeys = theViewCtrl.ChangeKeys();
+//!    const double aTimeStamp = theViewCtrl.EventTime();
+//!    WNT_HIDSpaceMouse aSpaceData (aDevInfo.hid.dwProductId, aRawInput->data.hid.bRawData, aRawInput->data.hid.dwSizeHid);
+//!    if (aSpaceData.IsTranslation())
+//!    {
+//!      // process translation input
+//!      bool isIdle = true, isQuadric = true;
+//!      const Graphic3d_Vec3d aTrans = aSpaceData.Translation (isIdle, isQuadric);
+//!      aKeys.KeyFromAxis (Aspect_VKey_NavSlideLeft, Aspect_VKey_NavSlideRight, aTimeStamp, aTrans.x());
+//!      aKeys.KeyFromAxis (Aspect_VKey_NavForward,   Aspect_VKey_NavBackward,   aTimeStamp, aTrans.y());
+//!      aKeys.KeyFromAxis (Aspect_VKey_NavSlideUp,   Aspect_VKey_NavSlideDown,  aTimeStamp, aTrans.z());
+//!    }
+//!    if (aSpaceData.IsRotation()) {} // process rotation input
+//!    if (aSpaceData.IsKeyState()) {} // process keys input
+//!    break;
+//!  }
+//! @endcode
+class WNT_HIDSpaceMouse
+{
+public:
+  //! Vendor HID identifier.
+  enum { VENDOR_ID_LOGITECH = 0x46D, VENDOR_ID_3DCONNEXION = 0x256F };
+
+  //! Return if product id is known by this class.
+  Standard_EXPORT static bool IsKnownProduct (unsigned long theProductId);
+
+public:
+  //! Main constructor.
+  Standard_EXPORT WNT_HIDSpaceMouse (unsigned long theProductId,
+                                     const Standard_Byte* theData,
+                                     Standard_Size theSize);
+
+  //! Return the raw value range.
+  int16_t RawValueRange() const { return myValueRange; }
+
+  //! Set the raw value range.
+  void SetRawValueRange (int16_t theRange) { myValueRange = theRange > myValueRange ? theRange : myValueRange; }
+
+  //! Return TRUE if data chunk defines new translation values.
+  bool IsTranslation() const
+  {
+    return myData[0] == SpaceRawInput_Translation
+        && (mySize == 7 || mySize == 13);
+  }
+
+  //! Return new translation values.
+  //! @param theIsIdle [out] flag indicating idle state (no translation)
+  //! @param theIsQuadric [in] flag to apply non-linear scale factor
+  //! @return vector of 3 elements defining translation values within [-1..1] range, 0 meaning idle,
+  //!         .x defining left/right slide, .y defining forward/backward and .z defining up/down slide.
+  Standard_EXPORT Graphic3d_Vec3d Translation (bool& theIsIdle,
+                                               bool theIsQuadric) const;
+
+  //! Return TRUE if data chunk defines new rotation values.
+  bool IsRotation() const
+  {
+    return (myData[0] == SpaceRawInput_Rotation    && mySize == 7)
+        || (myData[0] == SpaceRawInput_Translation && mySize == 13);
+  }
+
+  //! Return new rotation values.
+  //! @param theIsIdle [out] flag indicating idle state (no rotation)
+  //! @param theIsQuadric [in] flag to apply non-linear scale factor
+  //! @return vector of 3 elements defining rotation values within [-1..1] range, 0 meaning idle,
+  //!         .x defining tilt, .y defining roll and .z defining spin.
+  Standard_EXPORT Graphic3d_Vec3d Rotation (bool& theIsIdle,
+                                            bool theIsQuadric) const;
+
+  //! Return TRUE for key state data chunk.
+  bool IsKeyState() const { return myData[0] == SpaceRawInput_KeyState; }
+
+  //! Return new keystate.
+  uint32_t KeyState() const { return *reinterpret_cast<const uint32_t*>(myData + 1); }
+
+  //! Convert key state bit into virtual key.
+  Standard_EXPORT Aspect_VKey HidToSpaceKey (unsigned short theKeyBit) const;
+
+private:
+
+  //! Translate raw data chunk of 3 int16 values into normalized vec3.
+  //! The values are considered within the range [-350; 350], with 0 as neutral state.
+  Graphic3d_Vec3d fromRawVec3 (bool& theIsIdle,
+                               const Standard_Byte* theData,
+                               bool theIsTrans,
+                               bool theIsQuadric) const;
+
+  //! Data chunk type.
+  enum
+  {
+    SpaceRawInput_Translation = 0x01, //!< translation data chunk
+    SpaceRawInput_Rotation    = 0x02, //!< rotation    data chunk
+    SpaceRawInput_KeyState    = 0x03, //!< keystate    data chunk
+  };
+
+private:
+  const Standard_Byte* myData;       //!< RAW data chunk
+  Standard_Size        mySize;       //!< size of RAW data chunk
+  unsigned long        myProductId;  //!< product id
+  mutable int16_t      myValueRange; //!< RAW value range
+};
+
+#endif // _WNT_HIDSpaceMouse_Header
index 6051a4c..6408620 100644 (file)
@@ -24,6 +24,7 @@
 #include <Aspect_Convert.hxx>
 #include <Aspect_WindowDefinitionError.hxx>
 #include <Aspect_WindowError.hxx>
+#include <Message.hxx>
 #include <Standard_Type.hxx>
 #include <TCollection_ExtendedString.hxx>
 #include <WNT_WClass.hxx>
@@ -643,4 +644,59 @@ Aspect_VKeyMouse WNT_Window::MouseButtonsAsync()
   return aButtons;
 }
 
+// =======================================================================
+// function : RegisterRawInputDevices
+// purpose  :
+// =======================================================================
+int WNT_Window::RegisterRawInputDevices (unsigned int theRawDeviceMask)
+{
+  if (IsVirtual()
+   || myHWindow == NULL)
+  {
+    return 0;
+  }
+
+  // hidusage.h
+  enum HidUsagePage { THE_HID_USAGE_PAGE_GENERIC = 0x01 }; // HID_USAGE_PAGE_GENERIC
+  enum HidUsage
+  {
+    THE_HID_USAGE_GENERIC_MOUSE                 = 0x02, // HID_USAGE_GENERIC_MOUSE
+    THE_HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER = 0x08, // HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER
+  };
+
+  int aNbDevices = 0;
+  RAWINPUTDEVICE aRawInDevList[2];
+  if ((theRawDeviceMask & RawInputMask_Mouse) != 0)
+  {
+    // mouse
+    RAWINPUTDEVICE& aRawMouse = aRawInDevList[aNbDevices++];
+    aRawMouse.usUsagePage = THE_HID_USAGE_PAGE_GENERIC;
+    aRawMouse.usUsage     = THE_HID_USAGE_GENERIC_MOUSE;
+    aRawMouse.dwFlags     = RIDEV_INPUTSINK;
+    aRawMouse.hwndTarget  = (HWND )myHWindow;
+  }
+  if ((theRawDeviceMask & RawInputMask_SpaceMouse) != 0)
+  {
+    // space mouse
+    RAWINPUTDEVICE& aRawSpace = aRawInDevList[aNbDevices++];
+    aRawSpace.usUsagePage = THE_HID_USAGE_PAGE_GENERIC;
+    aRawSpace.usUsage     = THE_HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER;
+    aRawSpace.dwFlags     = 0; // RIDEV_DEVNOTIFY
+    aRawSpace.hwndTarget  = (HWND )myHWindow;
+  }
+
+  for (int aTryIter = aNbDevices; aTryIter > 0; --aTryIter)
+  {
+    if (::RegisterRawInputDevices (aRawInDevList, aTryIter, sizeof(aRawInDevList[0])))
+    {
+      return aTryIter;
+    }
+
+    Message::SendTrace (aRawInDevList[aTryIter - 1].usUsage == THE_HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER
+                      ? "Warning: RegisterRawInputDevices() failed to register RAW multi-axis controller input"
+                      : "Warning: RegisterRawInputDevices() failed to register RAW mouse input");
+  }
+  return 0;
+}
+
 #endif // _WIN32
index 4a60acb..c056a5e 100644 (file)
@@ -128,6 +128,18 @@ public:
   //! Method can be called from non-window thread, and system will also automatically aggregate multiple events into single one.
   Standard_EXPORT virtual void InvalidateContent (const Handle(Aspect_DisplayConnection)& theDisp = NULL) Standard_OVERRIDE;
 
+  //! Raw input flags.
+  enum RawInputMask
+  {
+    RawInputMask_Mouse      = 0x01, //!< HID_USAGE_GENERIC_MOUSE
+    RawInputMask_SpaceMouse = 0x02, //!< HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER
+  };
+
+  //! RegisterRawInputDevices() wrapper.
+  //! @param theRawDeviceMask [in] mask of RawInputMask flags
+  //! @return number of actually registered device types
+  Standard_EXPORT int RegisterRawInputDevices (unsigned int theRawDeviceMask);
+
   DEFINE_STANDARD_RTTIEXT(WNT_Window,Aspect_Window)
 
 protected: