]> git.sesse.net Git - vlc/blob - plugins/macosx/vout_macosx.c
b58dae5c0104730f2269137110d71b01fe0e8f60
[vlc] / plugins / macosx / vout_macosx.c
1 /*****************************************************************************
2  * vout_macosx.c: MacOS X video output plugin
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  *
6  * Authors: Colin Delacroix <colin@zoy.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  * 
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
21  *****************************************************************************/
22
23 #include "modules_inner.h"
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include "defs.h"
29
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdlib.h>                                                /* free() */
32 #include <string.h>                                            /* strerror() */
33
34 #include "config.h"
35 #include "common.h"
36 #include "threads.h"
37 #include "mtime.h"
38 #include "tests.h"
39
40 #include "intf_msg.h"
41
42 #include "video.h"
43 #include "video_output.h"
44
45 #include "modules.h"
46 #include "main.h"
47
48 #include "macosx_common.h"
49
50
51 /*****************************************************************************
52  * Constants & more
53  *****************************************************************************/
54
55 // Initial Window Constants
56 enum
57 {
58     kWindowOffset = 100
59 };
60
61 // where is the off screen
62 enum
63 {
64     kNoWhere = 0,
65     kInVRAM,
66     kInAGP,
67     kInSystem
68 };
69
70
71 /*****************************************************************************
72  * Local prototypes
73  *****************************************************************************/
74 static int  vout_Probe     ( probedata_t *p_data );
75 static int  vout_Create    ( struct vout_thread_s * );
76 static int  vout_Init      ( struct vout_thread_s * );
77 static void vout_End       ( struct vout_thread_s * );
78 static void vout_Destroy   ( struct vout_thread_s * );
79 static int  vout_Manage    ( struct vout_thread_s * );
80 static void vout_Display   ( struct vout_thread_s * );
81
82 /* OS specific */
83
84 static int CreateDisplay        ( struct vout_thread_s * );
85 static int MakeWindow           ( struct vout_thread_s * );
86 static int AllocBuffer          ( struct vout_thread_s * , short index );
87
88 void BlitToWindow               ( struct vout_thread_s * , short index );
89 GDHandle GetWindowDevice        ( struct vout_thread_s * );
90 void FillOffscreen              ( struct vout_thread_s * , short index);
91
92 void FindBestMemoryLocation( struct vout_thread_s * );
93
94 /*****************************************************************************
95  * Functions exported as capabilities. They are declared as static so that
96  * we don't pollute the namespace too much.
97  *****************************************************************************/
98 void _M( vout_getfunctions )( function_list_t * p_function_list )
99 {
100     p_function_list->pf_probe = vout_Probe;
101     p_function_list->functions.vout.pf_create     = vout_Create;
102     p_function_list->functions.vout.pf_init       = vout_Init;
103     p_function_list->functions.vout.pf_end        = vout_End;
104     p_function_list->functions.vout.pf_destroy    = vout_Destroy;
105     p_function_list->functions.vout.pf_manage     = vout_Manage;
106     p_function_list->functions.vout.pf_display    = vout_Display;
107     p_function_list->functions.vout.pf_setpalette = NULL;
108 }
109
110 /*****************************************************************************
111  * intf_Probe: return a score
112  *****************************************************************************/
113 static int vout_Probe( probedata_t *p_data )
114 {
115     if( TestMethod( VOUT_METHOD_VAR, "macosx" ) )
116     {
117         return( 999 );
118     }
119
120     return( 100 );
121 }
122
123 /*****************************************************************************
124  * vout_Create: allocates MacOS X video thread output method
125  *****************************************************************************
126  * This function allocates and initializes a MacOS X vout method.
127  *****************************************************************************/
128 static int vout_Create( vout_thread_t *p_vout )
129 {
130     //intf_ErrMsg( "vout_Create()" );
131
132     /* Allocate structure */
133     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
134     if( p_vout->p_sys == NULL )
135     {
136         intf_ErrMsg( "error: %s", strerror( ENOMEM ) );
137         return( 1 );
138     }
139
140     p_vout->p_sys->gwLocOffscreen = kNoWhere;
141     p_vout->p_sys->p_window       = NULL;
142     p_vout->p_sys->p_gw[ 0 ]      = NULL;
143     p_vout->p_sys->p_gw[ 1 ]      = NULL;
144     
145     if ( CreateDisplay( p_vout ) )
146     {
147         intf_ErrMsg( "vout error: can't open display" );
148         free( p_vout->p_sys );
149         return( 1 );
150     }
151
152 #if 0
153     intf_ErrMsg( "vout p_vout->i_width %d" , p_vout->i_width);
154     intf_ErrMsg( "vout p_vout->i_height %d" , p_vout->i_height);
155     intf_ErrMsg( "vout p_vout->i_bytes_per_pixel %d" , p_vout->i_bytes_per_pixel);
156     intf_ErrMsg( "vout p_vout->i_screen_depth %d" , p_vout->i_screen_depth);
157 #endif
158
159     return( 0 );
160 }
161
162 /*****************************************************************************
163  * Find the best memory (AGP, VRAM, system) location
164  *****************************************************************************/
165 void FindBestMemoryLocation( vout_thread_t *p_vout )
166 {
167     long versionSystem;
168
169     Gestalt( gestaltSystemVersion, &versionSystem );
170     if ( 0x00000900 <= ( versionSystem & 0x00000FF00  ) )
171     {
172         intf_ErrMsg( "FindBestMemoryLocation : gNewNewGWorld = true" );
173         p_vout->p_sys->gNewNewGWorld = true;
174     }
175     else
176     {
177         // now it is tricky
178         // we will try to allocate in VRAM and find out where the allocation really ended up.
179         GWorldPtr pgwTest = NULL;
180         Rect rectTest = {0, 0, 10, 10};
181         short wPixDepth = 
182             (**(GetPortPixMap( GetWindowPort( p_vout->p_sys->p_window ) ))).pixelSize;
183         GDHandle hgdWindow = GetWindowDevice( p_vout );
184
185         intf_ErrMsg( "FindBestMemoryLocation : gNewNewGWorld = false !" );
186 #if 0
187         p_vout->i_screen_depth = wPixDepth;
188         p_vout->i_bytes_per_pixel = wPixDepth;
189         p_vout->i_bytes_per_line = (**(**hgdWindow).gdPMap).rowBytes & 0x3FFF ;
190 #endif
191         if(    ( noErr == NewGWorld( &pgwTest, wPixDepth, &rectTest, NULL, hgdWindow,
192                                      noNewDevice | useDistantHdwrMem ) ) 
193             && ( pgwTest ) )
194         {
195             p_vout->p_sys->gNewNewGWorld = true;        
196         }
197         
198         if( pgwTest )
199         {
200             DisposeGWorld( pgwTest );
201         }
202     }
203 }
204
205 /*****************************************************************************
206  * CreateDisplay: setup display params...
207  *****************************************************************************/
208 static int CreateDisplay( vout_thread_t *p_vout )
209 {
210     PixMapHandle hPixmap0, hPixmap1;
211     void * hPixmapBaseAddr0, * hPixmapBaseAddr1;
212
213     //intf_ErrMsg( "CreateDisplay()" );
214
215     if( MakeWindow( p_vout ) )
216     {
217         intf_ErrMsg( "vout error: can't open window display" );
218         return( 1 );
219     }
220
221     // FindBestMemoryLocation( p_vout );
222
223     //try to allocate @ best location, will eventaully trickle down to worst
224     p_vout->p_sys->gwLocOffscreen = kInVRAM;
225     if( AllocBuffer( p_vout, 0 ) || AllocBuffer( p_vout, 1 ) )
226     {
227         intf_ErrMsg( "vout error: can't alloc offscreen buffers" );
228         return( 1 );
229     }
230
231 //FIXME ? - lock this down until the end...
232     hPixmap0 = GetGWorldPixMap( p_vout->p_sys->p_gw[0] );
233     LockPixels(hPixmap0);
234     hPixmap1 = GetGWorldPixMap( p_vout->p_sys->p_gw[1] );
235     LockPixels(hPixmap1);
236     
237     //FIXME hopefully this is the same for all Gworlds & window since they are the same size
238     p_vout->i_bytes_per_line = (**hPixmap0).rowBytes & 0x3FFF;
239
240     if ( (hPixmap0 == NULL) || (hPixmap1 == NULL) )
241     {
242         intf_ErrMsg( "vout error: pixmap problem");
243         UnlockPixels(hPixmap0);
244         UnlockPixels(hPixmap1);
245         return( 1 );
246     }
247
248     hPixmapBaseAddr0 = GetPixBaseAddr( hPixmap0 );
249     hPixmapBaseAddr1 = GetPixBaseAddr( hPixmap1 );
250     if ( (hPixmapBaseAddr0 == NULL) || (hPixmapBaseAddr1 == NULL) )
251     {
252         intf_ErrMsg( "vout error: pixmap base addr problem");
253         return( 1 );
254     }
255
256 //FIXME - if I ever dispose of the Gworlds and recreate them, i'll have a new address
257 //and I'll need to tell vout about them...  dunno what problems vout might have if we just updateGworld  
258     p_vout->pf_setbuffers( p_vout, hPixmapBaseAddr0, hPixmapBaseAddr1 );
259
260     return 0;
261 }
262
263 /*****************************************************************************
264  * MakeWindow: open and set-up a Mac OS main window
265  *****************************************************************************/
266 static int MakeWindow( vout_thread_t *p_vout )
267 {
268     int left = 0;
269     int top = 0;
270     int bottom = p_vout->i_height;
271     int right = p_vout->i_width;
272     ProcessSerialNumber PSN;
273
274     WindowAttributes windowAttr = kWindowStandardDocumentAttributes | 
275                                     kWindowStandardHandlerAttribute |
276                                     kWindowInWindowMenuAttribute;
277     
278     SetRect( &p_vout->p_sys->wrect, left, top, right, bottom );
279     OffsetRect( &p_vout->p_sys->wrect, kWindowOffset, kWindowOffset );
280
281     CreateNewWindow( kDocumentWindowClass, windowAttr, &p_vout->p_sys->wrect, &p_vout->p_sys->p_window );
282     if ( p_vout->p_sys->p_window == nil )
283     {
284         return( 1 );
285     }
286
287     InstallStandardEventHandler(GetWindowEventTarget(p_vout->p_sys->p_window));
288     SetPort( GetWindowPort( p_vout->p_sys->p_window ) );
289     SetWindowTitleWithCFString( p_vout->p_sys->p_window, CFSTR("VLC") );
290     ShowWindow( p_vout->p_sys->p_window );
291     SelectWindow( p_vout->p_sys->p_window );
292
293     //in case we are run from the command line, bring us to front instead of Terminal
294     GetCurrentProcess(&PSN);
295     SetFrontProcess(&PSN);
296
297 {
298     short wPixDepth = (**(GetPortPixMap( GetWindowPort( p_vout->p_sys->p_window ) ))).pixelSize;
299     p_vout->i_screen_depth = wPixDepth;
300     p_vout->i_bytes_per_pixel = p_vout->i_screen_depth / 8;
301     p_vout->i_bytes_per_line   = p_vout->i_width * p_vout->i_bytes_per_pixel;
302
303     p_vout->i_bytes_per_line = (**(**GetWindowDevice( p_vout )).gdPMap).rowBytes & 0x3FFF ;
304
305     switch ( p_vout->i_screen_depth )
306     {
307         case 32:
308         case 24:
309             p_vout->i_red_mask =   0xff0000;
310             p_vout->i_green_mask = 0xff00;
311             p_vout->i_blue_mask =  0xff;
312             break;
313         case 16:
314         case 15:
315             p_vout->i_red_mask =   0x00007c00;
316             p_vout->i_green_mask = 0x000003e0;
317             p_vout->i_blue_mask =  0x0000001f;
318             break;
319         default:
320             break;
321     }
322 }
323
324 #if 0
325     p_vout->i_red_lshift = 0x10;
326     p_vout->i_red_rshift = 0x0;
327     p_vout->i_green_lshift = 0x8;
328     p_vout->i_green_rshift = 0x0;
329     p_vout->i_blue_lshift = 0x0;
330     p_vout->i_blue_rshift = 0x0;
331
332     p_vout->i_white_pixel = 0xffffff;
333     p_vout->i_black_pixel = 0x0;
334     p_vout->i_gray_pixel = 0x808080;
335     p_vout->i_blue_pixel = 0x32;
336 #endif
337
338     return( 0 );
339 }
340
341 /*****************************************************************************
342  * AllocBuffer: forces offscreen allocation (if different than current) in
343  * memory type specified
344  *****************************************************************************/
345 static int AllocBuffer ( vout_thread_t *p_vout, short index )
346 {
347     Rect bounds;
348     GDHandle hgdWindow = GetWindowDevice( p_vout );
349
350     switch ( p_vout->p_sys->gwLocOffscreen )
351     {
352         case kInVRAM:
353             if ( noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth, 
354                 GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL, 
355                 hgdWindow, noNewDevice | useDistantHdwrMem ) )          
356             {
357                 intf_ErrMsg( "Allocate off screen image in VRAM" );
358                 break;
359             }
360             intf_ErrMsg( "Unable to allocate off screen image in VRAM, trying next best AGP" );
361             p_vout->p_sys->gwLocOffscreen = kInAGP;
362         case kInAGP:
363             if (noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth, 
364                 GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL, 
365                 hgdWindow, noNewDevice | useLocalHdwrMem ) )
366             {
367                 intf_ErrMsg( "Allocate off screen image in AGP" );
368                 break;
369             }
370             intf_ErrMsg( "Unable to allocate off screen image in AGP, trying next best System" );
371             p_vout->p_sys->gwLocOffscreen = kInSystem;
372         case kInSystem:
373         default:
374             if ( noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth, 
375                 GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL, 
376                 hgdWindow, noNewDevice | keepLocal) )
377             {
378                 intf_ErrMsg( "Allocate off screen image in System" );
379                 break;
380             }
381             intf_ErrMsg( "Unable to allocate off screen image in System, no options left - failing" );
382             p_vout->p_sys->gwLocOffscreen = kNoWhere;
383             return( 1 ); // nothing was allocated
384     } 
385     return( 0 );
386 }
387
388 /*****************************************************************************
389  * vout_Init: initialize video thread output method
390  *****************************************************************************/
391 static int vout_Init( vout_thread_t *p_vout )
392 {
393     //intf_ErrMsg( "vout_Init()" );
394     return( 0 );
395 }
396
397 /*****************************************************************************
398  * vout_End: terminate video thread output method
399  *****************************************************************************/
400 static void vout_End( vout_thread_t *p_vout )
401 {
402     //intf_ErrMsg( "vout_End()" );
403     ;
404 }
405
406 /*****************************************************************************
407  * vout_Destroy: destroy video thread output method
408  *****************************************************************************/
409 static void vout_Destroy( vout_thread_t *p_vout )
410 {
411     //intf_ErrMsg( "vout_Destroy()" );
412
413 //FIXME Big Lock around Gworlds
414     PixMapHandle hPixmap0, hPixmap1;
415     hPixmap0 = GetGWorldPixMap( p_vout->p_sys->p_gw[0] );
416     hPixmap1 = GetGWorldPixMap( p_vout->p_sys->p_gw[1] );
417     UnlockPixels(hPixmap0);
418     UnlockPixels(hPixmap1);
419
420     if ( p_vout->p_sys->p_gw[0] )
421     {
422         DisposeGWorld( p_vout->p_sys->p_gw[0] );
423     }
424     if ( p_vout->p_sys->p_gw[1] )
425     {
426         DisposeGWorld( p_vout->p_sys->p_gw[1] );
427     }
428     if ( p_vout->p_sys->p_window )
429     {
430         DisposeWindow( p_vout->p_sys->p_window );
431     }
432
433     free( p_vout->p_sys );
434 }
435
436 /*****************************************************************************
437  * vout_Manage: handle events
438  *****************************************************************************
439  * This function should be called regularly by video output thread. It manages
440  * console events. It returns a non null value on error.
441  *****************************************************************************/
442 static int vout_Manage( vout_thread_t *p_vout )
443 {
444 //    intf_ErrMsg( "vout_Manage()" );
445     return( 0 );
446 }
447
448 /*****************************************************************************
449  * vout_Display: displays previously rendered output
450  *****************************************************************************
451  * This function send the currently rendered image to image, waits until
452  * it is displayed and switch the two rendering buffers, preparing next frame.
453  *****************************************************************************/
454 static void vout_Display( vout_thread_t *p_vout )
455 {
456 //    intf_ErrMsg( "vout_Display()" );
457
458 //we should not be called if we set the status to paused or stopped via the interface
459 //    if ( p_vout->p_sys->playback_status != PAUSED &&  p_vout->p_sys->playback_status != STOPPED )
460         BlitToWindow ( p_vout, p_vout->i_buffer_index );
461 }
462
463
464 /*****************************************************************************
465  * flushQD: flushes buffered window area
466  *****************************************************************************/
467 void flushQD( vout_thread_t *p_vout )
468 {
469     CGrafPtr thePort;
470
471     //intf_ErrMsg( "flushQD()" );
472     
473     thePort = GetWindowPort( p_vout->p_sys->p_window );
474     
475     /* flush the entire port */
476     if (QDIsPortBuffered(thePort))
477         QDFlushPortBuffer(thePort, NULL);
478
479 #if 0
480     /* flush part of the port */
481     if (QDIsPortBuffered(thePort)) {
482         RgnHandle theRgn;
483         theRgn = NewRgn();
484             /* local port coordinates */
485         SetRectRgn(theRgn, 10, 10, 100, 30); 
486         QDFlushPortBuffer(thePort, theRgn);
487         DisposeRgn(theRgn);
488     }
489 #endif
490
491 }
492
493 /*****************************************************************************
494  * BlitToWindow: checks offscreen and blits it to the front
495  *****************************************************************************/
496
497 void BlitToWindow( vout_thread_t *p_vout, short index )
498 {
499     Rect rectDest, rectSource;
500     GrafPtr pCGrafSave, windowPort = GetWindowPort( p_vout->p_sys->p_window );
501
502     //intf_ErrMsg( "BlitToWindow() for %d", index );
503
504     GetPortBounds( p_vout->p_sys->p_gw[index], &rectSource );
505     GetPortBounds( windowPort, &rectDest );
506     
507     GetPort ( &pCGrafSave );
508     SetPortWindowPort( p_vout->p_sys->p_window );
509 //FIXME have global lock - kinda bad but oh well 
510 //    if ( LockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) ) )
511 //    {
512
513 //LockPortBits(GetWindowPort( p_vout->p_sys->p_window ));
514 //NoPurgePixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
515
516         CopyBits( GetPortBitMapForCopyBits( p_vout->p_sys->p_gw[index] ), 
517                     GetPortBitMapForCopyBits( GetWindowPort( p_vout->p_sys->p_window ) ), 
518                     &rectSource, &rectDest, srcCopy, NULL);
519
520 //UnlockPortBits(GetWindowPort( p_vout->p_sys->p_window ));
521 //AllowPurgePixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
522
523 //        UnlockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
524         //flushQD( p_vout );
525 //    }
526     SetPort ( pCGrafSave );
527 }
528
529
530 /*****************************************************************************
531  * GetWindowDevice: returns GDHandle that window resides on (most of it anyway)
532  *****************************************************************************/
533 GDHandle GetWindowDevice( vout_thread_t *p_vout )
534 {
535     GrafPtr pgpSave;
536     Rect rectWind, rectSect;
537     long greatestArea, sectArea;
538     GDHandle hgdNthDevice, hgdZoomOnThisDevice = NULL;
539     
540     //intf_ErrMsg( "GetWindowDevice()" );
541
542     GetPort( &pgpSave );
543     SetPortWindowPort( p_vout->p_sys->p_window );
544     GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &rectWind );
545     LocalToGlobal( ( Point* ) &rectWind.top );
546     LocalToGlobal( ( Point* ) &rectWind.bottom );
547     hgdNthDevice = GetDeviceList();
548     greatestArea = 0;
549     // check window against all gdRects in gDevice list and remember 
550     //  which gdRect contains largest area of window}
551     while ( hgdNthDevice )
552     {
553         if ( TestDeviceAttribute( hgdNthDevice, screenDevice ) )
554         {
555             if ( TestDeviceAttribute( hgdNthDevice, screenActive ) )
556             {
557                 // The SectRect routine calculates the intersection 
558                 //  of the window rectangle and this gDevice 
559                 //  rectangle and returns TRUE if the rectangles intersect, 
560                 //  FALSE if they don't.
561                 SectRect( &rectWind, &( **hgdNthDevice ).gdRect, &rectSect );
562                 // determine which screen holds greatest window area
563                 //  first, calculate area of rectangle on current device
564                 sectArea = ( long )( rectSect.right - rectSect.left ) * ( rectSect.bottom - rectSect.top );
565                 if ( sectArea > greatestArea )
566                 {
567                     greatestArea = sectArea;    // set greatest area so far
568                     hgdZoomOnThisDevice = hgdNthDevice; // set zoom device
569                 }
570                 hgdNthDevice = GetNextDevice( hgdNthDevice );
571             }
572         }
573     }   // of WHILE
574     SetPort( pgpSave );
575     return hgdZoomOnThisDevice;
576 }
577
578 /*****************************************************************************
579  * FillOffScreen: fills offscreen buffer with random bright color
580  *****************************************************************************/
581
582 void FillOffscreen( vout_thread_t *p_vout, short index )
583 {
584     static RGBColor rgbColorOld;
585     GDHandle hGDSave;
586     CGrafPtr pCGrafSave;
587     Rect rectSource;
588     RGBColor rgbColor;
589     
590     //intf_ErrMsg( "FillOffscreen" );
591
592     GetPortBounds( p_vout->p_sys->p_gw[index], &rectSource );
593     
594     do 
595         rgbColor.red = ( Random () + 32767) / 2 + 32767;
596     while ( abs ( rgbColor.red - rgbColorOld.red ) <  3000 );   
597     do 
598         rgbColor.green = (Random () + 32767) / 2 + 32767;
599     while ( abs ( rgbColor.green - rgbColorOld.green ) <  3000);
600     do 
601         rgbColor.blue = (Random () + 32767) / 2 + 32767;
602     while ( abs ( rgbColor.blue - rgbColorOld.blue ) <  3000);
603     
604     rgbColorOld = rgbColor;
605
606     GetGWorld( &pCGrafSave, &hGDSave );
607     SetGWorld( p_vout->p_sys->p_gw[index], NULL );
608 //FIXME have global lock - kinda bad but oh well 
609 //    if ( LockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) ) )
610 //    {
611         // draw some background
612         EraseRect( &rectSource );
613         RGBForeColor( &rgbColor );
614         PaintRect( &rectSource );
615 //        UnlockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
616 //    }
617     SetGWorld( pCGrafSave, hGDSave );
618 }