]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
* src/libvlc.h: Added a "snapshot-format" option to be able to select png or jpg...
[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 - (void)snapshot
337 {
338     vout_Control( p_vout, VOUT_SNAPSHOT );
339 }
340
341 - (BOOL)canBecomeKeyWindow
342 {
343     return YES;
344 }
345
346 /* Sometimes crashes VLC....
347 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
348 {
349         return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event];
350 }*/
351
352 - (void)keyDown:(NSEvent *)o_event
353 {
354     unichar key = 0;
355     vlc_value_t val;
356     unsigned int i_pressed_modifiers = 0;
357     val.i_int = 0;
358     
359     i_pressed_modifiers = [o_event modifierFlags];
360
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;
369
370     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
371
372     if( key )
373     {
374         /* Escape should always get you out of fullscreen */
375         if( key == (unichar) 0x1b )
376         {
377              if( [self isFullscreen] )
378              {
379                  [self toggleFullscreen];
380              }
381         }
382         else if ( key == ' ' )
383         {
384             vlc_value_t val;
385             val.i_int = config_GetInt( p_vout, "key-play-pause" );
386             var_Set( p_vout->p_vlc, "key-pressed", val );
387         }
388         else
389         {
390             val.i_int |= CocoaKeyToVLC( key );
391             var_Set( p_vout->p_vlc, "key-pressed", val );
392         }
393     }
394     else
395     {
396         [super keyDown: o_event];
397     }
398 }
399
400 - (void)updateTitle
401 {
402     NSMutableString * o_title;
403     playlist_t * p_playlist;
404     
405     if( p_vout == NULL )
406     {
407         return;
408     }
409     
410     p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
411                                                 FIND_ANYWHERE );
412     
413     if( p_playlist == NULL )
414     {
415         return;
416     }
417
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 );
423
424     if( o_title != nil )
425     {
426         NSRange prefix_range = [o_title rangeOfString: @"file:"];
427         if( prefix_range.location != NSNotFound )
428         {
429             [o_title deleteCharactersInRange: prefix_range];
430         }
431
432         [self setTitleWithRepresentedFilename: o_title];
433     }
434     else
435     {
436         [self setTitle: [NSString stringWithCString: VOUT_TITLE]];
437     }
438 }
439
440 /* This is actually the same as VLCControls::stop. */
441 - (BOOL)windowShouldClose:(id)sender
442 {
443     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
444                                                        FIND_ANYWHERE );
445     if( p_playlist == NULL )      
446     {
447         return NO;
448     }
449
450     playlist_Stop( p_playlist );
451     vlc_object_release( p_playlist );
452
453     /* The window will be closed by the intf later. */
454     return NO;
455 }
456
457 - (BOOL)acceptsFirstResponder
458 {
459     return YES;
460 }
461
462 - (BOOL)becomeFirstResponder
463 {
464     return YES;
465 }
466
467 - (BOOL)resignFirstResponder
468 {
469     /* We need to stay the first responder or we'll miss some
470        events */
471     return NO;
472 }
473
474 - (void)mouseDown:(NSEvent *)o_event
475 {
476     vlc_value_t val;
477
478     switch( [o_event type] )
479     {
480         case NSLeftMouseDown:
481         {
482             var_Get( p_vout, "mouse-button-down", &val );
483             val.i_int |= 1;
484             var_Set( p_vout, "mouse-button-down", val );
485         }
486         break;
487
488         default:
489             [super mouseDown: o_event];
490         break;
491     }
492 }
493
494 - (void)otherMouseDown:(NSEvent *)o_event
495 {
496     vlc_value_t val;
497
498     switch( [o_event type] )
499     {
500         case NSOtherMouseDown:
501         {
502             var_Get( p_vout, "mouse-button-down", &val );
503             val.i_int |= 2;
504             var_Set( p_vout, "mouse-button-down", val );
505         }
506         break;
507
508         default:
509             [super mouseDown: o_event];
510         break;
511     }
512 }
513
514 - (void)rightMouseDown:(NSEvent *)o_event
515 {
516     vlc_value_t val;
517
518     switch( [o_event type] )
519     {
520         case NSRightMouseDown:
521         {
522             var_Get( p_vout, "mouse-button-down", &val );
523             val.i_int |= 4;
524             var_Set( p_vout, "mouse-button-down", val );
525         }
526         break;
527
528         default:
529             [super mouseDown: o_event];
530         break;
531     }
532 }
533
534 - (void)mouseUp:(NSEvent *)o_event
535 {
536     vlc_value_t val;
537
538     switch( [o_event type] )
539     {
540         case NSLeftMouseUp:
541         {
542             vlc_value_t b_val;
543             b_val.b_bool = VLC_TRUE;
544             var_Set( p_vout, "mouse-clicked", b_val );
545
546             var_Get( p_vout, "mouse-button-down", &val );
547             val.i_int &= ~1;
548             var_Set( p_vout, "mouse-button-down", val );
549         }
550         break;
551
552         default:
553             [super mouseUp: o_event];
554         break;
555     }
556 }
557
558 - (void)otherMouseUp:(NSEvent *)o_event
559 {
560     vlc_value_t val;
561
562     switch( [o_event type] )
563     {
564         case NSOtherMouseUp:
565         {
566             var_Get( p_vout, "mouse-button-down", &val );
567             val.i_int &= ~2;
568             var_Set( p_vout, "mouse-button-down", val );
569         }
570         break;
571
572         default:
573             [super mouseUp: o_event];
574         break;
575     }
576 }
577
578 - (void)rightMouseUp:(NSEvent *)o_event
579 {
580     vlc_value_t val;
581
582     switch( [o_event type] )
583     {
584         case NSRightMouseUp:
585         {
586             var_Get( p_vout, "mouse-button-down", &val );
587             val.i_int &= ~4;
588             var_Set( p_vout, "mouse-button-down", val );
589         }
590         break;
591
592         default:
593             [super mouseUp: o_event];
594         break;
595     }
596 }
597
598 - (void)mouseDragged:(NSEvent *)o_event
599 {
600     [self mouseMoved: o_event];
601 }
602
603 - (void)otherMouseDragged:(NSEvent *)o_event
604 {
605     [self mouseMoved: o_event];
606 }
607
608 - (void)rightMouseDragged:(NSEvent *)o_event
609 {
610     [self mouseMoved: o_event];
611 }
612
613 - (void)mouseMoved:(NSEvent *)o_event
614 {   
615     NSPoint ml;
616     NSRect s_rect;
617     BOOL b_inside;
618     NSView * o_view;
619
620     i_time_mouse_last_moved = mdate();
621
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];
626
627     if( b_inside )
628     {
629         vlc_value_t val;
630         int i_width, i_height, i_x, i_y;
631
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 );
635
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 );
639
640         if( [[o_view className] isEqualToString: @"VLCGLView"] )
641         {
642             val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
643                         p_vout->render.i_height / i_height;
644         }
645         else
646         {
647             val.i_int = ( ((int)ml.y) - i_y ) * 
648                         p_vout->render.i_height / i_height;
649         }
650         var_Set( p_vout, "mouse-y", val );
651             
652         val.b_bool = VLC_TRUE;
653         var_Set( p_vout, "mouse-moved", val ); 
654     }
655
656     [super mouseMoved: o_event];
657 }
658
659 @end