
/*
 *
 * display.c - part of Danovitsch Webcam
 *
 * Copyright (C) 2001 by Daan Vreeken
 *
 * Published under the terms of the GNU Public License 2.0
 * (or any later version)
 *
 */



#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>


#include "general.h"
#include "webcam.h"
#include "io.h"
#include "display.h"





/**@BEGINFUNC**************************************************************

    Prototype  : void XUTILGetVisualBpp(
                      Display     *display,
                      XVisualInfo *vi,
                      TV_INT32    *Bpp_pixmap,
                      TV_INT32    *Bpp_fbuffer )

    Purpose    : Returns the number of bytes per pixel for the specified
                 visual.

                 NOTE:  This is not always (depth+7)/8, which is the reason 
                 for the existance of this convenience function.  And even
                 then this function will sometimes get it wrong because
                 Pixmap pixel geometry doesn't necessarily equate to Frame 
                 buffer pixel geometry.

                 BACKGROUND: Some X servers define 4 Bpp 24bpp modes as
                 depth 32 bpp (-bpp 32), while others define them as depth 
                 24 bpp (-bpp 24).  Others use implement both 24bpp and 32bpp
                 in a 3Bpp mode (e.g. 3.3.1 S3V server).  So we can't use 
                 visual depth alone as a determining value.

                 Alternatively we probe the pixmap formats that are
                 supported by the display to help determine the true Bpp.
                 For both cases, the pixmap bits_per_pixel for the
                 corresponding depth (24 or 32) is 32, usually reflecting 
                 the true depth of the frame buffer, but not always.
                 
                 Because without probing hardware we can't really tell
                 for sure what the frame buffer Bpp is, we allow the
                 user to tell us (for 24bpp and 32bpp anyway) using the
                 Bpp24bit and Bpp32bit settings

    Programmer : 29-Mar-97  Randall Hopper

    Parameters : display     - I: display that visual is on
                 vi          - I: info struct for a visual
                 Bpp_pixmap  - O: pixmap Bpp
                 Bpp_fbuffer - O: frame buffer Bpp

    Returns    : Bytes per pixel for the visual

    Globals    : None.

 **@ENDFUNC*****************************************************************/

void GetBpp(Display *Display, XVisualInfo *VI, int *BppPtr)
{
	static struct
	{
		VisualID	VisualID;
		long		BppPixmap;
		long		BppFBuffer;
	}			*VList = NULL;

	static int		VListLen = 0;
	int			I;
	int			Bpp = 0;

	/*  Look up cached value  */
	for (I=0; I<VListLen; I++)
		if (VList[I].VisualID==VI->visualid)
			break;

	/*  Didn't have a cached value?  Go figure it out.  */
	if (I>=VListLen)
	{
		XPixmapFormatValues	*PF;
		int			NumPF;
		int			PFI;

		/*  Try to grab Bpp from pixmap formats first  */
		PF=XListPixmapFormats(Display,&NumPF);
		if (PF!=NULL)
		{
			for (PFI=0; PFI<NumPF; PFI++)
				if (PF[PFI].depth==VI->depth)
					break;
			if (PFI<NumPF)
				Bpp=(PF[PFI].bits_per_pixel+7)/8;
			XFree(PF);
		}

		/*  Or fallback to using depth  */
		if (Bpp==0)
			Bpp=(VI->depth+7)/8;

		/*  And finally, add to Bpp cache  */
		VListLen++;
		VList=realloc(VList,sizeof(VList[0])*VListLen);
		if (VList==NULL)
		{
			printf("EEEK ! Out of memory!\n");
			exit(0);
		}
		I=VListLen-1;
		VList[I].VisualID=VI->visualid;
		VList[I].BppPixmap=Bpp;
		VList[I].BppFBuffer=Bpp;
	}

	if (BppPtr)
		*BppPtr=VList[I].BppPixmap;
}




int Display_MakeGC(XWnd *W)
{
	XGCValues	GCVals;
	GC		NewGC;
	
	NewGC=XCreateGC(W->Dsp,W->Wnd,0,&GCVals);
	
	if (NewGC)
	{
		XSetForeground(W->Dsp,NewGC,W->White);
		XSetBackground(W->Dsp,NewGC,W->Black);
		W->MyGC=NewGC;
		return 1;
	}
	else
		return 0;
}



XWnd *Display_CreateWindow(void)
{
	char		DisplayName[100];
	XWnd		*W = malloc(sizeof(*W));
	XWnd		**Ptr;
	XVisualInfo	Pref;
	int		Visuals;
	long		Nop;
	
	//sanity check
	if (W==NULL)
		ExitFatal("Could not allocate memory for Display_Wnd structure!");

	//get display name (or guess it)
	if (getenv("DISPLAY"))
		strcpy(DisplayName,(char *)getenv("DISPLAY"));
	else
		strcpy(DisplayName,":0.0");

	//open display
	W->Dsp=XOpenDisplay(DisplayName);
	if (W->Dsp==NULL)
		ExitFatal("Could not open display!");
	W->Scr=DefaultScreen(W->Dsp);
	W->Black=BlackPixel(W->Dsp,W->Scr);
	W->White=WhitePixel(W->Dsp,W->Scr);
	W->Vis=DefaultVisual(W->Dsp,W->Scr);
	W->MyDepth=DefaultDepth(W->Dsp,W->Scr);
	
	Pref.screen=W->Scr;
	W->VisInfo=XGetVisualInfo(W->Dsp,VisualScreenMask,&Pref,&Visuals);
	GetBpp(W->Dsp,W->VisInfo,&W->Bpp);
	
	printf("MyDepth: %d\n",W->MyDepth);
	
	
	//create window
	W->Wnd=XCreateSimpleWindow(W->Dsp,RootWindow(W->Dsp,W->Scr),10,10,20,20,5,W->Black,W->Black);
	
	if (!Display_MakeGC(W))
	{
		XDestroyWindow(W->Dsp,W->Wnd);
		ExitFatal("Could not make GC!");
	}
	
	XMapWindow(W->Dsp,W->Wnd);
	XmbSetWMProperties(W->Dsp,W->Wnd,Server_String,"dv",NULL,0,NULL,NULL,NULL);
	
	return W;
}



void Display_CreateImage(XWnd *W, int Width, int Height)
{
	W->Buffer=malloc(Width*Height*4);
	if (W->Buffer==NULL)
		ExitFatal("Could not allocate memory for image backbuffer!");
		
	W->XImg=XCreateImage(W->Dsp,W->Vis,W->MyDepth,ZPixmap,0,W->Buffer,Width,Height,32,0);
	W->ImgWidth=Width;
	W->ImgHeight=Height;
	
	XMapWindow(W->Dsp,W->Wnd);
	XFlush(W->Dsp);
}



void Display_DestroyImage(XWnd *W)
{
	XDestroyImage(W->XImg);
}



void Display_UpdateImage(XWnd *W)
{
	XPutImage(W->Dsp,W->Wnd,W->MyGC,W->XImg,0,0,5,5,W->ImgWidth,W->ImgHeight);
	XFlush(W->Dsp);
}



void Display_ResizeWindow(XWnd *W, int Width, int Height)
{
	XResizeWindow(W->Dsp,W->Wnd,Width,Height);
}



void Display_Init(void)
{
}



void Display_Kill(XWnd *W)
{
	//Destroy the window and close the display
	XDestroyWindow(W->Dsp,W->Wnd);
	XCloseDisplay(W->Dsp);
}


