/* 
    TVscreen Widget for Bt848 frame grabber driver

    Copyright (C) 1996,97 Marcus Metzler (mocm@thp.uni-koeln.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.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
 * TVscreen.c 
 */


#include "TVscreenP.h"
#include <sys/mman.h>
#include <unistd.h>

/*#define DEBUG*/

#define DEFAULT_WIDTH    320
#define DEFAULT_HEIGHT   240  

#define PAL_WIDTH 922
#define PAL_HEIGHT 576

#define NTSC_WIDTH 640
#define NTSC_HEIGHT 480

#define DEFAULT_DELAY 100

#define offset(field) XtOffsetOf(TVscreenRec, field)


static XtResource resources[] = {
  {
    XtNdevName, 
    XtCDevName, 
    XtRString, 
    sizeof(String),
    offset(TVscreen.devname), 
    XtRImmediate, 
    NULL
  },
  {
    XtNcap, 
    XtCCap, 
    XtRBoolean, 
    sizeof(Boolean),
    offset(TVscreen.CAP_ON), 
    XtRImmediate, 
    (XtPointer) False
  },
  {
    XtNnorm, 
    XtCNorm, 
    XtRInt, 
    sizeof(int),
    offset(TVscreen.NORM), 
    XtRImmediate, 
    (XtPointer) DNORM
  },
  {
    XtNfreq, 
    XtCFreq, 
    XtRInt, 
    sizeof(int),
    offset(TVscreen.FREQ), 
    XtRImmediate, 
    (XtPointer) 0
  },
  {
    XtNinputSelect, 
    XtCInputSelect, 
    XtRInt, 
    sizeof(int),
    offset(TVscreen.INPUT), 
    XtRImmediate, 
    (XtPointer) DIN
  },
  {
    XtNdelay, 
    XtCDelay, 
    XtRInt, 
    sizeof(int),
    offset(TVscreen.DELAY), 
    XtRImmediate, 
    (XtPointer) DEFAULT_DELAY
  },
  {
    XtNbright, 
    XtCBright, 
    XtRInt, 
    sizeof(int),
    offset(TVscreen.Bright), 
    XtRImmediate, 
    (XtPointer) DBRI
  },
  {
    XtNcol, 
    XtCCol, 
    XtRInt, 
    sizeof(int),
    offset(TVscreen.Color), 
    XtRImmediate, 
    (XtPointer) DCOL
  },
  {
    XtNhue, 
    XtCHue, 
    XtRInt, 
    sizeof(int),
    offset(TVscreen.Hue), 
    XtRImmediate, 
    (XtPointer) DHUE
  },
  {
    XtNcontrast, 
    XtCContrast, 
    XtRInt, 
    sizeof(int),
    offset(TVscreen.Contrast), 
    XtRImmediate, 
    (XtPointer) DCONTRAST
  },
  {
    XtNrefresh, 
    XtCRefresh, 
    XtRBoolean, 
    sizeof(Boolean),
    offset(TVscreen.REFRESH), 
    XtRImmediate, 
    (XtPointer) True
  },
  {
    XtNbpp, 
    XtCBpp, 
    XtRInt, 
    sizeof(int),
    offset(TVscreen.bpp), 
    XtRImmediate, 
    (XtPointer) 0
  },
  {
    XtNvis, 
    XtCVis, 
    XtRString, 
    sizeof(String),
    offset(TVscreen.visname), 
    XtRImmediate, 
    NULL
  },
  {
    XtNsnap, 
    XtCSnap, 
    XtRBoolean, 
    sizeof(Boolean),
    offset(TVscreen.snap), 
    XtRImmediate, 
    (XtPointer) False
  },
  {
    XtNrealW, 
    XtCRealW, 
    XtRInt, 
    sizeof(int),
    offset(TVscreen.realw), 
    XtRImmediate, 
    (XtPointer) 0
  },
  {
    XtNaudio, 
    XtCAudio,
    XtRInt, 
    sizeof(int),
    offset(TVscreen.audio), 
    XtRImmediate, 
    (XtPointer) 1
  },
};

/* Declaration of methods */

static void Initialize();
static void Realize();
static Boolean SetValues();
static void destroy();
static XtGeometryResult QueryGeometry();
static int rx,ry,rh,rw;

/* the following are actions of TVscreen */
static void pConfig();
static void addclip();
static void maketab();
static int getclip();
static void bt848_dma();
static void SetGeo();
static void cap();
static void setmaxsize();
static void ref();
static void refresh();
XErrorHandler xerror();

static Window lastrwin;
static XtIntervalId timer;
static Boolean Move=False;

TVscreenClassRec tvscreenClassRec = {
    {
    /* core_class fields */
    /* superclass        */ (WidgetClass) &coreClassRec,
    /* class_name        */ "TVscreen",
    /* widget_size       */ sizeof(TVscreenRec),
    /* class_initialize      */ NULL,
    /* class_part_initialize     */ NULL,
    /* class_inited          */ FALSE,
    /* initialize        */ Initialize,
    /* initialize_hook       */ NULL,
    /* realize           */ Realize,
    /* actions           */ NULL,
    /* num_actions       */ 0,
    /* resources         */ resources,
    /* num_resources         */ XtNumber(resources),
    /* xrm_class         */ NULLQUARK,
    /* compress_motion       */ TRUE,
    /* compress_exposure     */ XtExposeCompressMultiple,
    /* compress_enterleave   */ TRUE,
    /* visible_interest      */ FALSE,
    /* destroy           */ destroy,
    /* resize            */ NULL,
    /* expose            */ NULL,
    /* set_values        */ SetValues,
    /* set_values_hook       */ NULL,
    /* set_values_almost     */ XtInheritSetValuesAlmost,
    /* get_values_hook       */ NULL,
    /* accept_focus      */ NULL,
    /* version           */ XtVersion,
    /* callback_private      */ NULL,
    /* tm_table          */ NULL,
    /* query_geometry        */ QueryGeometry,
    /* display_accelerator       */ XtInheritDisplayAccelerator,
    /* extension                 */ NULL
    },
    {
    /* extension          */        0,
    },
};

WidgetClass tvscreenWidgetClass = (WidgetClass) & tvscreenClassRec;

/* Realize */
static void Realize(Widget w,
		    XtValueMask *value_mask,
		    XSetWindowAttributes *attributes)
{
  TVscreenWidget tw=(TVscreenWidget) w;
    
  XtCreateWindow(w, (unsigned int) InputOutput,
		 tw->TVscreen.visual, *value_mask, attributes);

  if (tw->TVscreen.CAP_ON){
#ifndef NOCARD
    struct video_channel vc;
#endif
    
    tw->TVscreen.Change=TRUE;
#ifndef NOCARD
    vc.channel = tw->TVscreen.INPUT;
    vc.norm  = tw->TVscreen.NORM;
    
    if(ioctl(tw->TVscreen.fbttv, VIDIOCSWIN, &tw->TVscreen.vwin))
      perror("VIDIOCSWIN");
    /* Set the input */

    if(ioctl(tw->TVscreen.fbttv, VIDIOCSCHAN, &vc))
      perror("VIDIOCSCHAN");

    /* Set the frequency */
    if(ioctl(tw->TVscreen.fbttv, VIDIOCSFREQ, &tw->TVscreen.frequency))
      perror("VIDIOCSFREQ");
#endif 
 }
} 


XErrorHandler xerror(Display *dpy, XErrorEvent *event)
{
  switch (event->error_code){
  case BadWindow:
#ifdef DEBUG
    printf("Bad Window ingnored\n");
#endif
    return 0 ;
    break;
      
  case BadMatch:
#ifdef DEBUG
    printf("BadMatch ingnored\n");
#endif
    return 0 ;
    break;

  case BadColor:
#ifdef DEBUG
    printf("BadColor ingnored\n");
#endif
    return 0 ;
    break;

    default:
      return 0;
  }
}

void ref(TVscreenWidget w)
{
#ifndef NOCARD
  int scr,depth;
  XSetWindowAttributes xswa;
  unsigned long mask;
  Window rwin,rootW;
  Display *disp;
  
  if (w->TVscreen.REFRESH){
    disp=XtDisplay((Widget)w);
    scr=DefaultScreen(disp);
    rootW= RootWindow(disp, scr);
    depth=w->core.depth;
    
    xswa.override_redirect = True;
    xswa.backing_store = NotUseful;
    xswa.save_under = False;
    xswa.background_pixel=BlackPixel(disp,scr);
    xswa.backing_store=NotUseful;
    mask = (CWBackPixel | CWSaveUnder | CWBackingStore|
	    CWOverrideRedirect );
    if(Move){
      rwin = XCreateWindow(disp,rootW,0, 0, 
			   w->TVscreen.vbuf.width,w->TVscreen.vbuf.height, 0,
			   CopyFromParent, InputOutput, CopyFromParent,
			   mask, &xswa);
      Move=False;
    } else {
      rwin = XCreateWindow(disp,rootW,
			   w->TVscreen.vwin.x-80, w->TVscreen.vwin.y-80, 
			   w->TVscreen.vwin.width+80,w->TVscreen.vwin.height+80,
			   0,CopyFromParent, InputOutput, CopyFromParent,
			   mask, &xswa);
    }
    lastrwin=rwin;
    XMapWindow(disp, rwin);
    XUnmapWindow(disp, rwin);
    XDestroyWindow(disp, rwin);
  }
#endif
}

 
static void refresh(w)
TVscreenWidget w;
{
  if (timer) XtRemoveTimeOut(timer);

  timer=XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)w),
			w->TVscreen.DELAY,(XtTimerCallbackProc)ref,w);
}


#ifndef NOCARD
static void addclip(cr,ncr,x,y,w,h)
struct video_clip *cr;
int *ncr; 
uint x,y,w,h;
{
  cr[*ncr].x=x;
  cr[*ncr].y=y;
  cr[*ncr].width=w;
  cr[*ncr].height=h;
  *ncr=*ncr+1;
} 
#endif

static void maketab(w, ncr)
     TVscreenWidget w;
     int ncr;
{
#ifndef NOCARD
  w->TVscreen.vwin.clips=w->TVscreen.cliprecs;
  w->TVscreen.vwin.clipcount=ncr;
  
  /*
   *	Set the clipping up.
   */
  
  if(ioctl(w->TVscreen.fbttv, VIDIOCSWIN, &w->TVscreen.vwin))
    perror("set clipping");
#endif
}

#ifndef NOCARD
static int getclip(w)
TVscreenWidget w;
{
  int ncr=0;
  int x,y,x2,y2,wx,wy;
  uint ww,wh;
  struct video_window vwin;
  struct video_buffer vbuf;
  Display *disp;
  XWindowAttributes wts;
  Window parent,win,parent2, root, rroot, *children, swin;
  uint nchildren, i;
  struct video_clip *cr; 
  Widget widpar;


  if (!XtIsRealized((Widget)w)) return;

  disp=XtDisplay((Widget)w);
  vwin=w->TVscreen.vwin;
  vbuf=w->TVscreen.vbuf;

  widpar=(Widget) w;
  while(XtParent(widpar)) widpar=XtParent(widpar);
  if((win=XtWindow(widpar))==None) return;

  XQueryTree(disp,win, &rroot, &parent, &children, &nchildren);
  if(nchildren) XFree((char *)children);

  cr=w->TVscreen.cliprecs;

  wx=vwin.x; wy=vwin.y;
  wh=vwin.height; ww=vwin.width;

  root=DefaultRootWindow(disp);
  swin=win;
  while (parent!=root) {
    swin=parent;
    XQueryTree(disp, swin, &rroot, &parent, &children, &nchildren);
    if (nchildren)
      XFree((char *) children);
  }
  XQueryTree(disp, root, &rroot, &parent2, &children, &nchildren);
  for (i=0; i<nchildren;i++) 
    if (children[i]==swin) break;
  i++;
  for (; i<nchildren; i++) {
    
    XGetWindowAttributes(disp, children[i], &wts);
    if (!(wts.map_state & IsViewable))  continue;
    
    x=wts.x-vwin.x; y=wts.y-vwin.y;
    x2=wts.width+2*wts.border_width;
    y2=wts.height+2*wts.border_width;
    addclip(cr, &ncr, x, y, x2, y2);
  }
  XFree((char *) children);


  maketab(w,ncr);
  return ncr;
}
#endif

#ifndef NOCARD
static Boolean onscreen(w,wts)
TVscreenWidget w;
XWindowAttributes wts;
{
  int x,x2,y,y2;
  uint ww,wh;
  struct video_window vwin;
  
  vwin=w->TVscreen.vwin;
  wh=vwin.height; ww=vwin.width;

  if (!(wts.map_state & IsViewable))
    return False;
  
  x=wts.x-vwin.x; y=wts.y-vwin.y;
  x2=x+wts.width+2*wts.border_width-1;
  y2=y+wts.height+2*wts.border_width-1;
  if ((x2 < 0) || (x >= (int)ww) ||
      (y2 < 0) || (y >= (int)wh))
    return False;
  return True;
}
#endif

static void bt848_dma(w,data)
Widget w;
uint data;
{
#ifndef NOCARD
  int f;
  TVscreenWidget cw = (TVscreenWidget) w;
  
  f=cw->TVscreen.fbttv;
/*  ioctl(f, BTTV_DMA, &data);*/
#endif
}  

static void cap(w,W)
TVscreenWidget w;
int W;
{
  if( XtIsRealized((Widget)w)) {
#ifndef NOCARD
    int one=1;
    int zero=0;
    if(W && w->TVscreen.CAP_ON)
      {
	if(ioctl(w->TVscreen.fbttv, VIDIOCCAPTURE, &one))
	  perror("capture on");
      }
    else
      {
	if(ioctl(w->TVscreen.fbttv, VIDIOCCAPTURE, &zero))
	  perror("capture off");
      }
#endif
  }
}

static void setmaxsize(cw)
TVscreenWidget cw;
{
  uint max_h,max_w;

  switch (cw->TVscreen.NORM){
  case PAL:
  case SECAM:
    max_w=PAL_WIDTH;
    max_h=PAL_HEIGHT;
    break;

  case NTSC:
    max_w=NTSC_WIDTH;
    max_h=NTSC_HEIGHT;
    break;
    
  default:
    max_w=DEFAULT_WIDTH;
    max_h=DEFAULT_HEIGHT;
    break;
  }

  cw->TVscreen.max_width=max_w;
  cw->TVscreen.max_height=max_h;
}

/* --------------------------------------------------------------------- */

#define AUDIO_AUTO     0
#define AUDIO_MONO     1
#define AUDIO_STEREO   2
#define AUDIO_BILANG1  3
#define AUDIO_BILANG2  4

static int amodes[5] = {
  0, VIDEO_SOUND_MONO, VIDEO_SOUND_STEREO, VIDEO_SOUND_LANG1, VIDEO_SOUND_LANG2
};

static char *audio_mode_name[] = {
    "auto",
    "mono",
    "stereo",
    "bi-lang 1",
    "bi-lang 2",
    NULL
};

void
set_audiomode(TVscreenWidget w, int mode)
{
    struct video_audio va;

    if (w->TVscreen.audio_state != mode) {
#ifdef AUDIO_DEBUG
	fprintf(stderr,"set_audiomode: switch to %s\n",audio_mode_name[mode]);
#endif
	va.audio=0;
	va.flags=0;
	va.mode=amodes[mode];
	ioctl(w->TVscreen.fbttv,VIDIOCSAUDIO,&va);
	w->TVscreen.audio_state = mode;
      }
}

/* for "audio = auto" */
void
audio_watch(XtPointer client_data, XtIntervalId *id)
{

}


static void
Initialize(treq, tnew, args, num_args)
Widget treq, tnew;
ArgList args;
Cardinal *num_args;
{
  Widget parent;
  char *bttvname;
  Screen *screen;
  Window rootw;
  int col;
  uint rwidth; 
  TVscreenWidget new = (TVscreenWidget) tnew;
  int major, minor, bank, ram;
  Display *disp;
  ushort i,r,rr;
  Colormap cmap;
  XColor *my;
  Visual *v;
  unsigned char *tm;
  int msize,ii;
  struct video_mbuf mbuf;

#ifndef NOCARD
  if(new->TVscreen.devname) bttvname=new->TVscreen.devname;
  else bttvname="/dev/video";

  new->TVscreen.devname=bttvname;

  if ((new->TVscreen.fbttv=open(bttvname,O_RDWR)) < 0) {
    printf("Cannot open %s\n",bttvname);
    exit(1);
  }

  if(ioctl(new->TVscreen.fbttv, VIDIOCGWIN, &(new->TVscreen.vwin)))
    perror("get window");
  if(ioctl(new->TVscreen.fbttv, VIDIOCGFBUF, &(new->TVscreen.vbuf)))
    perror("get frame buffer");
  if(ioctl(new->TVscreen.fbttv, VIDIOCGTUNER, &(new->TVscreen.vtuner)))
    perror("get tuner");
  new->TVscreen.vwin.width=DEFAULT_WIDTH;
  new->TVscreen.vwin.height=DEFAULT_HEIGHT;
  new->TVscreen.vwin.x=0;
  new->TVscreen.vwin.y=0;
  new->TVscreen.vbuf.width=XtScreen(tnew)->width;
  new->TVscreen.vbuf.height=XtScreen(tnew)->height;
  new->TVscreen.vbuf.base=0;
  new->TVscreen.vwin.clipcount=0;

  rwidth=new->TVscreen.vbuf.width;
  disp=XtDisplay(tnew);
#ifndef NODGA
  if (XF86DGAQueryVersion(disp, &major, &minor)) 
    XF86DGAGetVideoLL(disp, DefaultScreen(disp), &(new->TVscreen.vbuf.base),
		      &rwidth, &bank, &ram);
  else 
#endif
#endif
    if (rwidth<960)
      if (rwidth>640)
	rwidth=960;
  if (new->TVscreen.realw) rwidth=new->TVscreen.realw;
  
  new->TVscreen.dual=find_visual(tnew,new->TVscreen.visname,
				 &new->TVscreen.visual);
  v= new->TVscreen.visual;
#ifndef NOCARD
  if (new->TVscreen.bpp)
    new->TVscreen.vbuf.depth=new->TVscreen.bpp;
  else
    new->TVscreen.vbuf.depth=tnew->core.depth;
  if (new->TVscreen.dual)
    new->TVscreen.vbuf.depth=32;
  new->TVscreen.vbuf.bytesperline=rwidth*(0x38&(new->TVscreen.vbuf.depth+1))/8;
  
  if(ioctl(new->TVscreen.fbttv, VIDIOCSFBUF, &new->TVscreen.vbuf))
    {
      perror("set frame buffer");
      exit(1);
    }

  /* Alloc memory for snapshot */
  if(ioctl(new->TVscreen.fbttv, VIDIOCGMBUF, &(new->TVscreen.mbuf)))
    perror("get mbuf");
  msize = new->TVscreen.mbuf.size;
  /*printf("msize: 0x%08x\n",msize);*/
  tm=(unsigned char *)mmap(0,msize,PROT_READ|PROT_WRITE,
			   MAP_SHARED,new->TVscreen.fbttv,0);
  if (tm==(unsigned char *)-1)  {
    printf("Cannot mmap memory for snapshot\n");
    exit(1);
  }
  
#ifdef DEBUG
  printf("tm: 0x%08x\n", (int) tm);
#endif
  new->TVscreen.TargetMem=tm;
  
#endif

  new->TVscreen.CMSET=False;
  if (v->class == PseudoColor){
    new->TVscreen.CMSET=True;
    my=new->TVscreen.colors;
    for (i=0;i<256;i++){
      my[i].pixel=i;
      my[i].flags = DoRed | DoGreen | DoBlue;
    }
    XQueryColors(disp, new->core.colormap, my, 256);

    cmap=XCreateColormap(disp, DefaultRootWindow(disp),v, AllocAll);
    new->core.colormap=cmap;
    for (i=0;i<225;i++){ /* Set Colors 16 - 240 */
      r=i%25;
      my[i+16].green =(65535*(i/25)/8);  
      my[i+16].red =(65535*(r/5)/4);  
      rr=r%5;
      my[i+16].blue =(65535*rr/4);        
    }
    XStoreColors(disp,cmap,my,256);
  }

  new->core.width=DEFAULT_WIDTH;
  new->core.height=DEFAULT_HEIGHT; 


  parent=tnew;
  while (parent->core.parent){
    parent=parent->core.parent;
  }

  XtAddEventHandler(tnew,SubstructureNotifyMask | VisibilityChangeMask
		    | StructureNotifyMask | ExposureMask
		    | EnterWindowMask |LeaveWindowMask,
		    FALSE,pConfig,new);
  XtAddEventHandler(parent,SubstructureNotifyMask | VisibilityChangeMask
		    | StructureNotifyMask | ExposureMask,
		    FALSE,pConfig,new);
  rootw=DefaultRootWindow(XtDisplay(tnew));
  XSelectInput(XtDisplay(tnew),rootw, VisibilityChangeMask |
	       SubstructureNotifyMask | ExposureMask |StructureNotifyMask);
  
  
  XtRegisterDrawable(XtDisplay(tnew),rootw,tnew);



  new->TVscreen.Change=FALSE;
  setmaxsize(new);
  new->core.background_pixel=BlackPixel(XtDisplay(new),
					DefaultScreen(XtDisplay(new)));

  timer=0;
  XSetErrorHandler((XErrorHandler) xerror);
}


static void SetGeo(cw)
TVscreenWidget cw;
{
#ifndef NOCARD
  Widget parent;
  Position x,y;
  int dx,dy;
  Boolean CHECK;
  int oldx,oldy,oldw,oldh;

  oldx=cw->TVscreen.vwin.x;
  oldy=cw->TVscreen.vwin.y;
  oldw=cw->TVscreen.vwin.width;
  oldh=cw->TVscreen.vwin.height;
  
  CHECK=cw->TVscreen.Change;
  
  cw->TVscreen.vwin.flags=0;
  if (!XtIsRealized((Widget) cw)) return;

  parent=(Widget) cw;
  while(XtParent(parent)) parent=XtParent(parent);
  
  XtTranslateCoords((Widget)cw,0,0,&x,&y);
 
  if(oldh!=cw->core.height) CHECK=TRUE;
  if(oldw!=cw->core.width) CHECK=TRUE;

  dx=0;dy=0;
  if (cw->core.width > cw->TVscreen.max_width){
    dx=(cw->core.width- oldw)/2;
    if(oldw != cw->TVscreen.max_width){
      CHECK=TRUE;
      cw->TVscreen.vwin.width=cw->TVscreen.max_width & ~3;
    }
  } else {
    cw->TVscreen.vwin.width=cw->core.width & ~3;
  }

  if (cw->core.height > cw->TVscreen.max_height)
    {
      dy=(cw->core.height- cw->TVscreen.vwin.height)/2;
      if(oldh != cw->TVscreen.max_height)
	{
	  CHECK=TRUE;
	  cw->TVscreen.vwin.height=cw->TVscreen.max_height & ~3;
	}
    }
  else
    cw->TVscreen.vwin.height=cw->core.height & ~3;
  
  if(oldx!=x+dx){
    Move=True;
    CHECK=True;
  }
  if(oldy!=y+dy){
    Move=True;
    CHECK=True;
  }

  if ((cw->TVscreen.vbuf.depth==24)&&((x+dx)&3)) 
    {
      parent->core.x=parent->core.x-((x+dx) &3);
    }
  else if ((cw->TVscreen.vbuf.depth==16)&&((x+dx)&1))
    {
      parent->core.x=parent->core.x-((x+dx) &1);
    }
  else if ((cw->TVscreen.vbuf.depth==15)&&((x+dx)&1))
    {
      parent->core.x=parent->core.x-((x+dx) &1);
    }
  if ((y+dy)&1) 
    {
      parent->core.y=parent->core.y-((y+dy) &1);
    }
  
  if(CHECK)
    {
      cw->TVscreen.vwin.x=x+dx;
      cw->TVscreen.vwin.y=y+dy;
      if (cw->TVscreen.CAP_ON)
	{
	  if(ioctl(cw->TVscreen.fbttv, VIDIOCSWIN, &cw->TVscreen.vwin))
	    perror("set clipped window");
	}
    }  

  if(cw->TVscreen.CAP_ON &&  CHECK) getclip(cw); 
  if(!cw->TVscreen.Change && cw->TVscreen.CAP_ON && cw->TVscreen.Fresh) {
    refresh(cw);
    cw->TVscreen.Fresh=False;
  }
  cw->TVscreen.Change=FALSE;
#endif
}



static Boolean
SetValues(current, request, new, args, num_args)
Widget current, request, new;
ArgList args;
Cardinal *num_args;
{
  Widget parent;
  TVscreenWidget curcw = (TVscreenWidget) current;
  TVscreenWidget newcw = (TVscreenWidget) new;
  Boolean do_redisplay = False;
  int pictmod=0;
  struct video_picture vp;
  struct video_channel vc;

#ifndef NOCARD

  if (newcw->TVscreen.CAP_ON){
    if (!curcw->TVscreen.CAP_ON) {
      getclip(current);
      do_redisplay =True;
      newcw->TVscreen.Change=TRUE;
      SetGeo(newcw);
    }
  }
  else {
    vc.channel=newcw->TVscreen.INPUT;
    vc.norm=newcw->TVscreen.NORM;
    if (curcw->TVscreen.CAP_ON) {
      cap(newcw,0);
      system("xrefresh");
    }
    
    if(ioctl(newcw->TVscreen.fbttv, VIDIOCSCHAN, &vc))
      perror("change tuner");
  }

  if (newcw->TVscreen.frequency!=(ushort) newcw->TVscreen.FREQ)
    {
      newcw->TVscreen.frequency=newcw->TVscreen.FREQ;
      if(ioctl(newcw->TVscreen.fbttv, VIDIOCSFREQ, &newcw->TVscreen.frequency))
	perror("change frequency");
    }
  
  if (curcw->TVscreen.NORM != newcw->TVscreen.NORM ||
      curcw->TVscreen.INPUT !=newcw->TVscreen.INPUT)
    {
      vc.channel=newcw->TVscreen.INPUT;
      vc.norm=newcw->TVscreen.NORM;
      
      newcw->TVscreen.Change=TRUE;
      setmaxsize(newcw);
      SetGeo(newcw);
      if (newcw->TVscreen.NORM==NTSC)
	newcw->TVscreen.Fresh=TRUE;
      SetGeo(newcw);
      if(ioctl(newcw->TVscreen.fbttv, VIDIOCSCHAN, &vc))
	perror("change norm");
    }
  if(ioctl(newcw->TVscreen.fbttv, VIDIOCGPICT, &vp)==-1)
    perror("get picture params");
  
  if (curcw->TVscreen.Bright !=newcw->TVscreen.Bright)
    {
      vp.brightness=(newcw->TVscreen.Bright+128)<<8;
      pictmod=1;
    }
  
  if (curcw->TVscreen.Color !=newcw->TVscreen.Color)
    {
      vp.colour=newcw->TVscreen.Color<<7;
      pictmod=1;
    }
  
  if (curcw->TVscreen.Hue !=newcw->TVscreen.Hue)
    {
      vp.hue=((unsigned int)(newcw->TVscreen.Hue+128))<<8;
      pictmod=1;
    }
  
  
  if (curcw->TVscreen.Contrast !=newcw->TVscreen.Contrast)
    {
      vp.contrast=newcw->TVscreen.Contrast<<7;
      pictmod=1;
    }
  
  if(pictmod)
    {
      if(ioctl(newcw->TVscreen.fbttv, VIDIOCSPICT, &vp))
	perror("set picture params");
    }  

  if (curcw->TVscreen.audio != newcw->TVscreen.audio ||
      newcw->TVscreen.audio == 0){
    set_audiomode(newcw,newcw->TVscreen.audio);
  }
#endif

  return do_redisplay;
}



static void pConfig(wi,client_data,event)
     Widget wi;
     XtPointer client_data;
     XEvent *event;
{
  int fbttv;
  TVscreenWidget w=(TVscreenWidget) client_data;
  static Boolean UNMAPPED=False;
  XWindowAttributes wts1,wts2;
  Window checkw1,checkw2,wwin;
  Widget parent;

  if (!XtIsRealized((Widget)w)||(wwin=XtWindow((Widget)w)) == None) return;

  parent=(Widget)w;
  while (parent->core.parent){
    parent=parent->core.parent;
  }
  
  fbttv=w->TVscreen.fbttv;
  w->TVscreen.Fresh=True;
  cap(w,0);

  checkw1=event->xmap.window;
  checkw2=event->xany.window;
  if (w->TVscreen.CAP_ON)
    { 
      if ( wwin==checkw1 || wwin==checkw2 ) w->TVscreen.Fresh=False;
      else {
	if (checkw1){ 
	  XGetWindowAttributes(XtDisplay(w), checkw1, &wts1);
#ifndef NOCARD
	  if(!onscreen(w,wts1)) w->TVscreen.Fresh=False; 
#endif
	}
	if (checkw2) {
	  XGetWindowAttributes(XtDisplay(w), checkw2, &wts2);
#ifndef NOCARD
	  if(!onscreen(w,wts2)) w->TVscreen.Fresh=False; 
#endif
	}
      }
    }
  
  switch (event->type) {
  case VisibilityNotify:
    switch (event->xvisibility.state) {
    case VisibilityUnobscured:
      if (w->TVscreen.CAP_ON)
	{
	  if (checkw2 != DefaultRootWindow(XtDisplay(w))) {
	    w->TVscreen.Change=TRUE;
	    SetGeo(w);
	  }
	}
      break;
    case VisibilityFullyObscured:
      if (checkw2 != DefaultRootWindow(XtDisplay(w))) 
	cap(w,0);
      break;
    case VisibilityPartiallyObscured:
      if (w->TVscreen.CAP_ON)
	{
	  if (checkw2 != DefaultRootWindow(XtDisplay(w))) {
	    w->TVscreen.Change=TRUE;
	    SetGeo(w);
	  }
	}
      break;
    }
    break;

  case MapNotify:
    if ((DefaultRootWindow(XtDisplay(w)) != checkw2) && UNMAPPED){
      UNMAPPED=False;
      w->TVscreen.CAP_ON=True;
      cap(w,1);
      if ( w->TVscreen.Fresh ){
	refresh(w);
      }
    }
    if ((lastrwin != checkw1) && (w->TVscreen.CAP_ON) ){
      if ( w->TVscreen.Fresh ){
	refresh(w);
      }
    }
    break;


  case ConfigureNotify:
    cap(w,0);
    SetGeo(w);
    break;

  case DestroyNotify:
    if (wwin  == checkw2){
#ifdef DEBUG
      printf("Destroy\n");
#endif
      cap(w,0);
    }
    break;
    
  case UnmapNotify:
    if (DefaultRootWindow(XtDisplay(w)) != checkw2){
      cap(w,0);
      if (w->TVscreen.CAP_ON) UNMAPPED=True;
      if (w->TVscreen.CAP_ON) {
	if ( w->TVscreen.Fresh ){
	  refresh(w);
#ifdef DEBUG
    printf("UnmapNotify refresh\n");
#endif
	}
      }
      w->TVscreen.CAP_ON=False;
#ifdef DEBUG
      printf("Unmap\n");
#endif
    } else
    if (wwin  != event->xmap.window)
      if(w->TVscreen.CAP_ON) {
#ifndef NOCARD
	getclip(w); 
#endif
      }
    break;
  
  case EnterNotify:
    if (wwin  == checkw2){
      if (w->TVscreen.CAP_ON && w->TVscreen.CMSET){ 
	XInstallColormap(XtDisplay(w),w->core.colormap);
      }
    }
    break;

  case LeaveNotify:
/* Looks better this way */
/*    if (wwin  == checkw2){
      if (w->TVscreen.CAP_ON && w->TVscreen.CMSET){ 
	XUninstallColormap(XtDisplay(w),w->core.colormap);
      }
    }
*/ 
    break;
    
  }
  
  if(w->TVscreen.CAP_ON) {
	cap(w,1); 
      }
}

static XtGeometryResult QueryGeometry(w, proposed, answer)
     Widget w;
     XtWidgetGeometry *proposed, *answer;
{
  int reqm;
  TVscreenWidget cw = (TVscreenWidget) w;
  
  /* set fields we care about */
  reqm= CWWidth | CWHeight;
  answer->request_mode =reqm;
  
  /* initial width and height */
#ifndef NOCARD
  /* initial width and height */
  answer->width = (cw->TVscreen.vwin.width > 
		   cw->TVscreen.max_width) ? cw->TVscreen.max_width : 
		     cw->TVscreen.vwin.width;
  answer->height = (cw->TVscreen.vwin.height > 
		    cw->TVscreen.max_height) ? cw->TVscreen.max_height : 
		      cw->TVscreen.vwin.height;
  
  if (cw->TVscreen.vbuf.depth==24) 
    {
      answer->width = answer->width & ~3; 
      answer->height = answer->height & ~3; 
    }
  else if (cw->TVscreen.vbuf.depth==16)
    {
      answer->width = answer->width & ~1; 
      answer->height = answer->height & ~1; 
    }
  else if (cw->TVscreen.vbuf.depth==15)
    {
      answer->width = answer->width & ~1; 
      answer->height = answer->height & ~1; 
    }

    answer->width = DEFAULT_WIDTH; 
    answer->height = DEFAULT_HEIGHT; 

#endif


  if (  ((proposed->request_mode & (reqm)) == (reqm)) &&
      proposed->width == answer->width &&
      proposed->height == answer->height)
    return XtGeometryYes;
  else if (answer->width == cw->core.width &&
	   answer->height == cw->core.height)
    return XtGeometryNo;
  else
    return XtGeometryAlmost;
}

static void destroy(nw)
Widget nw;
{
  Widget parent;
  Window rootw;

  TVscreenWidget w=(TVscreenWidget) nw;

  w->TVscreen.CAP_ON=False;
  cap(w,0);
  if(w->TVscreen.CMSET){
    XFreeColormap(XtDisplay(w),w->core.colormap);
  }
  
  parent=nw;
  while (parent->core.parent){
    parent=parent->core.parent;
  }

  XtRemoveEventHandler(nw,SubstructureNotifyMask | VisibilityChangeMask
		    | StructureNotifyMask | ExposureMask
		    | EnterWindowMask |LeaveWindowMask,
		    FALSE,pConfig,w);
  XtRemoveEventHandler(parent,SubstructureNotifyMask | VisibilityChangeMask
		    | StructureNotifyMask | ExposureMask,
		    FALSE,pConfig,w);
  
  if (timer) XtRemoveTimeOut(timer);
  rootw=DefaultRootWindow(XtDisplay(nw));
  XtUnregisterDrawable(XtDisplay(nw),rootw);

  close(w->TVscreen.fbttv);
#ifdef DEBUG
  printf("Widget destroyed\n");
#endif

}
