2 * Copyright (C) 2012-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>
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"
35 #import <Foundation/Foundation.h>
36 #include <objc/runtime.h>
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"
44 #import "IOSExternalTouchController.h"
45 #import "IOSEAGLView.h"
47 const CGFloat timeSwitchingToExternalSecs = 6.0;
48 const CGFloat timeSwitchingToInternalSecs = 2.0;
49 const CGFloat timeFadeSecs = 2.0;
51 static CEvent screenChangeEvent;
53 @implementation IOSScreenManager
54 @synthesize _screenIdx;
55 @synthesize _externalScreen;
57 @synthesize _lastTouchControllerOrientation;
59 //--------------------------------------------------------------
60 - (void) fadeFromBlack:(CGFloat) delaySecs
62 if([_glView alpha] != 1.0)
64 [UIView animateWithDuration:timeFadeSecs delay:delaySecs options:UIViewAnimationOptionCurveEaseInOut animations:^{
65 [_glView setAlpha:1.0];
67 completion:^(BOOL finished){ screenChangeEvent.Set(); }];
70 //--------------------------------------------------------------
71 // the real screen/mode change method
72 - (void) setScreen:(unsigned int) screenIdx withMode:(UIScreenMode *)mode
74 UIScreen *newScreen = [[UIScreen screens] objectAtIndex:screenIdx];
75 bool toExternal = _screenIdx == 0 && _screenIdx != screenIdx;
78 [newScreen setCurrentMode:mode];
80 //mode couldn't be applied to external screen
82 if([newScreen currentMode] != mode)
84 NSLog(@"Error setting screen mode!");
85 screenChangeEvent.Set();
88 _screenIdx = screenIdx;
90 //inform the other layers
91 _externalScreen = screenIdx != 0;
93 [_glView setScreen:newScreen withFrameBufferResize:TRUE];//will also resize the framebuffer
97 // portrait on external screen means its landscape for xbmc
98 [g_xbmcController activateScreen:newScreen withOrientation:UIInterfaceOrientationPortrait];// will attach the screen to xbmc mainwindow
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
106 if(toExternal)//changing the external screen might need some time ...
108 //deactivate any overscan compensation when switching to external screens
109 if([newScreen respondsToSelector:@selector(overscanCompensation)])
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).
120 [newScreen setOverscanCompensation:(UIScreenOverscanCompensation)3];
122 [newScreen setOverscanCompensation:3];
124 CLog::Log(LOGDEBUG, "[IOSScreenManager] Disabling overscancompensation.");
128 CLog::Log(LOGDEBUG, "[IOSScreenManager] Disabling overscancompensation not supported on this iOS version.");
131 [[IOSScreenManager sharedInstance] fadeFromBlack:timeSwitchingToExternalSecs];
135 [[IOSScreenManager sharedInstance] fadeFromBlack:timeSwitchingToInternalSecs];
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);
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
150 bool activateExternalTouchController = false;
151 int screenIdx = [[dict objectForKey:@"screenIdx"] intValue];
152 UIScreenMode *mode = [dict objectForKey:@"screenMode"];
154 if([self willSwitchToInternal:screenIdx] && _externalTouchController != nil)
156 _lastTouchControllerOrientation = [_externalTouchController interfaceOrientation];
157 [_externalTouchController release];
158 _externalTouchController = nil;
161 if([self willSwitchToExternal:screenIdx])
163 activateExternalTouchController = true;
167 [UIView animateWithDuration:timeFadeSecs delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
168 [_glView setAlpha:0.0];
170 completion:^(BOOL finished)
172 [self setScreen:screenIdx withMode:mode];
173 if(activateExternalTouchController)
175 _externalTouchController = [[IOSExternalTouchController alloc] init];
179 //--------------------------------------------------------------
180 - (bool) changeScreen: (unsigned int)screenIdx withMode:(UIScreenMode *)mode
182 //screen has changed - get the new screen
183 if(screenIdx >= [[UIScreen screens] count])
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])
192 //put the params into a dict
193 NSNumber *idx = [NSNumber numberWithInt:screenIdx];
194 NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:mode, @"screenMode",
195 idx, @"screenIdx", nil];
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])
202 [self performSelectorOnMainThread:@selector(changeScreenSelector:) withObject:dict waitUntilDone:YES];
203 screenChangeEvent.WaitMSec(30000);
207 [self changeScreenSelector:dict];
210 // re-enumerate audio devices in that case too
211 // as we might gain passthrough capabilities via HDMI
212 CAEFactory::DeviceChange();
215 //--------------------------------------------------------------
216 - (bool) willSwitchToExternal:(unsigned int) screenIdx
218 if(_screenIdx == 0 && screenIdx != _screenIdx)
224 //--------------------------------------------------------------
225 - (bool) willSwitchToInternal:(unsigned int) screenIdx
227 if(_screenIdx != 0 && screenIdx == 0)
233 //--------------------------------------------------------------
234 + (CGRect) getLandscapeResolution:(UIScreen *)screen
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
240 #if __IPHONE_OS_VERSION_MIN_REQUIRED > __IPHONE_4_2
241 res.size = screen.preferredMode.size;
243 Class brwin = objc_getClass("BRWindow");
244 res.size = [brwin interfaceFrame].size;
247 //main screen is in portrait mode (physically) so exchange height and width
248 if(screen == [UIScreen mainScreen])
251 res.size = CGSizeMake(frame.size.height, frame.size.width);
256 //--------------------------------------------------------------
257 - (void) screenDisconnect
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)
263 RESOLUTION_INFO res = CDisplaySettings::Get().GetResolutionInfo(RES_DESKTOP);//internal screen default res
264 g_Windowing.SetFullScreen(true, res, false);
267 //--------------------------------------------------------------
268 + (void) updateResolutions
270 g_Windowing.UpdateResolutions();
272 //--------------------------------------------------------------
275 if(_externalTouchController != nil )
277 [_externalTouchController release];
281 //--------------------------------------------------------------
282 + (id) sharedInstance
284 static IOSScreenManager* sharedManager = nil;
285 static dispatch_once_t onceToken;
286 dispatch_once(&onceToken, ^{
287 sharedManager = [[self alloc] init];
289 return sharedManager;