* ./modules/gui/macosx/vout.m: the mousepointer is hidden in fullscreen view
[vlc.git] / modules / gui / macosx / vout.m
1 /*****************************************************************************
2  * vout.m: MacOS X video output plugin
3  *****************************************************************************
4  * Copyright (C) 2001-2003 VideoLAN
5  * $Id: vout.m,v 1.18 2003/01/24 06:21:03 hartman 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  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdlib.h>                                                /* free() */
32 #include <string.h>                                            /* strerror() */
33
34 #include <vlc/vlc.h>
35 #include <vlc/vout.h>
36 #include <vlc/aout.h>
37 #include <vlc/intf.h>
38
39 #include <Cocoa/Cocoa.h>
40 #include <QuickTime/QuickTime.h>
41
42 #include "intf.h"
43 #include "vout.h"
44
45 #define QT_MAX_DIRECTBUFFERS 10
46
47 struct picture_sys_t
48 {
49     void *p_info;
50     unsigned int i_size;
51
52     /* When using I420 output */
53     PlanarPixmapInfoYUV420 pixmap_i420;
54 };
55
56 /*****************************************************************************
57  * Local prototypes
58  *****************************************************************************/
59 static int  vout_Init      ( vout_thread_t * );
60 static void vout_End       ( vout_thread_t * );
61 static int  vout_Manage    ( vout_thread_t * );
62 static void vout_Display   ( vout_thread_t *, picture_t * );
63
64 static int  CoSendRequest      ( vout_thread_t *, SEL );
65 static int  CoCreateWindow     ( vout_thread_t * );
66 static int  CoDestroyWindow    ( vout_thread_t * );
67 static int  CoToggleFullscreen ( vout_thread_t * );
68
69 static void QTScaleMatrix      ( vout_thread_t * );
70 static int  QTCreateSequence   ( vout_thread_t * );
71 static void QTDestroySequence  ( vout_thread_t * );
72 static int  QTNewPicture       ( vout_thread_t *, picture_t * );
73 static void QTFreePicture      ( vout_thread_t *, picture_t * );
74
75 /*****************************************************************************
76  * OpenVideo: allocates MacOS X video thread output method
77  *****************************************************************************
78  * This function allocates and initializes a MacOS X vout method.
79  *****************************************************************************/
80 int E_(OpenVideo) ( vlc_object_t *p_this )
81 {   
82     vout_thread_t * p_vout = (vout_thread_t *)p_this;
83     OSErr err;
84     int i_timeout;
85
86     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
87     if( p_vout->p_sys == NULL )
88     {
89         msg_Err( p_vout, "out of memory" );
90         return( 1 );
91     }
92
93     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
94
95     /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
96     for( i_timeout = 20 ; i_timeout-- ; )
97     {
98         if( NSApp == NULL )
99         {
100             msleep( INTF_IDLE_SLEEP );
101         }
102     }
103
104     if( NSApp == NULL )
105     {
106         msg_Err( p_vout, "no MacOS X interface present" );
107         free( p_vout->p_sys );
108         return( 1 );
109     }
110
111     if( [NSApp respondsToSelector: @selector(getIntf)] )
112     {
113         intf_thread_t * p_intf;
114
115         for( i_timeout = 10 ; i_timeout-- ; )
116         {
117             if( ( p_intf = [NSApp getIntf] ) == NULL )
118             {
119                 msleep( INTF_IDLE_SLEEP );
120             }
121         }
122
123         if( p_intf == NULL )
124         {
125             msg_Err( p_vout, "MacOS X intf has getIntf, but is NULL" );
126             free( p_vout->p_sys );
127             return( 1 );
128         }
129     }
130
131     p_vout->p_sys->h_img_descr = 
132         (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
133     p_vout->p_sys->p_matrix = (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
134     p_vout->p_sys->p_fullscreen_state = NULL;
135
136     p_vout->p_sys->b_mouse_pointer_visible = 1;
137
138     /* set window size */
139     p_vout->p_sys->s_rect.size.width = p_vout->i_window_width;
140     p_vout->p_sys->s_rect.size.height = p_vout->i_window_height;
141
142     if( ( err = EnterMovies() ) != noErr )
143     {
144         msg_Err( p_vout, "EnterMovies failed: %d", err );
145         free( p_vout->p_sys->p_matrix );
146         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
147         free( p_vout->p_sys );
148         return( 1 );
149     } 
150
151     if( vout_ChromaCmp( p_vout->render.i_chroma, VLC_FOURCC('I','4','2','0') ) )
152     {
153         err = FindCodec( kYUV420CodecType, bestSpeedCodec,
154                          nil, &p_vout->p_sys->img_dc );
155         if( err == noErr && p_vout->p_sys->img_dc != 0 )
156         {
157             p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
158             p_vout->p_sys->i_codec = kYUV420CodecType;
159         }
160         else
161         {
162             msg_Err( p_vout, "failed to find an appropriate codec" );
163         }
164     }
165     else
166     {
167         msg_Err( p_vout, "chroma 0x%08x not supported",
168                          p_vout->render.i_chroma );
169     }
170
171     if( p_vout->p_sys->img_dc == 0 )
172     {
173         free( p_vout->p_sys->p_matrix );
174         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
175         free( p_vout->p_sys );
176         return( 1 );        
177     }
178
179     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
180     NSArray * o_screens = [NSScreen screens];
181     if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
182     {
183         int i = 1;
184         vlc_value_t val;
185         NSScreen * o_screen;
186
187         int i_option = config_GetInt( p_vout, "macosx-vdev" );
188
189         var_Create( p_vout, "video-device", VLC_VAR_STRING |
190                                             VLC_VAR_HASCHOICE ); 
191
192         NSEnumerator * o_enumerator = [o_screens objectEnumerator];
193
194         while( (o_screen = [o_enumerator nextObject]) != NULL )
195         {
196             char psz_temp[255];
197             NSRect s_rect = [o_screen frame];
198
199             snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1, 
200                       "%s %d (%dx%d)", _("Screen"), i,
201                       (int)s_rect.size.width, (int)s_rect.size.height ); 
202
203             val.psz_string = psz_temp;
204             var_Change( p_vout, "video-device", VLC_VAR_ADDCHOICE, &val );
205
206             if( ( i - 1 ) == i_option )
207             {
208                 var_Set( p_vout, "video-device", val );
209             }
210
211             i++;
212         }
213
214         var_AddCallback( p_vout, "video-device", vout_VarCallback,
215                          NULL );
216
217         val.b_bool = VLC_TRUE;
218         var_Set( p_vout, "intf-change", val );
219     }
220     [o_pool release];
221
222     if( CoCreateWindow( p_vout ) )
223     {
224         msg_Err( p_vout, "unable to create window" );
225         free( p_vout->p_sys->p_matrix );
226         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
227         free( p_vout->p_sys ); 
228         return( 1 );
229     }
230
231     p_vout->pf_init = vout_Init;
232     p_vout->pf_end = vout_End;
233     p_vout->pf_manage = vout_Manage;
234     p_vout->pf_render = NULL;
235     p_vout->pf_display = vout_Display;
236
237     return( 0 );
238 }
239
240 /*****************************************************************************
241  * vout_Init: initialize video thread output method
242  *****************************************************************************/
243 static int vout_Init( vout_thread_t *p_vout )
244 {
245     int i_index;
246     picture_t *p_pic;
247
248     I_OUTPUTPICTURES = 0;
249
250     /* Initialize the output structure; we already found a codec,
251      * and the corresponding chroma we will be using. Since we can
252      * arbitrary scale, stick to the coordinates and aspect. */
253     p_vout->output.i_width  = p_vout->render.i_width;
254     p_vout->output.i_height = p_vout->render.i_height;
255     p_vout->output.i_aspect = p_vout->render.i_aspect;
256
257     SetPort( p_vout->p_sys->p_qdport );
258     QTScaleMatrix( p_vout );
259
260     if( QTCreateSequence( p_vout ) )
261     {
262         msg_Err( p_vout, "unable to create sequence" );
263         return( 1 );
264     }
265
266     /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
267     while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
268     {
269         p_pic = NULL;
270
271         /* Find an empty picture slot */
272         for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
273         {
274             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
275             {
276                 p_pic = p_vout->p_picture + i_index;
277                 break;
278             }
279         }
280
281         /* Allocate the picture */
282         if( p_pic == NULL || QTNewPicture( p_vout, p_pic ) )
283         {
284             break;
285         }
286
287         p_pic->i_status = DESTROYED_PICTURE;
288         p_pic->i_type   = DIRECT_PICTURE;
289
290         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
291
292         I_OUTPUTPICTURES++;
293     }
294
295     return( 0 );
296 }
297
298 /*****************************************************************************
299  * vout_End: terminate video thread output method
300  *****************************************************************************/
301 static void vout_End( vout_thread_t *p_vout )
302 {
303     int i_index;
304
305     QTDestroySequence( p_vout );
306
307     /* Free the direct buffers we allocated */
308     for( i_index = I_OUTPUTPICTURES; i_index; )
309     {
310         i_index--;
311         QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
312     }
313 }
314
315 /*****************************************************************************
316  * CloseVideo: destroy video thread output method
317  *****************************************************************************/
318 void E_(CloseVideo) ( vlc_object_t *p_this )
319 {       
320     vout_thread_t * p_vout = (vout_thread_t *)p_this;     
321
322     if( CoDestroyWindow( p_vout ) )
323     {
324         msg_Err( p_vout, "unable to destroy window" );
325     }
326
327     if ( p_vout->p_sys->p_fullscreen_state != NULL )
328         EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
329
330     ExitMovies();
331
332     free( p_vout->p_sys->p_matrix );
333     DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
334
335     free( p_vout->p_sys );
336 }
337
338 /*****************************************************************************
339  * vout_Manage: handle events
340  *****************************************************************************
341  * This function should be called regularly by video output thread. It manages
342  * console events. It returns a non null value on error.
343  *****************************************************************************/
344 static int vout_Manage( vout_thread_t *p_vout )
345 {    
346     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
347     {
348         if( CoToggleFullscreen( p_vout ) )  
349         {
350             return( 1 );
351         }
352
353         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
354     }
355
356     if( p_vout->i_changes & VOUT_SIZE_CHANGE ) 
357     {
358         QTScaleMatrix( p_vout );
359         SetDSequenceMatrix( p_vout->p_sys->i_seq, 
360                             p_vout->p_sys->p_matrix );
361  
362         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
363     }
364
365     /* hide/show mouse cursor */
366     if( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
367     {
368         vlc_bool_t b_change = 0;
369
370         if( !p_vout->p_sys->b_mouse_pointer_visible )
371         {
372             CGDisplayShowCursor( kCGDirectMainDisplay );
373             p_vout->p_sys->b_mouse_pointer_visible = 1;
374             b_change = 1;
375         }
376         else if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 && 
377                    p_vout->p_sys->b_mouse_pointer_visible )
378         {
379             CGDisplayHideCursor( kCGDirectMainDisplay );
380             p_vout->p_sys->b_mouse_pointer_visible = 0;
381             b_change = 1;
382         }
383
384         if( b_change )
385         {
386             p_vout->p_sys->b_mouse_moved = 0;
387             p_vout->p_sys->i_time_mouse_last_moved = 0;
388         }
389     }
390
391     return( 0 );
392 }
393
394 /*****************************************************************************
395  * vout_Display: displays previously rendered output
396  *****************************************************************************
397  * This function sends the currently rendered image to the display.
398  *****************************************************************************/
399 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
400 {
401     OSErr err;
402     CodecFlags flags;
403
404     if( ( err = DecompressSequenceFrameS( 
405                     p_vout->p_sys->i_seq,
406                     p_pic->p_sys->p_info,
407                     p_pic->p_sys->i_size,                    
408                     codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
409     {
410         msg_Err( p_vout, "DecompressSequenceFrameS failed: %d", err );
411     }
412     else
413     {
414         QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
415     }
416 }
417
418 /*****************************************************************************
419  * CoSendRequest: send request to interface thread
420  *****************************************************************************
421  * Returns 0 on success, 1 otherwise
422  *****************************************************************************/
423 static int CoSendRequest( vout_thread_t *p_vout, SEL sel )
424 {
425     int i_ret = 0;
426
427     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
428     VLCVout * o_vlv = [[[VLCVout alloc] init] autorelease];
429
430     if( [o_vlv respondsToSelector: @selector(performSelectorOnMainThread:
431                                              withObject:waitUntilDone:)] )
432     {
433         [o_vlv performSelectorOnMainThread: sel
434             withObject: [NSValue valueWithPointer: p_vout]
435             waitUntilDone: YES];
436     }
437     else if( [NSApp respondsToSelector: @selector(getIntf)] )
438     {
439         NSArray * o_array;
440         NSValue * o_value;
441         NSPort * o_recv_port;
442         NSInvocation * o_inv;
443         NSPortMessage * o_msg;
444         intf_thread_t * p_intf;
445         NSMethodSignature * o_sig;
446
447         p_intf = (intf_thread_t *)[NSApp getIntf];
448
449         o_recv_port = [[NSPort port] retain];
450         o_value = [NSValue valueWithPointer: p_vout];
451
452         o_sig = [VLCVout instanceMethodSignatureForSelector: sel];
453         o_inv = [NSInvocation invocationWithMethodSignature: o_sig];
454         [o_inv setArgument: &o_value atIndex: 2];
455         [o_inv setTarget: o_vlv];
456         [o_inv setSelector: sel];
457
458         o_array = [NSArray arrayWithObject:
459             [NSData dataWithBytes: &o_inv length: sizeof(o_inv)]];
460         o_msg = [[NSPortMessage alloc]
461             initWithSendPort: p_intf->p_sys->o_sendport
462             receivePort: o_recv_port components: o_array];
463
464         p_vout->p_sys->o_lock =
465             [[NSConditionLock alloc] initWithCondition: 0];
466         [o_msg sendBeforeDate: [NSDate distantPast]];
467         [p_vout->p_sys->o_lock lockWhenCondition: 1];
468         [p_vout->p_sys->o_lock unlock];
469         [p_vout->p_sys->o_lock release];
470         p_vout->p_sys->o_lock = nil;
471
472         [o_msg release];
473         [o_recv_port release];
474     }
475     else
476     {
477         msg_Err( p_vout, "SendRequest: no way to communicate with mt" );
478         i_ret = 1;
479     }
480
481     [o_pool release];
482
483     return( i_ret );
484 }
485
486 /*****************************************************************************
487  * CoCreateWindow: create new window 
488  *****************************************************************************
489  * Returns 0 on success, 1 otherwise
490  *****************************************************************************/
491 static int CoCreateWindow( vout_thread_t *p_vout )
492 {
493     if( CoSendRequest( p_vout, @selector(createWindow:) ) )
494     {
495         msg_Err( p_vout, "CoSendRequest (createWindow) failed" );
496         return( 1 );
497     }
498
499     return( 0 );
500 }
501
502 /*****************************************************************************
503  * CoDestroyWindow: destroy window 
504  *****************************************************************************
505  * Returns 0 on success, 1 otherwise
506  *****************************************************************************/
507 static int CoDestroyWindow( vout_thread_t *p_vout )
508 {
509     if( !p_vout->p_sys->b_mouse_pointer_visible )
510     {
511         CGDisplayShowCursor( kCGDirectMainDisplay );
512         p_vout->p_sys->b_mouse_pointer_visible = 1;
513     }
514
515     if( CoSendRequest( p_vout, @selector(destroyWindow:) ) )
516     {
517         msg_Err( p_vout, "CoSendRequest (destroyWindow) failed" );
518         return( 1 );
519     }
520
521     return( 0 );
522 }
523
524 /*****************************************************************************
525  * CoToggleFullscreen: toggle fullscreen 
526  *****************************************************************************
527  * Returns 0 on success, 1 otherwise
528  *****************************************************************************/
529 static int CoToggleFullscreen( vout_thread_t *p_vout )
530 {
531     QTDestroySequence( p_vout );
532
533     if( CoDestroyWindow( p_vout ) )
534     {
535         msg_Err( p_vout, "unable to destroy window" );
536         return( 1 );
537     }
538     
539     p_vout->b_fullscreen = !p_vout->b_fullscreen;
540
541     config_PutInt( p_vout, "fullscreen", p_vout->b_fullscreen );
542
543     if( CoCreateWindow( p_vout ) )
544     {
545         msg_Err( p_vout, "unable to create window" );
546         return( 1 );
547     }
548
549     SetPort( p_vout->p_sys->p_qdport );
550     QTScaleMatrix( p_vout );
551
552     if( QTCreateSequence( p_vout ) )
553     {
554         msg_Err( p_vout, "unable to create sequence" );
555         return( 1 ); 
556     } 
557
558     return( 0 );
559 }
560
561 /*****************************************************************************
562  * QTScaleMatrix: scale matrix 
563  *****************************************************************************/
564 static void QTScaleMatrix( vout_thread_t *p_vout )
565 {
566     Rect s_rect;
567     unsigned int i_width, i_height;
568     Fixed factor_x, factor_y;
569     unsigned int i_offset_x = 0;
570     unsigned int i_offset_y = 0;
571
572     GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
573
574     i_width = s_rect.right - s_rect.left;
575     i_height = s_rect.bottom - s_rect.top;
576
577     if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
578     {
579         int i_adj_width = i_height * p_vout->output.i_aspect /
580                           VOUT_ASPECT_FACTOR;
581
582         factor_x = FixDiv( Long2Fix( i_adj_width ),
583                            Long2Fix( p_vout->output.i_width ) );
584         factor_y = FixDiv( Long2Fix( i_height ),
585                            Long2Fix( p_vout->output.i_height ) );
586
587         i_offset_x = (i_width - i_adj_width) / 2;
588     }
589     else
590     {
591         int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
592                            p_vout->output.i_aspect;
593
594         factor_x = FixDiv( Long2Fix( i_width ),
595                            Long2Fix( p_vout->output.i_width ) );
596         factor_y = FixDiv( Long2Fix( i_adj_height ),
597                            Long2Fix( p_vout->output.i_height ) );
598
599         i_offset_y = (i_height - i_adj_height) / 2;
600     }
601     /* the following is for the resize bar. Dirty hack (hartman) */
602     if ( !p_vout->b_fullscreen)
603     {
604         i_offset_y = i_offset_y - 12;
605     }
606
607     SetIdentityMatrix( p_vout->p_sys->p_matrix );
608
609     ScaleMatrix( p_vout->p_sys->p_matrix,
610                  factor_x, factor_y,
611                  Long2Fix(0), Long2Fix(0) );            
612
613     TranslateMatrix( p_vout->p_sys->p_matrix, 
614                      Long2Fix(i_offset_x), 
615                      Long2Fix(i_offset_y) );
616 }
617
618 /*****************************************************************************
619  * QTCreateSequence: create a new sequence 
620  *****************************************************************************
621  * Returns 0 on success, 1 otherwise
622  *****************************************************************************/
623 static int QTCreateSequence( vout_thread_t *p_vout )
624 {
625     OSErr err;
626     ImageDescriptionPtr p_descr;
627
628     HLock( (Handle)p_vout->p_sys->h_img_descr );
629     p_descr = *p_vout->p_sys->h_img_descr;
630
631     p_descr->idSize = sizeof(ImageDescription);
632     p_descr->cType = p_vout->p_sys->i_codec;
633     p_descr->version = 1;
634     p_descr->revisionLevel = 0;
635     p_descr->vendor = 'appl';
636     p_descr->width = p_vout->output.i_width;
637     p_descr->height = p_vout->output.i_height;
638     p_descr->hRes = Long2Fix(72);
639     p_descr->vRes = Long2Fix(72);
640     p_descr->spatialQuality = codecLosslessQuality;
641     p_descr->frameCount = 1;
642     p_descr->clutID = -1;
643     p_descr->dataSize = 0;
644     p_descr->depth = 24;
645
646     HUnlock( (Handle)p_vout->p_sys->h_img_descr );
647
648     if( ( err = DecompressSequenceBeginS( 
649                               &p_vout->p_sys->i_seq,
650                               p_vout->p_sys->h_img_descr,
651                               NULL, 0,
652                               p_vout->p_sys->p_qdport,
653                               NULL, NULL,
654                               p_vout->p_sys->p_matrix,
655                               0, NULL,
656                               codecFlagUseImageBuffer,
657                               codecLosslessQuality,
658                               p_vout->p_sys->img_dc ) ) )
659     {
660         msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
661         return( 1 );
662     }
663
664     return( 0 );
665 }
666
667 /*****************************************************************************
668  * QTDestroySequence: destroy sequence 
669  *****************************************************************************/
670 static void QTDestroySequence( vout_thread_t *p_vout )
671 {
672     CDSequenceEnd( p_vout->p_sys->i_seq );
673 }
674
675 /*****************************************************************************
676  * QTNewPicture: allocate a picture
677  *****************************************************************************
678  * Returns 0 on success, 1 otherwise
679  *****************************************************************************/
680 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
681 {
682     int i_width  = p_vout->output.i_width;
683     int i_height = p_vout->output.i_height;
684
685     /* We know the chroma, allocate a buffer which will be used
686      * directly by the decoder */
687     p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
688
689     if( p_pic->p_sys == NULL )
690     {
691         return( -1 );
692     }
693
694     switch( p_vout->output.i_chroma )
695     {
696         case VLC_FOURCC('I','4','2','0'):
697
698             p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
699             p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
700
701             /* Allocate the memory buffer */
702             p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
703                                           16, i_width * i_height * 3 / 2 );
704
705             /* Y buffer */
706             p_pic->Y_PIXELS = p_pic->p_data; 
707             p_pic->p[Y_PLANE].i_lines = i_height;
708             p_pic->p[Y_PLANE].i_pitch = i_width;
709             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
710             p_pic->p[Y_PLANE].i_visible_pitch = i_width;
711
712             /* U buffer */
713             p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
714             p_pic->p[U_PLANE].i_lines = i_height / 2;
715             p_pic->p[U_PLANE].i_pitch = i_width / 2;
716             p_pic->p[U_PLANE].i_pixel_pitch = 1;
717             p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
718
719             /* V buffer */
720             p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
721             p_pic->p[V_PLANE].i_lines = i_height / 2;
722             p_pic->p[V_PLANE].i_pitch = i_width / 2;
723             p_pic->p[V_PLANE].i_pixel_pitch = 1;
724             p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
725
726             /* We allocated 3 planes */
727             p_pic->i_planes = 3;
728
729 #define P p_pic->p_sys->pixmap_i420
730             P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
731                                        - p_pic->p_sys->p_info;
732             P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
733                                         - p_pic->p_sys->p_info;
734             P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
735                                         - p_pic->p_sys->p_info;
736
737             P.componentInfoY.rowBytes = i_width;
738             P.componentInfoCb.rowBytes = i_width / 2;
739             P.componentInfoCr.rowBytes = i_width / 2;
740 #undef P
741
742             break;
743
744     default:
745         /* Unknown chroma, tell the guy to get lost */
746         free( p_pic->p_sys );
747         msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
748                  p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
749         p_pic->i_planes = 0;
750         return( -1 );
751     }
752
753     return( 0 );
754 }
755
756 /*****************************************************************************
757  * QTFreePicture: destroy a picture allocated with QTNewPicture
758  *****************************************************************************/
759 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
760 {
761     switch( p_vout->output.i_chroma )
762     {
763         case VLC_FOURCC('I','4','2','0'):
764             free( p_pic->p_data_orig );
765             break;
766     }
767
768     free( p_pic->p_sys );
769 }
770
771 /*****************************************************************************
772  * VLCWindow implementation
773  *****************************************************************************/
774 @implementation VLCWindow
775
776 - (void)setVout:(vout_thread_t *)_p_vout
777 {
778     p_vout = _p_vout;
779 }
780
781 - (vout_thread_t *)getVout
782 {
783     return( p_vout );
784 }
785
786 - (void)toggleFullscreen
787 {
788     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
789 }
790
791 - (BOOL)isFullscreen
792 {
793     return( p_vout->b_fullscreen );
794 }
795
796 - (BOOL)canBecomeKeyWindow
797 {
798     return( YES );
799 }
800
801 - (void)keyDown:(NSEvent *)o_event
802 {
803     unichar key = 0;
804
805     if( [[o_event characters] length] )
806     {
807         key = [[o_event characters] characterAtIndex: 0];
808     }
809
810     switch( key )
811     {
812         case 'f': case 'F':
813             [self toggleFullscreen];
814             break;
815
816         case (unichar)0x1b: /* escape */
817             if( [self isFullscreen] )
818             {
819                 [self toggleFullscreen];
820             }
821             break;
822
823         case 'q': case 'Q':
824             p_vout->p_vlc->b_die = VLC_TRUE;
825             break;
826
827         case ' ':
828             input_SetStatus( p_vout, INPUT_STATUS_PAUSE );
829             break;
830
831         default:
832             [super keyDown: o_event];
833             break;
834     }
835 }
836
837 /* This is actually the same as VLCControls::stop. */
838 - (BOOL)windowShouldClose:(id)sender
839 {
840     intf_thread_t * p_intf = [NSApp getIntf];
841     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
842                                                        FIND_ANYWHERE );
843     if( p_playlist == NULL )      
844     {
845         return NO;
846     }
847
848     playlist_Stop( p_playlist );
849     vlc_object_release( p_playlist );
850     p_intf->p_sys->b_stopping = 1;
851
852     /* The window will be closed by the intf later. */
853     return NO;
854 }
855
856 @end
857
858 /*****************************************************************************
859  * VLCView implementation
860  *****************************************************************************/
861 @implementation VLCView
862
863 - (void)drawRect:(NSRect)rect
864 {
865     vout_thread_t * p_vout;
866     id o_window = [self window];
867     p_vout = (vout_thread_t *)[o_window getVout];
868     
869     if ( p_vout->b_fullscreen )
870     {
871         [[NSColor blackColor] set];
872         NSRectFill( rect );
873     }
874     [super drawRect: rect];
875
876     p_vout->i_changes |= VOUT_SIZE_CHANGE;
877 }
878
879 - (BOOL)acceptsFirstResponder
880 {
881     return( YES );
882 }
883
884 - (BOOL)becomeFirstResponder
885 {
886     [[self window] setAcceptsMouseMovedEvents: YES];
887     return( YES );
888 }
889
890 - (BOOL)resignFirstResponder
891 {
892     [[self window] setAcceptsMouseMovedEvents: NO];
893     return( YES );
894 }
895
896 - (void)mouseUp:(NSEvent *)o_event
897 {
898     vout_thread_t * p_vout;
899     id o_window = [self window];
900     p_vout = (vout_thread_t *)[o_window getVout];
901
902     switch( [o_event type] )
903     {
904         case NSLeftMouseUp:
905         {
906             vlc_value_t val;
907             val.b_bool = VLC_TRUE;
908             var_Set( p_vout, "mouse-clicked", val );        
909         }
910         break;
911
912         default:
913             [super mouseUp: o_event];
914         break;
915     }
916 }
917
918 - (void)mouseMoved:(NSEvent *)o_event
919 {
920     NSPoint ml;
921     NSRect s_rect;
922     BOOL b_inside;
923
924     vout_thread_t * p_vout;
925     id o_window = [self window];
926     p_vout = (vout_thread_t *)[o_window getVout];
927
928     s_rect = [self bounds];
929     ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
930     b_inside = [self mouse: ml inRect: s_rect];
931
932     if( b_inside )
933     {
934         vlc_value_t val;
935         int i_width, i_height, i_x, i_y;
936         
937         vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
938                                    (unsigned int)s_rect.size.height,
939                                    &i_x, &i_y, &i_width, &i_height );
940
941         val.i_int = ( ((int)ml.x) - i_x ) * 
942                     p_vout->render.i_width / i_width;
943         var_Set( p_vout, "mouse-x", val );
944
945         val.i_int = ( ((int)ml.y) - i_y ) * 
946                     p_vout->render.i_height / i_height;
947         var_Set( p_vout, "mouse-y", val );
948             
949         val.b_bool = VLC_TRUE;
950         var_Set( p_vout, "mouse-moved", val );
951         
952         p_vout->p_sys->i_time_mouse_last_moved = mdate();
953         p_vout->p_sys->b_mouse_moved = 1;
954     }
955     else
956     {
957         [super mouseMoved: o_event];
958     }
959 }
960
961 @end
962
963 /*****************************************************************************
964  * VLCVout implementation
965  *****************************************************************************/
966 @implementation VLCVout
967
968 - (void)createWindow:(NSValue *)o_value
969 {
970     vlc_value_t val;
971     VLCView * o_view;
972     NSScreen * o_screen;
973     vout_thread_t * p_vout;
974     id o_title;
975     vlc_bool_t b_main_screen;
976
977     intf_thread_t * p_intf = [NSApp getIntf];
978     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
979                                                FIND_ANYWHERE );
980     
981     p_vout = (vout_thread_t *)[o_value pointerValue];
982
983     p_vout->p_sys->o_window = [VLCWindow alloc];
984     [p_vout->p_sys->o_window setVout: p_vout];
985     [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
986
987     if( var_Get( p_vout, "video-device", &val ) < 0 )
988     {
989         o_screen = [NSScreen mainScreen];
990         b_main_screen = 1;
991     }
992     else
993     {
994         unsigned int i_index = 0;
995         NSArray *o_screens = [NSScreen screens];
996
997         if( !sscanf( val.psz_string, _("Screen %d"), &i_index ) ||
998             [o_screens count] < i_index )
999         {
1000             o_screen = [NSScreen mainScreen];
1001             b_main_screen = 1;
1002         }
1003         else
1004         {
1005             i_index--;
1006             o_screen = [o_screens objectAtIndex: i_index];
1007             config_PutInt( p_vout, "macosx-vdev", i_index );
1008             b_main_screen = (i_index == 0);
1009         } 
1010
1011         free( val.psz_string );
1012     } 
1013
1014     if( p_vout->b_fullscreen )
1015     {
1016         NSRect screen_rect = [o_screen frame];
1017         screen_rect.origin.x = screen_rect.origin.y = 0;
1018
1019         if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
1020             BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
1021                              NULL, NULL, fullScreenAllowEvents );
1022
1023         [p_vout->p_sys->o_window 
1024             initWithContentRect: screen_rect
1025             styleMask: NSBorderlessWindowMask
1026             backing: NSBackingStoreBuffered
1027             defer: NO screen: o_screen];
1028
1029         [p_vout->p_sys->o_window setLevel: NSModalPanelWindowLevel];
1030         p_vout->p_sys->b_mouse_moved = 1;
1031         p_vout->p_sys->i_time_mouse_last_moved = mdate();
1032     }
1033     else
1034     {
1035         unsigned int i_stylemask = NSTitledWindowMask |
1036                                    NSMiniaturizableWindowMask |
1037                                    NSClosableWindowMask |
1038                                    NSResizableWindowMask |
1039                                    NSTexturedBackgroundWindowMask;
1040         
1041         /* the following is for the resize bar. Dirty hack (hartman) */
1042         p_vout->p_sys->s_rect.size.height = p_vout->p_sys->s_rect.size.height + 24;
1043
1044         if ( p_vout->p_sys->p_fullscreen_state != NULL )
1045             EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
1046         p_vout->p_sys->p_fullscreen_state = NULL;
1047
1048         [p_vout->p_sys->o_window 
1049             initWithContentRect: p_vout->p_sys->s_rect
1050             styleMask: i_stylemask
1051             backing: NSBackingStoreBuffered
1052             defer: NO screen: o_screen];
1053
1054         if( !p_vout->p_sys->b_pos_saved )   
1055         {
1056             [p_vout->p_sys->o_window center];
1057         }
1058     }
1059
1060     o_view = [[VLCView alloc] init];
1061     /* FIXME: [o_view setMenu:] */
1062     [p_vout->p_sys->o_window setContentView: o_view];
1063     [o_view autorelease];
1064
1065     [o_view lockFocus];
1066     p_vout->p_sys->p_qdport = [o_view qdPort];
1067     [o_view unlockFocus];
1068
1069
1070     if( p_playlist == NULL )
1071     {
1072         return;
1073     }
1074
1075     vlc_mutex_lock( &p_playlist->object_lock );
1076     o_title = [NSString stringWithUTF8String: 
1077         p_playlist->pp_items[p_playlist->i_index]->psz_name]; 
1078     vlc_mutex_unlock( &p_playlist->object_lock ); 
1079
1080     vlc_object_release( p_playlist );
1081
1082     if (o_title)
1083     {
1084         [p_vout->p_sys->o_window setTitle: o_title];
1085         [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1086     }
1087     else
1088     {
1089         [p_vout->p_sys->o_window setTitle:
1090             [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
1091         [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1092     }
1093 }
1094
1095 - (void)destroyWindow:(NSValue *)o_value
1096 {
1097     vout_thread_t * p_vout;
1098
1099     p_vout = (vout_thread_t *)[o_value pointerValue];
1100
1101     if( !p_vout->b_fullscreen )
1102     {
1103         NSRect s_rect;
1104
1105         s_rect = [[p_vout->p_sys->o_window contentView] frame];
1106         p_vout->p_sys->s_rect.size = s_rect.size;
1107
1108         s_rect = [p_vout->p_sys->o_window frame];
1109         p_vout->p_sys->s_rect.origin = s_rect.origin;
1110
1111         p_vout->p_sys->b_pos_saved = 1;
1112     }
1113     else /* the following is for the resize bar. Dirty hack (hartman) */
1114     {
1115         p_vout->p_sys->s_rect.size.height = p_vout->p_sys->s_rect.size.height - 24;
1116     }
1117
1118     p_vout->p_sys->p_qdport = nil;
1119     [p_vout->p_sys->o_window close];
1120     p_vout->p_sys->o_window = nil;
1121 }
1122
1123 @end