// @(#)root/graf2d:$Id$ // Author: Timur Pocheptsov 29/11/2011 /************************************************************************* * Copyright (C) 1995-2012, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ #define DEBUG_ROOT_COCOA //#define NDEBUG #ifdef DEBUG_ROOT_COCOA #include #endif #include #include #include #include #include "ROOTApplicationDelegate.h" #include "ROOTOpenGLView.h" #include "CocoaPrivate.h" #include "QuartzWindow.h" #include "CocoaUtils.h" namespace ROOT { namespace MacOSX { namespace Details { //______________________________________________________________________________ CocoaPrivate::CocoaPrivate() : fCurrentDrawableID(GetRootWindowID() + 1), //Any real window has id > rootID. //0 is also used by some X11 functions as None. fFreeGLContextID(1), fApplicationDelegate([[ROOTApplicationDelegate alloc] init]) { //Init NSApplication, if it was not done yet. Util::AutoreleasePool pool; [NSApplication sharedApplication]; } //______________________________________________________________________________ CocoaPrivate::~CocoaPrivate() { } //______________________________________________________________________________ Window_t CocoaPrivate::GetRootWindowID()const { //First I had root ID == 0, but this is None in X11 and //it can be used by ROOT, for example, I had trouble with //gClient able to found TGWindow for None - crash! return 1; } //______________________________________________________________________________ bool CocoaPrivate::IsRootWindow(Window_t windowID)const { return windowID == GetRootWindowID(); } //______________________________________________________________________________ Drawable_t CocoaPrivate::RegisterDrawable(NSObject *nsObj) { //Return integer identifier for a new "drawable" (like in X11) if (fCurrentDrawableID == 999)//I have to skip this, many thanks to ROOT who uses 999 as "all windows". ++fCurrentDrawableID; Drawable_t newID = fCurrentDrawableID; if (fFreeDrawableIDs.size()) { newID = fFreeDrawableIDs.back(); fFreeDrawableIDs.pop_back(); } else fCurrentDrawableID++; assert(fDrawables.find(newID) == fDrawables.end() && "RegisterDrawable, id for new drawable is still in use"); fDrawables[newID] = nsObj; return newID; } //______________________________________________________________________________ NSObject *CocoaPrivate::GetDrawable(Drawable_t drawableID)const { const_drawable_iterator drawableIter = fDrawables.find(drawableID); #ifdef DEBUG_ROOT_COCOA if (drawableIter == fDrawables.end()) { NSLog(@"Fatal error: requested non-existing drawable %lu", drawableID); //We do not care about efficiency, ROOT's gonna die on assert :) std::vector::const_iterator deletedDrawable = std::find(fFreeDrawableIDs.begin(), fFreeDrawableIDs.end(), drawableID); if (deletedDrawable != fFreeDrawableIDs.end()) { NSLog(@"This drawable was deleted already"); } else { NSLog(@"This drawable not found among allocated/deleted drawables"); } } #endif assert(drawableIter != fDrawables.end() && "GetDrawable, non-existing drawable requested"); return drawableIter->second.Get(); } //______________________________________________________________________________ NSObject *CocoaPrivate::GetWindow(Window_t windowID)const { const_drawable_iterator winIter = fDrawables.find(windowID); #ifdef DEBUG_ROOT_COCOA if (winIter == fDrawables.end()) { NSLog(@"Fatal error: requested non-existing drawable %lu", windowID); //We do not care about efficiency, ROOT's gonna die on assert :) std::vector::const_iterator deletedDrawable = std::find(fFreeDrawableIDs.begin(), fFreeDrawableIDs.end(), windowID); if (deletedDrawable != fFreeDrawableIDs.end()) { NSLog(@"This window was deleted already"); } else { NSLog(@"This window not found among allocated/deleted drawables"); } } #endif assert(winIter != fDrawables.end() && "GetWindow, non-existing window requested"); return (NSObject *)winIter->second.Get(); } //______________________________________________________________________________ void CocoaPrivate::DeleteDrawable(Drawable_t drawableID) { drawable_iterator drawableIter = fDrawables.find(drawableID); assert(drawableIter != fDrawables.end() && "DeleteDrawable, non existing drawableID"); //Probably, I'll need some additional cleanup here later. Now just delete NSObject and //reuse its id. NSObject * const base = drawableIter->second.Get(); if ([base isKindOfClass : [QuartzView class]]) { [(QuartzView *)base removeFromSuperview]; ((QuartzView *)base).fParentView = nil; } else if ([base isKindOfClass : [QuartzWindow class]]) { QuartzWindow *qw = (QuartzWindow *)base; qw.fContentView.fParentView = nil; [qw.fContentView removeFromSuperview]; qw.contentView = nil; qw.fIsDeleted = YES; //Remove transient windows? /* const Util::NSScopeGuard children([[qw childWindows] copy]); for (QuartzWindow *child in children.Get()) { child.fMainWindow = nil; [qw removeChildWindow : child]; } */ if (qw.fMainWindow) { [qw.fMainWindow removeChildWindow : qw]; qw.fMainWindow = nil; } } // fFreeDrawableIDs.push_back(drawableID); fDrawables.erase(drawableIter);//StrongReference should do work here. } //______________________________________________________________________________ Handle_t CocoaPrivate::RegisterGLContext(NSOpenGLContext *glContext) { assert(fGLContextToHandle.find(glContext) == fGLContextToHandle.end() && "RegisterGLContext, context was registered already"); //Strong es-guarantee guarantee - if we have an exception, everything is rolled-back. bool contextInserted = false; try { fHandleToGLContext[fFreeGLContextID] = glContext; contextInserted = true; fGLContextToHandle[glContext] = fFreeGLContextID; } catch (const std::exception &) {//bad alloc in one of two insertions. if (contextInserted) fHandleToGLContext.erase(fHandleToGLContext.find(fFreeGLContextID)); throw; } return fFreeGLContextID++; } //______________________________________________________________________________ void CocoaPrivate::DeleteGLContext(Handle_t contextID) { assert(fHandleToGLContext.find(contextID) != fHandleToGLContext.end() && "DeleteGLContext, bad context id"); handle2ctx_map::iterator h2cIt = fHandleToGLContext.find(contextID); ctx2handle_map::iterator c2hIt = fGLContextToHandle.find(h2cIt->second.Get()); assert(c2hIt != fGLContextToHandle.end() && "DeleteGLContext, inconsistent context map"); fGLContextToHandle.erase(c2hIt); fHandleToGLContext.erase(h2cIt);//RAII does work here. } //______________________________________________________________________________ NSOpenGLContext *CocoaPrivate::GetGLContextForHandle(Handle_t ctxID) { if (fHandleToGLContext.find(ctxID) == fHandleToGLContext.end()) return nil; return fHandleToGLContext[ctxID].Get(); } //______________________________________________________________________________ Handle_t CocoaPrivate::GetHandleForGLContext(NSOpenGLContext *glContext) { if (fGLContextToHandle.find(glContext) == fGLContextToHandle.end()) return Handle_t(); return fGLContextToHandle[glContext]; } //______________________________________________________________________________ void CocoaPrivate::SetFakeGLWindow(QuartzWindow *fakeWin) { fFakeGLWindow.Reset(fakeWin); } //______________________________________________________________________________ QuartzWindow *CocoaPrivate::GetFakeGLWindow() { return fFakeGLWindow.Get(); } //______________________________________________________________________________ void CocoaPrivate::ReplaceDrawable(Drawable_t drawableID, NSObject *nsObj) { drawable_iterator drawableIter = fDrawables.find(drawableID); assert(drawableIter != fDrawables.end() && "ReplaceDrawable, can not replace non existing drawable"); drawableIter->second.Reset(nsObj); } }//Details }//MacOSX }//ROOT