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