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"
38 #include "XbmcContext.h"
41 #import <QuartzCore/QuartzCore.h>
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"
51 #import "IOSScreenManager.h"
53 #import "DarwinUtils.h"
54 #import "XBMCDebugHelpers.h"
56 //--------------------------------------------------------------
57 @interface IOSEAGLView (PrivateMethods)
58 - (void) setContext:(EAGLContext *)newContext;
59 - (void) createFramebuffer;
60 - (void) deleteFramebuffer;
61 - (void) runDisplayLink;
64 @implementation IOSEAGLView
65 @synthesize animating;
66 @synthesize xbmcAlive;
68 @synthesize currentScreen;
69 @synthesize framebufferResizeRequested;
71 // You must implement this method
74 return [CAEAGLLayer class];
76 //--------------------------------------------------------------
77 - (void) resizeFrameBuffer
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)
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 )
91 framebufferResizeRequested = TRUE;
92 [eaglLayer setFrame:frame];
96 - (void)layoutSubviews
98 if(framebufferResizeRequested)
100 framebufferResizeRequested = FALSE;
101 [self deinitDisplayLink];
102 [self deleteFramebuffer];
103 [self createFramebuffer];
104 [self setFramebuffer];
105 [self initDisplayLink];
109 - (CGFloat) getScreenScale:(UIScreen *)screen
112 if ([screen respondsToSelector:@selector(scale)])
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)
120 ret = [screen scale];
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())
128 ret = 2.0;//all retina devices have a scale factor of 2.0
134 - (void) setScreen:(UIScreen *)screen withFrameBufferResize:(BOOL)resize;
136 CGFloat scaleFactor = 1.0;
137 CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[self layer];
139 currentScreen = screen;
140 scaleFactor = [self getScreenScale: currentScreen];
142 //this will activate retina on supported devices
143 [eaglLayer setContentsScale:scaleFactor];
144 [self setContentScaleFactor:scaleFactor];
147 [self resizeFrameBuffer];
151 //--------------------------------------------------------------
152 - (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
155 framebufferResizeRequested = FALSE;
156 if ((self = [super initWithFrame:frame]))
159 CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
160 //set screen, handlescreenscale
162 [self setScreen:screen withFrameBufferResize:FALSE];
164 eaglLayer.opaque = TRUE;
165 eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
166 [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
167 kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
170 EAGLContext *aContext = [[EAGLContext alloc]
171 initWithAPI:kEAGLRenderingAPIOpenGLES2];
174 ELOG(@"Failed to create ES context");
175 else if (![EAGLContext setCurrentContext:aContext])
176 ELOG(@"Failed to set ES context current");
178 self.context = aContext;
184 [self setContext:context];
185 [self createFramebuffer];
186 [self setFramebuffer];
194 //--------------------------------------------------------------
198 [self deleteFramebuffer];
204 //--------------------------------------------------------------
205 - (EAGLContext *)context
209 //--------------------------------------------------------------
210 - (void)setContext:(EAGLContext *)newContext
213 if (context != newContext)
215 [self deleteFramebuffer];
218 context = [newContext retain];
220 [EAGLContext setCurrentContext:nil];
224 //--------------------------------------------------------------
225 - (void)createFramebuffer
227 if (context && !defaultFramebuffer)
230 [EAGLContext setCurrentContext:context];
232 // Create default framebuffer object.
233 glGenFramebuffers(1, &defaultFramebuffer);
234 glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
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);
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);
249 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
250 ELOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
253 //--------------------------------------------------------------
254 - (void) deleteFramebuffer
256 if (context && !pause)
259 [EAGLContext setCurrentContext:context];
261 if (defaultFramebuffer)
263 glDeleteFramebuffers(1, &defaultFramebuffer);
264 defaultFramebuffer = 0;
267 if (colorRenderbuffer)
269 glDeleteRenderbuffers(1, &colorRenderbuffer);
270 colorRenderbuffer = 0;
273 if (depthRenderbuffer)
275 glDeleteRenderbuffers(1, &depthRenderbuffer);
276 depthRenderbuffer = 0;
280 //--------------------------------------------------------------
281 - (void) setFramebuffer
283 if (context && !pause)
285 if ([EAGLContext currentContext] != context)
286 [EAGLContext setCurrentContext:context];
288 glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
290 if(framebufferHeight > framebufferWidth) {
291 glViewport(0, 0, framebufferHeight, framebufferWidth);
292 glScissor(0, 0, framebufferHeight, framebufferWidth);
296 glViewport(0, 0, framebufferWidth, framebufferHeight);
297 glScissor(0, 0, framebufferWidth, framebufferHeight);
301 //--------------------------------------------------------------
302 - (bool) presentFramebuffer
304 bool success = FALSE;
306 if (context && !pause)
308 if ([EAGLContext currentContext] != context)
309 [EAGLContext setCurrentContext:context];
311 glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
312 success = [context presentRenderbuffer:GL_RENDERBUFFER];
317 //--------------------------------------------------------------
318 - (void) pauseAnimation
322 g_application.SetInBackground(true);
324 //--------------------------------------------------------------
325 - (void) resumeAnimation
329 g_application.SetInBackground(false);
331 //--------------------------------------------------------------
332 - (void) startAnimation
335 if (!animating && context)
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];
348 //--------------------------------------------------------------
349 - (void) stopAnimation
352 if (animating && context)
354 [self deinitDisplayLink];
357 if (!g_application.m_bStop)
359 ThreadMessage tMsg = {TMSG_QUIT};
360 CApplicationMessenger::Get().SendMessage(tMsg);
362 // wait for animation thread to die
363 if ([animationThread isFinished] == NO)
364 [animationThreadLock lockWhenCondition:TRUE];
367 //--------------------------------------------------------------
368 - (void) runAnimation:(id) arg
370 CCocoaAutoPool outerpool;
371 // set up some xbmc specific relationships
372 XBMC::Context context;
373 bool readyToRun = true;
375 // signal we are alive
376 NSConditionLock* myLock = arg;
380 g_advancedSettings.m_logLevel = LOG_LEVEL_DEBUG;
381 g_advancedSettings.m_logLevelHint = LOG_LEVEL_DEBUG;
383 g_advancedSettings.m_logLevel = LOG_LEVEL_NORMAL;
384 g_advancedSettings.m_logLevelHint = LOG_LEVEL_NORMAL;
387 // Prevent child processes from becoming zombies on exit if not waited upon. See also Util::Command
389 memset(&sa, 0, sizeof(sa));
390 sa.sa_flags = SA_NOCLDWAIT;
391 sa.sa_handler = SIG_IGN;
392 sigaction(SIGCHLD, &sa, NULL);
394 setlocale(LC_NUMERIC, "C");
396 g_application.Preflight();
397 if (!g_application.Create())
400 ELOG(@"%sUnable to create application", __PRETTY_FUNCTION__);
403 if (!g_application.CreateGUI())
406 ELOG(@"%sUnable to create GUI", __PRETTY_FUNCTION__);
409 if (!g_application.Initialize())
412 ELOG(@"%sUnable to initialize application", __PRETTY_FUNCTION__);
417 g_advancedSettings.m_startFullScreen = true;
418 g_advancedSettings.m_canWindowed = false;
422 CCocoaAutoPool innerpool;
427 ELOG(@"%sException caught on main loop. Exiting", __PRETTY_FUNCTION__);
431 // signal we are dead
432 [myLock unlockWithCondition:TRUE];
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];
443 //--------------------------------------------------------------
444 - (void) runDisplayLink;
446 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
448 displayFPS = 1.0 / ([displayLink duration] * [displayLink frameInterval]);
449 if (animationThread && [animationThread isExecuting] == YES)
451 if (g_VideoReferenceClock.IsRunning())
452 g_VideoReferenceClock.VblankHandler(CurrentHostCounter(), displayFPS);
456 //--------------------------------------------------------------
457 - (void) initDisplayLink
459 //init with the appropriate display link for the
461 bool external = currentScreen != [UIScreen mainScreen];
465 fprintf(stderr,"InitDisplayLink on external");
469 fprintf(stderr,"InitDisplayLink on internal");
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]);
478 //--------------------------------------------------------------
479 - (void) deinitDisplayLink
481 [displayLink invalidate];
484 //--------------------------------------------------------------
485 - (double) getDisplayLinkFPS;
489 //--------------------------------------------------------------