#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <linux/videodev2.h>
#include <linux/fb.h>
#include <linux/matroxfb.h>
#include <linux/ivtvfb.h>

#define W 720
#define H 576

static unsigned short osd[720*576];

int main(int argc, char **argv)
{
	char *device = "/dev/fb0";
	struct fb_var_screeninfo vi;
	struct ivtvfb_dma_frame df;
	int fd;
	int i, j;

	if (argc > 1) device = argv[1];

	fd = open(device, O_RDWR);

	if (fd == -1) {
		fprintf(stderr, "cannot open %s\n", device);
		exit(-1);
	}

	ioctl(fd, FBIOGET_VSCREENINFO, &vi);
	vi.nonstd = 0;
	vi.bits_per_pixel = 16;
	vi.red.offset = 8;
	vi.red.length = 4;
	vi.green.offset = 4;
	vi.green.length = 4;
	vi.blue.offset = 0;
	vi.blue.length = 4;
	vi.transp.offset = 12;
	vi.transp.length = 1;
	vi.xres = W;
	vi.yres = H;
	vi.xres_virtual = W;
	vi.yres_virtual = H;
	vi.xoffset = 0;
	vi.yoffset = 0;
	ioctl(fd, FBIOPUT_VSCREENINFO, &vi);

	memset(osd, 0, sizeof(osd));
	df.source = osd;
	df.dest_offset = 0;
	df.count = sizeof(osd);
	ioctl(fd, IVTVFB_IOC_DMA_FRAME, &df);

	// go from black to white (opaque)
	for (i = 0; i < 16; i++) {
		for (j = 0; j < sizeof(osd) / 2; j++)
			osd[j] = i | (i << 4) | (i << 8);
		ioctl(fd, IVTVFB_IOC_DMA_FRAME, &df);
		ioctl(fd, FBIO_WAITFORVSYNC, 0);
		ioctl(fd, FBIO_WAITFORVSYNC, 0);
		ioctl(fd, FBIO_WAITFORVSYNC, 0);
		ioctl(fd, FBIO_WAITFORVSYNC, 0);
	}
	// go from opaque to transparent
	for (j = 0; j < sizeof(osd) / 2; j++)
		osd[j] |= (1 << 12);
	ioctl(fd, IVTVFB_IOC_DMA_FRAME, &df);
	sleep(1);

	ioctl(fd, FBIOGET_VSCREENINFO, &vi);
	vi.red.offset = 10;
	vi.red.length = 5;
	vi.green.offset = 5;
	vi.green.length = 5;
	vi.blue.offset = 0;
	vi.blue.length = 5;
	vi.transp.offset = 15;
	vi.transp.length = 1;
	ioctl(fd, FBIOPUT_VSCREENINFO, &vi);

	memset(osd, 0, sizeof(osd));
	df.source = osd;
	df.dest_offset = 0;
	df.count = sizeof(osd);
	ioctl(fd, IVTVFB_IOC_DMA_FRAME, &df);

	// go from black to white (opaque)
	for (i = 0; i < 32; i++) {
		for (j = 0; j < sizeof(osd) / 2; j++)
			osd[j] = i | (i << 5) | (i << 10);
		ioctl(fd, IVTVFB_IOC_DMA_FRAME, &df);
		ioctl(fd, FBIO_WAITFORVSYNC, 0);
		ioctl(fd, FBIO_WAITFORVSYNC, 0);
		ioctl(fd, FBIO_WAITFORVSYNC, 0);
		ioctl(fd, FBIO_WAITFORVSYNC, 0);
	}
	// go from opaque to transparent
	for (j = 0; j < sizeof(osd) / 2; j++)
		osd[j] |= (1 << 15);
	ioctl(fd, IVTVFB_IOC_DMA_FRAME, &df);
	sleep(1);

	ioctl(fd, FBIOGET_VSCREENINFO, &vi);
	vi.nonstd = 1;
	vi.red.offset = 11;
	vi.red.length = 5;
	vi.green.offset = 5;
	vi.green.length = 6;
	vi.blue.offset = 0;
	vi.blue.length = 5;
	vi.transp.offset = 0;
	vi.transp.length = 0;
	ioctl(fd, FBIOPUT_VSCREENINFO, &vi);

	memset(osd, 0, sizeof(osd));
	df.source = osd;
	df.dest_offset = 0;
	df.count = sizeof(osd);
	ioctl(fd, IVTVFB_IOC_DMA_FRAME, &df);

	// go from black to white (opaque)
	for (i = 0; i < 32; i++) {
		for (j = 0; j < sizeof(osd) / 2; j++)
			osd[j] = i | (i << 6) | (i << 11);
		ioctl(fd, IVTVFB_IOC_DMA_FRAME, &df);
		ioctl(fd, FBIO_WAITFORVSYNC, 0);
		ioctl(fd, FBIO_WAITFORVSYNC, 0);
		ioctl(fd, FBIO_WAITFORVSYNC, 0);
		ioctl(fd, FBIO_WAITFORVSYNC, 0);
	}

	return 0;
}
