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