]> git.sesse.net Git - vlc/blobdiff - plugins/macosx/vout_macosx.c
* Fixed the BeOS compile typo.
[vlc] / plugins / macosx / vout_macosx.c
index 50a71e18c4b35de62c9498bd83f68262d66f2e39..ac171ae7cb36958cfd32e46d0281a41af47b15ef 100644 (file)
@@ -3,7 +3,7 @@
  *****************************************************************************
  * Copyright (C) 2001 VideoLAN
  *
- * Authors: 
+ * Authors: Colin Delacroix <colin@zoy.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include "mtime.h"
 #include "tests.h"
 
+#include "intf_msg.h"
+
 #include "video.h"
 #include "video_output.h"
 
-#include "intf_msg.h"
-
 #include "modules.h"
+#include "main.h"
+
+#include "macosx_common.h"
 
-#define DUMMY_WIDTH 16
-#define DUMMY_HEIGHT 16
-#define DUMMY_BITS_PER_PLANE 16
-#define DUMMY_BYTES_PER_PIXEL 2
 
 /*****************************************************************************
- * vout_sys_t: MacOS X video output method descriptor
- *****************************************************************************
- * This structure is part of the video output thread descriptor.
- * It describes the MacOS X specific properties of an output thread.
+ * Constants & more
  *****************************************************************************/
-typedef struct vout_sys_s
+
+// Initial Window Constants
+enum
+{
+    kWindowOffset = 100
+};
+
+// where is the off screen
+enum
 {
-    /* MacOS X video memory */
-    byte_t *                    p_video;                      /* base adress */
-    size_t                      i_page_size;                    /* page size */
+    kNoWhere = 0,
+    kInVRAM,
+    kInAGP,
+    kInSystem
+};
 
-} vout_sys_t;
 
 /*****************************************************************************
  * Local prototypes
@@ -75,6 +80,18 @@ static void vout_Destroy   ( struct vout_thread_s * );
 static int  vout_Manage    ( struct vout_thread_s * );
 static void vout_Display   ( struct vout_thread_s * );
 
+/* OS specific */
+
+static int CreateDisplay       ( struct vout_thread_s * );
+static int MakeWindow          ( struct vout_thread_s * );
+static int AllocBuffer         ( struct vout_thread_s * , short index );
+
+void BlitToWindow              ( struct vout_thread_s * , short index );
+GDHandle GetWindowDevice       ( struct vout_thread_s * );
+void FillOffscreen             ( struct vout_thread_s * , short index);
+
+void FindBestMemoryLocation( struct vout_thread_s * );
+
 /*****************************************************************************
  * Functions exported as capabilities. They are declared as static so that
  * we don't pollute the namespace too much.
@@ -111,45 +128,270 @@ static int vout_Probe( probedata_t *p_data )
  *****************************************************************************/
 static int vout_Create( vout_thread_t *p_vout )
 {
+    //intf_ErrMsg( "vout_Create()" );
+
     /* Allocate structure */
     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
     if( p_vout->p_sys == NULL )
     {
-        intf_ErrMsg("error: %s", strerror(ENOMEM) );
+        intf_ErrMsg( "error: %s", strerror( ENOMEM ) );
         return( 1 );
     }
 
-    p_vout->i_width            = DUMMY_WIDTH;
-    p_vout->i_height           = DUMMY_HEIGHT;
-    p_vout->i_screen_depth     = DUMMY_BITS_PER_PLANE;
-    p_vout->i_bytes_per_pixel  = DUMMY_BYTES_PER_PIXEL;
-    p_vout->i_bytes_per_line   = DUMMY_WIDTH * DUMMY_BYTES_PER_PIXEL;
+    p_vout->p_sys->gwLocOffscreen = kNoWhere;
+    p_vout->p_sys->p_window       = NULL;
+    p_vout->p_sys->p_gw[ 0 ]      = NULL;
+    p_vout->p_sys->p_gw[ 1 ]      = NULL;
+    
+    if ( CreateDisplay( p_vout ) )
+    {
+        intf_ErrMsg( "vout error: can't open display" );
+        free( p_vout->p_sys );
+        return( 1 );
+    }
+
+#if 0
+    intf_ErrMsg( "vout p_vout->i_width %d" , p_vout->i_width);
+    intf_ErrMsg( "vout p_vout->i_height %d" , p_vout->i_height);
+    intf_ErrMsg( "vout p_vout->i_bytes_per_pixel %d" , p_vout->i_bytes_per_pixel);
+    intf_ErrMsg( "vout p_vout->i_screen_depth %d" , p_vout->i_screen_depth);
+#endif
+
+    return( 0 );
+}
 
-    p_vout->p_sys->i_page_size = DUMMY_WIDTH * DUMMY_HEIGHT
-                                  * DUMMY_BYTES_PER_PIXEL;
+/*****************************************************************************
+ * Find the best memory (AGP, VRAM, system) location
+ *****************************************************************************/
+void FindBestMemoryLocation( vout_thread_t *p_vout )
+{
+    long versionSystem;
 
-    /* Map two framebuffers a the very beginning of the fb */
-    p_vout->p_sys->p_video = malloc( 2 * p_vout->p_sys->i_page_size );
-    if( p_vout->p_sys->p_video == NULL )
+    Gestalt( gestaltSystemVersion, &versionSystem );
+    if ( 0x00000900 <= ( versionSystem & 0x00000FF00  ) )
+    {
+        intf_ErrMsg( "FindBestMemoryLocation : gNewNewGWorld = true" );
+        p_vout->p_sys->gNewNewGWorld = true;
+    }
+    else
+    {
+        // now it is tricky
+        // we will try to allocate in VRAM and find out where the allocation really ended up.
+        GWorldPtr pgwTest = NULL;
+        Rect rectTest = {0, 0, 10, 10};
+        short wPixDepth = 
+            (**(GetPortPixMap( GetWindowPort( p_vout->p_sys->p_window ) ))).pixelSize;
+        GDHandle hgdWindow = GetWindowDevice( p_vout );
+
+        intf_ErrMsg( "FindBestMemoryLocation : gNewNewGWorld = false !" );
+#if 0
+        p_vout->i_screen_depth = wPixDepth;
+        p_vout->i_bytes_per_pixel = wPixDepth;
+        p_vout->i_bytes_per_line = (**(**hgdWindow).gdPMap).rowBytes & 0x3FFF ;
+#endif
+        if(    ( noErr == NewGWorld( &pgwTest, wPixDepth, &rectTest, NULL, hgdWindow,
+                                     noNewDevice | useDistantHdwrMem ) ) 
+            && ( pgwTest ) )
+        {
+            p_vout->p_sys->gNewNewGWorld = true;       
+        }
+        
+        if( pgwTest )
+        {
+            DisposeGWorld( pgwTest );
+        }
+    }
+}
+
+/*****************************************************************************
+ * CreateDisplay: setup display params...
+ *****************************************************************************/
+static int CreateDisplay( vout_thread_t *p_vout )
+{
+    PixMapHandle hPixmap0, hPixmap1;
+    void * hPixmapBaseAddr0, * hPixmapBaseAddr1;
+
+    //intf_ErrMsg( "CreateDisplay()" );
+
+    if( MakeWindow( p_vout ) )
+    {
+        intf_ErrMsg( "vout error: can't open window display" );
+        return( 1 );
+    }
+
+    // FindBestMemoryLocation( p_vout );
+
+    //try to allocate @ best location, will eventaully trickle down to worst
+    p_vout->p_sys->gwLocOffscreen = kInVRAM;
+    if( AllocBuffer( p_vout, 0 ) || AllocBuffer( p_vout, 1 ) )
+    {
+        intf_ErrMsg( "vout error: can't alloc offscreen buffers" );
+        return( 1 );
+    }
+
+//FIXME ? - lock this down until the end...
+    hPixmap0 = GetGWorldPixMap( p_vout->p_sys->p_gw[0] );
+    LockPixels(hPixmap0);
+    hPixmap1 = GetGWorldPixMap( p_vout->p_sys->p_gw[1] );
+    LockPixels(hPixmap1);
+    
+    //FIXME hopefully this is the same for all Gworlds & window since they are the same size
+    p_vout->i_bytes_per_line = (**hPixmap0).rowBytes & 0x3FFF;
+
+    if ( (hPixmap0 == NULL) || (hPixmap1 == NULL) )
+    {
+        intf_ErrMsg( "vout error: pixmap problem");
+        UnlockPixels(hPixmap0);
+        UnlockPixels(hPixmap1);
+        return( 1 );
+    }
+
+    hPixmapBaseAddr0 = GetPixBaseAddr( hPixmap0 );
+    hPixmapBaseAddr1 = GetPixBaseAddr( hPixmap1 );
+    if ( (hPixmapBaseAddr0 == NULL) || (hPixmapBaseAddr1 == NULL) )
+    {
+        intf_ErrMsg( "vout error: pixmap base addr problem");
+        return( 1 );
+    }
+
+//FIXME - if I ever dispose of the Gworlds and recreate them, i'll have a new address
+//and I'll need to tell vout about them...  dunno what problems vout might have if we just updateGworld  
+    p_vout->pf_setbuffers( p_vout, hPixmapBaseAddr0, hPixmapBaseAddr1 );
+
+    return 0;
+}
+
+/*****************************************************************************
+ * MakeWindow: open and set-up a Mac OS main window
+ *****************************************************************************/
+static int MakeWindow( vout_thread_t *p_vout )
+{
+    int left = 0;
+    int top = 0;
+    int bottom = p_vout->i_height;
+    int right = p_vout->i_width;
+    ProcessSerialNumber PSN;
+
+    WindowAttributes windowAttr = kWindowStandardDocumentAttributes | 
+                                    kWindowStandardHandlerAttribute |
+                                    kWindowInWindowMenuAttribute;
+    
+    SetRect( &p_vout->p_sys->wrect, left, top, right, bottom );
+    OffsetRect( &p_vout->p_sys->wrect, kWindowOffset, kWindowOffset );
+
+    CreateNewWindow( kDocumentWindowClass, windowAttr, &p_vout->p_sys->wrect, &p_vout->p_sys->p_window );
+    if ( p_vout->p_sys->p_window == nil )
     {
-        intf_ErrMsg( "vout error: can't map video memory (%s)",
-                     strerror(errno) );
-        free( p_vout->p_sys );
         return( 1 );
     }
 
-    /* Set and initialize buffers */
-    vout_SetBuffers( p_vout, p_vout->p_sys->p_video,
-                     p_vout->p_sys->p_video + p_vout->p_sys->i_page_size );
+    InstallStandardEventHandler(GetWindowEventTarget(p_vout->p_sys->p_window));
+    SetPort( GetWindowPort( p_vout->p_sys->p_window ) );
+    SetWindowTitleWithCFString( p_vout->p_sys->p_window, CFSTR("VLC") );
+    ShowWindow( p_vout->p_sys->p_window );
+    SelectWindow( p_vout->p_sys->p_window );
+
+    //in case we are run from the command line, bring us to front instead of Terminal
+    GetCurrentProcess(&PSN);
+    SetFrontProcess(&PSN);
+
+{
+    short wPixDepth = (**(GetPortPixMap( GetWindowPort( p_vout->p_sys->p_window ) ))).pixelSize;
+    p_vout->i_screen_depth = wPixDepth;
+    p_vout->i_bytes_per_pixel = p_vout->i_screen_depth / 8;
+    p_vout->i_bytes_per_line   = p_vout->i_width * p_vout->i_bytes_per_pixel;
+
+    p_vout->i_bytes_per_line = (**(**GetWindowDevice( p_vout )).gdPMap).rowBytes & 0x3FFF ;
+
+    switch ( p_vout->i_screen_depth )
+    {
+        case 32:
+        case 24:
+            p_vout->i_red_mask =   0xff0000;
+            p_vout->i_green_mask = 0xff00;
+            p_vout->i_blue_mask =  0xff;
+            break;
+        case 16:
+        case 15:
+            p_vout->i_red_mask =   0x00007c00;
+            p_vout->i_green_mask = 0x000003e0;
+            p_vout->i_blue_mask =  0x0000001f;
+            break;
+        default:
+            break;
+    }
+}
+
+#if 0
+    p_vout->i_red_lshift = 0x10;
+    p_vout->i_red_rshift = 0x0;
+    p_vout->i_green_lshift = 0x8;
+    p_vout->i_green_rshift = 0x0;
+    p_vout->i_blue_lshift = 0x0;
+    p_vout->i_blue_rshift = 0x0;
+
+    p_vout->i_white_pixel = 0xffffff;
+    p_vout->i_black_pixel = 0x0;
+    p_vout->i_gray_pixel = 0x808080;
+    p_vout->i_blue_pixel = 0x32;
+#endif
 
     return( 0 );
 }
 
+/*****************************************************************************
+ * AllocBuffer: forces offscreen allocation (if different than current) in
+ * memory type specified
+ *****************************************************************************/
+static int AllocBuffer ( vout_thread_t *p_vout, short index )
+{
+    Rect bounds;
+    GDHandle hgdWindow = GetWindowDevice( p_vout );
+
+    switch ( p_vout->p_sys->gwLocOffscreen )
+    {
+        case kInVRAM:
+            if ( noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth, 
+                GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL, 
+                hgdWindow, noNewDevice | useDistantHdwrMem ) )         
+            {
+                intf_ErrMsg( "Allocate off screen image in VRAM" );
+                break;
+            }
+            intf_ErrMsg( "Unable to allocate off screen image in VRAM, trying next best AGP" );
+            p_vout->p_sys->gwLocOffscreen = kInAGP;
+        case kInAGP:
+            if (noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth, 
+                GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL, 
+                hgdWindow, noNewDevice | useLocalHdwrMem ) )
+            {
+                intf_ErrMsg( "Allocate off screen image in AGP" );
+                break;
+            }
+            intf_ErrMsg( "Unable to allocate off screen image in AGP, trying next best System" );
+            p_vout->p_sys->gwLocOffscreen = kInSystem;
+        case kInSystem:
+        default:
+            if ( noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth, 
+                GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL, 
+                hgdWindow, noNewDevice | keepLocal) )
+            {
+                intf_ErrMsg( "Allocate off screen image in System" );
+                break;
+            }
+            intf_ErrMsg( "Unable to allocate off screen image in System, no options left - failing" );
+            p_vout->p_sys->gwLocOffscreen = kNoWhere;
+            return( 1 ); // nothing was allocated
+    } 
+    return( 0 );
+}
+
 /*****************************************************************************
  * vout_Init: initialize video thread output method
  *****************************************************************************/
 static int vout_Init( vout_thread_t *p_vout )
 {
+    //intf_ErrMsg( "vout_Init()" );
     return( 0 );
 }
 
@@ -158,6 +400,7 @@ static int vout_Init( vout_thread_t *p_vout )
  *****************************************************************************/
 static void vout_End( vout_thread_t *p_vout )
 {
+    //intf_ErrMsg( "vout_End()" );
     ;
 }
 
@@ -166,7 +409,28 @@ static void vout_End( vout_thread_t *p_vout )
  *****************************************************************************/
 static void vout_Destroy( vout_thread_t *p_vout )
 {
-    free( p_vout->p_sys->p_video );
+    //intf_ErrMsg( "vout_Destroy()" );
+
+//FIXME Big Lock around Gworlds
+    PixMapHandle hPixmap0, hPixmap1;
+    hPixmap0 = GetGWorldPixMap( p_vout->p_sys->p_gw[0] );
+    hPixmap1 = GetGWorldPixMap( p_vout->p_sys->p_gw[1] );
+    UnlockPixels(hPixmap0);
+    UnlockPixels(hPixmap1);
+
+    if ( p_vout->p_sys->p_gw[0] )
+    {
+        DisposeGWorld( p_vout->p_sys->p_gw[0] );
+    }
+    if ( p_vout->p_sys->p_gw[1] )
+    {
+        DisposeGWorld( p_vout->p_sys->p_gw[1] );
+    }
+    if ( p_vout->p_sys->p_window )
+    {
+        DisposeWindow( p_vout->p_sys->p_window );
+    }
+
     free( p_vout->p_sys );
 }
 
@@ -178,6 +442,7 @@ static void vout_Destroy( vout_thread_t *p_vout )
  *****************************************************************************/
 static int vout_Manage( vout_thread_t *p_vout )
 {
+//    intf_ErrMsg( "vout_Manage()" );
     return( 0 );
 }
 
@@ -189,6 +454,166 @@ static int vout_Manage( vout_thread_t *p_vout )
  *****************************************************************************/
 static void vout_Display( vout_thread_t *p_vout )
 {
-    ;
+//    intf_ErrMsg( "vout_Display()" );
+
+//we should not be called if we set the status to paused or stopped via the interface
+//    if ( p_vout->p_sys->playback_status != PAUSED &&  p_vout->p_sys->playback_status != STOPPED )
+        BlitToWindow ( p_vout, p_vout->i_buffer_index );
 }
 
+
+/*****************************************************************************
+ * flushQD: flushes buffered window area
+ *****************************************************************************/
+void flushQD( vout_thread_t *p_vout )
+{
+    CGrafPtr thePort;
+
+    //intf_ErrMsg( "flushQD()" );
+    
+    thePort = GetWindowPort( p_vout->p_sys->p_window );
+    
+    /* flush the entire port */
+    if (QDIsPortBuffered(thePort))
+        QDFlushPortBuffer(thePort, NULL);
+
+#if 0
+    /* flush part of the port */
+    if (QDIsPortBuffered(thePort)) {
+        RgnHandle theRgn;
+        theRgn = NewRgn();
+            /* local port coordinates */
+        SetRectRgn(theRgn, 10, 10, 100, 30); 
+        QDFlushPortBuffer(thePort, theRgn);
+        DisposeRgn(theRgn);
+    }
+#endif
+
+}
+
+/*****************************************************************************
+ * BlitToWindow: checks offscreen and blits it to the front
+ *****************************************************************************/
+
+void BlitToWindow( vout_thread_t *p_vout, short index )
+{
+    Rect rectDest, rectSource;
+    GrafPtr pCGrafSave, windowPort = GetWindowPort( p_vout->p_sys->p_window );
+
+    //intf_ErrMsg( "BlitToWindow() for %d", index );
+
+    GetPortBounds( p_vout->p_sys->p_gw[index], &rectSource );
+    GetPortBounds( windowPort, &rectDest );
+    
+    GetPort ( &pCGrafSave );
+    SetPortWindowPort( p_vout->p_sys->p_window );
+//FIXME have global lock - kinda bad but oh well 
+//    if ( LockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) ) )
+//    {
+
+//LockPortBits(GetWindowPort( p_vout->p_sys->p_window ));
+//NoPurgePixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
+
+        CopyBits( GetPortBitMapForCopyBits( p_vout->p_sys->p_gw[index] ), 
+                    GetPortBitMapForCopyBits( GetWindowPort( p_vout->p_sys->p_window ) ), 
+                    &rectSource, &rectDest, srcCopy, NULL);
+
+//UnlockPortBits(GetWindowPort( p_vout->p_sys->p_window ));
+//AllowPurgePixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
+
+//        UnlockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
+        //flushQD( p_vout );
+//    }
+    SetPort ( pCGrafSave );
+}
+
+
+/*****************************************************************************
+ * GetWindowDevice: returns GDHandle that window resides on (most of it anyway)
+ *****************************************************************************/
+GDHandle GetWindowDevice( vout_thread_t *p_vout )
+{
+    GrafPtr pgpSave;
+    Rect rectWind, rectSect;
+    long greatestArea, sectArea;
+    GDHandle hgdNthDevice, hgdZoomOnThisDevice = NULL;
+    
+    //intf_ErrMsg( "GetWindowDevice()" );
+
+    GetPort( &pgpSave );
+    SetPortWindowPort( p_vout->p_sys->p_window );
+    GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &rectWind );
+    LocalToGlobal( ( Point* ) &rectWind.top );
+    LocalToGlobal( ( Point* ) &rectWind.bottom );
+    hgdNthDevice = GetDeviceList();
+    greatestArea = 0;
+    // check window against all gdRects in gDevice list and remember 
+    //  which gdRect contains largest area of window}
+    while ( hgdNthDevice )
+    {
+        if ( TestDeviceAttribute( hgdNthDevice, screenDevice ) )
+        {
+            if ( TestDeviceAttribute( hgdNthDevice, screenActive ) )
+            {
+                // The SectRect routine calculates the intersection 
+                //  of the window rectangle and this gDevice 
+                //  rectangle and returns TRUE if the rectangles intersect, 
+                //  FALSE if they don't.
+                SectRect( &rectWind, &( **hgdNthDevice ).gdRect, &rectSect );
+                // determine which screen holds greatest window area
+                //  first, calculate area of rectangle on current device
+                sectArea = ( long )( rectSect.right - rectSect.left ) * ( rectSect.bottom - rectSect.top );
+                if ( sectArea > greatestArea )
+                {
+                    greatestArea = sectArea;   // set greatest area so far
+                    hgdZoomOnThisDevice = hgdNthDevice;        // set zoom device
+                }
+                hgdNthDevice = GetNextDevice( hgdNthDevice );
+            }
+        }
+    }  // of WHILE
+    SetPort( pgpSave );
+    return hgdZoomOnThisDevice;
+}
+
+/*****************************************************************************
+ * FillOffScreen: fills offscreen buffer with random bright color
+ *****************************************************************************/
+
+void FillOffscreen( vout_thread_t *p_vout, short index )
+{
+    static RGBColor rgbColorOld;
+    GDHandle hGDSave;
+    CGrafPtr pCGrafSave;
+    Rect rectSource;
+    RGBColor rgbColor;
+    
+    //intf_ErrMsg( "FillOffscreen" );
+
+    GetPortBounds( p_vout->p_sys->p_gw[index], &rectSource );
+    
+    do 
+        rgbColor.red = ( Random () + 32767) / 2 + 32767;
+    while ( abs ( rgbColor.red - rgbColorOld.red ) <  3000 );  
+    do 
+        rgbColor.green = (Random () + 32767) / 2 + 32767;
+    while ( abs ( rgbColor.green - rgbColorOld.green ) <  3000);
+    do 
+        rgbColor.blue = (Random () + 32767) / 2 + 32767;
+    while ( abs ( rgbColor.blue - rgbColorOld.blue ) <  3000);
+    
+    rgbColorOld = rgbColor;
+
+    GetGWorld( &pCGrafSave, &hGDSave );
+    SetGWorld( p_vout->p_sys->p_gw[index], NULL );
+//FIXME have global lock - kinda bad but oh well 
+//    if ( LockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) ) )
+//    {
+        // draw some background
+        EraseRect( &rectSource );
+        RGBForeColor( &rgbColor );
+        PaintRect( &rectSource );
+//        UnlockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
+//    }
+    SetGWorld( pCGrafSave, hGDSave );
+}