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 #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() */
44 #include "video_output.h"
49 #include "macosx_common.h"
52 /*****************************************************************************
54 *****************************************************************************/
56 // Initial Window Constants
62 // where is the off screen
72 /*****************************************************************************
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 * );
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 );
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);
93 void FindBestMemoryLocation( struct vout_thread_s * );
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 )
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;
111 /*****************************************************************************
112 * intf_Probe: return a score
113 *****************************************************************************/
114 static int vout_Probe( probedata_t *p_data )
116 if( TestMethod( VOUT_METHOD_VAR, "macosx" ) )
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 )
131 //intf_ErrMsg( "vout_Create()" );
133 /* Allocate structure */
134 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
135 if( p_vout->p_sys == NULL )
137 intf_ErrMsg( "error: %s", strerror( ENOMEM ) );
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;
146 if ( CreateDisplay( p_vout ) )
148 intf_ErrMsg( "vout error: can't open display" );
149 free( p_vout->p_sys );
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);
163 /*****************************************************************************
164 * Find the best memory (AGP, VRAM, system) location
165 *****************************************************************************/
166 void FindBestMemoryLocation( vout_thread_t *p_vout )
170 Gestalt( gestaltSystemVersion, &versionSystem );
171 if ( 0x00000900 <= ( versionSystem & 0x00000FF00 ) )
173 intf_ErrMsg( "FindBestMemoryLocation : gNewNewGWorld = true" );
174 p_vout->p_sys->gNewNewGWorld = true;
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};
183 (**(GetPortPixMap( GetWindowPort( p_vout->p_sys->p_window ) ))).pixelSize;
184 GDHandle hgdWindow = GetWindowDevice( p_vout );
186 intf_ErrMsg( "FindBestMemoryLocation : gNewNewGWorld = false !" );
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 ;
192 if( ( noErr == NewGWorld( &pgwTest, wPixDepth, &rectTest, NULL, hgdWindow,
193 noNewDevice | useDistantHdwrMem ) )
196 p_vout->p_sys->gNewNewGWorld = true;
201 DisposeGWorld( pgwTest );
206 /*****************************************************************************
207 * CreateDisplay: setup display params...
208 *****************************************************************************/
209 static int CreateDisplay( vout_thread_t *p_vout )
211 PixMapHandle hPixmap0, hPixmap1;
212 void * hPixmapBaseAddr0, * hPixmapBaseAddr1;
214 //intf_ErrMsg( "CreateDisplay()" );
216 if( MakeWindow( p_vout ) )
218 intf_ErrMsg( "vout error: can't open window display" );
222 // FindBestMemoryLocation( p_vout );
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 ) )
228 intf_ErrMsg( "vout error: can't alloc offscreen buffers" );
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);
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;
241 if ( (hPixmap0 == NULL) || (hPixmap1 == NULL) )
243 intf_ErrMsg( "vout error: pixmap problem");
244 UnlockPixels(hPixmap0);
245 UnlockPixels(hPixmap1);
249 hPixmapBaseAddr0 = GetPixBaseAddr( hPixmap0 );
250 hPixmapBaseAddr1 = GetPixBaseAddr( hPixmap1 );
251 if ( (hPixmapBaseAddr0 == NULL) || (hPixmapBaseAddr1 == NULL) )
253 intf_ErrMsg( "vout error: pixmap base addr problem");
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 vout_SetBuffers( p_vout, hPixmapBaseAddr0, hPixmapBaseAddr1 );
264 /*****************************************************************************
265 * MakeWindow: open and set-up a Mac OS main window
266 *****************************************************************************/
267 static int MakeWindow( vout_thread_t *p_vout )
271 int bottom = p_vout->i_height;
272 int right = p_vout->i_width;
273 ProcessSerialNumber PSN;
275 WindowAttributes windowAttr = kWindowStandardDocumentAttributes |
276 kWindowStandardHandlerAttribute |
277 kWindowInWindowMenuAttribute;
279 SetRect( &p_vout->p_sys->wrect, left, top, right, bottom );
280 OffsetRect( &p_vout->p_sys->wrect, kWindowOffset, kWindowOffset );
282 CreateNewWindow( kDocumentWindowClass, windowAttr, &p_vout->p_sys->wrect, &p_vout->p_sys->p_window );
283 if ( p_vout->p_sys->p_window == nil )
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 );
294 //in case we are run from the command line, bring us to front instead of Terminal
295 GetCurrentProcess(&PSN);
296 SetFrontProcess(&PSN);
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;
304 p_vout->i_bytes_per_line = (**(**GetWindowDevice( p_vout )).gdPMap).rowBytes & 0x3FFF ;
306 switch ( p_vout->i_screen_depth )
310 p_vout->i_red_mask = 0xff0000;
311 p_vout->i_green_mask = 0xff00;
312 p_vout->i_blue_mask = 0xff;
316 p_vout->i_red_mask = 0x00007c00;
317 p_vout->i_green_mask = 0x000003e0;
318 p_vout->i_blue_mask = 0x0000001f;
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;
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;
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 )
349 GDHandle hgdWindow = GetWindowDevice( p_vout );
351 switch ( p_vout->p_sys->gwLocOffscreen )
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 ) )
358 intf_ErrMsg( "Allocate off screen image in VRAM" );
361 intf_ErrMsg( "Unable to allocate off screen image in VRAM, trying next best AGP" );
362 p_vout->p_sys->gwLocOffscreen = 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 ) )
368 intf_ErrMsg( "Allocate off screen image in AGP" );
371 intf_ErrMsg( "Unable to allocate off screen image in AGP, trying next best System" );
372 p_vout->p_sys->gwLocOffscreen = kInSystem;
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) )
379 intf_ErrMsg( "Allocate off screen image in System" );
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
389 /*****************************************************************************
390 * vout_Init: initialize video thread output method
391 *****************************************************************************/
392 static int vout_Init( vout_thread_t *p_vout )
394 //intf_ErrMsg( "vout_Init()" );
398 /*****************************************************************************
399 * vout_End: terminate video thread output method
400 *****************************************************************************/
401 static void vout_End( vout_thread_t *p_vout )
403 //intf_ErrMsg( "vout_End()" );
407 /*****************************************************************************
408 * vout_Destroy: destroy video thread output method
409 *****************************************************************************/
410 static void vout_Destroy( vout_thread_t *p_vout )
412 //intf_ErrMsg( "vout_Destroy()" );
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);
421 if ( p_vout->p_sys->p_gw[0] )
423 DisposeGWorld( p_vout->p_sys->p_gw[0] );
425 if ( p_vout->p_sys->p_gw[1] )
427 DisposeGWorld( p_vout->p_sys->p_gw[1] );
429 if ( p_vout->p_sys->p_window )
431 DisposeWindow( p_vout->p_sys->p_window );
434 free( p_vout->p_sys );
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 )
445 // intf_ErrMsg( "vout_Manage()" );
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 )
457 // intf_ErrMsg( "vout_Display()" );
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 );
465 /*****************************************************************************
466 * flushQD: flushes buffered window area
467 *****************************************************************************/
468 void flushQD( vout_thread_t *p_vout )
472 //intf_ErrMsg( "flushQD()" );
474 thePort = GetWindowPort( p_vout->p_sys->p_window );
476 /* flush the entire port */
477 if (QDIsPortBuffered(thePort))
478 QDFlushPortBuffer(thePort, NULL);
481 /* flush part of the port */
482 if (QDIsPortBuffered(thePort)) {
485 /* local port coordinates */
486 SetRectRgn(theRgn, 10, 10, 100, 30);
487 QDFlushPortBuffer(thePort, theRgn);
494 /*****************************************************************************
495 * BlitToWindow: checks offscreen and blits it to the front
496 *****************************************************************************/
498 void BlitToWindow( vout_thread_t *p_vout, short index )
500 Rect rectDest, rectSource;
501 GrafPtr pCGrafSave, windowPort = GetWindowPort( p_vout->p_sys->p_window );
503 //intf_ErrMsg( "BlitToWindow() for %d", index );
505 GetPortBounds( p_vout->p_sys->p_gw[index], &rectSource );
506 GetPortBounds( windowPort, &rectDest );
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] ) ) )
514 //LockPortBits(GetWindowPort( p_vout->p_sys->p_window ));
515 //NoPurgePixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
517 CopyBits( GetPortBitMapForCopyBits( p_vout->p_sys->p_gw[index] ),
518 GetPortBitMapForCopyBits( GetWindowPort( p_vout->p_sys->p_window ) ),
519 &rectSource, &rectDest, srcCopy, NULL);
521 //UnlockPortBits(GetWindowPort( p_vout->p_sys->p_window ));
522 //AllowPurgePixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
524 // UnlockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
527 SetPort ( pCGrafSave );
531 /*****************************************************************************
532 * GetWindowDevice: returns GDHandle that window resides on (most of it anyway)
533 *****************************************************************************/
534 GDHandle GetWindowDevice( vout_thread_t *p_vout )
537 Rect rectWind, rectSect;
538 long greatestArea, sectArea;
539 GDHandle hgdNthDevice, hgdZoomOnThisDevice = NULL;
541 //intf_ErrMsg( "GetWindowDevice()" );
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();
550 // check window against all gdRects in gDevice list and remember
551 // which gdRect contains largest area of window}
552 while ( hgdNthDevice )
554 if ( TestDeviceAttribute( hgdNthDevice, screenDevice ) )
556 if ( TestDeviceAttribute( hgdNthDevice, screenActive ) )
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 )
568 greatestArea = sectArea; // set greatest area so far
569 hgdZoomOnThisDevice = hgdNthDevice; // set zoom device
571 hgdNthDevice = GetNextDevice( hgdNthDevice );
576 return hgdZoomOnThisDevice;
579 /*****************************************************************************
580 * FillOffScreen: fills offscreen buffer with random bright color
581 *****************************************************************************/
583 void FillOffscreen( vout_thread_t *p_vout, short index )
585 static RGBColor rgbColorOld;
591 //intf_ErrMsg( "FillOffscreen" );
593 GetPortBounds( p_vout->p_sys->p_gw[index], &rectSource );
596 rgbColor.red = ( Random () + 32767) / 2 + 32767;
597 while ( abs ( rgbColor.red - rgbColorOld.red ) < 3000 );
599 rgbColor.green = (Random () + 32767) / 2 + 32767;
600 while ( abs ( rgbColor.green - rgbColorOld.green ) < 3000);
602 rgbColor.blue = (Random () + 32767) / 2 + 32767;
603 while ( abs ( rgbColor.blue - rgbColorOld.blue ) < 3000);
605 rgbColorOld = rgbColor;
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] ) ) )
612 // draw some background
613 EraseRect( &rectSource );
614 RGBForeColor( &rgbColor );
615 PaintRect( &rectSource );
616 // UnlockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
618 SetGWorld( pCGrafSave, hGDSave );