1 /*****************************************************************************
2 * vout_macosx.c: MacOS X video output plugin
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
6 * Authors: Colin Delacroix <colin@zoy.org>
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.
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.
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 *****************************************************************************/
23 #include "modules_inner.h"
25 /*****************************************************************************
27 *****************************************************************************/
30 #include <errno.h> /* ENOMEM */
31 #include <stdlib.h> /* free() */
32 #include <string.h> /* strerror() */
43 #include "video_output.h"
48 #include "macosx_common.h"
51 /*****************************************************************************
53 *****************************************************************************/
55 // Initial Window Constants
61 // where is the off screen
71 /*****************************************************************************
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 * );
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 );
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);
92 void FindBestMemoryLocation( struct vout_thread_s * );
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 )
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;
110 /*****************************************************************************
111 * intf_Probe: return a score
112 *****************************************************************************/
113 static int vout_Probe( probedata_t *p_data )
115 if( TestMethod( VOUT_METHOD_VAR, "macosx" ) )
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 )
130 //intf_ErrMsg( "vout_Create()" );
132 /* Allocate structure */
133 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
134 if( p_vout->p_sys == NULL )
136 intf_ErrMsg( "error: %s", strerror( ENOMEM ) );
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;
145 if ( CreateDisplay( p_vout ) )
147 intf_ErrMsg( "vout error: can't open display" );
148 free( p_vout->p_sys );
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);
162 /*****************************************************************************
163 * Find the best memory (AGP, VRAM, system) location
164 *****************************************************************************/
165 void FindBestMemoryLocation( vout_thread_t *p_vout )
169 Gestalt( gestaltSystemVersion, &versionSystem );
170 if ( 0x00000900 <= ( versionSystem & 0x00000FF00 ) )
172 intf_ErrMsg( "FindBestMemoryLocation : gNewNewGWorld = true" );
173 p_vout->p_sys->gNewNewGWorld = true;
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};
182 (**(GetPortPixMap( GetWindowPort( p_vout->p_sys->p_window ) ))).pixelSize;
183 GDHandle hgdWindow = GetWindowDevice( p_vout );
185 intf_ErrMsg( "FindBestMemoryLocation : gNewNewGWorld = false !" );
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 ;
191 if( ( noErr == NewGWorld( &pgwTest, wPixDepth, &rectTest, NULL, hgdWindow,
192 noNewDevice | useDistantHdwrMem ) )
195 p_vout->p_sys->gNewNewGWorld = true;
200 DisposeGWorld( pgwTest );
205 /*****************************************************************************
206 * CreateDisplay: setup display params...
207 *****************************************************************************/
208 static int CreateDisplay( vout_thread_t *p_vout )
210 PixMapHandle hPixmap0, hPixmap1;
211 void * hPixmapBaseAddr0, * hPixmapBaseAddr1;
213 //intf_ErrMsg( "CreateDisplay()" );
215 if( MakeWindow( p_vout ) )
217 intf_ErrMsg( "vout error: can't open window display" );
221 // FindBestMemoryLocation( p_vout );
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 ) )
227 intf_ErrMsg( "vout error: can't alloc offscreen buffers" );
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);
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;
240 if ( (hPixmap0 == NULL) || (hPixmap1 == NULL) )
242 intf_ErrMsg( "vout error: pixmap problem");
243 UnlockPixels(hPixmap0);
244 UnlockPixels(hPixmap1);
248 hPixmapBaseAddr0 = GetPixBaseAddr( hPixmap0 );
249 hPixmapBaseAddr1 = GetPixBaseAddr( hPixmap1 );
250 if ( (hPixmapBaseAddr0 == NULL) || (hPixmapBaseAddr1 == NULL) )
252 intf_ErrMsg( "vout error: pixmap base addr problem");
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 );
263 /*****************************************************************************
264 * MakeWindow: open and set-up a Mac OS main window
265 *****************************************************************************/
266 static int MakeWindow( vout_thread_t *p_vout )
270 int bottom = p_vout->i_height;
271 int right = p_vout->i_width;
272 ProcessSerialNumber PSN;
274 WindowAttributes windowAttr = kWindowStandardDocumentAttributes |
275 kWindowStandardHandlerAttribute |
276 kWindowInWindowMenuAttribute;
278 SetRect( &p_vout->p_sys->wrect, left, top, right, bottom );
279 OffsetRect( &p_vout->p_sys->wrect, kWindowOffset, kWindowOffset );
281 CreateNewWindow( kDocumentWindowClass, windowAttr, &p_vout->p_sys->wrect, &p_vout->p_sys->p_window );
282 if ( p_vout->p_sys->p_window == nil )
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 );
293 //in case we are run from the command line, bring us to front instead of Terminal
294 GetCurrentProcess(&PSN);
295 SetFrontProcess(&PSN);
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;
303 p_vout->i_bytes_per_line = (**(**GetWindowDevice( p_vout )).gdPMap).rowBytes & 0x3FFF ;
305 switch ( p_vout->i_screen_depth )
309 p_vout->i_red_mask = 0xff0000;
310 p_vout->i_green_mask = 0xff00;
311 p_vout->i_blue_mask = 0xff;
315 p_vout->i_red_mask = 0x00007c00;
316 p_vout->i_green_mask = 0x000003e0;
317 p_vout->i_blue_mask = 0x0000001f;
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;
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;
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 )
348 GDHandle hgdWindow = GetWindowDevice( p_vout );
350 switch ( p_vout->p_sys->gwLocOffscreen )
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 ) )
357 intf_ErrMsg( "Allocate off screen image in VRAM" );
360 intf_ErrMsg( "Unable to allocate off screen image in VRAM, trying next best AGP" );
361 p_vout->p_sys->gwLocOffscreen = 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 ) )
367 intf_ErrMsg( "Allocate off screen image in AGP" );
370 intf_ErrMsg( "Unable to allocate off screen image in AGP, trying next best System" );
371 p_vout->p_sys->gwLocOffscreen = kInSystem;
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) )
378 intf_ErrMsg( "Allocate off screen image in System" );
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
388 /*****************************************************************************
389 * vout_Init: initialize video thread output method
390 *****************************************************************************/
391 static int vout_Init( vout_thread_t *p_vout )
393 //intf_ErrMsg( "vout_Init()" );
397 /*****************************************************************************
398 * vout_End: terminate video thread output method
399 *****************************************************************************/
400 static void vout_End( vout_thread_t *p_vout )
402 //intf_ErrMsg( "vout_End()" );
406 /*****************************************************************************
407 * vout_Destroy: destroy video thread output method
408 *****************************************************************************/
409 static void vout_Destroy( vout_thread_t *p_vout )
411 //intf_ErrMsg( "vout_Destroy()" );
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);
420 if ( p_vout->p_sys->p_gw[0] )
422 DisposeGWorld( p_vout->p_sys->p_gw[0] );
424 if ( p_vout->p_sys->p_gw[1] )
426 DisposeGWorld( p_vout->p_sys->p_gw[1] );
428 if ( p_vout->p_sys->p_window )
430 DisposeWindow( p_vout->p_sys->p_window );
433 free( p_vout->p_sys );
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 )
444 // intf_ErrMsg( "vout_Manage()" );
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 )
456 // intf_ErrMsg( "vout_Display()" );
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 );
464 /*****************************************************************************
465 * flushQD: flushes buffered window area
466 *****************************************************************************/
467 void flushQD( vout_thread_t *p_vout )
471 //intf_ErrMsg( "flushQD()" );
473 thePort = GetWindowPort( p_vout->p_sys->p_window );
475 /* flush the entire port */
476 if (QDIsPortBuffered(thePort))
477 QDFlushPortBuffer(thePort, NULL);
480 /* flush part of the port */
481 if (QDIsPortBuffered(thePort)) {
484 /* local port coordinates */
485 SetRectRgn(theRgn, 10, 10, 100, 30);
486 QDFlushPortBuffer(thePort, theRgn);
493 /*****************************************************************************
494 * BlitToWindow: checks offscreen and blits it to the front
495 *****************************************************************************/
497 void BlitToWindow( vout_thread_t *p_vout, short index )
499 Rect rectDest, rectSource;
500 GrafPtr pCGrafSave, windowPort = GetWindowPort( p_vout->p_sys->p_window );
502 //intf_ErrMsg( "BlitToWindow() for %d", index );
504 GetPortBounds( p_vout->p_sys->p_gw[index], &rectSource );
505 GetPortBounds( windowPort, &rectDest );
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] ) ) )
513 //LockPortBits(GetWindowPort( p_vout->p_sys->p_window ));
514 //NoPurgePixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
516 CopyBits( GetPortBitMapForCopyBits( p_vout->p_sys->p_gw[index] ),
517 GetPortBitMapForCopyBits( GetWindowPort( p_vout->p_sys->p_window ) ),
518 &rectSource, &rectDest, srcCopy, NULL);
520 //UnlockPortBits(GetWindowPort( p_vout->p_sys->p_window ));
521 //AllowPurgePixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
523 // UnlockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
526 SetPort ( pCGrafSave );
530 /*****************************************************************************
531 * GetWindowDevice: returns GDHandle that window resides on (most of it anyway)
532 *****************************************************************************/
533 GDHandle GetWindowDevice( vout_thread_t *p_vout )
536 Rect rectWind, rectSect;
537 long greatestArea, sectArea;
538 GDHandle hgdNthDevice, hgdZoomOnThisDevice = NULL;
540 //intf_ErrMsg( "GetWindowDevice()" );
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();
549 // check window against all gdRects in gDevice list and remember
550 // which gdRect contains largest area of window}
551 while ( hgdNthDevice )
553 if ( TestDeviceAttribute( hgdNthDevice, screenDevice ) )
555 if ( TestDeviceAttribute( hgdNthDevice, screenActive ) )
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 )
567 greatestArea = sectArea; // set greatest area so far
568 hgdZoomOnThisDevice = hgdNthDevice; // set zoom device
570 hgdNthDevice = GetNextDevice( hgdNthDevice );
575 return hgdZoomOnThisDevice;
578 /*****************************************************************************
579 * FillOffScreen: fills offscreen buffer with random bright color
580 *****************************************************************************/
582 void FillOffscreen( vout_thread_t *p_vout, short index )
584 static RGBColor rgbColorOld;
590 //intf_ErrMsg( "FillOffscreen" );
592 GetPortBounds( p_vout->p_sys->p_gw[index], &rectSource );
595 rgbColor.red = ( Random () + 32767) / 2 + 32767;
596 while ( abs ( rgbColor.red - rgbColorOld.red ) < 3000 );
598 rgbColor.green = (Random () + 32767) / 2 + 32767;
599 while ( abs ( rgbColor.green - rgbColorOld.green ) < 3000);
601 rgbColor.blue = (Random () + 32767) / 2 + 32767;
602 while ( abs ( rgbColor.blue - rgbColorOld.blue ) < 3000);
604 rgbColorOld = rgbColor;
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] ) ) )
611 // draw some background
612 EraseRect( &rectSource );
613 RGBForeColor( &rgbColor );
614 PaintRect( &rectSource );
615 // UnlockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
617 SetGWorld( pCGrafSave, hGDSave );