You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
336 lines
10 KiB
C++
336 lines
10 KiB
C++
/*=========================================================================
|
|
|
|
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 <cassert>
|
|
|
|
#include "vtkTDxMotionEventInfo.h"
|
|
#include "vtkCommand.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkRenderWindowInteractor.h"
|
|
#include "vtkMath.h"
|
|
|
|
#include <map>
|
|
#include <cstring> // 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<id2;
|
|
}
|
|
};
|
|
|
|
|
|
// It would be better to have the following variables as member of variable
|
|
// but the message handle registration through the 3DConnexion SDK is a piece
|
|
// of crap: it only takes a callback pointer but no callback data!
|
|
std::map<UInt16,vtkTDxMacDevice *,vtkLessThanClientID> 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<const UInt16,vtkTDxMacDevice *>(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<UInt16,vtkTDxMacDevice *,vtkLessThanClientID>::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<unsigned char>(l);
|
|
result[l+1]=0;
|
|
size_t i=0;
|
|
while(i<l)
|
|
{
|
|
result[i+1]=static_cast<unsigned char>(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<UInt16,vtkTDxMacDevice *,vtkLessThanClientID>::iterator it;
|
|
vtkTDxMacDevice *device;
|
|
|
|
switch(messageType)
|
|
{
|
|
case kConnexionMsgDeviceState:
|
|
s=static_cast<ConnexionDeviceState *>(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;
|
|
}
|
|
}
|