]> git.sesse.net Git - vlc/blob - modules/gui/macosx/voutgl.m
Remove unneeded VOUT_CLOSE.
[vlc] / modules / gui / macosx / voutgl.m
1 /*****************************************************************************
2  * voutgl.m: MacOS X OpenGL provider
3  *****************************************************************************
4  * Copyright (C) 2001-2004, 2007 the VideoLAN team
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  *          Benjamin Pracht <bigben at videolan dot org>
13  *          Damien Fouilleul <damienf at videolan dot org>
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
28  *****************************************************************************/
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #include <errno.h>                                                 /* ENOMEM */
34 #include <stdlib.h>                                                /* free() */
35 #include <string.h>
36
37 #include <vlc_keys.h>
38
39 #include "intf.h"
40 #include "vout.h"
41
42 #include <OpenGL/OpenGL.h>
43 #include <OpenGL/gl.h>
44
45 #include <AGL/agl.h>
46
47 /*****************************************************************************
48  * VLCGLView interface
49  *****************************************************************************/
50 @interface VLCGLView : NSOpenGLView <VLCVoutViewResetting>
51 {
52     vout_thread_t * p_vout;
53 }
54
55 + (void)resetVout: (NSValue *) voutValue;
56 - (id) initWithVout: (vout_thread_t *) p_vout;
57 @end
58
59 struct vout_sys_t
60 {
61     NSAutoreleasePool * o_pool;
62     VLCGLView         * o_glview;
63     VLCVoutView       * o_vout_view;
64     bool          b_saved_frame;
65     NSRect              s_frame;
66     bool          b_got_frame;
67     /* Mozilla plugin-related variables */
68     bool          b_embedded;
69     AGLContext          agl_ctx;
70     AGLDrawable         agl_drawable;
71     int                 i_offx, i_offy;
72     int                 i_width, i_height;
73     WindowRef           theWindow;
74     WindowGroupRef      winGroup;
75     bool          b_clipped_out;
76     Rect                clipBounds, viewBounds;
77 };
78
79 /*****************************************************************************
80  * Local prototypes
81  *****************************************************************************/
82
83 static int  Init   ( vout_thread_t * p_vout );
84 static void End    ( vout_thread_t * p_vout );
85 static int  Manage ( vout_thread_t * p_vout );
86 static int  Control( vout_thread_t *, int, va_list );
87 static void Swap   ( vout_thread_t * p_vout );
88 static int  Lock   ( vout_thread_t * p_vout );
89 static void Unlock ( vout_thread_t * p_vout );
90
91 static int  aglInit   ( vout_thread_t * p_vout );
92 static void aglEnd    ( vout_thread_t * p_vout );
93 static int  aglManage ( vout_thread_t * p_vout );
94 static int  aglControl( vout_thread_t *, int, va_list );
95 static void aglSwap   ( vout_thread_t * p_vout );
96 static int  aglLock   ( vout_thread_t * p_vout );
97 static void aglUnlock ( vout_thread_t * p_vout );
98
99 int OpenVideoGL  ( vlc_object_t * p_this )
100 {
101     vout_thread_t * p_vout = (vout_thread_t *) p_this;
102     vlc_value_t value_drawable;
103
104     if( !CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay ) )
105     {
106         msg_Warn( p_vout, "no OpenGL hardware acceleration found. "
107                           "Video display might be slow" );
108     }
109     msg_Dbg( p_vout, "display is Quartz Extreme accelerated" );
110
111     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
112     if( p_vout->p_sys == NULL )
113         return( 1 );
114
115     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
116
117     var_Get( p_vout->p_libvlc, "drawable", &value_drawable );
118     if( value_drawable.i_int != 0 )
119     {
120         static const GLint ATTRIBUTES[] = {
121             AGL_WINDOW,
122             AGL_RGBA,
123             AGL_NO_RECOVERY,
124             AGL_ACCELERATED,
125             AGL_DOUBLEBUFFER,
126             AGL_RED_SIZE,   8,
127             AGL_GREEN_SIZE, 8,
128             AGL_BLUE_SIZE,  8,
129             AGL_ALPHA_SIZE, 8,
130             AGL_DEPTH_SIZE, 24,
131             AGL_NONE };
132
133         AGLPixelFormat pixFormat;
134
135         p_vout->p_sys->b_embedded = true;
136
137         pixFormat = aglChoosePixelFormat(NULL, 0, ATTRIBUTES);
138         if( NULL == pixFormat )
139         {
140             msg_Err( p_vout, "no screen renderer available for required attributes." );
141             return VLC_EGENERIC;
142         }
143  
144         p_vout->p_sys->agl_ctx = aglCreateContext(pixFormat, NULL);
145         aglDestroyPixelFormat(pixFormat);
146         if( NULL == p_vout->p_sys->agl_ctx )
147         {
148             msg_Err( p_vout, "cannot create AGL context." );
149             return VLC_EGENERIC;
150         }
151         else {
152             // tell opengl not to sync buffer swap with vertical retrace (too inefficient)
153             GLint param = 0;
154             aglSetInteger(p_vout->p_sys->agl_ctx, AGL_SWAP_INTERVAL, &param);
155             aglEnable(p_vout->p_sys->agl_ctx, AGL_SWAP_INTERVAL);
156         }
157
158         p_vout->pf_init             = aglInit;
159         p_vout->pf_end              = aglEnd;
160         p_vout->pf_manage           = aglManage;
161         p_vout->pf_control          = aglControl;
162         p_vout->pf_swap             = aglSwap;
163         p_vout->pf_lock             = aglLock;
164         p_vout->pf_unlock           = aglUnlock;
165     }
166     else
167     {
168         NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
169
170         p_vout->p_sys->b_embedded = false;
171
172         [VLCGLView performSelectorOnMainThread:@selector(initVout:) withObject:[NSValue valueWithPointer:p_vout] waitUntilDone:YES];
173
174         [o_pool release];
175
176         /* Check to see if initVout: was successfull */
177
178         if( !p_vout->p_sys->o_vout_view )
179         {
180             return VLC_EGENERIC;
181         }
182
183         p_vout->pf_init   = Init;
184         p_vout->pf_end    = End;
185         p_vout->pf_manage = Manage;
186         p_vout->pf_control= Control;
187         p_vout->pf_swap   = Swap;
188         p_vout->pf_lock   = Lock;
189         p_vout->pf_unlock = Unlock;
190     }
191     p_vout->p_sys->b_got_frame = false;
192
193     return VLC_SUCCESS;
194 }
195
196 void CloseVideoGL ( vlc_object_t * p_this )
197 {
198     vout_thread_t * p_vout = (vout_thread_t *) p_this;
199
200     msg_Dbg( p_this, "Closing" );
201
202     if( p_vout->p_sys->b_embedded )
203     {
204         aglDestroyContext(p_vout->p_sys->agl_ctx);
205     }
206     else if(VLCIntf && vlc_object_alive (VLCIntf))
207     {
208         NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
209
210         /* Close the window */
211         [p_vout->p_sys->o_vout_view performSelectorOnMainThread:@selector(closeVout) withObject:NULL waitUntilDone:YES];
212
213         [o_pool release];
214     }
215     /* Clean up */
216     free( p_vout->p_sys );
217 }
218
219 static int Init( vout_thread_t * p_vout )
220 {
221     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
222     return VLC_SUCCESS;
223 }
224
225 static void End( vout_thread_t * p_vout )
226 {
227     [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
228 }
229
230 static int Manage( vout_thread_t * p_vout )
231 {
232     if( p_vout->i_changes & VOUT_ASPECT_CHANGE )
233     {
234         [p_vout->p_sys->o_glview reshape];
235         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
236     }
237     if( p_vout->i_changes & VOUT_CROP_CHANGE )
238     {
239         [p_vout->p_sys->o_glview reshape];
240         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
241     }
242
243     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
244     {
245         NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
246
247         p_vout->b_fullscreen = !p_vout->b_fullscreen;
248
249         if( p_vout->b_fullscreen )
250             [p_vout->p_sys->o_vout_view enterFullscreen];
251         else
252             [p_vout->p_sys->o_vout_view leaveFullscreen];
253
254         [o_pool release];
255
256         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
257     }
258
259     if( p_vout->p_sys->o_vout_view )
260         [p_vout->p_sys->o_vout_view manage];
261     return VLC_SUCCESS;
262 }
263
264 /*****************************************************************************
265  * Control: control facility for the vout
266  *****************************************************************************/
267 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
268 {
269     bool b_arg;
270
271     switch( i_query )
272     {
273         case VOUT_SET_STAY_ON_TOP:
274             b_arg = (bool) va_arg( args, int );
275             [p_vout->p_sys->o_vout_view setOnTop: b_arg];
276             return VLC_SUCCESS;
277
278         case VOUT_REPARENT:
279         default:
280             return vout_vaControlDefault( p_vout, i_query, args );
281     }
282 }
283
284 static void Swap( vout_thread_t * p_vout )
285 {
286     p_vout->p_sys->b_got_frame = true;
287     [[p_vout->p_sys->o_glview openGLContext] flushBuffer];
288 }
289
290 static int Lock( vout_thread_t * p_vout )
291 {
292     if( kCGLNoError == CGLLockContext([[p_vout->p_sys->o_glview openGLContext] CGLContextObj]) )
293     {
294         [[p_vout->p_sys->o_glview openGLContext] makeCurrentContext];
295         return 0;
296     }
297     return 1;
298 }
299
300 static void Unlock( vout_thread_t * p_vout )
301 {
302     CGLUnlockContext([[p_vout->p_sys->o_glview openGLContext] CGLContextObj]);
303 }
304
305 /*****************************************************************************
306  * VLCGLView implementation
307  *****************************************************************************/
308 @implementation VLCGLView
309 + (void)initVout:(NSValue *)arg
310 {
311     vout_thread_t * p_vout = [arg pointerValue];
312
313     /* Create the GL view */
314     p_vout->p_sys->o_glview = [[VLCGLView alloc] initWithVout: p_vout];
315     [p_vout->p_sys->o_glview autorelease];
316
317     /* Spawn the window */
318     id old_vout = p_vout->p_sys->o_vout_view;
319     p_vout->p_sys->o_vout_view = [[VLCVoutView voutView: p_vout
320                                                 subView: p_vout->p_sys->o_glview frame: nil] retain];
321     [old_vout release];
322 }
323
324 /* This function will reset the o_vout_view. It's useful to go fullscreen. */
325 + (void)resetVout:(NSValue *) voutValue
326 {
327     vout_thread_t * p_vout = [voutValue pointerValue];
328     if( p_vout->b_fullscreen )
329     {
330         /* Save window size and position */
331         p_vout->p_sys->s_frame.size =
332             [p_vout->p_sys->o_vout_view frame].size;
333         p_vout->p_sys->s_frame.origin =
334             [[p_vout->p_sys->o_vout_view getWindow ]frame].origin;
335         p_vout->p_sys->b_saved_frame = true;
336     }
337
338     [p_vout->p_sys->o_vout_view closeVout];
339
340 #define o_glview p_vout->p_sys->o_glview
341     o_glview = [[VLCGLView alloc] initWithVout: p_vout];
342     [o_glview autorelease];
343  
344     if( p_vout->p_sys->b_saved_frame )
345     {
346         id old_vout = p_vout->p_sys->o_vout_view;
347         p_vout->p_sys->o_vout_view = [[VLCVoutView voutView: p_vout
348                                                     subView: o_glview
349                                                       frame: &p_vout->p_sys->s_frame] retain];
350         [old_vout release];
351     }
352     else
353     {
354         id old_vout = p_vout->p_sys->o_vout_view;
355         p_vout->p_sys->o_vout_view = [[VLCVoutView voutView: p_vout
356                                                     subView: o_glview frame: nil] retain];
357         [old_vout release];
358     }
359 #undef o_glview
360 }
361
362 - (id) initWithVout: (vout_thread_t *) vout
363 {
364     /* Must be called from main thread:
365      * "The NSView class is generally thread-safe, with a few exceptions. You
366      * should create, destroy, resize, move, and perform other operations on NSView
367      * objects only from the main thread of an application. Drawing from secondary
368      * threads is thread-safe as long as you bracket drawing calls with calls to
369      * lockFocusIfCanDraw and unlockFocus." Cocoa Thread Safety */
370
371     p_vout = vout;
372
373     NSOpenGLPixelFormatAttribute attribs[] =
374     {
375         NSOpenGLPFADoubleBuffer,
376         NSOpenGLPFAAccelerated,
377         NSOpenGLPFANoRecovery,
378         NSOpenGLPFAColorSize, 24,
379         NSOpenGLPFAAlphaSize, 8,
380         NSOpenGLPFADepthSize, 24,
381         NSOpenGLPFAWindow,
382         0
383     };
384
385     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
386         initWithAttributes: attribs];
387
388     if( !fmt )
389     {
390         msg_Warn( p_vout, "could not create OpenGL video output" );
391         return nil;
392     }
393
394     self = [super initWithFrame: NSMakeRect(0,0,10,10) pixelFormat: fmt];
395     [fmt release];
396
397     [[self openGLContext] makeCurrentContext];
398     [[self openGLContext] update];
399
400     /* Swap buffers only during the vertical retrace of the monitor.
401        http://developer.apple.com/documentation/GraphicsImaging/
402        Conceptual/OpenGL/chap5/chapter_5_section_44.html */
403     GLint params[] = { 1 };
404     CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, params );
405     return self;
406 }
407
408 - (BOOL)mouseDownCanMoveWindow
409 {
410     return YES;
411 }
412
413 - (void) reshape
414 {
415     int x, y;
416     vlc_value_t val;
417
418     Lock( p_vout );
419     NSRect bounds = [self bounds];
420
421     var_Get( p_vout, "macosx-stretch", &val );
422     if( val.b_bool )
423     {
424         x = bounds.size.width;
425         y = bounds.size.height;
426     }
427     else if( bounds.size.height * p_vout->fmt_in.i_visible_width *
428              p_vout->fmt_in.i_sar_num <
429              bounds.size.width * p_vout->fmt_in.i_visible_height *
430              p_vout->fmt_in.i_sar_den )
431     {
432         x = ( bounds.size.height * p_vout->fmt_in.i_visible_width *
433               p_vout->fmt_in.i_sar_num ) /
434             ( p_vout->fmt_in.i_visible_height * p_vout->fmt_in.i_sar_den);
435
436         y = bounds.size.height;
437     }
438     else
439     {
440         x = bounds.size.width;
441         y = ( bounds.size.width * p_vout->fmt_in.i_visible_height *
442               p_vout->fmt_in.i_sar_den) /
443             ( p_vout->fmt_in.i_visible_width * p_vout->fmt_in.i_sar_num  );
444     }
445
446     glViewport( ( bounds.size.width - x ) / 2,
447                 ( bounds.size.height - y ) / 2, x, y );
448
449     [super reshape];
450
451     if( p_vout->p_sys->b_got_frame )
452     {
453         /* Ask the opengl module to redraw */
454         vout_thread_t * p_parent;
455         p_parent = (vout_thread_t *) p_vout->p_parent;
456         Unlock( p_vout );
457         if( p_parent && p_parent->pf_display )
458         {
459             p_parent->pf_display( p_parent, NULL );
460         }
461     }
462     else
463     {
464         glClear( GL_COLOR_BUFFER_BIT );
465         Unlock( p_vout );
466     }
467 }
468
469 - (void) update
470 {
471     Lock( p_vout );
472     [super update];
473     Unlock( p_vout );
474 }
475
476 - (void) drawRect: (NSRect) rect
477 {
478     Lock( p_vout );
479     [[p_vout->p_sys->o_glview openGLContext] flushBuffer];
480     [super drawRect:rect];
481     Unlock( p_vout );
482 }
483
484 @end
485
486 /*****************************************************************************
487  * embedded AGL context implementation
488  *****************************************************************************/
489
490 static void aglSetViewport( vout_thread_t *p_vout, Rect viewBounds, Rect clipBounds );
491 static void aglReshape( vout_thread_t * p_vout );
492 static OSStatus WindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
493
494 static int aglInit( vout_thread_t * p_vout )
495 {
496     vlc_value_t val;
497
498     Rect viewBounds;
499     Rect clipBounds;
500  
501     var_Get( p_vout->p_libvlc, "drawable", &val );
502     p_vout->p_sys->agl_drawable = (AGLDrawable)val.i_int;
503     aglSetDrawable(p_vout->p_sys->agl_ctx, p_vout->p_sys->agl_drawable);
504
505     var_Get( p_vout->p_libvlc, "drawable-view-top", &val );
506     viewBounds.top = val.i_int;
507     var_Get( p_vout->p_libvlc, "drawable-view-left", &val );
508     viewBounds.left = val.i_int;
509     var_Get( p_vout->p_libvlc, "drawable-view-bottom", &val );
510     viewBounds.bottom = val.i_int;
511     var_Get( p_vout->p_libvlc, "drawable-view-right", &val );
512     viewBounds.right = val.i_int;
513     var_Get( p_vout->p_libvlc, "drawable-clip-top", &val );
514     clipBounds.top = val.i_int;
515     var_Get( p_vout->p_libvlc, "drawable-clip-left", &val );
516     clipBounds.left = val.i_int;
517     var_Get( p_vout->p_libvlc, "drawable-clip-bottom", &val );
518     clipBounds.bottom = val.i_int;
519     var_Get( p_vout->p_libvlc, "drawable-clip-right", &val );
520     clipBounds.right = val.i_int;
521
522     p_vout->p_sys->b_clipped_out = (clipBounds.top == clipBounds.bottom)
523                                  || (clipBounds.left == clipBounds.right);
524     if( ! p_vout->p_sys->b_clipped_out )
525     {
526         aglLock(p_vout);
527         aglSetViewport(p_vout, viewBounds, clipBounds);
528         aglReshape(p_vout);
529         aglUnlock(p_vout);
530     }
531     p_vout->p_sys->clipBounds = clipBounds;
532     p_vout->p_sys->viewBounds = viewBounds;
533
534     return VLC_SUCCESS;
535 }
536
537 static void aglEnd( vout_thread_t * p_vout )
538 {
539     aglSetCurrentContext(NULL);
540     if( p_vout->p_sys->theWindow ) DisposeWindow( p_vout->p_sys->theWindow );
541 }
542
543 static void aglReshape( vout_thread_t * p_vout )
544 {
545     unsigned int x, y;
546     unsigned int i_height = p_vout->p_sys->i_height;
547     unsigned int i_width  = p_vout->p_sys->i_width;
548
549     vout_PlacePicture(p_vout, i_width, i_height, &x, &y, &i_width, &i_height);
550
551     glViewport( p_vout->p_sys->i_offx + x, p_vout->p_sys->i_offy + y, i_width, i_height );
552
553     if( p_vout->p_sys->b_got_frame )
554     {
555         /* Ask the opengl module to redraw */
556         vout_thread_t * p_parent;
557         p_parent = (vout_thread_t *) p_vout->p_parent;
558         if( p_parent && p_parent->pf_display )
559         {
560             p_parent->pf_display( p_parent, NULL );
561         }
562     }
563     else
564     {
565         glClear( GL_COLOR_BUFFER_BIT );
566     }
567 }
568
569 /* private event class */
570 enum
571 {
572     kEventClassVLCPlugin = 'vlcp',
573 };
574 /* private event kinds */
575 enum
576 {
577     kEventVLCPluginShowFullscreen = 32768,
578     kEventVLCPluginHideFullscreen,
579 };
580
581 static void sendEventToMainThread(EventTargetRef target, UInt32 class, UInt32 kind)
582 {
583     EventRef myEvent;
584     if( noErr == CreateEvent(NULL, class, kind, 0, kEventAttributeNone, &myEvent) )
585     {
586         if( noErr == SetEventParameter(myEvent, kEventParamPostTarget, typeEventTargetRef, sizeof(EventTargetRef), &target) )
587         {
588             PostEventToQueue(GetMainEventQueue(), myEvent, kEventPriorityStandard);
589         }
590         ReleaseEvent(myEvent);
591     }
592 }
593
594 static int aglManage( vout_thread_t * p_vout )
595 {
596     if( p_vout->i_changes & VOUT_ASPECT_CHANGE )
597     {
598         aglLock( p_vout );
599         aglReshape(p_vout);
600         aglUnlock( p_vout );
601         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
602     }
603     if( p_vout->i_changes & VOUT_CROP_CHANGE )
604     {
605         aglLock( p_vout );
606         aglReshape(p_vout);
607         aglUnlock( p_vout );
608         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
609     }
610     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
611     {
612         aglSetDrawable(p_vout->p_sys->agl_ctx, NULL);
613         aglLock( p_vout );
614         if( p_vout->b_fullscreen )
615         {
616             /* Close the fullscreen window and resume normal drawing */
617             vlc_value_t val;
618             Rect viewBounds;
619             Rect clipBounds;
620
621             var_Get( p_vout->p_libvlc, "drawable", &val );
622             p_vout->p_sys->agl_drawable = (AGLDrawable)val.i_int;
623             aglSetDrawable(p_vout->p_sys->agl_ctx, p_vout->p_sys->agl_drawable);
624
625             var_Get( p_vout->p_libvlc, "drawable-view-top", &val );
626             viewBounds.top = val.i_int;
627             var_Get( p_vout->p_libvlc, "drawable-view-left", &val );
628             viewBounds.left = val.i_int;
629             var_Get( p_vout->p_libvlc, "drawable-view-bottom", &val );
630             viewBounds.bottom = val.i_int;
631             var_Get( p_vout->p_libvlc, "drawable-view-right", &val );
632             viewBounds.right = val.i_int;
633             var_Get( p_vout->p_libvlc, "drawable-clip-top", &val );
634             clipBounds.top = val.i_int;
635             var_Get( p_vout->p_libvlc, "drawable-clip-left", &val );
636             clipBounds.left = val.i_int;
637             var_Get( p_vout->p_libvlc, "drawable-clip-bottom", &val );
638             clipBounds.bottom = val.i_int;
639             var_Get( p_vout->p_libvlc, "drawable-clip-right", &val );
640             clipBounds.right = val.i_int;
641
642             aglSetCurrentContext(p_vout->p_sys->agl_ctx);
643             aglSetViewport(p_vout, viewBounds, clipBounds);
644
645             /* Most Carbon APIs are not thread-safe, therefore delagate some GUI visibilty update to the main thread */
646             sendEventToMainThread(GetWindowEventTarget(p_vout->p_sys->theWindow), kEventClassVLCPlugin, kEventVLCPluginHideFullscreen);
647         }
648         else
649         {
650             Rect deviceRect;
651  
652             GDHandle deviceHdl = GetMainDevice();
653             deviceRect = (*deviceHdl)->gdRect;
654  
655             if( !p_vout->p_sys->theWindow )
656             {
657                 /* Create a window */
658                 WindowAttributes    windowAttrs;
659
660                 windowAttrs = kWindowStandardDocumentAttributes
661                             | kWindowStandardHandlerAttribute
662                             | kWindowLiveResizeAttribute
663                             | kWindowNoShadowAttribute;
664  
665                 windowAttrs &= (~kWindowResizableAttribute);
666
667                 CreateNewWindow(kDocumentWindowClass, windowAttrs, &deviceRect, &p_vout->p_sys->theWindow);
668                 if( !p_vout->p_sys->winGroup )
669                 {
670                     CreateWindowGroup(0, &p_vout->p_sys->winGroup);
671                     SetWindowGroup(p_vout->p_sys->theWindow, p_vout->p_sys->winGroup);
672                     SetWindowGroupParent( p_vout->p_sys->winGroup, GetWindowGroupOfClass(kDocumentWindowClass) ) ;
673                 }
674  
675                 // Window title
676                 CFStringRef titleKey    = CFSTR("Fullscreen VLC media plugin");
677                 CFStringRef windowTitle = CFCopyLocalizedString(titleKey, NULL);
678                 SetWindowTitleWithCFString(p_vout->p_sys->theWindow, windowTitle);
679                 CFRelease(titleKey);
680                 CFRelease(windowTitle);
681  
682                 //Install event handler
683                 static const EventTypeSpec win_events[] = {
684                     { kEventClassMouse, kEventMouseDown },
685                     { kEventClassMouse, kEventMouseMoved },
686                     { kEventClassMouse, kEventMouseUp },
687                     { kEventClassWindow, kEventWindowClosed },
688                     { kEventClassWindow, kEventWindowBoundsChanged },
689                     { kEventClassCommand, kEventCommandProcess },
690                     { kEventClassVLCPlugin, kEventVLCPluginShowFullscreen },
691                     { kEventClassVLCPlugin, kEventVLCPluginHideFullscreen },
692                 };
693                 InstallWindowEventHandler (p_vout->p_sys->theWindow, NewEventHandlerUPP (WindowEventHandler), GetEventTypeCount(win_events), win_events, p_vout, NULL);
694             }
695             else
696             {
697                 /* just in case device resolution changed */
698                 SetWindowBounds(p_vout->p_sys->theWindow, kWindowContentRgn, &deviceRect);
699             }
700             glClear( GL_COLOR_BUFFER_BIT );
701             p_vout->p_sys->agl_drawable = (AGLDrawable)GetWindowPort(p_vout->p_sys->theWindow);
702             aglSetDrawable(p_vout->p_sys->agl_ctx, p_vout->p_sys->agl_drawable);
703             aglSetCurrentContext(p_vout->p_sys->agl_ctx);
704             aglSetViewport(p_vout, deviceRect, deviceRect);
705             //aglSetFullScreen(p_vout->p_sys->agl_ctx, device_width, device_height, 0, 0);
706
707             /* Most Carbon APIs are not thread-safe, therefore delagate some GUI visibilty update to the main thread */
708             sendEventToMainThread(GetWindowEventTarget(p_vout->p_sys->theWindow), kEventClassVLCPlugin, kEventVLCPluginShowFullscreen);
709         }
710         aglReshape(p_vout);
711         aglUnlock( p_vout );
712         p_vout->b_fullscreen = !p_vout->b_fullscreen;
713         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
714     }
715     return VLC_SUCCESS;
716 }
717
718 static int aglControl( vout_thread_t *p_vout, int i_query, va_list args )
719 {
720     switch( i_query )
721     {
722         case VOUT_SET_VIEWPORT:
723         {
724             Rect viewBounds, clipBounds;
725             viewBounds.top = va_arg( args, int);
726             viewBounds.left = va_arg( args, int);
727             viewBounds.bottom = va_arg( args, int);
728             viewBounds.right = va_arg( args, int);
729             clipBounds.top = va_arg( args, int);
730             clipBounds.left = va_arg( args, int);
731             clipBounds.bottom = va_arg( args, int);
732             clipBounds.right = va_arg( args, int);
733  
734             if( !p_vout->b_fullscreen )
735             {
736                 /*
737                 ** check that the clip rect is not empty, as this is used
738                 ** by Firefox to prevent a plugin from displaying during
739                 ** a scrolling event. In this case we just prevent buffers
740                 ** from being swapped and ignore clipping as this is less
741                 ** disruptive than a GL geometry change
742                 */
743
744                 p_vout->p_sys->b_clipped_out = (clipBounds.top == clipBounds.bottom)
745                                              || (clipBounds.left == clipBounds.right);
746                 if( ! p_vout->p_sys->b_clipped_out )
747                 {
748                     /* ignore consecutive viewport update with identical parameters */
749                     if( memcmp(&clipBounds, &(p_vout->p_sys->clipBounds), sizeof(clipBounds) )
750                      && memcmp(&viewBounds, &(p_vout->p_sys->viewBounds), sizeof(viewBounds)) )
751                     {
752                         aglLock( p_vout );
753                         aglSetViewport(p_vout, viewBounds, clipBounds);
754                         aglReshape( p_vout );
755                         aglUnlock( p_vout );
756                         p_vout->p_sys->clipBounds = clipBounds;
757                         p_vout->p_sys->viewBounds = viewBounds;
758                     }
759                 }
760             }
761             return VLC_SUCCESS;
762         }
763
764         case VOUT_REDRAW_RECT:
765         {
766             vout_thread_t * p_parent;
767             Rect areaBounds;
768
769             areaBounds.top = va_arg( args, int);
770             areaBounds.left = va_arg( args, int);
771             areaBounds.bottom = va_arg( args, int);
772             areaBounds.right = va_arg( args, int);
773
774             /* Ask the opengl module to redraw */
775             p_parent = (vout_thread_t *) p_vout->p_parent;
776             if( p_parent && p_parent->pf_display )
777             {
778                 p_parent->pf_display( p_parent, NULL );
779             }
780             return VLC_SUCCESS;
781         }
782
783         case VOUT_REPARENT:
784         {
785             AGLDrawable drawable = (AGLDrawable)va_arg( args, int);
786             if( !p_vout->b_fullscreen && drawable != p_vout->p_sys->agl_drawable )
787             {
788                 p_vout->p_sys->agl_drawable = drawable;
789                 aglSetDrawable(p_vout->p_sys->agl_ctx, drawable);
790             }
791             return VLC_SUCCESS;
792         }
793
794         default:
795             return vout_vaControlDefault( p_vout, i_query, args );
796     }
797 }
798
799 static void aglSwap( vout_thread_t * p_vout )
800 {
801     if( ! p_vout->p_sys->b_clipped_out )
802     {
803         p_vout->p_sys->b_got_frame = true;
804         aglSwapBuffers(p_vout->p_sys->agl_ctx);
805     }
806     else
807     {
808         /* drop frame */
809         glFlush();
810     }
811 }
812
813 /* Enter this function with the p_vout locked */
814 static void aglSetViewport( vout_thread_t *p_vout, Rect viewBounds, Rect clipBounds )
815 {
816     // mozilla plugin provides coordinates based on port bounds
817     // however AGL coordinates are based on window structure region
818     // and are vertically flipped
819     GLint rect[4];
820     CGrafPtr port = (CGrafPtr)p_vout->p_sys->agl_drawable;
821     Rect winBounds, clientBounds;
822
823     GetWindowBounds(GetWindowFromPort(port),
824         kWindowStructureRgn, &winBounds);
825     GetWindowBounds(GetWindowFromPort(port),
826         kWindowContentRgn, &clientBounds);
827
828     /* update video clipping bounds in drawable */
829     rect[0] = (clientBounds.left-winBounds.left)
830             + clipBounds.left;                  // from window left edge
831     rect[1] = (winBounds.bottom-winBounds.top)
832             - (clientBounds.top-winBounds.top)
833             - clipBounds.bottom;                // from window bottom edge
834     rect[2] = clipBounds.right-clipBounds.left; // width
835     rect[3] = clipBounds.bottom-clipBounds.top; // height
836     aglSetInteger(p_vout->p_sys->agl_ctx, AGL_BUFFER_RECT, rect);
837     aglEnable(p_vout->p_sys->agl_ctx, AGL_BUFFER_RECT);
838
839     /* update video internal bounds in drawable */
840     p_vout->p_sys->i_width  = viewBounds.right-viewBounds.left;
841     p_vout->p_sys->i_height = viewBounds.bottom-viewBounds.top;
842     p_vout->p_sys->i_offx   = -clipBounds.left - viewBounds.left;
843     p_vout->p_sys->i_offy   = clipBounds.bottom + viewBounds.top
844                             - p_vout->p_sys->i_height;
845
846     aglUpdateContext(p_vout->p_sys->agl_ctx);
847 }
848
849 //default window event handler
850 static pascal OSStatus WindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
851 {
852     OSStatus result = noErr;
853     UInt32 class = GetEventClass (event);
854     UInt32 kind = GetEventKind (event);
855     vout_thread_t *p_vout = (vout_thread_t *)userData;
856
857     result = CallNextEventHandler(nextHandler, event);
858     if(class == kEventClassCommand)
859     {
860         HICommand theHICommand;
861         GetEventParameter( event, kEventParamDirectObject, typeHICommand, NULL, sizeof( HICommand ), NULL, &theHICommand );
862  
863         switch ( theHICommand.commandID )
864         {
865             default:
866                 result = eventNotHandledErr;
867         }
868     }
869     else if(class == kEventClassWindow)
870     {
871         WindowRef     window;
872         Rect          rectPort = {0,0,0,0};
873  
874         GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &window);
875
876         if(window)
877         {
878             GetPortBounds(GetWindowPort(window), &rectPort);
879         }
880
881         switch (kind)
882         {
883             case kEventWindowClosed:
884             case kEventWindowZoomed:
885             case kEventWindowBoundsChanged:
886                 break;
887  
888             default:
889                 result = eventNotHandledErr;
890         }
891     }
892     else if(class == kEventClassMouse)
893     {
894         switch (kind)
895         {
896             case kEventMouseDown:
897             {
898                 UInt16     button;
899  
900                 GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
901                 switch (button)
902                 {
903                     case kEventMouseButtonPrimary:
904                     {
905                         vlc_value_t val;
906
907                         var_Get( p_vout, "mouse-button-down", &val );
908                         val.i_int |= 1;
909                         var_Set( p_vout, "mouse-button-down", val );
910                         break;
911                     }
912                     case kEventMouseButtonSecondary:
913                     {
914                         vlc_value_t val;
915
916                         var_Get( p_vout, "mouse-button-down", &val );
917                         val.i_int |= 2;
918                         var_Set( p_vout, "mouse-button-down", val );
919                         break;
920                     }
921                     case kEventMouseButtonTertiary:
922                     {
923                         vlc_value_t val;
924
925                         var_Get( p_vout, "mouse-button-down", &val );
926                         val.i_int |= 4;
927                         var_Set( p_vout, "mouse-button-down", val );
928                         break;
929                     }
930                     default:
931                         result = eventNotHandledErr;
932                 }
933                 break;
934             }
935
936             case kEventMouseUp:
937             {
938                 UInt16     button;
939  
940                 GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
941                 switch (button)
942                 {
943                     case kEventMouseButtonPrimary:
944                     {
945                         UInt32 clickCount = 0;
946                         GetEventParameter(event, kEventParamClickCount, typeUInt32, NULL, sizeof(clickCount), NULL, &clickCount);
947                         if( clickCount > 1 )
948                         {
949                             vlc_value_t val;
950
951                             val.b_bool = false;
952                             var_Set((vout_thread_t *) p_vout->p_parent, "fullscreen", val);
953                         }
954                         else
955                         {
956                             vlc_value_t val;
957
958                             var_SetBool( p_vout, "mouse-clicked", true );
959
960                             var_Get( p_vout, "mouse-button-down", &val );
961                             val.i_int &= ~1;
962                             var_Set( p_vout, "mouse-button-down", val );
963                         }
964                         break;
965                     }
966                     case kEventMouseButtonSecondary:
967                     {
968                         vlc_value_t val;
969
970                         var_Get( p_vout, "mouse-button-down", &val );
971                         val.i_int &= ~2;
972                         var_Set( p_vout, "mouse-button-down", val );
973                         break;
974                     }
975                     case kEventMouseButtonTertiary:
976                     {
977                         vlc_value_t val;
978
979                         var_Get( p_vout, "mouse-button-down", &val );
980                         val.i_int &= ~2;
981                         var_Set( p_vout, "mouse-button-down", val );
982                         break;
983                     }
984                     default:
985                         result = eventNotHandledErr;
986                 }
987                 break;
988             }
989
990             case kEventMouseMoved:
991             {
992                 Point ml;
993                 vlc_value_t val;
994
995                 unsigned int i_x, i_y;
996                 unsigned int i_height = p_vout->p_sys->i_height;
997                 unsigned int i_width  = p_vout->p_sys->i_width;
998
999                 vout_PlacePicture(p_vout, i_width, i_height, &i_x, &i_y, &i_width, &i_height);
1000
1001                 GetEventParameter(event, kEventParamWindowMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &ml);
1002  
1003                 val.i_int = ( ((int)ml.h) - i_x ) *
1004                             p_vout->render.i_width / i_width;
1005                 var_Set( p_vout, "mouse-x", val );
1006
1007                 val.i_int = ( ((int)ml.v) - i_y ) *
1008                             p_vout->render.i_height / i_height;
1009
1010                 var_Set( p_vout, "mouse-y", val );
1011
1012                 val.b_bool = true;
1013                 var_Set( p_vout, "mouse-moved", val );
1014
1015                 break;
1016             }
1017  
1018             default:
1019                 result = eventNotHandledErr;
1020         }
1021     }
1022     else if(class == kEventClassTextInput)
1023     {
1024         switch (kind)
1025         {
1026             case kEventTextInputUnicodeForKeyEvent:
1027             {
1028                 break;
1029             }
1030             default:
1031                 result = eventNotHandledErr;
1032         }
1033     }
1034     else if(class == kEventClassVLCPlugin)
1035     {
1036         switch (kind)
1037         {
1038             case kEventVLCPluginShowFullscreen:
1039                 ShowWindow (p_vout->p_sys->theWindow);
1040                 SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1041                 //CGDisplayHideCursor(kCGDirectMainDisplay);
1042                 break;
1043             case kEventVLCPluginHideFullscreen:
1044                 HideWindow (p_vout->p_sys->theWindow);
1045                 SetSystemUIMode( kUIModeNormal, 0);
1046                 CGDisplayShowCursor(kCGDirectMainDisplay);
1047                 break;
1048             default:
1049                 result = eventNotHandledErr;
1050                 break;
1051         }
1052     }
1053     return result;
1054 }
1055
1056 static int aglLock( vout_thread_t * p_vout )
1057 {
1058         /* get the underlying CGL context */
1059     CGLContextObj cglContext;
1060     if( aglGetCGLContext(p_vout->p_sys->agl_ctx, (void**)&cglContext) )
1061     {
1062         if( kCGLNoError == CGLLockContext( cglContext ) )
1063         {
1064             aglSetCurrentContext(p_vout->p_sys->agl_ctx);
1065             return 0;
1066         }
1067     }
1068     return 1;
1069 }
1070
1071 static void aglUnlock( vout_thread_t * p_vout )
1072 {
1073         /* get the underlying CGL context */
1074     CGLContextObj cglContext;
1075     if( aglGetCGLContext(p_vout->p_sys->agl_ctx, (void**)&cglContext) )
1076     {
1077         CGLUnlockContext( cglContext );
1078     }
1079 }
1080
1081