ArDrone SDK 1.8 added
[mardrone] / mardrone / ARDrone_SDK_Version_1_8_20110726 / ControlEngine / iPhone / Classes / Hide / TVOut.m
1 //
2 //  TVOut.m
3 //  ARDroneEngine
4 //
5 //  Created by Frédéric D'HAEYER on 22/12/09.
6 //  Copyright 2009 Parrot. All rights reserved.
7 //
8 #import "TVOut.h"
9
10 #ifdef ENABLE_AUTO_TVOUT
11 #import <QuartzCore/QuartzCore.h>
12 #import <CoreGraphics/CoreGraphics.h>
13
14 NSString * const UIApplicationDidSetupScreenMirroringNotification = @"UIApplicationDidSetupScreenMirroringNotification";
15 NSString * const UIApplicationDidDisableScreenMirroringNotification = @"UIApplicationDidDisableScreenMirroringNotification";
16
17 // Assuming CA loops at 60.0 fps (which is true on iPhone OS 3 : iPhone, iPad...)
18 #define CORE_ANIMATION_MAX_FRAMES_PER_SECOND (60)
19
20 CGImageRef UIGetScreenImage(); // Not so private API anymore
21
22 static CFTimeInterval startTime = 0;
23 static NSUInteger frames = 0;
24
25 @interface TVOut (ScreenMirroringPrivate)
26
27 - (void) setupMirroringForScreen:(UIScreen *)anExternalScreen;
28 - (void) disableMirroringOnCurrentScreen;
29 - (void) updateMirroredScreenOnDisplayLink;
30
31 @end
32
33 @implementation TVOut
34
35 static double targetFramesPerSecond = 10.0;
36 static CADisplayLink *displayLink = nil;
37 static UIScreen *mirroredScreen = nil;
38 static UIWindow *mirroredScreenWindow = nil;
39 static UIImageView *mirroredImageView = nil;
40
41 - (BOOL) isScreenMirroringActive
42 {
43         return (displayLink && !displayLink.paused);
44 }
45
46 - (UIScreen *) currentMirroringScreen
47 {
48         return mirroredScreen;
49 }
50
51 - (void) setupScreenMirroring
52 {
53         [self setupScreenMirroringWithFramesPerSecond:targetFramesPerSecond];
54 }
55
56 - (void) setupScreenMirroringWithFramesPerSecond:(double)fps
57 {
58         // Set the desired frame rate
59         targetFramesPerSecond = fps;
60         
61         // Register for screen notifications
62         NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
63         [center addObserver:self selector:@selector(screenDidConnect:) name:UIScreenDidConnectNotification object:nil]; 
64         [center addObserver:self selector:@selector(screenDidDisconnect:) name:UIScreenDidDisconnectNotification object:nil]; 
65         [center addObserver:self selector:@selector(screenModeDidChange:) name:UIScreenModeDidChangeNotification object:nil]; 
66         
67         // Register for interface orientation changes (so we don't need to query on every frame refresh)
68         [center addObserver:self selector:@selector(interfaceOrientationWillChange:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
69         
70         // Setup screen mirroring for an existing screen
71         NSArray *connectedScreens = [UIScreen screens];
72         if ([connectedScreens count] > 1) {
73                 UIScreen *mainScreen = [UIScreen mainScreen];
74                 for (UIScreen *aScreen in connectedScreens) {
75                         if (aScreen != mainScreen) {
76                                 // We've found an external screen !
77                                 [self setupMirroringForScreen:aScreen];
78                                 break;
79                         }
80                 }
81         }
82 }
83
84 - (void) disableScreenMirroring
85 {
86         // Unregister from screen notifications
87         NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
88         [center removeObserver:self name:UIScreenDidConnectNotification object:nil];
89         [center removeObserver:self name:UIScreenDidDisconnectNotification object:nil];
90         [center removeObserver:self name:UIScreenModeDidChangeNotification object:nil];
91         
92         // Device orientation
93         [center removeObserver:self name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
94         
95         // Remove mirroring
96         [self disableMirroringOnCurrentScreen];
97 }
98
99 #pragma mark -
100 #pragma mark UIScreen notifications
101
102 - (void) screenDidConnect:(NSNotification *)aNotification
103 {
104         NSLog(@"A new screen got connected: %@", [aNotification object]);
105         [self setupMirroringForScreen:[aNotification object]];
106 }
107
108 - (void) screenDidDisconnect:(NSNotification *)aNotification
109 {
110         NSLog(@"A screen got disconnected: %@", [aNotification object]);
111         [self disableMirroringOnCurrentScreen];
112 }
113
114 - (void) screenModeDidChange:(NSNotification *)aNotification
115 {
116         UIScreen *someScreen = [aNotification object];
117         NSLog(@"The screen mode for a screen did change: %@", [someScreen currentMode]);
118         
119         // Disable, then reenable with new config
120         [self disableMirroringOnCurrentScreen];
121         [self setupMirroringForScreen:[aNotification object]];
122 }
123
124 #pragma mark -
125 #pragma mark Interface orientation changes notification
126
127 - (void) updateMirroredWindowTransformForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
128 {
129         // Grab the secondary window layer
130         CALayer *mirrorLayer = mirroredScreenWindow.layer;
131         
132         // Rotate the screenshot to match interface orientation
133         switch (interfaceOrientation) {
134                 case UIInterfaceOrientationPortrait:
135                         mirrorLayer.transform = CATransform3DIdentity;
136                         break;
137                 case UIInterfaceOrientationLandscapeLeft:
138                         mirrorLayer.transform = CATransform3DMakeRotation(M_PI / 2, 0.0f, 0.0f, 1.0f);
139                         break;
140                 case UIInterfaceOrientationLandscapeRight:
141                         mirrorLayer.transform = CATransform3DMakeRotation(-(M_PI / 2), 0.0f, 0.0f, 1.0f);
142                         break;
143                 case UIInterfaceOrientationPortraitUpsideDown:
144                         mirrorLayer.transform = CATransform3DMakeRotation(M_PI, 0.0f, 0.0f, 1.0f);
145                         break;
146                 default:
147                         break;
148         }
149 }
150
151 - (void) interfaceOrientationWillChange:(NSNotification *)aNotification
152 {
153         NSDictionary *userInfo = [aNotification userInfo];
154         UIInterfaceOrientation newInterfaceOrientation = (UIInterfaceOrientation) [[userInfo objectForKey:UIApplicationStatusBarOrientationUserInfoKey] unsignedIntegerValue];
155         [self updateMirroredWindowTransformForInterfaceOrientation:newInterfaceOrientation];
156 }
157
158 #pragma mark -
159 #pragma mark Screen mirroring
160
161 - (void) setupMirroringForScreen:(UIScreen *)anExternalScreen
162 {       
163         // Reset timer
164         startTime = CFAbsoluteTimeGetCurrent();
165         frames = 0;
166         
167         // Set the new screen to mirror
168         BOOL done = NO;
169         UIScreenMode *mainScreenMode = [UIScreen mainScreen].currentMode;
170         for (UIScreenMode *externalScreenMode in anExternalScreen.availableModes) {
171                 if (CGSizeEqualToSize(externalScreenMode.size, mainScreenMode.size)) {
172                         // Select a screen that matches the main screen
173                         anExternalScreen.currentMode = externalScreenMode;
174                         done = YES;
175                         break;
176                 }
177         }
178         
179         if (!done && [anExternalScreen.availableModes count]) {
180                 anExternalScreen.currentMode = [anExternalScreen.availableModes objectAtIndex:0];
181         }
182         
183         [mirroredScreen release];
184         mirroredScreen = [anExternalScreen retain];
185         
186         // Setup window in external screen      
187         UIWindow *newWindow = [[UIWindow alloc] initWithFrame:mirroredScreen.bounds];
188         newWindow.opaque = YES;
189         newWindow.hidden = NO;
190         newWindow.backgroundColor = [UIColor blackColor];
191         newWindow.layer.contentsGravity = kCAGravityResizeAspect;
192         [mirroredScreenWindow release];
193         mirroredScreenWindow = [newWindow retain];
194         mirroredScreenWindow.screen = mirroredScreen;
195         [newWindow release];
196         
197         // Apply transform on mirrored window to match device's interface orientation
198         [self updateMirroredWindowTransformForInterfaceOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
199         
200         // Setup periodic callbacks
201         [displayLink invalidate];
202         [displayLink release], displayLink = nil;
203         
204         // Setup display link sync
205         displayLink = [[CADisplayLink displayLinkWithTarget:self selector:@selector(updateMirroredScreenOnDisplayLink)] retain];
206         [displayLink setFrameInterval:(targetFramesPerSecond >= CORE_ANIMATION_MAX_FRAMES_PER_SECOND) ? 1 : (CORE_ANIMATION_MAX_FRAMES_PER_SECOND / targetFramesPerSecond)];
207         
208         // We MUST add ourselves in the commons run loop in order to mirror during UITrackingRunLoopMode.
209         // Otherwise, the display won't be updated while fingering are touching the screen.
210         // This has a major impact on performance though...
211         [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
212         
213         // Post notification advertisting that we're setting up mirroring for the external screen
214         [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidSetupScreenMirroringNotification object:anExternalScreen];
215 }
216
217 - (void) disableMirroringOnCurrentScreen
218 {
219         // Post notification advertisting that we're tearing down mirroring
220         [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidDisableScreenMirroringNotification object:mirroredScreen];
221         
222         [displayLink invalidate];
223         [displayLink release], displayLink = nil;
224         
225         [mirroredScreen release], mirroredScreen = nil;
226         [mirroredScreenWindow release], mirroredScreenWindow = nil;
227         [mirroredImageView release], mirroredImageView = nil;
228 }
229
230 - (void) updateMirroredScreenOnDisplayLink
231 {
232         // Get a screenshot of the main window
233         CGImageRef mainWindowScreenshot = UIGetScreenImage();
234         if (mainWindowScreenshot) {
235                 // Copy to secondary screen
236                 mirroredScreenWindow.layer.contents = (id) mainWindowScreenshot;
237                 // Clean up as UIGetScreenImage does NOT respect retain / release semantics
238                 CFRelease(mainWindowScreenshot); 
239         }
240 }
241
242 @end
243
244 #endif // ENABLE_AUTO_TVOUT