6bc07f76d7a7c5b9f16ac6742fcd993fa4e3532f
[vuplus_xbmc] / xbmc / osx / IOSEAGLView.mm
1 /*
2  *      Copyright (C) 2010-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 //hack around problem with xbmc's typedef int BOOL
22 // and obj-c's typedef unsigned char BOOL
23 #define BOOL XBMC_BOOL 
24 #include <sys/resource.h>
25 #include <signal.h>
26 #include <stdio.h>
27
28 #include "system.h"
29 #include "AdvancedSettings.h"
30 #include "FileItem.h"
31 #include "Application.h"
32 #include "ApplicationMessenger.h"
33 #include "WindowingFactory.h"
34 #include "VideoReferenceClock.h"
35 #include "utils/log.h"
36 #include "utils/TimeUtils.h"
37 #include "Util.h"
38 #undef BOOL
39
40 #import <QuartzCore/QuartzCore.h>
41
42 #import <OpenGLES/ES2/gl.h>
43 #import <OpenGLES/ES2/glext.h>
44 #import "IOSEAGLView.h"
45 #import "XBMCController.h"
46 #import "IOSScreenManager.h"
47 #import "AutoPool.h"
48 #import "DarwinUtils.h"
49 #import "XBMCDebugHelpers.h"
50
51 //--------------------------------------------------------------
52 @interface IOSEAGLView (PrivateMethods)
53 - (void) setContext:(EAGLContext *)newContext;
54 - (void) createFramebuffer;
55 - (void) deleteFramebuffer;
56 - (void) runDisplayLink;
57 @end
58
59 @implementation IOSEAGLView
60 @synthesize animating;
61 @synthesize xbmcAlive;
62 @synthesize pause;
63 @synthesize currentScreen;
64 @synthesize framebufferResizeRequested;
65
66 // You must implement this method
67 + (Class) layerClass
68 {
69   return [CAEAGLLayer class];
70 }
71 //--------------------------------------------------------------
72 - (void) resizeFrameBuffer
73 {
74   CGRect frame = [IOSScreenManager getLandscapeResolution: currentScreen]; 
75   CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[self layer];  
76   //allow a maximum framebuffer size of 1080p
77   //needed for tvout on iPad3/4 and iphone4/5 and maybe AppleTV3
78   if(frame.size.width * frame.size.height > 2073600)
79     return;
80   //resize the layer - ios will delay this
81   //and call layoutSubviews when its done with resizing
82   //so the real framebuffer resize is done there then ...
83   if(framebufferWidth != frame.size.width ||
84      framebufferHeight != frame.size.height )
85   {
86     framebufferResizeRequested = TRUE;
87     [eaglLayer setFrame:frame];
88   }
89 }
90
91 - (void)layoutSubviews
92 {
93   if(framebufferResizeRequested)
94   {
95     framebufferResizeRequested = FALSE;
96     [self deinitDisplayLink];
97     [self deleteFramebuffer];
98     [self createFramebuffer];
99     [self setFramebuffer];  
100     [self initDisplayLink];
101   }
102 }
103
104 - (CGFloat) getScreenScale:(UIScreen *)screen
105 {
106   CGFloat ret = 1.0;
107   if ([screen respondsToSelector:@selector(scale)])
108   {    
109     // atv2 reports 0.0 for scale - thats why we check this here
110     // normal other iDevices report 1.0 here
111     // retina devices report 2.0 here
112     // this info is true as of 19.3.2012.
113     if([screen scale] > 1.0)
114     {
115       ret = [screen scale];
116     }
117     
118     //if no retina display scale detected yet -
119     //ensure retina resolution on supported devices mainScreen
120     //even on older iOS SDKs
121     if (ret == 1.0 && screen == [UIScreen mainScreen] && DarwinHasRetina())
122     {
123       ret = 2.0;//all retina devices have a scale factor of 2.0
124     }
125   }
126   return ret;
127 }
128
129 - (void) setScreen:(UIScreen *)screen withFrameBufferResize:(BOOL)resize;
130 {
131   CGFloat scaleFactor = 1.0;
132   CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[self layer];
133
134   currentScreen = screen;
135   scaleFactor = [self getScreenScale: currentScreen];
136
137   //this will activate retina on supported devices
138   [eaglLayer setContentsScale:scaleFactor];
139   [self setContentScaleFactor:scaleFactor];
140   if(resize)
141   {
142     [self resizeFrameBuffer];
143   }
144 }
145
146 //--------------------------------------------------------------
147 - (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
148 {
149   //PRINT_SIGNATURE();
150   framebufferResizeRequested = FALSE;
151   if ((self = [super initWithFrame:frame]))
152   {
153     // Get the layer
154     CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
155     //set screen, handlescreenscale
156     //and set frame size
157     [self setScreen:screen withFrameBufferResize:FALSE];
158
159     eaglLayer.opaque = TRUE;
160     eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
161       [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
162       kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
163       nil];
164                 
165     EAGLContext *aContext = [[EAGLContext alloc] 
166       initWithAPI:kEAGLRenderingAPIOpenGLES2];
167     
168     if (!aContext)
169       ELOG(@"Failed to create ES context");
170     else if (![EAGLContext setCurrentContext:aContext])
171       ELOG(@"Failed to set ES context current");
172     
173     self.context = aContext;
174     [aContext release];
175
176     animating = FALSE;
177     xbmcAlive = FALSE;
178     pause = FALSE;
179     [self setContext:context];
180     [self createFramebuffer];
181     [self setFramebuffer];
182
183                 displayLink = nil;
184   }
185
186   return self;
187 }
188
189 //--------------------------------------------------------------
190 - (void) dealloc
191 {
192   //PRINT_SIGNATURE();
193   [self deleteFramebuffer];    
194   [context release];
195   
196   [super dealloc];
197 }
198
199 //--------------------------------------------------------------
200 - (EAGLContext *)context
201 {
202   return context;
203 }
204 //--------------------------------------------------------------
205 - (void)setContext:(EAGLContext *)newContext
206 {
207   PRINT_SIGNATURE();
208   if (context != newContext)
209   {
210     [self deleteFramebuffer];
211     
212     [context release];
213     context = [newContext retain];
214     
215     [EAGLContext setCurrentContext:nil];
216   }
217 }
218
219 //--------------------------------------------------------------
220 - (void)createFramebuffer
221 {
222   if (context && !defaultFramebuffer)
223   {
224     //PRINT_SIGNATURE();
225     [EAGLContext setCurrentContext:context];
226     
227     // Create default framebuffer object.
228     glGenFramebuffers(1, &defaultFramebuffer);
229     glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
230     
231     // Create color render buffer and allocate backing store.
232     glGenRenderbuffers(1, &colorRenderbuffer);
233     glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
234     [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
235     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth);
236     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight);
237     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
238
239     glGenRenderbuffers(1, &depthRenderbuffer);
240     glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
241     glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, framebufferWidth, framebufferHeight);
242     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
243     
244     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
245       ELOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
246   }
247 }
248 //--------------------------------------------------------------
249 - (void) deleteFramebuffer
250 {
251   if (context && !pause)
252   {
253     PRINT_SIGNATURE();
254     [EAGLContext setCurrentContext:context];
255     
256     if (defaultFramebuffer)
257     {
258       glDeleteFramebuffers(1, &defaultFramebuffer);
259       defaultFramebuffer = 0;
260     }
261     
262     if (colorRenderbuffer)
263     {
264       glDeleteRenderbuffers(1, &colorRenderbuffer);
265       colorRenderbuffer = 0;
266     }
267
268     if (depthRenderbuffer)
269     {
270       glDeleteRenderbuffers(1, &depthRenderbuffer);
271       depthRenderbuffer = 0;
272     }
273   }
274 }
275 //--------------------------------------------------------------
276 - (void) setFramebuffer
277 {
278   if (context && !pause)
279   {
280     if ([EAGLContext currentContext] != context)
281       [EAGLContext setCurrentContext:context];
282     
283     glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
284     
285     if(framebufferHeight > framebufferWidth) {
286       glViewport(0, 0, framebufferHeight, framebufferWidth);
287       glScissor(0, 0, framebufferHeight, framebufferWidth);
288     } 
289     else
290     {
291       glViewport(0, 0, framebufferWidth, framebufferHeight);
292       glScissor(0, 0, framebufferWidth, framebufferHeight);
293     }
294   }
295 }
296 //--------------------------------------------------------------
297 - (bool) presentFramebuffer
298 {
299   bool success = FALSE;
300   
301   if (context && !pause)
302   {
303     if ([EAGLContext currentContext] != context)
304       [EAGLContext setCurrentContext:context];
305     
306     glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
307     success = [context presentRenderbuffer:GL_RENDERBUFFER];
308   }
309   
310   return success;
311 }
312 //--------------------------------------------------------------
313 - (void) pauseAnimation
314 {
315   PRINT_SIGNATURE();
316   pause = TRUE;
317   g_application.SetInBackground(true);
318 }
319 //--------------------------------------------------------------
320 - (void) resumeAnimation
321 {
322   PRINT_SIGNATURE();
323   pause = FALSE;
324   g_application.SetInBackground(false);
325 }
326 //--------------------------------------------------------------
327 - (void) startAnimation
328 {
329   PRINT_SIGNATURE();
330         if (!animating && context)
331         {
332                 animating = TRUE;
333
334     // kick off an animation thread
335     animationThreadLock = [[NSConditionLock alloc] initWithCondition: FALSE];
336     animationThread = [[NSThread alloc] initWithTarget:self
337       selector:@selector(runAnimation:)
338       object:animationThreadLock];
339     [animationThread start];
340     [self initDisplayLink];
341         }
342 }
343 //--------------------------------------------------------------
344 - (void) stopAnimation
345 {
346   PRINT_SIGNATURE();
347         if (animating && context)
348         {
349     [self deinitDisplayLink];
350                 animating = FALSE;
351     xbmcAlive = FALSE;
352     if (!g_application.m_bStop)
353     {
354       ThreadMessage tMsg = {TMSG_QUIT};
355       CApplicationMessenger::Get().SendMessage(tMsg);
356     }
357     // wait for animation thread to die
358     if ([animationThread isFinished] == NO)
359       [animationThreadLock lockWhenCondition:TRUE];
360         }
361 }
362 //--------------------------------------------------------------
363 - (void) runAnimation:(id) arg
364 {
365   CCocoaAutoPool outerpool;
366   bool readyToRun = true;
367
368   // signal we are alive
369   NSConditionLock* myLock = arg;
370   [myLock lock];
371   
372   #ifdef _DEBUG
373     g_advancedSettings.m_logLevel     = LOG_LEVEL_DEBUG;
374     g_advancedSettings.m_logLevelHint = LOG_LEVEL_DEBUG;
375   #else
376     g_advancedSettings.m_logLevel     = LOG_LEVEL_NORMAL;
377     g_advancedSettings.m_logLevelHint = LOG_LEVEL_NORMAL;
378   #endif
379
380   // Prevent child processes from becoming zombies on exit if not waited upon. See also Util::Command
381   struct sigaction sa;
382   memset(&sa, 0, sizeof(sa));
383   sa.sa_flags = SA_NOCLDWAIT;
384   sa.sa_handler = SIG_IGN;
385   sigaction(SIGCHLD, &sa, NULL);
386
387   setlocale(LC_NUMERIC, "C");
388  
389   g_application.Preflight();
390   if (!g_application.Create())
391   {
392     readyToRun = false;
393     ELOG(@"%sUnable to create application", __PRETTY_FUNCTION__);
394   }
395
396   if (!g_application.CreateGUI())
397   {
398     readyToRun = false;
399     ELOG(@"%sUnable to create GUI", __PRETTY_FUNCTION__);
400   }
401
402   if (!g_application.Initialize())
403   {
404     readyToRun = false;
405     ELOG(@"%sUnable to initialize application", __PRETTY_FUNCTION__);
406   }
407   
408   if (readyToRun)
409   {
410     g_advancedSettings.m_startFullScreen = true;
411     g_advancedSettings.m_canWindowed = false;
412     xbmcAlive = TRUE;
413     try
414     {
415       CCocoaAutoPool innerpool;
416       g_application.Run();
417     }
418     catch(...)
419     {
420       ELOG(@"%sException caught on main loop. Exiting", __PRETTY_FUNCTION__);
421     }
422   }
423
424   // signal we are dead
425   [myLock unlockWithCondition:TRUE];
426
427   // grrr, xbmc does not shutdown properly and leaves
428   // several classes in an indeterminant state, we must exit and
429   // reload Lowtide/AppleTV, boo.
430   [g_xbmcController enableScreenSaver];
431   [g_xbmcController enableSystemSleep];
432   //[g_xbmcController applicationDidExit];
433   exit(0);
434 }
435
436 //--------------------------------------------------------------
437 - (void) runDisplayLink;
438 {
439   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
440
441   displayFPS = 1.0 / ([displayLink duration] * [displayLink frameInterval]);
442   if (animationThread && [animationThread isExecuting] == YES)
443   {
444     if (g_VideoReferenceClock.IsRunning())
445       g_VideoReferenceClock.VblankHandler(CurrentHostCounter(), displayFPS);
446   }
447   [pool release];
448 }
449 //--------------------------------------------------------------
450 - (void) initDisplayLink
451 {
452   //init with the appropriate display link for the
453   //used screen
454   bool external = currentScreen != [UIScreen mainScreen];
455   
456   if(external)
457   {
458     fprintf(stderr,"InitDisplayLink on external");
459   }
460   else
461   {
462     fprintf(stderr,"InitDisplayLink on internal");
463   }
464   
465   
466   displayLink = [currentScreen displayLinkWithTarget:self selector:@selector(runDisplayLink)];
467   [displayLink setFrameInterval:1];
468   [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
469   displayFPS = 1.0 / ([displayLink duration] * [displayLink frameInterval]);
470 }
471 //--------------------------------------------------------------
472 - (void) deinitDisplayLink
473 {
474   [displayLink invalidate];
475   displayLink = nil;
476 }
477 //--------------------------------------------------------------
478 - (double) getDisplayLinkFPS;
479 {
480   return displayFPS;
481 }
482 //--------------------------------------------------------------
483 @end