1 /*****************************************************************************
2 * vout_macosx.c: MacOS X video output plugin
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
6 * Authors: Colin Delacroix <colin@zoy.org>
7 * Eugenio Jarosiewicz <ej0@cise.ufl.edu>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 #define MODULE_NAME macosx
25 #include "modules_inner.h"
27 /*****************************************************************************
29 *****************************************************************************/
32 #include <errno.h> /* ENOMEM */
33 #include <stdlib.h> /* free() */
34 #include <string.h> /* strerror() */
43 #include "video_output.h"
51 #ifndef __CARBONPREFIX__
52 #define __CARBONPREFIX__
54 // Needed for carbonization
55 #define TARGET_API_MAC_CARBON 1
57 // For the pascal to C or C to pascal string conversions in carbon
61 #include <Carbon/Carbon.h>
63 // Initial Window Constants
69 // where is the off screen
78 /*****************************************************************************
79 * vout_sys_t: MacOS X video output method descriptor
80 *****************************************************************************
81 * This structure is part of the video output thread descriptor.
82 * It describes the MacOS X specific properties of an output thread.
83 *****************************************************************************/
84 typedef struct vout_sys_s
86 /* MacOS X video memory */
87 byte_t * p_video; /* base adress */
88 size_t i_page_size; /* page size */
94 Boolean gNewNewGWorld; /* can we allocate in VRAm or AGP memory ? */
105 /*****************************************************************************
107 *****************************************************************************/
108 static int vout_Probe ( probedata_t *p_data );
109 static int vout_Create ( struct vout_thread_s * );
110 static int vout_Init ( struct vout_thread_s * );
111 static void vout_End ( struct vout_thread_s * );
112 static void vout_Destroy ( struct vout_thread_s * );
113 static int vout_Manage ( struct vout_thread_s * );
114 static void vout_Display ( struct vout_thread_s * );
118 static int CreateDisplay ( struct vout_thread_s * );
119 static int MakeWindow ( struct vout_thread_s * );
120 static int AllocBuffer ( struct vout_thread_s * , short index );
122 void BlitToWindow ( struct vout_thread_s * , short index );
123 GDHandle GetWindowDevice ( struct vout_thread_s * );
124 void FillOffscreen ( struct vout_thread_s * , short index);
126 void FindBestMemoryLocation( struct vout_thread_s * );
128 /*****************************************************************************
129 * Functions exported as capabilities. They are declared as static so that
130 * we don't pollute the namespace too much.
131 *****************************************************************************/
132 void _M( vout_getfunctions )( function_list_t * p_function_list )
134 p_function_list->pf_probe = vout_Probe;
135 p_function_list->functions.vout.pf_create = vout_Create;
136 p_function_list->functions.vout.pf_init = vout_Init;
137 p_function_list->functions.vout.pf_end = vout_End;
138 p_function_list->functions.vout.pf_destroy = vout_Destroy;
139 p_function_list->functions.vout.pf_manage = vout_Manage;
140 p_function_list->functions.vout.pf_display = vout_Display;
141 p_function_list->functions.vout.pf_setpalette = NULL;
144 /*****************************************************************************
145 * intf_Probe: return a score
146 *****************************************************************************/
147 static int vout_Probe( probedata_t *p_data )
149 if( TestMethod( VOUT_METHOD_VAR, "macosx" ) )
157 /*****************************************************************************
158 * vout_Create: allocates MacOS X video thread output method
159 *****************************************************************************
160 * This function allocates and initializes a MacOS X vout method.
161 *****************************************************************************/
162 static int vout_Create( vout_thread_t *p_vout )
164 //intf_ErrMsg( "vout_Create()" );
166 /* Allocate structure */
167 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
168 if( p_vout->p_sys == NULL )
170 intf_ErrMsg( "error: %s", strerror( ENOMEM ) );
174 p_vout->p_sys->gwLocOffscreen = kNoWhere;
175 p_vout->p_sys->p_window = NULL;
176 p_vout->p_sys->p_gw[ 0 ] = NULL;
177 p_vout->p_sys->p_gw[ 1 ] = NULL;
178 p_vout->p_sys->i_page_size = p_vout->i_width * p_vout->i_height
179 * p_vout->i_bytes_per_pixel;
181 if ( CreateDisplay( p_vout ) )
183 intf_ErrMsg( "vout error: can't open display" );
184 free( p_vout->p_sys );
189 intf_ErrMsg( "vout p_vout->i_width %d" , p_vout->i_width);
190 intf_ErrMsg( "vout p_vout->i_height %d" , p_vout->i_height);
191 intf_ErrMsg( "vout p_vout->i_bytes_per_pixel %d" , p_vout->i_bytes_per_pixel);
192 intf_ErrMsg( "vout p_vout->i_screen_depth %d" , p_vout->i_screen_depth);
193 intf_ErrMsg( "vout p_vout->p_sys->i_page_size %d" , p_vout->p_sys->i_page_size);
197 /* Map two framebuffers a the very beginning of the fb */
198 p_vout->p_sys->p_video = malloc( 2 * p_vout->p_sys->i_page_size );
199 if( p_vout->p_sys->p_video == NULL )
201 intf_ErrMsg( "vout error: can't map video memory (%s)",
203 free( p_vout->p_sys );
206 /* Set and initialize buffers */
207 vout_SetBuffers( p_vout, p_vout->p_sys->p_video,
208 p_vout->p_sys->p_video + p_vout->p_sys->i_page_size );
214 /*****************************************************************************
215 * Find the best memory (AGP, VRAM, system) location
216 *****************************************************************************/
217 void FindBestMemoryLocation( vout_thread_t *p_vout )
221 Gestalt( gestaltSystemVersion, &versionSystem );
222 if ( 0x00000900 <= ( versionSystem & 0x00000FF00 ) )
224 intf_ErrMsg( "FindBestMemoryLocation : gNewNewGWorld = true" );
225 p_vout->p_sys->gNewNewGWorld = true;
230 // we will try to allocate in VRAM and find out where the allocation really ended up.
231 GWorldPtr pgwTest = NULL;
232 Rect rectTest = {0, 0, 10, 10};
234 (**(GetPortPixMap( GetWindowPort( p_vout->p_sys->p_window ) ))).pixelSize;
235 GDHandle hgdWindow = GetWindowDevice( p_vout );
237 intf_ErrMsg( "FindBestMemoryLocation : gNewNewGWorld = false !" );
240 p_vout->i_screen_depth = wPixDepth;
241 p_vout->i_bytes_per_pixel = wPixDepth;
242 p_vout->i_bytes_per_line = p_vout->i_width * p_vout->i_bytes_per_pixel;
243 p_vout->p_sys->i_page_size = p_vout->i_width * p_vout->i_height * p_vout->i_bytes_per_pixel;
244 //p_vout->i_bytes_per_line = (**(**hgdWindow).gdPMap).rowBytes & 0x3FFF ;
246 if( ( noErr == NewGWorld( &pgwTest, wPixDepth, &rectTest, NULL, hgdWindow,
247 noNewDevice | useDistantHdwrMem ) )
250 p_vout->p_sys->gNewNewGWorld = true;
255 DisposeGWorld( pgwTest );
260 /*****************************************************************************
261 * CreateDisplay: setup display params...
262 *****************************************************************************/
263 static int CreateDisplay( vout_thread_t *p_vout )
265 PixMapHandle hPixmap0, hPixmap1;
266 void * hPixmapBaseAddr0, * hPixmapBaseAddr1;
268 //intf_ErrMsg( "CreateDisplay()" );
270 if( MakeWindow( p_vout ) )
272 intf_ErrMsg( "vout error: can't open window display" );
276 // FindBestMemoryLocation( p_vout );
278 //try to allocate @ best location, will eventaully trickle down to worst
279 p_vout->p_sys->gwLocOffscreen = kInVRAM;
280 if( AllocBuffer( p_vout, 0 ) || AllocBuffer( p_vout, 1 ) )
282 intf_ErrMsg( "vout error: can't alloc offscreen buffers" );
286 hPixmap0 = GetGWorldPixMap( p_vout->p_sys->p_gw[0] );
287 //FIXME BIGTIME - in SDL they just lock this down until the end...KLUDGE
288 //but alas sounds good to me to try it.
289 //well fuck a duck it works.
290 LockPixels(hPixmap0);
291 hPixmap1 = GetGWorldPixMap( p_vout->p_sys->p_gw[1] );
292 LockPixels(hPixmap1);
294 //FIXME hopefully this is the same for all Gworlds & window since they are the same size
295 p_vout->i_bytes_per_line = (**hPixmap0).rowBytes & 0x3FFF;
297 if ( (hPixmap0 == NULL) || (hPixmap1 == NULL) )
299 intf_ErrMsg( "vout error: pixmap problem");
300 UnlockPixels(hPixmap0);
301 UnlockPixels(hPixmap1);
305 hPixmapBaseAddr0 = GetPixBaseAddr( hPixmap0 );
306 hPixmapBaseAddr1 = GetPixBaseAddr( hPixmap1 );
307 if ( (hPixmapBaseAddr0 == NULL) || (hPixmapBaseAddr1 == NULL) )
309 intf_ErrMsg( "vout error: pixmap base addr problem");
313 //FIXME TODO - if I ever dispose of the Gworlds and recreate them, i'll have a new address
314 //and I'll need to tell vout about them... dunno what problems vout might have if we just updateGworld
315 vout_SetBuffers( p_vout, hPixmapBaseAddr0, hPixmapBaseAddr1 );
320 /*****************************************************************************
321 * MakeWindow: open and set-up a Mac OS main window
322 *****************************************************************************/
323 static int MakeWindow( vout_thread_t *p_vout )
327 int bottom = p_vout->i_height;
328 int right = p_vout->i_width;
330 WindowAttributes windowAttr = kWindowStandardDocumentAttributes |
331 kWindowStandardHandlerAttribute |
332 kWindowInWindowMenuAttribute;
334 SetRect( &p_vout->p_sys->wrect, left, top, right, bottom );
335 OffsetRect( &p_vout->p_sys->wrect, kWindowOffset, kWindowOffset );
337 CreateNewWindow( kDocumentWindowClass, windowAttr, &p_vout->p_sys->wrect, &p_vout->p_sys->p_window );
338 if ( p_vout->p_sys->p_window == nil )
343 InstallStandardEventHandler(GetWindowEventTarget(p_vout->p_sys->p_window));
344 SetPort( GetWindowPort( p_vout->p_sys->p_window ) );
345 SetWindowTitleWithCFString( p_vout->p_sys->p_window, CFSTR("VLC") );
346 // ShowWindow( p_vout->p_sys->p_window );
347 TransitionWindow( p_vout->p_sys->p_window, kWindowZoomTransitionEffect, kWindowShowTransitionAction, NULL);
348 BringToFront( p_vout->p_sys->p_window );
351 short wPixDepth = (**(GetPortPixMap( GetWindowPort( p_vout->p_sys->p_window ) ))).pixelSize;
352 p_vout->i_screen_depth = wPixDepth;
353 p_vout->i_bytes_per_pixel = p_vout->i_screen_depth / 8;
354 p_vout->i_bytes_per_line = p_vout->i_width * p_vout->i_bytes_per_pixel;
355 p_vout->p_sys->i_page_size = p_vout->i_width * p_vout->i_height * p_vout->i_bytes_per_pixel;
359 p_vout->i_bytes_per_line = (**(**GetWindowDevice( p_vout )).gdPMap).rowBytes & 0x3FFF ;
361 switch ( p_vout->i_screen_depth )
365 p_vout->i_red_mask = 0xff0000;
366 p_vout->i_green_mask = 0xff00;
367 p_vout->i_blue_mask = 0xff;
371 p_vout->i_red_mask = 0x00007c00;
372 p_vout->i_green_mask = 0x000003e0;
373 p_vout->i_blue_mask = 0x0000001f;
380 //EJ - not sure about these...
382 p_vout->i_red_lshift = 0x10;
383 p_vout->i_red_rshift = 0x0;
384 p_vout->i_green_lshift = 0x8;
385 p_vout->i_green_rshift = 0x0;
386 p_vout->i_blue_lshift = 0x0;
387 p_vout->i_blue_rshift = 0x0;
389 p_vout->i_white_pixel = 0xffffff;
390 p_vout->i_black_pixel = 0x0;
391 p_vout->i_gray_pixel = 0x808080;
392 p_vout->i_blue_pixel = 0x32;
399 /*****************************************************************************
400 * AllocBuffer: forces offscreen allocation (if different than current) in memory type specified
401 *****************************************************************************/
402 static int AllocBuffer ( vout_thread_t *p_vout, short index )
405 GDHandle hgdWindow = GetWindowDevice( p_vout );
407 switch ( p_vout->p_sys->gwLocOffscreen )
410 if ( noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth,
411 GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL,
412 hgdWindow, noNewDevice | useDistantHdwrMem ) )
414 intf_ErrMsg( "Allocate off screen image in VRAM" );
417 intf_ErrMsg( "Unable to allocate off screen image in VRAM, trying next best AGP" );
418 p_vout->p_sys->gwLocOffscreen = kInAGP;
420 if (noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth,
421 GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL,
422 hgdWindow, noNewDevice | useLocalHdwrMem ) )
424 intf_ErrMsg( "Allocate off screen image in AGP" );
427 intf_ErrMsg( "Unable to allocate off screen image in AGP, trying next best System" );
428 p_vout->p_sys->gwLocOffscreen = kInSystem;
431 if ( noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth,
432 GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL,
433 hgdWindow, noNewDevice | keepLocal) )
435 intf_ErrMsg( "Allocate off screen image in System" );
438 intf_ErrMsg( "Unable to allocate off screen image in System, no options left - failing" );
439 p_vout->p_sys->gwLocOffscreen = kNoWhere;
440 return( 1 ); // nothing was allocated
445 /*****************************************************************************
446 * vout_Init: initialize video thread output method
447 *****************************************************************************/
448 static int vout_Init( vout_thread_t *p_vout )
450 //intf_ErrMsg( "vout_Init()" );
454 /*****************************************************************************
455 * vout_End: terminate video thread output method
456 *****************************************************************************/
457 static void vout_End( vout_thread_t *p_vout )
459 //intf_ErrMsg( "vout_End()" );
463 /*****************************************************************************
464 * vout_Destroy: destroy video thread output method
465 *****************************************************************************/
466 static void vout_Destroy( vout_thread_t *p_vout )
468 //intf_ErrMsg( "vout_Destroy()" );
470 //FIXME KLUDGE to lock pixels
473 PixMapHandle hPixmap0, hPixmap1;
474 hPixmap0 = GetGWorldPixMap( p_vout->p_sys->p_gw[0] );
475 hPixmap1 = GetGWorldPixMap( p_vout->p_sys->p_gw[1] );
476 UnlockPixels(hPixmap0);
477 UnlockPixels(hPixmap1);
482 if ( p_vout->p_sys->p_gw[0] )
484 DisposeGWorld( p_vout->p_sys->p_gw[0] );
486 if ( p_vout->p_sys->p_gw[1] )
488 DisposeGWorld( p_vout->p_sys->p_gw[1] );
490 if ( p_vout->p_sys->p_window )
492 DisposeWindow( p_vout->p_sys->p_window );
495 free( p_vout->p_sys->p_video );
496 free( p_vout->p_sys );
499 /*****************************************************************************
500 * vout_Manage: handle events
501 *****************************************************************************
502 * This function should be called regularly by video output thread. It manages
503 * console events. It returns a non null value on error.
504 *****************************************************************************/
505 static int vout_Manage( vout_thread_t *p_vout )
507 // intf_ErrMsg( "vout_Manage()" );
511 /*****************************************************************************
512 * vout_Display: displays previously rendered output
513 *****************************************************************************
514 * This function send the currently rendered image to image, waits until
515 * it is displayed and switch the two rendering buffers, preparing next frame.
516 *****************************************************************************/
517 static void vout_Display( vout_thread_t *p_vout )
519 // intf_ErrMsg( "vout_Display()" );
521 BlitToWindow ( p_vout, p_vout->i_buffer_index );
525 /*****************************************************************************
526 * flushQD: flushes buffered window area
527 *****************************************************************************/
528 void flushQD( vout_thread_t *p_vout )
532 //intf_ErrMsg( "flushQD()" );
534 thePort = GetWindowPort( p_vout->p_sys->p_window );
536 /* flush the entire port */
537 if (QDIsPortBuffered(thePort))
538 QDFlushPortBuffer(thePort, NULL);
541 /* flush part of the port */
542 if (QDIsPortBuffered(thePort)) {
545 /* local port coordinates */
546 SetRectRgn(theRgn, 10, 10, 100, 30);
547 QDFlushPortBuffer(thePort, theRgn);
554 /*****************************************************************************
555 * BlitToWindow: checks offscreen and blits it to the front
556 *****************************************************************************/
558 void BlitToWindow( vout_thread_t *p_vout, short index )
560 Rect rectDest, rectSource;
561 GrafPtr pCGrafSave, windowPort = GetWindowPort( p_vout->p_sys->p_window );
563 //intf_ErrMsg( "BlitToWindow() for %d", index );
565 GetPortBounds( p_vout->p_sys->p_gw[index], &rectSource );
566 GetPortBounds( windowPort, &rectDest );
568 GetPort ( &pCGrafSave );
569 SetPortWindowPort( p_vout->p_sys->p_window );
570 //FIXME have global lock - kinda bad but oh well
571 // if ( LockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) ) )
573 CopyBits( GetPortBitMapForCopyBits( p_vout->p_sys->p_gw[index] ),
574 GetPortBitMapForCopyBits( GetWindowPort( p_vout->p_sys->p_window ) ),
575 &rectSource, &rectDest, srcCopy, NULL);
576 // UnlockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
583 intf_ErrMsg( "error: Could not LockPixels" );
586 SetPort ( pCGrafSave );
590 /*****************************************************************************
591 * GetWindowDevice: returns GDHandle that window resides on (most of it anyway)
592 *****************************************************************************/
593 GDHandle GetWindowDevice( vout_thread_t *p_vout )
596 Rect rectWind, rectSect;
597 long greatestArea, sectArea;
598 GDHandle hgdNthDevice, hgdZoomOnThisDevice = NULL;
600 //intf_ErrMsg( "GetWindowDevice()" );
603 SetPortWindowPort( p_vout->p_sys->p_window );
604 GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &rectWind );
605 LocalToGlobal( ( Point* ) &rectWind.top );
606 LocalToGlobal( ( Point* ) &rectWind.bottom );
607 hgdNthDevice = GetDeviceList();
609 // check window against all gdRects in gDevice list and remember
610 // which gdRect contains largest area of window}
611 while ( hgdNthDevice )
613 if ( TestDeviceAttribute( hgdNthDevice, screenDevice ) )
615 if ( TestDeviceAttribute( hgdNthDevice, screenActive ) )
617 // The SectRect routine calculates the intersection
618 // of the window rectangle and this gDevice
619 // rectangle and returns TRUE if the rectangles intersect,
620 // FALSE if they don't.
621 SectRect( &rectWind, &( **hgdNthDevice ).gdRect, &rectSect );
622 // determine which screen holds greatest window area
623 // first, calculate area of rectangle on current device
624 sectArea = ( long )( rectSect.right - rectSect.left ) * ( rectSect.bottom - rectSect.top );
625 if ( sectArea > greatestArea )
627 greatestArea = sectArea; // set greatest area so far
628 hgdZoomOnThisDevice = hgdNthDevice; // set zoom device
630 hgdNthDevice = GetNextDevice( hgdNthDevice );
635 return hgdZoomOnThisDevice;
638 /*****************************************************************************
639 * FillOffScreen: fills offscreen buffer with random bright color
640 *****************************************************************************/
642 void FillOffscreen( vout_thread_t *p_vout, short index )
644 static RGBColor rgbColorOld;
650 //intf_ErrMsg( "FillOffscreen" );
652 GetPortBounds( p_vout->p_sys->p_gw[index], &rectSource );
655 rgbColor.red = ( Random () + 32767) / 2 + 32767;
656 while ( abs ( rgbColor.red - rgbColorOld.red ) < 3000 );
658 rgbColor.green = (Random () + 32767) / 2 + 32767;
659 while ( abs ( rgbColor.green - rgbColorOld.green ) < 3000);
661 rgbColor.blue = (Random () + 32767) / 2 + 32767;
662 while ( abs ( rgbColor.blue - rgbColorOld.blue ) < 3000);
664 rgbColorOld = rgbColor;
666 GetGWorld( &pCGrafSave, &hGDSave );
667 SetGWorld( p_vout->p_sys->p_gw[index], NULL );
668 //FIXME have global lock - kinda bad but oh well
669 // if ( LockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) ) )
671 // draw some background
672 EraseRect( &rectSource );
673 RGBForeColor( &rgbColor );
674 PaintRect( &rectSource );
675 // UnlockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
677 SetGWorld( pCGrafSave, hGDSave );