From 20ffe1f866cef1a60921e6a912c4d57787fa2a3f Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 6 Jul 2012 13:02:09 +0200 Subject: [PATCH] 0000787: Patch for Space navigator support on windows --- CMakeLists.txt | 5 + .../GuiApplicationNativeEventAwareWin32.cpp | 718 ++++++++++++++++++ src/Gui/3Dconnexion/I3dMouseParams.h | 91 +++ src/Gui/3Dconnexion/MouseParameters.cpp | 95 +++ src/Gui/3Dconnexion/MouseParameters.h | 56 ++ src/Gui/CMakeLists.txt | 15 + src/Gui/GuiApplicationNativeEventAware.cpp | 36 +- src/Gui/GuiApplicationNativeEventAware.h | 76 +- src/Gui/Splashscreen.cpp | 51 ++ src/Tools/makedist.py | 1 + 10 files changed, 1124 insertions(+), 20 deletions(-) create mode 100644 src/Gui/3Dconnexion/GuiApplicationNativeEventAwareWin32.cpp create mode 100644 src/Gui/3Dconnexion/I3dMouseParams.h create mode 100644 src/Gui/3Dconnexion/MouseParameters.cpp create mode 100644 src/Gui/3Dconnexion/MouseParameters.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2864f687b..671b30cfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,6 +104,11 @@ OPTION(FREECAD_BUILD_TEMPLATE "Build the FreeCAD template module which is only f OPTION(FREECAD_BUILD_DEBIAN "Prepare for a build of a Debian package" OFF) OPTION(FREECAD_USE_EXTERNAL_ZIPIOS "Use system installed zipios++ instead of the bundled." OFF) OPTION(FREECAD_USE_EXTERNAL_PIVY "Use system installed python-pivy instead of the bundled." OFF) +if(MSVC) +OPTION(FREECAD_USE_3DCONNEXION "Use the 3D connexion SDK to support 3d mouse." ON) +else(MSVC) +set(FREECAD_USE_3DCONNEXION OFF) +endif(MSVC) # if this is set override some options if (FREECAD_BUILD_DEBIAN) diff --git a/src/Gui/3Dconnexion/GuiApplicationNativeEventAwareWin32.cpp b/src/Gui/3Dconnexion/GuiApplicationNativeEventAwareWin32.cpp new file mode 100644 index 000000000..425a7af7a --- /dev/null +++ b/src/Gui/3Dconnexion/GuiApplicationNativeEventAwareWin32.cpp @@ -0,0 +1,718 @@ + +/* +Development tools and related technology provided under license from 3Dconnexion. +(c) 1992 - 2012 3Dconnexion. All rights reserved +*/ + +/* +Support for Qt added by David Dibben at +http://www.codegardening.com/2011/02/using-3dconnexion-mouse-with-qt.html +*/ + +/* +See also: +http://www.3dconnexion.com/forum/viewtopic.php?f=19&t=4968&sid=72c018bdcf0e6edc99a6effb5c0c48d9 +*/ + +#include "PreCompiled.h" + +#include +#include +#include +#include +#include +#include "GuiApplicationNativeEventAware.h" +#include "SpaceballEvent.h" + +// Windows dependencies, enumerators and global variables +#ifdef _USE_3DCONNEXION_SDK + +#include +#include +#include + +#define LOGITECH_VENDOR_ID 0x46d +#define _CONSTANT_INPUT_PERIOD 0 + +#ifndef RIDEV_DEVNOTIFY +#define RIDEV_DEVNOTIFY 0x00002000 +#endif + +#define _TRACE_WM_INPUT_PERIOD 0 +#define _TRACE_RI_TYPE 0 +#define _TRACE_RIDI_DEVICENAME 0 +#define _TRACE_RIDI_DEVICEINFO 0 +#define _TRACE_RI_RAWDATA 0 +#define _TRACE_3DINPUT_PERIOD 0 + +#ifdef _WIN64 +typedef unsigned __int64 QWORD; +#endif // _WIN64 + +static const int kTimeToLive = 5; + +enum e3dconnexion_pid { + eSpacePilot = 0xc625, + eSpaceNavigator = 0xc626, + eSpaceExplorer = 0xc627, + eSpaceNavigatorForNotebooks = 0xc628, + eSpacePilotPRO = 0xc629 +}; + +enum e3dmouse_virtual_key +{ + V3DK_INVALID=0 + , V3DK_MENU=1, V3DK_FIT + , V3DK_TOP, V3DK_LEFT, V3DK_RIGHT, V3DK_FRONT, V3DK_BOTTOM, V3DK_BACK + , V3DK_CW, V3DK_CCW + , V3DK_ISO1, V3DK_ISO2 + , V3DK_1, V3DK_2, V3DK_3, V3DK_4, V3DK_5, V3DK_6, V3DK_7, V3DK_8, V3DK_9, V3DK_10 + , V3DK_ESC, V3DK_ALT, V3DK_SHIFT, V3DK_CTRL + , V3DK_ROTATE, V3DK_PANZOOM, V3DK_DOMINANT + , V3DK_PLUS, V3DK_MINUS +}; + +struct tag_VirtualKeys +{ + e3dconnexion_pid pid; + size_t nKeys; + e3dmouse_virtual_key *vkeys; +}; + +static const e3dmouse_virtual_key SpaceExplorerKeys [] = +{ + V3DK_INVALID // there is no button 0 + , V3DK_1, V3DK_2 + , V3DK_TOP, V3DK_LEFT, V3DK_RIGHT, V3DK_FRONT + , V3DK_ESC, V3DK_ALT, V3DK_SHIFT, V3DK_CTRL + , V3DK_FIT, V3DK_MENU + , V3DK_PLUS, V3DK_MINUS + , V3DK_ROTATE +}; + +static const e3dmouse_virtual_key SpacePilotKeys [] = +{ + V3DK_INVALID + , V3DK_1, V3DK_2, V3DK_3, V3DK_4, V3DK_5, V3DK_6 + , V3DK_TOP, V3DK_LEFT, V3DK_RIGHT, V3DK_FRONT + , V3DK_ESC, V3DK_ALT, V3DK_SHIFT, V3DK_CTRL + , V3DK_FIT, V3DK_MENU + , V3DK_PLUS, V3DK_MINUS + , V3DK_DOMINANT, V3DK_ROTATE +}; + +static const struct tag_VirtualKeys _3dmouseVirtualKeys[]= +{ + eSpacePilot + , sizeof(SpacePilotKeys)/sizeof(SpacePilotKeys[0]) + , const_cast(SpacePilotKeys), + eSpaceExplorer + , sizeof(SpaceExplorerKeys)/sizeof(SpaceExplorerKeys[0]) + , const_cast(SpaceExplorerKeys) +}; + + +// Methods for windows events + +/*! + Converts a hid device keycode (button identifier) of a pre-2009 3Dconnexion USB device to the standard 3d mouse virtual key definition. + + \a pid USB Product ID (PID) of 3D mouse device + \a hidKeyCode Hid keycode as retrieved from a Raw Input packet + + \return The standard 3d mouse virtual key (button identifier) or zero if an error occurs. + + Converts a hid device keycode (button identifier) of a pre-2009 3Dconnexion USB device + to the standard 3d mouse virtual key definition. +*/ + +unsigned short HidToVirtualKey(unsigned long pid, unsigned short hidKeyCode) +{ + unsigned short virtualkey=hidKeyCode; + for (size_t i=0; imessage == WM_INPUT) { + HRAWINPUT hRawInput = reinterpret_cast(message->lParam); + gMouseInput->OnRawInput(RIM_INPUT,hRawInput); + if (result != 0) { + result = 0; + } + return true; + } + + return false; +} + + +/*! + Access the mouse parameters structure +*/ +I3dMouseParam& Gui::GUIApplicationNativeEventAware::MouseParams() +{ + return f3dMouseParams; +} + +/*! + Access the mouse parameters structure +*/ +const I3dMouseParam& Gui::GUIApplicationNativeEventAware::MouseParams() const +{ + return f3dMouseParams; +} + +/*! + Called with the processed motion data when a 3D mouse event is received + + The default implementation emits a Move3d signal with the motion data +*/ +void Gui::GUIApplicationNativeEventAware::Move3d(HANDLE device, std::vector& motionData) +{ + Q_UNUSED(device); + + QWidget *currentWidget = this->focusWidget(); + if (!currentWidget) + currentWidget = mainWindow; + + int x, y, z, rx, ry, rz; + x = ceil(motionData[0])*(-1); + y = ceil(motionData[1]); + z = ceil(motionData[2]); + rx = ceil(motionData[3])*(-1); + ry = ceil(motionData[4]); + rz = ceil(motionData[5]); + + Spaceball::MotionEvent *motionEvent = new Spaceball::MotionEvent(); + motionEvent->setTranslations(x, y, z); + motionEvent->setRotations(rx, ry, rz); + this->postEvent(currentWidget, motionEvent); +} + +/*! + Called when a 3D mouse key is pressed + + The default implementation emits a On3dmouseKeyDown signal with the key code. +*/ +void Gui::GUIApplicationNativeEventAware::On3dmouseKeyDown(HANDLE device, int virtualKeyCode) +{ + Q_UNUSED(device); + + QWidget *currentWidget = this->focusWidget(); + if (!currentWidget) + currentWidget = mainWindow; + + Spaceball::ButtonEvent *buttonEvent = new Spaceball::ButtonEvent(); + buttonEvent->setButtonNumber(virtualKeyCode - 1); + buttonEvent->setButtonStatus(Spaceball::BUTTON_PRESSED); + this->postEvent(currentWidget, buttonEvent); +} + +/*! + Called when a 3D mouse key is released + + The default implementation emits a On3dmouseKeyUp signal with the key code. +*/ +void Gui::GUIApplicationNativeEventAware::On3dmouseKeyUp(HANDLE device, int virtualKeyCode) +{ + Q_UNUSED(device); + + QWidget *currentWidget = this->focusWidget(); + if (!currentWidget) + currentWidget = mainWindow; + + Spaceball::ButtonEvent *buttonEvent = new Spaceball::ButtonEvent(); + buttonEvent->setButtonNumber(virtualKeyCode - 1); + buttonEvent->setButtonStatus(Spaceball::BUTTON_RELEASED); + this->postEvent(currentWidget, buttonEvent); +} + +/*! + Get an initialized array of PRAWINPUTDEVICE for the 3D devices + + pNumDevices returns the number of devices to register. Currently this is always 1. + */ +static PRAWINPUTDEVICE GetDevicesToRegister(unsigned int* pNumDevices) +{ + // Array of raw input devices to register + static RAWINPUTDEVICE sRawInputDevices[] = { + {0x01, 0x08, 0x00, 0x00} // Usage Page = 0x01 Generic Desktop Page, Usage Id= 0x08 Multi-axis Controller + }; + + if (pNumDevices) { + *pNumDevices = sizeof(sRawInputDevices) / sizeof(sRawInputDevices[0]); + } + + return sRawInputDevices; +} + +/*! + Detect the 3D mouse +*/ +bool Gui::GUIApplicationNativeEventAware::Is3dmouseAttached() +{ + unsigned int numDevicesOfInterest = 0; + PRAWINPUTDEVICE devicesToRegister = GetDevicesToRegister(&numDevicesOfInterest); + + unsigned int nDevices = 0; + + if (::GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0) { + return false; + } + + if (nDevices == 0) return false; + + std::vector rawInputDeviceList(nDevices); + if (::GetRawInputDeviceList(&rawInputDeviceList[0], &nDevices, sizeof(RAWINPUTDEVICELIST)) == static_cast(-1)) { + return false; + } + + for (unsigned int i = 0; i < nDevices; ++i) { + RID_DEVICE_INFO rdi = {sizeof(rdi)}; + unsigned int cbSize = sizeof(rdi); + + if (GetRawInputDeviceInfo(rawInputDeviceList[i].hDevice, RIDI_DEVICEINFO, &rdi, &cbSize) > 0) { + //skip non HID and non logitec (3DConnexion) devices + if (rdi.dwType != RIM_TYPEHID || rdi.hid.dwVendorId != LOGITECH_VENDOR_ID) { + continue; + } + + //check if devices matches Multi-axis Controller + for (unsigned int j = 0; j < numDevicesOfInterest; ++j) { + if (devicesToRegister[j].usUsage == rdi.hid.usUsage + && devicesToRegister[j].usUsagePage == rdi.hid.usUsagePage) { + return true; + } + } + } + } + return false; +} + + + +/*! + Initialize the window to recieve raw-input messages + + This needs to be called initially so that Windows will send the messages from the 3D mouse to the window. +*/ +bool Gui::GUIApplicationNativeEventAware::InitializeRawInput(HWND hwndTarget) +{ + fWindow = hwndTarget; + + // Simply fail if there is no window + if (!hwndTarget) return false; + + unsigned int numDevices = 0; + PRAWINPUTDEVICE devicesToRegister = GetDevicesToRegister(&numDevices); + + if (numDevices == 0) return false; + + // Get OS version. + OSVERSIONINFO osvi = {sizeof(OSVERSIONINFO),0}; + ::GetVersionEx(&osvi); + + unsigned int cbSize = sizeof (devicesToRegister[0]); + for (size_t i = 0; i < numDevices; i++) { + // Set the target window to use + //devicesToRegister[i].hwndTarget = hwndTarget; + + // If Vista or newer, enable receiving the WM_INPUT_DEVICE_CHANGE message. + if (osvi.dwMajorVersion >= 6) { + devicesToRegister[i].dwFlags |= RIDEV_DEVNOTIFY; + } + } + return (::RegisterRawInputDevices(devicesToRegister, numDevices, cbSize) != FALSE); +} + + +/*! + Get the raw input data from Windows + + Includes workaround for incorrect alignment of the RAWINPUT structure on x64 os + when running as Wow64 (copied directly from 3DConnexion code) +*/ + +UINT Gui::GUIApplicationNativeEventAware::GetRawInputBuffer(PRAWINPUT pData, PUINT pcbSize, UINT cbSizeHeader) +{ +#ifdef _WIN64 + return ::GetRawInputBuffer(pData, pcbSize, cbSizeHeader); +#else + BOOL bIsWow64 = FALSE; + ::IsWow64Process(GetCurrentProcess(), &bIsWow64); + if (!bIsWow64 || pData==NULL) { + return ::GetRawInputBuffer(pData, pcbSize, cbSizeHeader); + } else { + HWND hwndTarget = fWindow; //fParent->winId(); + + size_t cbDataSize=0; + UINT nCount=0; + PRAWINPUT pri = pData; + + MSG msg; + while (PeekMessage(&msg, hwndTarget, WM_INPUT, WM_INPUT, PM_NOREMOVE)) { + HRAWINPUT hRawInput = reinterpret_cast(msg.lParam); + size_t cbSize = *pcbSize - cbDataSize; + if (::GetRawInputData(hRawInput, RID_INPUT, pri, &cbSize, cbSizeHeader) == static_cast(-1)) { + if (nCount==0) { + return static_cast(-1); + } else { + break; + } + } + ++nCount; + + // Remove the message for the data just read + PeekMessage(&msg, hwndTarget, WM_INPUT, WM_INPUT, PM_REMOVE); + + pri = NEXTRAWINPUTBLOCK(pri); + cbDataSize = reinterpret_cast(pri) - reinterpret_cast(pData); + if (cbDataSize >= *pcbSize) { + cbDataSize = *pcbSize; + break; + } + } + return nCount; + } +#endif +} + +/*! + Process the raw input device data + + On3dmouseInput() does all the preprocessing of the rawinput device data before + finally calling the Move3d method. +*/ + +void Gui::GUIApplicationNativeEventAware::On3dmouseInput() +{ + // Don't do any data processing in background + bool bIsForeground = (::GetActiveWindow() != NULL); + if (!bIsForeground) { + // set all cached data to zero so that a zero event is seen and the cached data deleted + for (std::map::iterator it = fDevice2Data.begin(); it != fDevice2Data.end(); it++) { + it->second.fAxes.assign(6, .0); + it->second.fIsDirty = true; + } + } + + DWORD dwNow = ::GetTickCount(); // Current time; + DWORD dwElapsedTime; // Elapsed time since we were last here + + if (0 == fLast3dmouseInputTime) { + dwElapsedTime = 10; // System timer resolution + } else { + dwElapsedTime = dwNow - fLast3dmouseInputTime; + if (fLast3dmouseInputTime > dwNow) { + dwElapsedTime = ~dwElapsedTime+1; + } + if (dwElapsedTime<1) { + dwElapsedTime=1; + } else if (dwElapsedTime > 500) { + // Check for wild numbers because the device was removed while sending data + dwElapsedTime = 10; + } + } + +#if _TRACE_3DINPUT_PERIOD + qDebug("On3DmouseInput() period is %dms\n", dwElapsedTime); +#endif + + float mouseData2Rotation; + // v = w * r, we don't know r yet so lets assume r=1.) + float mouseData2PanZoom; + + // Grab the I3dmouseParam interface + I3dMouseParam& i3dmouseParam = f3dMouseParams; + // Take a look at the users preferred speed setting and adjust the sensitivity accordingly + I3dMouseSensor::ESpeed speedSetting = i3dmouseParam.GetSpeed(); + // See "Programming for the 3D Mouse", Section 5.1.3 + float speed = (speedSetting == I3dMouseSensor::kLowSpeed ? 0.25f : speedSetting == I3dMouseSensor::kHighSpeed ? 4.f : 1.f); + + // Multiplying by the following will convert the 3d mouse data to real world units + mouseData2PanZoom = speed; + mouseData2Rotation = speed; + + std::map::iterator iterator=fDevice2Data.begin(); + while (iterator != fDevice2Data.end()) { + + // If we have not received data for a while send a zero event + if ((--(iterator->second.fTimeToLive)) == 0) { + iterator->second.fAxes.assign(6, .0); + } else if (/*!t_bPoll3dmouse &&*/ !iterator->second.fIsDirty) { + // If we are not polling then only handle the data that was actually received + ++iterator; + continue; + } + iterator->second.fIsDirty=false; + + // get a copy of the device + HANDLE hdevice = iterator->first; + + // get a copy of the motion vectors and apply the user filters + std::vector motionData = iterator->second.fAxes; + + // apply the user filters + + // Pan Zoom filter + // See "Programming for the 3D Mouse", Section 5.1.2 + if (!i3dmouseParam.IsPanZoom()) { + // Pan zoom is switched off so set the translation vector values to zero + motionData[0] = motionData[1] = motionData[2] = 0.; + } + + // Rotate filter + // See "Programming for the 3D Mouse", Section 5.1.1 + if (!i3dmouseParam.IsRotate()) { + // Rotate is switched off so set the rotation vector values to zero + motionData[3] = motionData[4] = motionData[5] = 0.; + } + + // convert the translation vector into physical data + for (int axis = 0; axis < 3; axis++) { + motionData[axis] *= mouseData2PanZoom; + } + // convert the directed Rotate vector into physical data + // See "Programming for the 3D Mouse", Section 7.2.2 + for (int axis = 3; axis < 6; axis++) { + motionData[axis] *= mouseData2Rotation; + } + + // Now that the data has had the filters and sensitivty settings applied + // calculate the displacements since the last view update + for (int axis = 0; axis < 6; axis++) { + motionData[axis] *= dwElapsedTime; + } + + + // Now a bit of book keeping before passing on the data + if (iterator->second.IsZero()) { + iterator = fDevice2Data.erase(iterator); + } else { + ++iterator; + } + + // Work out which will be the next device + HANDLE hNextDevice = 0; + if (iterator != fDevice2Data.end()) { + hNextDevice = iterator->first; + } + + // Pass the 3dmouse input to the view controller + Move3d(hdevice, motionData); + + // Because we don't know what happened in the previous call, the cache might have + // changed so reload the iterator + iterator = fDevice2Data.find(hNextDevice); + } + + if (!fDevice2Data.empty()) { + fLast3dmouseInputTime = dwNow; + } else { + fLast3dmouseInputTime = 0; + } +} + +/*! + Called when new raw input data is available +*/ +void Gui::GUIApplicationNativeEventAware::OnRawInput(UINT nInputCode, HRAWINPUT hRawInput) +{ + const size_t cbSizeOfBuffer=1024; + BYTE pBuffer[cbSizeOfBuffer]; + + PRAWINPUT pRawInput = reinterpret_cast(pBuffer); + UINT cbSize = cbSizeOfBuffer; + + if (::GetRawInputData(hRawInput, RID_INPUT, pRawInput, &cbSize, sizeof(RAWINPUTHEADER)) == static_cast(-1)) { + return; + } + + bool b3dmouseInput = TranslateRawInputData(nInputCode, pRawInput); + ::DefRawInputProc(&pRawInput, 1, sizeof(RAWINPUTHEADER)); + + // Check for any buffered messages + cbSize = cbSizeOfBuffer; + UINT nCount = this->GetRawInputBuffer(pRawInput, &cbSize, sizeof(RAWINPUTHEADER)); + if (nCount == (UINT)-1) { + qDebug ("GetRawInputBuffer returned error %d\n", GetLastError()); + } + + while (nCount>0 && nCount != static_cast(-1)) { + PRAWINPUT pri = pRawInput; + UINT nInput; + for (nInput=0; nInputGetRawInputBuffer(pRawInput, &cbSize, sizeof(RAWINPUTHEADER)); + } + + // If we have mouse input data for the app then tell tha app about it + if (b3dmouseInput) { + On3dmouseInput(); + } +} + + + +bool Gui::GUIApplicationNativeEventAware::TranslateRawInputData(UINT nInputCode, PRAWINPUT pRawInput) +{ + bool bIsForeground = (nInputCode == RIM_INPUT); + +#if _TRACE_RI_TYPE + qDebug("Rawinput.header.dwType=0x%x\n", pRawInput->header.dwType); +#endif + // We are not interested in keyboard or mouse data received via raw input + if (pRawInput->header.dwType != RIM_TYPEHID) return false; + +#if _TRACE_RIDI_DEVICENAME + UINT dwSize=0; + if (::GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICENAME, NULL, &dwSize) == 0) { + std::vector szDeviceName(dwSize+1); + if (::GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICENAME, &szDeviceName[0], &dwSize) >0) { + qDebug("Device Name = %s\nDevice handle = 0x%x\n", &szDeviceName[0], pRawInput->header.hDevice); + } + } +#endif + + RID_DEVICE_INFO sRidDeviceInfo; + sRidDeviceInfo.cbSize = sizeof(RID_DEVICE_INFO); + UINT cbSize = sizeof(RID_DEVICE_INFO); + + if (::GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICEINFO, &sRidDeviceInfo, &cbSize) == cbSize) { +#if _TRACE_RIDI_DEVICEINFO + switch (sRidDeviceInfo.dwType) { + case RIM_TYPEMOUSE: + qDebug("\tsRidDeviceInfo.dwType=RIM_TYPEMOUSE\n"); + break; + case RIM_TYPEKEYBOARD: + qDebug("\tsRidDeviceInfo.dwType=RIM_TYPEKEYBOARD\n"); + break; + case RIM_TYPEHID: + qDebug("\tsRidDeviceInfo.dwType=RIM_TYPEHID\n"); + qDebug("\tVendor=0x%x\n\tProduct=0x%x\n\tUsagePage=0x%x\n\tUsage=0x%x\n", + sRidDeviceInfo.hid.dwVendorId, + sRidDeviceInfo.hid.dwProductId, + sRidDeviceInfo.hid.usUsagePage, + sRidDeviceInfo.hid.usUsage); + break; + } +#endif + + if (sRidDeviceInfo.hid.dwVendorId == LOGITECH_VENDOR_ID) { + if (pRawInput->data.hid.bRawData[0] == 0x01) { // Translation vector + + TInputData& deviceData = fDevice2Data[pRawInput->header.hDevice]; + deviceData.fTimeToLive = kTimeToLive; + if (bIsForeground) { + short* pnRawData = reinterpret_cast(&pRawInput->data.hid.bRawData[1]); + // Cache the pan zoom data + deviceData.fAxes[0] = static_cast(pnRawData[0]); + deviceData.fAxes[1] = static_cast(pnRawData[1]); + deviceData.fAxes[2] = static_cast(pnRawData[2]); + +#if _TRACE_RI_RAWDATA + qDebug("Pan/Zoom RI Data =\t0x%x,\t0x%x,\t0x%x\n", + pnRawData[0], pnRawData[1], pnRawData[2]); +#endif + if (pRawInput->data.hid.dwSizeHid >= 13) {// Highspeed package + // Cache the rotation data + deviceData.fAxes[3] = static_cast(pnRawData[3]); + deviceData.fAxes[4] = static_cast(pnRawData[4]); + deviceData.fAxes[5] = static_cast(pnRawData[5]); + deviceData.fIsDirty = true; +#if _TRACE_RI_RAWDATA + qDebug("Rotation RI Data =\t0x%x,\t0x%x,\t0x%x\n", + pnRawData[3], pnRawData[4], pnRawData[5]); +#endif + return true; + } + } else { // Zero out the data if the app is not in forground + deviceData.fAxes.assign(6, 0.f); + } + } else if (pRawInput->data.hid.bRawData[0] == 0x02) { // Rotation vector + // If we are not in foreground do nothing + // The rotation vector was zeroed out with the translation vector in the previous message + if (bIsForeground) { + TInputData& deviceData = fDevice2Data[pRawInput->header.hDevice]; + deviceData.fTimeToLive = kTimeToLive; + + short* pnRawData = reinterpret_cast(&pRawInput->data.hid.bRawData[1]); + // Cache the rotation data + deviceData.fAxes[3] = static_cast(pnRawData[0]); + deviceData.fAxes[4] = static_cast(pnRawData[1]); + deviceData.fAxes[5] = static_cast(pnRawData[2]); + deviceData.fIsDirty = true; + +#if _TRACE_RI_RAWDATA + qDebug("Rotation RI Data =\t0x%x,\t0x%x,\t0x%x\n", + pnRawData[0], pnRawData[1], pnRawData[2]); +#endif + return true; + } + } else if (pRawInput->data.hid.bRawData[0] == 0x03) { // Keystate change + // this is a package that contains 3d mouse keystate information + // bit0=key1, bit=key2 etc. + + + unsigned long dwKeystate = *reinterpret_cast(&pRawInput->data.hid.bRawData[1]); +#if _TRACE_RI_RAWDATA + qDebug("ButtonData =0x%x\n", dwKeystate); +#endif + // Log the keystate changes + unsigned long dwOldKeystate = fDevice2Keystate[pRawInput->header.hDevice]; + if (dwKeystate != 0) { + fDevice2Keystate[pRawInput->header.hDevice] = dwKeystate; + } else { + fDevice2Keystate.erase(pRawInput->header.hDevice); + } + + // Only call the keystate change handlers if the app is in foreground + if (bIsForeground) { + unsigned long dwChange = dwKeystate ^ dwOldKeystate; + + + for (int nKeycode=1; nKeycode<33; nKeycode++) { + if (dwChange & 0x01) { + int nVirtualKeyCode = HidToVirtualKey(sRidDeviceInfo.hid.dwProductId, nKeycode); + if (nVirtualKeyCode) { + if (dwKeystate&0x01) { + On3dmouseKeyDown(pRawInput->header.hDevice, nVirtualKeyCode); + } else { + On3dmouseKeyUp(pRawInput->header.hDevice, nVirtualKeyCode); + } + } + } + dwChange >>=1; + dwKeystate >>=1; + } + } + } + } + } + return false; +} +#endif // _USE_3DCONNEXION_SDK diff --git a/src/Gui/3Dconnexion/I3dMouseParams.h b/src/Gui/3Dconnexion/I3dMouseParams.h new file mode 100644 index 000000000..62d3072d8 --- /dev/null +++ b/src/Gui/3Dconnexion/I3dMouseParams.h @@ -0,0 +1,91 @@ + +/* +Development tools and related technology provided under license from 3Dconnexion. +(c) 1992 - 2012 3Dconnexion. All rights reserved +*/ + +#ifndef I3D_MOUSE_PARAMS_H +#define I3D_MOUSE_PARAMS_H + +/* + Parameters for the 3D mouse based on the SDK from 3Dconnexion +*/ + +class I3dMouseSensor +{ +public: + + enum ESpeed { + kLowSpeed = 0, + kMidSpeed, + kHighSpeed + }; + +virtual bool IsPanZoom() const = 0; +virtual bool IsRotate() const = 0; +virtual ESpeed GetSpeed() const = 0; + +virtual void SetPanZoom(bool isPanZoom) = 0; +virtual void SetRotate(bool isRotate) = 0; +virtual void SetSpeed(ESpeed speed) = 0; + +protected: + +virtual ~I3dMouseSensor() {} + +}; + + +class I3dMouseNavigation +{ +public: + + enum EPivot { + kManualPivot = 0, + kAutoPivot, + kAutoPivotOverride + }; + + enum ENavigation { + kObjectMode = 0, + kCameraMode, + kFlyMode, + kWalkMode, + kHelicopterMode + }; + + enum EPivotVisibility { + kHidePivot = 0, + kShowPivot, + kShowMovingPivot + }; + + +virtual ENavigation GetNavigationMode() const = 0; +virtual EPivot GetPivotMode() const = 0; +virtual EPivotVisibility GetPivotVisibility() const = 0; +virtual bool IsLockHorizon() const = 0; + +virtual void SetLockHorizon(bool bOn) = 0; +virtual void SetNavigationMode(ENavigation navigation) = 0; +virtual void SetPivotMode(EPivot pivot) = 0; +virtual void SetPivotVisibility(EPivotVisibility visibility) = 0; + +protected: + +virtual ~I3dMouseNavigation(){} + + +}; + + +class I3dMouseParam : public I3dMouseSensor, public I3dMouseNavigation +{ +public: + +virtual ~I3dMouseParam() {} + +}; + +#endif // I3D_MOUSE_PARAMS_H + diff --git a/src/Gui/3Dconnexion/MouseParameters.cpp b/src/Gui/3Dconnexion/MouseParameters.cpp new file mode 100644 index 000000000..5a822fded --- /dev/null +++ b/src/Gui/3Dconnexion/MouseParameters.cpp @@ -0,0 +1,95 @@ + +/* +Development tools and related technology provided under license from 3Dconnexion. +(c) 1992 - 2012 3Dconnexion. All rights reserved +*/ + + +#include "PreCompiled.h" +#include "MouseParameters.h" + +MouseParameters::MouseParameters(): fNavigation(kObjectMode) + , fPivot(kAutoPivot) + , fPivotVisibility(kShowPivot) + , fIsLockHorizon(true) + , fIsPanZoom(true) + , fIsRotate(true) + , fSpeed(kLowSpeed) +{ +} + +MouseParameters::~MouseParameters() +{ +} + +bool MouseParameters::IsPanZoom() const +{ + return fIsPanZoom; +} + +bool MouseParameters::IsRotate() const +{ + return fIsRotate; +} + +MouseParameters::ESpeed MouseParameters::GetSpeed() const +{ + return fSpeed; +} + +void MouseParameters::SetPanZoom(bool isPanZoom) +{ + fIsPanZoom=isPanZoom; +} + +void MouseParameters::SetRotate(bool isRotate) +{ + fIsRotate=isRotate; +} + +void MouseParameters::SetSpeed(ESpeed speed) +{ + fSpeed=speed; +} + + +MouseParameters::ENavigation MouseParameters::GetNavigationMode() const +{ + return fNavigation; +} + +MouseParameters::EPivot MouseParameters::GetPivotMode() const +{ + return fPivot; +} + +MouseParameters::EPivotVisibility MouseParameters::GetPivotVisibility() const +{ + return fPivotVisibility; +} + +bool MouseParameters::IsLockHorizon() const +{ + return fIsLockHorizon; +} + +void MouseParameters::SetLockHorizon(bool bOn) +{ + fIsLockHorizon=bOn; +} + +void MouseParameters::SetNavigationMode(ENavigation navigation) +{ + fNavigation=navigation; +} + +void MouseParameters::SetPivotMode(EPivot pivot) +{ + if (fPivot!=kManualPivot || pivot!=kAutoPivotOverride) + fPivot = pivot; +} + +void MouseParameters::SetPivotVisibility(EPivotVisibility visibility) +{ + fPivotVisibility = visibility; +} diff --git a/src/Gui/3Dconnexion/MouseParameters.h b/src/Gui/3Dconnexion/MouseParameters.h new file mode 100644 index 000000000..3e00701bf --- /dev/null +++ b/src/Gui/3Dconnexion/MouseParameters.h @@ -0,0 +1,56 @@ + +/* +Development tools and related technology provided under license from 3Dconnexion. +(c) 1992 - 2012 3Dconnexion. All rights reserved +*/ + + +#ifndef T3D_MOUSE_PARAMS_H +#define T3D_MOUSE_PARAMS_H + +#include "I3dMouseParams.h" + + +class MouseParameters : public I3dMouseParam +{ +public: + MouseParameters(); + ~MouseParameters(); + + // I3dmouseSensor interface + bool IsPanZoom() const; + bool IsRotate() const; + ESpeed GetSpeed() const; + + void SetPanZoom(bool isPanZoom); + void SetRotate(bool isRotate); + void SetSpeed(ESpeed speed); + + // I3dmouseNavigation interface + ENavigation GetNavigationMode() const; + EPivot GetPivotMode() const; + EPivotVisibility GetPivotVisibility() const; + bool IsLockHorizon() const; + + void SetLockHorizon(bool bOn); + void SetNavigationMode(ENavigation navigation); + void SetPivotMode(EPivot pivot); + void SetPivotVisibility(EPivotVisibility visibility); + +private: + + MouseParameters (const MouseParameters&); + const MouseParameters& operator =(const MouseParameters&); + + ENavigation fNavigation; + EPivot fPivot; + EPivotVisibility fPivotVisibility; + bool fIsLockHorizon; + + bool fIsPanZoom; + bool fIsRotate; + ESpeed fSpeed; +}; + +#endif // T3D_MOUSE_PARAMS_H + diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index b7788cfcd..b4bf7c5a5 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -3,6 +3,9 @@ if(WIN32) add_definitions(-DFCGui -DQIIS_MAKEDLL) endif(WIN32) +if (FREECAD_USE_3DCONNEXION) +add_definitions(-D_USE_3DCONNEXION_SDK) +endif(FREECAD_USE_3DCONNEXION) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} @@ -78,6 +81,17 @@ SET(FreeCADGui_XML_SRCS ) SOURCE_GROUP("XML" FILES ${FreeCADApp_XML_SRCS}) +# The 3D Connexion SDK files +if(FREECAD_USE_3DCONNEXION) +SET(FreeCADGui_SDK_SRCS + 3Dconnexion/I3dMouseParams.h + 3Dconnexion/MouseParameters.cpp + 3Dconnexion/MouseParameters.h + 3Dconnexion/GuiApplicationNativeEventAwareWin32.cpp +) +SOURCE_GROUP("3D connexion SDK" FILES ${FreeCADGui_SDK_SRCS}) +endif(FREECAD_USE_3DCONNEXION) + set(Gui_MOC_HDRS Action.h CallTips.h @@ -769,6 +783,7 @@ SET(FreeCADGui_SRCS ) SET(FreeCADGui_SRCS + ${FreeCADGui_SDK_SRCS} ${FreeCADGui_CPP_SRCS} ${FreeCADGui_XML_SRCS} ${iis_MOC_SRCS} diff --git a/src/Gui/GuiApplicationNativeEventAware.cpp b/src/Gui/GuiApplicationNativeEventAware.cpp index e741fcfa4..f14beb3bb 100644 --- a/src/Gui/GuiApplicationNativeEventAware.cpp +++ b/src/Gui/GuiApplicationNativeEventAware.cpp @@ -40,11 +40,10 @@ #endif #endif -#ifdef Q_WS_WIN -#include +#ifdef _USE_3DCONNEXION_SDK +Gui::GUIApplicationNativeEventAware* Gui::GUIApplicationNativeEventAware::gMouseInput = 0; #endif - Gui::GUIApplicationNativeEventAware::GUIApplicationNativeEventAware(int &argc, char *argv[]) : QApplication (argc, argv), spaceballPresent(false) { @@ -59,11 +58,18 @@ Gui::GUIApplicationNativeEventAware::~GUIApplicationNativeEventAware() else Base::Console().Log("Disconnected from spacenav daemon\n"); #endif + +#ifdef _USE_3DCONNEXION_SDK + if (gMouseInput == this) { + gMouseInput = 0; + } +#endif } void Gui::GUIApplicationNativeEventAware::initSpaceball(QMainWindow *window) { mainWindow = window; + #ifdef SPNAV_FOUND if (spnav_x11_open(QX11Info::display(), window->winId()) == -1) Base::Console().Log("Couldn't connect to spacenav daemon\n"); @@ -74,6 +80,19 @@ void Gui::GUIApplicationNativeEventAware::initSpaceball(QMainWindow *window) } #endif +#ifdef _USE_3DCONNEXION_SDK + spaceballPresent = Is3dmouseAttached(); + + if (spaceballPresent) { + fLast3dmouseInputTime = 0; + + if (InitializeRawInput(mainWindow->winId())){ + gMouseInput = this; + qApp->setEventFilter(Gui::GUIApplicationNativeEventAware::RawInputEventFilter); + } + } +#endif // _USE_3DCONNEXION_SDK + Spaceball::MotionEvent::MotionEventType = QEvent::registerEventType(); Spaceball::ButtonEvent::ButtonEventType = QEvent::registerEventType(); } @@ -146,15 +165,8 @@ bool Gui::GUIApplicationNativeEventAware::x11EventFilter(XEvent *event) return true; #else return false; -#endif +#endif // SPNAV_FOUND } -#endif - -#ifdef Q_WS_WIN -bool Gui::GUIApplicationNativeEventAware::winEventFilter(MSG *msg, long *result) -{ - return false; -} -#endif +#endif // Q_WS_X11 #include "moc_GuiApplicationNativeEventAware.cpp" diff --git a/src/Gui/GuiApplicationNativeEventAware.h b/src/Gui/GuiApplicationNativeEventAware.h index b43245a65..113dd4b1f 100644 --- a/src/Gui/GuiApplicationNativeEventAware.h +++ b/src/Gui/GuiApplicationNativeEventAware.h @@ -29,6 +29,19 @@ class QMainWindow; + +#ifdef _USE_3DCONNEXION_SDK +#include "3Dconnexion/MouseParameters.h" + +#include +#include + +// #define _WIN32_WINNT 0x0501 //target at least windows XP + +#include + +#endif // _USE_3DCONNEXION_SDK + namespace Gui { class GUIApplicationNativeEventAware : public QApplication @@ -38,18 +51,65 @@ namespace Gui GUIApplicationNativeEventAware(int &argc, char *argv[]); ~GUIApplicationNativeEventAware(); void initSpaceball(QMainWindow *window); + bool isSpaceballPresent() const {return spaceballPresent;} bool processSpaceballEvent(QObject *object, QEvent *event); -#ifdef Q_WS_X11 - bool x11EventFilter(XEvent *event); -#endif - -#ifdef Q_WS_WIN - bool winEventFilter(MSG *msg, long *result); -#endif - bool isSpaceballPresent(){return spaceballPresent;} private: bool spaceballPresent; QMainWindow *mainWindow; + +// For X11 +#ifdef Q_WS_X11 + public: + bool x11EventFilter(XEvent *event); +#endif // Q_WS_X11 + +// For Windows +#ifdef _USE_3DCONNEXION_SDK + public: + static bool Is3dmouseAttached(); + + I3dMouseParam& MouseParams(); + const I3dMouseParam& MouseParams() const; + + virtual void Move3d(HANDLE device, std::vector& motionData); + virtual void On3dmouseKeyDown(HANDLE device, int virtualKeyCode); + virtual void On3dmouseKeyUp(HANDLE device, int virtualKeyCode); + + private: + bool InitializeRawInput(HWND hwndTarget); + static bool RawInputEventFilter(void* msg, long* result); + void OnRawInput(UINT nInputCode, HRAWINPUT hRawInput); + UINT GetRawInputBuffer(PRAWINPUT pData, PUINT pcbSize, UINT cbSizeHeader); + bool TranslateRawInputData(UINT nInputCode, PRAWINPUT pRawInput); + void On3dmouseInput(); + + class TInputData + { + public: + TInputData() : fAxes(6) {} + + bool IsZero() { + return (0.==fAxes[0] && 0.==fAxes[1] && 0.==fAxes[2] && + 0.==fAxes[3] && 0.==fAxes[4] && 0.==fAxes[5]); + } + + int fTimeToLive; // For telling if the device was unplugged while sending data + bool fIsDirty; + std::vector fAxes; + }; + + HWND fWindow; + + // Data cache to handle multiple rawinput devices + std::map< HANDLE, TInputData> fDevice2Data; + std::map< HANDLE, unsigned long> fDevice2Keystate; + // 3dmouse parameters + MouseParameters f3dMouseParams; // Rotate, Pan Zoom etc. + // use to calculate distance traveled since last event + DWORD fLast3dmouseInputTime; + static Gui::GUIApplicationNativeEventAware* gMouseInput; +#endif // _USE_3DCONNEXION_SDK }; } + #endif // GUIAPPLICATIONNATIVEEVENTAWARE_H diff --git a/src/Gui/Splashscreen.cpp b/src/Gui/Splashscreen.cpp index 769af1a07..46f1183e9 100644 --- a/src/Gui/Splashscreen.cpp +++ b/src/Gui/Splashscreen.cpp @@ -26,6 +26,7 @@ # include # include # include +# include # include # include # include @@ -182,7 +183,11 @@ AboutDialogFactory::~AboutDialogFactory() QDialog *AboutDialogFactory::create(QWidget *parent) const { +#ifdef _USE_3DCONNEXION_SDK + return new AboutDialog(true, parent); +#else return new AboutDialog(false, parent); +#endif } const AboutDialogFactory *AboutDialogFactory::defaultFactory() @@ -334,8 +339,54 @@ void AboutDialog::setupLabels() } } +namespace Gui { +namespace Dialog { + +class GuiExport LicenseDialog : public QDialog +{ +public: + LicenseDialog(QWidget *parent = 0) : QDialog(parent, Qt::FramelessWindowHint) + { + QString info; +#ifdef _USE_3DCONNEXION_SDK + info = QString::fromAscii( + "3D Mouse Support:\n" + "Development tools and related technology provided under license from 3Dconnexion.\n" + "(c) 1992 - 2012 3Dconnexion. All rights reserved"); +#endif + statusLabel = new QLabel(info); + buttonBox = new QDialogButtonBox; + buttonBox->setStandardButtons(QDialogButtonBox::Ok); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + + QHBoxLayout *topLayout = new QHBoxLayout; + topLayout->addWidget(statusLabel); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(topLayout); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + setWindowTitle(tr("Copyright")); + } + ~LicenseDialog() + { + } + +private: + QLabel *statusLabel; + QDialogButtonBox *buttonBox; +}; + +} // namespace Dialog +} // namespace Gui + void AboutDialog::on_licenseButton_clicked() { +#ifdef _USE_3DCONNEXION_SDK + LicenseDialog dlg(this); + dlg.exec(); +#endif } void AboutDialog::on_copyButton_clicked() diff --git a/src/Tools/makedist.py b/src/Tools/makedist.py index e42c69a33..66c4a3ea9 100644 --- a/src/Tools/makedist.py +++ b/src/Tools/makedist.py @@ -34,6 +34,7 @@ def main(): gitattr.write("zipios++ export-ignore\n") gitattr.write("Pivy-0.5 export-ignore\n") gitattr.write("Pivy export-ignore\n") + gitattr.write("3Dconnexion export-ignore\n") gitattr.write("Kuka export-ignore\n") gitattr.close()