]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
* configure.ac.in: Disabled -Wtraditional as it produces much more bogus
[vlc] / modules / gui / macosx / vout.m
1 /*****************************************************************************
2  * vout.m: MacOS X video output plugin
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: vout.m,v 1.6 2002/12/07 23:50:30 massiot 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  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <errno.h>                                                 /* ENOMEM */
30 #include <stdlib.h>                                                /* free() */
31 #include <string.h>                                            /* strerror() */
32
33 #include <vlc/vlc.h>
34 #include <vlc/vout.h>
35 #include <vlc/aout.h>
36 #include <vlc/intf.h>
37
38 #include <Cocoa/Cocoa.h>
39 #include <QuickTime/QuickTime.h>
40
41 #include "intf.h"
42 #include "vout.h"
43
44 #define QT_MAX_DIRECTBUFFERS 10
45
46 struct picture_sys_t
47 {
48     void *p_info;
49     unsigned int i_size;
50
51     /* When using I420 output */
52     PlanarPixmapInfoYUV420 pixmap_i420;
53 };
54
55 /*****************************************************************************
56  * Local prototypes
57  *****************************************************************************/
58 static int  vout_Init      ( vout_thread_t * );
59 static void vout_End       ( vout_thread_t * );
60 static int  vout_Manage    ( vout_thread_t * );
61 static void vout_Display   ( vout_thread_t *, picture_t * );
62
63 static int  CoSendRequest      ( vout_thread_t *, long );
64 static int  CoCreateWindow     ( vout_thread_t * );
65 static int  CoDestroyWindow    ( vout_thread_t * );
66 static int  CoToggleFullscreen ( vout_thread_t * );
67
68 static void QTScaleMatrix      ( vout_thread_t * );
69 static int  QTCreateSequence   ( vout_thread_t * );
70 static void QTDestroySequence  ( vout_thread_t * );
71 static int  QTNewPicture       ( vout_thread_t *, picture_t * );
72 static void QTFreePicture      ( vout_thread_t *, picture_t * );
73
74 /*****************************************************************************
75  * OpenVideo: allocates MacOS X video thread output method
76  *****************************************************************************
77  * This function allocates and initializes a MacOS X vout method.
78  *****************************************************************************/
79 int E_(OpenVideo) ( vlc_object_t *p_this )
80 {   
81     vout_thread_t * p_vout = (vout_thread_t *)p_this;
82     OSErr err;
83     int i_timeout;
84
85     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
86     if( p_vout->p_sys == NULL )
87     {
88         msg_Err( p_vout, "out of memory" );
89         return( 1 );
90     }
91
92     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
93
94     /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
95     for( i_timeout = 20 ; i_timeout-- ; )
96     {
97         vlc_list_t * p_list = vlc_list_find( p_vout, VLC_OBJECT_INTF,
98                                                      FIND_ANYWHERE );
99         intf_thread_t ** pp_intf = (intf_thread_t **)p_list->pp_objects;
100
101         /* Parse the list of interfaces to see if one suits us */
102         for( ; *pp_intf ; pp_intf++ )
103         {
104             if( (*pp_intf)->p_module &&
105               !strcmp( (*pp_intf)->p_module->psz_object_name, MODULE_STRING ) )
106             {
107                 vlc_object_yield( *pp_intf );
108                 p_vout->p_sys->p_intf = *pp_intf;
109                 break;
110             }
111         }
112         vlc_list_release( p_list );
113
114         if( p_vout->p_sys->p_intf == NULL )
115         {
116             msleep( INTF_IDLE_SLEEP );
117         }
118     }
119
120     if( p_vout->p_sys->p_intf == NULL )
121     {
122         msg_Err( p_vout, "no MacOS X interface present" );
123         free( p_vout->p_sys );
124         return( 1 );
125     }
126
127     p_vout->p_sys->h_img_descr = 
128         (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
129     p_vout->p_sys->p_matrix = (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
130
131     p_vout->p_sys->b_mouse_pointer_visible = 1;
132
133     /* set window size */
134     p_vout->p_sys->s_rect.size.width = p_vout->i_window_width;
135     p_vout->p_sys->s_rect.size.height = p_vout->i_window_height;
136
137     if( ( err = EnterMovies() ) != noErr )
138     {
139         msg_Err( p_vout, "EnterMovies failed: %d", err );
140         free( p_vout->p_sys->p_matrix );
141         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
142         vlc_object_release( p_vout->p_sys->p_intf );
143         free( p_vout->p_sys );
144         return( 1 );
145     } 
146
147     if( vout_ChromaCmp( p_vout->render.i_chroma, VLC_FOURCC('I','4','2','0') ) )
148     {
149         err = FindCodec( kYUV420CodecType, bestSpeedCodec,
150                          nil, &p_vout->p_sys->img_dc );
151         if( err == noErr && p_vout->p_sys->img_dc != 0 )
152         {
153             p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
154             p_vout->p_sys->i_codec = kYUV420CodecType;
155         }
156         else
157         {
158             msg_Err( p_vout, "failed to find an appropriate codec" );
159         }
160     }
161     else
162     {
163         msg_Err( p_vout, "chroma 0x%08x not supported",
164                          p_vout->render.i_chroma );
165     }
166
167     if( p_vout->p_sys->img_dc == 0 )
168     {
169         free( p_vout->p_sys->p_matrix );
170         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
171         vlc_object_release( p_vout->p_sys->p_intf );
172         free( p_vout->p_sys );
173         return( 1 );        
174     }
175
176     NSArray * p_screens = [NSScreen screens];
177     if ( [p_screens count] > 0 )
178     {
179         vlc_value_t val;
180         var_Destroy( p_vout, "video-device" );
181         var_Create( p_vout, "video-device", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
182         NSEnumerator * p_enumerator = [p_screens objectEnumerator];
183         NSScreen * p_screen;
184         int i = 1;
185         while ( (p_screen = [p_enumerator nextObject]) != NULL )
186         {
187             NSRect p_rect = [p_screen frame];
188             char psz_temp[255];
189             snprintf(psz_temp, sizeof(psz_temp), "%s %d (%dx%d)",
190                      _("Screen"), i, (int)p_rect.size.width,
191                      (int)p_rect.size.height);
192             val.psz_string = psz_temp;
193             var_Change( p_vout, "video-device", VLC_VAR_ADDCHOICE, &val );
194             i++;
195         }
196         var_AddCallback( p_vout, "video-device", vout_VarCallback,
197                          NULL );
198
199         val.b_bool = VLC_TRUE;
200         var_Set( p_vout, "intf-change", val );
201     }
202
203     if( CoCreateWindow( p_vout ) )
204     {
205         msg_Err( p_vout, "unable to create window" );
206         free( p_vout->p_sys->p_matrix );
207         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
208         vlc_object_release( p_vout->p_sys->p_intf );
209         free( p_vout->p_sys ); 
210         return( 1 );
211     }
212
213     p_vout->pf_init = vout_Init;
214     p_vout->pf_end = vout_End;
215     p_vout->pf_manage = vout_Manage;
216     p_vout->pf_render = NULL;
217     p_vout->pf_display = vout_Display;
218
219     return( 0 );
220 }
221
222 /*****************************************************************************
223  * vout_Init: initialize video thread output method
224  *****************************************************************************/
225 static int vout_Init( vout_thread_t *p_vout )
226 {
227     int i_index;
228     picture_t *p_pic;
229
230     I_OUTPUTPICTURES = 0;
231
232     /* Initialize the output structure; we already found a codec,
233      * and the corresponding chroma we will be using. Since we can
234      * arbitrary scale, stick to the coordinates and aspect. */
235     p_vout->output.i_width  = p_vout->render.i_width;
236     p_vout->output.i_height = p_vout->render.i_height;
237     p_vout->output.i_aspect = p_vout->render.i_aspect;
238
239     SetPort( p_vout->p_sys->p_qdport );
240     QTScaleMatrix( p_vout );
241
242     if( QTCreateSequence( p_vout ) )
243     {
244         msg_Err( p_vout, "unable to create sequence" );
245         return( 1 );
246     }
247
248     /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
249     while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
250     {
251         p_pic = NULL;
252
253         /* Find an empty picture slot */
254         for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
255         {
256             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
257             {
258                 p_pic = p_vout->p_picture + i_index;
259                 break;
260             }
261         }
262
263         /* Allocate the picture */
264         if( p_pic == NULL || QTNewPicture( p_vout, p_pic ) )
265         {
266             break;
267         }
268
269         p_pic->i_status = DESTROYED_PICTURE;
270         p_pic->i_type   = DIRECT_PICTURE;
271
272         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
273
274         I_OUTPUTPICTURES++;
275     }
276
277     return( 0 );
278 }
279
280 /*****************************************************************************
281  * vout_End: terminate video thread output method
282  *****************************************************************************/
283 static void vout_End( vout_thread_t *p_vout )
284 {
285     int i_index;
286
287     QTDestroySequence( p_vout );
288
289     /* Free the direct buffers we allocated */
290     for( i_index = I_OUTPUTPICTURES; i_index; )
291     {
292         i_index--;
293         QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
294     }
295 }
296
297 /*****************************************************************************
298  * CloseVideo: destroy video thread output method
299  *****************************************************************************/
300 void E_(CloseVideo) ( vlc_object_t *p_this )
301 {       
302     vout_thread_t * p_vout = (vout_thread_t *)p_this;     
303
304     if( CoDestroyWindow( p_vout ) )
305     {
306         msg_Err( p_vout, "unable to destroy window" );
307     }
308
309     ExitMovies();
310
311     free( p_vout->p_sys->p_matrix );
312     DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
313
314     vlc_object_release( p_vout->p_sys->p_intf );
315
316     free( p_vout->p_sys );
317 }
318
319 /*****************************************************************************
320  * vout_Manage: handle events
321  *****************************************************************************
322  * This function should be called regularly by video output thread. It manages
323  * console events. It returns a non null value on error.
324  *****************************************************************************/
325 static int vout_Manage( vout_thread_t *p_vout )
326 {    
327     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
328     {
329         if( CoToggleFullscreen( p_vout ) )  
330         {
331             return( 1 );
332         }
333
334         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
335     }
336
337     if( p_vout->i_changes & VOUT_SIZE_CHANGE ) 
338     {
339         QTScaleMatrix( p_vout );
340         SetDSequenceMatrix( p_vout->p_sys->i_seq, 
341                             p_vout->p_sys->p_matrix );
342  
343         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
344     }
345
346     /* hide/show mouse cursor */
347     if( p_vout->p_sys->b_mouse_moved ||
348         p_vout->p_sys->i_time_mouse_last_moved )
349     {
350         vlc_bool_t b_change = 0;
351
352         if( !p_vout->p_sys->b_mouse_pointer_visible )
353         {
354             CGDisplayShowCursor( kCGDirectMainDisplay );
355             b_change = 1;
356         }
357 #if 0
358         else if( !p_vout->p_sys->b_mouse_moved && 
359             mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 &&
360             p_vout->p_sys->b_mouse_pointer_visible )
361         {
362             CGDisplayHideCursor( kCGDirectMainDisplay );
363             b_change = 1;
364         }
365 #endif
366
367         if( b_change )
368         {
369             p_vout->p_sys->i_time_mouse_last_moved = 0;
370             p_vout->p_sys->b_mouse_moved = 0;
371             p_vout->p_sys->b_mouse_pointer_visible =
372                 !p_vout->p_sys->b_mouse_pointer_visible;
373         }
374     }
375
376     return( 0 );
377 }
378
379 /*****************************************************************************
380  * vout_Display: displays previously rendered output
381  *****************************************************************************
382  * This function sends the currently rendered image to the display.
383  *****************************************************************************/
384 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
385 {
386     OSErr err;
387     CodecFlags flags;
388
389     if( ( err = DecompressSequenceFrameS( 
390                     p_vout->p_sys->i_seq,
391                     p_pic->p_sys->p_info,
392                     p_pic->p_sys->i_size,                    
393                     codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
394     {
395         msg_Err( p_vout, "DecompressSequenceFrameS failed: %d", err );
396     }
397     else
398     {
399         QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
400     }
401 }
402
403 /*****************************************************************************
404  * CoSendRequest: send request to interface thread
405  *****************************************************************************
406  * Returns 0 on success, 1 otherwise
407  *****************************************************************************/
408 static int CoSendRequest( vout_thread_t *p_vout, long i_request )
409 {
410     NSArray *o_array;
411     NSPortMessage *o_msg;
412     struct vout_req_t req;
413     struct vout_req_t *p_req = &req;
414     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
415     NSPort *recvPort = [[NSPort port] retain];
416
417     memset( &req, 0, sizeof(req) );
418     req.i_type = i_request;
419     req.p_vout = p_vout;
420
421     req.o_lock = [[NSConditionLock alloc] initWithCondition: 0];
422
423     o_array = [NSArray arrayWithObject:
424         [NSData dataWithBytes: &p_req length: sizeof(void *)]];
425     o_msg = [[NSPortMessage alloc]
426         initWithSendPort: p_vout->p_sys->p_intf->p_sys->o_sendport
427         receivePort: recvPort components: o_array]; 
428
429     [o_msg sendBeforeDate: [NSDate distantPast]];
430
431     [req.o_lock lockWhenCondition: 1];
432     [req.o_lock unlock];
433
434     [o_msg release];
435     [req.o_lock release];
436
437     [recvPort release];
438     [o_pool release];
439
440     return( !req.i_result );
441 }
442
443 /*****************************************************************************
444  * CoCreateWindow: create new window 
445  *****************************************************************************
446  * Returns 0 on success, 1 otherwise
447  *****************************************************************************/
448 static int CoCreateWindow( vout_thread_t *p_vout )
449 {
450     if( CoSendRequest( p_vout, VOUT_REQ_CREATE_WINDOW ) )
451     {
452         msg_Err( p_vout, "CoSendRequest (CREATE_WINDOW) failed" );
453         return( 1 );
454     }
455
456     return( 0 );
457 }
458
459 /*****************************************************************************
460  * CoDestroyWindow: destroy window 
461  *****************************************************************************
462  * Returns 0 on success, 1 otherwise
463  *****************************************************************************/
464 static int CoDestroyWindow( vout_thread_t *p_vout )
465 {
466     if( !p_vout->p_sys->b_mouse_pointer_visible )
467     {
468         CGDisplayShowCursor( kCGDirectMainDisplay );
469         p_vout->p_sys->b_mouse_pointer_visible = 1;
470     }
471
472     if( CoSendRequest( p_vout, VOUT_REQ_DESTROY_WINDOW ) )
473     {
474         msg_Err( p_vout, "CoSendRequest (DESTROY_WINDOW) failed" );
475         return( 1 );
476     }
477
478     return( 0 );
479 }
480
481 /*****************************************************************************
482  * CoToggleFullscreen: toggle fullscreen 
483  *****************************************************************************
484  * Returns 0 on success, 1 otherwise
485  *****************************************************************************/
486 static int CoToggleFullscreen( vout_thread_t *p_vout )
487 {
488     QTDestroySequence( p_vout );
489
490     if( CoDestroyWindow( p_vout ) )
491     {
492         msg_Err( p_vout, "unable to destroy window" );
493         return( 1 );
494     }
495     
496     p_vout->b_fullscreen = !p_vout->b_fullscreen;
497
498     if( p_vout->b_fullscreen )
499     {
500         HideMenuBar();
501     }
502     else
503     {
504         ShowMenuBar();
505     }
506
507     if( CoCreateWindow( p_vout ) )
508     {
509         msg_Err( p_vout, "unable to create window" );
510         return( 1 );
511     }
512
513     SetPort( p_vout->p_sys->p_qdport );
514     QTScaleMatrix( p_vout );
515
516     if( QTCreateSequence( p_vout ) )
517     {
518         msg_Err( p_vout, "unable to create sequence" );
519         return( 1 ); 
520     } 
521
522     return( 0 );
523 }
524
525 /*****************************************************************************
526  * QTScaleMatrix: scale matrix 
527  *****************************************************************************/
528 static void QTScaleMatrix( vout_thread_t *p_vout )
529 {
530     Rect s_rect;
531     int i_width, i_height;
532     Fixed factor_x, factor_y;
533     int i_offset_x = 0;
534     int i_offset_y = 0;
535
536     GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
537
538     i_width = s_rect.right - s_rect.left;
539     i_height = s_rect.bottom - s_rect.top;
540
541     if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
542     {
543         int i_adj_width = i_height * p_vout->output.i_aspect /
544                           VOUT_ASPECT_FACTOR;
545
546         factor_x = FixDiv( Long2Fix( i_adj_width ),
547                            Long2Fix( p_vout->output.i_width ) );
548         factor_y = FixDiv( Long2Fix( i_height ),
549                            Long2Fix( p_vout->output.i_height ) );
550
551         i_offset_x = (i_width - i_adj_width) / 2;
552     }
553     else
554     {
555         int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
556                            p_vout->output.i_aspect;
557
558         factor_x = FixDiv( Long2Fix( i_width ),
559                            Long2Fix( p_vout->output.i_width ) );
560         factor_y = FixDiv( Long2Fix( i_adj_height ),
561                            Long2Fix( p_vout->output.i_height ) );
562
563         i_offset_y = (i_height - i_adj_height) / 2;
564     }
565
566     SetIdentityMatrix( p_vout->p_sys->p_matrix );
567
568     ScaleMatrix( p_vout->p_sys->p_matrix,
569                  factor_x, factor_y,
570                  Long2Fix(0), Long2Fix(0) );            
571
572     TranslateMatrix( p_vout->p_sys->p_matrix, 
573                      Long2Fix(i_offset_x), 
574                      Long2Fix(i_offset_y) );
575 }
576
577 /*****************************************************************************
578  * QTCreateSequence: create a new sequence 
579  *****************************************************************************
580  * Returns 0 on success, 1 otherwise
581  *****************************************************************************/
582 static int QTCreateSequence( vout_thread_t *p_vout )
583 {
584     OSErr err;
585     ImageDescriptionPtr p_descr;
586
587     HLock( (Handle)p_vout->p_sys->h_img_descr );
588     p_descr = *p_vout->p_sys->h_img_descr;
589
590     p_descr->idSize = sizeof(ImageDescription);
591     p_descr->cType = p_vout->p_sys->i_codec;
592     p_descr->version = 1;
593     p_descr->revisionLevel = 0;
594     p_descr->vendor = 'appl';
595     p_descr->width = p_vout->output.i_width;
596     p_descr->height = p_vout->output.i_height;
597     p_descr->hRes = Long2Fix(72);
598     p_descr->vRes = Long2Fix(72);
599     p_descr->spatialQuality = codecLosslessQuality;
600     p_descr->frameCount = 1;
601     p_descr->clutID = -1;
602     p_descr->dataSize = 0;
603     p_descr->depth = 24;
604
605     HUnlock( (Handle)p_vout->p_sys->h_img_descr );
606
607     if( ( err = DecompressSequenceBeginS( 
608                               &p_vout->p_sys->i_seq,
609                               p_vout->p_sys->h_img_descr,
610                               NULL, 0,
611                               p_vout->p_sys->p_qdport,
612                               NULL, NULL,
613                               p_vout->p_sys->p_matrix,
614                               0, NULL,
615                               codecFlagUseImageBuffer,
616                               codecLosslessQuality,
617                               p_vout->p_sys->img_dc ) ) )
618     {
619         msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
620         return( 1 );
621     }
622
623     return( 0 );
624 }
625
626 /*****************************************************************************
627  * QTDestroySequence: destroy sequence 
628  *****************************************************************************/
629 static void QTDestroySequence( vout_thread_t *p_vout )
630 {
631     CDSequenceEnd( p_vout->p_sys->i_seq );
632 }
633
634 /*****************************************************************************
635  * QTNewPicture: allocate a picture
636  *****************************************************************************
637  * Returns 0 on success, 1 otherwise
638  *****************************************************************************/
639 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
640 {
641     int i_width  = p_vout->output.i_width;
642     int i_height = p_vout->output.i_height;
643
644     /* We know the chroma, allocate a buffer which will be used
645      * directly by the decoder */
646     p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
647
648     if( p_pic->p_sys == NULL )
649     {
650         return( -1 );
651     }
652
653     switch( p_vout->output.i_chroma )
654     {
655         case VLC_FOURCC('I','4','2','0'):
656
657             p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
658             p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
659
660             /* Allocate the memory buffer */
661             p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
662                                           16, i_width * i_height * 3 / 2 );
663
664             /* Y buffer */
665             p_pic->Y_PIXELS = p_pic->p_data; 
666             p_pic->p[Y_PLANE].i_lines = i_height;
667             p_pic->p[Y_PLANE].i_pitch = i_width;
668             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
669             p_pic->p[Y_PLANE].i_visible_pitch = i_width;
670
671             /* U buffer */
672             p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
673             p_pic->p[U_PLANE].i_lines = i_height / 2;
674             p_pic->p[U_PLANE].i_pitch = i_width / 2;
675             p_pic->p[U_PLANE].i_pixel_pitch = 1;
676             p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
677
678             /* V buffer */
679             p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
680             p_pic->p[V_PLANE].i_lines = i_height / 2;
681             p_pic->p[V_PLANE].i_pitch = i_width / 2;
682             p_pic->p[V_PLANE].i_pixel_pitch = 1;
683             p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
684
685             /* We allocated 3 planes */
686             p_pic->i_planes = 3;
687
688 #define P p_pic->p_sys->pixmap_i420
689             P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
690                                        - p_pic->p_sys->p_info;
691             P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
692                                         - p_pic->p_sys->p_info;
693             P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
694                                         - p_pic->p_sys->p_info;
695
696             P.componentInfoY.rowBytes = i_width;
697             P.componentInfoCb.rowBytes = i_width / 2;
698             P.componentInfoCr.rowBytes = i_width / 2;
699 #undef P
700
701             break;
702
703     default:
704         /* Unknown chroma, tell the guy to get lost */
705         free( p_pic->p_sys );
706         msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
707                  p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
708         p_pic->i_planes = 0;
709         return( -1 );
710     }
711
712     return( 0 );
713 }
714
715 /*****************************************************************************
716  * QTFreePicture: destroy a picture allocated with QTNewPicture
717  *****************************************************************************/
718 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
719 {
720     switch( p_vout->output.i_chroma )
721     {
722         case VLC_FOURCC('I','4','2','0'):
723             free( p_pic->p_data_orig );
724             break;
725     }
726
727     free( p_pic->p_sys );
728 }
729
730 /*****************************************************************************
731  * VLCWindow implementation
732  *****************************************************************************/
733 @implementation VLCWindow
734
735 - (void)setVout:(vout_thread_t *)_p_vout
736 {
737     p_vout = _p_vout;
738 }
739
740 - (void)toggleFullscreen
741 {
742     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
743 }
744
745 - (BOOL)isFullscreen
746 {
747     return( p_vout->b_fullscreen );
748 }
749
750 - (BOOL)canBecomeKeyWindow
751 {
752     return( YES );
753 }
754
755 - (void)keyDown:(NSEvent *)o_event
756 {
757     unichar key = 0;
758
759     if( [[o_event characters] length] )
760     {
761         key = [[o_event characters] characterAtIndex: 0];
762     }
763
764     switch( key )
765     {
766         case (unichar)0xf700: /* up-arrow */
767         { 
768             aout_instance_t * p_aout = vlc_object_find( p_vout, VLC_OBJECT_AOUT,
769                                                         FIND_ANYWHERE );
770             if ( p_aout != NULL ) 
771             {
772                 aout_VolumeUp( p_aout, 1, NULL );
773                 vlc_object_release( (vlc_object_t *)p_aout );
774             }
775         } 
776         break;
777
778         case (unichar)0xf701: /* down-arrow */
779         {
780             aout_instance_t * p_aout = vlc_object_find( p_vout, VLC_OBJECT_AOUT,
781                                                         FIND_ANYWHERE );
782             if ( p_aout != NULL ) 
783             {
784                 aout_VolumeDown( p_aout, 1, NULL );
785                 vlc_object_release( (vlc_object_t *)p_aout );
786             }
787         }
788         break;
789
790         case 'f': case 'F':
791             [self toggleFullscreen];
792             break;
793
794         case (unichar)0x1b: /* escape */
795             if( [self isFullscreen] )
796             {
797                 [self toggleFullscreen];
798             }
799             break;
800
801         case 'q': case 'Q':
802             p_vout->p_vlc->b_die = VLC_TRUE;
803             break;
804
805         case ' ':
806             input_SetStatus( p_vout, INPUT_STATUS_PAUSE );
807             break;
808
809         default:
810             [super keyDown: o_event];
811             break;
812     }
813 }
814
815 @end
816
817 /*****************************************************************************
818  * VLCView implementation
819  *****************************************************************************/
820 @implementation VLCView
821
822 - (void)setVout:(vout_thread_t *)_p_vout
823 {
824     p_vout = _p_vout;
825 }
826
827 - (void)drawRect:(NSRect)rect
828 {
829     [[NSColor blackColor] set];
830     NSRectFill( rect );
831     [super drawRect: rect];
832
833     p_vout->i_changes |= VOUT_SIZE_CHANGE;
834 }
835
836 - (BOOL)acceptsFirstResponder
837 {
838     return( YES );
839 }
840
841 - (BOOL)becomeFirstResponder
842 {
843     [[self window] setAcceptsMouseMovedEvents: YES];
844     return( YES );
845 }
846
847 - (BOOL)resignFirstResponder
848 {
849     [[self window] setAcceptsMouseMovedEvents: NO];
850     return( YES );
851 }
852
853 - (void)mouseUp:(NSEvent *)o_event
854 {
855     switch( [o_event type] )
856     {
857         case NSLeftMouseUp:
858         {
859             vlc_value_t val;
860             val.b_bool = VLC_TRUE;
861             var_Set( p_vout, "mouse-clicked", val );        
862         }
863         break;
864
865         default:
866             [super mouseUp: o_event];
867         break;
868     }
869 }
870
871 - (void)mouseMoved:(NSEvent *)o_event
872 {
873     NSPoint ml;
874     NSRect s_rect;
875     BOOL b_inside;
876
877     s_rect = [self bounds];
878     ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
879     b_inside = [self mouse: ml inRect: s_rect];
880
881     if( b_inside )
882     {
883         vlc_value_t val;
884         int i_width, i_height, i_x, i_y;
885
886         vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
887                                    (unsigned int)s_rect.size.height,
888                                    &i_x, &i_y, &i_width, &i_height );
889
890         val.i_int = ( ((int)ml.x) - i_x ) * 
891                     p_vout->render.i_width / i_width;
892         var_Set( p_vout, "mouse-x", val );
893
894         val.i_int = ( ((int)ml.y) - i_y ) * 
895                     p_vout->render.i_height / i_height;
896         var_Set( p_vout, "mouse-y", val );
897
898         val.b_bool = VLC_TRUE;
899         var_Set( p_vout, "mouse-moved", val );
900     }
901     else
902     {
903         [super mouseMoved: o_event];
904     }
905 }
906
907 @end