/* Written by Stone Horse.
 * Public domain.
 */
#include <iostream.h>
#include <errors.h>
#include <strings.h>
#include <zstrings.h>
#include <ioctl.h>
#include <memory.h>
#include <linux/fb.h>
#include <unix.h>
#include <fbtools.h>
#include <graph.h>

static struct fb_var_screeninfo vinfo;

int fb_open(const char *device, struct graph_screen *scr)
{
	int ret;
	struct fb_fix_screeninfo finfo;

	/* Open the file for reading and writing */
	if (device == NULL)
		device = "/dev/fb0";
	(*scr).fd = open(device, O_RDWR);
	if ((*scr).fd < 0) {
		putz(2, __FILE__);
		putz(2, ": Failed to open file ");
		putz(2, device);
		putz(2, ": ");
		put_line(2, strerror(-(*scr).fd));
		return -1;
	}
	/* Get variable screen information */
	ret = ioctl((*scr).fd, FBIOGET_VSCREENINFO, &vinfo);
	if (ret < 0) {
		putz(2, __FILE__);
		putz(2, ": ioctl: ");
		put_line(2, strerror(-ret));
		return -1;
	}
	if (vinfo.bits_per_pixel != 16 && vinfo.bits_per_pixel != 32) {
		putz(2, __FILE__);
		return -1;
		putz(2, ": Unsupported bit depth\n");
	}
	/* Get fixed screen information */
	ret = ioctl((*scr).fd, FBIOGET_FSCREENINFO, &finfo);
	if (ret < 0) {
		putz(2, __FILE__);
		putz(2, ": ioctl: ");
		put_line(2, strerror(-ret));
		return -1;
	}
	/* Get the size of screen in bytes */
	(*scr).line_length = finfo.line_length;
	(*scr).xres = vinfo.xres_virtual;
	(*scr).yres = vinfo.yres_virtual;
	(*scr).bits_per_pixel = vinfo.bits_per_pixel;
	(*scr).size = vinfo.yres_virtual * finfo.line_length;
	(*scr).physcr = mmap(0, (*scr).size, PROT_READ | PROT_WRITE, MAP_SHARED, (*scr).fd, 0);
	if (*(int *)(*scr).physcr > -4096 && *(int *)(*scr).physcr < 0) {
		putz(2, __FILE__);
		putz(2, ": mmap: ");
		put_line(2, strerror(-*(int *)(*scr).physcr));
		return -1;
	}
	return 0;
}

int fb_close(struct graph_screen *scr)
{
	int ret;

	ret = ioctl((*scr).fd, FBIOPUT_VSCREENINFO, &vinfo);
	if (ret < 0)
		return ret;
	if ((*scr).size != 0) {
		munmap((*scr).physcr, (*scr).size);
		close((*scr).fd);
	}
	close((*scr).fd);

	return 0;
}
