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.
nmWTAI-Platform/3rd/VTK7.1/source/VTK-7.1.1/Rendering/OpenGL/vtkCocoaRenderWindowInterac...

495 lines
17 KiB
Plaintext

/*=========================================================================
Program: Visualization Toolkit
Module: vtkCocoaRenderWindowInteractor.mm
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.
=========================================================================*/
#import <Cocoa/Cocoa.h>
#import "vtkCocoaMacOSXSDKCompatibility.h" // Needed to support old SDKs
#import "vtkCocoaRenderWindowInteractor.h"
#import "vtkCocoaRenderWindow.h"
#import "vtkCommand.h"
#import "vtkObjectFactory.h"
#ifdef VTK_USE_TDX
#import "vtkTDxMacDevice.h"
#endif
//----------------------------------------------------------------------------
vtkStandardNewMacro(vtkCocoaRenderWindowInteractor);
//----------------------------------------------------------------------------
void (*vtkCocoaRenderWindowInteractor::ClassExitMethod)(void *) = (void (*)(void *))NULL;
void *vtkCocoaRenderWindowInteractor::ClassExitMethodArg = (void *)NULL;
void (*vtkCocoaRenderWindowInteractor::ClassExitMethodArgDelete)(void *) = (void (*)(void *))NULL;
//----------------------------------------------------------------------------
// This is a private class and an implementation detail, do not use it.
//----------------------------------------------------------------------------
// As vtk is both crossplatform and a library, we don't know if it is being
// used in a 'regular Cocoa application' or as a 'pure vtk application'.
// By the former we mean a regular Cocoa application that happens to have
// a vtkCocoaGLView, by the latter we mean an application that only uses
// VTK APIs (which happen to use Cocoa as an implementation detail).
// Specifically, we can't know if NSApplicationMain() was ever called
// (which is usually done in main()), nor whether the NSApplication exists.
//
// Any code that uses Cocoa needs to be sure that NSAutoreleasePools are
// correctly setup. Specifically, an NSAutoreleasePool needs to exist before
// any Cocoa objects are autoreleased, which may happen as a side effect of
// pretty much any Cocoa use. Once we start the event loop by calling
// either NSApplicationMain() or [NSApp run] then a new pool will be created
// for every event. In a 'regular Cocoa application' NSApplicationMain() is
// called at the start of main() and so we know a pool will be in place before
// any vtk code is used. But in a 'pure vtk application' the event loop does
// not start until [NSApp run] is called by this class's Start() method. The
// problem thus is that other VTK code may be called before Start() and that
// code may call Cocoa and thus autorelease objects with no autorelease pool
// in place. The (ugly) solution is to create a 'pool of last resort' so
// that we know a pool is always in place.
// See: <http://lists.apple.com/archives/cocoa-dev/2006/Sep/msg00222.html>
//
// With garbage collection (GC), autorelease pools are a thing of the past,
// and this hack is not needed. However, Obj-C code can be compiled in
// 1 of 4 memory management modes: Manual Retain Release (MRR), GC supported,
// GC required, or Automatic Reference Counting (ARC). Library code like
// VTK should work with all 4; currently all are supported except ARC.
class vtkEarlyCocoaSetup
{
public:
vtkEarlyCocoaSetup()
{
this->CreatePoolOfLastResort();
}
virtual ~vtkEarlyCocoaSetup()
{
this->DestroyPoolOfLastResort();
}
protected:
void DestroyPoolOfLastResort()
{
[Pool drain];
Pool = nil;
}
void CreatePoolOfLastResort()
{
Pool = [[NSAutoreleasePool alloc] init];
}
private:
NSAutoreleasePool *Pool;
};
// We create a global/static instance of this class to ensure that we have an
// autorelease pool before main() starts (the C++ constructor for a global
// object runs before main). Note: I am unable to find a place to delete this
// object safely, but having it around for the lifetime of the process is ok.
static vtkEarlyCocoaSetup * gEarlyCocoaSetup = new vtkEarlyCocoaSetup();
//----------------------------------------------------------------------------
// This is a private class and an implementation detail, do not use it.
//----------------------------------------------------------------------------
@interface vtkCocoaTimer : NSObject
{
@private
NSTimer *_timer;
vtkCocoaRenderWindowInteractor *_interactor;
int _timerId;
}
- (id)initWithInteractor:(vtkCocoaRenderWindowInteractor *)myInteractor
timerId:(int)myTimerId;
- (void)startTimerWithInterval:(NSTimeInterval)interval
repeating:(BOOL)repeating;
- (void)stopTimer;
- (void)timerFired:(NSTimer *)myTimer;
@end
//----------------------------------------------------------------------------
@implementation vtkCocoaTimer
//----------------------------------------------------------------------------
- (id)initWithInteractor:(vtkCocoaRenderWindowInteractor *)myInteractor
timerId:(int)myTimerId
{
self = [super init];
if (self)
{
_interactor = myInteractor;
_timerId = myTimerId;
}
return self;
}
//----------------------------------------------------------------------------
- (void)timerFired:(NSTimer *)myTimer
{
(void)myTimer;
_interactor->InvokeEvent(vtkCommand::TimerEvent, &_timerId);
}
//----------------------------------------------------------------------------
- (void)startTimerWithInterval:(NSTimeInterval)interval
repeating:(BOOL)repeating
{
_timer = [NSTimer timerWithTimeInterval:interval
target:self
selector:@selector(timerFired:)
userInfo:nil
repeats:repeating];
#if !VTK_OBJC_IS_ARC
[_timer retain];
#endif
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:_timer forMode:NSDefaultRunLoopMode];
[runLoop addTimer:_timer forMode:NSEventTrackingRunLoopMode];
}
//----------------------------------------------------------------------------
- (void)stopTimer
{
[_timer invalidate];
#if !VTK_OBJC_IS_ARC
[_timer release];
#endif
_timer = nil;
}
@end
//----------------------------------------------------------------------------
// Private
static void VTKStartNSApplicationEventLoop(void)
{
// Start the NSApplication's run loop
NSApplication *application = [NSApplication sharedApplication];
[application run];
}
//----------------------------------------------------------------------------
// Private
static void VTKStopNSApplicationEventLoop(void)
{
// Stop the current run loop. Do not terminate as it will not return to
// main. However, the stop message puts the run loop asleep. Let's send
// some event to wake it up so it can get out of the loop.
NSApplication *application = [NSApplication sharedApplication];
[application stop:application];
NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
location:NSZeroPoint
modifierFlags:0
timestamp:0
windowNumber:-1
context:nil
subtype:0
data1:0
data2:0];
[application postEvent:event atStart:YES];
}
//----------------------------------------------------------------------------
vtkCocoaRenderWindowInteractor::vtkCocoaRenderWindowInteractor()
{
// First, create the cocoa objects manager. The dictionary is empty so
// essentially all objects are initialized to NULL.
NSMutableDictionary *cocoaManager = [NSMutableDictionary dictionary];
// SetCocoaManager works like an Obj-C setter, so do like Obj-C and
// init the ivar to null first.
this->CocoaManager = NULL;
this->SetCocoaManager(reinterpret_cast<void *>(cocoaManager));
[cocoaManager self]; // prevent premature collection under GC.
NSMutableDictionary *timerDictionary = [NSMutableDictionary dictionary];
this->SetTimerDictionary(reinterpret_cast<void *>(timerDictionary));
[timerDictionary self]; // prevent premature collection under GC.
#ifdef VTK_USE_TDX
this->Device=vtkTDxMacDevice::New();
#endif
}
//----------------------------------------------------------------------------
vtkCocoaRenderWindowInteractor::~vtkCocoaRenderWindowInteractor()
{
this->Enabled = 0;
this->SetTimerDictionary(NULL);
this->SetCocoaManager(NULL);
#ifdef VTK_USE_TDX
this->Device->Delete();
#endif
}
//----------------------------------------------------------------------------
void vtkCocoaRenderWindowInteractor::StartEventLoop()
{
VTKStartNSApplicationEventLoop();
}
//----------------------------------------------------------------------------
// Begin processing keyboard strokes.
void vtkCocoaRenderWindowInteractor::Initialize()
{
// make sure we have a RenderWindow and camera
if ( !this->RenderWindow )
{
vtkErrorMacro(<<"No renderer defined!");
return;
}
if (this->Initialized)
{
return;
}
this->Initialized = 1;
// get the info we need from the RenderingWindow
vtkCocoaRenderWindow *renWin = (vtkCocoaRenderWindow *)(this->RenderWindow);
renWin->Start();
int *size = renWin->GetSize();
renWin->GetPosition(); // update values of this->Position[2]
this->Enable();
this->Size[0] = size[0];
this->Size[1] = size[1];
}
//----------------------------------------------------------------------------
void vtkCocoaRenderWindowInteractor::Enable()
{
if (this->Enabled)
{
return;
}
// Set the RenderWindow's interactor so that when the vtkCocoaGLView tries
// to handle events from the OS it will either handle them or ignore them
this->GetRenderWindow()->SetInteractor(this);
#ifdef VTK_USE_TDX
if(this->UseTDx)
{
this->Device->SetInteractor(this);
this->Device->Initialize();
}
#endif
this->Enabled = 1;
this->Modified();
}
//----------------------------------------------------------------------------
void vtkCocoaRenderWindowInteractor::Disable()
{
if (!this->Enabled)
{
return;
}
#ifdef VTK_USE_TDX
if(this->Device->GetInitialized())
{
this->Device->Close();
}
#endif
// Set the RenderWindow's interactor so that when the vtkCocoaGLView tries
// to handle events from the OS it will either handle them or ignore them
this->GetRenderWindow()->SetInteractor(NULL);
this->Enabled = 0;
this->Modified();
}
//----------------------------------------------------------------------------
void vtkCocoaRenderWindowInteractor::TerminateApp()
{
VTKStopNSApplicationEventLoop();
}
//----------------------------------------------------------------------------
int vtkCocoaRenderWindowInteractor::InternalCreateTimer(int timerId,
int timerType, unsigned long duration)
{
BOOL repeating = NO;
if (RepeatingTimer == timerType)
{
repeating = YES;
}
// Create a vtkCocoaTimer and add it to a dictionary using the timerId
// as key, this will let us find the vtkCocoaTimer later by timerId
vtkCocoaTimer *cocoaTimer = [[vtkCocoaTimer alloc] initWithInteractor:this
timerId:timerId];
NSString *timerIdAsStr = [NSString stringWithFormat:@"%i", timerId];
NSMutableDictionary *timerDict = (NSMutableDictionary*)(this->GetTimerDictionary());
[timerDict setObject:cocoaTimer forKey:timerIdAsStr];
[cocoaTimer startTimerWithInterval:((NSTimeInterval)duration/1000.0)
repeating:repeating];
#if !VTK_OBJC_IS_ARC
[cocoaTimer release];
#endif
// In this implementation, timerId and platformTimerId are the same
int platformTimerId = timerId;
return platformTimerId;
}
//----------------------------------------------------------------------------
int vtkCocoaRenderWindowInteractor::InternalDestroyTimer(int platformTimerId)
{
// In this implementation, timerId and platformTimerId are the same;
// but calling this anyway is more correct
int timerId = this->GetVTKTimerId(platformTimerId);
NSString *timerIdAsStr = [NSString stringWithFormat:@"%i", timerId];
NSMutableDictionary *timerDict = (NSMutableDictionary*)(this->GetTimerDictionary());
vtkCocoaTimer* cocoaTimer = [timerDict objectForKey:timerIdAsStr];
if (nil != cocoaTimer)
{
[cocoaTimer stopTimer];
[timerDict removeObjectForKey:timerIdAsStr];
return 1; // success
}
else
{
return 0; // fail
}
}
//----------------------------------------------------------------------------
// Specify the default function to be called when an interactor needs to exit.
// This callback is overridden by an instance ExitMethod that is defined.
void vtkCocoaRenderWindowInteractor::SetClassExitMethod(void (*f)(void *),void *arg)
{
if ( f != vtkCocoaRenderWindowInteractor::ClassExitMethod
|| arg != vtkCocoaRenderWindowInteractor::ClassExitMethodArg)
{
// delete the current arg if there is a delete method
if ((vtkCocoaRenderWindowInteractor::ClassExitMethodArg)
&& (vtkCocoaRenderWindowInteractor::ClassExitMethodArgDelete))
{
(*vtkCocoaRenderWindowInteractor::ClassExitMethodArgDelete)
(vtkCocoaRenderWindowInteractor::ClassExitMethodArg);
}
vtkCocoaRenderWindowInteractor::ClassExitMethod = f;
vtkCocoaRenderWindowInteractor::ClassExitMethodArg = arg;
// no call to this->Modified() since this is a class member function
}
}
//----------------------------------------------------------------------------
// Set the arg delete method. This is used to free user memory.
void vtkCocoaRenderWindowInteractor::SetClassExitMethodArgDelete(void (*f)(void *))
{
if (f != vtkCocoaRenderWindowInteractor::ClassExitMethodArgDelete)
{
vtkCocoaRenderWindowInteractor::ClassExitMethodArgDelete = f;
// no call to this->Modified() since this is a class member function
}
}
//----------------------------------------------------------------------------
void vtkCocoaRenderWindowInteractor::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
}
//----------------------------------------------------------------------------
void vtkCocoaRenderWindowInteractor::ExitCallback()
{
if (this->HasObserver(vtkCommand::ExitEvent))
{
this->InvokeEvent(vtkCommand::ExitEvent,NULL);
}
else if (this->ClassExitMethod)
{
(*this->ClassExitMethod)(this->ClassExitMethodArg);
}
this->TerminateApp();
}
//----------------------------------------------------------------------------
void vtkCocoaRenderWindowInteractor::SetTimerDictionary(void *dictionary)
{
if (dictionary != NULL)
{
NSMutableDictionary *manager =
reinterpret_cast<NSMutableDictionary *>(this->GetCocoaManager());
[manager setObject:reinterpret_cast<id>(dictionary)
forKey:@"TimerDictionary"];
}
else
{
NSMutableDictionary *manager =
reinterpret_cast<NSMutableDictionary *>(this->GetCocoaManager());
[manager removeObjectForKey:@"TimerDictionary"];
}
}
//----------------------------------------------------------------------------
void *vtkCocoaRenderWindowInteractor::GetTimerDictionary()
{
NSMutableDictionary *manager =
reinterpret_cast<NSMutableDictionary *>(this->GetCocoaManager());
return reinterpret_cast<void *>([manager objectForKey:@"TimerDictionary"]);
}
//----------------------------------------------------------------------------
void vtkCocoaRenderWindowInteractor::SetCocoaManager(void *manager)
{
NSMutableDictionary *currentCocoaManager =
reinterpret_cast<NSMutableDictionary *>(this->CocoaManager);
NSMutableDictionary *newCocoaManager =
reinterpret_cast<NSMutableDictionary *>(manager);
if (currentCocoaManager != newCocoaManager)
{
// Why not use Cocoa's retain and release? Without garbage collection
// (GC), the two are equivalent anyway because of 'toll free bridging',
// so no problem there. With GC, retain and release do nothing, but
// CFRetain and CFRelease still manipulate the internal reference count.
// We need that, since we are not using strong references (we don't want
// it collected out from under us!).
if (currentCocoaManager)
{
CFRelease(currentCocoaManager);
}
if (newCocoaManager)
{
this->CocoaManager = const_cast<void *>(CFRetain (newCocoaManager));
}
else
{
this->CocoaManager = NULL;
}
}
}
//----------------------------------------------------------------------------
void *vtkCocoaRenderWindowInteractor::GetCocoaManager()
{
return this->CocoaManager;
}