]> git.sesse.net Git - vlc/blob - modules/gui/macosx/embeddedwindow.m
Zoom fullscreen effect on Mac OS X 10.4.
[vlc] / modules / gui / macosx / embeddedwindow.m
1 /*****************************************************************************
2  * embeddedwindow.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2005-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Benjamin Pracht <bigben at videolan dot org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 /* DisableScreenUpdates, SetSystemUIMode, ... */
29 #import <QuickTime/QuickTime.h>
30
31 #import "intf.h"
32 #import "controls.h"
33 #import "vout.h"
34 #import "embeddedwindow.h"
35 #import "fspanel.h"
36
37 /*****************************************************************************
38  * VLCEmbeddedWindow Implementation
39  *****************************************************************************/
40
41 @implementation VLCEmbeddedWindow
42
43 - (void)awakeFromNib
44 {
45     [self setDelegate: self];
46
47     [o_btn_backward setToolTip: _NS("Rewind")];
48     [o_btn_forward setToolTip: _NS("Fast Forward")];
49     [o_btn_fullscreen setToolTip: _NS("Fullscreen")];
50     [o_btn_play setToolTip: _NS("Play")];
51     [o_slider setToolTip: _NS("Position")];
52
53     o_img_play = [NSImage imageNamed: @"play_embedded"];
54     o_img_play_pressed = [NSImage imageNamed: @"play_embedded_blue"];
55     o_img_pause = [NSImage imageNamed: @"pause_embedded"];
56     o_img_pause_pressed = [NSImage imageNamed: @"pause_embedded_blue"];
57
58     o_saved_frame = NSMakeRect( 0.0f, 0.0f, 0.0f, 0.0f );
59
60     /* Useful to save o_view frame in fullscreen mode */
61     o_temp_view = [[NSView alloc] init];
62     [o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
63
64     o_fullscreen_window = nil;
65     o_fullscreen_anim1 = o_fullscreen_anim2 = nil;
66
67     /* Not fullscreen when we wake up */
68     [o_btn_fullscreen setState: NO];
69 }
70
71 - (void)setTime:(NSString *)o_arg_time position:(float)f_position
72 {
73     [o_time setStringValue: o_arg_time];
74     [o_slider setFloatValue: f_position];
75 }
76
77 - (void)playStatusUpdated:(int)i_status
78 {
79     if( i_status == PLAYING_S )
80     {
81         [o_btn_play setImage: o_img_pause];
82         [o_btn_play setAlternateImage: o_img_pause_pressed];
83         [o_btn_play setToolTip: _NS("Pause")];
84     }
85     else
86     {
87         [o_btn_play setImage: o_img_play];
88         [o_btn_play setAlternateImage: o_img_play_pressed];
89         [o_btn_play setToolTip: _NS("Play")];
90     }
91 }
92
93 - (void)setSeekable:(BOOL)b_seekable
94 {
95     [o_btn_forward setEnabled: b_seekable];
96     [o_btn_backward setEnabled: b_seekable];
97     [o_slider setEnabled: b_seekable];
98 }
99
100 - (void)zoom:(id)sender
101 {
102     if( ![self isZoomed] )
103     {
104         NSRect zoomRect = [[self screen] frame];
105         o_saved_frame = [self frame];
106         /* we don't have to take care of the eventual menu bar and dock
107           as zoomRect will be cropped automatically by setFrame:display:
108           to the right rectangle */
109         [self setFrame: zoomRect display: YES animate: YES];
110     }
111     else
112     {
113         /* unzoom to the saved_frame if the o_saved_frame coords look sound
114            (just in case) */
115         if( o_saved_frame.size.width > 0 && o_saved_frame.size.height > 0 )
116             [self setFrame: o_saved_frame display: YES animate: YES];
117     }
118 }
119
120 - (BOOL)windowShouldClose:(id)sender
121 {
122     playlist_t * p_playlist = pl_Yield( VLCIntf );
123
124     playlist_Stop( p_playlist );
125     vlc_object_release( p_playlist );
126     return YES;
127 }
128
129 /*****************************************************************************
130  * Fullscreen support
131  */
132 - (void)enterFullscreen
133 {
134     NSMutableDictionary *dict1, *dict2;
135     NSScreen *screen;
136     NSRect screen_rect;
137     NSRect rect;
138     vout_thread_t *p_vout = vlc_object_find( VLCIntf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
139     BOOL blackout_other_displays = var_GetBool( p_vout, "macosx-black" );
140
141     /* We should get the screen pointed by var_GetInteger( p_vout, "video-device" ) */
142     screen = [self screen];
143
144     vlc_object_release( p_vout );
145
146     screen_rect = [screen frame];
147
148     [o_btn_fullscreen setState: YES];
149
150     [NSCursor setHiddenUntilMouseMoves: YES];
151     
152     if (blackout_other_displays)
153         ; /* We should do something like [screen blackoutOtherScreens]; */
154
155     /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
156     if (!o_fullscreen_window)
157     {
158         /* We can't change the styleMask of an already created NSWindow, so we create an other window, and do eye catching stuff */
159
160         rect = [[o_view superview] convertRect: [o_view frame] toView: nil]; /* Convert to Window base coord */
161         rect.origin.x += [self frame].origin.x;
162         rect.origin.y += [self frame].origin.y;
163         o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
164         [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
165         [o_fullscreen_window setCanBecomeKeyWindow: YES];
166
167         if (![self isVisible] || [self alphaValue] == 0.0 || MACOS_VERSION < 10.4f)
168         {
169             /* We don't animate if we are not visible or if we are running on
170              * Mac OS X <10.4 which doesn't support NSAnimation, instead we
171              * simply fade the display */
172             CGDisplayFadeReservationToken token;
173             
174             [o_fullscreen_window setFrame:screen_rect display:NO];
175             
176             CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
177             CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
178             
179             if (screen == [NSScreen mainScreen])
180                 SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
181             
182             [[self contentView] replaceSubview:o_view with:o_temp_view];
183             [o_temp_view setFrame:[o_view frame]];
184             [o_fullscreen_window setContentView:o_view];
185             [o_fullscreen_window makeKeyAndOrderFront:self];
186             [self orderOut: self];
187
188             CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
189             CGReleaseDisplayFadeReservation( token);
190             [self hasBecomeFullscreen];
191             return;
192         }
193         
194         /* Make sure we don't see the o_view disappearing of the screen during this operation */
195         DisableScreenUpdates();
196         [[self contentView] replaceSubview:o_view with:o_temp_view];
197         [o_temp_view setFrame:[o_view frame]];
198         [o_fullscreen_window setContentView:o_view];
199         [o_fullscreen_window makeKeyAndOrderFront:self];
200         EnableScreenUpdates();
201     }
202
203     if (MACOS_VERSION < 10.4f)
204     {
205         /* We were already fullscreen nothing to do when NSAnimation
206          * is not supported */
207         return;
208     }
209
210     if (o_fullscreen_anim1)
211     {
212         [o_fullscreen_anim1 stopAnimation];
213         [o_fullscreen_anim1 release];
214     }
215     if (o_fullscreen_anim2)
216     {
217         [o_fullscreen_anim2 stopAnimation];
218         [o_fullscreen_anim2 release];
219     }
220  
221     if (screen == [NSScreen mainScreen])
222         SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
223
224     dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
225     dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
226
227     [dict1 setObject:self forKey:NSViewAnimationTargetKey];
228     [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
229
230     [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
231     [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
232     [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
233
234     /* Strategy with NSAnimation allocation:
235         - Keep at most 2 animation at a time
236         - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
237     */
238     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
239     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
240
241     [dict1 release];
242     [dict2 release];
243
244     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
245     [o_fullscreen_anim1 setDuration: 0.3];
246     [o_fullscreen_anim1 setFrameRate: 30];
247     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
248     [o_fullscreen_anim2 setDuration: 0.2];
249     [o_fullscreen_anim2 setFrameRate: 30];
250
251     [o_fullscreen_anim2 setDelegate: self];
252     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
253
254     [o_fullscreen_anim1 startAnimation];
255 }
256
257 - (void)hasBecomeFullscreen
258 {
259     [o_fullscreen_window makeFirstResponder: [[[VLCMain sharedInstance] getControls] getVoutView]];
260
261     [o_fullscreen_window makeKeyWindow];
262     [o_fullscreen_window setAcceptsMouseMovedEvents: TRUE];
263
264     /* tell the fspanel to move itself to front next time it's triggered */
265     [[[[VLCMain sharedInstance] getControls] getFSPanel] setVoutWasUpdated: 0 /* Get this right */];
266     
267     [[[[VLCMain sharedInstance] getControls] getFSPanel] setActive: nil];
268 }
269
270 - (void)leaveFullscreen
271 {
272     NSMutableDictionary *dict1, *dict2;
273     NSRect frame;
274     
275     [o_btn_fullscreen setState: NO];
276
277     /* Don't do anything if o_fullscreen_window is already closed */
278     if (!o_fullscreen_window)
279         return;
280
281     if (MACOS_VERSION < 10.4f)
282     {
283         /* We don't animate if we are not visible or if we are running on
284         * Mac OS X <10.4 which doesn't support NSAnimation, instead we
285         * simply fade the display */
286         CGDisplayFadeReservationToken token;
287
288         CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
289         CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
290
291         [[[[VLCMain sharedInstance] getControls] getFSPanel] setNonActive: nil];
292         SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
293
294         [self makeKeyAndOrderFront:self];
295         [self hasEndedFullscreen];
296
297         CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
298         CGReleaseDisplayFadeReservation( token);
299         return;
300     }
301
302     [[[[VLCMain sharedInstance] getControls] getFSPanel] setNonActive: nil];
303     SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
304
305     if (o_fullscreen_anim1)
306     {
307         [o_fullscreen_anim1 stopAnimation];
308         [o_fullscreen_anim1 release];
309     }
310     if (o_fullscreen_anim2)
311     {
312         [o_fullscreen_anim2 stopAnimation];
313         [o_fullscreen_anim2 release];
314     }
315
316     frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
317     frame.origin.x += [self frame].origin.x; 
318     frame.origin.y += [self frame].origin.y;
319
320     dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
321     [dict2 setObject:self forKey:NSViewAnimationTargetKey];
322     [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
323
324     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
325     [dict2 release];
326
327     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
328     [o_fullscreen_anim2 setDuration: 0.3];
329     [o_fullscreen_anim2 setFrameRate: 30];
330
331     [o_fullscreen_anim2 setDelegate: self];
332
333     dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
334
335     [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
336     [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
337     [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
338
339     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
340     [dict1 release];
341
342     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
343     [o_fullscreen_anim1 setDuration: 0.2];
344     [o_fullscreen_anim1 setFrameRate: 30];
345     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
346     [o_fullscreen_anim1 startAnimation];
347 }
348
349 - (void)hasEndedFullscreen
350 {
351     /* This function is private and should be only triggered at the end of the fullscreen change animation */
352     /* Make sure we don't see the o_view disappearing of the screen during this operation */
353     DisableScreenUpdates();
354     [o_view retain];
355     [o_view removeFromSuperviewWithoutNeedingDisplay];
356     [[self contentView] replaceSubview:o_temp_view with:o_view];
357     [o_view release];
358     [o_view setFrame:[o_temp_view frame]];
359     [self makeKeyAndOrderFront:self];
360     [o_fullscreen_window orderOut: self];
361     EnableScreenUpdates();
362
363     [o_fullscreen_window release];
364     o_fullscreen_window = nil;
365 }
366
367 - (void)animationDidEnd:(NSAnimation*)animation
368 {
369     NSArray *viewAnimations;
370
371     if ([animation currentValue] < 1.0)
372         return;
373
374     /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
375     viewAnimations = [o_fullscreen_anim2 viewAnimations];
376     if ([viewAnimations count] >=1 &&
377         [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
378     {
379         /* Fullscreen ended */
380         [self hasEndedFullscreen];
381     }
382     else
383     {
384         /* Fullscreen started */
385         [self hasBecomeFullscreen];
386     }
387 }
388
389 @end