]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
* Fix some crashes because of new playlist core
[vlc] / modules / gui / macosx / vout.m
1 /*****************************************************************************
2  * vout.m: MacOS X video output module
3  *****************************************************************************
4  * Copyright (C) 2001-2003 VideoLAN
5  * $Id$
6  *
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>
12  *
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.
17  * 
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.
22  *
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  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include <errno.h>                                                 /* ENOMEM */
32 #include <stdlib.h>                                                /* free() */
33 #include <string.h>                                            /* strerror() */
34
35 /* BeginFullScreen, EndFullScreen */
36 #include <QuickTime/QuickTime.h>
37
38 #include <vlc_keys.h>
39
40 #include "intf.h"
41 #include "vout.h"
42
43 /*****************************************************************************
44  * VLCWindow implementation
45  *****************************************************************************/
46 @implementation VLCWindow
47
48 - (id)initWithVout:(vout_thread_t *)_p_vout frame:(NSRect *)s_frame
49 {
50     [self setReleasedWhenClosed: YES];
51
52     p_vout = _p_vout;
53     p_fullscreen_state = NULL;
54     i_time_mouse_last_moved = mdate();
55
56     NSScreen * o_screen;
57     vlc_bool_t b_main_screen;
58
59     /* Find out on which screen to open the window */
60     int i_device = var_GetInteger( p_vout, "video-device" );
61     if( i_device < 0 )
62     {
63          /* No preference specified. Use the main screen */
64         o_screen = [NSScreen mainScreen];
65         b_main_screen = 1;
66     }
67     else
68     {
69         NSArray *o_screens = [NSScreen screens];
70         
71         if( [o_screens count] < (unsigned) i_device )
72         {
73             o_screen = [NSScreen mainScreen];
74             b_main_screen = 1;
75         }
76         else
77         {
78             i_device--;
79             o_screen = [o_screens objectAtIndex: i_device];
80             var_SetInteger( p_vout, "macosx-vdev", i_device );
81             b_main_screen = ( i_device == 0 );
82         }
83     }
84
85     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
86
87     if( p_vout->b_fullscreen )
88     {
89         NSRect screen_rect = [o_screen frame];
90         screen_rect.origin.x = screen_rect.origin.y = 0;
91
92         /* Creates a window with size: screen_rect on o_screen */
93         [self initWithContentRect: screen_rect
94               styleMask: NSBorderlessWindowMask
95               backing: NSBackingStoreBuffered
96               defer: YES screen: o_screen];
97
98         if( b_main_screen )
99         {
100             BeginFullScreen( &p_fullscreen_state, NULL, 0, 0,
101                              NULL, NULL, fullScreenAllowEvents );
102         }
103     }
104     else
105     {
106         unsigned int i_stylemask = NSTitledWindowMask |
107                                    NSMiniaturizableWindowMask |
108                                    NSClosableWindowMask |
109                                    NSResizableWindowMask;
110
111         NSRect s_rect;
112         if( !s_frame )
113         {
114             s_rect.size.width  = p_vout->i_window_width;
115             s_rect.size.height = p_vout->i_window_height;
116         }
117         else
118         {
119             s_rect = *s_frame;
120         }
121        
122         [self initWithContentRect: s_rect
123               styleMask: i_stylemask
124               backing: NSBackingStoreBuffered
125               defer: YES screen: o_screen];
126
127         [self setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )];
128
129         if( var_GetBool( p_vout, "video-on-top" ) )
130         {
131             [self setLevel: NSStatusWindowLevel];
132         }
133
134         if( !s_frame )
135         {
136             [self center];
137         }
138     }
139
140     [self updateTitle];
141     [self makeKeyAndOrderFront: nil];
142
143     /* We'll catch mouse events */
144     [self setAcceptsMouseMovedEvents: YES];
145     [self makeFirstResponder: self];
146     
147     [o_pool release];
148     return self;
149 }
150
151 - (void)close
152 {
153     if( p_fullscreen_state )
154     {
155         EndFullScreen( p_fullscreen_state, NULL );
156     }
157     [super close];
158 }
159
160 - (void)setOnTop:(bool)b_on_top
161 {
162     if( b_on_top )
163     {
164         [self setLevel: NSStatusWindowLevel];
165     }
166     else
167     {
168         [self setLevel: NSNormalWindowLevel];
169     }
170 }
171
172 - (void)hideMouse:(bool)b_hide
173 {
174     BOOL b_inside;
175     NSPoint ml;
176     NSView *o_contents = [self contentView];
177     
178     ml = [self convertScreenToBase:[NSEvent mouseLocation]];
179     ml = [o_contents convertPoint:ml fromView:nil];
180     b_inside = [o_contents mouse: ml inRect: [o_contents bounds]];
181     
182     if( b_hide && b_inside )
183     {
184         [NSCursor setHiddenUntilMouseMoves: YES];
185     }
186     else if( !b_hide )
187     {
188         [NSCursor setHiddenUntilMouseMoves: NO];
189     }
190 }
191
192 - (void)manage
193 {
194     if( p_fullscreen_state )
195     {
196         if( mdate() - i_time_mouse_last_moved > 3000000 )
197         {
198             [self hideMouse: YES];
199         }
200     }
201     else
202     {
203         [self hideMouse: NO];
204     }
205
206     /* Disable screensaver */
207     UpdateSystemActivity( UsrActivity );
208 }
209
210 - (void)scaleWindowWithFactor: (float)factor
211 {
212     NSSize newsize;
213     int i_corrected_height, i_corrected_width;
214     NSPoint topleftbase;
215     NSPoint topleftscreen;
216     
217     if ( !p_vout->b_fullscreen )
218     {
219         topleftbase.x = 0;
220         topleftbase.y = [self frame].size.height;
221         topleftscreen = [self convertBaseToScreen: topleftbase];
222         
223         if( p_vout->render.i_height * p_vout->render.i_aspect > 
224                         p_vout->render.i_width * VOUT_ASPECT_FACTOR )
225         {
226             i_corrected_width = p_vout->render.i_height * p_vout->render.i_aspect /
227                                             VOUT_ASPECT_FACTOR;
228             newsize.width = (int) ( i_corrected_width * factor );
229             newsize.height = (int) ( p_vout->render.i_height * factor );
230         }
231         else
232         {
233             i_corrected_height = p_vout->render.i_width * VOUT_ASPECT_FACTOR /
234                                             p_vout->render.i_aspect;
235             newsize.width = (int) ( p_vout->render.i_width * factor );
236             newsize.height = (int) ( i_corrected_height * factor );
237         }
238     
239         [self setContentSize: newsize];
240         
241         [self setFrameTopLeftPoint: topleftscreen];
242         p_vout->i_changes |= VOUT_SIZE_CHANGE;
243     }
244 }
245
246 - (void)toggleFloatOnTop
247 {
248     vlc_value_t val;
249     if( var_Get( p_vout, "video-on-top", &val )>=0 && val.b_bool)
250     {
251         val.b_bool = VLC_FALSE;
252         var_Set( p_vout, "video-on-top", val );
253     }
254     else
255     {
256         val.b_bool = VLC_TRUE;
257         var_Set( p_vout, "video-on-top", val );
258     }
259 }
260
261 - (void)toggleFullscreen
262 {
263     vlc_value_t val;
264     val.b_bool = !p_vout->b_fullscreen;
265     var_Set( p_vout, "fullscreen", val );
266 }
267
268 - (BOOL)isFullscreen
269 {
270     return( p_vout->b_fullscreen );
271 }
272
273 - (BOOL)canBecomeKeyWindow
274 {
275     return YES;
276 }
277
278 /* Sometimes crashes VLC....
279 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
280 {
281         return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event];
282 }*/
283
284 - (void)keyDown:(NSEvent *)o_event
285 {
286     unichar key = 0;
287     vlc_value_t val;
288     unsigned int i_pressed_modifiers = 0;
289     val.i_int = 0;
290     
291     i_pressed_modifiers = [o_event modifierFlags];
292
293     if( i_pressed_modifiers & NSShiftKeyMask )
294         val.i_int |= KEY_MODIFIER_SHIFT;
295     if( i_pressed_modifiers & NSControlKeyMask )
296         val.i_int |= KEY_MODIFIER_CTRL;
297     if( i_pressed_modifiers & NSAlternateKeyMask )
298         val.i_int |= KEY_MODIFIER_ALT;
299     if( i_pressed_modifiers & NSCommandKeyMask )
300         val.i_int |= KEY_MODIFIER_COMMAND;
301
302     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
303
304     if( key )
305     {
306         /* Escape should always get you out of fullscreen */
307         if( key == (unichar) 0x1b )
308         {
309              if( [self isFullscreen] )
310              {
311                  [self toggleFullscreen];
312              }
313         }
314         else if ( key == ' ' )
315         {
316             vlc_value_t val;
317             val.i_int = config_GetInt( p_vout, "key-play-pause" );
318             var_Set( p_vout->p_vlc, "key-pressed", val );
319         }
320         else
321         {
322             val.i_int |= CocoaKeyToVLC( key );
323             var_Set( p_vout->p_vlc, "key-pressed", val );
324         }
325     }
326     else
327     {
328         [super keyDown: o_event];
329     }
330 }
331
332 - (void)updateTitle
333 {
334     NSMutableString * o_title;
335     playlist_t * p_playlist;
336     
337     if( p_vout == NULL )
338     {
339         return;
340     }
341     
342     p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
343                                                 FIND_ANYWHERE );
344     
345     if( p_playlist == NULL )
346     {
347         return;
348     }
349
350     vlc_mutex_lock( &p_playlist->object_lock );
351     o_title = [NSMutableString stringWithUTF8String: 
352         p_playlist->p_input.psz_uri]; 
353     vlc_mutex_unlock( &p_playlist->object_lock );
354     vlc_object_release( p_playlist );
355
356     if( o_title != nil )
357     {
358         NSRange prefix_range = [o_title rangeOfString: @"file:"];
359         if( prefix_range.location != NSNotFound )
360         {
361             [o_title deleteCharactersInRange: prefix_range];
362         }
363
364         [self setTitleWithRepresentedFilename: o_title];
365     }
366     else
367     {
368         [self setTitle: [NSString stringWithCString: VOUT_TITLE]];
369     }
370 }
371
372 /* This is actually the same as VLCControls::stop. */
373 - (BOOL)windowShouldClose:(id)sender
374 {
375     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
376                                                        FIND_ANYWHERE );
377     if( p_playlist == NULL )      
378     {
379         return NO;
380     }
381
382     playlist_Stop( p_playlist );
383     vlc_object_release( p_playlist );
384
385     /* The window will be closed by the intf later. */
386     return NO;
387 }
388
389 - (BOOL)acceptsFirstResponder
390 {
391     return YES;
392 }
393
394 - (BOOL)becomeFirstResponder
395 {
396     return YES;
397 }
398
399 - (BOOL)resignFirstResponder
400 {
401     /* We need to stay the first responder or we'll miss some
402        events */
403     return NO;
404 }
405
406 - (void)mouseDown:(NSEvent *)o_event
407 {
408     vlc_value_t val;
409
410     switch( [o_event type] )
411     {
412         case NSLeftMouseDown:
413         {
414             var_Get( p_vout, "mouse-button-down", &val );
415             val.i_int |= 1;
416             var_Set( p_vout, "mouse-button-down", val );
417         }
418         break;
419
420         default:
421             [super mouseDown: o_event];
422         break;
423     }
424 }
425
426 - (void)otherMouseDown:(NSEvent *)o_event
427 {
428     vlc_value_t val;
429
430     switch( [o_event type] )
431     {
432         case NSOtherMouseDown:
433         {
434             var_Get( p_vout, "mouse-button-down", &val );
435             val.i_int |= 2;
436             var_Set( p_vout, "mouse-button-down", val );
437         }
438         break;
439
440         default:
441             [super mouseDown: o_event];
442         break;
443     }
444 }
445
446 - (void)rightMouseDown:(NSEvent *)o_event
447 {
448     vlc_value_t val;
449
450     switch( [o_event type] )
451     {
452         case NSRightMouseDown:
453         {
454             var_Get( p_vout, "mouse-button-down", &val );
455             val.i_int |= 4;
456             var_Set( p_vout, "mouse-button-down", val );
457         }
458         break;
459
460         default:
461             [super mouseDown: o_event];
462         break;
463     }
464 }
465
466 - (void)mouseUp:(NSEvent *)o_event
467 {
468     vlc_value_t val;
469
470     switch( [o_event type] )
471     {
472         case NSLeftMouseUp:
473         {
474             vlc_value_t b_val;
475             b_val.b_bool = VLC_TRUE;
476             var_Set( p_vout, "mouse-clicked", b_val );
477
478             var_Get( p_vout, "mouse-button-down", &val );
479             val.i_int &= ~1;
480             var_Set( p_vout, "mouse-button-down", val );
481         }
482         break;
483
484         default:
485             [super mouseUp: o_event];
486         break;
487     }
488 }
489
490 - (void)otherMouseUp:(NSEvent *)o_event
491 {
492     vlc_value_t val;
493
494     switch( [o_event type] )
495     {
496         case NSOtherMouseUp:
497         {
498             var_Get( p_vout, "mouse-button-down", &val );
499             val.i_int &= ~2;
500             var_Set( p_vout, "mouse-button-down", val );
501         }
502         break;
503
504         default:
505             [super mouseUp: o_event];
506         break;
507     }
508 }
509
510 - (void)rightMouseUp:(NSEvent *)o_event
511 {
512     vlc_value_t val;
513
514     switch( [o_event type] )
515     {
516         case NSRightMouseUp:
517         {
518             var_Get( p_vout, "mouse-button-down", &val );
519             val.i_int &= ~4;
520             var_Set( p_vout, "mouse-button-down", val );
521         }
522         break;
523
524         default:
525             [super mouseUp: o_event];
526         break;
527     }
528 }
529
530 - (void)mouseDragged:(NSEvent *)o_event
531 {
532     [self mouseMoved: o_event];
533 }
534
535 - (void)otherMouseDragged:(NSEvent *)o_event
536 {
537     [self mouseMoved: o_event];
538 }
539
540 - (void)rightMouseDragged:(NSEvent *)o_event
541 {
542     [self mouseMoved: o_event];
543 }
544
545 - (void)mouseMoved:(NSEvent *)o_event
546 {   
547     NSPoint ml;
548     NSRect s_rect;
549     BOOL b_inside;
550     NSView * o_view;
551
552     i_time_mouse_last_moved = mdate();
553
554     o_view = [self contentView];
555     s_rect = [o_view bounds];
556     ml = [o_view convertPoint: [o_event locationInWindow] fromView: nil];
557     b_inside = [o_view mouse: ml inRect: s_rect];
558
559     if( b_inside )
560     {
561         vlc_value_t val;
562         int i_width, i_height, i_x, i_y;
563
564         vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
565                                    (unsigned int)s_rect.size.height,
566                                    &i_x, &i_y, &i_width, &i_height );
567
568         val.i_int = ( ((int)ml.x) - i_x ) *  
569                     p_vout->render.i_width / i_width;
570         var_Set( p_vout, "mouse-x", val );
571
572         if( [[o_view className] isEqualToString: @"VLCGLView"] )
573         {
574             val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
575                         p_vout->render.i_height / i_height;
576         }
577         else
578         {
579             val.i_int = ( ((int)ml.y) - i_y ) * 
580                         p_vout->render.i_height / i_height;
581         }
582         var_Set( p_vout, "mouse-y", val );
583             
584         val.b_bool = VLC_TRUE;
585         var_Set( p_vout, "mouse-moved", val ); 
586     }
587
588     [super mouseMoved: o_event];
589 }
590
591 @end