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