/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: unixsurf.cpp,v 1.2.40.5 2004/07/20 23:43:41 nhart Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include <errno.h>

#include "hxcom.h"
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxvsurf.h"
#include "hxslist.h"
#include "hxheap.h"
#include "hxtick.h"
#include "colormap.h"
#include "hxthread.h"
#include "ihxpckts.h"
#include "unixsurf.h"
#include "unixroot.h"
#include "unixsite.h"
#include "basesite.h"
#include "shmhelp.h"

#if defined(_LINUX) && defined(_OVERLAY)
#include "hxprefs.h"
CUnixSurf* CUnixSurf::zm_pXvOwner     = NULL;
#endif


CUnixSurf::CUnixSurf( IUnknown* pContext, CHXBaseSite* pSite )
    : CBaseSurface( pContext, pSite )
#if defined(_LINUX) && defined(_OVERLAY)
    , m_bWallPaperMode(FALSE)
    , m_nPortID(-1)
    , m_bPaintClipList(TRUE)
    , m_ulColorKey(0x01020304)
    , m_bStretchToFill(FALSE)
    , m_pXvImage(NULL)
    , m_atomColorKey(None)
    , m_atomClipKey(None)
#if defined(HELIX_FEATURE_HARDWARE_COLOR_CONTROLS)
    , m_atomBrightness(None)
    , m_atomContrast(None)
    , m_atomHue(None)
    , m_atomSaturation(None)
    , m_nCurrBrightness(0)
    , m_nCurrContrast(0)
    , m_nCurrHue(0)
    , m_nCurrSaturation(0)
#endif    
    , m_ulLastOverlayUpdateTime(0)
#endif
    , m_pcVideoBuf(NULL)
    , m_ulFourCCID(0)
    , m_nVideoPitch(0)
    , m_bUseShm(FALSE)
    , m_display(NULL)
    , m_GC(0)
    , m_nScreenNumber(0)
    , m_shmInfo(NULL)
    , m_nShmId(NULL)
    , m_nCurrentBuffer(0)
    , m_nCompletionEventID(-1)
    , m_nMultiBufferCount(2)
{
    int i=0;
    
#if defined(_LINUX) && defined(_OVERLAY)
    HX_ASSERT( m_pContext );
    IHXPreferences* pPreferences = NULL;   
    if( m_pContext && HXR_OK == m_pContext->QueryInterface( IID_IHXPreferences, (void **) &pPreferences))
    { 
        IHXBuffer *pBuffer = NULL;
        //This makes the overlay appear on the root window....
        //Just for inhouse testing right now...
        pPreferences->ReadPref("OverlayMode", pBuffer);
        if(pBuffer)
        {
            m_bUseOverlays = (::atoi((const char*)pBuffer->GetBuffer()) == 1);
            HX_RELEASE(pBuffer);
        }
        else
        {
            if (pPreferences->ReadPref("UseOverlay", pBuffer) == HXR_OK)
            {
                m_bUseOverlays = ::atoi((char*) pBuffer->GetBuffer()) == 1;
            }
            HX_RELEASE(pBuffer);
        }

        //This makes the overlay appear on the root window....
        //Just for inhouse testing right now...
        pPreferences->ReadPref("WallPaperMode", pBuffer);
        if (pBuffer)
        {
            m_bWallPaperMode = (::atoi((const char*)pBuffer->GetBuffer()) == 1);
            HX_RELEASE(pBuffer);
        }
        //This is used only when WallPaperMode=1 and stretches the
        //presentation to fill the whole root window NOT preserving
        //aspect ratio.
        pPreferences->ReadPref("StretchToFill", pBuffer);
        if (pBuffer)
        {
            m_bStretchToFill = (::atoi((const char*)pBuffer->GetBuffer()) == 1);
            HX_RELEASE(pBuffer);
        }

        //This is used on ATI cards for debugging. It tells the driver
        //to paint the window with the color key before each blt, or not.
        pPreferences->ReadPref("PaintClipList", pBuffer);
        if (pBuffer)
        {
            m_bPaintClipList = (::atoi((const char*)pBuffer->GetBuffer()) == 1);
            HX_RELEASE(pBuffer);
        }
        //This is the color key to use. If not set each card's driver will
        //choose one.
        pPreferences->ReadPref("ColorKey", pBuffer);
        if (pBuffer)
        {
            char *pszTmp   = NULL;
            const char *pszColor = (const char*)pBuffer->GetBuffer();
            long lTmp = ::strtol(pszColor, &pszTmp, 0);
            HX_RELEASE(pBuffer);
            if( lTmp>=0 && pszTmp!=pszColor && errno!=ERANGE )
            {
                m_ulColorKey = lTmp;
            }
        }
        //Find out how much multi buffering to do
        m_nMultiBufferCount = 2; //default to 2.
        pPreferences->ReadPref("MultiBufferingCount", pBuffer);
        if (pBuffer)
        {
            char *pszTmp   = NULL;
            const char *pszBufferCount = (const char*)pBuffer->GetBuffer();
            long lTmp = ::strtol(pszBufferCount, &pszTmp, 0);
            HX_RELEASE(pBuffer);
            if( pszTmp!=pszBufferCount && errno!=ERANGE )
            {
                m_nMultiBufferCount = lTmp;
            }
        }
        if( m_nMultiBufferCount < 1 )
            m_nMultiBufferCount = 1;
      
        HX_RELEASE( pPreferences );
    }

    if( m_bUseOverlays )
    {
        //Alloc our multi buffer lists.
        m_pXvImage   = new XvImage* [m_nMultiBufferCount];
        m_pcVideoBuf = new UCHAR* [m_nMultiBufferCount];
        m_shmInfo    = new XShmSegmentInfo[m_nMultiBufferCount];
        m_nShmId     = new int[m_nMultiBufferCount];
        for( i=0 ; i<m_nMultiBufferCount ; i++ )
        {
            m_pXvImage[i] = NULL;
            m_pcVideoBuf[i] = NULL;
            m_nShmId[i] = -1;
        }
    }
   
#endif //_LINUX && _OVERLAY
   
}

CUnixSurf::~CUnixSurf()
{
    int i = 0;
    
    if( m_GC)
    {
        XLockDisplay(m_display);
        XFreeGC(m_display, m_GC);
	XUnlockDisplay(m_display);
        m_GC=0;
    }
    
#if defined(_LINUX) && defined(_OVERLAY)
   
    if( m_pXvImage )
    {
        for( i=0; i<m_nMultiBufferCount; i++ )
        {
            XFree( m_pXvImage[i] );
        }
        HX_VECTOR_DELETE(m_pXvImage);
    }
   
    if( m_nPortID!=-1 )
    {
	XLockDisplay(m_display);
        XvUngrabPort( m_display, m_nPortID, CurrentTime );
	XUnlockDisplay(m_display);
        m_nPortID=-1;
        if( zm_pXvOwner==this )
        {
            zm_pXvOwner = NULL;
        }
    }
    if( m_pcVideoBuf != NULL )
    {
        for( i=0 ; i<m_nMultiBufferCount ; i++ )
        {
            if( m_nShmId[i] != -1 && m_bUseShm )
            {
                ShmHelp::DetachSharedRegion( &m_pcVideoBuf[i], &m_shmInfo[i] );
                m_nShmId[i] = -1;
            }
            else
            {
                HX_VECTOR_DELETE(m_pcVideoBuf[i]);
            }
        }
        HX_VECTOR_DELETE(m_pcVideoBuf);
        HX_VECTOR_DELETE(m_shmInfo);
    }
    memset( &m_surfaceSize, 0, sizeof(m_surfaceSize) );
   
#endif //_LINUX && _OVERLAY
   
}

void CUnixSurf::_ReleaseSurface(CBaseRootSurface* pSurface)
{
    //Release our grab on the port and assorted memory.....
    _ReleaseSurface();
}

void CUnixSurf::_DrawBlack(void* pWindow)
{
    //XXXgfw fill whole window with black? Just our rect?
}

HX_RESULT CUnixSurf::_BltToPrimary(HXxRect& rDestRect, HXxRect& rSrcRect)
{
#ifdef _DEBUG   
    fprintf( stderr, "CUnixSurf::_BltToPrimary is called...\n" );
#endif   
    return HXR_FAIL;
}


void CUnixSurf::_CreateBuffer()
{
    int i = 0;
#if defined(_LINUX) && defined(_OVERLAY)
    //Clean up the old stuff....
    if( m_pcVideoBuf != NULL )
    {
        for( i=0 ; i<m_nMultiBufferCount ; i++ )
        {
            if( m_pcVideoBuf[i] != NULL )
            {
                if( m_nShmId[i] != -1 && m_bUseShm )
                {
                    ShmHelp::DetachSharedRegion( &m_pcVideoBuf[i], &m_shmInfo[i] );
                    m_nShmId[i] = -1;
                }
                else
                {
                    HX_VECTOR_DELETE(m_pcVideoBuf[i]);
                }
                m_pcVideoBuf[i] = NULL;
            }
        }
    }
    
    if( m_pXvImage )
    {
        for( i=0; i<m_nMultiBufferCount; i++ )
        {
            if( m_pXvImage[i] != NULL )
            {
                XFree( m_pXvImage[i] );
                m_pXvImage[i] = NULL;
            }
        }
    }

    m_nVideoPitch = 0;
    HX_ASSERT( m_display );

    //Clear BitmapInfo struct....
    HXBitmapInfo bmi;
    memset( &bmi, 0, sizeof(HXBitmapInfo) );
    int nResult = MakeBitmap( &bmi,
                              sizeof(bmi),
                              m_nSurfaceCID,
                              m_surfaceSize.cx,
                              m_surfaceSize.cy,
                              NULL,
                              0);
    if( nResult )
    {
        ShmHelp::Init(m_display);
        m_bUseShm = ShmHelp::ShmAvailable();
        for( i=0 ; i<m_nMultiBufferCount ; i++ )
        {
            if( m_bUseShm )
            {
                HX_RESULT retVal = ShmHelp::CreateSharedRegion( bmi.bmiHeader.biSizeImage,
                                                                &m_pcVideoBuf[i],
                                                                &m_nShmId[i],
                                                                &m_shmInfo[i]
                                                                );
                if( retVal != HXR_OK )
                {
                    HX_ASSERT("Can't alloc shared memory segment." == NULL );
                    m_nShmId[i]     = -1;
                    m_pcVideoBuf[i] = NULL;
                }
            }
          
            //did the shm alloc work?
            if( m_pcVideoBuf[i] == NULL )
            {
                m_bUseShm        = FALSE;
                m_nShmId[i]      = -1;
                m_pcVideoBuf[i]  = new UCHAR[bmi.bmiHeader.biSizeImage];
            }
            HX_ASSERT( m_pcVideoBuf );
        }
        m_nVideoPitch = GetBitmapPitch(&bmi);
    }
   
    HX_ASSERT( m_nVideoPitch != 0 );

    //Now create the XvImage to blt to.....
    HX_ASSERT( m_nPortID != -1 );

    for( i=0 ; i<m_nMultiBufferCount ; i++ )
    {
        HX_ASSERT( m_pXvImage[i] == NULL );

        if( m_bUseShm )
        {
	    XLockDisplay(m_display);
            m_pXvImage[i]  = XvShmCreateImage( m_display,
                                               m_nPortID,
                                               m_ulFourCCID,
                                               (char*)m_pcVideoBuf[i],
                                               m_surfaceSize.cx,
                                               m_surfaceSize.cy,
                                               &m_shmInfo[i]
                                               );
	    XUnlockDisplay(m_display);
        }
        else
        {
	    XLockDisplay(m_display);
            m_pXvImage[i] = XvCreateImage( m_display,
                                           m_nPortID,
                                           m_ulFourCCID,
                                           (char*)m_pcVideoBuf[i],
                                           m_surfaceSize.cx,
                                           m_surfaceSize.cy
                                           );
	    XUnlockDisplay(m_display);
        }
          
        if( m_pXvImage[i] == NULL )
        {
            HX_ASSERT("Could not create overlay surface"==NULL );
            //XXgfw, well, what to do here????
        }
    }

    if( m_bUseShm )
    {
        //Find the shm completion event ID.
	XLockDisplay(m_display);
        m_nCompletionEventID = XShmGetEventBase(m_display) + ShmCompletion;
	XUnlockDisplay(m_display);
    }
   
#endif
}

BOOL CUnixSurf::_OverlayAvailable(int nFourCCWanted, int* pnPortID )
{
    BOOL bRetVal = FALSE;
    
#if defined(_LINUX) && defined(_OVERLAY)
    if( !m_bUseOverlays )
    {
        return bRetVal;
    }
   

    unsigned int nNumAdaptors = 0;
    int nImageCount  = 0;
    int nPortID      = -1;
    int i = 0;
    int j = 0;
    int k = 0;
    unsigned int ver = 0;
    unsigned int rel = 0;
    unsigned int req = 0;
    unsigned int ev  = 0;
    unsigned int err = 0;
    int nRetCode = 0;
    XvImageFormatValues *pImageFormats = NULL;
    XvAdaptorInfo       *pAdaptorInfo  = NULL;

    //First, we need to map our cid to a FourCC.
    ULONG32 ulFourCC = 0;
    switch(nFourCCWanted)
    {
       case -1:
           //nothing requested
           break;
       case CID_I420:
           ulFourCC = MAKEFOURCC('I','4','2','0');
           break;
       case CID_YV12:
           ulFourCC = MAKEFOURCC('Y','V','1','2');
           break;
       case CID_YVU9:
           ulFourCC = MAKEFOURCC('Y','V','U','9');
           break;
       case CID_YUY2:
           ulFourCC = MAKEFOURCC('Y','U','Y','2');
           break;
       case CID_UYVY:
           ulFourCC = MAKEFOURCC('U','Y','V','Y');
           break;
       default:
           HX_ASSERT( "Unkown CID" == NULL );
    }
   
    if( m_pSite )
    {
        HX_ASSERT( m_pSite->GetWindow() );
        m_display = (Display*)((m_pSite->GetWindow())->display);
    }

    HX_ASSERT( m_display );

    //XXXgfw this code assumes that there is *only* one overlay available
    //on a machine. We need to change this as more become available....
    if( zm_pXvOwner != NULL )
    {
        //Someone owns the *one* overlay
        goto doneChecking;
    }
   
    XLockDisplay(m_display);
    nRetCode = XvQueryExtension(m_display, &ver, &rel, &req, &ev, &err);
    XUnlockDisplay(m_display);
    if( nRetCode != Success )
    {
        //Our X-Server doesn't support XVideo at all.
        bRetVal = FALSE;
        goto doneChecking;
    }
    XLockDisplay(m_display);
    nRetCode = XvQueryAdaptors( m_display,
                                DefaultRootWindow(m_display),
                                &nNumAdaptors,
                                &pAdaptorInfo );
    XUnlockDisplay(m_display);
    if( nRetCode == Success && nNumAdaptors ) // pjg/gfw geForceII returns 0 adaptors with garbage return pointer
    {
        for(i=0 ; i<nNumAdaptors ; i++)
        {
            if(pAdaptorInfo[i].type & XvImageMask)
            {
                if( ulFourCC==0 )
                {
                    //User just wants to know if we have overlays at all.
                    bRetVal = TRUE;
                    goto doneChecking;
                }
                XLockDisplay(m_display);
                pImageFormats = XvListImageFormats(m_display, pAdaptorInfo[i].base_id, &nImageCount);  
	        XUnlockDisplay(m_display);
                for( j=0 ; j<nImageCount ; j++)
                {
                    if(pImageFormats[j].id == ulFourCC)
                    {
                        for( k=0 ; k<pAdaptorInfo[i].num_ports; k++)
                        {
				XLockDisplay(m_display);
				int nSuc = XvGrabPort(m_display, pAdaptorInfo[i].base_id+k, CurrentTime);
                                XUnlockDisplay(m_display);
			       	if(Success == nSuc)
                            {
                                //XXXgfw later, when we see graphics cards under linux
                                //that have more than one overlay available we will need
                                //to update this code......
                        
                                //Hey! We found one!
                                nPortID = pAdaptorInfo[i].base_id+k;
				XLockDisplay(m_display);
                                XvUngrabPort(m_display,
                                             pAdaptorInfo[i].base_id+k,
                                             CurrentTime );
				XUnlockDisplay(m_display);
                                break;
                            }
                        }
                    }
                    if(nPortID != -1)
                        break;
                }
                XFree(pImageFormats);
            }
            if(nPortID != -1)
                break;
        }
        XvFreeAdaptorInfo(pAdaptorInfo);
      
        if( nPortID != -1 )
        {
            bRetVal = TRUE;
            if( NULL != pnPortID )
                *pnPortID = nPortID;
            m_ulFourCCID = ulFourCC;
        }
      
//        fprintf( stderr, "Found a port that supports %d(%p), it is %d\n",
//                 nFourCCWanted, (void*)ulFourCC,nPortID );
    }
  doneChecking:

#endif   
   
    return bRetVal;
}


HX_RESULT CUnixSurf::_GetCaps(UINT32 *pfSurfaceCaps)
{
    *pfSurfaceCaps = 0;
    if( _OverlayAvailable() )
    {
        *pfSurfaceCaps |= HX_OVERLAY;
    }
   
    return HXR_OK;
}

HX_RESULT CUnixSurf::_CreateOverlay(BOOL bOverlay, int cid, int x, int y)
{
    HX_RESULT retVal = HXR_FAIL;
    int       nSuc   = Success;

//     fprintf( stderr, "CUnixSurf::_CreateOverlay(%p): bOverlay:%d cid:%d x,y: %d %d\n",
//              this, bOverlay, cid, x, y);

    if( x & 15 )
    {
        x = (x&~15)+16;
    }

#if defined(_LINUX) && defined(_OVERLAY) 
    if( bOverlay )
    {
        int  nPortID    = 0;
        BOOL bAvailable = FALSE;
        bAvailable = _OverlayAvailable( cid, &nPortID );

        if( bAvailable )
        {
            HX_ASSERT(m_display);
            //Grab the port.
	     XLockDisplay(m_display);
            nSuc = XvGrabPort( m_display, nPortID, CurrentTime );
	     XUnlockDisplay(m_display);
            if( nSuc == Success )
            {
                //Take ownership of the overlay. A connection to the XServer
                //can grab the overlay port as much as it wants so we can't
                //rely on that to make sure that we only have one surface 
                //using the overlay.
                m_nPortID   = nPortID;
                zm_pXvOwner = this;
            
                //So far so good. Now we need to grab all of our atoms to control
                //the overlay
		XLockDisplay(m_display);
                m_atomColorKey = XInternAtom(m_display, "XV_COLOR_KEY", True);
		XUnlockDisplay(m_display);
                if( m_atomColorKey == None )
                {
                    //There isn't a naming convention for any of these atoms. we
                    //have to disover at run time what they are.
		    XLockDisplay(m_display);
                    m_atomColorKey  = XInternAtom(m_display, "XV_COLORKEY", True);
		    XUnlockDisplay(m_display);
                }
                XLockDisplay(m_display);
                m_atomClipKey    = XInternAtom(m_display, "XV_PAINT_CLIPLIST", True);
		XUnlockDisplay(m_display);

                //Now set some of the atoms we read from the prefs
                if( None != m_atomClipKey)
                {
		     XLockDisplay(m_display);
                    XvSetPortAttribute(m_display, m_nPortID, m_atomClipKey, m_bPaintClipList);
		     XUnlockDisplay(m_display);
                }            

#if defined(HELIX_FEATURE_HARDWARE_COLOR_CONTROLS)
		XLockDisplay(m_display);
                m_atomBrightness = XInternAtom(m_display, "XV_BRIGHTNESS", True);
                m_atomContrast   = XInternAtom(m_display, "XV_CONTRAST", True);
                m_atomHue        = XInternAtom(m_display, "XV_HUE", True);
                m_atomSaturation = XInternAtom(m_display, "XV_SATURATION", True);
                //Get the min and max values for each settable atom.
                int nNumAttributes = 0;
                XvAttribute* pPAList = XvQueryPortAttributes( m_display,
                                                              m_nPortID,
                                                              &nNumAttributes );
		XUnlockDisplay(m_display);
                for( int nIdx=0; nIdx < nNumAttributes; nIdx++ )
                {
                    const char* pszName = pPAList[nIdx].name;
                    int         nMin    = pPAList[nIdx].min_value;
                    int         nMax    = pPAList[nIdx].max_value;
                    
                    stPortAttribute paTmp(nMin, nMax);
                    
                    if( !strcmp(pszName, "XV_BRIGHTNESS" ))
                    {
		         XLockDisplay(m_display);
                        XvGetPortAttribute( m_display, m_nPortID, m_atomBrightness, &m_nCurrBrightness );
			XUnlockDisplay(m_display);
                        paBrightness = paTmp;
                    }
                    if( !strcmp(pszName, "XV_CONTRAST" ))
                    {
		         XLockDisplay(m_display);
                        XvGetPortAttribute( m_display, m_nPortID, m_atomContrast, &m_nCurrContrast );
			XUnlockDisplay(m_display);
                        paContrast = paTmp;
                    }
                    if( !strcmp(pszName, "XV_HUE" ))
                    {
		         XLockDisplay(m_display);
                        XvGetPortAttribute( m_display, m_nPortID, m_atomHue, &m_nCurrHue );
			XUnlockDisplay(m_display);
                        paHue = paTmp;
                    }
                    if( !strcmp(pszName, "XV_SATURATION" ))
                    {
		         XLockDisplay(m_display);
                        XvGetPortAttribute( m_display, m_nPortID, m_atomSaturation, &m_nCurrSaturation );
			XUnlockDisplay(m_display);
                        paSaturation = paTmp;
                    }
                }
                
                if( pPAList )
		    XFree( pPAList );
#endif
                //Now we need to create the overlay surface???
                m_surfaceSize.cx = x;
                m_surfaceSize.cy = y;
                m_nSurfaceCID    = cid;
                m_nBltMode       = HX_OVERLAY_BLT;
                _CreateBuffer();
                //Return good.
                retVal = HXR_OK;
            }
        }
    } //if(bOverlay)...
#endif   
    return retVal;
}

HX_RESULT CUnixSurf::_LockInternalSurface( UCHAR**  ppSurfPtr,
                                           LONG32*  pnSurfPitch,
                                           HXxSize& notused )
{
    HX_RESULT retVal = HXR_OK;

    //Flip internal buffers....
    m_nCurrentBuffer++;
    if( m_nCurrentBuffer >= m_nMultiBufferCount )
    {
        m_nCurrentBuffer=0;
    }

    HX_ASSERT( m_pcVideoBuf[m_nCurrentBuffer] );
    HX_ASSERT( m_nVideoPitch != 0 );
   
    *ppSurfPtr   = (UCHAR*)m_pcVideoBuf[m_nCurrentBuffer];
    *pnSurfPitch = m_nVideoPitch;

    return retVal;
}

HX_RESULT CUnixSurf::_UnlockInternalSurface(UCHAR* pSurfPtr)
{
    return HXR_OK;
}

void CUnixSurf::_SetupDCObjects(HXxDC hxxDC, void** phOldBrush, void** phOldPen)
{
}

void CUnixSurf::_FillRectangle(HXxDC hxxDC,
                               UINT32 left, UINT32 top,
                               UINT32 right, UINT32 bottom)
{
    if( !hxxDC )
        return;

    // XXXNH: constrain ourselves to the current image size.  When the site
    // is shrunk (eg: via user action) we can enter a situation where a
    // damaged rect is queued up for drawing that is larger than the current
    // image.  We will crash if we don't constrain it to the current size.
    XImage* pImage = (XImage*)hxxDC;
    if (right > pImage->width)
	right = pImage->width;
    if (bottom > pImage->height)
	bottom = pImage->height;

    for( int x=left; x<=right-1; x++ )
    {
        for( int y=top; y<=bottom-1; y++ )
        {
#if defined _LINUX &&  defined _OVERLAY
            XPutPixel( pImage, x, y, m_ulColorKey );
#endif
        }
    }
}

void CUnixSurf::_RestoreDCObjects(HXxDC hxxDC, void* hOldBrush, void* hOldPen)
{
}

void CUnixSurf::_GetCompositionSurfaceHXxDC(HXxDC *hdc)
{
    //This needs to return a drawable that points to the composition surface...
    CUnixRootSurf* pSurface = (CUnixRootSurf*)m_pSite->GetRootSurface();
    XImage *pImage = pSurface->_GetCompositionSurfaceDrawable();
    *hdc = (HXxDC)pImage;
}

void CUnixSurf::_ReleaseCompositionSurfaceHXxDC(HXxDC hdc)
{
    //Nothing to do on unix...
}

INT32 CUnixSurf::_InsureColorMatch(INT32 InColor)
{
    return InColor;
}

void CUnixSurf::_SetColorKey(INT32 nColorSpaceLowValue,INT32 nColorSpaceHighValue)
{
#if defined(_LINUX) && defined(_OVERLAY)   
    static BOOL bDoneItAlready = FALSE;
   
    if( m_atomColorKey != None && !bDoneItAlready)
    {
        if( m_ulColorKey == 0x01020304 )
        {
            m_ulColorKey = nColorSpaceHighValue;
            //The user did not set a color key preference...
	     XLockDisplay(m_display);
            XvSetPortAttribute( m_display, m_nPortID, m_atomColorKey, nColorSpaceHighValue );
	     XUnlockDisplay(m_display);
        }
        else
        {
            XLockDisplay(m_display);
            XvSetPortAttribute( m_display, m_nPortID, m_atomColorKey, m_ulColorKey );
	     XUnlockDisplay(m_display);
        }
        bDoneItAlready = TRUE;
    }
#endif   
}

static BOOL CheckIt(Display* dis, XEvent* event, XPointer arg)
{
    BOOL ret = (event->type==(int)arg);
    return ret;
}

#if defined(HELIX_FEATURE_HARDWARE_COLOR_CONTROLS)
inline int _scaleIt( float v, stPortAttribute at )
{
    int nRetVal = (int)(((v+1)*(at.nMaxValue-at.nMinValue))/2+at.nMinValue);
    if( nRetVal < at.nMinValue )
        nRetVal = at.nMinValue;
    if( nRetVal > at.nMaxValue )
        nRetVal = at.nMaxValue;
}

BOOL CUnixSurf::HasHWColorConrols()
{
    BOOL retVal=FALSE;

    if( None != m_atomBrightness &&
        None != m_atomHue        &&
        None != m_atomSaturation &&
        None != m_atomContrast )
    {
        retVal = TRUE;
    }
    return retVal;
}

void CUnixSurf::SetHWColorControls()
{
    float fBright     = m_pSite->GetBrightness();
    float fContrast   = m_pSite->GetContrast();
    float fHue        = m_pSite->GetHue();
    float fSaturation = m_pSite->GetSaturation();
    if( None != m_atomBrightness )
    {
        int nVal = _scaleIt( fBright, paBrightness);
        if( nVal != m_nCurrBrightness )
        {
	     XLockDisplay(m_display);
            XvSetPortAttribute(m_display, m_nPortID, m_atomBrightness, nVal);
	     XUnlockDisplay(m_display);
            m_nCurrBrightness = nVal;
        }
    }            
    if( None != m_atomContrast)
    {
        int nVal = _scaleIt( fContrast, paContrast);
        if( nVal != m_nCurrContrast )
        {
	     XLockDisplay(m_display);
            XvSetPortAttribute(m_display, m_nPortID, m_atomContrast, nVal);
	     XUnlockDisplay(m_display);
            m_nCurrContrast = nVal;
        }
    }            
    if( None != m_atomHue)
    {
        int nVal = _scaleIt( fHue, paHue);
        if( nVal != m_nCurrHue )
        {
	    XLockDisplay(m_display);
            XvSetPortAttribute(m_display, m_nPortID, m_atomHue, nVal);
	    XUnlockDisplay(m_display);
            m_nCurrHue = nVal;
        }
    }            
    if( None != m_atomSaturation)
    {
        int nVal = _scaleIt( fSaturation, paSaturation);
        if( nVal != m_nCurrSaturation )
        {
	     XLockDisplay(m_display);
            XvSetPortAttribute(m_display, m_nPortID, m_atomSaturation, nVal);
	     XUnlockDisplay(m_display);
            m_nCurrSaturation = nVal;
        }
    }
}
#endif        

void CUnixSurf::_UpdateOverlay(HXxRect* dest, HXxRect* src, INT32 inFlags)
{
    HXxWindow*        pWin         = m_pSite->GetWindow();
    CBaseRootSurface* pRootSurface = m_pSite->GetRootSurface();
    int               nRet         = 0;
    Window            winSurface   = 0;
    HXxRect           rectDest     = {0,0,0,0};
    memcpy( &rectDest, dest, sizeof( HXxRect ) ); /* Flawfinder: ignore */

    HX_ASSERT( m_nMultiBufferCount );

#if defined(_LINUX) && defined(_OVERLAY)

    m_ulLastOverlayUpdateTime = HX_GET_TICKCOUNT();
   
    //Draw on the root window if the user wants it. This feature isn't tested
    //and has known problems for certain window managers that create their own
    //window over the root. Looks really cool when it works though. :) Turn
    //this on set the color key to the background color of all your xterms.
    //Very nice.
    if( !m_bWallPaperMode )
    {
        winSurface = (Window)pWin->window;
    }
    else
    {
        winSurface = DefaultRootWindow(m_display);
    }
   
    HX_ASSERT( pWin );

#ifdef _DEBUG   
//     static ULONG32 ulLast  =0;
//     static ULONG32 ulCount =0;
//     static ULONG32 ulCountTot =0;
//     static double  fpsTot=0;
//     ULONG32        ulCurr  = HX_GET_TICKCOUNT();
//     if( ulCurr-ulLast>1000 )
//     {
//        float fps = (float)ulCount/((float)(ulCurr-ulLast)/1000.0);

//        fpsTot += fps;
//        ulCountTot++;
      
//        fprintf( stderr, "%lu elapsed microseconds. %lu frames.  %f FPS (Ave: %f)\n",
//                 ulCurr-ulLast, ulCount, fps, fpsTot/(double)ulCountTot );
//        ulCount = 0;
//        ulLast  = ulCurr;
//     }
//     ulCount++;
#endif   

    //if we are on the root window, calculate new dest rect.
    //Copy the one passed in.
    if( m_bWallPaperMode )
    {
	 XLockDisplay(m_display);
        Screen* pScreen = XDefaultScreenOfDisplay(m_display);
	 XUnlockDisplay(m_display);
        UINT16 uHorzRes = WidthOfScreen(pScreen);
        UINT16 uVertRes = HeightOfScreen(pScreen);

        //if the user wants to stretchtofill then make the dest rect the
        //whole screen, otherwise preserve aspec ration.
        if( m_bStretchToFill )
        {
            //Make it the whole screen.
            rectDest.left   = 0;
            rectDest.top    = 0;
            rectDest.right  = uHorzRes;
            rectDest.bottom = uVertRes;
        }
        else
        {
            //maintain aspect ration.
            //Scale it.
            float fXScale   = (float)uHorzRes/(float)(dest->right-dest->left);
            float fYScale   = (float)uVertRes/(float)(dest->bottom-dest->top);
            float fScale    = (fXScale<fYScale) ? fXScale : fYScale;
            int nWidth      = (int)(fScale*(dest->right-dest->left)+.5);
            int nHeight     = (int)(fScale*(dest->bottom-dest->top)+.5);
            rectDest.left   = 0;
            rectDest.top    = 0;
         
            if( nWidth<uHorzRes )
                rectDest.left = (uHorzRes-nWidth)/2;

            if( nHeight<uVertRes )
                rectDest.top  = (uVertRes-nHeight)/2;

            rectDest.right  = rectDest.left+nWidth;
            rectDest.bottom = rectDest.top+nHeight;
        }
    }


    if( m_bUseShm )
    {

	 XLockDisplay(m_display);
        nRet = XvShmPutImage( m_display,
                              m_nPortID,
                              winSurface,
                              ((CUnixRootSurf*)pRootSurface)->GetGC(),
                              m_pXvImage[m_nCurrentBuffer],
                              src->left, 
                              src->top,
                              src->right - src->left, 
                              src->bottom - src->top,
                              rectDest.left, 
                              rectDest.top,
                              rectDest.right - rectDest.left,      
                              rectDest.bottom - rectDest.top,
                              True
                              );
	XUnlockDisplay(m_display);

    }
    else
    {
        XLockDisplay(m_display);
        nRet = XvPutImage( m_display,
                           m_nPortID, 
                           winSurface,
                           ((CUnixRootSurf*)pRootSurface)->GetGC(),
                           m_pXvImage[m_nCurrentBuffer],		   
                           src->left, 
                           src->top,
                           src->right - src->left, 
                           src->bottom - src->top,
                           rectDest.left, 
                           rectDest.top,
                           rectDest.right - rectDest.left,      
                           rectDest.bottom - rectDest.top
                           );
	XUnlockDisplay(m_display);
    }

    if( m_bUseShm && m_nMultiBufferCount==1 )
    {
        //If we aren't at least double buffering and we are using
        //shared memory, make sure we wait for the completion of the
        //copy of the shared memory segment to the server. Tear not
        //want not.
        HX_ASSERT( m_nCompletionEventID > 0 );
        XEvent event;
	 XLockDisplay(m_display);
        XIfEvent( m_display, &event, CheckIt, (XPointer)m_nCompletionEventID );
	 XUnlockDisplay(m_display);
    }
   
#endif   
}

BOOL CUnixSurf::_IsSurfaceVisible()
{
    return TRUE;
}

void CUnixSurf::_ReleaseSurface()
{
    int i=0;
    
#if defined(_LINUX) && defined(_OVERLAY)   
    //Release all of our overlay resources.....
    if( m_nPortID!=-1 )
    {
        XLockDisplay(m_display);
        XvUngrabPort( m_display, m_nPortID, CurrentTime );
	 XUnlockDisplay(m_display);
        m_nPortID=-1;
        if( zm_pXvOwner==this )
        {
            zm_pXvOwner = NULL;
        }
    }
    if( m_pcVideoBuf != NULL )
    {
        for( i=0 ; i<m_nMultiBufferCount ; i++ )
        {
            if( m_nShmId[i] != -1 && m_bUseShm )
            {
                ShmHelp::DetachSharedRegion( &m_pcVideoBuf[i], &m_shmInfo[i] );
                m_nShmId[i] = -1;
            }
            else
            {
                HX_VECTOR_DELETE(m_pcVideoBuf[i]);
            }
            m_pcVideoBuf[i] = NULL;
        }
    }
    
    if( m_pXvImage )
    {
        for( i=0; i<m_nMultiBufferCount; i++ )
        {
            XFree( m_pXvImage[i] );
            m_pXvImage[i] = NULL;
        }
    }

    memset( &m_surfaceSize, 0, sizeof(m_surfaceSize) );
    m_nSurfaceCID = 1234; //what else?
#endif   
}

HXxDC CUnixSurf::_GetDC(HXxWindow*)
{
    return (HXxDC)m_GC;
}

void CUnixSurf::_ReleaseDC(HXxWindow*, HXxDC)
{
}

void CUnixSurf::_GetWindowDeviceCords(HXxRect* rect )
{
    memset( rect, 0, sizeof( HXxRect) );
}

