ios: fixed, use full path on XBMCController.h
[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 #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"
49 #endif
50 #import "IOSScreenManager.h"
51 #import "AutoPool.h"
52 #import "DarwinUtils.h"
53 #import "XBMCDebugHelpers.h"
54
55 //--------------------------------------------------------------
56 @interface IOSEAGLView (PrivateMethods)
57 - (void) setContext:(EAGLContext *)newContext;
58 - (void) createFramebuffer;
59 - (void) deleteFramebuffer;
60 - (void) runDisplayLink;
61 @end
62
63 @implementation IOSEAGLView
64 @synthesize animating;
65 @synthesize xbmcAlive;
66 @synthesize pause;
67 @synthesize currentScreen;
68 @synthesize framebufferResizeRequested;
69
70 // You must implement this method
71 + (Class) layerClass
72 {
73   return [CAEAGLLayer class];
74 }
75 //--------------------------------------------------------------
76 - (void) resizeFrameBuffer
77 {
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)
83     return;
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 )
89   {
90     framebufferResizeRequested = TRUE;
91     [eaglLayer setFrame:frame];
92   }
93 }
94
95 - (void)layoutSubviews
96 {
97   if(framebufferResizeRequested)
98   {
99     framebufferResizeRequested = FALSE;
100     [self deinitDisplayLink];
101     [self deleteFramebuffer];
102     [self createFramebuffer];
103     [self setFramebuffer];  
104     [self initDisplayLink];
105   }
106 }
107
108 - (CGFloat) getScreenScale:(UIScreen *)screen
109 {
110   CGFloat ret = 1.0;
111   if ([screen respondsToSelector:@selector(scale)])
112   {    
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)
118     {
119       ret = [screen scale];
120     }
121     
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())
126     {
127       ret = 2.0;//all retina devices have a scale factor of 2.0
128     }
129   }
130   return ret;
131 }
132
133 - (void) setScreen:(UIScreen *)screen withFrameBufferResize:(BOOL)resize;
134 {
135   CGFloat scaleFactor = 1.0;
136   CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[self layer];
137
138   currentScreen = screen;
139   scaleFactor = [self getScreenScale: currentScreen];
140
141   //this will activate retina on supported devices
142   [eaglLayer setContentsScale:scaleFactor];
143   [self setContentScaleFactor:scaleFactor];
144   if(resize)
145   {
146     [self resizeFrameBuffer];
147   }
148 }
149
150 //--------------------------------------------------------------
151 - (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
152 {
153   //PRINT_SIGNATURE();
154   framebufferResizeRequested = FALSE;
155   if ((self = [super initWithFrame:frame]))
156   {
157     // Get the layer
158     CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
159     //set screen, handlescreenscale
160     //and set frame size
161     [self setScreen:screen withFrameBufferResize:FALSE];
162
163     eaglLayer.opaque = TRUE;
164     eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
165       [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
166       kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
167       nil];
168                 
169     EAGLContext *aContext = [[EAGLContext alloc] 
170       initWithAPI:kEAGLRenderingAPIOpenGLES2];
171     
172     if (!aContext)
173       ELOG(@"Failed to create ES context");
174     else if (![EAGLContext setCurrentContext:aContext])
175       ELOG(@"Failed to set ES context current");
176     
177     self.context = aContext;
178     [aContext release];
179
180     animating = FALSE;
181     xbmcAlive = FALSE;
182     pause = FALSE;
183     [self setContext:context];
184     [self createFramebuffer];
185     [self setFramebuffer];
186
187                 displayLink = nil;
188   }
189
190   return self;
191 }
192
193 //--------------------------------------------------------------
194 - (void) dealloc
195 {
196   //PRINT_SIGNATURE();
197   [self deleteFramebuffer];    
198   [context release];
199   
200   [super dealloc];
201 }
202
203 //--------------------------------------------------------------
204 - (EAGLContext *)context
205 {
206   return context;
207 }
208 //--------------------------------------------------------------
209 - (void)setContext:(EAGLContext *)newContext
210 {
211   PRINT_SIGNATURE();
212   if (context != newContext)
213   {
214     [self deleteFramebuffer];
215     
216     [context release];
217     context = [newContext retain];
218     
219     [EAGLContext setCurrentContext:nil];
220   }
221 }
222
223 //--------------------------------------------------------------
224 - (void)createFramebuffer
225 {
226   if (context && !defaultFramebuffer)
227   {
228     //PRINT_SIGNATURE();
229     [EAGLContext setCurrentContext:context];
230     
231     // Create default framebuffer object.
232     glGenFramebuffers(1, &defaultFramebuffer);
233     glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
234     
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);
242
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);
247     
248     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
249       ELOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
250   }
251 }
252 //--------------------------------------------------------------
253 - (void) deleteFramebuffer
254 {
255   if (context && !pause)
256   {
257     PRINT_SIGNATURE();
258     [EAGLContext setCurrentContext:context];
259     
260     if (defaultFramebuffer)
261     {
262       glDeleteFramebuffers(1, &defaultFramebuffer);
263       defaultFramebuffer = 0;
264     }
265     
266     if (colorRenderbuffer)
267     {
268       glDeleteRenderbuffers(1, &colorRenderbuffer);
269       colorRenderbuffer = 0;
270     }
271
272     if (depthRenderbuffer)
273     {
274       glDeleteRenderbuffers(1, &depthRenderbuffer);
275       depthRenderbuffer = 0;
276     }
277   }
278 }
279 //--------------------------------------------------------------
280 - (void) setFramebuffer
281 {
282   if (context && !pause)
283   {
284     if ([EAGLContext currentContext] != context)
285       [EAGLContext setCurrentContext:context];
286     
287     glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
288     
289     if(framebufferHeight > framebufferWidth) {
290       glViewport(0, 0, framebufferHeight, framebufferWidth);
291       glScissor(0, 0, framebufferHeight, framebufferWidth);
292     } 
293     else
294     {
295       glViewport(0, 0, framebufferWidth, framebufferHeight);
296       glScissor(0, 0, framebufferWidth, framebufferHeight);
297     }
298   }
299 }
300 //--------------------------------------------------------------
301 - (bool) presentFramebuffer
302 {
303   bool success = FALSE;
304   
305   if (context && !pause)
306   {
307     if ([EAGLContext currentContext] != context)
308       [EAGLContext setCurrentContext:context];
309     
310     glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
311     success = [context presentRenderbuffer:GL_RENDERBUFFER];
312   }
313   
314   return success;
315 }
316 //--------------------------------------------------------------
317 - (void) pauseAnimation
318 {
319   PRINT_SIGNATURE();
320   pause = TRUE;
321   g_application.SetInBackground(true);
322 }
323 //--------------------------------------------------------------
324 - (void) resumeAnimation
325 {
326   PRINT_SIGNATURE();
327   pause = FALSE;
328   g_application.SetInBackground(false);
329 }
330 //--------------------------------------------------------------
331 - (void) startAnimation
332 {
333   PRINT_SIGNATURE();
334         if (!animating && context)
335         {
336                 animating = TRUE;
337
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];
345         }
346 }
347 //--------------------------------------------------------------
348 - (void) stopAnimation
349 {
350   PRINT_SIGNATURE();
351         if (animating && context)
352         {
353     [self deinitDisplayLink];
354                 animating = FALSE;
355     xbmcAlive = FALSE;
356     if (!g_application.m_bStop)
357     {
358       ThreadMessage tMsg = {TMSG_QUIT};
359       CApplicationMessenger::Get().SendMessage(tMsg);
360     }
361     // wait for animation thread to die
362     if ([animationThread isFinished] == NO)
363       [animationThreadLock lockWhenCondition:TRUE];
364         }
365 }
366 //--------------------------------------------------------------
367 - (void) runAnimation:(id) arg
368 {
369   CCocoaAutoPool outerpool;
370   bool readyToRun = true;
371
372   // signal we are alive
373   NSConditionLock* myLock = arg;
374   [myLock lock];
375   
376   #ifdef _DEBUG
377     g_advancedSettings.m_logLevel     = LOG_LEVEL_DEBUG;
378     g_advancedSettings.m_logLevelHint = LOG_LEVEL_DEBUG;
379   #else
380     g_advancedSettings.m_logLevel     = LOG_LEVEL_NORMAL;
381     g_advancedSettings.m_logLevelHint = LOG_LEVEL_NORMAL;
382   #endif
383
384   // Prevent child processes from becoming zombies on exit if not waited upon. See also Util::Command
385   struct sigaction sa;
386   memset(&sa, 0, sizeof(sa));
387   sa.sa_flags = SA_NOCLDWAIT;
388   sa.sa_handler = SIG_IGN;
389   sigaction(SIGCHLD, &sa, NULL);
390
391   setlocale(LC_NUMERIC, "C");
392  
393   g_application.Preflight();
394   if (!g_application.Create())
395   {
396     readyToRun = false;
397     ELOG(@"%sUnable to create application", __PRETTY_FUNCTION__);
398   }
399
400   if (!g_application.CreateGUI())
401   {
402     readyToRun = false;
403     ELOG(@"%sUnable to create GUI", __PRETTY_FUNCTION__);
404   }
405
406   if (!g_application.Initialize())
407   {
408     readyToRun = false;
409     ELOG(@"%sUnable to initialize application", __PRETTY_FUNCTION__);
410   }
411   
412   if (readyToRun)
413   {
414     g_advancedSettings.m_startFullScreen = true;
415     g_advancedSettings.m_canWindowed = false;
416     xbmcAlive = TRUE;
417     try
418     {
419       CCocoaAutoPool innerpool;
420       g_application.Run();
421     }
422     catch(...)
423     {
424       ELOG(@"%sException caught on main loop. Exiting", __PRETTY_FUNCTION__);
425     }
426   }
427
428   // signal we are dead
429   [myLock unlockWithCondition:TRUE];
430
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];
437   exit(0);
438 }
439
440 //--------------------------------------------------------------
441 - (void) runDisplayLink;
442 {
443   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
444
445   displayFPS = 1.0 / ([displayLink duration] * [displayLink frameInterval]);
446   if (animationThread && [animationThread isExecuting] == YES)
447   {
448     if (g_VideoReferenceClock.IsRunning())
449       g_VideoReferenceClock.VblankHandler(CurrentHostCounter(), displayFPS);
450   }
451   [pool release];
452 }
453 //--------------------------------------------------------------
454 - (void) initDisplayLink
455 {
456   //init with the appropriate display link for the
457   //used screen
458   bool external = currentScreen != [UIScreen mainScreen];
459   
460   if(external)
461   {
462     fprintf(stderr,"InitDisplayLink on external");
463   }
464   else
465   {
466     fprintf(stderr,"InitDisplayLink on internal");
467   }
468   
469   
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]);
474 }
475 //--------------------------------------------------------------
476 - (void) deinitDisplayLink
477 {
478   [displayLink invalidate];
479   displayLink = nil;
480 }
481 //--------------------------------------------------------------
482 - (double) getDisplayLinkFPS;
483 {
484   return displayFPS;
485 }
486 //--------------------------------------------------------------
487 @end