/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: unixroot.cpp,v 1.3.4.2 2004/07/09 12:48:45 pankajgupta 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 ***** */

// for shared memory
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/utsname.h>

#include "hxcom.h"
#include "hxwintyp.h"
#include "hxvsurf.h"
#include "hxslist.h"
#include "colormap.h"
#include "hxprefs.h"
#include "hxtick.h"
#include "hxthread.h"
#include "basesite.h"
#include "unixroot.h"
#include "unixsite.h"
#include "unixcmap.h"
#include "shmhelp.h"

//  #ifdef _DEBUG
//  #include <X11/extensions/xf86vmode.h> //for debug video sync rates..
//  #endif

//Work around AIX problem.
#ifdef _AIX
#  define MAX_SHARED_REGIONS ((UINT32)1)
#else
#  define MAX_SHARED_REGIONS ((UINT32)9999)
#endif

CUnixRootSurf::CUnixRootSurf(IUnknown* pContext, CHXBaseSite* pSite )
   : CBaseRootSurface(pContext, pSite)
   , m_bUseShm(FALSE)
   , m_nShmId(0)
   , m_pDisplay(NULL)
   , m_GC(0)
   , m_nScreenNumber(0)
   , m_pXImage(NULL)
   , m_pVisual(NULL)
   , m_unDepth(0)
   , m_pScreen(NULL)
   , m_pYUVScratchBits(NULL)
   , m_nYUVScratchPitch(0)
   , m_pScratchBits(NULL)
   , m_nScratchPitch(0)
   , m_nBitsPerPixel(0)
   , m_nCompositionSize(0)
{
}

HX_RESULT CUnixRootSurf::Init()
{
   //get window and display from main Site.
   HXxWindow* pWindow = m_pSite->GetWindow();
   HX_ASSERT(pWindow);

   m_pDisplay = (Display*)pWindow->display;
   m_window   = (Window)pWindow->window;
    
   HX_ASSERT( m_pDisplay );
   HX_ASSERT( m_window );

   //
   // Now see if our X11 server supports the Shared Memory extension.
   //
   ShmHelp::Init(m_pDisplay);
   m_bUseShm = ShmHelp::ShmAvailable();
   
   //Create the graphics context 
   XGCValues values;
 
   XLockDisplay(m_pDisplay);  
   m_GC = XCreateGC(m_pDisplay, m_window, 0, &values);
   
    //Get X window attributes & visual
   XWindowAttributes attr;
   XGetWindowAttributes(m_pDisplay, m_window, &attr);
   XUnlockDisplay(m_pDisplay);
   m_pVisual = attr.visual;

   // get visual info & depth 
   int nv=0;
   XVisualInfo visInfo;
   memset(&visInfo, 0, sizeof(XVisualInfo));
   XLockDisplay(m_pDisplay);
   visInfo.visualid = XVisualIDFromVisual(m_pVisual);
   XVisualInfo* pVisualInfo = XGetVisualInfo (m_pDisplay, VisualIDMask, &visInfo, &nv);
   m_unDepth       = pVisualInfo->depth;
   m_nScreenNumber = DefaultScreen(m_pDisplay);
   m_pScreen       = XScreenOfDisplay(m_pDisplay, m_nScreenNumber);
   XUnlockDisplay(m_pDisplay);
   m_colormap      = HXGetXColormap(m_pDisplay, m_window);

   // get pixmap (blt) information for the best depth we can display
   int i=0;
   int nNum=0;
   XLockDisplay(m_pDisplay);
   XPixmapFormatValues *pixmap_formats = XListPixmapFormats(m_pDisplay, &nNum);
   XUnlockDisplay(m_pDisplay);
   if(pixmap_formats) 
   {
      for (i=0 ; i<nNum; i++)
      {
         if (pixmap_formats[i].depth == m_unDepth)
         {
            m_nBitsPerPixel = pixmap_formats[i].bits_per_pixel;
         }
      }
      XFree(pixmap_formats);
      pixmap_formats = NULL;
   }

   memset(&m_bmiSave, 0, sizeof(HXBitmapInfo));
   m_bmiSave.bmiHeader.biBitCount    = m_nBitsPerPixel;
   m_bmiSave.bmiHeader.biCompression = (m_unDepth==8 ? BI_RGB : BI_BITFIELDS);
   m_bmiSave.un.dwBitMask[0]         = pVisualInfo->red_mask;
   m_bmiSave.un.dwBitMask[1]         = pVisualInfo->green_mask;
   m_bmiSave.un.dwBitMask[2]         = pVisualInfo->blue_mask;
   //Set primary surface CID.
   m_nCompositionSurfaceCID = GetBitmapColor(&m_bmiSave);
   XFree( pVisualInfo );

//  #ifdef _DEBUG
//     //Lets find out what Hsync and Vsync rates we have for this display.
//     //Ripped from xvidtune.c
//     XF86VidModeModeLine mode_line;
//     int    scrn=0; 
//     int    dot_clock;
//     double vsync_hz, HSyncRate,HTotal, VTotal, PixelClock;
//     if(XF86VidModeGetModeLine(m_pDisplay, scrn, &dot_clock, &mode_line))
//     {
//        PixelClock=dot_clock;
//        HTotal = mode_line.htotal;
//        VTotal = mode_line.vtotal;
//        HSyncRate = PixelClock*1000.0/HTotal;
//        vsync_hz = HSyncRate/VTotal;
//        fprintf( stderr, "This Display's Hsync rate is: %f and Vsync: %f\n",
//                 HSyncRate, vsync_hz );
//     }
   
//  #endif
   
   return HXR_OK;
}

CUnixRootSurf::~CUnixRootSurf()
{
   _DestroyCompositionSurface();

   if( m_GC )
   {
      XLockDisplay(m_pDisplay);
      XFreeGC( m_pDisplay, m_GC );
      XUnlockDisplay(m_pDisplay);
      m_GC=0;
   }
   if (m_pVisual)
   {
      /* PJG: don't have to free visuals */
      m_pVisual = NULL;
   }
   
   if(m_bUseShm)
   {
      if( m_pCompositionSurface != NULL )
         ShmHelp::DetachSharedRegion(&m_pCompositionSurface, &m_shmInfo);
   }
   else
   {
      HX_DELETE(m_pCompositionSurface);
      m_nCompositionSize=0;
      m_bCompositionSurfaceCreated=FALSE;
   }
   
   //XXXgfw any more clean up for these two?
   HX_VECTOR_DELETE( m_pScratchBits );
   HX_VECTOR_DELETE( m_pYUVScratchBits );

}

HX_RESULT CUnixRootSurf::_ResizeVideoBuffer( INT32 nSize)
{
   HX_RESULT retVal=HXR_OK;

   if(nSize <= m_nCompositionSize)
      return retVal;

   if(m_bUseShm) 
   {
      if( m_pCompositionSurface != NULL )
         retVal = ShmHelp::DetachSharedRegion(&m_pCompositionSurface, &m_shmInfo);
      if( retVal==HXR_OK )
      {
         retVal = ShmHelp::CreateSharedRegion( nSize,
                                               &m_pCompositionSurface,
                                               &m_nShmId,
                                               &m_shmInfo
                                               );
         
      }
      if( retVal != HXR_OK )
      {
         m_bCompositionSurfaceCreated = FALSE;
         m_nCompositionSize           = 0;
         m_bUseShm = FALSE;
      }
      else
      {
         //It all worked
         m_nCompositionSize           = nSize;
         m_bCompositionSurfaceCreated = TRUE;
         return retVal;
      }
      
   }

   //We need to fall through here so that if the shared memory stuff
   //above fails we can create it the old fashioned way.
   if(m_pCompositionSurface == NULL)
   {
      m_pCompositionSurface = (UCHAR*) malloc(nSize);
   }
   else
   {
      m_pCompositionSurface = (UCHAR*) realloc(m_pCompositionSurface, nSize);
   }
   if( m_pCompositionSurface )
   {
      m_nCompositionSize = nSize;
   }
   else
   {
      HX_ASSERT("We can't alloc the composition surface." == NULL );
      m_nCompositionSize = 0;
   }

   return retVal;
}

HX_RESULT CUnixRootSurf::_DebugBlt( UCHAR* pImageData, HXBitmapInfoHeader* pBitmapInfo,
                     HXxRect& rDestRect, HXxRect& rSrcRect)
{
   HX_ASSERT( m_window );
   HX_ASSERT( m_pDisplay );
   HX_ASSERT( m_GC );
   XLockDisplay(m_pDisplay);
   XSetForeground( m_pDisplay, m_GC, WhitePixel(m_pDisplay, 0 ));
   XSetBackground( m_pDisplay, m_GC, BlackPixel(m_pDisplay, 0 ));
   XSetLineAttributes(m_pDisplay, m_GC, 5, LineSolid, CapRound, JoinRound );
   XSetForeground( m_pDisplay, m_GC, WhitePixel(m_pDisplay, 0) );
   XFillRectangle( m_pDisplay, m_window, m_GC,
                   rDestRect.left, rDestRect.top,
                   rDestRect.right-rDestRect.left,
                   rDestRect.bottom-rDestRect.top
                   );
   XDrawRectangle( m_pDisplay, m_window, m_GC,
                   rDestRect.left, rDestRect.top,
                   rDestRect.right-rDestRect.left,
                   rDestRect.bottom-rDestRect.top
                   );
   XUnlockDisplay(m_pDisplay);
   return HXR_OK;
}

void CUnixRootSurf::_GetYUVScratchWidthHeight(UINT32* pWidth, UINT32* pHeight)
{
   *pWidth     = m_bmiYUVScratch.bmiHeader.biWidth;
   *pHeight    = m_bmiYUVScratch.bmiHeader.biHeight;
}

XImage* CUnixRootSurf::_GetCompositionSurfaceDrawable()
{
   return m_pXImage;
}



//Not used except for XING.....
void CUnixRootSurf::_CreateYUVScratchSurface(UINT32 width, UINT32 height)
{
   //Remove old bits....
   HX_DELETE(m_pYUVScratchBits);

   //Clear BitmapInfo struct....
   memset(&m_bmiYUVScratch, 0, sizeof(HXBitmapInfo));
   int nResult = MakeBitmap( &m_bmiYUVScratch,
                             sizeof(m_bmiYUVScratch),
                             CID_YUY2,
                             width,
                             height,
                             NULL,
                             0);
   if( nResult )
   {
      m_pYUVScratchBits  = new UCHAR[m_bmiYUVScratch.bmiHeader.biSizeImage];
      m_nYUVScratchPitch = GetBitmapPitch(&m_bmiYUVScratch);
   }
}


void CUnixRootSurf::_GetYUVScratchSurfacePointer(UCHAR** pYUVBits, INT32* YUVPitch)
{
   *pYUVBits = m_pYUVScratchBits;
   *YUVPitch = m_nYUVScratchPitch;
}

HX_RESULT CUnixRootSurf::CreateScratchSurface( int nCompositionSurfaceCID, HXxSize* pSize)
{
   //Remove old bits....
   HX_DELETE(m_pScratchBits);
   
   //Clear BitmapInfo struct....
   memset(&m_bmiScratch, 0, sizeof(HXBitmapInfo));
   int nResult = MakeBitmap( &m_bmiScratch,
                             sizeof(m_bmiScratch),
                             nCompositionSurfaceCID,
                             pSize->cx,
                             pSize->cy,
                             NULL,
                             0);
   if( nResult )
   {
      m_pScratchBits  = new UCHAR[m_bmiScratch.bmiHeader.biSizeImage];
      m_nScratchPitch = GetBitmapPitch(&m_bmiScratch);
   }

   return nResult? HXR_OK : HXR_FAIL;
}

HX_RESULT CUnixRootSurf::ScratchLock(UCHAR** pBits, INT32* pPitch)
{
   *pBits  = m_pScratchBits;
   *pPitch = m_nScratchPitch;
   return HXR_OK;
}

HX_RESULT CUnixRootSurf::ScratchUnlock(UCHAR* pBits)
{
   return HXR_OK;
}

HX_RESULT CUnixRootSurf::_MinimalUnlock(HXxWindow* pWindow)
{
//     Window win = m_window;
//     HX_ASSERT(win);
//     HXxSize hxxSize;
//     m_pSite->GetSize(hxxSize);
   
//     if (m_bUseShm)
//     {
//        XShmPutImage(m_pDisplay,
//                     win,
//                     m_GC,
//                     m_pXImage,
//                     0, 
//                     0,
//                     0,
//                     0,
//                     hxxSize.cx,      
//                     hxxSize.cy,
//                     False
//                     );
//     }
//     else
//     {
//        XPutImage(m_pDisplay,
//                  win,
//                  m_GC,
//                  m_pXImage,
//                  0, 
//                  0,
//                  0,
//                  0,
//                  hxxSize.cx,
//                  hxxSize.cy
//                  );
//     }
     return HXR_OK;
}

HX_RESULT CUnixRootSurf::_LockComposition(UCHAR** pBits, INT32* pPitch)
{
   HX_RESULT retVal = HXR_OK;
   if( !m_bCompositionSurfaceCreated || m_pCompositionSurface==NULL )
   {
      retVal = _CreateCompositionSurface();
   }
   HX_ASSERT( m_pCompositionSurface );
   *pBits  = m_pCompositionSurface;
   *pPitch = m_nCompositionPitch;
   return HXR_OK;
}


void CUnixRootSurf::_BltFromScratchToComposition( HXxRect& rDestRect, HXxRect& rSrcRect)
{
   HX_ASSERT( "Not implemented on unix yet...."==NULL );
}

HX_RESULT CUnixRootSurf::_CreateCompositionSurface()
{
   HX_RESULT retVal = HXR_FAIL;

   if(m_bCompositionSurfaceCreated)
   {
      return HXR_OK;
   }

   HX_ASSERT( !m_bCompositionSurfaceCreated );
   HX_ASSERT( m_pSite );

   //Create a BMI to describe the composition surface
   HXxSize hxxSize;
   m_pSite->GetSize(hxxSize);
   memcpy(&m_compositionSize, &hxxSize, sizeof(HXxSize)); /* Flawfinder: ignore */

   // find out how big we want to allocate.
   if (m_pSite->IsFullScreen())
   {
      UINT16 unDummy=0;
      UINT16 unHorzRes=0;
      UINT16 unVertRes=0;
      m_pSite->_GetDeviceCaps(NULL, unDummy, unHorzRes, unVertRes );
      m_allocatedCompositionSize.cx = unHorzRes;
      m_allocatedCompositionSize.cy = unVertRes;
   }
   else
   {
      m_allocatedCompositionSize.cx = m_compositionSize.cx;
      m_allocatedCompositionSize.cy = m_compositionSize.cy;
   }

   //XXXgfw uhhhh, OK.
   if( m_compositionSize.cx > m_allocatedCompositionSize.cx ||
       m_compositionSize.cy > m_allocatedCompositionSize.cy )
   {
      m_allocatedCompositionSize.cx = m_compositionSize.cx;
      m_allocatedCompositionSize.cy = m_compositionSize.cy;
   }

   //Make the bitmap header struct.
   m_bmiComposition.bmiHeader.biBitCount    = m_bmiSave.bmiHeader.biBitCount;
   m_bmiComposition.bmiHeader.biCompression = m_bmiSave.bmiHeader.biCompression;
   m_bmiComposition.un.dwBitMask[0]         = m_bmiSave.un.dwBitMask[0];
   m_bmiComposition.un.dwBitMask[1]         = m_bmiSave.un.dwBitMask[1];
   m_bmiComposition.un.dwBitMask[2]         = m_bmiSave.un.dwBitMask[2];
   int nResult = MakeBitmap( &m_bmiComposition,
                             sizeof(m_bmiComposition),
                             m_nCompositionSurfaceCID,
                             m_allocatedCompositionSize.cx,
                             m_allocatedCompositionSize.cy,
                             NULL,
                             0
                             );
   m_bmiComposition.bmiHeader.biBitCount    = m_bmiSave.bmiHeader.biBitCount;
   m_bmiComposition.bmiHeader.biCompression = m_bmiSave.bmiHeader.biCompression;
   m_bmiComposition.un.dwBitMask[0]         = m_bmiSave.un.dwBitMask[0];
   m_bmiComposition.un.dwBitMask[1]         = m_bmiSave.un.dwBitMask[1];
   m_bmiComposition.un.dwBitMask[2]         = m_bmiSave.un.dwBitMask[2];

   //Now create the bits....
   _ResizeVideoBuffer( m_bmiComposition.bmiHeader.biSizeImage );
   m_nCompositionPitch = GetBitmapPitch( &m_bmiComposition );

   if( m_pXImage )
   {
      XFree( m_pXImage );
   }

   if( m_bUseShm )
   {
      XLockDisplay(m_pDisplay);
      m_pXImage  = XShmCreateImage( m_pDisplay,
                                    m_pVisual,
                                    m_unDepth,
                                    ZPixmap,
                                    (char*)m_pCompositionSurface,
                                    &m_shmInfo,
                                    m_allocatedCompositionSize.cx,
                                    m_allocatedCompositionSize.cy
                                    );
      XUnlockDisplay(m_pDisplay);
   }
   else
   {
      XLockDisplay(m_pDisplay);	   
      m_pXImage  = XCreateImage( m_pDisplay,
                                 m_pVisual,
                                 m_unDepth,
                                 ZPixmap,
                                 0,
                                 (char*)m_pCompositionSurface,
                                 m_allocatedCompositionSize.cx,
                                 m_allocatedCompositionSize.cy,
                                 32,
                                 0);
      XUnlockDisplay(m_pDisplay);
   }
   
   if( m_pXImage )
   {
      m_bCompositionSurfaceCreated = TRUE;
#ifdef _BIG_ENDIAN
      m_pXImage->byte_order = MSBFirst;
#else
      m_pXImage->byte_order = LSBFirst;
#endif      
      retVal = HXR_OK;
   }
   return retVal;
}

void CUnixRootSurf::_MinimalBlt(HXxRect& srcRect, HXxRect& destRect)
{
   Window win = m_window;
   HX_ASSERT(win);

   if (m_bUseShm)
   {
      XLockDisplay(m_pDisplay);
      XShmPutImage(m_pDisplay,
                   win,
                   m_GC,
                   m_pXImage,
                   destRect.left, 
                   destRect.top,
                   destRect.left,
                   destRect.top,
                   destRect.right - destRect.left,      
                   destRect.bottom - destRect.top,
                   False
                   );
      XUnlockDisplay(m_pDisplay);
   }
   else
   {
      XLockDisplay(m_pDisplay);
      XPutImage(m_pDisplay,
                win,
                m_GC,
                m_pXImage,
                srcRect.left, 
                srcRect.top,
                destRect.left,
                destRect.top,
                destRect.right - destRect.left,      
                destRect.bottom - destRect.top);
      XUnlockDisplay(m_pDisplay);
   }
}

HX_RESULT CUnixRootSurf::_DestroyCompositionSurface()
{
   HX_RESULT retVal = HXR_OK;
   if( m_bCompositionSurfaceCreated )
   {
      if( m_pXImage )
      {
         XFree(m_pXImage);
         m_pXImage = NULL;
      }
      m_bCompositionSurfaceCreated = FALSE;
   }
   
   return retVal;
}



HX_RESULT CUnixRootSurf::BeginOptimizedBlt(HXBitmapInfoHeader* pBitmapInfo)
{
#ifdef _DEBUG
   fprintf(stderr, "CUnixRootSurf::BeginOptimizedBlt Needs to be written\n" );
#endif
   return HXR_NOTIMPL;
}

HX_RESULT CUnixRootSurf::OptimizedBlt( UCHAR*   pImageBits,
                                       HXxRect& rDestRect,
                                       HXxRect& rSrcRect)
{
#ifdef _DEBUG
   fprintf(stderr, "CUnixRootSurf::OptimizedBlt Needs to be written\n" );
#endif
   return HXR_NOTIMPL;
}

HX_RESULT CUnixRootSurf::EndOptimizedBlt(void)
{
#ifdef _DEBUG
   fprintf(stderr, "CUnixRootSurf::EndOptimizedBlt Needs to be written\n" );
#endif
   return HXR_NOTIMPL;
}

HX_RESULT CUnixRootSurf::GetOptimizedFormat(HX_COMPRESSION_TYPE& ulType)
{
#ifdef _DEBUG
   fprintf(stderr, "CUnixRootSurf::GetOptimizedFormat Needs to be written\n" );
#endif
   return HXR_NOTIMPL;
}

HX_RESULT CUnixRootSurf::GetPreferredFormat(HX_COMPRESSION_TYPE& ulType)
{
#ifdef _DEBUG
   fprintf(stderr, "CUnixRootSurf::GetPreferredFormat Needs to be written\n" );
#endif
   return HXR_NOTIMPL;
}

GC CUnixRootSurf::GetGC()
{
   return m_GC;
}
