/***************************************************************************
                          kvdr.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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "kvdr.h"
#include <kwin.h>
#include <qmsgbox.h>
#include <qfile.h>
#include <qimage.h>
#include <kconfig.h>
#include <time.h>
#include <sys/time.h>
#include <signal.h>
#include <stdlib.h>
#include "kvdr.xpm"
//#include <assert.h>

extern "C" {
#ifdef HAVE_STDLIB_H
	#undef HAVE_STDLIB_H
#endif
#include <jpeglib.h>
}

// includes needed for X stuff
#include <X11/Xlib.h>
#include <X11/extensions/xf86dga.h>
#include <X11/extensions/xf86vmstr.h>
#include <X11/extensions/xf86vmode.h>

#ifdef USE_DPMS
extern "C" {
#include <X11/extensions/dpms.h>
}
#endif

#ifdef HAVE_LIBXV
#include "kvdr_xv.h"
#endif

XF86VidModeModeInfo **vm_modelines;

//XError-Handler:
XErrorHandler xerror(Display *, XErrorEvent *event)
{
  return 0;
}

#define MIN_SIZE_X 160
#define MIN_SIZE_Y 120
#define MAX_X_PAL 768
#define MAX_Y_PAL 576
#define MAX_X_NTSC 640
#define MAX_Y_NTSC 480

//some constants used for the PiP-Overlay
#define PIP_SIZE 3   //2..6 makes sense: PIP is 1/2..1/6 size of image
#define PIP_V_OFF 10 //if you want the overlay not in the edges moves the PiP >0..x pixel inside
#define PIP_H_OFF 10

//limits of XV-hardware
#define XV_MAX_XVINFO 1024 //although xvinfo tells me 2048x2048
#define XV_MAX_W (XV_MAX_XVINFO/2) //it's because we paint 4 video's at once.

//config-file-stuff:
#define GROUP_GCONFIG     "CONFIG__general_config"

//not the best but it does'nt compile otherwise:
static const char *labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;

Kvdr::Kvdr(QWidget *parent, const char *name)
: QWidget(parent, name,
  //should give us the required events, but crashs us instead: WType_Desktop
  Qt::WPaintClever | Qt::WResizeNoErase | Qt::WPaintUnclipped |
  Qt::WRepaintNoErase | Qt::WPaintDesktop )
{
	//just for sure
//	assert((PIP_V_OFF>=0) && (PIP_H_OFF>=0) && (PIP_SIZE>2) && (PIP_SIZE<10));
  //cmdline-arguments:
  args = KCmdLineArgs::parsedArgs();
  //some Qt-Stuff:
  QColor bkgc(0,0,0);//Black is useful...
  setMinimumSize( MIN_SIZE_X, MIN_SIZE_Y );
  setBackgroundColor(bkgc);
  setIcon(QPixmap((const char **)kvdr));
  oldXErrorHandler= (int(*)())XSetErrorHandler((XErrorHandler) xerror);
  disp=qt_xdisplay();
  root=DefaultRootWindow(disp);//is this correct?
  screen=XDefaultScreen(disp);

  /* guess physical screen format */
  int width, bank, ram;
  base = NULL;
  if(!XF86DGAGetVideoLL(disp,screen,(int*)&base,&width,&bank,&ram))
  {
    base=NULL;
  }
  int n,i,pixmap_bytes=0;
  XPixmapFormatValues *pf = XListPixmapFormats(disp,&n);
  for (i = 0; i < n; i++) {
    if (pf[i].depth == QColor::numBitPlanes())
      pixmap_bytes = pf[i].bits_per_pixel/8;
  }
  //get color-depth from commandline:
  int tbpp=(args->getOption("b")).toUShort();
  if(tbpp==15 || tbpp==16 || tbpp==24 || tbpp==32)
  {
    bpp=tbpp;//yes, was given
  }
  else
  {
    bpp=pixmap_bytes*8;//as calculated
  }
  // little endian (intel) - dvb does'nt know about big endian anyway!?
  switch (pixmap_bytes)
  {
    case 1: palette = VIDEO_PALETTE_GREY; break;
    case 2: palette = (bpp==15) ? VIDEO_PALETTE_RGB555 :
                                  VIDEO_PALETTE_RGB565;
                                  break;
    case 3: palette = VIDEO_PALETTE_RGB24; break;
    case 4: palette = VIDEO_PALETTE_RGB32; break;
  }
  XWindowAttributes wts;
  if( XGetWindowAttributes(disp, root, &wts))
  {
    display_width=wts.width;display_height=wts.height;
  }
  else
  {
    display_width=0;display_height=0;
  }

  //get the available vidmodes:
  if(-1==XF86VidModeGetAllModeLines(disp,screen,&vm_count,&vm_modelines))
    perror("XF86VidModeGetAllModeLines");

  // now my status variables:
  ismute=false;  isfullscreen=false; ismultivideo=false;
  ovlstat=false; store=false; isPAL=true;dirty_position=true;
  ovlFbSet=false; ovlGeoSet=false; ovlStat=false;ovlClipCount=0;
  didRefresh=false;RefreshEntireScreen=false;cnfdlg=NULL;
  vdr_timeout=0;VidDev=0;vdr_mb_shown=false;
  pip_pos=0;rem_hdisplay=isPAL?MAX_X_PAL:MAX_X_NTSC;rem_vdisplay=isPAL?MAX_Y_PAL:MAX_Y_NTSC;
  dev=-1;rdev=-1;//mixer-things

#ifdef HAVE_LIBXV
  isXv=false;XvDi=0;stopXv=false;
  for(int i=0;i<=3;i++)
  {
		xv_mem[i]=NULL;xv_vm[i]=NULL;xv_mb[i]=0;
  }
  if (args->isSet("i"))
  {
    int t=args->getOption("i").toUShort();
    if (t>=0 && t<=2) XvDi=t;
  }
#endif
  brightness=40000;colour=33000;hue=33000;contrast=40000;
  video_aspect_ratio=4.0/3.0;
  aspect_ratio=video_aspect_ratio/(video_aspect_ratio-(float)display_width/display_height+1.0);

  i_size_x=200;i_size_y=(int)((float)i_size_x/aspect_ratio);
  i_g_size_x=isPAL?MAX_X_PAL:MAX_X_NTSC;i_g_size_y=isPAL?MAX_Y_PAL:MAX_Y_NTSC;
  i_jpg_qual=80;vdr_port=2001;Volume=0;FrontRear=50;MixerChannel=0;basename="";
  num_of_consecutive_expose_events=0;use_dvb_mixer=false;o_cursor=0;

  //Now overwrite it with .kvdrrc-values if available:
  load_kvdrrc();

  if(args->isSet("s") && args->getOption("s").toUShort()<=3)
  {
  	pip_dev=args->getOption("s").toUShort();
  }
  else if(VidDev==0)
  {
  	pip_dev=1;
  }
	else if(VidDev>0)
	{
		pip_dev=0;
	}

  userKeys = new KAccel( this );

  //Todo: make this configurable out of .kvdrrc (via get-key or choice-list)
  userKeys->insert("Quit",i18n("Quit"),"Quit",Key_Q,this,SLOT(QuitSLOT()));
  userKeys->insert("Help",i18n("Help"),"Help",Key_H,this,SLOT(HelpSLOT()));
  userKeys->insert("Fullscreen",i18n("Fullscreen"),"Fullscreen",Key_F,this,SLOT(FullscreenSLOT()));
  userKeys->insert("Mute",i18n("Mute"),"Mute",Key_Enter,this,SLOT(MuteSLOT()));
  userKeys->insert("VolumeUp",i18n("VolumeUp"),"VolumeUp",Key_Plus,this,SLOT(VolumeUpSLOT()));
  userKeys->insert("VolumeDown",i18n("VolumeDown"),"VolumeDown",Key_Minus,this,SLOT(VolumeDownSLOT()));
  userKeys->insert("RearVolumeUp",i18n("RearVolumeUp"),"RearVolumeUp",CTRL|Key_Plus,this,SLOT(RearVolumeUpSLOT()));
  userKeys->insert("RearVolumeDown",i18n("RearVolumeDown"),"RearVolumeDown",CTRL|Key_Minus,this,SLOT(RearVolumeDownSLOT()));
  userKeys->insert("GrabJpg",i18n("GrabJpg"),"GrabJpg",Key_J,this,SLOT(GrabJpgSLOT()));
  userKeys->insert("GrabJpg1",i18n("GrabJpg"),"GrabJpg1",Key_Space,this,SLOT(GrabJpgSLOT()));
  userKeys->insert("GrabPnm",i18n("GrabPnm"),"GrabPnm",Key_P,this,SLOT(GrabPnmSLOT()));
  userKeys->insert("OverlayOnOff",i18n("OverlayOnOff"),"OverlayOnOff",Key_O,this,SLOT(OverlayOnOffSLOT(int)));
  userKeys->insert("Zap Video Dev",i18n("Zap Video Dev"),"Zap Video Dev",Key_Z,this,SLOT(ZapVideoDevSlot()));
  userKeys->insert("Multi Video Dev",i18n("Multi Video Dev"),"Multi Video Dev",Key_X,this,SLOT(MultiVideoDevSlot()));
  userKeys->insert("Configuration",i18n("Configuration"),"Configuration",Key_C,this,SLOT(ConfigurationSLOT()));
  userKeys->insert("PiP",i18n("PiP"),"PiP",Key_I,this,SLOT(PiPposSlot()));
  userKeys->insert("Pip-Zap",i18n("Pip-Zap"),"PiP-Zap",Key_S,this,SLOT(PiPzapSlot()));
  userKeys->insert("PiP-Exchange",i18n("PiP-Exchange"),"PiP-Exchange",Key_E,this,SLOT(PipexchangeSlot()));
  userKeys->insert("Cursor",i18n("Cursor"),"Cursor",Key_A,this,SLOT(OvlCursorSlot()));
#ifdef HAVE_LIBXV
  userKeys->insert("Xv-Deinterlace",i18n("Xv-Deinterlace"),"Xv-Deinterlace",Key_D,this,SLOT(XvDeinterlaceSLOT()));
  userKeys->insert("Xv-Display",i18n("Xv-Display"),"Xv-Display",Key_V,this,SLOT(XvSLOT()));
#endif
  connect(&AdjustSizeTimer,SIGNAL(timeout()),this,SLOT(AdjustAspectRatioSlot()));
  connect(&RefreshDisplayTimer,SIGNAL(timeout()),this,SLOT(RefreshDisplaySlot()));
  connect(&CheckClippingTimer,SIGNAL(timeout()),this,SLOT(CheckClippingTimerSlot()));
  //now the key-assignments for all the keys in vdr:
  userKeys->insert("MenuKey",i18n("MenuKey"),"MenuKey",Key_M,this,SLOT(KeyMenuSLOT()));
  userKeys->insert("OkKey",i18n("OkKey"),"OkKey",Key_Return,this,SLOT(KeyOkSLOT()));
  userKeys->insert("KeyLeft",i18n("KeyLeft"),"KeyLeft",Key_Left,this,SLOT(KeyLeftSLOT()));
  userKeys->insert("KeyRight",i18n("KeyRight"),"KeyRight",Key_Right,this,SLOT(KeyRightSLOT()));
  userKeys->insert("KeyUpd",i18n("KeyUp"),"KeyUp",Key_Up,this,SLOT(KeyUpSLOT()));
  userKeys->insert("KeyDown",i18n("KeyDown"),"KeyDown",Key_Down,this,SLOT(KeyDownSLOT()));
  userKeys->insert("KeyBack",i18n("KeyBack"),"KeyBack",Key_Backspace,this,SLOT(KeyBackSLOT()));
  userKeys->insert("KeyRed",i18n("KeyRed"),"KeyRed",Key_F1,this,SLOT(KeyRedSLOT()));
  userKeys->insert("KeyGreen",i18n("KeyGreen"),"KeyGreen",Key_F2,this,SLOT(KeyGreenSLOT()));
  userKeys->insert("KeyYellow",i18n("KeyYellow"),"KeyYellow",Key_F3,this,SLOT(KeyYellowSLOT()));
  userKeys->insert("KeyBlue",i18n("KeyBlue"),"KeyBlue",Key_F4,this,SLOT(KeyBlueSLOT()));
  userKeys->insert("Key0",i18n("Key0"),"Key0",Key_0,this,SLOT(Key0SLOT()));
  userKeys->insert("Key1",i18n("Key1"),"Key1",Key_1,this,SLOT(Key1SLOT()));
  userKeys->insert("Key2",i18n("Key2"),"Key2",Key_2,this,SLOT(Key2SLOT()));
  userKeys->insert("Key3",i18n("Key3"),"Key3",Key_3,this,SLOT(Key3SLOT()));
  userKeys->insert("Key4",i18n("Key4"),"Key4",Key_4,this,SLOT(Key4SLOT()));
  userKeys->insert("Key5",i18n("Key5"),"Key5",Key_5,this,SLOT(Key5SLOT()));
  userKeys->insert("Key6",i18n("Key6"),"Key6",Key_6,this,SLOT(Key6SLOT()));
  userKeys->insert("Key7",i18n("Key7"),"Key7",Key_7,this,SLOT(Key7SLOT()));
  userKeys->insert("Key8",i18n("Key8"),"Key8",Key_8,this,SLOT(Key8SLOT()));
  userKeys->insert("Key9",i18n("Key9"),"Key9",Key_9,this,SLOT(Key9SLOT()));

  //Special-Functions ;-)
  userKeys->insert("Record",i18n("Record"),"Record",Key_R,this,SLOT(RecordSLOT()));
  userKeys->insert("Page Up",i18n("Page Up"),"PgUp",Key_PageUp,this,SLOT(PgUpSLOT()));
  userKeys->insert("Page Down",i18n("Page Down"),"PgDown",Key_PageDown,this,SLOT(PgDownSLOT()));

  //open the connection to vdr:
  svdrpc=new cSVDRPC();
  svdrpc->Open(vdr_port);

  //decide which mixer to use dvb or sound card
  if (args->isSet("a"))
  {
  	use_dvb_mixer=true;
  }

  //restore volume settings out of kvdrrc
  if (!use_dvb_mixer)
  {
    StrMixerChannel = "";
    register char *p1, *p2 = InitMixer();
    if (p2) {
      while ((p1 = strchr(p2, ':'))) {
        *p1 = 0;
        mixerdevlist.append(p2);

        p2 = p1 + 1;
      } /* while */
      StrMixerChannel = mixerdevlist.at(MixerChannel);
      SetVolume(Volume, StrMixerChannel, FrontRear); //restore Volume
    } /* if */
  }
  if (args->isSet("u")) {
    ismute=true;
    MuteSLOT();//unmute sound
  }
  //Disable Screensaver & APM, make this kvdrrc configurable...
  kde_sc_was_running=(system("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null | sed 's/1/true/g' | grep true 2>/dev/null >/dev/null")==0);
#ifdef USE_DPMS
  DPMSInfo(disp, &dpms_state, &dpms_on);
  if(args->isSet("n"))
  {
  	DPMSDisable(disp);
  	system("dcop kdesktop KScreensaverIface enable false 2>/dev/null >/dev/null");
  }
#endif
  if(-1==XGetScreenSaver(disp,&ss_timeout,&ss_interval,&ss_prefer_blanking,&ss_allow_exposures))
    perror("XGetScreenSaver");


  f_size_x=isPAL?MAX_X_PAL:MAX_X_NTSC;f_size_y=(int)((float)f_size_x/aspect_ratio);
  if(f_size_y>(isPAL?MAX_Y_PAL:MAX_Y_NTSC))
  {
    f_size_y=isPAL?MAX_Y_PAL:MAX_Y_NTSC;f_size_x=(int)((float)f_size_y*aspect_ratio);
  }
  // set XEvents we want to receive
  const uint stdWidgetEventMask =                 // X event mask
        KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | KeymapStateMask |
        ButtonMotionMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask | ExposureMask |
        VisibilityChangeMask | StructureNotifyMask | SubstructureNotifyMask;
  // root window -- need visibility, sub-, structure
  //this-winId(); //instead of "root" ??
  if(-1==XSelectInput(disp,root, VisibilityChangeMask |
               StructureNotifyMask | SubstructureNotifyMask ))
    perror("XSelectInput-rootw");

  // tv window -- need visibility, structure
  if(-1==XSelectInput(disp,(Window) winId(), stdWidgetEventMask ))
    perror("XSelectInput-win");
	//call v4l-conf for each device once
	if (!(args->isSet("x")))
	{
    for(int i=0;i<=3;i++)
    {
    	KProcess v4l;
    	QString s;s.sprintf("%s%d",DEFAULT_VIDEO_DEV,i);
      if(tbpp==15 || tbpp==16 || tbpp==24 || tbpp==32)
      	v4l << "v4l-conf" << "-c" << s << "-b" << args->getOption("b");
      else
      	v4l << "v4l-conf" << "-c" << s;
      v4l.start(KProcess::Block,KProcess::Stderr);
      while (v4l.isRunning());
    }
  }
  //open video-device for overlay & grab capabilities:
  QString s; s.sprintf("%s%d",DEFAULT_VIDEO_DEV,VidDev);
  video_fd.setName((const char*)s);
  video_fd.open(IO_ReadWrite);
  if (video_fd.handle()==-1)
  {
	  QString s; s.sprintf("could not open %s%d\nCheck if other programs opened it\nthat you have rw-permission to the device\nor press 'C' and choose the right one.",
	                       DEFAULT_VIDEO_DEV,VidDev);
    QMessageBox::critical(this,"kvdr:",i18n(s));	
  }
  //Set the Frambuffer and Image properties:
	if (!(args->isSet("x")))
	{
    OvlF(display_width,display_height,(int)base,bpp,palette);
    OvlP(brightness,colour,hue,contrast);
    ovlstat=true;
    setMaximumSize(f_size_x,f_size_y);
  }
  prevSize=geometry();
  if (i_size_x>0) resize(i_size_x,i_size_y);//only when initial size is greater than zero...
  AdjustSizeTimer.start(10,true);//does correct Size now...
  CheckClippingTimer.start(100,false);
  orgCursor=ArrowCursor;//cursor();//make mouse visible on moves
  setMouseTracking(true);
  crs_hide_cnt=0;
  if(args->isSet("f"))
  {
    FullscreenSLOT();
  }

#ifdef HAVE_LIBXV
	Xvdsp=NULL;
#endif
}

Kvdr::~Kvdr()
{
#ifdef HAVE_LIBXV
	if (Xvdsp) delete Xvdsp;
#endif
  //Workaround to wrong position on session restore:
  KConfig *config=kapp->config();
  config->setGroup(GROUP_GCONFIG);
  config->writeEntry("lp_x",pos().x());
  config->writeEntry("lp_y",pos().y());
  //undo anything:
  if(res_was_switched)
    XF86VidModeSwitchToMode(disp,screen,vm_modelines[0]);
#ifdef USE_DPMS
  if(dpms_on) DPMSEnable(disp);//reenable only if previously enabled
#endif
  if(-1==XSetScreenSaver(disp,ss_timeout,ss_interval,ss_prefer_blanking,ss_allow_exposures))
    perror("XSetScreenSaver");
  if(kde_sc_was_running) system("dcop kdesktop KScreensaverIface enable true 2>/dev/null >/dev/null");
  XSetErrorHandler((XErrorHandler)oldXErrorHandler);
  OvlO(0);
  svdrpc->CmdQuit();
  usleep(100000);//just wait to let vdr close the connection
  delete userKeys;
  delete svdrpc;
  if(cnfdlg) delete cnfdlg;
  ExitMixer();
}

bool Kvdr::OvlF(int SizeX, int SizeY, int FbAddr, int Bpp, int Palette)
{
  // this is the problematic part!
  struct video_buffer vb;
  if(!ismultivideo)
  {
    int result = 0;
    if (video_fd.handle() < 0)
      return false;
    if (SizeX <= 0 || SizeY <= 0 || FbAddr == 0 || Bpp / 8 > 4 ||
        Bpp / 8 <= 0 || Palette <= 0 || Palette > 13 || ovlClipCount < 0 ||
        SizeX > 4096 || SizeY > 4096)
    {
       ovlFbSet = ovlGeoSet = false;
       OvlO(false);
       return false;
    }
    result |= ioctl(video_fd.handle(), VIDIOCGFBUF, &vb);
    if (result) perror("VIDIOCGFBUF");
    if (vb.base!=(void*)FbAddr || vb.depth!=Bpp || vb.height!=SizeY ||
        vb.width!=SizeX
        //test appearantly misleading and not required: || vb.bytesperline!=((vb.depth + 1) / 8) * vb.width
       )
    {
    	 fprintf(stderr,"kvdr and v4l-conf disagree about the framebuffer geometry\noverlay will not be shown\n");
       fprintf(stderr,"kvdr: FbAddr=%x Bpp=%d height=%d width=%d bps=%d\n",(unsigned int)vb.base,vb.depth,vb.height,vb.width,vb.bytesperline);
       fprintf(stderr,"v4l : FbAddr=%x Bpp=%d height=%d width=%d bps=%d\n",FbAddr,Bpp,SizeY,SizeX,(vb.depth+1)/8*vb.width);
       ovlFbSet = ovlGeoSet = false;
       ovlClipCount = 0;
       OvlO(false);
       return false;
    }
    else
    {
       ovlFbSizeX = SizeX;
       ovlFbSizeY = SizeY;
       ovlBpp = Bpp;
       ovlPalette = Palette;
       ovlFbSet = true;
       return true;
    }
  }
  else //ismultivideo
  {
  }
  return true;
}

bool Kvdr::OvlP(int Brightness, int Colour, int Hue, int Contrast)
{
	if(isXv) return false;
  struct video_picture vp;
  if(!ismultivideo)
  {
    if (video_fd.handle() < 0)
      return false;
    if (!ovlFbSet)
       return false;
    int result = 0;
    ovlBrightness = Brightness;
    ovlColour = Colour;
    ovlHue = Hue;
    ovlContrast = Contrast;
    result |= ioctl(video_fd.handle(), VIDIOCGPICT, &vp);
    vp.brightness = Brightness;
    vp.colour = Colour;
    vp.hue = Hue;
    vp.contrast = Contrast;
    vp.depth = ovlBpp;
    vp.palette = ovlPalette;
    result |= ioctl(video_fd.handle(), VIDIOCSPICT, &vp);
    return result == 0;
  }
  else //ismultivideo
  {
    for(int i=0;i<=3;i++)
    {
    	if(multivideo_fd[i].handle()!=-1)
    	{
        if(-1==ioctl(multivideo_fd[i].handle(), VIDIOCGPICT, &vp))
        {
        	QString s;s.sprintf("multi-VIDIOCGPICT %d",i);
         	perror(s);
        }
        vp.brightness = Brightness;
        vp.colour = Colour;
        vp.hue = Hue;
        vp.contrast = Contrast;
        vp.depth = ovlBpp;
        vp.palette = ovlPalette;
        if(-1==ioctl(multivideo_fd[i].handle(), VIDIOCSPICT, &vp))
        {
        	QString s;s.sprintf("multi-VIDIOCSPICT %d",i);
         	perror(s);
        }
      }
    }
  }
  return true;
}

bool Kvdr::OvlO(bool Value)
{
	if (isXv) return false;
  int one = 1;
  int zero = 0;
	if (!ismultivideo)
	{
    if (video_fd.handle() < 0)
      return false;
    int result = 0;
    if (!ovlGeoSet && Value)
       return false;
    result |= ioctl(video_fd.handle(), VIDIOCCAPTURE, Value ? &one : &zero);
    if (result) perror("VIDIOCCAPTURE");
    ovlStat = Value;
    if (result)
    {
      ovlStat = false;
      return false;
    }
  }
  else //ismultivideo
  {
    for(int i=0;i<=3;i++)
    {
    	if(multivideo_fd[i].handle()!=-1)
    	{
        if(-1==ioctl(multivideo_fd[i].handle(), VIDIOCCAPTURE, Value ? &one : &zero))
        {
        	QString s;s.sprintf("multi-VIDIOCCAPTURE %d",i);
         	perror(s);
        }
      }
    }
  }
  return true;
}

//temporary place for modified conversion yuv2->rgb24 (borrowed from libavifile)

#define uint8_t unsigned char

struct yuv;

struct lookuptable
{
    int m_plY[256];
    int m_plRV[256];
    int m_plGV[256];
    int m_plGU[256];
    int m_plBU[256];
    lookuptable();
};

#define uint_t unsigned int
#define uint16_t unsigned short
#define uint32_t unsigned long

struct __attribute__((__packed__)) col
{
    static lookuptable t;
    uint8_t r,g,b;
    col() {}
    col(uint8_t R, uint8_t G, uint8_t B) :r(R),g(G),b(B) {}
    inline col(yuv YUV);
    int CREF() const { return (int(b) << 16) + (int(g) << 8) + int(r); }
    uint_t Y() const
    {
        int _Y= 6392l * b + 33055l * g + 16853l * r + 0x8000;
        return (_Y>>16)+16;
    }
    uint16_t bgr15() const {
        return (uint16_t) (((((((r<<5)&0xff00)|g)<<5)&0xfff00)|b)>>3);
    }
    uint16_t bgr16() const {
        return (uint16_t) (((((((r<<5)&0xff00)|g)<<6)&0xfff00)|b)>>3);
    }
    uint32_t bgr32() const { return (uint32_t) ((r<<24) | (g<<16) | b); }
};

struct __attribute__((__packed__)) yuv
{
    uint8_t Y;
    uint8_t Cb;/* aka U */
    uint8_t Cr;/* aka V */
    yuv() {}
    yuv(uint8_t _Y, uint8_t _Cb, uint8_t _Cr)
        :Y(_Y), Cb(_Cb), Cr(_Cr) {}
    yuv(col Col)
    {
        int _Y = ((25*Col.b + 129*Col.g + 66*Col.r) >> 8) + 0x10;
        if (_Y < 0x10) _Y = 0x10;
        else if (_Y > 0xef) _Y = 0xef;
        Y = _Y;

        int _Cb = ((112*Col.b -74*Col.g -38*Col.r) >> 8) + 0x80;
        if (_Cb < 0x10) _Cb = 0x10;
        else if (_Cb > 0xef) _Cb = 0xef;
        Cb = _Cb;

        int _Cr = ((-18*Col.b -94*Col.g + 112*Col.r) >> 8) + 0x80;
        if (_Cr < 0x10) _Cr = 0x10;
        else if (_Cr > 0xef) _Cr = 0xef;
        Cr = _Cr;
    }
};

struct __attribute__((__packed__)) uyvy
{
    uint8_t Cb;/* aka U */
    uint8_t Y;
    uint8_t Cr;/* aka V */
    uyvy() {}
    uyvy(uint8_t _Cb, uint8_t _Y, uint8_t _Cr)
        :Cb(_Cb), Y(_Y), Cr(_Cr) {}
    uyvy(col Col)
    {
        int _Y = ((25*Col.b + 129*Col.g + 66*Col.r) >> 8) + 0x10;
        if (_Y < 0x10) _Y = 0x10;
        else if (_Y > 0xef) _Y = 0xef;
        Y = _Y;

        int _Cb = ((112*Col.b -74*Col.g -38*Col.r) >> 8) + 0x80;
        if (_Cb < 0x10) _Cb = 0x10;
        else if (_Cb > 0xef) _Cb = 0xef;
        Cb = _Cb;

        int _Cr = ((-18*Col.b -94*Col.g + 112*Col.r) >> 8) + 0x80;
        if (_Cr < 0x10) _Cr = 0x10;
        else if (_Cr > 0xef) _Cr = 0xef;
        Cr = _Cr;
    }
};

inline col::col(yuv YUV)
{
    int y = t.m_plY[YUV.Y];

    int B = (y + t.m_plBU[YUV.Cb]) >> 8;
    if (B < 0) B = 0;
    else if (B > 0xff) B = 0xff;
    b = B;

    int G = (y + t.m_plGU[YUV.Cb] + t.m_plGV[YUV.Cr]) >> 8;
    if (G < 0) G = 0;
    else if (G > 0xff) G = 0xff;
    g = G;

    int R = (y + t.m_plRV[YUV.Cr]) >> 8;
    if (R < 0) R = 0;
    else if (R > 0xff) R = 0xff;
    r = R;

}

#define SHORTLINEARGS uint8_t* dest, const uint8_t* src, int w

static void yuy2_bgr24_c(SHORTLINEARGS)
{
    col* d = (col*) dest;
    while (w--)
    {
	yuv p;

	p.Y = src[0];
	p.Cb = src[1];
	p.Cr = src[3];
	d[0] = p;
	p.Y = src[2];
	d[1] = p;

        src += 4;//takes 2pixel at once
        d += 2;//2pixels are 6 bytes
    }
}

static void uyvy_bgr24_c(SHORTLINEARGS)
{
    //printf("uyvy_bgr24_c %d\n", w);
    col* d = (col*) dest;
    while (w--)
    {
	yuv p;

	p.Cb = src[0];
	p.Y = src[1];
	p.Cr = src[2];
	d[0] = p;
	p.Y = src[3];
	d[1] = p;

        src += 4;
        d += 2;
    }
}

lookuptable col::t = lookuptable();

lookuptable::lookuptable()
{
    for(int i = 255; i >= 0; i--)
    {
        m_plY[i] = 298 * (i - 16);
        m_plRV[i] = 408 * (i - 128);
        m_plGV[i] = -208 * (i - 128);
        m_plGU[i] = -100 * (i - 128);
        m_plBU[i] = 517 * (i - 128);
    }
}

//end-temporary-place

bool Kvdr::GImg(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY)
{
  int v,result = 0;
  // just do this once?
  for(v=0;v<=(ismultivideo?3:0);v++)
  {
    if((ismultivideo?multivideo_fd[v].handle():video_fd.handle())!=-1)
    {
    	bool have_to_unmap=false;
      unsigned char *mem=NULL;
      int msize=0;
      struct video_mbuf mbuf;
      struct video_mmap vm;
      vm.frame = 0;
      vm.format = VIDEO_PALETTE_RGB24;
	OvlO(false);
#ifdef HAVE_LIBXV
    	if(!isXv)
#endif    	
    	{
        struct video_window vw;
        ioctl(video_fd.handle(), VIDIOCGWIN,  &vw);
        vw.x = x();
        vw.y = y();
        vw.width = 0;
        vw.height = 0;
        vw.flags = 0;
        vw.clips = ovlClipRects[0];
        vw.clipcount = 0;
        ioctl(video_fd.handle(), VIDIOCSWIN, &vw);
        struct video_capability vc;
        result |= ioctl(ismultivideo?multivideo_fd[v].handle():video_fd.handle(), VIDIOCGCAP, &vc);
        if ((SizeX > 0) && (SizeX <= vc.maxwidth) &&
            (SizeY > 0) && (SizeY <= vc.maxheight))
        {
          vm.width = SizeX;
          vm.height = SizeY;
        }
        else
        {
          vm.width = vc.maxwidth;
          vm.height = vc.maxheight;
        }
        result |= ioctl(ismultivideo?multivideo_fd[v].handle():video_fd.handle(), VIDIOCGMBUF, &mbuf);
        msize = mbuf.size;
        mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, ismultivideo?multivideo_fd[v].handle():video_fd.handle(), 0);
        if (!mem || mem == (unsigned char *)-1) continue;
        have_to_unmap=true;
        // this needs to be done every time:
        result |= ioctl(ismultivideo?multivideo_fd[v].handle():video_fd.handle(), VIDIOCMCAPTURE, &vm);
        result |= ioctl(ismultivideo?multivideo_fd[v].handle():video_fd.handle(), VIDIOCSYNC, &vm.frame);
        // make RGB out of BGR:
        int memsize = vm.width * vm.height;
        unsigned char *mem1 = mem;
        for (int i = 0; i < memsize; i++)
        {
          register unsigned char tmp = mem1[2];
          mem1[2] = mem1[0];
          mem1[0] = tmp;
          mem1 += 3;
        }
      }
#ifdef HAVE_LIBXV
      else
      {
      	if (!(xv_mem[v]) || !(xv_vm[v]) || !(xv_mb[v])) continue; //with next video-device ... return false;
      	mem=(unsigned char*)malloc((xv_vm[v])->width * (xv_vm[v])->height * 3);
        result |= ioctl(ismultivideo?multivideo_fd[v].handle():video_fd.handle(), VIDIOCSYNC, &(xv_vm[v])->frame);
	 	    uyvy_bgr24_c(mem, (unsigned char*)(xv_mem[v])+(xv_mb[v])->offsets[(xv_vm[v])->frame], (xv_vm[v])->width/2*(xv_vm[v])->height);
	 	    (xv_vm[v])->frame=(xv_vm[v])->frame?0:1;//toggle it
	 	    vm.width=(xv_vm[v])->width;vm.height=(xv_vm[v])->height;
      }
#endif
      OverlayOnOffSLOT(1);//      OvlO(ovlstat);
      //continue with writing the files
      if (Quality < 0)
         Quality = 255; //XXX is this 'best'???

      FILE *f;
      if(ismultivideo)
      {
      	//insert videodev for distinction:
        QString s,ext;
        s=FileName;
        ext=s.right(4);
        s=s.left(s.length()-4);
        s.sprintf("%s_Ovl%d%s",(const char*)s,v,(const char*)ext);
        f = fopen(s, "wb");
      }
      else
      {
        f = fopen(FileName, "wb");
      }
      if (f)
      {
        if (Jpeg)
        {
          // write JPEG file:
          struct jpeg_compress_struct cinfo;
          struct jpeg_error_mgr jerr;
          cinfo.err = jpeg_std_error(&jerr);
          jpeg_create_compress(&cinfo);
          jpeg_stdio_dest(&cinfo, f);
          cinfo.image_width = vm.width;
          cinfo.image_height = vm.height;
          cinfo.input_components = 3;
          cinfo.in_color_space = JCS_RGB;

          jpeg_set_defaults(&cinfo);
          jpeg_set_quality(&cinfo, Quality, true);
          jpeg_start_compress(&cinfo, true);

          int rs = vm.width * 3;
          JSAMPROW rp[vm.height];
          for (int k = 0; k < vm.height; k++)
              rp[k] = &mem[rs * k];
          jpeg_write_scanlines(&cinfo, rp, vm.height);
          jpeg_finish_compress(&cinfo);
          jpeg_destroy_compress(&cinfo);
        }
        else
        {
          // write PNM file:
          if (fprintf(f, "P6\n%d\n%d\n255\n", vm.width, vm.height) < 0 ||
              fwrite(mem, vm.width * vm.height * 3, 1, f) < 0)
          {
            result |= 1;
          }
        }
    	  fclose(f);
      }
      else
      {
        result |= 1;
      }
      if(have_to_unmap) munmap(mem, msize);//in case we did it self
      else if (mem) free(mem);//in case we did it from xv
    }//end-if handle-1
  }//end-for-loop
    
  // switch the Overlay on again (gf: why have i to do anything again?)
  OvlG(ovlSizeX, ovlSizeY, ovlPosX, ovlPosY);
  OvlO(true);
  
  if (ovlFbSet)
    OvlP(ovlBrightness, ovlColour, ovlHue, ovlContrast);

  return result == 0;
}

bool Kvdr::OvlG(int SizeX, int SizeY, int PosX, int PosY)
{
	if(isXv) return false;
  struct video_capability vc;
	if (!ismultivideo)
	{
    if (video_fd.handle() < 0)
      return false;
    int result = 0;
    result |= ioctl(video_fd.handle(), VIDIOCGCAP, &vc);
    if (result) perror("VIDIOCGCAP");
    if (!ovlFbSet)
       return false;
    if (SizeX < vc.minwidth || SizeY < vc.minheight ||
        SizeX > vc.maxwidth || SizeY>vc.maxheight
       )
    {
      ovlGeoSet = false;
      OvlO(false);
      return false;
    }
    else
    {
      struct video_window vw;
      result |= ioctl(video_fd.handle(), VIDIOCGWIN,  &vw);
      if (result) perror("VIDIOCGWIN");
      vw.x = PosX;
      vw.y = PosY;
      vw.width = SizeX;
      vw.height = SizeY;
      vw.chromakey = ovlPalette;
      vw.flags = 0;
      vw.clips = ovlClipRects[0];
      vw.clipcount = ovlClipCount;
      result |= ioctl(video_fd.handle(), VIDIOCSWIN, &vw);
      if (result) perror("VIDIOCSWIN");
      if (result)
      {
        ovlGeoSet = false;
        ovlClipCount = 0;
        return false;
      }
      else
      {
         ovlSizeX = SizeX;
         ovlSizeY = SizeY;
         ovlPosX = PosX;
         ovlPosY = PosY;
         ovlGeoSet = true;
         ovlStat = true;
         return true;
      }
    }
  }
  else //ismultivideo!
  {
  	for(int i=0;i<=3;i++)
  	{
  		if(multivideo_fd[i].handle()!=-1)
  		{
        if(-1==ioctl(multivideo_fd[i].handle(), VIDIOCGCAP, &vc))
        {
         	QString s;s.sprintf("multi-VIDIOCGCAP %d",i);
        	perror(s);
        }
        if (SizeX <= vc.maxwidth && SizeY<=vc.maxheight)
        {
      		int x=i&1?1:0;
      		int y=i&2?1:0;
          struct video_window vw;
          if(-1==ioctl(multivideo_fd[i].handle(), VIDIOCGWIN,&vw))
          {
          	QString s;s.sprintf("multi-VIDIOCGWIN %d",i);
          	perror(s);
          }
          if(pip_pos)
          {
          	if(i==VidDev)
          	{
              vw.x = PosX;
              vw.y = PosY;
              vw.width = SizeX;
              vw.height = SizeY;
  	          vw.clipcount = ovlClipCount+1;//+1 because we punched the hole for the PiP !
	          }
	          else
	          {
            	int pip_x=0,pip_y=0;
            	int pip_w=(int)(SizeX/PIP_SIZE);
            	int pip_h=(int)(SizeY/PIP_SIZE);
            	switch(pip_pos)
            	{
            		case 1:pip_x=PosX+SizeX-pip_w-PIP_H_OFF;
            					 pip_y=PosY+PIP_V_OFF;
            					 break;
            		case 2:pip_x=PosX+PIP_H_OFF;
            					 pip_y=PosY+PIP_V_OFF;
            					 break;
            		case 3:pip_x=PosX+PIP_H_OFF;
            					 pip_y=PosY+SizeY-pip_h-PIP_V_OFF;
            					 break;
            		case 4:pip_x=PosX+SizeX-pip_w-PIP_H_OFF;
            					 pip_y=PosY+SizeY-pip_h-PIP_V_OFF;
            					 break;
            	}
              vw.x = pip_x;
              vw.y = pip_y;
              vw.width = pip_w;
              vw.height = pip_h;
	 	          vw.clipcount = ovlClipCount;
            }
          }
          else
          {
            vw.x = PosX+x*SizeX/2;
            vw.y = PosY+y*SizeY/2;
            vw.width = SizeX/2;
            vw.height = SizeY/2;
            vw.clipcount = ovlClipCount;
          }
          vw.chromakey = ovlPalette;
          vw.flags = 0;
          vw.clips = ovlClipRects[i];
          if(-1==ioctl(multivideo_fd[i].handle(), VIDIOCSWIN, &vw))
          {
          	QString s;s.sprintf("multi-VIDIOCSWIN %d %d %d %d %d %d %d %d\n",
          	        i,vw.x,vw.y,vw.width,vw.height,vw.chromakey,vw.flags,vw.clipcount);
          	perror(s);
          }
        }
      }
    }
    ovlSizeX = SizeX;
    ovlSizeY = SizeY;
    ovlPosX = PosX;
    ovlPosY = PosY;
    ovlGeoSet = true;
    ovlStat = true;
  }
  return true;
}

bool Kvdr::OvlC(int ClipCount, CRect *cr)
{
//	assert(ClipCount<=MAXCLIPRECTS);
	if (!ismultivideo)
	{
    if (video_fd.handle() < 0)
      return false;
    if (ovlGeoSet && ovlFbSet)
    {
      for (int j = 0; j < ClipCount; j++)
      {
        ovlClipRects[0][j].x = cr[j].x;
        ovlClipRects[0][j].y = cr[j].y;
        ovlClipRects[0][j].width = cr[j].width;
        ovlClipRects[0][j].height = cr[j].height;
        ovlClipRects[0][j].next = &(ovlClipRects[0][j + 1]);
      }
      ovlClipCount = ClipCount;
      //use it:
      return OvlG(ovlSizeX, ovlSizeY, ovlPosX, ovlPosY);
    }
  }
  else //ismultivideo
  {
   	int pip_x=0,pip_y=0;
   	int pip_w=(int)(ovlSizeX/PIP_SIZE);
   	int pip_h=(int)(ovlSizeY/PIP_SIZE);
   	switch(pip_pos)
   	{
   		case 1:pip_x=ovlSizeX-pip_w-PIP_H_OFF;
   					 pip_y=PIP_V_OFF;
   					 break;
   		case 2:pip_x=PIP_H_OFF;
   					 pip_y=PIP_V_OFF;
   					 break;
   		case 3:pip_x=PIP_H_OFF;
   					 pip_y=ovlSizeY-pip_h-PIP_V_OFF;
   					 break;
   		case 4:pip_x=ovlSizeX-pip_w-PIP_H_OFF;
   					 pip_y=ovlSizeY-pip_h-PIP_V_OFF;
   					 break;
   	}
  	for(int i=0;i<=3;i++)
  	{
  		if(multivideo_fd[i].handle()!=-1)
  		{
  		  int j;
        for (j = 0; j < ClipCount; j++)
        {
          if(pip_pos)
          {
            if(i==VidDev)
            {
              ovlClipRects[i][j].x = cr[j].x;
              ovlClipRects[i][j].y = cr[j].y;
              ovlClipRects[i][j].width = cr[j].width;
              ovlClipRects[i][j].height = cr[j].height;
            }
            else
            { //only so far it overlaps the PiP:
              ovlClipRects[i][j].x = cr[j].x-pip_x;
              ovlClipRects[i][j].y = cr[j].y-pip_y;
              ovlClipRects[i][j].width = cr[j].width;
              ovlClipRects[i][j].height = cr[j].height;
            }
          }
          else
          {
          	int x=i&1?1:0;
          	int y=i&2?1:0;
            ovlClipRects[i][j].x = cr[j].x-ovlSizeX/2*x;
            ovlClipRects[i][j].y = cr[j].y-ovlSizeY/2*y;
            ovlClipRects[i][j].width = cr[j].width;
            ovlClipRects[i][j].height = cr[j].height;
          }
          ovlClipRects[i][j].next = &(ovlClipRects[i][j + 1]);
        }
        if(i==VidDev && pip_pos)
        { //need to punch out the hole for the PiP
          ovlClipRects[i][j].x = pip_x;
          ovlClipRects[i][j].y = pip_y;
          ovlClipRects[i][j].width = pip_w;
          ovlClipRects[i][j].height = pip_h;
				}
      }
    }
    ovlClipCount = ClipCount;//remember +1 in case of PiP-Underlaying-Overlay
    return OvlG(ovlSizeX, ovlSizeY, ovlPosX, ovlPosY);
  }
  return false;
}

#ifdef HAVE_LIBXV
extern KApplication *akvdr;
#endif

void Kvdr::QuitSLOT()
{
#ifdef HAVE_LIBXV
	if (isXv)
	{
		//quit Xv-first
		isXv=false;
	}
#endif
  kapp->quit();
}


#ifdef HAVE_LIBXV

void Kvdr::XvDeinterlaceSLOT()
{
  if(XvDi==2)
    XvDi=0;
  else
    XvDi++;
}

void Kvdr::XvSLOT()
{
	if(((args->isSet("x")) && isXv)) return;
  if(!isXv)
  {
    //set-up Xv:
	  if (!Xvdsp) Xvdsp=new XvDisplayer(this,9,9,0,0,isPAL?50:60);//cut away borders,frequency: l,r,t,b,freq
		if (!Xvdsp->usable())
		{ //Xv is not usable, why ever...
		  QMessageBox::critical(this,"kvdr:",i18n("Xv-display could not be activated\n, \
		                                           please check if the Xserver extensions\n \
		                                           'xvideo' and 'v4l' are in your XF86config!"));
	  	delete Xvdsp;
  		Xvdsp=NULL;
			isXv=false;
			return;
		}
		else
		{
      //store old-Size and allow larger window:
//    	QRect geom=geometry();
      setMaximumSize(2048,2048);
  		//go into loop until switched of
  		isXv=true;
      bool wasfullscreen=false;
  		//set up continous grabbing
  		int my_viddev=VidDev;
  		int my_ismv,my_pip_pos,my_pip_dev;
  		static int recursion=0;
      OverlayOnOffSLOT(-1);
zapped:
			if(recursion==1) return;//no rescursive call desired
			recursion++;
//			printf("recursion=%d\n",recursion);
			usleep(60000);//not to fast... (dvb can't take it!)
      my_ismv=ismultivideo;
      my_pip_pos=pip_pos;
 		  my_viddev=VidDev;
      my_pip_dev=pip_dev;
    	QRect geom=geometry();
//printf("a_ismulti=%d pip_pos=%d pip_dev=%d VidDev=%d\n",my_ismv,my_pip_pos,my_pip_dev,my_viddev);
//printf("a0:%d 1:%d 2:%d 3:%d 4:%d\n",video_fd.handle(),multivideo_fd[0].handle(),multivideo_fd[1].handle(),multivideo_fd[2].handle(),multivideo_fd[3].handle());
    	if(my_ismv)
    	{
    		//open at maximum 4 videodev's
        video_fd.close();
    		for(int i=0;i<=3;i++)
    		{
    			if(multivideo_fd[i].isOpen()) continue;
    			QString s;s.sprintf("%s%i",DEFAULT_VIDEO_DEV,i);
    			multivideo_fd[i].setName(s);
    			multivideo_fd[i].open(IO_Raw | IO_ReadWrite);
    		}
    	}
    	else
    	{
    		//close all but previous-videodev
    		for(int i=0;i<=3;i++)
    		{
    			multivideo_fd[i].close();
    		}
  		int szap=0;
zappy:   		
      	QString s;
      	s.sprintf("%s%d",DEFAULT_VIDEO_DEV,my_viddev);
      	if(video_fd.isOpen()) video_fd.close();
    		video_fd.setName((const char*)s);
        video_fd.open(IO_Raw | IO_ReadWrite);
        if(video_fd.handle()==-1)
        {
//					printf("zap-no-extra-device-found=%d\n",szap);
					if(szap<4)
					{
						szap++;
						if(VidDev<3) VidDev++; else VidDev=0;
						my_viddev=VidDev;
						goto zappy;//up to for times we search a free device
					}
					else
					{
//						printf("szap=%d\n",szap);
					}
        }
    	}
//printf("b0:%d 1:%d 2:%d 3:%d 4:%d\n",video_fd.handle(),multivideo_fd[0].handle(),multivideo_fd[1].handle(),multivideo_fd[2].handle(),multivideo_fd[3].handle());
      struct video_window vw;
   	  struct video_mbuf mbuf[4];
      struct video_mmap vm[4];
      char *mem[4]={0,0,0,0};
      vm[0].frame = 0;vm[1].frame = 0;vm[2].frame = 0;vm[3].frame = 0;
      vm[0].height = 0;vm[1].height = 0;vm[2].height = 0;vm[3].height = 0;
      vm[0].width = 0;vm[1].width = 0;vm[2].width = 0;vm[3].width = 0;
      int w=(geom.width() >>3)<<((my_ismv && (my_pip_pos==0))?2:3);
    	int h=(geom.height()>>3)<<((my_ismv && (my_pip_pos==0))?2:3);
     	if(isPAL)
     	{
      	if(w>MAX_X_PAL) w=MAX_X_PAL;
      	if(h>MAX_Y_PAL) h=MAX_Y_PAL;
     	}
     	else
     	{
      	if(w>MAX_X_NTSC) w=MAX_X_NTSC;
      	if(h>MAX_Y_NTSC) h=MAX_Y_NTSC;
			}
			if(my_pip_pos!=0)
			{
				if(w>XV_MAX_W)
				{
					h=(h*XV_MAX_W)/w;
				  w=XV_MAX_W;//limit on my graphics-card
				}
			}
    	int w_pip=(int)(w/PIP_SIZE);
    	int h_pip=(int)(h/PIP_SIZE);
      struct video_capability vc[4];
      for(int i=0;i<=(my_ismv?3:0);i++)
      {
    	  if( (my_ismv?multivideo_fd[i].handle():video_fd.handle() )!=-1 )
       	{
          ioctl(my_ismv?multivideo_fd[i].handle():video_fd.handle(), VIDIOCGWIN,  &vw);
          vw.x = 0;//geom.x();
          vw.y = 0;//geom.y();
          vw.width = 0;
          vw.height = 0;
          vw.chromakey = VIDEO_PALETTE_UYVY;//YUV422;
          vw.flags = 0;
          vw.clips = ovlClipRects[0];
          vw.clipcount = 0;
          ioctl(my_ismv?multivideo_fd[i].handle():video_fd.handle(), VIDIOCSWIN, &vw);
       		vm[i].width =(my_ismv && i==my_pip_dev && my_pip_dev!=my_viddev && my_pip_pos!=0)?w_pip:w;
       		vm[i].height=(my_ismv && i==my_pip_dev && my_pip_dev!=my_viddev && my_pip_pos!=0)?h_pip:h;
          if (-1==ioctl(my_ismv?multivideo_fd[i].handle():video_fd.handle(), VIDIOCGCAP, &(vc[i])))
          {
          	perror("Xv-VIDIOCGCAP");
          }
//          if ((vm[i].width > vc[i].maxwidth) || (vm[i].height > vc[i].maxheight))
          if ((vm[i].width > 352) || (vm[i].height > 288))
          { //should not happen, otherwise we get some trouble with the PiP-size
            vm[i].width = vc[i].maxwidth;//352
            vm[i].height = vc[i].maxheight;//288
          }
          vm[i].format = VIDEO_PALETTE_UYVY;//YUV422;
         	//first frame (even)
          if (-1==ioctl(my_ismv?multivideo_fd[i].handle():video_fd.handle(), VIDIOCGMBUF, &mbuf[i]))
          {
          	perror("Xv-VIDIOCGMBUF");
          }
          mem[i] = (char *)mmap(0, mbuf[i].size, PROT_READ | PROT_WRITE, MAP_SHARED, my_ismv?multivideo_fd[i].handle():video_fd.handle(), 0);
          xv_mem[i]=mem[i];
          xv_vm[i]=&vm[i];
          xv_mb[i]=&mbuf[i];
//					printf("open:%d:%dx%d\n",i,vm[i].width,vm[i].height);
        }
        else
        {
        	mem[i]=NULL;
          xv_mem[i]=mem[i];
          xv_vm[i]=NULL;
          xv_mb[i]=NULL;
//					printf("close:%d\n",i);
        }
        //start flip-flop-grabbing:
        if((my_ismv?multivideo_fd[i].handle():video_fd.handle())!=-1)
        {
          ioctl(my_ismv?multivideo_fd[i].handle():video_fd.handle(), VIDIOCMCAPTURE, &(vm[i]));
          vm[i].frame=vm[i].frame?0:1;
          ioctl(my_ismv?multivideo_fd[i].handle():video_fd.handle(), VIDIOCMCAPTURE, &(vm[i]));
        }
      }
      if(mem[my_ismv?my_viddev:0]==NULL || vm[my_ismv?my_viddev:0].width==0 || vm[my_ismv?my_viddev:0].height==0)
      { //this happens if we switch to fast between modes, no idea what is wrong then
//      	printf("trapped: %d %x %dx%d\n",my_viddev,mem[my_ismv?my_viddev:0],vm[my_ismv?my_viddev:0].width,vm[my_ismv?my_viddev:0].height);fflush(stdout);
      	recursion--;//it's save now
				return;
      }
      if(size()!=Xvdsp->size())
	     	Xvdsp->RecreateXImage(vm[my_ismv?my_viddev:0].width*((my_ismv && (my_pip_pos==0))?2:1),vm[my_ismv?my_viddev:0].height*((my_ismv && (my_pip_pos==0))?2:1));
      char fstat[2]={0,0};
      char *multibuf=NULL;
      int mbs=vm[my_ismv?my_viddev:0].width*vm[my_ismv?my_viddev:0].height*((my_ismv && (my_pip_pos==0))?8:2);//size of buffer we need
//      assert(mbs<=MAX_X_PAL*MAX_Y_PAL*(my_ismv?8:2));
      if(my_ismv) multibuf=(char*)malloc(mbs);
//      yuv black(col(0,0,0));
      uyvy black(col(0,0,0));
      int yuv_black=(unsigned short)*((unsigned short*)((void*)&black));
 	    if(multibuf)
 	    {
 	    	for(int i=0;i<(mbs/2);i++) *((unsigned short*)multibuf+i)=yuv_black;
 	    }
 	    //DEBUG
 	    printf("ismulti=%d pip_pos=%d pip_dev=%d VidDev=%d mbs=%d\n",my_ismv,my_pip_pos,my_pip_dev,my_viddev,mbs);
 	    printf("ximage: %dx%d\n",Xvdsp->size().width(),Xvdsp->size().height());//vm[my_viddev].width*((my_ismv && (my_pip_pos==0))?2:1),vm[my_viddev].height*((my_ismv && (my_pip_pos==0))?2:1));
      for(int i=0;i<4;i++) printf("%d:%dx%d\n",i,vm[i].width,vm[i].height);
      fflush(stdout);
      //END-DEBUG
  		while( isXv && (VidDev==my_viddev) && (ismultivideo==my_ismv) && (my_pip_pos==pip_pos) &&
  		       (my_pip_dev==pip_dev) && (width()==geom.width()) && (height()==geom.height())
  		     )
  		{ //loop until user-input
        if(fstat[vm[0].frame]==0 && mem[my_ismv?my_viddev:0])
        { //should'nt we wait for Sync first? However it seems, we do appearantly so ;-)
          if(multibuf && my_ismv && !pip_pos)
          { //now copy the frames into position
          	for(register int i=0;i<=3;i++)
          	{
          	  if(multivideo_fd[i].handle()!=-1)
          	  {
            	  register char *mbi=multibuf+((i&1)?vm[my_viddev].width*2:0)+((i>1)?vm[my_viddev].width*vm[my_viddev].height*4:0);
            	  register char *cpi=mem[i]+mbuf[i].offsets[vm[i].frame];
            	  for(int j=0;j<vm[0].height;j++)
            	  {
//            	  		assert(mbi+j*vm[my_viddev].width*4+vm[my_viddev].width*2-multibuf<=mbs);
              	  	memcpy(mbi+j*vm[my_viddev].width*4,cpi+j*vm[my_viddev].width*2,vm[my_viddev].width*2);
            	  }
          	  }
          	}
          }
          else if(pip_pos)
          {
            if(multivideo_fd[my_viddev].handle()!=-1)
            { //we assume that vm[my_viddev] is the size of geom() and fit's exactly into multibuf
//       	  		assert(vm[my_viddev].width*vm[my_viddev].height*2<=mbs);
	           	memcpy(multibuf,mem[my_viddev]+mbuf[my_viddev].offsets[vm[my_viddev].frame],vm[my_viddev].width*vm[my_viddev].height*2);
            }
            //we have to copy the content of the PiP into position...
            //we assume, vm[my_pip_dev] is geom()/PIP_SIZE and lines can be copied apiece
          	int pip_x=0,pip_y=0;
          	int pip_w=(int)(vm[my_viddev].width/PIP_SIZE);
          	int pip_h=(int)(vm[my_viddev].height/PIP_SIZE);
          	switch(pip_pos)
          	{
          		case 1:pip_x=vm[my_viddev].width -pip_w-PIP_H_OFF;
          					 pip_y=PIP_V_OFF;
          					 break;
          		case 2:pip_x=PIP_H_OFF;
          					 pip_y=PIP_V_OFF;
          					 break;
          		case 3:pip_x=PIP_H_OFF;
          					 pip_y=vm[my_viddev].height-pip_h-PIP_V_OFF;
          					 break;
          		case 4:pip_x=vm[my_viddev].width -pip_w-PIP_H_OFF;
          					 pip_y=vm[my_viddev].height-pip_h-PIP_V_OFF;
          					 break;
          	}
          	pip_x=pip_x&(~0x0001);//otherwise the colors get scrambled ;-)
          	register char *mbi=multibuf+pip_x*2+vm[my_viddev].width*2*pip_y;
          	register char *cpi=mem[my_pip_dev]+mbuf[my_pip_dev].offsets[vm[my_pip_dev].frame];
          	for(register int i=0;i<pip_h;i++)
          	{
//          	  assert(mbi+i*vm[my_viddev].width*2+pip_w*2-multibuf<=mbs);
          	  if(my_pip_dev!=-1 && multivideo_fd[my_pip_dev].handle()!=-1 && multibuf)
            		memcpy(mbi+i*vm[my_viddev].width*2,cpi+i*pip_w*2,pip_w*2);
              else if(multibuf && vm[my_viddev].width>0)
                for(register int j=0;j<pip_w;j++) *((unsigned short*)mbi+i*vm[my_viddev].width+j)=yuv_black;
          	}
          }
    			switch(XvDi)
    			{
    			  case 0://put last complete frame 25Hz interlaced:
    			    if(multibuf && my_ismv)
	    	        Xvdsp->put_shm(multibuf);
    			    else
	    	        Xvdsp->put_shm(mem[0]+mbuf[0].offsets[vm[0].frame]);
    	        break;
    			  case 1://put last complete frame 50Hz deinterlaced:
    			    if(multibuf && my_ismv)
	    	        Xvdsp->put_di_shm(multibuf);
    			    else
	    	        Xvdsp->put_di_shm(mem[0]+mbuf[0].offsets[vm[0].frame]);
    	        break;
    			  case 2://put last field1 25Hz deinterlaced:
	  			    if(multibuf && my_ismv)
	    	        Xvdsp->put_di_field1(multibuf);
    			    else
		  	        Xvdsp->put_di_field1(mem[0]+mbuf[0].offsets[vm[0].frame]);
    	        break;
  	      }
  	      fstat[vm[0].frame]=1;
  	    }
        else
        {
        	usleep(10000);
        }
//  		  //in case we changed the size:
//        if(width()!=geom.width() || height()!=geom.height())
//        {
//        	printf("resize-to: %dx%d\n",width(),height());fflush(stdout);
//        	geom=geometry();
//        	int w=(geom.width() >>3)<<((my_ismv && (my_pip_pos==0))?2:3);
//        	int h=(geom.height()>>3)<<((my_ismv && (my_pip_pos==0))?2:3);
//         	if(isPAL)
//         	{
//          	if(w>MAX_X_PAL) w=MAX_X_PAL;
//          	if(h>MAX_Y_PAL) h=MAX_Y_PAL;
//         	}
//         	else
//         	{
//          	if(w>MAX_X_NTSC) w=MAX_X_NTSC;
//          	if(h>MAX_Y_NTSC) h=MAX_Y_NTSC;
//    			}
//    			if(my_pip_pos!=0)
//    			{
//    				if(w>XV_MAX_W)
//    				{
//    					h=(h*XV_MAX_W)/w;
//    				  w=XV_MAX_W;//limit on my graphics-card!
//    				}
//    			}
//        	int w_pip=(int)(w/PIP_SIZE);
//        	int h_pip=(int)(h/PIP_SIZE);
//          for(int i=0;i<=(ismultivideo?3:0);i++)
//          {
//         		vm[i].width =(i==my_pip_dev && my_pip_dev!=my_viddev && my_pip_pos!=0)?w_pip:w;
//         		vm[i].height=(i==my_pip_dev && my_pip_dev!=my_viddev && my_pip_pos!=0)?h_pip:h;
//            if ((vm[i].width > vc[i].maxwidth) && (vm[i].height > vc[i].maxheight))
//            {
//              vm[i].width = vc[i].maxwidth;
//              vm[i].height = vc[i].maxheight;
//            }
//          }
//        	Xvdsp->RecreateXImage(vm[my_viddev].width*((my_ismv && (my_pip_pos==0))?2:1),vm[my_viddev].height*((my_ismv && (my_pip_pos==0))?2:1));
//        	if(multibuf)
//        	{
//        		free(multibuf);
//        		mbs=vm[my_viddev].width*vm[my_viddev].height*((my_ismv && (my_pip_pos==0))?8:2);
////			      assert(mbs<=MAX_X_PAL*MAX_Y_PAL*(my_ismv?8:2));
//        		multibuf=(char*)malloc(mbs);
//      	    if(multibuf)
//      	    {
//      	    	for(int i=0;i<(mbs/2);i++) *((unsigned short*)multibuf+i)=yuv_black;
//      	    }
//        	}
//        }
        //grab with the (new) size
        bool nograb=true;
        for(int i=0;i<=(my_ismv?3:0);i++)
        {
    	    if( !stopXv && ( -1!=(my_ismv?multivideo_fd[i].handle():video_fd.handle()) ) )
    	    {
            vm[i].frame=vm[i].frame?0:1;
            if(i==my_viddev) ioctl(my_ismv?multivideo_fd[i].handle():video_fd.handle(), VIDIOCSYNC, &(vm[i].frame));
            ioctl(my_ismv?multivideo_fd[i].handle():video_fd.handle(), VIDIOCMCAPTURE, &(vm[i]));
      	    fstat[vm[0].frame]=0;//not exactly true, but very likely :-)
      	    nograb=false;
          }
        }
       	if(nograb) usleep(40000);
  		  akvdr->processEvents();
  		  if (isfullscreen) wasfullscreen=true;
   	  }//end while-loop
      for(int i=0;i<=3;i++)
      {
    		xv_mem[i]=NULL;xv_vm[i]=NULL;xv_mb[i]=NULL;
				if(mem[i]) {munmap(mem[i], mbuf[i].size);mem[i]=NULL;}
      }
      if(multibuf){free(multibuf);multibuf=NULL;}
      recursion--;//it's save now.
  		if( (VidDev!=my_viddev) || (ismultivideo!=my_ismv) || (my_pip_pos!=pip_pos) ||
  		    (my_pip_dev!=pip_dev) ||
  		    (width()!=geom.width()) || (height()!=geom.height()) )
    		    goto zapped;//this was no exit, just zapped
    	if (Xvdsp) delete Xvdsp;
    	Xvdsp=NULL;
  		//set old size:
  		if(!(args->isSet("x")))
  		{
        if(isfullscreen)
        {
          setMaximumSize(isPAL?rem_hdisplay:MAX_X_NTSC,isPAL?rem_vdisplay:MAX_Y_NTSC);
      		resize(isPAL?rem_hdisplay:MAX_X_NTSC,isPAL?rem_vdisplay:MAX_Y_NTSC);
        }
        else
        {
          setMaximumSize(f_size_x,f_size_y);
      		resize(geom.width(),geom.height());
        }
        OverlayOnOffSLOT(1);
        usleep(40000);
        OverlayOnOffSLOT(-1);
        usleep(40000);
        OverlayOnOffSLOT(1);//workaround for dvb-driver-failure!
        if (ovlFbSet)
          OvlP(ovlBrightness, ovlColour, ovlHue, ovlContrast);
      }
      else
      {
      	akvdr->exit_loop();
      	akvdr->quit();//sometimes we hang inside eventloop recursion?
      	exit(0);//...therefore we go for sure.
      }
 	  }
  }
  else
  {
  	isXv=false;
  }
}
#endif

void Kvdr::ZapVideoDevSlot()
{
	int xzap=0;
	if (ismultivideo && !isXv) return;
	int temp_dev=VidDev;
zap_again:
  if(temp_dev<3) temp_dev++;
  else temp_dev=0;
  if(pip_pos && ismultivideo)
  { //take care that we don't are the same
  	if(temp_dev==pip_dev || !multivideo_fd[temp_dev].isOpen())
  	{ //we have to use the next available video if any available
  		if(xzap>4)
  			pip_dev=-1;//we have only one card available!
  		else
  		{
  			xzap++;
  			goto zap_again;
  		}
  	}
  }
  VidDev=temp_dev;//here we change it finally
	if(isXv) return;//XvSLOT handles that itself...
	if (video_fd.handle()!=-1)
	{
		OverlayOnOffSLOT(-1);
		video_fd.close();
	}
	QString s;
	s.sprintf("%s%d",DEFAULT_VIDEO_DEV,VidDev);
	video_fd.setName((const char*)s);
	video_fd.open(IO_ReadWrite);
	if(video_fd.handle()==-1)
	{
		if(xzap<4)
		{
  		xzap++;
  	  goto zap_again;
	  }
	  else
	  {
	  	return;//should never happen anyway: vid_dev taken away below kvdr
	  }
	}
  OvlF(display_width,display_height,(int)base,bpp,palette);
  OvlP(brightness,colour,hue,contrast);
	OverlayOnOffSLOT(1);
	usleep(20000);//not to fast...
}

void Kvdr::MultiVideoDevSlot()
{
	ismultivideo=!ismultivideo;
	if(pip_dev==VidDev) PiPzapSlot();
	if (isXv) return;//XvSlot handles that itself!
  OverlayOnOffSLOT(-1);
	if(ismultivideo)
	{
		//open at maximum 4 videodev's
    video_fd.close();
		for(int i=0;i<=3;i++)
		{
			QString s;s.sprintf("%s%i",DEFAULT_VIDEO_DEV,i);
			multivideo_fd[i].setName(s);
			multivideo_fd[i].open(IO_ReadWrite);
		}
	}
	else
	{
		//close all but previous-videodev
		for(int i=0;i<=3;i++)
		{
			multivideo_fd[i].close();
		}
    video_fd.open(IO_ReadWrite);
	}
  OverlayOnOffSLOT(1);
	usleep(20000);//not to fast...
}

void Kvdr::HelpSLOT()
{
  kapp->invokeHelp("","kvdr");
}

void Kvdr::FullscreenSLOT()
{
  isfullscreen=!isfullscreen;
  if(isfullscreen)
  {
    saveGeom=geometry();
    savePos=frameGeometry();
    // get the number of the fullscreen-modeline:
    int i,j=-1,k=-1;
    for (i = 0; i < vm_count; i++)
    {
   	  if ((isPAL?768:MAX_X_NTSC) == vm_modelines[i]->hdisplay &&
   	      (isPAL?576:MAX_Y_NTSC) == vm_modelines[i]->vdisplay)
   	      j=i;
   	  if ((isPAL?800:MAX_X_NTSC) == vm_modelines[i]->hdisplay &&
   	      (isPAL?600:MAX_Y_NTSC) == vm_modelines[i]->vdisplay)
   	   	  k=i;
    }
    if(j!=-1) i=j;//use perfect modeline if available
    else if(k!=-1) i=k;//use 800x600 if no perfect one available
    //else: use desktop-resolution!
    if (i != 0 && i != vm_count && !(args->isSet("d") && isXv))
    {
	    XF86VidModeSwitchToMode(disp,screen,vm_modelines[i]);
	    res_was_switched= true;
    }
    else
    {
  	  res_was_switched=false;
      i=0;
    }
    XF86VidModeSetViewPort(disp,screen,0,0);
    if (!isXv)
    {
      OvlO(0);
      setMaximumSize(vm_modelines[i]->hdisplay,vm_modelines[i]->vdisplay);
      rem_hdisplay=vm_modelines[i]->hdisplay;rem_vdisplay=vm_modelines[i]->vdisplay;//need for XvSLOT()
    }
    resize(vm_modelines[i]->hdisplay,vm_modelines[i]->vdisplay);
//    if (!isXv) setMinimumSize(vm_modelines[i]->hdisplay,vm_modelines[i]->vdisplay);
    move(savePos.x()-saveGeom.x(),savePos.y()-saveGeom.y());//failure in Qt!
    float aspect_ratio=video_aspect_ratio/1.33*
               ((float)vm_modelines[i]->hdisplay/
                        vm_modelines[i]->vdisplay);
    f_x=isPAL?MAX_X_PAL:MAX_X_NTSC,f_y=(int)((float)f_x/aspect_ratio);
    if (isPAL)
	    f_y=f_y>MAX_Y_PAL?MAX_Y_PAL:f_y;
	  else
	    f_y=f_y>MAX_Y_NTSC?MAX_Y_NTSC:f_y;	
    if (!isXv)
    {
      OvlG(f_x,f_y,(vm_modelines[i]->hdisplay-f_x)/2,
                   (vm_modelines[i]->vdisplay-f_y)/2);
      OvlO(1);ovlstat=true;//makes sense - doesn't it?
    }
#ifdef USE_DPMS
    DPMSDisable(disp);
#endif
    if(-1==XSetScreenSaver(disp,0,ss_interval,ss_prefer_blanking,ss_allow_exposures))
      perror("XSetScreenSaver-off");
    system("dcop kdesktop KScreensaverIface enable false 2>/dev/null >/dev/null");
    refreshDisplay(savePos);
    orgpos=QCursor::pos();
    QCursor::setPos(0,0);
  }
  else
  {
    if(res_was_switched)
    {
	     XF86VidModeSwitchToMode(disp,screen, vm_modelines[0]);
       QCursor::setPos(orgpos);
		}
    if (!isXv)
    {
      OvlO(0);
//      setMinimumSize( MIN_SIZE_X, MIN_SIZE_Y );
    }
    move(savePos.x(),savePos.y());
    if(!isXv) OvlG(saveGeom.width(),saveGeom.height(),saveGeom.x(),saveGeom.y());
    resize(saveGeom.width(),saveGeom.height());
    if (!isXv) setMaximumSize(f_size_x,f_size_y);
   	QRect geom= geometry();//ohne Rahmen, sicher ist sicher!
   	if (!isXv)
   	{
      OvlG(geom.width(),geom.height(),geom.x(),geom.y());
  	  OvlO(1);ovlstat=true;//not to bad?
	  }
#ifdef USE_DPMS
    if(dpms_on) DPMSEnable(disp);//reenable only if previously enabled
#endif
    if(-1==XSetScreenSaver(disp,ss_timeout,ss_interval,ss_prefer_blanking,ss_allow_exposures))
      perror("XSetScreenSaver-on");
    if(kde_sc_was_running) system("dcop kdesktop KScreensaverIface enable true 2>/dev/null >/dev/null");
    QWidget *d = kapp->desktop();
    QRect rect(0,0,d->width(),d->height());
    refreshDisplay(rect);
    RefreshDisplayTimer.start(100,true);//does refresh display in 100ms
  }
  if(pip_pos && ismultivideo) OvlC(ovlClipCount,cr);//at least in PiP-Mode we need it!
}

void Kvdr::GetNextFilename(QString& fn, const char* basename, const char *ext)
{
  time_t now=time(NULL);
  localtime(&now);
  QTime temp;temp.start();
  QString CName;
//  if (!ismultivideo)
//    CName=QString(svdrpc->CmdChan())+QString("-");
//  else
    CName="";//works not reliable and not fast enough, therefore - skip it..
  int n=0;
  QFile qf;
  do
  {
#ifdef USE_VFAT_FS
    fn.sprintf("%s%s%d.%02d.%02d-%02d;%02d;%02d-%02d%s",basename,
#else
    fn.sprintf("%s%s%d.%02d.%02d-%02d:%02d:%02d-%02d%s",basename,
#endif
                                     (const char*)CName,
                                     QDate::currentDate().year(),
                                     QDate::currentDate().month(),
                                     QDate::currentDate().day(),
                                     temp.hour(),
                                     temp.minute(),
                                     temp.second(),n,ext);
    qf.setName(fn);
  }while(qf.exists() && (n++<30));
}

void Kvdr::GrabJpgSLOT()
{
  QString fn;
  QString ext=".jpg";
  GetNextFilename(fn,basename,ext);
  GImg(fn,1,i_jpg_qual,i_g_size_x,i_g_size_y);
}

void Kvdr::GrabPnmSLOT()
{
  QString fn;
  QString ext=".pnm";
  GetNextFilename(fn,basename,ext);
  GImg(fn,0,0,i_g_size_x,i_g_size_y);
}

void Kvdr::OverlayOnOffSLOT(int state)
{
	if (isXv) return;
	switch(state)
	{
		case 0:  ovlstat=!ovlstat;break;
		case 1:  ovlstat=true;break;
		case -1: ovlstat=false;break;
		default: ovlstat=!ovlstat;break;
	}
  if(ovlstat)
  {
    QRect geom= geometry();//ohne Rahmen
    if(isfullscreen)
    {
      OvlG(f_x,f_y,(geom.width() -f_x)/2,
                              (geom.height()-f_y)/2);
    }
    else
    {
      OvlG(geom.width(),geom.height(),geom.x(),geom.y());
    }
    doClipping();
  }
  OvlO(ovlstat);
}

void Kvdr::ConfigurationSLOT()
{
    if(!cnfdlg)cnfdlg= new ckvdrcfg(this,i18n("Kvdr configuration dialog"));
    cnfdlg->show();
    if(isfullscreen && res_was_switched)
    { //in fullscreen mode we need to move the cnf-dialog to the center of the viewport:
      cnfdlg->move(isPAL?(rem_hdisplay-cnfdlg->maximumSize().width())/2:(MAX_X_NTSC-cnfdlg->maximumSize().width())/2,
                   isPAL?(rem_vdisplay-cnfdlg->maximumSize().height())/2:(MAX_Y_NTSC-cnfdlg->maximumSize().height())/2);
      cnfdlg->raise();//to have it visible
    }
    //somehow we lose this here, so we set it again:
    const uint stdWidgetEventMask =
        KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | KeymapStateMask |
        ButtonMotionMask | FocusChangeMask | ExposureMask | VisibilityChangeMask |
        StructureNotifyMask | SubstructureNotifyMask| VisibilityChangeMask | StructureNotifyMask;
    // tv window -- need visibility, structure
    if(-1==XSelectInput(disp,(Window) winId(), stdWidgetEventMask ))
      perror("XSelectInput-winId");
    if(-1==XSelectInput(disp,root, VisibilityChangeMask |
              StructureNotifyMask | SubstructureNotifyMask ))
      perror("XSelectInput-winId");

}

void Kvdr::moveEvent(QMoveEvent * ev)
{
	if(isXv) return;
  //workaround for Session-Restore-Wrong-MoveEvent-Problem:
  if(dirty_position && (ev->pos().x()==0) && (ev->pos().y()==0))
  {
		if (!(args->isSet("x"))) move(lp_x,lp_y);
		dirty_position=false;
  }
  if(ovlstat)
  {
    QRect geom= geometry();//ohne Rahmen
    if(isfullscreen)
    {
      OvlG(f_x,f_y,(geom.width()-f_x)/2+geom.x(),
                   (geom.height()-f_y)/2+geom.y());
    }
    else
    {
      OvlG(geom.width(),geom.height(),geom.x(),geom.y());
    }
    refreshDisplay(prevSize);
    prevSize=geom;
    refreshDisplay(geom);
  }
}

void Kvdr::resizeEvent(QResizeEvent * ev)
{
  QRect geom= geometry();//ohne Rahmen
  AdjustSizeTimer.start(100,true);//does correct Size in 100ms...
  if(ovlstat && !isfullscreen)
  {
    OvlG(geom.width(),geom.height(),geom.x(),geom.y());
    refreshDisplay(prevSize);
    prevSize=geom;
    refreshDisplay(geom);
  }
  else
  {
    prevSize=geom;
  }
}

void Kvdr::hideEvent(QHideEvent * ev)
{
  ovlstat=false;
}

void Kvdr::showEvent(QShowEvent * ev)
{
  ovlstat=true;
}

void Kvdr::AdjustAspectRatioSlot()
{
  QRect geom=geometry();//ohne Rahmen
  float actARatio=(float)geom.width()/geom.height();
  if(!isfullscreen)
  {
    if( (actARatio>(aspect_ratio*1.01))
        || //let it have 1% failure without action...
        (actARatio<(aspect_ratio*0.99))
      )
    {
      //choose the closest shrinking-target-size:
      if(actARatio>aspect_ratio)
      { //it's a bit to long, make it shorter:
        geom.setWidth((int)((float)geom.height()*aspect_ratio));
      }
      else
      { //it's a bit to high, make it smaller:
        geom.setHeight((int)((float)geom.width()/aspect_ratio));
      }
      if(geometry()!=geom)
      { //only if we changed something:
	      resize(geom.width(),geom.height());
	      if (ovlStat) OvlG(geom.width(),geom.height(),geom.x(),geom.y());
	      if (ovlStat) refreshDisplay(prevSize);
		    prevSize=geom;
  	    if (ovlStat) refreshDisplay(geom);
      }
    }
  }
}

#define safety 20
void Kvdr::refreshDisplay(const QRect& rect)
{
  XSetWindowAttributes xswa;
  unsigned long mask;
  Window rwin;

  xswa.override_redirect = true;
  xswa.backing_store = NotUseful;
  xswa.save_under = false;
  mask = ( CWBackingStore | CWSaveUnder |
           CWOverrideRedirect
         );

  rwin = XCreateWindow(disp, root, rect.x()-safety, rect.y()-safety,
                       rect.width()+2*safety,rect.height()+2*safety,
                       0, CopyFromParent, InputOutput, CopyFromParent,
                       mask, &xswa);
  XMapWindow(disp, rwin);
  XUnmapWindow(disp, rwin);
  XDestroyWindow(disp, rwin);
}

#define MAXSS 32767
#define MINSS (-32768)

void Kvdr::addCRect(int &cc, int x, int y, int w, int h)
{
  if( (x<=MAXSS) && (y<=MAXSS) && (w<=MAXSS) && (h<=MAXSS) &&
      (x>=MINSS) && (y>=MINSS) && (w>=MINSS) && (h>=MINSS) &&
      (cc<MAXCLIPRECTS)
    )
  {
    cr[cc].x=x;
    cr[cc].y=y;
    cr[cc].width=w;
    cr[cc].height=h;
    cc++;
  }
}

void Kvdr::CheckClippingTimerSlot()
{
	static int chk_cnt=0;
  if(crs_hide_cnt++>CSR_HIDE_TIME) setCursor(blankCursor);//hide cursor after some time
#ifdef HAVE_LIBXV
	if(	((args->isSet("x"))) && !isXv )
  {
  	XvSLOT();
  }
#endif
  if(!svdrpc->Connected())
  {
      if(!vdr_mb_shown) //show only one message box ...
      {
        vdr_mb_shown=true;
        if(chk_cnt>3)
        {
		  	    QMessageBox::critical(this,"kvdr:",i18n("could not connect to vdr-daemon,\n"\
                                       "check if vdr is running,\n"\
                                       "kvdr uses the same port (press 'C'),\n"\
                                       "that no other application like\n"\
                                       "telnet or another kvdr is connected\n"\
                                       "and if vdr is alive!"));
        }
        else chk_cnt++;
				svdrpc->Close();
				svdrpc->Open(vdr_port);
//in conflict with multivideo!:				
				//reopen video-dev in case there was a driver-reload:
      	if (video_fd.handle()!=-1)
      	{
      		OverlayOnOffSLOT(-1);
      		video_fd.close();
      	}
      	QString s;
      	s.sprintf("%s%d",DEFAULT_VIDEO_DEV,VidDev);
      	video_fd.setName((const char*)s);
      	video_fd.open(IO_ReadWrite);
      	if( !(args->isSet("x")) )
      	{
          OvlF(display_width,display_height,(int)base,bpp,palette);
          OvlP(brightness,colour,hue,contrast);
      		OverlayOnOffSLOT(1);
        }
        else
        {
        	isXv=false;
        }
				vdr_mb_shown=false;
      }
  }
  if(!isXv)
  {
    //copy clipping-array for comparison
    memcpy(cr1,cr,sizeof(*cr)*MAXCLIPRECTS);
    bzero(cr,sizeof(*cr)*MAXCLIPRECTS);
    doClipping(0);
  }
  //shows the overlay again if we switched the desktop
  KWin::WindowInfo wi=KWin::WindowInfo(winId(),0,0);
  if(wi.isOnCurrentDesktop()) OvlO(1);
}

void Kvdr::doClipping(bool doit)
{
  if (!ovlstat) return;
  int wx, wy, ww, wh;
  int clipcount=0;
  int x,y,x2,y2;
  XWindowAttributes wts;
  Window parent, win, rroot,*children;
  uint nchildren, i;

  //take current geometry
  QRect geom=geometry();
  if(isfullscreen)
  {
    wx=geom.x()+(geom.width()-f_x)/2,
    wy=geom.y()+(geom.height()-f_y)/2;
    ww=geom.width();wh=geom.height();
  }
  else
  {
    wx=geom.x();wy=geom.y();ww=geom.width();wh=geom.height();
  }

  //hide parts of overlay that go beyond screen:
  if (wx<0)
  {
    addCRect(clipcount, display_width-wx-ww,wy-1,display_width, wy-1+wh);
  }
  if (wy<0)
  {
    addCRect(clipcount, 0, 0, ww, (uint)(-wy));
  }
  if ((wx+ww) > display_width)
  {
    addCRect(clipcount, 0,wy+1,wx+ww-display_width, wy+1+wh);
  }
  if ((wy+wh) > display_height)
  {
    addCRect(clipcount, 0, display_height-wy, ww, wh);
  }

  win=(Window)winId();
  for (;;)
  {
    XQueryTree(disp, win, &rroot, &parent, &children, &nchildren);
    XFree((char *) children);
    if (root == parent) break;
    win = parent;
  }
  XQueryTree(disp, root, &rroot, &parent, &children, &nchildren);
  for (i = 0; i < nchildren; i++ )
    if ( children[i]==win ) break;
  for ( i++; i < nchildren; i++  )
  {
    XGetWindowAttributes(disp, children[i], &wts);
    if ( !(wts.map_state & IsViewable) ) continue;

    x=wts.x-wx;
    y=wts.y-wy;
    x2=x+wts.width+2*wts.border_width;
    y2=y+wts.height+2*wts.border_width;

    if ( (x2 >= 0) && (x < ww) && (y2 >= 0) && (y < wh) )
    {
      if ( x < 0 )   x=0;
      if ( y < 0 )   y=0;
      if ( x2 > ww ) x2=ww;
      if ( y2 > wh ) y2=wh;
      addCRect(clipcount, x, y, x2-x, y2-y );
    }
    else
      continue;
  }
  XFree((char *) children);
  if(0!=memcmp(cr1,cr,sizeof(*cr)*MAXCLIPRECTS) || doit)
  { //only clip new if something has changed or if forced!
	  OvlC(clipcount,cr);
	  RefreshDisplayTimer.start(200,true);//does refresh display in 100ms
  	didRefresh=3;
  }
}

void Kvdr::RefreshDisplaySlot()
{
  if(RefreshEntireScreen)
  {
    QRect r(0,0,display_width,display_height);
    refreshDisplay(r);
    RefreshEntireScreen=false;
  }
  else
  {
    QRect r=geometry();
    refreshDisplay(r);
  }
}

void Kvdr::MuteSLOT()
{
  ismute=!ismute;
  if(use_dvb_mixer)
  {
    svdrpc->CmdTKey("Mute");
    return;
  }
  if (ismute)
	  SetVolume(0, StrMixerChannel, 0);
  else
	  SetVolume(Volume, StrMixerChannel, FrontRear); // restore Volume
}

void Kvdr::VolumeUpSLOT()
{
  if(use_dvb_mixer)
  {
    svdrpc->CmdTKey("Volume+");
    return;
  }
  if(Volume<100) Volume++;
	SetVolume(Volume,StrMixerChannel,FrontRear);
}

void Kvdr::VolumeDownSLOT()
{
  if(use_dvb_mixer)
  {
    svdrpc->CmdTKey("Volume-");
    return;
  }
  if(Volume>0) Volume--;
	SetVolume(Volume,StrMixerChannel,FrontRear);
}

void Kvdr::RearVolumeUpSLOT()
{
  if(use_dvb_mixer)
  {
    return;
  }
  if(FrontRear<100) FrontRear++;
 	SetVolume(Volume,StrMixerChannel,FrontRear);
}

void Kvdr::RearVolumeDownSLOT()
{
  if(use_dvb_mixer)
  {
    return;
  }
  if(FrontRear>0) FrontRear--;
 	SetVolume(Volume,StrMixerChannel,FrontRear);
}

void Kvdr::SetVolume(int Volume, QString MGroup, int FrontRear)
{
  if(use_dvb_mixer)
  {
    return;
  }
  SetMixer(MGroup, Volume, FrontRear);
}

void Kvdr::PiPposSlot()
{
	if(!ismultivideo) return;//only here we can see it!
	if(pip_pos<4) pip_pos++;
	else pip_pos=0;
	if(isXv) return;//XvSlot handels that himself
  OvlC(ovlClipCount,cr);
  if(pip_pos==0) RefreshDisplaySlot();
}

void Kvdr::PiPzapSlot()
{ //should use the next free device, in case there are only 2 does nothing?
	int xzap=0;
	if(!ismultivideo || !pip_pos) return;//only if we see the PiP
	int temp_dev=pip_dev;
  pip_zapped:
	if(temp_dev<3) temp_dev++;
	else temp_dev=0;//only from 0..3
	if(temp_dev==VidDev || !multivideo_fd[temp_dev].isOpen())
	{ //we have to take the next free device
		if(xzap>4)
			pip_dev=-1;//we have only one card available!
		else
		{
			xzap++;
			goto pip_zapped;
		}
	}
	pip_dev=temp_dev;//here we take it
	if(isXv) return;//XvSlot handels that himself
  OvlC(ovlClipCount,cr);
}

void Kvdr::PipexchangeSlot()
{ //exchanges Pip
	if(!ismultivideo || !pip_pos) return;//only if we see the PiP
	int temp;
	//should be atomic:
	if(multivideo_fd[pip_dev].isOpen() && multivideo_fd[VidDev].isOpen())
	{
  	temp=pip_dev;
  	pip_dev=VidDev;
  	VidDev=temp;
	}
	//end-atomic
	if(isXv) return;//XvSlot handels that himself
  OvlC(ovlClipCount,cr);
}

void Kvdr::OvlCursorSlot()
{ //moves the "ovl-cursor" for chan,mute,volume-commands, in case only 2 does what?
	if(o_cursor<3) o_cursor++;
	else o_cursor=0;//only from 0..3
}

void Kvdr::load_kvdrrc()
{
  KConfig *config=kapp->config();

  config->setGroup(GROUP_GCONFIG);

  isPAL=config->readNumEntry("isPal",isPAL);
  video_aspect_ratio=config->readDoubleNumEntry("video_ascpect_ratio",
                                                video_aspect_ratio);
  lp_x=config->readNumEntry("lp_x",lp_x);
  lp_y=config->readNumEntry("lp_y",lp_y);
  i_size_x=config->readNumEntry("i_size_x",i_size_x);
  i_size_y=config->readNumEntry("i_size_y",i_size_y);
  i_g_size_x=config->readNumEntry("i_g_size_x",i_g_size_x);
  i_g_size_y=config->readNumEntry("i_g_size_y",i_g_size_y);
  i_jpg_qual=config->readNumEntry("i_jpg_qual",i_jpg_qual);
  vdr_port=config->readNumEntry("vdr_port",vdr_port);
  colour=config->readNumEntry("colour",colour);
  brightness=config->readNumEntry("brightness",brightness);
  hue=config->readNumEntry("hue",hue);
  contrast=config->readNumEntry("contrast",contrast);
  basename=config->readEntry("basename",basename);
  Volume=config->readNumEntry("Volume",Volume);
  FrontRear=config->readNumEntry("FrontRear",FrontRear);
  MixerChannel=config->readNumEntry("MixerChannel",MixerChannel);
  VidDev=config->readNumEntry("VidDev",VidDev);
//  use_dvb_mixer=config->readNumEntry("use_dvb_mixer",use_dvb_mixer);
  if(config->readNumEntry("ShowHelp",1))
  { //automatically invoke HTML-help the very first time:
    kapp->invokeHelp("index-3.html","kvdr");
    config->writeEntry("ShowHelp",0);
  }
}

void Kvdr::save_kvdrrc()
{
  KConfig *config=kapp->config();

  config->setGroup(GROUP_GCONFIG);
  config->writeEntry("isPal",isPAL);
  config->writeEntry("video_ascpect_ratio",video_aspect_ratio);
  config->writeEntry("i_size_x",i_size_x);
  config->writeEntry("i_size_y",i_size_y);
  config->writeEntry("i_g_size_x",i_g_size_x);
  config->writeEntry("i_g_size_y",i_g_size_y);
  config->writeEntry("i_jpg_qual",i_jpg_qual);
  config->writeEntry("vdr_port",vdr_port);
  config->writeEntry("colour",colour);
  config->writeEntry("brightness",brightness);
  config->writeEntry("hue",hue);
  config->writeEntry("contrast",contrast);
  config->writeEntry("basename",basename);
  config->writeEntry("Volume",Volume);
  config->writeEntry("FrontRear",FrontRear);
  config->writeEntry("MixerChannel",MixerChannel);
  config->writeEntry("VidDev",VidDev);
//  config->writeEntry("use_dvb_mixer",use_dvb_mixer);
}

//now the tricky X11-Event-stuff:
void Kvdr::mouseMoveEvent( QMouseEvent *event )
{
//    if (crs_hide_cnt>CSR_HIDE_TIME)
    {
      setCursor(orgCursor);
    }
    crs_hide_cnt=0;
}

bool Kvdr::x11Event( XEvent *event )
{
	if(isXv) return false;
  if ( isVisible() && ovlstat && (event->xvisibility.window == (Window) winId()) )
  {
    if(event->type==Expose)
    {num_of_consecutive_expose_events++;}
    else
    {num_of_consecutive_expose_events=0;}
    switch ( event->type )
    {
    case Expose:
      {
          if(didRefresh>0)
          {
            if(num_of_consecutive_expose_events>4)
            { //did something moved in front of kvdr?
              RefreshEntireScreen=true;
//  						KWin::RefreshDecorations();//would be enough!
							//unfortunately i don't know of such a function :-((
            }
          }
					else if ( !event->xexpose.count )
					{
					  //printf("Exposed\n");fflush(stdout);
            doClipping();
					}
      }
      break;
    case VisibilityNotify:
      newVisibility= event->xvisibility.state;
      switch (event->xvisibility.state)
      {
        case VisibilityFullyObscured:
        case VisibilityPartiallyObscured:
        case VisibilityUnobscured:
             if(didRefresh>0){didRefresh--;break;}
             doClipping();
             //printf("Visibility %d\n",event->type);fflush(stdout);
	           break;
      }
      break;
    case UnmapNotify:
      OvlO(0);
      RefreshDisplayTimer.start(10,true);//does refresh display in 10ms
      break;
    case MapNotify:
      OvlO(1);
      break;
		case 10:
		case 33:
						 doClipping();
	           break;
    //default:fprintf(stderr,"no-event %d\n",event->type);
    }
  }
  return false;
}

void Kvdr::ExitMixer(void)
{
  if (mix.handle() != -1)
    mix.close();
  dev = rdev = -1;
} /* ExitMixer */

char* Kvdr::InitMixer(void) // Return a list of Mixer Devices, or NULL if no mixer available
{
  int i, devmask, recmask, recsrc, stereomask, volume;
  ExitMixer();
  *mixer_devices = 0;
  /* first mixer device. If "mixer0" does'nt work, try "mixer" */
  mix.setName("/dev/mixer1");
  mix.open(IO_ReadWrite);
  if (mix.handle() == -1)
  {
	  mix.setName("/dev/mixer");
  	mix.open(IO_ReadWrite);
    if (mix.handle() == -1)
      return(NULL);
  } /* if */

  if (-1 == ioctl(mix.handle(), MIXER_READ(SOUND_MIXER_DEVMASK), &devmask) ||
      -1 == ioctl(mix.handle(), MIXER_READ(SOUND_MIXER_STEREODEVS), &stereomask) ||
      -1 == ioctl(mix.handle(), MIXER_READ(SOUND_MIXER_RECMASK), &recmask) ||
      -1 == ioctl(mix.handle(), MIXER_READ(SOUND_MIXER_RECSRC), &recsrc))
  {
    mix.close();
    return(NULL);
  } /* if */
  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
  {
    if ((1 << i) & devmask)
    {
      if (-1 == ioctl(mix.handle(), MIXER_READ(i), &volume))
      {
        mix.close();
	    	return(NULL);
      } /* if */
      strcat(mixer_devices, labels[i]);
      strcat(mixer_devices, ":");
    } /* if */
  } /* for */
  return(*mixer_devices ? mixer_devices : NULL);
} /* InitMixer */


void Kvdr::SetMixer(const char *device, int FrontVolume, int RearVolume)
{
  int i, devmask, volume, recsrc = 0;
  if (mix.handle() != -1)
  {
    if (1)//dev == -1)
    {
      if (-1 == ioctl(mix.handle(), MIXER_READ(SOUND_MIXER_DEVMASK), &devmask))
        return;

      for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
      {
				if ((1 << i) & devmask && !strcasecmp(labels[i], device))
				{
				  if (-1 == ioctl(mix.handle(), MIXER_READ(i), &volume))
            return;
				  else
	    			dev = i;
				} /* if */

				if ((1 << i) & devmask && !strcasecmp(labels[i], REAR))
				{
				  if (-1 == ioctl(mix.handle(), MIXER_READ(i), &volume))
            return;
				  else
				  {
				    rdev = i;
	    			recsrc |= (1 << rdev); /* Turn on recording */
				    ioctl(mix.handle(), SOUND_MIXER_WRITE_RECSRC, &recsrc);
				  } /* else */
				} /* if */
      } /* for */
    } /* if */
    if (dev != -1)
    {
      FrontVolume &= 0x7f;
      volume = FrontVolume | (FrontVolume << 8);

      (void)ioctl(mix.handle(), MIXER_WRITE(dev), &volume);
    } /* if */
    if (rdev != -1)
    {
      RearVolume &= 0x7f;
      volume = RearVolume | (RearVolume << 8);
      (void)ioctl(mix.handle(), MIXER_WRITE(rdev), &volume);
    } /* if */
  } /* if */
} /* SetMixer */
