1// Created on: 2012-11-12
2// Created by: Kirill Gavrilov
3// Copyright (c) 2012-2014 OPEN CASCADE SAS
4//
5// This file is part of Open CASCADE Technology software library.
6//
7// This library is free software; you can redistribute it and/or modify it under
8// the terms of the GNU Lesser General Public License version 2.1 as published
9// by the Free Software Foundation, with special exception defined in the file
10// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11// distribution for complete text of the license and disclaimer of any warranty.
12//
13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement.
15
16#if defined(__APPLE__) && !defined(HAVE_XLIB)
17
18#ifndef GL_GLEXT_LEGACY
19#define GL_GLEXT_LEGACY // To prevent inclusion of system glext.h on Mac OS X 10.6.8
20#endif
21
22// macOS 10.4 deprecated OpenGL framework - suppress useless warnings
23#define GL_SILENCE_DEPRECATION
24
25#import <TargetConditionals.h>
26
27#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
28  #import <UIKit/UIKit.h>
29#else
30  #import <Cocoa/Cocoa.h>
31
32#if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7)
33@interface NSView (LionAPI)
34- (NSSize )convertSizeToBacking: (NSSize )theSize;
35@end
36#endif
37
38#endif
39
40#include <OpenGl_Window.hxx>
41#include <OpenGl_FrameBuffer.hxx>
42
43#include <OpenGl_Context.hxx>
44#include <Aspect_GraphicDeviceDefinitionError.hxx>
45#include <Cocoa_LocalPool.hxx>
46#include <TCollection_AsciiString.hxx>
47#include <TCollection_ExtendedString.hxx>
48
49#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
50  //
51#else
52  #include <OpenGL/CGLRenderers.h>
53#endif
54
55// =======================================================================
56// function : OpenGl_Window
57// purpose  :
58// =======================================================================
59OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
60                              const Handle(Aspect_Window)&  thePlatformWindow,
61                              Aspect_RenderingContext       theGContext,
62                              const Handle(OpenGl_Caps)&    theCaps,
63                              const Handle(OpenGl_Context)& theShareCtx)
64: myGlContext (new OpenGl_Context (theCaps)),
65  myOwnGContext (theGContext == 0),
66  myPlatformWindow (thePlatformWindow),
67#if defined(__APPLE__) && defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
68  myUIView (NULL),
69#endif
70  mySwapInterval (theCaps->swapInterval)
71{
72  (void )theDriver;
73  myPlatformWindow->Size (myWidth, myHeight);
74
75#if defined(__APPLE__)
76  myWidthPt  = myWidth;
77  myHeightPt = myHeight;
78#endif
79
80#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
81  EAGLContext* aGLContext = theGContext;
82  if (aGLContext == NULL)
83  {
84    void* aViewPtr = (void* )myPlatformWindow->NativeHandle();
85
86    myUIView = (__bridge UIView* )aViewPtr;
87    CAEAGLLayer* anEaglLayer = (CAEAGLLayer* )myUIView.layer;
88    anEaglLayer.opaque = TRUE;
89    anEaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
90                                        [NSNumber numberWithBool: FALSE], kEAGLDrawablePropertyRetainedBacking,
91                                        kEAGLColorFormatRGBA8,            kEAGLDrawablePropertyColorFormat,
92                                        NULL];
93
94    aGLContext = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES3];
95    if (aGLContext == NULL
96    || ![EAGLContext setCurrentContext: aGLContext])
97    {
98      aGLContext = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES2];
99
100      if (aGLContext == NULL
101      || ![EAGLContext setCurrentContext: aGLContext])
102      {
103        TCollection_AsciiString aMsg ("OpenGl_Window::CreateWindow: EAGLContext creation failed");
104        throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
105        return;
106      }
107    }
108
109    myGlContext->Init (aGLContext, Standard_False);
110  }
111  else
112  {
113    if (![EAGLContext setCurrentContext: aGLContext])
114    {
115      TCollection_AsciiString aMsg ("OpenGl_Window::CreateWindow: EAGLContext can not be assigned");
116      throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
117      return;
118    }
119
120    myGlContext->Init (aGLContext, Standard_False);
121  }
122#else
123
124  Cocoa_LocalPool aLocalPool;
125
126  // all GL context within one OpenGl_GraphicDriver should be shared!
127  NSOpenGLContext* aGLCtxShare = theShareCtx.IsNull() ? NULL : theShareCtx->myGContext;
128  NSOpenGLContext* aGLContext  = theGContext;
129  bool isCore = false;
130  if (aGLContext == NULL)
131  {
132    NSOpenGLPixelFormatAttribute anAttribs[32] = {};
133    Standard_Integer aLastAttrib = 0;
134    //anAttribs[aLastAttrib++] = NSOpenGLPFAColorSize;    anAttribs[aLastAttrib++] = 32,
135    anAttribs[aLastAttrib++] = NSOpenGLPFADepthSize;    anAttribs[aLastAttrib++] = 24;
136    anAttribs[aLastAttrib++] = NSOpenGLPFAStencilSize;  anAttribs[aLastAttrib++] = 8;
137    anAttribs[aLastAttrib++] = NSOpenGLPFADoubleBuffer;
138    if (theCaps->contextNoAccel)
139    {
140      anAttribs[aLastAttrib++] = NSOpenGLPFARendererID;
141      anAttribs[aLastAttrib++] = (NSOpenGLPixelFormatAttribute )kCGLRendererGenericFloatID;
142    }
143    else
144    {
145      anAttribs[aLastAttrib++] = NSOpenGLPFAAccelerated;
146    }
147    anAttribs[aLastAttrib] = 0;
148    const Standard_Integer aLastMainAttrib = aLastAttrib;
149    Standard_Integer aTryCore   = 0;
150    Standard_Integer aTryStereo = 0;
151    for (aTryCore = 1; aTryCore >= 0; --aTryCore)
152    {
153      aLastAttrib = aLastMainAttrib;
154      if (aTryCore == 1)
155      {
156        if (theCaps->contextCompatible)
157        {
158          continue;
159        }
160
161        // supported since OS X 10.7+
162        anAttribs[aLastAttrib++] = 99;     // NSOpenGLPFAOpenGLProfile
163        anAttribs[aLastAttrib++] = 0x3200; // NSOpenGLProfileVersion3_2Core
164      }
165
166      for (aTryStereo = 1; aTryStereo >= 0; --aTryStereo)
167      {
168        if (aTryStereo == 1)
169        {
170          if (!theCaps->contextStereo)
171          {
172            continue;
173          }
174
175          // deprecated since macOS 10.12 without replacement
176          Standard_DISABLE_DEPRECATION_WARNINGS
177          anAttribs[aLastAttrib++] = NSOpenGLPFAStereo;
178          Standard_ENABLE_DEPRECATION_WARNINGS
179        }
180
181        anAttribs[aLastAttrib] = 0;
182
183        NSOpenGLPixelFormat* aGLFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes: anAttribs] autorelease];
184        aGLContext = [[NSOpenGLContext alloc] initWithFormat: aGLFormat
185                                                shareContext: aGLCtxShare];
186        if (aGLContext != NULL)
187        {
188          break;
189        }
190      }
191
192      if (aGLContext != NULL)
193      {
194        break;
195      }
196    }
197
198    if (aGLContext == NULL)
199    {
200      TCollection_AsciiString aMsg ("OpenGl_Window::CreateWindow: NSOpenGLContext creation failed");
201      throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
202      return;
203    }
204
205    if (aTryStereo == 0
206     && theCaps->contextStereo)
207    {
208      TCollection_ExtendedString aMsg("OpenGl_Window::CreateWindow: QuadBuffer is unavailable!");
209      myGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, 0, GL_DEBUG_SEVERITY_LOW, aMsg);
210    }
211    if (aTryCore == 0
212    && !theCaps->contextCompatible)
213    {
214      TCollection_ExtendedString aMsg("OpenGl_Window::CreateWindow: core profile creation failed.");
215      myGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW, aMsg);
216    }
217
218    NSView* aView = (NSView* )myPlatformWindow->NativeHandle();
219  Standard_DISABLE_DEPRECATION_WARNINGS
220    [aGLContext setView: aView];
221  Standard_ENABLE_DEPRECATION_WARNINGS
222    isCore = (aTryCore == 1);
223  }
224
225  myGlContext->Init (aGLContext, isCore);
226#endif
227
228  myGlContext->Share (theShareCtx);
229  myGlContext->SetSwapInterval (mySwapInterval);
230  Init();
231}
232
233// =======================================================================
234// function : ~OpenGl_Window
235// purpose  :
236// =======================================================================
237OpenGl_Window::~OpenGl_Window()
238{
239  if (!myOwnGContext
240   ||  myGlContext.IsNull())
241  {
242    myGlContext.Nullify();
243    return;
244  }
245
246#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
247  myGlContext.Nullify();
248  [EAGLContext setCurrentContext: NULL];
249  myUIView = NULL;
250#else
251  NSOpenGLContext* aGLCtx = myGlContext->myGContext;
252  myGlContext.Nullify();
253
254  [NSOpenGLContext clearCurrentContext];
255  [aGLCtx clearDrawable];
256  [aGLCtx release];
257#endif
258}
259
260// =======================================================================
261// function : Resize
262// purpose  : call_subr_resize
263// =======================================================================
264void OpenGl_Window::Resize()
265{
266  // If the size is not changed - do nothing
267  Standard_Integer aWidthPt  = 0;
268  Standard_Integer aHeightPt = 0;
269  myPlatformWindow->Size (aWidthPt, aHeightPt);
270  if (myWidthPt  == aWidthPt
271   && myHeightPt == aHeightPt)
272  {
273  #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
274    return;
275  #else
276    // check backing store change (moving to another screen)
277    NSOpenGLContext* aGLCtx = myGlContext->myGContext;
278  Standard_DISABLE_DEPRECATION_WARNINGS
279    NSView* aView = [aGLCtx view];
280  Standard_ENABLE_DEPRECATION_WARNINGS
281    if (![aView respondsToSelector: @selector(convertSizeToBacking:)])
282    {
283      return;
284    }
285
286    NSRect aBounds = [aView bounds];
287    NSSize aRes    = [aView convertSizeToBacking: aBounds.size];
288    if (myWidth  == Standard_Integer(aRes.width)
289     && myHeight == Standard_Integer(aRes.height))
290    {
291      return;
292    }
293  #endif
294  }
295
296  myWidthPt  = aWidthPt;
297  myHeightPt = aHeightPt;
298
299  Init();
300}
301
302// =======================================================================
303// function : Init
304// purpose  :
305// =======================================================================
306void OpenGl_Window::Init()
307{
308  if (!Activate())
309  {
310    return;
311  }
312
313#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
314  Handle(OpenGl_FrameBuffer) aDefFbo = myGlContext->SetDefaultFrameBuffer (NULL);
315  if (!aDefFbo.IsNull())
316  {
317    aDefFbo->Release (myGlContext.operator->());
318  }
319  else
320  {
321    aDefFbo = new OpenGl_FrameBuffer();
322  }
323
324  if (myOwnGContext)
325  {
326    EAGLContext* aGLCtx      = myGlContext->myGContext;
327    CAEAGLLayer* anEaglLayer = (CAEAGLLayer* )myUIView.layer;
328    GLuint aWinRBColor = 0;
329    ::glGenRenderbuffers (1, &aWinRBColor);
330    ::glBindRenderbuffer (GL_RENDERBUFFER, aWinRBColor);
331    [aGLCtx renderbufferStorage: GL_RENDERBUFFER fromDrawable: anEaglLayer];
332    ::glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,  &myWidth);
333    ::glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &myHeight);
334    ::glBindRenderbuffer (GL_RENDERBUFFER, 0);
335
336    if (!aDefFbo->InitWithRB (myGlContext, Graphic3d_Vec2i (myWidth, myHeight), GL_RGBA8, GL_DEPTH24_STENCIL8, aWinRBColor))
337    {
338      TCollection_AsciiString aMsg ("OpenGl_Window::CreateWindow: default FBO creation failed");
339      throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
340      return;
341    }
342  }
343  else
344  {
345    if (!aDefFbo->InitWrapper (myGlContext))
346    {
347      TCollection_AsciiString aMsg ("OpenGl_Window::CreateWindow: default FBO wrapper creation failed");
348      throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
349      return;
350    }
351
352    myWidth  = aDefFbo->GetVPSizeX();
353    myHeight = aDefFbo->GetVPSizeY();
354  }
355  myGlContext->SetDefaultFrameBuffer (aDefFbo);
356  aDefFbo->BindBuffer (myGlContext);
357  aDefFbo.Nullify();
358#else
359  NSOpenGLContext* aGLCtx  = myGlContext->myGContext;
360Standard_DISABLE_DEPRECATION_WARNINGS
361  NSView*          aView   = [aGLCtx view];
362Standard_ENABLE_DEPRECATION_WARNINGS
363  NSRect           aBounds = [aView bounds];
364
365  // we should call this method each time when window is resized
366  [aGLCtx update];
367
368  if ([aView respondsToSelector: @selector(convertSizeToBacking:)])
369  {
370    NSSize aRes = [aView convertSizeToBacking: aBounds.size];
371    myWidth  = Standard_Integer(aRes.width);
372    myHeight = Standard_Integer(aRes.height);
373  }
374  else
375  {
376    myWidth  = Standard_Integer(aBounds.size.width);
377    myHeight = Standard_Integer(aBounds.size.height);
378  }
379  myWidthPt  = Standard_Integer(aBounds.size.width);
380  myHeightPt = Standard_Integer(aBounds.size.height);
381#endif
382
383  ::glDisable (GL_DITHER);
384  ::glDisable (GL_SCISSOR_TEST);
385  ::glViewport (0, 0, myWidth, myHeight);
386#if !defined(GL_ES_VERSION_2_0)
387  ::glDrawBuffer (GL_BACK);
388  if (myGlContext->core11ffp != NULL)
389  {
390    ::glMatrixMode (GL_MODELVIEW);
391  }
392#endif
393}
394
395// =======================================================================
396// function : SetSwapInterval
397// purpose  :
398// =======================================================================
399void OpenGl_Window::SetSwapInterval (Standard_Boolean theToForceNoSync)
400{
401  const Standard_Integer aSwapInterval = theToForceNoSync ? 0 : myGlContext->caps->swapInterval;
402  if (mySwapInterval != aSwapInterval)
403  {
404    mySwapInterval = aSwapInterval;
405    myGlContext->SetSwapInterval (mySwapInterval);
406  }
407}
408
409#endif // __APPLE__
410