/*========================================================================= Program: Visualization Toolkit Module: vtkTDxWinDevice.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkTDxWinDevice.h" // Most of the code is derived from the SDK with sample code // Cube3dPolling.cpp from archive Cube3Dpolling.zip from 3DConnexion. #include #include "vtkTDxMotionEventInfo.h" #include "vtkCommand.h" #include "vtkObjectFactory.h" #include "vtkRenderWindowInteractor.h" #include vtkStandardNewMacro(vtkTDxWinDevice); #include // for CComPtr<> (a smart pointer) #include // for UINT_PTR // for ISensor and IKeyboard #import "progid:TDxInput.Device.1" no_namespace VOID CALLBACK vtkTDxWinDeviceTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); class vtkLessThanWindowHandle { public: bool operator()(const HWND &h1, const HWND &h2) const { return h1 vtkWindowHandleToDeviceObject; std::map vtkWindowHandleToDeviceObjectConnection; class vtkTDxWinDevicePrivate { public: vtkTDxWinDevicePrivate() { this->TimerId=0; this->Sensor=0; this->Keyboard=0; this->KeyStates=0; this->LastTimeStamp=0; this->Interactor=0; } UINT_PTR TimerId; CComPtr Sensor; CComPtr Keyboard; __int64 KeyStates; DWORD LastTimeStamp; vtkRenderWindowInteractor *Interactor; }; vtkTDxWinDevicePrivate vtkTDxWinDevicePrivateObject; HRESULT HresultCodes[13]={S_OK, REGDB_E_CLASSNOTREG, CLASS_E_NOAGGREGATION, E_NOINTERFACE, E_POINTER, E_ABORT, E_ACCESSDENIED, E_FAIL, E_HANDLE, E_INVALIDARG, E_NOTIMPL, E_OUTOFMEMORY, E_UNEXPECTED}; const char *HresultStrings[13]={"S_OK", "REGDB_E_CLASSNOTREG", "CLASS_E_NOAGGREGATION", "E_NOINTERFACE", "E_POINTER", "E_ABORT", "E_ACCESSDENIED", "E_FAIL", "E_HANDLE", "E_INVALIDARG", "E_NOTIMPL", "E_OUTOFMEMORY", "E_UNEXPECTED"}; const UINT_PTR VTK_IDT_TDX_TIMER=1664; // Random beer. // ---------------------------------------------------------------------------- // Description: // Return a human readable version of an HRESULT. const char *HresultCodeToString(HRESULT hr) { int i=0; bool found=false; while(!found && i<13) { found=HresultCodes[i]==hr; ++i; } const char *result; if(found) { result=HresultStrings[i-1]; } else { result="unknown"; } return result; } // ---------------------------------------------------------------------------- // Description: // Default constructor. vtkTDxWinDevice::vtkTDxWinDevice() { this->WindowHandle=0; this->Private=new vtkTDxWinDevicePrivate; this->IsListening=false; // this->DebugOn(); } // ---------------------------------------------------------------------------- // Description: // Destructor. If the device is not initialized, do nothing. If the device // is initialized, close the device. vtkTDxWinDevice::~vtkTDxWinDevice() { if(this->Initialized) { this->Close(); } delete this->Private; } // ---------------------------------------------------------------------------- // Description: // Get the handle of the window. Initial value is 0. HWND vtkTDxWinDevice::GetWindowHandle() const { return this->WindowHandle; } // ---------------------------------------------------------------------------- // Description: // Set the handle of the window. // \pre not_yet_initialized: !GetInitialized() void vtkTDxWinDevice::SetWindowHandle(HWND hWnd) { assert("pre: not_yet_initialized" && !this->GetInitialized()); if(this->WindowHandle!=hWnd) { this->WindowHandle=hWnd; this->Modified(); } } // ---------------------------------------------------------------------------- // Description: // Initialize the device with the current display and window ids. // It updates the value of GetInitialized(). // Initialization can fail. You must look for the value of // GetInitialized() before processing further. // \pre not_yet_initialized: !GetInitialized() void vtkTDxWinDevice::Initialize() { assert("pre: not_yet_initialized" && !this->GetInitialized()); bool status=false; HRESULT hr=0; CComPtr device; bool alreadyConnected=!vtkWindowHandleToDeviceObjectConnection.empty(); if(alreadyConnected) { // take the first one, to copy the device information. std::map::iterator it=vtkWindowHandleToDeviceObjectConnection.begin(); vtkTDxWinDevice *other=(*it).second; vtkTDxWinDevicePrivate *o=this->Private; vtkTDxWinDevicePrivate *otherO=other->Private; o->Sensor=otherO->Sensor; o->Keyboard=otherO->Keyboard; o->Interactor=this->Interactor; vtkWindowHandleToDeviceObjectConnection.insert(std::pair(this->WindowHandle,this)); this->Initialized=true; } else { hr=::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED ); if (!SUCCEEDED(hr)) { vtkWarningMacro( << "CoInitializeEx failed. hresult=0x" << hex << hr << dec << HresultCodeToString(hr)); this->Initialized=status; return; } // Create the device object hr=device.CoCreateInstance(__uuidof(Device)); status=SUCCEEDED(hr); if(status) { CComPtr d; hr=device.QueryInterface(&d); status=SUCCEEDED(hr); if(status) { vtkTDxWinDevicePrivate *o= this->Private; // Get the interfaces to the sensor and the keyboard; o->Sensor=d->Sensor; o->Keyboard=d->Keyboard; o->Interactor=this->Interactor; // Connect to the driver d->Connect(); vtkWindowHandleToDeviceObjectConnection.insert(std::pair(this->WindowHandle,this)); vtkDebugMacro(<< "Connected to COM-object for 3dConnexion device."); } else { vtkWarningMacro(<<"Could not get the device interface. hresult=0x" << hex << hr << dec << HresultCodeToString(hr)); } } else { // CoCreateInstance Failed. // It means there is no driver installed. // Just return silently: don't display warning message. } this->Initialized=status; } } // ---------------------------------------------------------------------------- bool vtkTDxWinDevice::GetIsListening() const { return this->IsListening; } // ---------------------------------------------------------------------------- // \pre initialized: GetInitialized() // \pre not_yet: !GetIsListening() void vtkTDxWinDevice::StartListening() { assert("pre: initialized" && this->GetInitialized()); assert("pre: not_yet" && !this->GetIsListening()); // Create timer used to poll the 3dconnexion device this->Private->TimerId =::SetTimer(this->WindowHandle,VTK_IDT_TDX_TIMER,25, vtkTDxWinDeviceTimerProc); vtkWindowHandleToDeviceObject.insert( std::pair(this->WindowHandle,this)); this->IsListening=true; vtkDebugMacro(<< "Start listening on window=" << this->WindowHandle); } // ---------------------------------------------------------------------------- // \pre initialized: GetInitialized() // \pre is_listening: GetIsListening() void vtkTDxWinDevice::StopListening() { assert("pre: initialized" && this->GetInitialized()); assert("pre: is_listening" && this->GetIsListening()); const UINT_PTR uIDEvent = this->WindowHandle != 0 ? VTK_IDT_TDX_TIMER : this->Private->TimerId; // Kill the timer used to poll the sensor and keyboard ::KillTimer(this->WindowHandle, uIDEvent); this->Private->TimerId=0; this->IsListening=false; std::map::iterator it=vtkWindowHandleToDeviceObject.find(this->WindowHandle); if(it==vtkWindowHandleToDeviceObject.end()) { vtkErrorMacro(<< "No matching vtkTDxWinDevice object for window hwnd=" << this->WindowHandle); } else { vtkWindowHandleToDeviceObject.erase(it); } vtkDebugMacro(<< "Stop listening on window=" << this->WindowHandle); } // ---------------------------------------------------------------------------- // Description: // Close the device. This is called by the destructor. // You don't have to close the device explicitly, as the destructor do it // automatically, but you can. // \pre initialized: GetInitialized(). // \post restored: !GetInitialized() void vtkTDxWinDevice::Close() { assert("pre: initialized" && this->GetInitialized()); vtkDebugMacro(<< "Close()" ); CComPtr d; if(this->IsListening) { this->StopListening(); } std::map::iterator it=vtkWindowHandleToDeviceObjectConnection.find(this->WindowHandle); if(it==vtkWindowHandleToDeviceObjectConnection.end()) { vtkErrorMacro(<< "No matching vtkTDxWinDevice object for window hwnd=" << this->WindowHandle); } else { vtkWindowHandleToDeviceObjectConnection.erase(it); } if(vtkWindowHandleToDeviceObjectConnection.empty()) { // Release the sensor and keyboard interfaces vtkTDxWinDevicePrivate *o=this->Private; if(o->Sensor!=0) { o->Sensor->get_Device(reinterpret_cast(&d)); o->Sensor.Release(); } if(o->Keyboard!=0) { o->Keyboard.Release(); } if(d!=0) { // Disconnect it from the driver d->Disconnect(); d.Release(); } } this->Initialized=false; assert("post: restored" && !this->GetInitialized()); } // ---------------------------------------------------------------------------- void vtkTDxWinDevice::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); } // ---------------------------------------------------------------------------- void vtkTDxWinDevice::ProcessEvent(void) { vtkTDxWinDevicePrivate *o=this->Private; if(o->Keyboard!=0) { // Check if any change to the keyboard state try { long nKeys; nKeys = o->Keyboard->Keys; long i; for (i=1; i<=nKeys; i++) { __int64 mask = (__int64)1<<(i-1); VARIANT_BOOL isPressed; isPressed = o->Keyboard->IsKeyDown(i); if (isPressed == VARIANT_TRUE) { if (!(o->KeyStates & mask)) { o->KeyStates |= mask; int buttonInfo=static_cast(i); vtkDebugMacro(<<"button press event:"<< buttonInfo); o->Interactor->InvokeEvent(vtkCommand::TDxButtonPressEvent, &buttonInfo); } } else { o->KeyStates &= ~mask; } } // Test the special keys for (i=30; i<=31; i++) { __int64 mask = (__int64)1<<(i-1); VARIANT_BOOL isPressed; isPressed = o->Keyboard->IsKeyDown(i); if (isPressed == VARIANT_TRUE) { if (!(o->KeyStates & mask)) { o->KeyStates |= mask; int buttonInfo=static_cast(i); vtkDebugMacro(<<"button press event (special):"<< buttonInfo); o->Interactor->InvokeEvent(vtkCommand::TDxButtonPressEvent, &buttonInfo); } } else { o->KeyStates &= ~mask; } } } catch (...) { // Some sort of exception handling } } if(o->Sensor!=0) { try { CComPtr r=o->Sensor->Rotation; CComPtr t=o->Sensor->Translation; // On Windows, The angle/axis object is // the instant rotation with the vector of rotation+one angle. // which is different from the Mac and Unix API... // Check if the cap is still displaced if(r->Angle > 0. || t->Length > 0.) { double timeFactor=1.0; DWORD currentTime=::GetTickCount(); if(o->LastTimeStamp!=0) { timeFactor=static_cast(currentTime-o->LastTimeStamp) /o->Sensor->Period; } o->LastTimeStamp=currentTime; vtkTDxMotionEventInfo motionInfo; motionInfo.X=t->X; motionInfo.Y=t->Y; motionInfo.Z=t->Z; motionInfo.Angle=r->Angle; motionInfo.AxisX=r->X; motionInfo.AxisY=r->Y; motionInfo.AxisZ=r->Z; o->Interactor->InvokeEvent(vtkCommand::TDxMotionEvent, &motionInfo); } else { o->LastTimeStamp=0; } r.Release(); t.Release(); } catch (...) { // Some sort of exception handling } } } // ---------------------------------------------------------------------------- // The timer callback is used to poll the 3d input device for change of // keystates and the cap displacement values. VOID CALLBACK vtkTDxWinDeviceTimerProc(HWND hwnd, UINT vtkNotUsed(uMsg), UINT_PTR vtkNotUsed(idEvent), DWORD vtkNotUsed(dwTime)) { std::map::iterator it=vtkWindowHandleToDeviceObject.find(hwnd); if(it==vtkWindowHandleToDeviceObject.end()) { //vtkGenericWarningMacro(<< "No matching vtkTDxWinDevice object for window hwnd=" << hwnd); return; } vtkTDxWinDevice *device=(*it).second; device->ProcessEvent(); }