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