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 #if defined(TARGET_DARWIN_IOS_ATV2)
46 #import "xbmc/osx/atv2/XBMCController.h"
47 #elif defined(TARGET_DARWIN_IOS)
48 #import "xbmc/osx/ios/XBMCController.h"
50 #import "IOSScreenManager.h"
52 #import "DarwinUtils.h"
53 #import "XBMCDebugHelpers.h"
55 //--------------------------------------------------------------
56 @interface IOSEAGLView (PrivateMethods)
57 - (void) setContext:(EAGLContext *)newContext;
58 - (void) createFramebuffer;
59 - (void) deleteFramebuffer;
60 - (void) runDisplayLink;
63 @implementation IOSEAGLView
64 @synthesize animating;
65 @synthesize xbmcAlive;
67 @synthesize currentScreen;
68 @synthesize framebufferResizeRequested;
70 // You must implement this method
73 return [CAEAGLLayer class];
75 //--------------------------------------------------------------
76 - (void) resizeFrameBuffer
78 CGRect frame = [IOSScreenManager getLandscapeResolution: currentScreen];
79 CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[self layer];
80 //allow a maximum framebuffer size of 1080p
81 //needed for tvout on iPad3/4 and iphone4/5 and maybe AppleTV3
82 if(frame.size.width * frame.size.height > 2073600)
84 //resize the layer - ios will delay this
85 //and call layoutSubviews when its done with resizing
86 //so the real framebuffer resize is done there then ...
87 if(framebufferWidth != frame.size.width ||
88 framebufferHeight != frame.size.height )
90 framebufferResizeRequested = TRUE;
91 [eaglLayer setFrame:frame];
95 - (void)layoutSubviews
97 if(framebufferResizeRequested)
99 framebufferResizeRequested = FALSE;
100 [self deinitDisplayLink];
101 [self deleteFramebuffer];
102 [self createFramebuffer];
103 [self setFramebuffer];
104 [self initDisplayLink];
108 - (CGFloat) getScreenScale:(UIScreen *)screen
111 if ([screen respondsToSelector:@selector(scale)])
113 // atv2 reports 0.0 for scale - thats why we check this here
114 // normal other iDevices report 1.0 here
115 // retina devices report 2.0 here
116 // this info is true as of 19.3.2012.
117 if([screen scale] > 1.0)
119 ret = [screen scale];
122 //if no retina display scale detected yet -
123 //ensure retina resolution on supported devices mainScreen
124 //even on older iOS SDKs
125 if (ret == 1.0 && screen == [UIScreen mainScreen] && DarwinHasRetina())
127 ret = 2.0;//all retina devices have a scale factor of 2.0
133 - (void) setScreen:(UIScreen *)screen withFrameBufferResize:(BOOL)resize;
135 CGFloat scaleFactor = 1.0;
136 CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[self layer];
138 currentScreen = screen;
139 scaleFactor = [self getScreenScale: currentScreen];
141 //this will activate retina on supported devices
142 [eaglLayer setContentsScale:scaleFactor];
143 [self setContentScaleFactor:scaleFactor];
146 [self resizeFrameBuffer];
150 //--------------------------------------------------------------
151 - (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
154 framebufferResizeRequested = FALSE;
155 if ((self = [super initWithFrame:frame]))
158 CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
159 //set screen, handlescreenscale
161 [self setScreen:screen withFrameBufferResize:FALSE];
163 eaglLayer.opaque = TRUE;
164 eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
165 [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
166 kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
169 EAGLContext *aContext = [[EAGLContext alloc]
170 initWithAPI:kEAGLRenderingAPIOpenGLES2];
173 ELOG(@"Failed to create ES context");
174 else if (![EAGLContext setCurrentContext:aContext])
175 ELOG(@"Failed to set ES context current");
177 self.context = aContext;
183 [self setContext:context];
184 [self createFramebuffer];
185 [self setFramebuffer];
193 //--------------------------------------------------------------
197 [self deleteFramebuffer];
203 //--------------------------------------------------------------
204 - (EAGLContext *)context
208 //--------------------------------------------------------------
209 - (void)setContext:(EAGLContext *)newContext
212 if (context != newContext)
214 [self deleteFramebuffer];
217 context = [newContext retain];
219 [EAGLContext setCurrentContext:nil];
223 //--------------------------------------------------------------
224 - (void)createFramebuffer
226 if (context && !defaultFramebuffer)
229 [EAGLContext setCurrentContext:context];
231 // Create default framebuffer object.
232 glGenFramebuffers(1, &defaultFramebuffer);
233 glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
235 // Create color render buffer and allocate backing store.
236 glGenRenderbuffers(1, &colorRenderbuffer);
237 glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
238 [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
239 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth);
240 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight);
241 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
243 glGenRenderbuffers(1, &depthRenderbuffer);
244 glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
245 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, framebufferWidth, framebufferHeight);
246 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
248 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
249 ELOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
252 //--------------------------------------------------------------
253 - (void) deleteFramebuffer
255 if (context && !pause)
258 [EAGLContext setCurrentContext:context];
260 if (defaultFramebuffer)
262 glDeleteFramebuffers(1, &defaultFramebuffer);
263 defaultFramebuffer = 0;
266 if (colorRenderbuffer)
268 glDeleteRenderbuffers(1, &colorRenderbuffer);
269 colorRenderbuffer = 0;
272 if (depthRenderbuffer)
274 glDeleteRenderbuffers(1, &depthRenderbuffer);
275 depthRenderbuffer = 0;
279 //--------------------------------------------------------------
280 - (void) setFramebuffer
282 if (context && !pause)
284 if ([EAGLContext currentContext] != context)
285 [EAGLContext setCurrentContext:context];
287 glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
289 if(framebufferHeight > framebufferWidth) {
290 glViewport(0, 0, framebufferHeight, framebufferWidth);
291 glScissor(0, 0, framebufferHeight, framebufferWidth);
295 glViewport(0, 0, framebufferWidth, framebufferHeight);
296 glScissor(0, 0, framebufferWidth, framebufferHeight);
300 //--------------------------------------------------------------
301 - (bool) presentFramebuffer
303 bool success = FALSE;
305 if (context && !pause)
307 if ([EAGLContext currentContext] != context)
308 [EAGLContext setCurrentContext:context];
310 glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
311 success = [context presentRenderbuffer:GL_RENDERBUFFER];
316 //--------------------------------------------------------------
317 - (void) pauseAnimation
321 g_application.SetInBackground(true);
323 //--------------------------------------------------------------
324 - (void) resumeAnimation
328 g_application.SetInBackground(false);
330 //--------------------------------------------------------------
331 - (void) startAnimation
334 if (!animating && context)
338 // kick off an animation thread
339 animationThreadLock = [[NSConditionLock alloc] initWithCondition: FALSE];
340 animationThread = [[NSThread alloc] initWithTarget:self
341 selector:@selector(runAnimation:)
342 object:animationThreadLock];
343 [animationThread start];
344 [self initDisplayLink];
347 //--------------------------------------------------------------
348 - (void) stopAnimation
351 if (animating && context)
353 [self deinitDisplayLink];
356 if (!g_application.m_bStop)
358 ThreadMessage tMsg = {TMSG_QUIT};
359 CApplicationMessenger::Get().SendMessage(tMsg);
361 // wait for animation thread to die
362 if ([animationThread isFinished] == NO)
363 [animationThreadLock lockWhenCondition:TRUE];
366 //--------------------------------------------------------------
367 - (void) runAnimation:(id) arg
369 CCocoaAutoPool outerpool;
370 bool readyToRun = true;
372 // signal we are alive
373 NSConditionLock* myLock = arg;
377 g_advancedSettings.m_logLevel = LOG_LEVEL_DEBUG;
378 g_advancedSettings.m_logLevelHint = LOG_LEVEL_DEBUG;
380 g_advancedSettings.m_logLevel = LOG_LEVEL_NORMAL;
381 g_advancedSettings.m_logLevelHint = LOG_LEVEL_NORMAL;
384 // Prevent child processes from becoming zombies on exit if not waited upon. See also Util::Command
386 memset(&sa, 0, sizeof(sa));
387 sa.sa_flags = SA_NOCLDWAIT;
388 sa.sa_handler = SIG_IGN;
389 sigaction(SIGCHLD, &sa, NULL);
391 setlocale(LC_NUMERIC, "C");
393 g_application.Preflight();
394 if (!g_application.Create())
397 ELOG(@"%sUnable to create application", __PRETTY_FUNCTION__);
400 if (!g_application.CreateGUI())
403 ELOG(@"%sUnable to create GUI", __PRETTY_FUNCTION__);
406 if (!g_application.Initialize())
409 ELOG(@"%sUnable to initialize application", __PRETTY_FUNCTION__);
414 g_advancedSettings.m_startFullScreen = true;
415 g_advancedSettings.m_canWindowed = false;
419 CCocoaAutoPool innerpool;
424 ELOG(@"%sException caught on main loop. Exiting", __PRETTY_FUNCTION__);
428 // signal we are dead
429 [myLock unlockWithCondition:TRUE];
431 // grrr, xbmc does not shutdown properly and leaves
432 // several classes in an indeterminant state, we must exit and
433 // reload Lowtide/AppleTV, boo.
434 [g_xbmcController enableScreenSaver];
435 [g_xbmcController enableSystemSleep];
436 //[g_xbmcController applicationDidExit];
440 //--------------------------------------------------------------
441 - (void) runDisplayLink;
443 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
445 displayFPS = 1.0 / ([displayLink duration] * [displayLink frameInterval]);
446 if (animationThread && [animationThread isExecuting] == YES)
448 if (g_VideoReferenceClock.IsRunning())
449 g_VideoReferenceClock.VblankHandler(CurrentHostCounter(), displayFPS);
453 //--------------------------------------------------------------
454 - (void) initDisplayLink
456 //init with the appropriate display link for the
458 bool external = currentScreen != [UIScreen mainScreen];
462 fprintf(stderr,"InitDisplayLink on external");
466 fprintf(stderr,"InitDisplayLink on internal");
470 displayLink = [currentScreen displayLinkWithTarget:self selector:@selector(runDisplayLink)];
471 [displayLink setFrameInterval:1];
472 [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
473 displayFPS = 1.0 / ([displayLink duration] * [displayLink frameInterval]);
475 //--------------------------------------------------------------
476 - (void) deinitDisplayLink
478 [displayLink invalidate];
481 //--------------------------------------------------------------
482 - (double) getDisplayLinkFPS;
486 //--------------------------------------------------------------