317e6c8922d3c68f512cde84da461c0d48d2eea7
[vlc.git] / extras / MacOSX / Framework / Sources / VLCVideoView.m
1 /*****************************************************************************
2  * VLCVideoView.h: VLC.framework VLCVideoView implementation
3  *****************************************************************************
4  * Copyright (C) 2007 Pierre d'Herbemont
5  * Copyright (C) 2007 the VideoLAN team
6  * $Id$
7  *
8  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #import "VLCVideoView.h"
26 #import "VLCLibrary.h"
27 #import "VLCEventManager.h"
28
29 /* Libvlc */
30 #include <vlc/vlc.h>
31 #include <vlc/libvlc.h>
32
33 #import <QuartzCore/QuartzCore.h>
34
35 /* Notifications */
36 NSString *VLCVideoViewEnteredFullScreen = @"VLCVideoViewEnteredFullScreen";
37 NSString *VLCVideoViewLeftFullScreen = @"VLCVideoViewLeftFullScreen";
38
39 /******************************************************************************
40  * Soon deprecated stuff 
41  */
42
43 /* This is a forward reference to VLCOpenGLVoutView specified in minimal_macosx
44    library.  We could get rid of this, but it prevents warnings from the 
45    compiler. (Scheduled to deletion) */
46 @interface VLCOpenGLVoutView : NSView
47 - (void)detachFromVout;
48 @end
49
50 /* Depreacted methods */
51 @interface VLCVideoView (Deprecated)
52 - (void)setStretchesVideo:(BOOL)value;
53 - (BOOL)stretchesVideo;
54
55 - (void)addVoutSubview:(NSView *)aView;  /* (Scheduled to deletion) */
56 - (void)removeVoutSubview:(NSView *)aView;  /* (Scheduled to deletion) */
57 @end
58
59 /******************************************************************************
60  * VLCVideoView (Private) 
61  */
62 @interface VLCVideoView ()
63 /* Property */
64 @property (readwrite, assign) BOOL fullscreen;
65 @end
66
67 @interface VLCVideoView (Private)
68 /* Method */
69 - (void)addVoutLayer:(CALayer *)aLayer;
70 @end
71
72
73
74 /******************************************************************************
75  * Interface & Implementation VLCConstraintLayoutManager
76  *
77  * Manage the size of the video layer 
78  */
79 @interface VLCConstraintLayoutManager : CAConstraintLayoutManager
80 {
81     CGSize originalVideoSize;
82     BOOL  fillScreenEntirely;
83 }
84 @property BOOL  fillScreenEntirely;
85 @property CGSize originalVideoSize;
86 @end
87
88 @implementation VLCConstraintLayoutManager 
89 @synthesize  fillScreenEntirely;
90 @synthesize  originalVideoSize;
91 - (id)init
92 {
93     if( self = [super init] )
94     {
95         self.originalVideoSize = CGSizeMake(0., 0.);
96         self.fillScreenEntirely = NO;
97     }
98     return self;
99
100 }
101 + (id)layoutManager
102 {
103     return [[[VLCConstraintLayoutManager alloc] init] autorelease];
104 }
105 - (void)layoutSublayersOfLayer:(CALayer *)layer
106 {
107     /* Called by CA, when our rootLayer needs layout */
108     [super layoutSublayersOfLayer:layer];
109
110     /* After having done everything normally resize the vlcopengllayer */
111     if( [[layer sublayers] count] > 0 && [[[[layer sublayers] objectAtIndex:0] name] isEqualToString:@"vlcopengllayer"])
112     {
113         CALayer * videolayer = [[layer sublayers] objectAtIndex:0];
114         CGRect bounds = layer.bounds;
115         float new_height = (bounds.size.width * originalVideoSize.height) / originalVideoSize.width;
116
117         if( fillScreenEntirely )
118         {
119             if( bounds.size.height > new_height )
120                 bounds.size.height = new_height;
121             else
122                 bounds.size.width = (bounds.size.height * originalVideoSize.width) / originalVideoSize.height;
123         }
124         else
125         {
126             if( bounds.size.height > new_height )
127                 bounds.size.width = (bounds.size.height * originalVideoSize.width) / originalVideoSize.height;
128             else
129                 bounds.size.height = new_height;
130         }
131
132         bounds.origin = CGPointMake( 0.0, 0.0 );
133         videolayer.bounds = bounds;
134         videolayer.position = CGPointMake((layer.bounds.size.width - layer.bounds.origin.x)/2, (layer.bounds.size.height - layer.bounds.origin.y)/2);
135     }
136 }
137 @end
138
139 /******************************************************************************
140  * Implementation VLCVideoView 
141  */
142
143 @implementation VLCVideoView
144 @synthesize fullscreen;
145
146 - (BOOL)fillScreen
147 {
148     return [layoutManager fillScreenEntirely];
149 }
150 - (void)setFillScreen:(BOOL)fillScreen
151 {
152     [layoutManager setFillScreenEntirely:fillScreen];
153     [[self layer] setNeedsLayout];
154 }
155
156
157 - (id)initWithFrame:(NSRect)rect
158 {
159     if (self = [super initWithFrame:rect]) 
160     {
161         delegate = nil;
162         [self setBackColor:[NSColor blackColor]];
163         [self setStretchesVideo:NO];
164         [self setAutoresizesSubviews:YES];
165         [self setFillScreen: NO];
166         layoutManager = [VLCConstraintLayoutManager layoutManager];
167     }
168     return self;
169 }
170
171 - (void)dealloc
172 {
173     [layoutManager release];
174     delegate = nil;
175     [backColor release];
176     [super dealloc];
177 }
178
179 - (void)setDelegate:(id)value
180 {
181     delegate = value;
182 }
183
184 - (id)delegate
185 {
186     return delegate;
187 }
188
189 - (void)setBackColor:(NSColor *)value
190 {
191     if (backColor != value)
192     {
193         [backColor release];
194         backColor = [value retain];
195     }
196 }
197
198 - (NSColor *)backColor
199 {
200     return backColor;
201 }
202
203 /* This is a LibVLC notification that we're about to enter into full screen,
204    there is no other place where I can see where we can trap this event */
205 - (void)enterFullscreen
206 {
207     // Go ahead and send a notification to the world we're going into full screen
208     [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self 
209                                                    withDelegateMethod:nil 
210                                                  withNotificationName:VLCVideoViewEnteredFullScreen];
211     
212     [super enterFullScreenMode:[[self window] screen] withOptions:nil];
213     self.fullscreen = YES;
214 }
215
216 /* This is a LibVLC notification that we're about to enter leaving full screen,
217    there is no other place where I can see where we can trap this event */
218 - (void)leaveFullscreen
219 {
220     // Go ahead and send a notification to the world we're leaving full screen
221     [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self 
222                                                    withDelegateMethod:nil 
223                                                  withNotificationName:VLCVideoViewLeftFullScreen];
224     
225     // There is nothing else to do, as this object strictly displays the video feed
226     [super exitFullScreenModeWithOptions:nil];
227     self.fullscreen = NO;
228 }
229
230 - (void)drawRect:(NSRect)aRect
231 {
232     [self lockFocus];
233     [backColor set];
234     NSRectFill(aRect);
235     [self unlockFocus];
236 }
237
238 - (BOOL)isOpaque
239 {
240     return YES;
241 }
242
243 - (void)mouseDown:(NSEvent *)theEvent
244 {
245     if([theEvent clickCount] != 2)
246         return;
247     if(self.fullscreen)
248         [self leaveFullscreen];
249     else
250         [self enterFullscreen];
251 }
252 @end
253
254 /******************************************************************************
255  * Implementation VLCVideoView  (Private)
256  */
257
258 @implementation VLCVideoView (Private)
259
260 /* This is called by the libvlc module 'opengllayer' as soon as there is one 
261  * vout available
262  */
263 - (void)addVoutLayer:(CALayer *)aLayer
264 {
265     [CATransaction begin];
266     [self setWantsLayer: YES];
267         CALayer * rootLayer = [self layer];
268
269     aLayer.name = @"vlcopengllayer";
270
271     [layoutManager setOriginalVideoSize:aLayer.bounds.size];
272     [rootLayer setLayoutManager:layoutManager];
273     [rootLayer insertSublayer:aLayer atIndex:0];
274
275     [aLayer setNeedsLayout];
276     [aLayer setNeedsDisplay];
277     [rootLayer setNeedsDisplay];
278     [rootLayer layoutIfNeeded];
279     [CATransaction commit];
280 }
281
282 @end
283
284 /******************************************************************************
285  * Implementation VLCVideoView  (Deprecated)
286  */
287
288 @implementation VLCVideoView (Deprecated)
289
290 - (void)setStretchesVideo:(BOOL)value
291 {
292     stretchesVideo = value;
293 }
294
295 - (BOOL)stretchesVideo
296 {
297     return stretchesVideo;
298 }
299
300 /* This is called by the libvlc module 'minimal_macosx' as soon as there is one 
301  * vout available
302  */
303 - (void)addVoutSubview:(NSView *)aView /* (Scheduled to deletion) */
304 {
305     /* This is where the real video comes from */
306     if( [[self subviews] count] )
307     {
308         /* XXX: This is a hack until core gets fixed */
309         int i;
310         for( i = 0; i < [[self subviews] count]; i++ )
311         {
312             [[[self subviews] objectAtIndex:i] detachFromVout];
313             [[[self subviews] objectAtIndex:i] retain];
314             [[[self subviews] objectAtIndex:i] removeFromSuperview];
315         }
316     }
317
318     [aView setFrame:[self bounds]];
319     
320     [self addSubview:aView];
321
322     // TODO: Should we let the media player specify these values?
323     [aView setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
324 }
325
326 - (void)removeVoutSubview:(NSView *)view /* (Scheduled to deletion) */
327 {
328     // Should we do something?  I don't know, however the protocol requires
329     // this to be implemented
330 }
331 @end