/*========================================================================= Program: Visualization Toolkit Module: vtkTDxMacDevice.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 "vtkTDxMacDevice.h" #include #include "vtkTDxMotionEventInfo.h" #include "vtkCommand.h" #include "vtkObjectFactory.h" #include "vtkRenderWindowInteractor.h" #include "vtkMath.h" #include #include // for strlen() vtkStandardNewMacro(vtkTDxMacDevice); void vtkTDxMacDeviceMessageHandler(io_connect_t connection, natural_t messageType, void *messageArgument); class vtkLessThanClientID { public: bool operator()(const UInt16 &id1, const UInt16 &id2) const { return id1 vtkClientIDToDeviceObject; // ---------------------------------------------------------------------------- // Description: // Default constructor. vtkTDxMacDevice::vtkTDxMacDevice() { this->ClientApplicationName=0; this->SetClientApplicationName("3DxClientTest"); this->ClientID=0; this->LastButtonState=0; // all buttons released. // this->DebugOn(); } // ---------------------------------------------------------------------------- // Description: // Destructor. If the device is not initialized, do nothing. If the device // is initialized, close the device. vtkTDxMacDevice::~vtkTDxMacDevice() { if(this->Initialized) { this->Close(); } this->SetClientApplicationName(0); } // ---------------------------------------------------------------------------- // 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() // \pre valid_name: GetClientApplicationName()!=0 void vtkTDxMacDevice::Initialize() { assert("pre: not_yet_initialized" && !this->GetInitialized()); assert("pre: valid_name" && this->GetClientApplicationName()!=0); if(vtkClientIDToDeviceObject.empty()) { OSErr result=InstallConnexionHandlers(vtkTDxMacDeviceMessageHandler,0L,0L); this->Initialized=result==noErr; // 0 } else { this->Initialized=true; } this->LastButtonState=0; // all buttons released. unsigned char *pString= this->CStringToPascalString(this->ClientApplicationName); // this does not work, we have to use kConnexionClientWildcard and '\0' // this->ClientID=RegisterConnexionClient('MCTt',pString,kConnexionClientModeTakeOver,kConnexionMaskAll); this->ClientID=RegisterConnexionClient(kConnexionClientWildcard,'\0', kConnexionClientModeTakeOver, kConnexionMaskAll); vtkDebugMacro(<< "Registered with ClientID=" << this->ClientID); delete[] pString; vtkClientIDToDeviceObject.insert(std::pair(this->ClientID,this)); } // ---------------------------------------------------------------------------- // 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 vtkTDxMacDevice::Close() { assert("pre: initialized" && this->GetInitialized()); vtkDebugMacro(<< "Close()" ); UnregisterConnexionClient(this->ClientID); // remove it from map std::map::iterator it= vtkClientIDToDeviceObject.find(this->ClientID); if(it==vtkClientIDToDeviceObject.end()) { vtkErrorMacro(<< "No matching vtkTDxMacDevice object for clientID=" << this->ClientID); } else { vtkClientIDToDeviceObject.erase(it); } // only if the map is empty. if(vtkClientIDToDeviceObject.empty()) { CleanupConnexionHandlers(); } this->ClientID=0; this->LastButtonState=0; // all buttons released. this->Initialized=false; assert("post: restored" && !this->GetInitialized()); } // ---------------------------------------------------------------------------- // Description: // Translate the X11 event by invoking a VTK event, if the event came from // the device. // Return true if the event passed in argument was effectively an event from // the device, return false otherwise. // \pre initialized: GetInitialized() // \pre s_exists: s!=0 // \pre client_matches: s->client==this->ClientID void vtkTDxMacDevice::ProcessEvent(const ConnexionDeviceState *s) { assert("pre: initialized" && this->GetInitialized()); assert("pre: s_exists" && s!=0); assert("pre: client_matches" && s->client==this->ClientID); vtkTDxMotionEventInfo motionInfo; int buttonInfo; double axis[3]; UInt16 mask; bool pressed; switch(s->command) { case kConnexionCmdHandleAxis: vtkDebugMacro(<< "it is kConnexionCmdHandleAxis"); motionInfo.X=s->axis[0]; // Tx: SInt16 between -1024 and 1024 // On Mac, the Y and Z axes are reversed (wrong). We want to have a // right-handed coordinate system, so positive Y has to go from bottom to // top and positive Z has to come towards us, as on Windows. motionInfo.Y=-s->axis[1]; // Ty: SInt16 between -1024 and 1024 motionInfo.Z=-s->axis[2]; // Tz: SInt16 between -1024 and 1014 axis[0]=s->axis[3]; // Rx: SInt16 between -1024 and 1024 // On Mac, the Y and Z axes are reversed (wrong). axis[1]=-s->axis[4]; // Ry: SInt16 between -1024 and 1024 axis[2]=-s->axis[5]; // Rz: SInt16 between -1024 and 1024 motionInfo.Angle=vtkMath::Norm(axis); if(motionInfo.Angle==0.0) { motionInfo.AxisX=0.0; motionInfo.AxisY=0.0; motionInfo.AxisZ=1.0; } else { motionInfo.AxisX=axis[0]/motionInfo.Angle; motionInfo.AxisY=axis[1]/motionInfo.Angle; motionInfo.AxisZ=axis[2]/motionInfo.Angle; } this->Interactor->InvokeEvent(vtkCommand::TDxMotionEvent,&motionInfo); break; case kConnexionCmdHandleButtons: // find which button changed // (from release to press or from press to release) // mask shoud have only one bit at 1, the one of the button that changed. vtkDebugMacro("lastbuttons=" << hex << this->LastButtonState << dec); vtkDebugMacro("buttons=" << hex << s->buttons << dec); mask=s->buttons^this->LastButtonState; vtkDebugMacro("mask=" << hex << mask << dec); this->LastButtonState=s->buttons; pressed=(s->buttons&mask)!=0; // find the button number (starting at 0). buttonInfo=0; mask=mask>>1; while(mask!=0) { mask=mask>>1; ++buttonInfo; } if(pressed) { vtkDebugMacro(<< "it is kConnexionCmdHandleButtons (press) event"); this->Interactor->InvokeEvent(vtkCommand::TDxButtonPressEvent, &buttonInfo); } else { vtkDebugMacro(<< "it is kConnexionCmdHandleButtons (release) event"); this->Interactor->InvokeEvent(vtkCommand::TDxButtonReleaseEvent, &buttonInfo); } break; default: // ignore kConnexionCmd(None|HandleRawData|AppSpecific) break; } } // ---------------------------------------------------------------------------- void vtkTDxMacDevice::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "ClientApplicationName="; if(this->ClientApplicationName!=0) { os << this->ClientApplicationName << endl; } else { os << "(none)" << endl; } } // ---------------------------------------------------------------------------- // Description: // Convert a C string to a Pascal String. Allocation happens with new[]. // It is the responsibility of the user to call delete[]. // // Apple specific. String literal starting with \p are pascal strings: it is // an unsigned char array starting with the length and terminated by \0. The // length does not include the length value neither the \0. // Pascal strings literal are allowed with apple specific gcc option // -fpascal-strings. Pascal literal are writable is -fwritable-strings is // set. -Wwrite-strings is a related warning flag. // \pre s_exists: s!=0 // \pre s_small_enough: strlen(s)<=255 // \post result_exists: result!=0 unsigned char *vtkTDxMacDevice::CStringToPascalString(const char *s) { assert("pre: s_exists" && s!=0); assert("pre: s_small_enough" && strlen(s)<=255); size_t l=strlen(s); unsigned char *result=new unsigned char[l+2]; result[0]=static_cast(l); result[l+1]=0; size_t i=0; while(i(s[i]); ++i; } assert("post result_exists" && result!=0); return result; } // ---------------------------------------------------------------------------- // The timer callback is used to poll the 3d input device for change of // keystates and the cap displacement values. void vtkTDxMacDeviceMessageHandler(io_connect_t connection, natural_t messageType, void *messageArgument) { ConnexionDeviceState *s; std::map::iterator it; vtkTDxMacDevice *device; switch(messageType) { case kConnexionMsgDeviceState: s=static_cast(messageArgument); it=vtkClientIDToDeviceObject.find(s->client); if(it==vtkClientIDToDeviceObject.end()) { // it can happen during initialization phase because of a race condition: // clientID is registered with the system, events can happen but the // line that records the clientID is the map is not executed yet. // No worries. return; } device=(*it).second; device->ProcessEvent(s); break; default: // other messageTypes can happen and should be ignored. break; } }