1 /*****************************************************************************
2 * vout.m: MacOS X video output module
3 *****************************************************************************
4 * Copyright (C) 2001-2003 VideoLAN
7 * Authors: Colin Delacroix <colin@zoy.org>
8 * Florian G. Pflug <fgp@phlo.org>
9 * Jon Lech Johansen <jon-vl@nanocrew.net>
10 * Derk-Jan Hartman <hartman at videolan dot org>
11 * Eric Petit <titer@m0k.org>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
26 *****************************************************************************/
28 /*****************************************************************************
30 *****************************************************************************/
31 #include <errno.h> /* ENOMEM */
32 #include <stdlib.h> /* free() */
33 #include <string.h> /* strerror() */
35 /* BeginFullScreen, EndFullScreen */
36 #include <QuickTime/QuickTime.h>
43 /*****************************************************************************
44 * VLCWindow implementation
45 *****************************************************************************/
46 @implementation VLCWindow
48 - (id)initWithVout:(vout_thread_t *)_p_vout frame:(NSRect *)s_frame
50 [self setReleasedWhenClosed: YES];
54 /* p_real_vout: the vout we have to use to check for video-on-top
55 and a few other things. If we are the QuickTime output, it's us.
56 It we are the OpenGL provider, it is our parent. */
57 if( p_vout->i_object_type == VLC_OBJECT_OPENGL )
59 p_real_vout = (vout_thread_t *) p_vout->p_parent;
66 p_fullscreen_state = NULL;
67 i_time_mouse_last_moved = mdate();
70 vlc_bool_t b_main_screen;
72 var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
73 var_Create( p_vout, "macosx-fill", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
74 var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
75 var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
77 /* Setup the menuitem for the multiple displays. Read the vlc preference (macosx-vdev) for the primary display */
78 NSArray * o_screens = [NSScreen screens];
79 if( [o_screens count] > 0 && var_Type( p_real_vout, "video-device" ) == 0 )
82 vlc_value_t val, val2, text;
85 var_Get( p_real_vout, "macosx-vdev", &val );
87 var_Create( p_real_vout, "video-device", VLC_VAR_INTEGER |
89 text.psz_string = _("Video device");
90 var_Change( p_real_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
92 NSEnumerator * o_enumerator = [o_screens objectEnumerator];
94 while( (o_screen = [o_enumerator nextObject]) != NULL )
97 NSRect s_rect = [o_screen frame];
99 snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
100 "%s %d (%dx%d)", _("Screen"), i,
101 (int)s_rect.size.width, (int)s_rect.size.height );
103 text.psz_string = psz_temp;
105 var_Change( p_real_vout, "video-device",
106 VLC_VAR_ADDCHOICE, &val2, &text );
108 if( ( i - 1 ) == val.i_int )
110 var_Set( p_real_vout, "video-device", val2 );
115 var_AddCallback( p_real_vout, "video-device", vout_VarCallback,
118 val2.b_bool = VLC_TRUE;
119 var_Set( p_real_vout, "intf-change", val2 );
122 /* Find out on which screen to open the window */
123 int i_device = var_GetInteger( p_real_vout, "video-device" );
126 /* No preference specified. Use the main screen */
127 o_screen = [NSScreen mainScreen];
132 NSArray *o_screens = [NSScreen screens];
134 if( [o_screens count] < (unsigned) i_device )
136 o_screen = [NSScreen mainScreen];
142 o_screen = [o_screens objectAtIndex: i_device];
143 var_SetInteger( p_real_vout, "macosx-vdev", i_device );
144 b_main_screen = ( i_device == 0 );
148 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
150 if( p_vout->b_fullscreen )
152 NSRect screen_rect = [o_screen frame];
153 screen_rect.origin.x = screen_rect.origin.y = 0;
155 /* Creates a window with size: screen_rect on o_screen */
156 [self initWithContentRect: screen_rect
157 styleMask: NSBorderlessWindowMask
158 backing: NSBackingStoreBuffered
159 defer: YES screen: o_screen];
163 BeginFullScreen( &p_fullscreen_state, NULL, 0, 0,
164 NULL, NULL, fullScreenAllowEvents );
169 unsigned int i_stylemask = NSTitledWindowMask |
170 NSMiniaturizableWindowMask |
171 NSClosableWindowMask |
172 NSResizableWindowMask;
177 s_rect.size.width = p_vout->i_window_width;
178 s_rect.size.height = p_vout->i_window_height;
185 [self initWithContentRect: s_rect
186 styleMask: i_stylemask
187 backing: NSBackingStoreBuffered
188 defer: YES screen: o_screen];
190 [self setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )];
192 if( var_GetBool( p_real_vout, "video-on-top" ) )
194 [self setLevel: NSStatusWindowLevel];
204 [self makeKeyAndOrderFront: nil];
206 /* We'll catch mouse events */
207 [self setAcceptsMouseMovedEvents: YES];
208 [self makeFirstResponder: self];
216 if( p_fullscreen_state )
218 EndFullScreen( p_fullscreen_state, NULL );
223 - (void)setOnTop:(bool)b_on_top
227 [self setLevel: NSStatusWindowLevel];
231 [self setLevel: NSNormalWindowLevel];
235 - (void)hideMouse:(bool)b_hide
239 NSView *o_contents = [self contentView];
241 ml = [self convertScreenToBase:[NSEvent mouseLocation]];
242 ml = [o_contents convertPoint:ml fromView:nil];
243 b_inside = [o_contents mouse: ml inRect: [o_contents bounds]];
245 if( b_hide && b_inside )
247 [NSCursor setHiddenUntilMouseMoves: YES];
251 [NSCursor setHiddenUntilMouseMoves: NO];
257 if( p_fullscreen_state )
259 if( mdate() - i_time_mouse_last_moved > 3000000 )
261 [self hideMouse: YES];
266 [self hideMouse: NO];
269 /* Disable screensaver */
270 UpdateSystemActivity( UsrActivity );
273 - (void)scaleWindowWithFactor: (float)factor
276 int i_corrected_height, i_corrected_width;
278 NSPoint topleftscreen;
280 if ( !p_vout->b_fullscreen )
283 topleftbase.y = [self frame].size.height;
284 topleftscreen = [self convertBaseToScreen: topleftbase];
286 if( p_vout->render.i_height * p_vout->render.i_aspect >
287 p_vout->render.i_width * VOUT_ASPECT_FACTOR )
289 i_corrected_width = p_vout->render.i_height * p_vout->render.i_aspect /
291 newsize.width = (int) ( i_corrected_width * factor );
292 newsize.height = (int) ( p_vout->render.i_height * factor );
296 i_corrected_height = p_vout->render.i_width * VOUT_ASPECT_FACTOR /
297 p_vout->render.i_aspect;
298 newsize.width = (int) ( p_vout->render.i_width * factor );
299 newsize.height = (int) ( i_corrected_height * factor );
302 [self setContentSize: newsize];
304 [self setFrameTopLeftPoint: topleftscreen];
305 p_vout->i_changes |= VOUT_SIZE_CHANGE;
309 - (void)toggleFloatOnTop
312 if( var_Get( p_vout, "video-on-top", &val )>=0 && val.b_bool)
314 val.b_bool = VLC_FALSE;
315 var_Set( p_vout, "video-on-top", val );
319 val.b_bool = VLC_TRUE;
320 var_Set( p_vout, "video-on-top", val );
324 - (void)toggleFullscreen
327 val.b_bool = !p_real_vout->b_fullscreen;
328 var_Set( p_real_vout, "fullscreen", val );
333 return( p_vout->b_fullscreen );
338 vout_Control( p_vout, VOUT_SNAPSHOT );
341 - (BOOL)canBecomeKeyWindow
346 /* Sometimes crashes VLC....
347 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
349 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event];
352 - (void)keyDown:(NSEvent *)o_event
356 unsigned int i_pressed_modifiers = 0;
359 i_pressed_modifiers = [o_event modifierFlags];
361 if( i_pressed_modifiers & NSShiftKeyMask )
362 val.i_int |= KEY_MODIFIER_SHIFT;
363 if( i_pressed_modifiers & NSControlKeyMask )
364 val.i_int |= KEY_MODIFIER_CTRL;
365 if( i_pressed_modifiers & NSAlternateKeyMask )
366 val.i_int |= KEY_MODIFIER_ALT;
367 if( i_pressed_modifiers & NSCommandKeyMask )
368 val.i_int |= KEY_MODIFIER_COMMAND;
370 key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
374 /* Escape should always get you out of fullscreen */
375 if( key == (unichar) 0x1b )
377 if( [self isFullscreen] )
379 [self toggleFullscreen];
382 else if ( key == ' ' )
385 val.i_int = config_GetInt( p_vout, "key-play-pause" );
386 var_Set( p_vout->p_vlc, "key-pressed", val );
390 val.i_int |= CocoaKeyToVLC( key );
391 var_Set( p_vout->p_vlc, "key-pressed", val );
396 [super keyDown: o_event];
402 NSMutableString * o_title;
403 playlist_t * p_playlist;
410 p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
413 if( p_playlist == NULL )
418 vlc_mutex_lock( &p_playlist->object_lock );
419 o_title = [NSMutableString stringWithUTF8String:
420 p_playlist->status.p_item->input.psz_uri];
421 vlc_mutex_unlock( &p_playlist->object_lock );
422 vlc_object_release( p_playlist );
426 NSRange prefix_range = [o_title rangeOfString: @"file:"];
427 if( prefix_range.location != NSNotFound )
429 [o_title deleteCharactersInRange: prefix_range];
432 [self setTitleWithRepresentedFilename: o_title];
436 [self setTitle: [NSString stringWithCString: VOUT_TITLE]];
440 /* This is actually the same as VLCControls::stop. */
441 - (BOOL)windowShouldClose:(id)sender
443 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
445 if( p_playlist == NULL )
450 playlist_Stop( p_playlist );
451 vlc_object_release( p_playlist );
453 /* The window will be closed by the intf later. */
457 - (BOOL)acceptsFirstResponder
462 - (BOOL)becomeFirstResponder
467 - (BOOL)resignFirstResponder
469 /* We need to stay the first responder or we'll miss some
474 - (void)mouseDown:(NSEvent *)o_event
478 switch( [o_event type] )
480 case NSLeftMouseDown:
482 var_Get( p_vout, "mouse-button-down", &val );
484 var_Set( p_vout, "mouse-button-down", val );
489 [super mouseDown: o_event];
494 - (void)otherMouseDown:(NSEvent *)o_event
498 switch( [o_event type] )
500 case NSOtherMouseDown:
502 var_Get( p_vout, "mouse-button-down", &val );
504 var_Set( p_vout, "mouse-button-down", val );
509 [super mouseDown: o_event];
514 - (void)rightMouseDown:(NSEvent *)o_event
518 switch( [o_event type] )
520 case NSRightMouseDown:
522 var_Get( p_vout, "mouse-button-down", &val );
524 var_Set( p_vout, "mouse-button-down", val );
529 [super mouseDown: o_event];
534 - (void)mouseUp:(NSEvent *)o_event
538 switch( [o_event type] )
543 b_val.b_bool = VLC_TRUE;
544 var_Set( p_vout, "mouse-clicked", b_val );
546 var_Get( p_vout, "mouse-button-down", &val );
548 var_Set( p_vout, "mouse-button-down", val );
553 [super mouseUp: o_event];
558 - (void)otherMouseUp:(NSEvent *)o_event
562 switch( [o_event type] )
566 var_Get( p_vout, "mouse-button-down", &val );
568 var_Set( p_vout, "mouse-button-down", val );
573 [super mouseUp: o_event];
578 - (void)rightMouseUp:(NSEvent *)o_event
582 switch( [o_event type] )
586 var_Get( p_vout, "mouse-button-down", &val );
588 var_Set( p_vout, "mouse-button-down", val );
593 [super mouseUp: o_event];
598 - (void)mouseDragged:(NSEvent *)o_event
600 [self mouseMoved: o_event];
603 - (void)otherMouseDragged:(NSEvent *)o_event
605 [self mouseMoved: o_event];
608 - (void)rightMouseDragged:(NSEvent *)o_event
610 [self mouseMoved: o_event];
613 - (void)mouseMoved:(NSEvent *)o_event
620 i_time_mouse_last_moved = mdate();
622 o_view = [self contentView];
623 s_rect = [o_view bounds];
624 ml = [o_view convertPoint: [o_event locationInWindow] fromView: nil];
625 b_inside = [o_view mouse: ml inRect: s_rect];
630 int i_width, i_height, i_x, i_y;
632 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
633 (unsigned int)s_rect.size.height,
634 &i_x, &i_y, &i_width, &i_height );
636 val.i_int = ( ((int)ml.x) - i_x ) *
637 p_vout->render.i_width / i_width;
638 var_Set( p_vout, "mouse-x", val );
640 if( [[o_view className] isEqualToString: @"VLCGLView"] )
642 val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
643 p_vout->render.i_height / i_height;
647 val.i_int = ( ((int)ml.y) - i_y ) *
648 p_vout->render.i_height / i_height;
650 var_Set( p_vout, "mouse-y", val );
652 val.b_bool = VLC_TRUE;
653 var_Set( p_vout, "mouse-moved", val );
656 [super mouseMoved: o_event];