/*
 * device driver for the screen and mouse devices
 */

#include <linea.h>
#include <memory.h>
#include <device.h>
#include <errno.h>
#include <osbind.h>
#include <string.h>

#undef RED
#undef GREEN
#undef BLUE
#include <screen.h>
#include <mouse.h>

#define SCR_FD	mkdev(0x04, 0x01)
#define MOUS_FD	mkdev(0x04, 0x02)

#define MULT (256/SCR_RGB_VALUES)
#define RGB_B 0x000f
#define RGB_G 0x00f0
#define RGB_R 0x0f00

static long _scr_pos;		/* screen device read position */
static int _mous_x, _mous_y;

static long scr_open(name, flags, mode)
	char *name;
	int flags;
	unsigned mode;
{
	if (!strcmp(name, "SCR:")) {
		(void)linea0();	/* initialize line A variables */
		_scr_pos = 0;
		return SCR_FD;
	}
	if (!strcmp(name, "MOUS:")) {
		(void)linea0();
		_mous_x = GCURX; _mous_y = GCURY;
		return MOUS_FD;
	}
	errno = ENOENT;		/* shouldn't happen */
	return -1;
}

static long scr_close(fd)
int fd;
{
	return 0;
}

static long scr_read(fd, buf, nbytes)
	int fd;
	char *buf;
	long nbytes;
{
	static struct mouse_buf mouse;
	long cnt = 0;
	char *from;

	if (fd == SCR_FD) {
		cnt = ((long)V_X_MAX * V_Y_MAX * (long)VPLANES)/8;
		cnt -= _scr_pos;
		if (nbytes < cnt)
			cnt = nbytes;
		from = (char *)Logbase() + _scr_pos;
		if (cnt < 0) return 0;
		bcopy(from, buf, cnt);
		_scr_pos += cnt;
		return cnt;
	}
	if (fd == MOUS_FD) {
		cnt = (nbytes < sizeof(mouse)) ? nbytes : sizeof(mouse);
		mouse.m_buttons = MOUSE_BT;
		mouse.m_dx = GCURX - _mous_x; _mous_x = GCURX;
		mouse.m_dy = GCURY - _mous_y; _mous_y = GCURY;
		from = (char *) &mouse;
		nbytes = cnt;
		while (cnt-- > 0)
			*buf++ = *from++;
		return nbytes;
	}
	errno = EBADF;
	return -1;
}

static long scr_write(fd, buf, nbytes)
	int fd;
	char *buf;
	long nbytes;
{
	char *to;
	long result;

	if (fd == SCR_FD) {
		to = (char *)Logbase() + _scr_pos;
		result = ((long)V_X_MAX*V_Y_MAX*(long)VPLANES)/8;
		result -= _scr_pos;
		if (nbytes < result) result = nbytes;
		if (result <= 0) return 0;
		bcopy(buf, to, result);
		_scr_pos += result;
		return result;
	}
	if (fd == MOUS_FD) {
		errno = EACCESS;
	}
	else
		errno = EBADF;
	return -1;
}

static long scr_ioctl(fd, func, arg)
	int fd;
	int func;
	void *arg;
{
	short i, color;

	struct scr_param *p = (struct scr_param *)arg;
	struct scr_palette *c = (struct scr_palette *)arg;

	if (fd != SCR_FD) {
		errno = ENOTTY;
		return -1;
	}

	switch(func) {
	case SCRGETPARAM:
		p->scr_base = (long)Logbase();
		p->scr_width = V_X_MAX;
		p->scr_height = V_Y_MAX;
		p->scr_depth = VPLANES;
		p->scr_mode = Getrez();
		break;
	case SCRSETPARAM:
		Setscreen(-1L, -1L, p->scr_mode);
		Setscreen(p->scr_base, p->scr_base, -1);
		break;
	case SCRGETCOLOR:
		for (i = 0; i < SCR_NUM_COLORS; i++) {
			color = Setcolor(i, -1);
			c->scr_rgb[i][BLUE] = MULT * (color & RGB_B);
			c->scr_rgb[i][GREEN] = MULT * ((color & RGB_G) >> 4);
			c->scr_rgb[i][RED] = MULT * ((color & RGB_R) >> 8);
		}
		break;
	case SCRSETCOLOR:
		for(i = 0; i < SCR_NUM_COLORS; i++) {
			color = c->scr_rgb[i][RED] / MULT;
			color = color << 4;
			color |= c->scr_rgb[i][GREEN] / MULT;
			color = color << 4;
			color |=  c->scr_rgb[i][BLUE] / MULT;
			(void)Setcolor(i, color);
		}
		break;
	default:
		errno = EINVAL;
		return -1;
	}
	return 0;
}

static struct _device scrn = {
	"SCR:", "screen", SCR_FD,
	scr_open, scr_close, scr_read, scr_write, scr_ioctl,
	0
};

static struct _device mouse = {
	"MOUS:", "mouse", MOUS_FD,
	scr_open, scr_close, scr_read, scr_write, scr_ioctl,
	0
};

void
install_screen()
{
	_install_device(&scrn);
	_install_device(&mouse);
}
