]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
macosx/vout* : OpenGL VRAM texturing finaly works correctly now.
[vlc] / modules / gui / macosx / vout.m
1 /*****************************************************************************
2  * vout.m: MacOS X video output module
3  *****************************************************************************
4  * Copyright (C) 2001-2003 VideoLAN
5  * $Id: vout.m,v 1.77 2004/02/03 13:00:27 titer Exp $
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 <thedj@users.sourceforge.net>
11  *          Eric Petit <titer@m0k.org>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  * 
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include <errno.h>                                                 /* ENOMEM */
32 #include <stdlib.h>                                                /* free() */
33 #include <string.h>                                            /* strerror() */
34
35 #include <QuickTime/QuickTime.h>
36
37 #include <OpenGL/OpenGL.h>
38 #include <OpenGL/gl.h>
39 #include <OpenGL/glext.h>
40
41 #include <vlc_keys.h>
42
43 #include "intf.h"
44 #include "vout.h"
45
46 #define QT_MAX_DIRECTBUFFERS 10
47 #define VL_MAX_DISPLAYS 16
48
49 struct picture_sys_t
50 {
51     void *p_info;
52     unsigned int i_size;
53
54     /* When using I420 output */
55     PlanarPixmapInfoYUV420 pixmap_i420;
56 };
57
58 /*****************************************************************************
59  * Local prototypes
60  *****************************************************************************/
61
62 static int  vout_Init      ( vout_thread_t * );
63 static void vout_End       ( vout_thread_t * );
64 static int  vout_Manage    ( vout_thread_t * );
65 static void vout_Display   ( vout_thread_t *, picture_t * );
66
67 static int  CoSendRequest      ( vout_thread_t *, SEL );
68 static int  CoCreateWindow     ( vout_thread_t * );
69 static int  CoDestroyWindow    ( vout_thread_t * );
70 static int  CoToggleFullscreen ( vout_thread_t * );
71
72 static void VLCHideMouse       ( vout_thread_t *, BOOL );
73
74 static void QTScaleMatrix      ( vout_thread_t * );
75 static int  QTCreateSequence   ( vout_thread_t * );
76 static void QTDestroySequence  ( vout_thread_t * );
77 static int  QTNewPicture       ( vout_thread_t *, picture_t * );
78 static void QTFreePicture      ( vout_thread_t *, picture_t * );
79
80 /*****************************************************************************
81  * OpenVideo: allocates MacOS X video thread output method
82  *****************************************************************************
83  * This function allocates and initializes a MacOS X vout method.
84  *****************************************************************************/
85 int E_(OpenVideo) ( vlc_object_t *p_this )
86 {   
87     vout_thread_t * p_vout = (vout_thread_t *)p_this;
88     OSErr err;
89     int i_timeout;
90
91     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
92     if( p_vout->p_sys == NULL )
93     {
94         msg_Err( p_vout, "out of memory" );
95         return( 1 );
96     }
97
98     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
99
100     /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
101     for( i_timeout = 20 ; i_timeout-- ; )
102     {
103         if( NSApp == NULL )
104         {
105             msleep( INTF_IDLE_SLEEP );
106         }
107     }
108
109     if( NSApp == NULL )
110     {
111         /* no MacOS X intf, unable to communicate with MT */
112         msg_Err( p_vout, "no MacOS X interface present" );
113         free( p_vout->p_sys );
114         return( 1 );
115     }
116
117     if( [NSApp respondsToSelector: @selector(getIntf)] )
118     {
119         intf_thread_t * p_intf;
120
121         for( i_timeout = 10 ; i_timeout-- ; )
122         {
123             if( ( p_intf = [NSApp getIntf] ) == NULL )
124             {
125                 msleep( INTF_IDLE_SLEEP );
126             }
127         }
128
129         if( p_intf == NULL )
130         {
131             msg_Err( p_vout, "MacOS X intf has getIntf, but is NULL" );
132             free( p_vout->p_sys );
133             return( 1 );
134         }
135     }
136
137     p_vout->p_sys->b_mouse_moved = VLC_TRUE;
138     p_vout->p_sys->i_time_mouse_last_moved = mdate();
139
140     /* set window size */
141     p_vout->p_sys->s_rect.size.width = p_vout->i_window_width;
142     p_vout->p_sys->s_rect.size.height = p_vout->i_window_height;
143
144     /* Check if we should use QuickTime or OpenGL */
145     p_vout->p_sys->i_opengl = config_GetInt( p_vout, "macosx-opengl" );
146
147     if( !p_vout->p_sys->i_opengl )
148     {
149         /* Initialize QuickTime */
150         p_vout->p_sys->h_img_descr = 
151             (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
152         p_vout->p_sys->p_matrix =
153             (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
154         p_vout->p_sys->p_fullscreen_state = NULL;
155
156         if( ( err = EnterMovies() ) != noErr )
157         {
158             msg_Err( p_vout, "EnterMovies failed: %d", err );
159             free( p_vout->p_sys->p_matrix );
160             DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
161             free( p_vout->p_sys );
162             return( 1 );
163         } 
164
165         /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
166         vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
167
168         err = FindCodec( kYUV420CodecType, bestSpeedCodec,
169                             nil, &p_vout->p_sys->img_dc );
170         
171         vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
172         if( err == noErr && p_vout->p_sys->img_dc != 0 )
173         {
174             p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
175             p_vout->p_sys->i_codec = kYUV420CodecType;
176         }
177         else
178         {
179             msg_Err( p_vout, "failed to find an appropriate codec" );
180         }
181
182         if( p_vout->p_sys->img_dc == 0 )
183         {
184             free( p_vout->p_sys->p_matrix );
185             DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
186             free( p_vout->p_sys );
187             return VLC_EGENERIC;        
188         }
189     }
190
191     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
192     NSArray * o_screens = [NSScreen screens];
193     if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
194     {
195         int i = 1;
196         vlc_value_t val, text;
197         NSScreen * o_screen;
198
199         int i_option = config_GetInt( p_vout, "macosx-vdev" );
200
201         var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
202                                             VLC_VAR_HASCHOICE ); 
203         text.psz_string = _("Video device");
204         var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
205         
206         NSEnumerator * o_enumerator = [o_screens objectEnumerator];
207
208         while( (o_screen = [o_enumerator nextObject]) != NULL )
209         {
210             char psz_temp[255];
211             NSRect s_rect = [o_screen frame];
212
213             snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1, 
214                       "%s %d (%dx%d)", _("Screen"), i,
215                       (int)s_rect.size.width, (int)s_rect.size.height ); 
216
217             text.psz_string = psz_temp;
218             val.i_int = i;
219             var_Change( p_vout, "video-device",
220                         VLC_VAR_ADDCHOICE, &val, &text );
221
222             if( ( i - 1 ) == i_option )
223             {
224                 var_Set( p_vout, "video-device", val );
225             }
226             i++;
227         }
228
229         var_AddCallback( p_vout, "video-device", vout_VarCallback,
230                          NULL );
231
232         val.b_bool = VLC_TRUE;
233         var_Set( p_vout, "intf-change", val );
234     }
235     [o_pool release];
236
237     if( CoCreateWindow( p_vout ) )
238     {
239         msg_Err( p_vout, "unable to create window" );
240         if( !p_vout->p_sys->i_opengl )
241         {
242             free( p_vout->p_sys->p_matrix );
243             DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
244         }
245         free( p_vout->p_sys ); 
246         return( 1 );
247     }
248
249     p_vout->pf_init = vout_Init;
250     p_vout->pf_end = vout_End;
251     p_vout->pf_manage = vout_Manage;
252     p_vout->pf_render = NULL;
253     p_vout->pf_display = vout_Display;
254
255     return( 0 );
256 }
257
258 /*****************************************************************************
259  * vout_Init: initialize video thread output method
260  *****************************************************************************/
261 static int vout_Init( vout_thread_t *p_vout )
262 {
263     int i_index;
264     picture_t *p_pic;
265
266     I_OUTPUTPICTURES = 0;
267
268     /* Initialize the output structure; we already found a codec,
269      * and the corresponding chroma we will be using. Since we can
270      * arbitrary scale, stick to the coordinates and aspect. */
271     p_vout->output.i_width  = p_vout->render.i_width;
272     p_vout->output.i_height = p_vout->render.i_height;
273     p_vout->output.i_aspect = p_vout->render.i_aspect;
274
275     if( !p_vout->p_sys->i_opengl )
276     {
277         SetPort( p_vout->p_sys->p_qdport );
278         QTScaleMatrix( p_vout );
279
280         if( QTCreateSequence( p_vout ) )
281         {
282             msg_Err( p_vout, "unable to create sequence" );
283             return( 1 );
284         }
285     }
286     else
287     {
288         p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
289         p_vout->output.i_rmask  = 0xFF0000;
290         p_vout->output.i_gmask  = 0x00FF00;
291         p_vout->output.i_bmask  = 0x0000FF;
292     }
293
294     /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
295     while( I_OUTPUTPICTURES <
296            p_vout->p_sys->i_opengl ? 1 : QT_MAX_DIRECTBUFFERS )
297     {
298         p_pic = NULL;
299
300         /* Find an empty picture slot */
301         for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
302         {
303             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
304             {
305                 p_pic = p_vout->p_picture + i_index;
306                 break;
307             }
308         }
309
310         /* Allocate the picture */
311         if( p_pic == NULL )
312         {
313             break;
314         }
315
316         if( !p_vout->p_sys->i_opengl )
317         {
318             if( QTNewPicture( p_vout, p_pic ) )
319             {
320                 break;
321             }
322         }
323         else
324         {
325             /* Nothing special to do, we just need a basic allocated
326                picture_t */
327             vout_AllocatePicture( p_vout, p_pic, p_vout->output.i_chroma,
328                     p_vout->output.i_width, p_vout->output.i_height,
329                     p_vout->output.i_aspect );
330         }
331
332         p_pic->i_status = DESTROYED_PICTURE;
333         p_pic->i_type   = DIRECT_PICTURE;
334
335         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
336
337         I_OUTPUTPICTURES++;
338     }
339
340     if( p_vout->p_sys->i_opengl )
341     {
342         [p_vout->p_sys->o_glview lockFocus];
343         [p_vout->p_sys->o_glview initTextures];
344         [p_vout->p_sys->o_glview unlockFocus];
345     }
346
347     return( 0 );
348 }
349
350 /*****************************************************************************
351  * vout_End: terminate video thread output method
352  *****************************************************************************/
353 static void vout_End( vout_thread_t *p_vout )
354 {
355     int i_index;
356
357     if( !p_vout->p_sys->i_opengl )
358     {
359         QTDestroySequence( p_vout );
360     }
361
362     /* Free the direct buffers we allocated */
363     for( i_index = I_OUTPUTPICTURES; i_index; )
364     {
365         i_index--;
366         if( !p_vout->p_sys->i_opengl )
367         {
368             QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
369         }
370         else
371         {
372             free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
373         }
374     }
375 }
376
377 /*****************************************************************************
378  * CloseVideo: destroy video thread output method
379  *****************************************************************************/
380 void E_(CloseVideo) ( vlc_object_t *p_this )
381 {       
382     vout_thread_t * p_vout = (vout_thread_t *)p_this;     
383
384     if( CoDestroyWindow( p_vout ) )
385     {
386         msg_Err( p_vout, "unable to destroy window" );
387     }
388
389     if( !p_vout->p_sys->i_opengl )
390     {
391         if ( p_vout->p_sys->p_fullscreen_state != NULL )
392             EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
393
394         ExitMovies();
395
396         free( p_vout->p_sys->p_matrix );
397         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
398     }
399
400     free( p_vout->p_sys );
401 }
402
403 /*****************************************************************************
404  * vout_Manage: handle events
405  *****************************************************************************
406  * This function should be called regularly by video output thread. It manages
407  * console events. It returns a non null value on error.
408  *****************************************************************************/
409 static int vout_Manage( vout_thread_t *p_vout )
410 {
411     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
412     {
413         if( CoToggleFullscreen( p_vout ) )  
414         {
415             return( 1 );
416         }
417
418         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
419     }
420
421     if( p_vout->i_changes & VOUT_SIZE_CHANGE ) 
422     {
423         if( !p_vout->p_sys->i_opengl )
424         {
425             QTScaleMatrix( p_vout );
426             SetDSequenceMatrix( p_vout->p_sys->i_seq, 
427                                 p_vout->p_sys->p_matrix );
428         }
429  
430         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
431     }
432
433     /* hide/show mouse cursor 
434      * this code looks unnecessarily complicated, but is necessary like this.
435      * it has to deal with multiple monitors and therefore checks a lot */
436     if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
437     {
438         if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 )
439         {
440             VLCHideMouse( p_vout, YES );
441         }
442     }
443     else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
444     {
445         VLCHideMouse( p_vout, NO );
446     }
447     
448     /* disable screen saver */
449     UpdateSystemActivity( UsrActivity );
450     
451     return( 0 );
452 }
453
454 /*****************************************************************************
455  * vout_Display: displays previously rendered output
456  *****************************************************************************
457  * This function sends the currently rendered image to the display.
458  *****************************************************************************/
459 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
460 {
461     if( !p_vout->p_sys->i_opengl )
462     {
463         OSErr err;
464         CodecFlags flags;
465
466         if( ( err = DecompressSequenceFrameS( 
467                         p_vout->p_sys->i_seq,
468                         p_pic->p_sys->p_info,
469                         p_pic->p_sys->i_size,                    
470                         codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
471         {
472             msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
473         }
474         else
475         {
476             QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
477         }
478     }
479     else
480     {
481         if( [p_vout->p_sys->o_glview lockFocusIfCanDraw] )
482         {
483             /* Texture gotta be reload before the buffer is filled
484                (thanks to gcc from arstechnica forums) */
485             [p_vout->p_sys->o_glview drawRect:
486                 [p_vout->p_sys->o_glview bounds]];
487             [p_vout->p_sys->o_glview reloadTexture];
488             [p_vout->p_sys->o_glview unlockFocus];
489         }
490     }
491 }
492
493 /*****************************************************************************
494  * CoSendRequest: send request to interface thread
495  *****************************************************************************
496  * Returns 0 on success, 1 otherwise
497  *****************************************************************************/
498 static int CoSendRequest( vout_thread_t *p_vout, SEL sel )
499 {
500     int i_ret = 0;
501     vlc_value_t val;
502     intf_thread_t * p_intf;
503
504     VLCVout * o_vlv = [[VLCVout alloc] init];
505
506     if( ( i_ret = ExecuteOnMainThread( o_vlv, sel, (void *)p_vout ) ) )
507     {
508         msg_Err( p_vout, "SendRequest: no way to communicate with mt" );
509     }
510
511     [o_vlv release];
512
513     /*This makes this function dependant of the presence of a macosx 
514     interface. We do not check if this interface exists, since it has 
515     already been done before.*/
516
517     p_intf = [NSApp getIntf];
518
519     val.b_bool = VLC_TRUE;
520     var_Create(p_intf,"intf-change",VLC_VAR_BOOL);
521     var_Set(p_intf, "intf-change",val);
522
523     return( i_ret );
524 }
525
526 /*****************************************************************************
527  * CoCreateWindow: create new window 
528  *****************************************************************************
529  * Returns 0 on success, 1 otherwise
530  *****************************************************************************/
531 static int CoCreateWindow( vout_thread_t *p_vout )
532 {
533     if( CoSendRequest( p_vout, @selector(createWindow:) ) )
534     {
535         msg_Err( p_vout, "CoSendRequest (createWindow) failed" );
536         return( 1 );
537     }
538     
539     return( 0 );
540 }
541
542 /*****************************************************************************
543  * CoDestroyWindow: destroy window 
544  *****************************************************************************
545  * Returns 0 on success, 1 otherwise
546  *****************************************************************************/
547 static int CoDestroyWindow( vout_thread_t *p_vout )
548 {
549
550     VLCHideMouse( p_vout, NO );
551
552     if( CoSendRequest( p_vout, @selector(destroyWindow:) ) )
553     {
554         msg_Err( p_vout, "CoSendRequest (destroyWindow) failed" );
555         return( 1 );
556     }
557
558     return( 0 );
559 }
560
561 /*****************************************************************************
562  * CoToggleFullscreen: toggle fullscreen 
563  *****************************************************************************
564  * Returns 0 on success, 1 otherwise
565  *****************************************************************************/
566 static int CoToggleFullscreen( vout_thread_t *p_vout )
567 {
568     if( p_vout->p_sys->i_opengl )
569     {
570         /* TODO */
571         return 0;
572     }
573     
574     QTDestroySequence( p_vout );
575
576     if( CoDestroyWindow( p_vout ) )
577     {
578         msg_Err( p_vout, "unable to destroy window" );
579         return( 1 );
580     }
581     
582     p_vout->b_fullscreen = !p_vout->b_fullscreen;
583
584     if( CoCreateWindow( p_vout ) )
585     {
586         msg_Err( p_vout, "unable to create window" );
587         return( 1 );
588     }
589
590     SetPort( p_vout->p_sys->p_qdport );
591     QTScaleMatrix( p_vout );
592
593     if( QTCreateSequence( p_vout ) )
594     {
595         msg_Err( p_vout, "unable to create sequence" );
596         return( 1 ); 
597     } 
598
599     return( 0 );
600 }
601
602 /*****************************************************************************
603  * VLCHideMouse: if b_hide then hide the cursor
604  *****************************************************************************/
605 static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
606 {
607     BOOL b_inside;
608     NSRect s_rect;
609     NSPoint ml;
610     NSWindow *o_window = p_vout->p_sys->o_window;
611     NSView *o_contents = [o_window contentView];
612     
613     s_rect = [o_contents bounds];
614     ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
615     ml = [o_contents convertPoint:ml fromView:nil];
616     b_inside = [o_contents mouse: ml inRect: s_rect];
617     
618     if ( b_hide && b_inside )
619     {
620         /* only hide if mouse over VLCQTView */
621         [NSCursor setHiddenUntilMouseMoves: YES];
622     }
623     else if ( !b_hide )
624     {
625         [NSCursor setHiddenUntilMouseMoves: NO];
626     }
627     p_vout->p_sys->b_mouse_moved = NO;
628     p_vout->p_sys->i_time_mouse_last_moved = mdate();
629     return;
630 }
631
632 /*****************************************************************************
633  * QTScaleMatrix: scale matrix 
634  *****************************************************************************/
635 static void QTScaleMatrix( vout_thread_t *p_vout )
636 {
637     Rect s_rect;
638     unsigned int i_width, i_height;
639     Fixed factor_x, factor_y;
640     unsigned int i_offset_x = 0;
641     unsigned int i_offset_y = 0;
642
643     GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
644
645     i_width = s_rect.right - s_rect.left;
646     i_height = s_rect.bottom - s_rect.top;
647
648     if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
649     {
650         int i_adj_width = i_height * p_vout->output.i_aspect /
651                           VOUT_ASPECT_FACTOR;
652
653         factor_x = FixDiv( Long2Fix( i_adj_width ),
654                            Long2Fix( p_vout->output.i_width ) );
655         factor_y = FixDiv( Long2Fix( i_height ),
656                            Long2Fix( p_vout->output.i_height ) );
657
658         i_offset_x = (i_width - i_adj_width) / 2;
659     }
660     else
661     {
662         int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
663                            p_vout->output.i_aspect;
664
665         factor_x = FixDiv( Long2Fix( i_width ),
666                            Long2Fix( p_vout->output.i_width ) );
667         factor_y = FixDiv( Long2Fix( i_adj_height ),
668                            Long2Fix( p_vout->output.i_height ) );
669
670         i_offset_y = (i_height - i_adj_height) / 2;
671     }
672     
673     SetIdentityMatrix( p_vout->p_sys->p_matrix );
674
675     ScaleMatrix( p_vout->p_sys->p_matrix,
676                  factor_x, factor_y,
677                  Long2Fix(0), Long2Fix(0) );            
678
679     TranslateMatrix( p_vout->p_sys->p_matrix, 
680                      Long2Fix(i_offset_x), 
681                      Long2Fix(i_offset_y) );
682
683 }
684
685 /*****************************************************************************
686  * QTCreateSequence: create a new sequence 
687  *****************************************************************************
688  * Returns 0 on success, 1 otherwise
689  *****************************************************************************/
690 static int QTCreateSequence( vout_thread_t *p_vout )
691 {
692     OSErr err;
693     ImageDescriptionPtr p_descr;
694
695     HLock( (Handle)p_vout->p_sys->h_img_descr );
696     p_descr = *p_vout->p_sys->h_img_descr;
697
698     p_descr->idSize = sizeof(ImageDescription);
699     p_descr->cType = p_vout->p_sys->i_codec;
700     p_descr->version = 1;
701     p_descr->revisionLevel = 0;
702     p_descr->vendor = 'appl';
703     p_descr->width = p_vout->output.i_width;
704     p_descr->height = p_vout->output.i_height;
705     p_descr->hRes = Long2Fix(72);
706     p_descr->vRes = Long2Fix(72);
707     p_descr->spatialQuality = codecLosslessQuality;
708     p_descr->frameCount = 1;
709     p_descr->clutID = -1;
710     p_descr->dataSize = 0;
711     p_descr->depth = 24;
712
713     HUnlock( (Handle)p_vout->p_sys->h_img_descr );
714
715     if( ( err = DecompressSequenceBeginS( 
716                               &p_vout->p_sys->i_seq,
717                               p_vout->p_sys->h_img_descr,
718                               NULL, 0,
719                               p_vout->p_sys->p_qdport,
720                               NULL, NULL,
721                               p_vout->p_sys->p_matrix,
722                               0, NULL,
723                               codecFlagUseImageBuffer,
724                               codecLosslessQuality,
725                               p_vout->p_sys->img_dc ) ) )
726     {
727         msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
728         return( 1 );
729     }
730
731     return( 0 );
732 }
733
734 /*****************************************************************************
735  * QTDestroySequence: destroy sequence 
736  *****************************************************************************/
737 static void QTDestroySequence( vout_thread_t *p_vout )
738 {
739     CDSequenceEnd( p_vout->p_sys->i_seq );
740 }
741
742 /*****************************************************************************
743  * QTNewPicture: allocate a picture
744  *****************************************************************************
745  * Returns 0 on success, 1 otherwise
746  *****************************************************************************/
747 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
748 {
749     int i_width  = p_vout->output.i_width;
750     int i_height = p_vout->output.i_height;
751
752     /* We know the chroma, allocate a buffer which will be used
753      * directly by the decoder */
754     p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
755
756     if( p_pic->p_sys == NULL )
757     {
758         return( -1 );
759     }
760
761     switch( p_vout->output.i_chroma )
762     {
763         case VLC_FOURCC('I','4','2','0'):
764
765             p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
766             p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
767
768             /* Allocate the memory buffer */
769             p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
770                                           16, i_width * i_height * 3 / 2 );
771
772             /* Y buffer */
773             p_pic->Y_PIXELS = p_pic->p_data; 
774             p_pic->p[Y_PLANE].i_lines = i_height;
775             p_pic->p[Y_PLANE].i_pitch = i_width;
776             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
777             p_pic->p[Y_PLANE].i_visible_pitch = i_width;
778
779             /* U buffer */
780             p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
781             p_pic->p[U_PLANE].i_lines = i_height / 2;
782             p_pic->p[U_PLANE].i_pitch = i_width / 2;
783             p_pic->p[U_PLANE].i_pixel_pitch = 1;
784             p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
785
786             /* V buffer */
787             p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
788             p_pic->p[V_PLANE].i_lines = i_height / 2;
789             p_pic->p[V_PLANE].i_pitch = i_width / 2;
790             p_pic->p[V_PLANE].i_pixel_pitch = 1;
791             p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
792
793             /* We allocated 3 planes */
794             p_pic->i_planes = 3;
795
796 #define P p_pic->p_sys->pixmap_i420
797             P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
798                                        - p_pic->p_sys->p_info;
799             P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
800                                         - p_pic->p_sys->p_info;
801             P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
802                                         - p_pic->p_sys->p_info;
803
804             P.componentInfoY.rowBytes = i_width;
805             P.componentInfoCb.rowBytes = i_width / 2;
806             P.componentInfoCr.rowBytes = i_width / 2;
807 #undef P
808
809             break;
810
811     default:
812         /* Unknown chroma, tell the guy to get lost */
813         free( p_pic->p_sys );
814         msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
815                  p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
816         p_pic->i_planes = 0;
817         return( -1 );
818     }
819
820     return( 0 );
821 }
822
823 /*****************************************************************************
824  * QTFreePicture: destroy a picture allocated with QTNewPicture
825  *****************************************************************************/
826 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
827 {
828     switch( p_vout->output.i_chroma )
829     {
830         case VLC_FOURCC('I','4','2','0'):
831             free( p_pic->p_data_orig );
832             break;
833     }
834
835     free( p_pic->p_sys );
836 }
837
838 /*****************************************************************************
839  * VLCWindow implementation
840  *****************************************************************************/
841 @implementation VLCWindow
842
843 - (void)setVout:(vout_thread_t *)_p_vout
844 {
845     p_vout = _p_vout;
846 }
847
848 - (vout_thread_t *)getVout
849 {
850     return( p_vout );
851 }
852
853 - (void)scaleWindowWithFactor: (float)factor
854 {
855     NSSize newsize;
856     int i_corrected_height, i_corrected_width;
857     NSPoint topleftbase;
858     NSPoint topleftscreen;
859     
860     if ( !p_vout->b_fullscreen )
861     {
862         topleftbase.x = 0;
863         topleftbase.y = [self frame].size.height;
864         topleftscreen = [self convertBaseToScreen: topleftbase];
865         
866         if( p_vout->output.i_height * p_vout->output.i_aspect > 
867                         p_vout->output.i_width * VOUT_ASPECT_FACTOR )
868         {
869             i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect /
870                                             VOUT_ASPECT_FACTOR;
871             newsize.width = (int) ( i_corrected_width * factor );
872             newsize.height = (int) ( p_vout->render.i_height * factor );
873         }
874         else
875         {
876             i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR /
877                                             p_vout->output.i_aspect;
878             newsize.width = (int) ( p_vout->render.i_width * factor );
879             newsize.height = (int) ( i_corrected_height * factor );
880         }
881     
882         [self setContentSize: newsize];
883         
884         [self setFrameTopLeftPoint: topleftscreen];
885         p_vout->i_changes |= VOUT_SIZE_CHANGE;
886     }
887 }
888
889 - (void)toggleFloatOnTop
890 {
891     if( config_GetInt( p_vout, "video-on-top" ) )
892     {
893         config_PutInt( p_vout, "video-on-top", 0 );
894         [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
895     }
896     else
897     {
898         config_PutInt( p_vout, "video-on-top", 1 );
899         [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
900     }
901 }
902
903 - (void)toggleFullscreen
904 {
905     config_PutInt(p_vout, "fullscreen", !p_vout->b_fullscreen);
906     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
907 }
908
909 - (BOOL)isFullscreen
910 {
911     return( p_vout->b_fullscreen );
912 }
913
914 - (BOOL)canBecomeKeyWindow
915 {
916     return( YES );
917 }
918
919 - (void)keyDown:(NSEvent *)o_event
920 {
921     unichar key = 0;
922     vlc_value_t val;
923     unsigned int i_pressed_modifiers = 0;
924     val.i_int = 0;
925     
926     i_pressed_modifiers = [o_event modifierFlags];
927
928     if( i_pressed_modifiers & NSShiftKeyMask )
929         val.i_int |= KEY_MODIFIER_SHIFT;
930     if( i_pressed_modifiers & NSControlKeyMask )
931         val.i_int |= KEY_MODIFIER_CTRL;
932     if( i_pressed_modifiers & NSAlternateKeyMask )
933         val.i_int |= KEY_MODIFIER_ALT;
934     if( i_pressed_modifiers & NSCommandKeyMask )
935         val.i_int |= KEY_MODIFIER_COMMAND;
936
937     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
938
939     if( key )
940     {
941         /* Escape should always get you out of fullscreen */
942         if( key == (unichar) 0x1b )
943         {
944              if( [self isFullscreen] )
945              {
946                  [self toggleFullscreen];
947              }
948         }
949         else if ( key == ' ' )
950         {
951              playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
952                                                      FIND_ANYWHERE );
953              if ( p_playlist != NULL )
954              {
955                  playlist_Pause( p_playlist );
956                  vlc_object_release( p_playlist);
957              }
958         }
959         else
960         {
961             val.i_int |= CocoaKeyToVLC( key );
962             var_Set( p_vout->p_vlc, "key-pressed", val );
963         }
964     }
965     else
966     {
967         [super keyDown: o_event];
968     }
969 }
970
971 - (void)updateTitle
972 {
973     NSMutableString * o_title;
974     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
975                                                        FIND_ANYWHERE );
976     
977     if( p_playlist == NULL )
978     {
979         return;
980     }
981
982     vlc_mutex_lock( &p_playlist->object_lock );
983     o_title = [NSMutableString stringWithUTF8String: 
984         p_playlist->pp_items[p_playlist->i_index]->psz_uri]; 
985     vlc_mutex_unlock( &p_playlist->object_lock );
986
987     vlc_object_release( p_playlist );
988
989     if( o_title != nil )
990     {
991         NSRange prefix_range = [o_title rangeOfString: @"file:"];
992         if( prefix_range.location != NSNotFound )
993         {
994             [o_title deleteCharactersInRange: prefix_range];
995         }
996
997         [self setTitleWithRepresentedFilename: o_title];
998     }
999     else
1000     {
1001         [self setTitle:
1002             [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
1003     }
1004 }
1005
1006 /* This is actually the same as VLCControls::stop. */
1007 - (BOOL)windowShouldClose:(id)sender
1008 {
1009     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1010                                                        FIND_ANYWHERE );
1011     if( p_playlist == NULL )      
1012     {
1013         return NO;
1014     }
1015
1016     playlist_Stop( p_playlist );
1017     vlc_object_release( p_playlist );
1018
1019     /* The window will be closed by the intf later. */
1020     return NO;
1021 }
1022
1023 @end
1024
1025 /*****************************************************************************
1026  * VLCQTView implementation
1027  *****************************************************************************/
1028 @implementation VLCQTView
1029
1030 - (void)drawRect:(NSRect)rect
1031 {
1032     vout_thread_t * p_vout;
1033     id o_window = [self window];
1034     p_vout = (vout_thread_t *)[o_window getVout];
1035     
1036     [[NSColor blackColor] set];
1037     NSRectFill( rect );
1038     [super drawRect: rect];
1039
1040     p_vout->i_changes |= VOUT_SIZE_CHANGE;
1041 }
1042
1043 - (BOOL)acceptsFirstResponder
1044 {
1045     return( YES );
1046 }
1047
1048 - (BOOL)becomeFirstResponder
1049 {
1050     vout_thread_t * p_vout;
1051     id o_window = [self window];
1052     p_vout = (vout_thread_t *)[o_window getVout];
1053     
1054     [o_window setAcceptsMouseMovedEvents: YES];
1055     return( YES );
1056 }
1057
1058 - (BOOL)resignFirstResponder
1059 {
1060     vout_thread_t * p_vout;
1061     id o_window = [self window];
1062     p_vout = (vout_thread_t *)[o_window getVout];
1063     
1064     [o_window setAcceptsMouseMovedEvents: NO];
1065     VLCHideMouse( p_vout, NO );
1066     return( YES );
1067 }
1068
1069 - (void)mouseDown:(NSEvent *)o_event
1070 {
1071     vout_thread_t * p_vout;
1072     id o_window = [self window];
1073     p_vout = (vout_thread_t *)[o_window getVout];
1074     vlc_value_t val;
1075
1076     switch( [o_event type] )
1077     {        
1078         case NSLeftMouseDown:
1079         {
1080             var_Get( p_vout, "mouse-button-down", &val );
1081             val.i_int |= 1;
1082             var_Set( p_vout, "mouse-button-down", val );
1083         }
1084         break;
1085         
1086         default:
1087             [super mouseDown: o_event];
1088         break;
1089     }
1090 }
1091
1092 - (void)otherMouseDown:(NSEvent *)o_event
1093 {
1094     /* This is not the the wheel button. you need to poll the
1095      * mouseWheel event for that. other is a third, forth or fifth button */
1096     vout_thread_t * p_vout;
1097     id o_window = [self window];
1098     p_vout = (vout_thread_t *)[o_window getVout];
1099     vlc_value_t val;
1100
1101     switch( [o_event type] )
1102     {
1103         case NSOtherMouseDown:
1104         {
1105             var_Get( p_vout, "mouse-button-down", &val );
1106             val.i_int |= 2;
1107             var_Set( p_vout, "mouse-button-down", val );
1108         }
1109         break;
1110         
1111         default:
1112             [super mouseDown: o_event];
1113         break;
1114     }
1115 }
1116
1117 - (void)rightMouseDown:(NSEvent *)o_event
1118 {
1119     vout_thread_t * p_vout;
1120     id o_window = [self window];
1121     p_vout = (vout_thread_t *)[o_window getVout];
1122     vlc_value_t val;
1123
1124     switch( [o_event type] )
1125     {
1126         case NSRightMouseDown:
1127         {
1128             var_Get( p_vout, "mouse-button-down", &val );
1129             val.i_int |= 4;
1130             var_Set( p_vout, "mouse-button-down", val );
1131         }
1132         break;
1133         
1134         default:
1135             [super mouseDown: o_event];
1136         break;
1137     }
1138 }
1139
1140 - (void)mouseUp:(NSEvent *)o_event
1141 {
1142     vout_thread_t * p_vout;
1143     id o_window = [self window];
1144     p_vout = (vout_thread_t *)[o_window getVout];
1145     vlc_value_t val;
1146
1147     switch( [o_event type] )
1148     {
1149         case NSLeftMouseUp:
1150         {
1151             vlc_value_t b_val;
1152             b_val.b_bool = VLC_TRUE;
1153             var_Set( p_vout, "mouse-clicked", b_val );
1154             
1155             var_Get( p_vout, "mouse-button-down", &val );
1156             val.i_int &= ~1;
1157             var_Set( p_vout, "mouse-button-down", val );
1158         }
1159         break;
1160                 
1161         default:
1162             [super mouseUp: o_event];
1163         break;
1164     }
1165 }
1166
1167 - (void)otherMouseUp:(NSEvent *)o_event
1168 {
1169     vout_thread_t * p_vout;
1170     id o_window = [self window];
1171     p_vout = (vout_thread_t *)[o_window getVout];
1172     vlc_value_t val;
1173
1174     switch( [o_event type] )
1175     {
1176         case NSOtherMouseUp:
1177         {
1178             var_Get( p_vout, "mouse-button-down", &val );
1179             val.i_int &= ~2;
1180             var_Set( p_vout, "mouse-button-down", val );
1181         }
1182         break;
1183                 
1184         default:
1185             [super mouseUp: o_event];
1186         break;
1187     }
1188 }
1189
1190 - (void)rightMouseUp:(NSEvent *)o_event
1191 {
1192     vout_thread_t * p_vout;
1193     id o_window = [self window];
1194     p_vout = (vout_thread_t *)[o_window getVout];
1195     vlc_value_t val;
1196
1197     switch( [o_event type] )
1198     {
1199         case NSRightMouseUp:
1200         {
1201             var_Get( p_vout, "mouse-button-down", &val );
1202             val.i_int &= ~4;
1203             var_Set( p_vout, "mouse-button-down", val );
1204         }
1205         break;
1206         
1207         default:
1208             [super mouseUp: o_event];
1209         break;
1210     }
1211 }
1212
1213 - (void)mouseDragged:(NSEvent *)o_event
1214 {
1215     [self mouseMoved:o_event];
1216 }
1217
1218 - (void)otherMouseDragged:(NSEvent *)o_event
1219 {
1220     [self mouseMoved:o_event];
1221 }
1222
1223 - (void)rightMouseDragged:(NSEvent *)o_event
1224 {
1225     [self mouseMoved:o_event];
1226 }
1227
1228 - (void)mouseMoved:(NSEvent *)o_event
1229 {
1230     NSPoint ml;
1231     NSRect s_rect;
1232     BOOL b_inside;
1233
1234     vout_thread_t * p_vout;
1235     id o_window = [self window];
1236     p_vout = (vout_thread_t *)[o_window getVout];
1237
1238     s_rect = [self bounds];
1239     ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1240     b_inside = [self mouse: ml inRect: s_rect];
1241
1242     if( b_inside )
1243     {
1244         vlc_value_t val;
1245         int i_width, i_height, i_x, i_y;
1246         
1247         vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1248                                    (unsigned int)s_rect.size.height,
1249                                    &i_x, &i_y, &i_width, &i_height );
1250
1251         val.i_int = ( ((int)ml.x) - i_x ) * 
1252                     p_vout->render.i_width / i_width;
1253         var_Set( p_vout, "mouse-x", val );
1254
1255         val.i_int = ( ((int)ml.y) - i_y ) * 
1256                     p_vout->render.i_height / i_height;
1257         var_Set( p_vout, "mouse-y", val );
1258             
1259         val.b_bool = VLC_TRUE;
1260         var_Set( p_vout, "mouse-moved", val );
1261         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1262         p_vout->p_sys->b_mouse_moved = YES;
1263     }
1264
1265     [super mouseMoved: o_event];
1266 }
1267
1268 @end
1269
1270 /*****************************************************************************
1271  * VLCGLView implementation
1272  *****************************************************************************/
1273 @implementation VLCGLView
1274
1275
1276 - (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
1277 {
1278     p_vout = _p_vout;
1279     
1280     NSOpenGLPixelFormatAttribute attribs[] =
1281     {
1282         NSOpenGLPFAAccelerated,
1283         NSOpenGLPFANoRecovery,
1284         NSOpenGLPFADoubleBuffer,
1285         NSOpenGLPFAWindow,
1286         0
1287     };
1288
1289     NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1290         initWithAttributes: attribs];
1291
1292     if( !fmt )
1293     {
1294         fprintf( stderr, "Cannot create NSOpenGLPixelFormat\n" );
1295         return nil;
1296     }
1297
1298     self = [super initWithFrame:frame pixelFormat: fmt];
1299
1300     [[self openGLContext] makeCurrentContext];
1301     [[self openGLContext] update];
1302
1303     glClearColor( 0.0, 0.0, 0.0, 0.0 );
1304
1305     b_init_done = 0;
1306
1307     return self;
1308 }
1309
1310 - (void) reshape
1311 {
1312     [[self openGLContext] makeCurrentContext];
1313
1314     NSRect bounds = [self bounds];
1315
1316     glViewport( 0, 0, (GLint) bounds.size.width,
1317                 (GLint) bounds.size.height );
1318
1319     /* Quad size is set in order to preserve the aspect ratio */
1320     if( bounds.size.height * p_vout->output.i_aspect <
1321         bounds.size.width * VOUT_ASPECT_FACTOR )
1322     {
1323         f_x = bounds.size.height * p_vout->render.i_aspect /
1324             VOUT_ASPECT_FACTOR / bounds.size.width;
1325         f_y = 1.0;
1326     }
1327     else
1328     {
1329         f_x = 1.0;
1330         f_y = bounds.size.width * VOUT_ASPECT_FACTOR /
1331             p_vout->render.i_aspect / bounds.size.height;
1332     }
1333 }
1334
1335 - (void) initTextures
1336 {
1337     [[self openGLContext] makeCurrentContext];
1338
1339     /* Create textures */
1340     glGenTextures( 1, &i_texture );
1341
1342     glEnable( GL_TEXTURE_RECTANGLE_EXT );
1343     glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
1344
1345     glBindTexture( GL_TEXTURE_RECTANGLE_EXT, i_texture );
1346
1347     /* Use VRAM texturing */
1348     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1349             GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1350
1351     /* Tell the driver not to make a copy of the texture but to use
1352        our buffer */
1353     glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
1354
1355     /* Linear interpolation */
1356     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1357             GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1358     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1359             GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1360
1361     /* I have no idea what this exactly does, but it seems to be
1362        necessary for scaling */
1363     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1364             GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
1365     glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1366             GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1367     glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
1368
1369     glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
1370             p_vout->output.i_width, p_vout->output.i_height, 0,
1371             GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1372             PP_OUTPUTPICTURE[0]->p_data );
1373
1374     b_init_done = 1;
1375 }
1376
1377 - (void) reloadTexture
1378 {
1379     [[self openGLContext] makeCurrentContext];
1380
1381     glBindTexture( GL_TEXTURE_RECTANGLE_EXT, i_texture );
1382
1383     /* glTexSubImage2D is faster than glTexImage2D
1384        http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
1385        TextureRange/MainOpenGLView.m.htm */
1386     glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
1387             p_vout->output.i_width, p_vout->output.i_height,
1388             GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1389             PP_OUTPUTPICTURE[0]->p_data );
1390 }
1391
1392 - (void) drawRect: (NSRect) rect
1393 {
1394     [[self openGLContext] makeCurrentContext];
1395
1396     /* Swap buffers only during the vertical retrace of the monitor.
1397        http://developer.apple.com/documentation/GraphicsImaging/
1398        Conceptual/OpenGL/chap5/chapter_5_section_44.html */
1399     long params[] = { 1 };
1400     CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
1401                      params );
1402     
1403     /* Black background */
1404     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1405
1406     if( !b_init_done )
1407     {
1408         [[self openGLContext] flushBuffer];
1409         return;
1410     }
1411
1412     /* Draw a quad with our texture on it */
1413     glBindTexture( GL_TEXTURE_RECTANGLE_EXT, i_texture );
1414     glBegin( GL_QUADS );
1415         /* Top left */
1416         glTexCoord2f( 0.0, 0.0 );
1417         glVertex2f( - f_x, f_y );
1418         /* Bottom left */
1419         glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1420         glVertex2f( - f_x, - f_y );
1421         /* Bottom right */
1422         glTexCoord2f( (float) p_vout->output.i_width,
1423                       (float) p_vout->output.i_height );
1424         glVertex2f( f_x, - f_y );
1425         /* Top right */
1426         glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1427         glVertex2f( f_x, f_y );
1428     glEnd();
1429
1430     /* Wait for the job to be done */
1431     [[self openGLContext] flushBuffer];
1432 }
1433
1434 @end
1435
1436 /*****************************************************************************
1437  * VLCVout implementation
1438  *****************************************************************************/
1439 @implementation VLCVout
1440
1441 - (void)createWindow:(NSValue *)o_value
1442 {
1443     vlc_value_t val;
1444     VLCQTView * o_view;
1445     NSScreen * o_screen;
1446     vout_thread_t * p_vout;
1447     vlc_bool_t b_main_screen;
1448     
1449     p_vout = (vout_thread_t *)[o_value pointerValue];
1450
1451     p_vout->p_sys->o_window = [VLCWindow alloc];
1452     [p_vout->p_sys->o_window setVout: p_vout];
1453     [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
1454
1455     if( var_Get( p_vout, "video-device", &val ) < 0 )
1456     {
1457         o_screen = [NSScreen mainScreen];
1458         b_main_screen = 1;
1459     }
1460     else
1461     {
1462         NSArray *o_screens = [NSScreen screens];
1463         unsigned int i_index = val.i_int;
1464         
1465         if( [o_screens count] < i_index )
1466         {
1467             o_screen = [NSScreen mainScreen];
1468             b_main_screen = 1;
1469         }
1470         else
1471         {
1472             i_index--;
1473             o_screen = [o_screens objectAtIndex: i_index];
1474             config_PutInt( p_vout, "macosx-vdev", i_index );
1475             b_main_screen = (i_index == 0);
1476         }
1477     } 
1478
1479     if( p_vout->p_sys->i_opengl )
1480     {
1481         /* XXX Fix fullscreen mode */
1482         p_vout->b_fullscreen = 0;
1483     }
1484
1485     if( p_vout->b_fullscreen )
1486     {
1487         NSRect screen_rect = [o_screen frame];
1488         screen_rect.origin.x = screen_rect.origin.y = 0;
1489
1490         if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
1491             BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
1492                              NULL, NULL, fullScreenAllowEvents );
1493
1494         [p_vout->p_sys->o_window 
1495             initWithContentRect: screen_rect
1496             styleMask: NSBorderlessWindowMask
1497             backing: NSBackingStoreBuffered
1498             defer: NO screen: o_screen];
1499
1500         //[p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1];
1501         p_vout->p_sys->b_mouse_moved = YES;
1502         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1503     }
1504     else
1505     {
1506         unsigned int i_stylemask = NSTitledWindowMask |
1507                                    NSMiniaturizableWindowMask |
1508                                    NSClosableWindowMask |
1509                                    NSResizableWindowMask;
1510         
1511         if( !p_vout->p_sys->i_opengl )
1512         {
1513             if ( p_vout->p_sys->p_fullscreen_state != NULL )
1514                 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
1515             p_vout->p_sys->p_fullscreen_state = NULL;
1516         }
1517
1518         [p_vout->p_sys->o_window 
1519             initWithContentRect: p_vout->p_sys->s_rect
1520             styleMask: i_stylemask
1521             backing: NSBackingStoreBuffered
1522             defer: NO screen: o_screen];
1523
1524         [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )];
1525         
1526         if( config_GetInt( p_vout, "video-on-top" ) )
1527         {
1528             [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1529         }
1530         
1531         if( !p_vout->p_sys->b_pos_saved )   
1532         {
1533             [p_vout->p_sys->o_window center];
1534         }
1535     }
1536
1537     if( !p_vout->p_sys->i_opengl )
1538     {
1539         o_view = [[VLCQTView alloc] init];
1540         /* FIXME: [o_view setMenu:] */
1541         [p_vout->p_sys->o_window setContentView: o_view];
1542         [o_view autorelease];
1543
1544         [o_view lockFocus];
1545         p_vout->p_sys->p_qdport = [o_view qdPort];
1546         [o_view unlockFocus];
1547     }
1548     else
1549     {
1550 #define o_glview p_vout->p_sys->o_glview
1551         o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect vout: p_vout];
1552         [p_vout->p_sys->o_window setContentView: o_glview];
1553         [o_glview autorelease];
1554 #undef o_glview
1555     }
1556     
1557     [p_vout->p_sys->o_window updateTitle];
1558     [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1559
1560 }
1561
1562 - (void)destroyWindow:(NSValue *)o_value
1563 {
1564     vout_thread_t * p_vout;
1565
1566     p_vout = (vout_thread_t *)[o_value pointerValue];
1567
1568     if( !p_vout->b_fullscreen )
1569     {
1570         NSRect s_rect;
1571
1572         s_rect = [[p_vout->p_sys->o_window contentView] frame];
1573         p_vout->p_sys->s_rect.size = s_rect.size;
1574
1575         s_rect = [p_vout->p_sys->o_window frame];
1576         p_vout->p_sys->s_rect.origin = s_rect.origin;
1577
1578         p_vout->p_sys->b_pos_saved = YES;
1579     }
1580     
1581     p_vout->p_sys->p_qdport = nil;
1582     [p_vout->p_sys->o_window close];
1583     p_vout->p_sys->o_window = nil;
1584 }
1585
1586 @end