Merge pull request #4314 from MartijnKaijser/beta1
[vuplus_xbmc] / xbmc / osx / IOSScreenManager.mm
1 /*
2  *      Copyright (C) 2012-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 "utils/log.h"
27 #include "settings/DisplaySettings.h"
28 #include "threads/Event.h"
29 #include "Application.h"
30 #include "WindowingFactory.h"
31 #include "settings/DisplaySettings.h"
32 #include "cores/AudioEngine/AEFactory.h"
33 #undef BOOL
34
35 #import <Foundation/Foundation.h>
36 #include <objc/runtime.h>
37
38 #import "IOSScreenManager.h"
39 #if defined(TARGET_DARWIN_IOS_ATV2)
40 #import "xbmc/osx/atv2/XBMCController.h"
41 #elif defined(TARGET_DARWIN_IOS)
42 #import "xbmc/osx/ios/XBMCController.h"
43 #endif
44 #import "IOSExternalTouchController.h"
45 #import "IOSEAGLView.h"
46
47 const CGFloat timeSwitchingToExternalSecs = 6.0;
48 const CGFloat timeSwitchingToInternalSecs = 2.0;
49 const CGFloat timeFadeSecs                = 2.0;
50
51 static CEvent screenChangeEvent;
52
53 @implementation IOSScreenManager
54 @synthesize _screenIdx;
55 @synthesize _externalScreen;
56 @synthesize _glView;
57 @synthesize _lastTouchControllerOrientation;
58
59 //--------------------------------------------------------------
60 - (void) fadeFromBlack:(CGFloat) delaySecs
61 {
62   if([_glView alpha] != 1.0)
63   {
64     [UIView animateWithDuration:timeFadeSecs delay:delaySecs options:UIViewAnimationOptionCurveEaseInOut animations:^{
65       [_glView setAlpha:1.0];
66     }
67     completion:^(BOOL finished){   screenChangeEvent.Set(); }];
68   }
69 }
70 //--------------------------------------------------------------
71 // the real screen/mode change method
72 - (void) setScreen:(unsigned int) screenIdx withMode:(UIScreenMode *)mode
73 {
74     UIScreen *newScreen = [[UIScreen screens] objectAtIndex:screenIdx];
75     bool toExternal = _screenIdx == 0 && _screenIdx != screenIdx;
76
77     //set new screen mode
78     [newScreen setCurrentMode:mode];
79
80     //mode couldn't be applied to external screen
81     //wonkey screen!
82     if([newScreen currentMode] != mode)
83     {
84       NSLog(@"Error setting screen mode!");
85       screenChangeEvent.Set();
86       return;
87     }
88     _screenIdx = screenIdx;
89
90     //inform the other layers
91     _externalScreen = screenIdx != 0;
92
93     [_glView setScreen:newScreen withFrameBufferResize:TRUE];//will also resize the framebuffer
94
95     if (toExternal)
96     {
97       // portrait on external screen means its landscape for xbmc
98       [g_xbmcController activateScreen:newScreen withOrientation:UIInterfaceOrientationPortrait];// will attach the screen to xbmc mainwindow
99     }
100     else
101     {
102       // switching back to internal - use same orientation as we used for the touch controller
103       [g_xbmcController activateScreen:newScreen withOrientation:_lastTouchControllerOrientation];// will attach the screen to xbmc mainwindow
104     }
105
106     if(toExternal)//changing the external screen might need some time ...
107     {
108       //deactivate any overscan compensation when switching to external screens
109       if([newScreen respondsToSelector:@selector(overscanCompensation)])
110       {
111         //since iOS5.0 tvout has an default overscan compensation and property
112         //we need to switch it off here so that the tv can handle any
113         //needed overscan compensation (else on tvs without "just scan" option
114         //we might end up with black borders.
115         //Beside that in Apples documentation to setOverscanCompensation
116         //the parameter enum is lacking the UIScreenOverscanCompensationNone value.
117         //Someone on stackoverflow figured out that value 3 is for turning it off
118         //(though there is no enum value for it).
119 #ifdef __IPHONE_5_0
120         [newScreen setOverscanCompensation:(UIScreenOverscanCompensation)3];
121 #else
122         [newScreen setOverscanCompensation:3];
123 #endif
124         CLog::Log(LOGDEBUG, "[IOSScreenManager] Disabling overscancompensation.");
125       }
126       else
127       {
128         CLog::Log(LOGDEBUG, "[IOSScreenManager] Disabling overscancompensation not supported on this iOS version.");
129       }
130
131       [[IOSScreenManager sharedInstance] fadeFromBlack:timeSwitchingToExternalSecs];
132     }
133     else
134     {
135       [[IOSScreenManager sharedInstance] fadeFromBlack:timeSwitchingToInternalSecs];
136     }
137
138     int w = [[newScreen currentMode] size].width;
139     int h = [[newScreen currentMode] size].height;
140     NSLog(@"Switched to screen %i with %i x %i",screenIdx, w ,h);
141 }
142 //--------------------------------------------------------------
143 // - will fade current screen to black
144 // - change mode and screen
145 // - optionally activate external touchscreen controller when
146 // switching to external screen
147 // - fade back from black
148 - (void) changeScreenSelector:(NSDictionary *)dict
149 {
150   bool activateExternalTouchController = false;
151   int screenIdx = [[dict objectForKey:@"screenIdx"] intValue];
152   UIScreenMode *mode = [dict objectForKey:@"screenMode"];
153
154   if([self willSwitchToInternal:screenIdx] && _externalTouchController != nil)
155   {
156     _lastTouchControllerOrientation = [_externalTouchController interfaceOrientation];
157     [_externalTouchController release];
158     _externalTouchController = nil;
159   }
160
161   if([self willSwitchToExternal:screenIdx])
162   {
163     activateExternalTouchController = true;
164   }
165
166
167   [UIView animateWithDuration:timeFadeSecs delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
168     [_glView setAlpha:0.0];
169   }
170   completion:^(BOOL finished)
171   {
172     [self setScreen:screenIdx withMode:mode];
173     if(activateExternalTouchController)
174     {
175       _externalTouchController = [[IOSExternalTouchController alloc] init];
176     }
177   }];
178 }
179 //--------------------------------------------------------------
180 - (bool) changeScreen: (unsigned int)screenIdx withMode:(UIScreenMode *)mode
181 {
182   //screen has changed - get the new screen
183   if(screenIdx >= [[UIScreen screens] count])
184     return false;
185
186   //if we are about to switch to current screen
187   //with current mode - don't do anything
188   if(screenIdx == _screenIdx &&
189     mode == (UIScreenMode *)[[[UIScreen screens] objectAtIndex:screenIdx] currentMode])
190     return true;
191
192   //put the params into a dict
193   NSNumber *idx = [NSNumber numberWithInt:screenIdx];
194   NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:mode, @"screenMode",
195                                                                   idx,  @"screenIdx", nil];
196
197
198   CLog::Log(LOGINFO, "Changing screen to %d with %f x %f",screenIdx,[mode size].width, [mode size].height);
199   //ensure that the screen change is done in the mainthread
200   if([NSThread currentThread] != [NSThread mainThread])
201   {
202     [self performSelectorOnMainThread:@selector(changeScreenSelector:) withObject:dict  waitUntilDone:YES];
203     screenChangeEvent.WaitMSec(30000);
204   }
205   else
206   {
207     [self changeScreenSelector:dict];
208   }
209
210   // re-enumerate audio devices in that case too
211   // as we might gain passthrough capabilities via HDMI
212   CAEFactory::DeviceChange();
213   return true;
214 }
215 //--------------------------------------------------------------
216 - (bool) willSwitchToExternal:(unsigned int) screenIdx
217 {
218   if(_screenIdx == 0 && screenIdx != _screenIdx)
219   {
220     return true;
221   }
222   return false;
223 }
224 //--------------------------------------------------------------
225 - (bool) willSwitchToInternal:(unsigned int) screenIdx
226 {
227   if(_screenIdx != 0 && screenIdx == 0)
228   {
229     return true;
230   }
231   return false;
232 }
233 //--------------------------------------------------------------
234 + (CGRect) getLandscapeResolution:(UIScreen *)screen
235 {
236   CGRect res = [screen bounds];
237 #ifdef TARGET_DARWIN_IOS_ATV2
238   //because bounds returns f00bar on atv2 - we return the preferred resolution (which mostly is the
239   //right resolution
240 #if __IPHONE_OS_VERSION_MIN_REQUIRED > __IPHONE_4_2
241   res.size = screen.preferredMode.size;
242 #else
243   Class brwin = objc_getClass("BRWindow");
244   res.size = [brwin interfaceFrame].size;
245 #endif
246 #else
247   //main screen is in portrait mode (physically) so exchange height and width
248   if(screen == [UIScreen mainScreen])
249   {
250     CGRect frame = res;
251     res.size = CGSizeMake(frame.size.height, frame.size.width);
252   }
253 #endif
254   return res;
255 }
256 //--------------------------------------------------------------
257 - (void) screenDisconnect
258 {
259   //if we are on external screen and he was disconnected
260   //change back to internal screen
261   if([[UIScreen screens] count] == 1 && _screenIdx != 0)
262   {
263     RESOLUTION_INFO res = CDisplaySettings::Get().GetResolutionInfo(RES_DESKTOP);//internal screen default res
264     g_Windowing.SetFullScreen(true, res, false);
265   }
266 }
267 //--------------------------------------------------------------
268 + (void) updateResolutions
269 {
270   g_Windowing.UpdateResolutions();
271 }
272 //--------------------------------------------------------------
273 - (void) dealloc
274 {
275   if(_externalTouchController != nil )
276   {
277     [_externalTouchController release];
278   }
279   [super dealloc];
280 }
281 //--------------------------------------------------------------
282 + (id) sharedInstance
283 {
284         static IOSScreenManager* sharedManager = nil;
285         static dispatch_once_t onceToken;
286         dispatch_once(&onceToken, ^{
287    sharedManager = [[self alloc] init];
288         });
289         return sharedManager;
290 }
291 @end