/***************************************************************************
                          kvdr_xv.cpp  -  description
                             -------------------
    begin                : Sat Aug  5 13:32:09 MEST 2000
    copyright            : (C) 2000 by Guido
    email                : gfiala@s.netic.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
 /* started from the source codes of kino from Arne Schirmbacher */

#define QT_CLEAN_NAMESPACE
#include <qwidget.h>
#include <qpaintdevice.h>
#include <qwindowdefs.h>
#include <qrect.h>
#include "kvdr_xv.h"
#include <stdio.h>
#include <unistd.h>
#include <kapp.h>
//#include <assert.h>

extern KApplication *akvdr;

XvDisplayer::XvDisplayer( QWidget *q, uint _lb, uint _rb, uint _tb, uint _bb, unsigned long freq )
{
	lb=_lb;rb=_rb;tb=_tb;bb=_bb;
	shmInfo.shmaddr = NULL;
	gotPort = false;
	unsigned int count;
	XvAdaptorInfo	*adaptorInfo;
	this->q=q;
	disp=qt_xdisplay();
  this->window = q->handle();
  xvImage=NULL;
  shmInfo.shmaddr=NULL;
#if (QT_VERSION<300)
  gc=qt_xget_temp_gc(false);
#else
  gc=qt_xget_temp_gc(qt_xscreen(),false);
#endif

 	if ( XvQueryAdaptors(disp, window, &count, &adaptorInfo) ==  Success )
 	{
    for (unsigned int n = 0; gotPort == false && n < count; ++n)
    {
      for ( port = adaptorInfo[n].base_id;
  	  port < adaptorInfo[n].base_id + adaptorInfo[n].num_ports;
  	  port ++ )
  	  {
        if (XvGrabPort(disp, port, CurrentTime) == 0)
        {
  				int formats;
  				XvImageFormatValues  *list;
  				list = XvListImageFormats(disp, port, &formats);
  				for ( int i = 0; i < formats; i ++ )
  				{
  	        if ( list[i].id == 0x59565955/*0x32595559*/ && !gotPort ) gotPort = true;
  				}
  				if ( !gotPort )
  				{
  					XvUngrabPort( disp, port, CurrentTime );
  				}
  				else
  				{
  					grabbedPort = port;
  					break;
  				}
        }
 		  }
 	  }
  }
	else
	{
		gotPort = false;
	}
//	put_video();	
//  xv_setfreq(freq);
//	xv_setfilterquality(1);//less cpu and quality?
}

void XvDisplayer::RecreateXImage(int width, int height)
{
//	assert(width>0 && height>0);
  if (xvImage != NULL)
    XvStopVideo(disp, port, window);

  if (shmInfo.shmaddr != NULL)
  {
    XShmDetach(disp, &shmInfo);
    shmdt(shmInfo.shmaddr);
    shmctl(shmInfo.shmid, IPC_RMID, 0);
    shmInfo.shmaddr=NULL;
  }
	if(xvImage)
	{
	  XFree(xvImage);
    xvImage=NULL;
	}
 	if ( gotPort && (width>0) && (height>0))
 	{
 		xvImage = XvShmCreateImage(disp, port, 0x59565955/*0x32595559*/, 0, width, height+1, &shmInfo);//+1 avoid Xcrashes on my V3k!
 		shmInfo.shmid = shmget( IPC_PRIVATE, xvImage->data_size, IPC_CREAT | 0777);
 		shmInfo.shmaddr = (char *) shmat( shmInfo.shmid, 0, 0);
 		xvImage->data = shmInfo.shmaddr;
 		shmInfo.readOnly = 0;
 		if (!XShmAttach( disp, &shmInfo))
 		{
 			gotPort = false;
 		}
    img_width=width;img_height=height;
  }
  else
  {
    img_width=width;img_height=height;
  }
}

XvDisplayer::~XvDisplayer()
{
	if ( gotPort )
	{
		XvUngrabPort( disp, grabbedPort, CurrentTime );
	}
  if (xvImage != NULL)
    XvStopVideo(disp, port, window);

  if (shmInfo.shmaddr != NULL)
  {
    XShmDetach(disp, &shmInfo);
    shmdt(shmInfo.shmaddr);
    shmctl(shmInfo.shmid, IPC_RMID, 0);
  }
  if (xvImage != NULL)
    XFree(xvImage);
}

bool XvDisplayer::usable()
{
	return gotPort;
}

QSize XvDisplayer::size()
{
	return QSize(img_width,img_height);
}

void XvDisplayer::xv_setfreq(unsigned long freq)
{
	Atom xv_freq=XInternAtom(disp, "XV_FREQ", False);
  if (0!=XvSetPortAttribute(disp,port,xv_freq,freq))
   	perror("Xv-freq");
  XSync(disp,False);
}

void XvDisplayer::xv_setfilterquality(unsigned long quality)
{
	Atom xv_quality=XInternAtom(disp, "XV_FILTER_QUALITY", False);
  if (0!=XvSetPortAttribute(disp,port,xv_quality,quality))
   	perror("Xv-Qualtiy");
  XSync(disp,False);
}

void XvDisplayer::put_video()
{
 	if (0!=XvPutVideo(disp,port,window,gc,
 	           lb,tb,img_width-rb,img_height-bb,
 	           0,0,q->width(),q->height()))
 	  perror("XvPutVideo");
}

void XvDisplayer::put_shm(char *data)
{
	if(data && xvImage)
	{
  	memcpy( xvImage->data, data, xvImage->data_size );//optimizable?
    XvShmPutImage(disp, port, window, gc, xvImage,
            	    lb, tb, img_width-rb,img_height-bb,//-1 because it crashes the X-Server without!
  	     			    0, 0, q->width(),q->height(),false);
    XSync(disp,false);
  }
}

void XvDisplayer::put_di_shm(char *data)
{
	if(data && xvImage)
	{
    //extract both half-frames
    int a,c,j;
    j=xvImage->width*2;//xvImage->data_size/img_height;
  	a=0;
  	c=0;
    for(int i= 0; i < img_height;i+=2)
    {
    	memcpy((void*)(xvImage->data+c),(void*)(data+a),j);
    	a+=j*2;c+=j;
    }
    XvShmPutImage(disp, port, window, gc, xvImage,
              	  lb, tb/2, img_width-rb,(img_height-bb)/2,
      	    		  0, 0, q->width(),q->height(),false);
    XSync(disp,false);
  	a=j;
  	c=0;
    for(int i= 0; i < img_height;i+=2)
    {
    	memcpy((void*)(xvImage->data+c),(void*)(data+a),j);
    	a+=j*2;c+=j;      	
    }
  	XvShmPutImage(disp, port, window, gc, xvImage,
    	         	  lb, tb/2, img_width-rb,(img_height-bb)/2,
      	  			  0, 0, q->width(),q->height(),false);
    XSync(disp,false);
  }
}

void XvDisplayer::put_di_field1(char *data)
{
	if(data && xvImage)
	{
    //extract both half-frames
    int a,c,j;
    j=xvImage->width*2;//xvImage->data_size/img_height;
  	a=0;
  	c=0;
    for(int i= 0; i < img_height;i+=2)
    {
    	memcpy((void*)(xvImage->data+c),(void*)(data+a),j);
    	a+=j*2;c+=j;
    }
    XvShmPutImage(disp, port, window, gc, xvImage,
              	  lb, tb/2, img_width-rb,(img_height-bb)/2,
      	    		  0, 0, q->width(),q->height(),false);
    XSync(disp,false);
  }
}
