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