]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
ALL: OS X OpenGL provider
[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: NO 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: NO 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->pp_items[p_playlist->i_index]->input.psz_uri]; 
353     vlc_mutex_unlock( &p_playlist->object_lock );
354
355     vlc_object_release( p_playlist );
356
357     if( o_title != nil )
358     {
359         NSRange prefix_range = [o_title rangeOfString: @"file:"];
360         if( prefix_range.location != NSNotFound )
361         {
362             [o_title deleteCharactersInRange: prefix_range];
363         }
364
365         [self setTitleWithRepresentedFilename: o_title];
366     }
367     else
368     {
369         [self setTitle: [NSString stringWithCString: VOUT_TITLE]];
370     }
371 }
372
373 /* This is actually the same as VLCControls::stop. */
374 - (BOOL)windowShouldClose:(id)sender
375 {
376     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
377                                                        FIND_ANYWHERE );
378     if( p_playlist == NULL )      
379     {
380         return NO;
381     }
382
383     playlist_Stop( p_playlist );
384     vlc_object_release( p_playlist );
385
386     /* The window will be closed by the intf later. */
387     return NO;
388 }
389
390 - (BOOL)acceptsFirstResponder
391 {
392     return YES;
393 }
394
395 - (BOOL)becomeFirstResponder
396 {
397     return YES;
398 }
399
400 - (BOOL)resignFirstResponder
401 {
402     /* We need to stay the first responder or we'll miss some
403        events */
404     return NO;
405 }
406
407 - (void)mouseDown:(NSEvent *)o_event
408 {
409     vlc_value_t val;
410
411     switch( [o_event type] )
412     {
413         case NSLeftMouseDown:
414         {
415             var_Get( p_vout, "mouse-button-down", &val );
416             val.i_int |= 1;
417             var_Set( p_vout, "mouse-button-down", val );
418         }
419         break;
420
421         default:
422             [super mouseDown: o_event];
423         break;
424     }
425 }
426
427 - (void)otherMouseDown:(NSEvent *)o_event
428 {
429     vlc_value_t val;
430
431     switch( [o_event type] )
432     {
433         case NSOtherMouseDown:
434         {
435             var_Get( p_vout, "mouse-button-down", &val );
436             val.i_int |= 2;
437             var_Set( p_vout, "mouse-button-down", val );
438         }
439         break;
440
441         default:
442             [super mouseDown: o_event];
443         break;
444     }
445 }
446
447 - (void)rightMouseDown:(NSEvent *)o_event
448 {
449     vlc_value_t val;
450
451     switch( [o_event type] )
452     {
453         case NSRightMouseDown:
454         {
455             var_Get( p_vout, "mouse-button-down", &val );
456             val.i_int |= 4;
457             var_Set( p_vout, "mouse-button-down", val );
458         }
459         break;
460
461         default:
462             [super mouseDown: o_event];
463         break;
464     }
465 }
466
467 - (void)mouseUp:(NSEvent *)o_event
468 {
469     vlc_value_t val;
470
471     switch( [o_event type] )
472     {
473         case NSLeftMouseUp:
474         {
475             vlc_value_t b_val;
476             b_val.b_bool = VLC_TRUE;
477             var_Set( p_vout, "mouse-clicked", b_val );
478
479             var_Get( p_vout, "mouse-button-down", &val );
480             val.i_int &= ~1;
481             var_Set( p_vout, "mouse-button-down", val );
482         }
483         break;
484
485         default:
486             [super mouseUp: o_event];
487         break;
488     }
489 }
490
491 - (void)otherMouseUp:(NSEvent *)o_event
492 {
493     vlc_value_t val;
494
495     switch( [o_event type] )
496     {
497         case NSOtherMouseUp:
498         {
499             var_Get( p_vout, "mouse-button-down", &val );
500             val.i_int &= ~2;
501             var_Set( p_vout, "mouse-button-down", val );
502         }
503         break;
504
505         default:
506             [super mouseUp: o_event];
507         break;
508     }
509 }
510
511 - (void)rightMouseUp:(NSEvent *)o_event
512 {
513     vlc_value_t val;
514
515     switch( [o_event type] )
516     {
517         case NSRightMouseUp:
518         {
519             var_Get( p_vout, "mouse-button-down", &val );
520             val.i_int &= ~4;
521             var_Set( p_vout, "mouse-button-down", val );
522         }
523         break;
524
525         default:
526             [super mouseUp: o_event];
527         break;
528     }
529 }
530
531 - (void)mouseDragged:(NSEvent *)o_event
532 {
533     [self mouseMoved: o_event];
534 }
535
536 - (void)otherMouseDragged:(NSEvent *)o_event
537 {
538     [self mouseMoved: o_event];
539 }
540
541 - (void)rightMouseDragged:(NSEvent *)o_event
542 {
543     [self mouseMoved: o_event];
544 }
545
546 - (void)mouseMoved:(NSEvent *)o_event
547 {   
548     NSPoint ml;
549     NSRect s_rect;
550     BOOL b_inside;
551     NSView * o_view;
552
553     i_time_mouse_last_moved = mdate();
554
555     o_view = [self contentView];
556     s_rect = [o_view bounds];
557     ml = [o_view convertPoint: [o_event locationInWindow] fromView: nil];
558     b_inside = [o_view mouse: ml inRect: s_rect];
559
560     if( b_inside )
561     {
562         vlc_value_t val;
563         int i_width, i_height, i_x, i_y;
564
565         vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
566                                    (unsigned int)s_rect.size.height,
567                                    &i_x, &i_y, &i_width, &i_height );
568
569         val.i_int = ( ((int)ml.x) - i_x ) *  
570                     p_vout->render.i_width / i_width;
571         var_Set( p_vout, "mouse-x", val );
572
573         if( [[o_view className] isEqualToString: @"VLCGLView"] )
574         {
575             val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
576                         p_vout->render.i_height / i_height;
577         }
578         else
579         {
580             val.i_int = ( ((int)ml.y) - i_y ) * 
581                         p_vout->render.i_height / i_height;
582         }
583         var_Set( p_vout, "mouse-y", val );
584             
585         val.b_bool = VLC_TRUE;
586         var_Set( p_vout, "mouse-moved", val ); 
587     }
588
589     [super mouseMoved: o_event];
590 }
591
592 @end