/* ================================================================*\

	grab.c -- a program to grab images from the WinVision
		frame grabber and display them in real time
		using an X window. It allows continuous display
		by pression 'c', or one image at a time by pressing
		the space bar. It gets the image in raw format
		from the device driver (i.e. without any type
		of aspect ratio expansion).

		This program makes use of the colormap capability of
		the device driver.

	(C) Carlos Puchol, 1993
	    cpg@cs.utexas.edu
	    P.O. Box 7817
	    Austin, TX 78717-7817

	This program may be distributed/modified/copied under the terms
	of the GNU GENERAL PUBLIC LICENSE. It comes as is, with no
	guarantees.

\* ================================================================*/

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

#include <stdio.h>

#define FG_HPOS_SET		0x0001
#define FG_HPOS_GET		0x0002
#define FG_VPOS_SET		0x0003
#define FG_VPOS_GET		0x0004
#define FG_BOARD_SET		0x0005
#define FG_BOARD_GET		0x0006
#define FG_CMAP_SET		0x0007
#define FG_EXPANSION_SET	0x0008

#define WIDTH	187
#define HEIGHT	241

#define MODE_STOP	0
#define MODE_CONTINUOUS	1

static int	mode = MODE_STOP;
static int	fd;		/* Frame grabber file */

static int image_count = 0;

#define TOTXE           310

typedef struct {
        int     HSize;
        int     VSize;
        int     HSkip;
        int     VSkip;
        unsigned char   pixels[HEIGHT][WIDTH];
        } t_image;

t_image image;

Display	*display;
int	screen;
Visual	*visual;
int	depth;
XImage	*ximage;

unsigned char pixel_map[256];		/* Pixel mappings to image */


void main(argc, argv)
int argc;
char **argv;
{
	Window win;
	unsigned int width, height, x, y;     /* window size and position */
	unsigned int borderwidth = 4;	      /* four pixels */
	unsigned int display_width, display_height;
	unsigned int icon_width, icon_height;
	char *window_name = "Image Grabbing Window";
	char *icon_name = "grab";
	Pixmap icon_pixmap;
	XSizeHints size_hints;
	XEvent report;
	GC gc;
	char *display_name = NULL;
	char	buf;
	KeySym	ks;
	XComposeStatus status;


	/* connect to X server */

	if ( (display=XOpenDisplay(display_name)) == NULL ) {
		(void) fprintf( stderr, "grab: cannot connect to X server %s\\n",
				XDisplayName(display_name));
		exit( -1 );
	}

	/* get screen size from display structure macro */
	screen = DefaultScreen(display);
	display_width = DisplayWidth(display, screen);
	display_height = DisplayHeight(display, screen);
	visual = DefaultVisual(display, screen);
	depth = DefaultDepth(display, screen);

	allocate_grey_colormap();

	printf("Controls: C - continuous mode capture & display.\n");
	printf("          Q - quits the program.\n");
	printf("          Any other key - stops capture.\n");
	printf("          Space Bar - capture one frame.\n");

	open_fgrabber();

	/* place window */
	x = display_width/3, y = display_height/3;

	/* size window with enough room for text */
	width = WIDTH, height = HEIGHT;

	/* create opaque window */
	win = XCreateSimpleWindow(display, RootWindow(display,screen), x, y, 
			width, height, borderwidth, BlackPixel(display,
	    		screen), WhitePixel(display,screen));

	/* Set resize hints */
	size_hints.flags = PPosition | PSize | PMaxSize | PMinSize;
	size_hints.x = x;
	size_hints.y = y;
	size_hints.width = width;
	size_hints.height = height;
	size_hints.max_width = width;
	size_hints.max_height = height;
	size_hints.min_width = width;
	size_hints.min_height = height;

	/* set Properties for window manager (always before mapping) */
	XSetStandardProperties(display, win, window_name, icon_name, 
	    None, argv, argc, &size_hints);

	/* Select event types wanted */
	XSelectInput(display, win, ExposureMask | KeyPressMask | 
			ButtonPressMask | StructureNotifyMask);

	/* create GC for text and drawing */
	get_GC(win, &gc);

	ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
			(char *) image.pixels, width, height, 8, 0);

	if (ximage == NULL) printf("Sorry, could not create image.");

	/* Display window */
	XMapWindow(display, win);

	/* get events, use first to display text and graphics */
	while (1)  {
		if (mode == MODE_CONTINUOUS) {
			place_graphics(win, gc, width, height);
			XFlush(display);
			if (!XCheckWindowEvent(display, win, ExposureMask |
				KeyPressMask | ButtonPressMask |
				StructureNotifyMask, &report))
			   continue;
		} else XNextEvent(display, &report);

		switch  (report.type) {
		case Expose:
			/* get all other Expose events on the queue */
			while (XCheckTypedEvent(display, Expose, &report));

			/* place graphics in window, */
			place_graphics(win, gc, width, height);
			XFlush(display);

			/*reflect_window(win, win2, gc, width, height);*/
			break;
		case ConfigureNotify:
			/* window has been resized, change width and
			 * height to send to place_text and place_graphics
			 * in next Expose */
			break;
		case ButtonPress:
			break;
		case KeyPress:
			mode = XLookupString((XKeyEvent *)&report, &buf, 1, &ks, &status);
			switch (buf) {
			case ' ':
				mode = MODE_STOP;
				place_graphics(win, gc, width, height);
				XFlush(display);
				break;
			case 'q':
			case 'Q':
				printf("Images done: %d\n", image_count);
				exit(0);
				break;
			case 'c':
			case 'C':
				mode = MODE_CONTINUOUS;
				break;
			default:
				mode = MODE_STOP;
			}
			break;
		default:
			/* all events selected by StructureNotifyMask
			 * except ConfigureNotify are thrown away here,
			 * since nothing is done with them */
			break;
		} /* end switch */
	} /* end while */
}

allocate_grey_colormap()
{
	int	i;
	XColor color;
	Colormap default_cmap;

	default_cmap   = DefaultColormap(display, screen);

	for (i=0; i < 64 ; i ++) {
		
		color.blue=color.green=color.red =
			(unsigned short)(((0xFFFFL) * i)/63);
		color.blue = color.red;
		if (!XAllocColor(display, default_cmap, &color))
			printf("Could not allocate grey tone %d.", i);
		pixel_map[(unsigned char) (i<<2)] = (unsigned char) color.pixel;
		/*printf("Color %d (%02x) is pixel: %08lx\n", i,
			pixel_map[(i<<2)], color.pixel);*/

	}
}

get_GC(win, gc)
Window win;
GC *gc;
{
	unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
	XGCValues values;
	unsigned int line_width = 6;
	int line_style = LineOnOffDash;
	int cap_style = CapRound;
	int join_style = JoinRound;
	int dash_offset = 0;
	static char dash_list[] = {
		12, 24	};
	int list_length = 2;

	/* Create default Graphics Context */
	*gc = XCreateGC(display, win, valuemask, &values);

	/* specify black foreground since default may be white on white */
	XSetForeground(display, *gc, BlackPixel(display,screen));

	/* set line attributes */
	XSetLineAttributes(display, *gc, line_width, line_style, cap_style, 
			join_style);

	/* set dashes to be line_width in length */
	XSetDashes(display, *gc, dash_offset, dash_list, list_length);

	XSetFunction(display, *gc, GXcopy);
}

place_graphics(win, gc, window_width, window_height)
Window win;
GC gc;
unsigned int window_width, window_height;
{
	grab_image();
	XPutImage(display, win, gc, ximage, 0, 0, 0, 0, window_width, window_height);
	image_count++;
}

open_fgrabber()
{
	int	res;

	if ((fd=open("/dev/fgrabber", O_RDWR)) < 0 ) {
		perror("Could not open frame grabber, giving up");
		exit(-1);
	}

	image.HSize=WIDTH;
	image.VSize=HEIGHT;
	image.HSkip=0;
	image.VSkip=0;
	
	res = ioctl(fd, FG_CMAP_SET, pixel_map);
	if (res < 0) {
		perror ("Trying the FG_CMAP_SET IOCTL to the frame grabber");
		exit(-1);
	}

	/* camera 1-4 = 0x268, 0x368, 0x2e8, 0x3e8 */

	res = ioctl(fd, FG_EXPANSION_SET, 0);
	if (res < 0) {
		perror ("Trying the FG_EXPANSION_SET IOCTL to the frame grabber");
		exit(-1);
	}

	res = ioctl(fd, FG_BOARD_SET, 0x268);
	if (res < 0) {
		perror ("Trying the FG_BOARD_SET IOCTL to the frame grabber");
		exit(-1);
	}
}

grab_image()
{
	int	res, x, y;


	res = read(fd, &image, sizeof(image));
	if (res < 0) {
		perror ("Reading from frame grabber");
		exit(-1);
	}
}
