2 * Copyright (C) 2010-2013 Team XBMC
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)
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.
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/>.
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>
29 #include "AdvancedSettings.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"
40 #import <QuartzCore/QuartzCore.h>
42 #import <OpenGLES/ES2/gl.h>
43 #import <OpenGLES/ES2/glext.h>
44 #import "IOSEAGLView.h"
45 #import "XBMCController.h"
46 #import "IOSScreenManager.h"
48 #import "DarwinUtils.h"
49 #import "XBMCDebugHelpers.h"
51 //--------------------------------------------------------------
52 @interface IOSEAGLView (PrivateMethods)
53 - (void) setContext:(EAGLContext *)newContext;
54 - (void) createFramebuffer;
55 - (void) deleteFramebuffer;
56 - (void) runDisplayLink;
59 @implementation IOSEAGLView
60 @synthesize animating;
61 @synthesize xbmcAlive;
63 @synthesize currentScreen;
64 @synthesize framebufferResizeRequested;
66 // You must implement this method
69 return [CAEAGLLayer class];
71 //--------------------------------------------------------------
72 - (void) resizeFrameBuffer
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)
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 )
86 framebufferResizeRequested = TRUE;
87 [eaglLayer setFrame:frame];
91 - (void)layoutSubviews
93 if(framebufferResizeRequested)
95 framebufferResizeRequested = FALSE;
96 [self deinitDisplayLink];
97 [self deleteFramebuffer];
98 [self createFramebuffer];
99 [self setFramebuffer];
100 [self initDisplayLink];
104 - (CGFloat) getScreenScale:(UIScreen *)screen
107 if ([screen respondsToSelector:@selector(scale)])
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)
115 ret = [screen scale];
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())
123 ret = 2.0;//all retina devices have a scale factor of 2.0
129 - (void) setScreen:(UIScreen *)screen withFrameBufferResize:(BOOL)resize;
131 CGFloat scaleFactor = 1.0;
132 CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[self layer];
134 currentScreen = screen;
135 scaleFactor = [self getScreenScale: currentScreen];
137 //this will activate retina on supported devices
138 [eaglLayer setContentsScale:scaleFactor];
139 [self setContentScaleFactor:scaleFactor];
142 [self resizeFrameBuffer];
146 //--------------------------------------------------------------
147 - (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
150 framebufferResizeRequested = FALSE;
151 if ((self = [super initWithFrame:frame]))
154 CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
155 //set screen, handlescreenscale
157 [self setScreen:screen withFrameBufferResize:FALSE];
159 eaglLayer.opaque = TRUE;
160 eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
161 [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
162 kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
165 EAGLContext *aContext = [[EAGLContext alloc]
166 initWithAPI:kEAGLRenderingAPIOpenGLES2];
169 ELOG(@"Failed to create ES context");
170 else if (![EAGLContext setCurrentContext:aContext])
171 ELOG(@"Failed to set ES context current");
173 self.context = aContext;
179 [self setContext:context];
180 [self createFramebuffer];
181 [self setFramebuffer];
189 //--------------------------------------------------------------
193 [self deleteFramebuffer];
199 //--------------------------------------------------------------
200 - (EAGLContext *)context
204 //--------------------------------------------------------------
205 - (void)setContext:(EAGLContext *)newContext
208 if (context != newContext)
210 [self deleteFramebuffer];
213 context = [newContext retain];
215 [EAGLContext setCurrentContext:nil];
219 //--------------------------------------------------------------
220 - (void)createFramebuffer
222 if (context && !defaultFramebuffer)
225 [EAGLContext setCurrentContext:context];
227 // Create default framebuffer object.
228 glGenFramebuffers(1, &defaultFramebuffer);
229 glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
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);
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);
244 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
245 ELOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
248 //--------------------------------------------------------------
249 - (void) deleteFramebuffer
251 if (context && !pause)
254 [EAGLContext setCurrentContext:context];
256 if (defaultFramebuffer)
258 glDeleteFramebuffers(1, &defaultFramebuffer);
259 defaultFramebuffer = 0;
262 if (colorRenderbuffer)
264 glDeleteRenderbuffers(1, &colorRenderbuffer);
265 colorRenderbuffer = 0;
268 if (depthRenderbuffer)
270 glDeleteRenderbuffers(1, &depthRenderbuffer);
271 depthRenderbuffer = 0;
275 //--------------------------------------------------------------
276 - (void) setFramebuffer
278 if (context && !pause)
280 if ([EAGLContext currentContext] != context)
281 [EAGLContext setCurrentContext:context];
283 glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
285 if(framebufferHeight > framebufferWidth) {
286 glViewport(0, 0, framebufferHeight, framebufferWidth);
287 glScissor(0, 0, framebufferHeight, framebufferWidth);
291 glViewport(0, 0, framebufferWidth, framebufferHeight);
292 glScissor(0, 0, framebufferWidth, framebufferHeight);
296 //--------------------------------------------------------------
297 - (bool) presentFramebuffer
299 bool success = FALSE;
301 if (context && !pause)
303 if ([EAGLContext currentContext] != context)
304 [EAGLContext setCurrentContext:context];
306 glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
307 success = [context presentRenderbuffer:GL_RENDERBUFFER];
312 //--------------------------------------------------------------
313 - (void) pauseAnimation
317 g_application.SetInBackground(true);
319 //--------------------------------------------------------------
320 - (void) resumeAnimation
324 g_application.SetInBackground(false);
326 //--------------------------------------------------------------
327 - (void) startAnimation
330 if (!animating && context)
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];
343 //--------------------------------------------------------------
344 - (void) stopAnimation
347 if (animating && context)
349 [self deinitDisplayLink];
352 if (!g_application.m_bStop)
354 ThreadMessage tMsg = {TMSG_QUIT};
355 CApplicationMessenger::Get().SendMessage(tMsg);
357 // wait for animation thread to die
358 if ([animationThread isFinished] == NO)
359 [animationThreadLock lockWhenCondition:TRUE];
362 //--------------------------------------------------------------
363 - (void) runAnimation:(id) arg
365 CCocoaAutoPool outerpool;
366 bool readyToRun = true;
368 // signal we are alive
369 NSConditionLock* myLock = arg;
373 g_advancedSettings.m_logLevel = LOG_LEVEL_DEBUG;
374 g_advancedSettings.m_logLevelHint = LOG_LEVEL_DEBUG;
376 g_advancedSettings.m_logLevel = LOG_LEVEL_NORMAL;
377 g_advancedSettings.m_logLevelHint = LOG_LEVEL_NORMAL;
380 // Prevent child processes from becoming zombies on exit if not waited upon. See also Util::Command
382 memset(&sa, 0, sizeof(sa));
383 sa.sa_flags = SA_NOCLDWAIT;
384 sa.sa_handler = SIG_IGN;
385 sigaction(SIGCHLD, &sa, NULL);
387 setlocale(LC_NUMERIC, "C");
389 g_application.Preflight();
390 if (!g_application.Create())
393 ELOG(@"%sUnable to create application", __PRETTY_FUNCTION__);
396 if (!g_application.CreateGUI())
399 ELOG(@"%sUnable to create GUI", __PRETTY_FUNCTION__);
402 if (!g_application.Initialize())
405 ELOG(@"%sUnable to initialize application", __PRETTY_FUNCTION__);
410 g_advancedSettings.m_startFullScreen = true;
411 g_advancedSettings.m_canWindowed = false;
415 CCocoaAutoPool innerpool;
420 ELOG(@"%sException caught on main loop. Exiting", __PRETTY_FUNCTION__);
424 // signal we are dead
425 [myLock unlockWithCondition:TRUE];
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];
436 //--------------------------------------------------------------
437 - (void) runDisplayLink;
439 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
441 displayFPS = 1.0 / ([displayLink duration] * [displayLink frameInterval]);
442 if (animationThread && [animationThread isExecuting] == YES)
444 if (g_VideoReferenceClock.IsRunning())
445 g_VideoReferenceClock.VblankHandler(CurrentHostCounter(), displayFPS);
449 //--------------------------------------------------------------
450 - (void) initDisplayLink
452 //init with the appropriate display link for the
454 bool external = currentScreen != [UIScreen mainScreen];
458 fprintf(stderr,"InitDisplayLink on external");
462 fprintf(stderr,"InitDisplayLink on internal");
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]);
471 //--------------------------------------------------------------
472 - (void) deinitDisplayLink
474 [displayLink invalidate];
477 //--------------------------------------------------------------
478 - (double) getDisplayLinkFPS;
482 //--------------------------------------------------------------