1 /*****************************************************************************
2 * vout_macosx.c: MacOS X video output plugin
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
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 #define MODULE_NAME macosx
24 #include "modules_inner.h"
26 /*****************************************************************************
28 *****************************************************************************/
31 #include <errno.h> /* ENOMEM */
32 #include <stdlib.h> /* free() */
33 #include <string.h> /* strerror() */
42 #include "video_output.h"
50 #ifndef __CARBONPREFIX__
51 #define __CARBONPREFIX__
53 // Needed for carbonization
54 #define TARGET_API_MAC_CARBON 1
56 // For the pascal to C or C to pascal string conversions in carbon
60 #include <Carbon/Carbon.h>
62 // Initial Window Constants
68 // where is the off screen
77 /*****************************************************************************
78 * vout_sys_t: MacOS X video output method descriptor
79 *****************************************************************************
80 * This structure is part of the video output thread descriptor.
81 * It describes the MacOS X specific properties of an output thread.
82 *****************************************************************************/
83 typedef struct vout_sys_s
85 /* MacOS X video memory */
86 byte_t * p_video; /* base adress */
87 size_t i_page_size; /* page size */
93 Boolean gNewNewGWorld; /* can we allocate in VRAm or AGP memory ? */
104 /*****************************************************************************
106 *****************************************************************************/
107 static int vout_Probe ( probedata_t *p_data );
108 static int vout_Create ( struct vout_thread_s * );
109 static int vout_Init ( struct vout_thread_s * );
110 static void vout_End ( struct vout_thread_s * );
111 static void vout_Destroy ( struct vout_thread_s * );
112 static int vout_Manage ( struct vout_thread_s * );
113 static void vout_Display ( struct vout_thread_s * );
117 static int CreateDisplay ( struct vout_thread_s * );
118 static int MakeWindow ( struct vout_thread_s * );
119 static int AllocBuffer ( struct vout_thread_s * , short index );
121 void BlitToWindow ( struct vout_thread_s * , short index );
122 GDHandle GetWindowDevice ( struct vout_thread_s * );
123 void FillOffscreen ( struct vout_thread_s * , short index);
125 void FindBestMemoryLocation( struct vout_thread_s * );
127 /*****************************************************************************
128 * Functions exported as capabilities. They are declared as static so that
129 * we don't pollute the namespace too much.
130 *****************************************************************************/
131 void _M( vout_getfunctions )( function_list_t * p_function_list )
133 p_function_list->pf_probe = vout_Probe;
134 p_function_list->functions.vout.pf_create = vout_Create;
135 p_function_list->functions.vout.pf_init = vout_Init;
136 p_function_list->functions.vout.pf_end = vout_End;
137 p_function_list->functions.vout.pf_destroy = vout_Destroy;
138 p_function_list->functions.vout.pf_manage = vout_Manage;
139 p_function_list->functions.vout.pf_display = vout_Display;
140 p_function_list->functions.vout.pf_setpalette = NULL;
143 /*****************************************************************************
144 * intf_Probe: return a score
145 *****************************************************************************/
146 static int vout_Probe( probedata_t *p_data )
148 if( TestMethod( VOUT_METHOD_VAR, "macosx" ) )
156 /*****************************************************************************
157 * vout_Create: allocates MacOS X video thread output method
158 *****************************************************************************
159 * This function allocates and initializes a MacOS X vout method.
160 *****************************************************************************/
161 static int vout_Create( vout_thread_t *p_vout )
163 //intf_ErrMsg( "vout_Create()" );
165 /* Allocate structure */
166 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
167 if( p_vout->p_sys == NULL )
169 intf_ErrMsg( "error: %s", strerror( ENOMEM ) );
173 p_vout->p_sys->gwLocOffscreen = kNoWhere;
174 p_vout->p_sys->p_window = NULL;
175 p_vout->p_sys->p_gw[ 0 ] = NULL;
176 p_vout->p_sys->p_gw[ 1 ] = NULL;
177 p_vout->p_sys->i_page_size = p_vout->i_width * p_vout->i_height
178 * p_vout->i_bytes_per_pixel;
180 if ( CreateDisplay( p_vout ) )
182 intf_ErrMsg( "vout error: can't open display" );
183 free( p_vout->p_sys );
188 intf_ErrMsg( "vout p_vout->i_width %d" , p_vout->i_width);
189 intf_ErrMsg( "vout p_vout->i_height %d" , p_vout->i_height);
190 intf_ErrMsg( "vout p_vout->i_bytes_per_pixel %d" , p_vout->i_bytes_per_pixel);
191 intf_ErrMsg( "vout p_vout->i_screen_depth %d" , p_vout->i_screen_depth);
192 intf_ErrMsg( "vout p_vout->p_sys->i_page_size %d" , p_vout->p_sys->i_page_size);
196 /* Map two framebuffers a the very beginning of the fb */
197 p_vout->p_sys->p_video = malloc( 2 * p_vout->p_sys->i_page_size );
198 if( p_vout->p_sys->p_video == NULL )
200 intf_ErrMsg( "vout error: can't map video memory (%s)",
202 free( p_vout->p_sys );
205 /* Set and initialize buffers */
206 vout_SetBuffers( p_vout, p_vout->p_sys->p_video,
207 p_vout->p_sys->p_video + p_vout->p_sys->i_page_size );
213 /*****************************************************************************
214 * Find the best memory (AGP, VRAM, system) location
215 *****************************************************************************/
216 void FindBestMemoryLocation( vout_thread_t *p_vout )
220 Gestalt( gestaltSystemVersion, &versionSystem );
221 if ( 0x00000900 <= ( versionSystem & 0x00000FF00 ) )
223 intf_ErrMsg( "FindBestMemoryLocation : gNewNewGWorld = true" );
224 p_vout->p_sys->gNewNewGWorld = true;
229 // we will try to allocate in VRAM and find out where the allocation really ended up.
230 GWorldPtr pgwTest = NULL;
231 Rect rectTest = {0, 0, 10, 10};
233 (**(GetPortPixMap( GetWindowPort( p_vout->p_sys->p_window ) ))).pixelSize;
234 GDHandle hgdWindow = GetWindowDevice( p_vout );
236 intf_ErrMsg( "FindBestMemoryLocation : gNewNewGWorld = false !" );
238 p_vout->i_screen_depth = wPixDepth;
239 p_vout->i_bytes_per_pixel = wPixDepth;
240 p_vout->i_bytes_per_line = p_vout->i_width * p_vout->i_bytes_per_pixel;
241 p_vout->p_sys->i_page_size = p_vout->i_width * p_vout->i_height * p_vout->i_bytes_per_pixel;
242 //p_vout->i_bytes_per_line = (**(**hgdWindow).gdPMap).rowBytes & 0x3FFF ;
244 if( ( noErr == NewGWorld( &pgwTest, wPixDepth, &rectTest, NULL, hgdWindow,
245 noNewDevice | useDistantHdwrMem ) )
248 p_vout->p_sys->gNewNewGWorld = true;
253 DisposeGWorld( pgwTest );
258 /*****************************************************************************
259 * CreateDisplay: setup display params...
260 *****************************************************************************/
261 static int CreateDisplay( vout_thread_t *p_vout )
263 PixMapHandle hPixmap0, hPixmap1;
264 void * hPixmapBaseAddr0, * hPixmapBaseAddr1;
266 //intf_ErrMsg( "CreateDisplay()" );
268 if( MakeWindow( p_vout ) )
270 intf_ErrMsg( "vout error: can't open window display" );
274 // FindBestMemoryLocation( p_vout );
276 //try to allocate @ best location, will eventaully trickle down to worst
277 p_vout->p_sys->gwLocOffscreen = kInVRAM;
278 if( AllocBuffer( p_vout, 0 ) || AllocBuffer( p_vout, 1 ) )
280 intf_ErrMsg( "vout error: can't alloc offscreen buffers" );
284 //FIXME ? - lock this down until the end...
285 hPixmap0 = GetGWorldPixMap( p_vout->p_sys->p_gw[0] );
286 LockPixels(hPixmap0);
287 hPixmap1 = GetGWorldPixMap( p_vout->p_sys->p_gw[1] );
288 LockPixels(hPixmap1);
290 //FIXME hopefully this is the same for all Gworlds & window since they are the same size
291 p_vout->i_bytes_per_line = (**hPixmap0).rowBytes & 0x3FFF;
293 if ( (hPixmap0 == NULL) || (hPixmap1 == NULL) )
295 intf_ErrMsg( "vout error: pixmap problem");
296 UnlockPixels(hPixmap0);
297 UnlockPixels(hPixmap1);
301 hPixmapBaseAddr0 = GetPixBaseAddr( hPixmap0 );
302 hPixmapBaseAddr1 = GetPixBaseAddr( hPixmap1 );
303 if ( (hPixmapBaseAddr0 == NULL) || (hPixmapBaseAddr1 == NULL) )
305 intf_ErrMsg( "vout error: pixmap base addr problem");
309 //FIXME - if I ever dispose of the Gworlds and recreate them, i'll have a new address
310 //and I'll need to tell vout about them... dunno what problems vout might have if we just updateGworld
311 vout_SetBuffers( p_vout, hPixmapBaseAddr0, hPixmapBaseAddr1 );
316 /*****************************************************************************
317 * MakeWindow: open and set-up a Mac OS main window
318 *****************************************************************************/
319 static int MakeWindow( vout_thread_t *p_vout )
323 int bottom = p_vout->i_height;
324 int right = p_vout->i_width;
326 WindowAttributes windowAttr = kWindowStandardDocumentAttributes |
327 kWindowStandardHandlerAttribute |
328 kWindowInWindowMenuAttribute;
330 SetRect( &p_vout->p_sys->wrect, left, top, right, bottom );
331 OffsetRect( &p_vout->p_sys->wrect, kWindowOffset, kWindowOffset );
333 CreateNewWindow( kDocumentWindowClass, windowAttr, &p_vout->p_sys->wrect, &p_vout->p_sys->p_window );
334 if ( p_vout->p_sys->p_window == nil )
339 InstallStandardEventHandler(GetWindowEventTarget(p_vout->p_sys->p_window));
340 SetPort( GetWindowPort( p_vout->p_sys->p_window ) );
341 SetWindowTitleWithCFString( p_vout->p_sys->p_window, CFSTR("VLC") );
342 // ShowWindow( p_vout->p_sys->p_window );
343 TransitionWindow( p_vout->p_sys->p_window, kWindowZoomTransitionEffect, kWindowShowTransitionAction, NULL);
344 BringToFront( p_vout->p_sys->p_window );
347 short wPixDepth = (**(GetPortPixMap( GetWindowPort( p_vout->p_sys->p_window ) ))).pixelSize;
348 p_vout->i_screen_depth = wPixDepth;
349 p_vout->i_bytes_per_pixel = p_vout->i_screen_depth / 8;
350 p_vout->i_bytes_per_line = p_vout->i_width * p_vout->i_bytes_per_pixel;
351 p_vout->p_sys->i_page_size = p_vout->i_width * p_vout->i_height * p_vout->i_bytes_per_pixel;
353 p_vout->i_bytes_per_line = (**(**GetWindowDevice( p_vout )).gdPMap).rowBytes & 0x3FFF ;
355 switch ( p_vout->i_screen_depth )
359 p_vout->i_red_mask = 0xff0000;
360 p_vout->i_green_mask = 0xff00;
361 p_vout->i_blue_mask = 0xff;
365 p_vout->i_red_mask = 0x00007c00;
366 p_vout->i_green_mask = 0x000003e0;
367 p_vout->i_blue_mask = 0x0000001f;
374 p_vout->i_red_lshift = 0x10;
375 p_vout->i_red_rshift = 0x0;
376 p_vout->i_green_lshift = 0x8;
377 p_vout->i_green_rshift = 0x0;
378 p_vout->i_blue_lshift = 0x0;
379 p_vout->i_blue_rshift = 0x0;
381 p_vout->i_white_pixel = 0xffffff;
382 p_vout->i_black_pixel = 0x0;
383 p_vout->i_gray_pixel = 0x808080;
384 p_vout->i_blue_pixel = 0x32;
391 /*****************************************************************************
392 * AllocBuffer: forces offscreen allocation (if different than current) in memory type specified
393 *****************************************************************************/
394 static int AllocBuffer ( vout_thread_t *p_vout, short index )
397 GDHandle hgdWindow = GetWindowDevice( p_vout );
399 switch ( p_vout->p_sys->gwLocOffscreen )
402 if ( noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth,
403 GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL,
404 hgdWindow, noNewDevice | useDistantHdwrMem ) )
406 intf_ErrMsg( "Allocate off screen image in VRAM" );
409 intf_ErrMsg( "Unable to allocate off screen image in VRAM, trying next best AGP" );
410 p_vout->p_sys->gwLocOffscreen = kInAGP;
412 if (noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth,
413 GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL,
414 hgdWindow, noNewDevice | useLocalHdwrMem ) )
416 intf_ErrMsg( "Allocate off screen image in AGP" );
419 intf_ErrMsg( "Unable to allocate off screen image in AGP, trying next best System" );
420 p_vout->p_sys->gwLocOffscreen = kInSystem;
423 if ( noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth,
424 GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL,
425 hgdWindow, noNewDevice | keepLocal) )
427 intf_ErrMsg( "Allocate off screen image in System" );
430 intf_ErrMsg( "Unable to allocate off screen image in System, no options left - failing" );
431 p_vout->p_sys->gwLocOffscreen = kNoWhere;
432 return( 1 ); // nothing was allocated
437 /*****************************************************************************
438 * vout_Init: initialize video thread output method
439 *****************************************************************************/
440 static int vout_Init( vout_thread_t *p_vout )
442 //intf_ErrMsg( "vout_Init()" );
446 /*****************************************************************************
447 * vout_End: terminate video thread output method
448 *****************************************************************************/
449 static void vout_End( vout_thread_t *p_vout )
451 //intf_ErrMsg( "vout_End()" );
455 /*****************************************************************************
456 * vout_Destroy: destroy video thread output method
457 *****************************************************************************/
458 static void vout_Destroy( vout_thread_t *p_vout )
460 //intf_ErrMsg( "vout_Destroy()" );
462 //FIXME KLUDGE to lock pixels
465 PixMapHandle hPixmap0, hPixmap1;
466 hPixmap0 = GetGWorldPixMap( p_vout->p_sys->p_gw[0] );
467 hPixmap1 = GetGWorldPixMap( p_vout->p_sys->p_gw[1] );
468 UnlockPixels(hPixmap0);
469 UnlockPixels(hPixmap1);
474 if ( p_vout->p_sys->p_gw[0] )
476 DisposeGWorld( p_vout->p_sys->p_gw[0] );
478 if ( p_vout->p_sys->p_gw[1] )
480 DisposeGWorld( p_vout->p_sys->p_gw[1] );
482 if ( p_vout->p_sys->p_window )
484 DisposeWindow( p_vout->p_sys->p_window );
487 free( p_vout->p_sys->p_video );
488 free( p_vout->p_sys );
491 /*****************************************************************************
492 * vout_Manage: handle events
493 *****************************************************************************
494 * This function should be called regularly by video output thread. It manages
495 * console events. It returns a non null value on error.
496 *****************************************************************************/
497 static int vout_Manage( vout_thread_t *p_vout )
499 // intf_ErrMsg( "vout_Manage()" );
503 /*****************************************************************************
504 * vout_Display: displays previously rendered output
505 *****************************************************************************
506 * This function send the currently rendered image to image, waits until
507 * it is displayed and switch the two rendering buffers, preparing next frame.
508 *****************************************************************************/
509 static void vout_Display( vout_thread_t *p_vout )
511 // intf_ErrMsg( "vout_Display()" );
513 BlitToWindow ( p_vout, p_vout->i_buffer_index );
517 /*****************************************************************************
518 * flushQD: flushes buffered window area
519 *****************************************************************************/
520 void flushQD( vout_thread_t *p_vout )
524 //intf_ErrMsg( "flushQD()" );
526 thePort = GetWindowPort( p_vout->p_sys->p_window );
528 /* flush the entire port */
529 if (QDIsPortBuffered(thePort))
530 QDFlushPortBuffer(thePort, NULL);
533 /* flush part of the port */
534 if (QDIsPortBuffered(thePort)) {
537 /* local port coordinates */
538 SetRectRgn(theRgn, 10, 10, 100, 30);
539 QDFlushPortBuffer(thePort, theRgn);
546 /*****************************************************************************
547 * BlitToWindow: checks offscreen and blits it to the front
548 *****************************************************************************/
550 void BlitToWindow( vout_thread_t *p_vout, short index )
552 Rect rectDest, rectSource;
553 GrafPtr pCGrafSave, windowPort = GetWindowPort( p_vout->p_sys->p_window );
555 //intf_ErrMsg( "BlitToWindow() for %d", index );
557 GetPortBounds( p_vout->p_sys->p_gw[index], &rectSource );
558 GetPortBounds( windowPort, &rectDest );
560 GetPort ( &pCGrafSave );
561 SetPortWindowPort( p_vout->p_sys->p_window );
562 //FIXME have global lock - kinda bad but oh well
563 // if ( LockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) ) )
565 CopyBits( GetPortBitMapForCopyBits( p_vout->p_sys->p_gw[index] ),
566 GetPortBitMapForCopyBits( GetWindowPort( p_vout->p_sys->p_window ) ),
567 &rectSource, &rectDest, srcCopy, NULL);
568 // UnlockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
571 SetPort ( pCGrafSave );
575 /*****************************************************************************
576 * GetWindowDevice: returns GDHandle that window resides on (most of it anyway)
577 *****************************************************************************/
578 GDHandle GetWindowDevice( vout_thread_t *p_vout )
581 Rect rectWind, rectSect;
582 long greatestArea, sectArea;
583 GDHandle hgdNthDevice, hgdZoomOnThisDevice = NULL;
585 //intf_ErrMsg( "GetWindowDevice()" );
588 SetPortWindowPort( p_vout->p_sys->p_window );
589 GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &rectWind );
590 LocalToGlobal( ( Point* ) &rectWind.top );
591 LocalToGlobal( ( Point* ) &rectWind.bottom );
592 hgdNthDevice = GetDeviceList();
594 // check window against all gdRects in gDevice list and remember
595 // which gdRect contains largest area of window}
596 while ( hgdNthDevice )
598 if ( TestDeviceAttribute( hgdNthDevice, screenDevice ) )
600 if ( TestDeviceAttribute( hgdNthDevice, screenActive ) )
602 // The SectRect routine calculates the intersection
603 // of the window rectangle and this gDevice
604 // rectangle and returns TRUE if the rectangles intersect,
605 // FALSE if they don't.
606 SectRect( &rectWind, &( **hgdNthDevice ).gdRect, &rectSect );
607 // determine which screen holds greatest window area
608 // first, calculate area of rectangle on current device
609 sectArea = ( long )( rectSect.right - rectSect.left ) * ( rectSect.bottom - rectSect.top );
610 if ( sectArea > greatestArea )
612 greatestArea = sectArea; // set greatest area so far
613 hgdZoomOnThisDevice = hgdNthDevice; // set zoom device
615 hgdNthDevice = GetNextDevice( hgdNthDevice );
620 return hgdZoomOnThisDevice;
623 /*****************************************************************************
624 * FillOffScreen: fills offscreen buffer with random bright color
625 *****************************************************************************/
627 void FillOffscreen( vout_thread_t *p_vout, short index )
629 static RGBColor rgbColorOld;
635 //intf_ErrMsg( "FillOffscreen" );
637 GetPortBounds( p_vout->p_sys->p_gw[index], &rectSource );
640 rgbColor.red = ( Random () + 32767) / 2 + 32767;
641 while ( abs ( rgbColor.red - rgbColorOld.red ) < 3000 );
643 rgbColor.green = (Random () + 32767) / 2 + 32767;
644 while ( abs ( rgbColor.green - rgbColorOld.green ) < 3000);
646 rgbColor.blue = (Random () + 32767) / 2 + 32767;
647 while ( abs ( rgbColor.blue - rgbColorOld.blue ) < 3000);
649 rgbColorOld = rgbColor;
651 GetGWorld( &pCGrafSave, &hGDSave );
652 SetGWorld( p_vout->p_sys->p_gw[index], NULL );
653 //FIXME have global lock - kinda bad but oh well
654 // if ( LockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) ) )
656 // draw some background
657 EraseRect( &rectSource );
658 RGBForeColor( &rgbColor );
659 PaintRect( &rectSource );
660 // UnlockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
662 SetGWorld( pCGrafSave, hGDSave );