/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:x_emul.c 12.0$ */
/* $ACIS:x_emul.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/cacons/RCS/x_emul.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:x_emul.c 12.0$";
#endif

#include "param.h"
#include "errno.h"
#include "file.h"
#include "conf.h"
#include "dir.h"
#include "user.h"
#include "ioctl.h"
#include "tty.h"

#include "../machinecons/screen_conf.h"
#include "../machinecons/xio.h"
#include "../machinecons/qevent.h"
#include "tbdefs.h"
#include "tbioctl.h"
#include "../machinecons/consdefs.h"
#include "../machinecons/kls.h"
#include "xemul.h"

struct XBuffArea *XBuff = (struct XBuffArea *)(MagicXBuffAddr);
int XBuffIndex = 0;
extern struct timeval time;
struct x_softc	x_softc[NXEMUL];
static int x_open_count = 0;

x_open(dev, tp, si)
dev_t dev;
register struct tty *tp;
register SCREEN_INFO *si;
{
	register EMUL_INFO *ei = &si->ine;
	struct XBuffArea *xb;
	struct x_softc *xs;

EDEBUG (0x08, aed_pr("x_open\n"));
	if ((ei->flag & X_ISOPEN) == 0) {
		E_SLOT(ei->unit, XBuffIndex);
		if ((unsigned) ei->unit >= NXEMUL)
			return(ENXIO);		/* not enough NXEMUL's */
		ei->flag = X_ISOPEN;
		SET_SLOT(ei->unit, XBuffIndex);
		xb = &XBuff [ei->unit];
		xs = &x_softc[ei->unit];
		xs->x_size.x_x_min = 0;
		xs->x_size.x_y_min = 0;
		xs->x_size.x_x_max = screen_sw[WS].hbits;
		xs->x_size.x_y_max = screen_sw[WS].vbits;
		xs->x_notrack = 0;	/* default kernel mouse tracking */
		xs->x_cursor = 1;	/* default kernel mouse tracking */
		xb->xioa.status = 0;
		xb->xioa.ibuff = xb->ibuff;
		xb->xioa.ihead = 0;
		xb->xioa.itail = 0;
		xb->xioa.iqsize = XMAXEVQ;
		xb->xioa.make_break = 0;
		xb->xioa.hotspot.x = xb->xioa.hotspot.y = 0;
		apa_initialize();
		show_locator(&(xb->xioa.hmbox));
		x_open_count++;
	}
	return (0);

}

x_close(tp, si)
register struct tty *tp;
register SCREEN_INFO *si;
{
	register EMUL_INFO *ei = &si->ine;
	EDEBUG (0x08, aed_pr("x_close\n"));

	ei->flag = 0;
	CLEAR_SLOT(ei->unit, XBuffIndex);
	ei->unit = 0;
	x_open_count--;
	/*
	 * on the last close, set the keyboard to default rept/mkbrk
	 * if possible.
	 */
	if (x_open_count == 0) {
		 set_default_key_types(KALLRPT, HAVE_REPEAT_MKBRK);
	}
}



x_read(tp, uio, si)
register struct tty *tp;
struct uio *uio;
register SCREEN_INFO *si;
{
	EDEBUG (0x08, aed_pr("x_read\n"));
	/* sleep on the line discipline so the csh doesn't die */
	return ((*linesw[tp->t_line].l_read)(tp, uio));
}

x_write(tp, uio, si)
register struct tty *tp;
struct uio *uio;
register SCREEN_INFO *si;
{
	EDEBUG (0x08, aed_pr("x_write\n"));
	return (ENXIO);
}

extern	int	beep_time;
extern	int	beep_freqh;
extern	int	beep_freql;
extern	int	beep_vol;

x_ioctl(tp, cmd, data, si)
register struct tty *tp;
int cmd;
caddr_t data;
register SCREEN_INFO *si;
{
	register EMUL_INFO *ei = &si->ine;
	register struct XBuffArea *xb = &XBuff [ei->unit];
	register struct x_softc *xs = &x_softc[ei->unit];
	register int volume,next_event,last_event;
	int s;

	EDEBUG (0x10, aed_pr("x_ioctl: cmd (%x)\n", cmd));

#ifdef ATR
	s = spl5();
#else 
	s = spl1();
#endif
	switch (cmd) {
	case QIOCSMSTATE:	/* Set mouse state */
		xb->xioa.mouse = *((XCursor *)data);
	EDEBUG (0x10, aed_pr("state: (%d,%d)\n", xb->xioa.mouse.x, xb->xioa.mouse.y));
		if (xs->x_cursor) {
		  pos_locator(xb->xioa.mouse.x, xb->xioa.mouse.y, &(xb->xioa.hmbox));
		}
		break;
	case QIOCADDR:
	EDEBUG (0x10, aed_pr("Addr: (%x)\n", data));
		*((struct XBuffArea **) data) = xb;
		break;
	case QIOCLDCUR:
	EDEBUG (0x10, aed_pr("LDCUR: (%x)\n", data));
		load_locator(data, &(xb->xioa.hmbox));
		break;
#ifndef NO_OLD_CURSOR
	/*
	 * This ioctl provides bianary compatibility with the old X 
	 * (as for as the cursor is concerned)
	 */
	case QIOOLDCUR:
		{
		QIOLocator tmp;

		bcopy(data,&tmp,sizeof(QIOOldLoc));
		tmp.fg = 0xffffffff;
		tmp.bg = 0;
		load_locator(&tmp, &(xb->xioa.hmbox));
		}
		break;
#endif NO_OLD_CURSOR
	case QIOCSHOWCUR:
	EDEBUG (0x10, aed_pr("SHOWCUR\n"));
		/*
		 * Don't do kernel cursor tracking if the user has changed
		 * the screen size.
		 */
		if (xs->x_notrack) {
			(void) splx(s);
			return (EINVAL);
		}
		apa_initialize();
		show_locator(&(xb->xioa.hmbox));
		xs->x_cursor = 1;
		break;
	case QIOCHIDECUR:
	EDEBUG (0x10, aed_pr("HIDECUR\n"));
		hide_locator((unsigned short *)data);
		xs->x_cursor = 0;
		break;
	case QIOCBELL:
		volume = (*(int *)data)/3;
		speaker(volume+1, beep_freqh, beep_freql, 
						beep_time*(*(int *)data));
		break;
	case QIOCCLICK:
	EDEBUG (0x10, aed_pr("CLICK\n"));
		{
		/* come up with hardwar values in assending order */
		 static int click_array[] = {0,200,100,190,180,93,1,50,80};

		 volume = *(int *)data;
		 if (volume == 0) {
			KEYCLICK_AUTO(0);
			break;
		 }
		 if (volume > 8) {
			return(EINVAL);
		 }
		 KEYCLICK_AUTO(1);
		 click_duration(click_array[volume]);
		 break;
		}
	case QIOCAUTOREP:
	EDEBUG (0x10, aed_pr("AUTOREP\n"));
		volume = *(int *)data;
		if (volume)
			set_default_key_types(KALLRPT, HAVE_REPEAT_MKBRK);
		else
			set_default_key_types(KALLMKBRK, HAVE_REPEAT_MKBRK);
		break;
	case QIOCSETCAPSL:
	EDEBUG (0x10, aed_pr("CAPSLOCK\n"));
		set_kbd_led(CAPS_LED);
		break;
	case QIOCSETNUML:
	EDEBUG (0x10, aed_pr("NUMLOCK\n"));
		set_kbd_led(NUM_LED);
		break;
	case QIOCSETSCROLLL:
	EDEBUG (0x10, aed_pr("SCROLLLOCK\n"));
		set_kbd_led(SCROLL_LED);
		break;
	case QIOCCLRCAPSL:
	EDEBUG (0x10, aed_pr("CAPSLOCK\n"));
		set_kbd_led(~CAPS_LED);
		break;
	case QIOCCLRNUML:
	EDEBUG (0x10, aed_pr("NUMLOCK\n"));
		set_kbd_led(~NUM_LED);
		break;
	case QIOCCLRSCROLLL:
	EDEBUG (0x10, aed_pr("SCROLLLOCK\n"));
		set_kbd_led(~SCROLL_LED);
		break;
	case QIOCSETSIZE:
		xs->x_size = *(struct x_screen_size *)data;
		xs->x_notrack = 0;
		xs->x_cursor = 0;
		break;
	case QIOCGETSIZE:
		*(struct x_screen_size *)data = xs->x_size;
		break;
	case QIOCADDEVENT:
	case QIOCMUSTADD:
		/*
		 * allow the server to add an event to it's own event queue
		 */
		last_event = xb->xioa.itail;
		if ((next_event = EVROUND(xb->xioa.itail + 1)) ==
							xb->xioa.ihead) {
			if (cmd == QIOCADDEVENT) {
				(void) splx(s);
				return(ENOBUFS);
			}
			last_event =EVROUND(xb->xioa.itail-1);
			next_event = xb->xioa.itail;
		}
		bcopy(data,&(xb->ibuff[last_event]),sizeof(XEvent));
		xb->xioa.itail = next_event;

		if (xb->xioa.itail != xb->xioa.ihead) {
			if (ei->rsel) {
				EDEBUG(0x08, aed_pr("x_rint do selwakeup\n"));
				selwakeup(ei->rsel, 0);
				ei->rsel = 0;
			}
		}
		break;
	default:
		(void) splx(s);
		return (-1);
	}
	(void) splx(s);
	return (0);
}

int	last_scan;
int	cons_switch_focus_code;

x_rint(data, tp, si)
int data;
struct tty *tp;
register SCREEN_INFO *si;
{
	register EMUL_INFO *ei = &si->ine;
	register struct XBuffArea *xb = &XBuff [ei->unit];
	register XEvent *xep;
	register int next_event, last_event;
	struct tbmspos *msposp;
	struct tb *tbp;
	XCursor *cur;
	static u_short old_buts = 0;
	int intr_reason = ei->intr_reason;
	static int last_but = 0;
	struct x_softc	*xs = &x_softc[ei->unit];
	u_short toy;

EDEBUG(0x08, aed_pr("x_rint data (%x) intr (%x)\n", data, intr_reason));

	cur = &(xb->xioa.mouse);
	ei->intr_reason = 0;

	/* Get index of next queue entry and check if it is available */
	if ((next_event = EVROUND(xb->xioa.itail + 1)) == xb->xioa.ihead)
		return(1);

EDEBUG(0x08, aed_pr("x_rint: xb (%x)\n", xb));
EDEBUG(0x08, aed_pr("x_rint tail (%x) head (%x)\n", xb->xioa.itail, xb->xioa.ihead));

	/* Get the current Time of Year */
	toy = TOY;

	/* Determine the event */
	switch (intr_reason) {
	case E_INT_KBD:
		/* Is the code telling us what key was let go? */
EDEBUG(0x08, aed_pr("x_rint E_INT_KBD\n"));
#ifndef CONS_FOCUS
		if ((data == cons_switch_focus_code) && (last_scan != 0xF0)) {
			cons_focus (SCREEN_SWITCH);
			last_scan = data;
			break;
		}
		last_scan = data;
#endif CONS_FOCUS
		if (data == BREAK) {
			xb->xioa.make_break = 1;
		} else {

			/* Get current event queue structure */
			xep = &xb->ibuff[xb->xioa.itail];
EDEBUG(0x08, aed_pr("x_rint put event at (%x)\n", xep));

			/* Put a button event on the queue */
			xep->xe_type = XE_BUTTON;
			xep->xe_device = XE_DKB;
			xep->xe_x = cur->x;
			xep->xe_y = cur->y;
			xep->xe_time = toy;
			xep->xe_key = (u_char) data;
			xb->xioa.itail = next_event;

			/* Put in the direction of the key event */
			if (xb->xioa.make_break) {
				xep->xe_direction = XE_KBTUP;
				xb->xioa.make_break = 0;
			} else {
				xep->xe_direction = XE_KBTDOWN;
			}
EDEBUG(0x08, aed_pr("x_rint put event done\n"));
		}
		break;
	case E_INT_MOUSE:
	    /* Check for any mouse events */
	    tbp = (struct tb *)(tp->T_LINEP);

	    while (tbp->lastindex != tbp->curindex) {
		msposp = &(tbp->tbpos[tbp->lastindex]);
		tbp->lastindex = (tbp->lastindex + 1) % TBQUEUESIZE;

EDEBUG(0x08, aed_pr("x_rint x,y(%d,%d)\n", msposp->xpos, msposp->ypos));
EDEBUG(0x08, aed_pr(" msposp (%x) lastindex (%d) curindex (%d)\n", msposp, tbp->lastindex, tbp->curindex));

		/* Check for mouse motion event */
		if (msposp->xpos || msposp->ypos) {
			struct x_screen_size *xss = &xs->x_size;
			register int xMin = xss->x_x_min - xb->xioa.hotspot.x;
			register int yMin = xss->x_y_min - xb->xioa.hotspot.y;
			register int xMax = xss->x_x_max + xb->xioa.hotspot.x;
			register int yMax = xss->x_y_max + xb->xioa.hotspot.y;

			cur->x += msposp->xpos;
			cur->y += msposp->ypos;

			/* Keep Mouse in Boundaries */
			if (cur->x < xMin)
				cur->x = xMin;
			if (cur->y < yMin)
				cur->y = yMin;
			if (cur->x > xMax)
				cur->x = xMax;
			if (cur->y > yMax)
				cur->y = yMax;

			/* Update Cursor Position */
			if (xs->x_cursor)
				pos_locator (cur->x, cur->y, &(xb->xioa.hmbox));

			/* no event if inside box */
			if (cur->y < xb->xioa.mbox.bottom &&
				cur->y >= xb->xioa.mbox.top &&
				cur->x < xb->xioa.mbox.right &&
				cur->x >= xb->xioa.mbox.left) {
EDEBUG(0x08, aed_pr("x_rint mouse (%d,%d) in box(%d,%d)->(%d,%d)\n",
	cur->x, cur->y, xb->xioa.mbox.left, xb->xioa.mbox.top,
	xb->xioa.mbox.right, xb->xioa.mbox.bottom));
					goto x_rint_but;
			}

			/* trash box */
			xb->xioa.mbox.bottom = 0;

			/* Get Last Entry in event queue */
			last_event = EVROUND(xb->xioa.itail-1);

			/* Check if last entry in event queue has been read yet
			 * or is currently being read
			 */
			if ((xb->xioa.itail != xb->xioa.ihead) &&
				(last_event != xb->xioa.ihead)) {

				xep = &xb->ibuff[last_event];
				/* Check if last event was a Mouse Motion */
				if (xep->xe_type == XE_MMOTION) {
					/* Update event to current
					 * mouse postiion
					 */
					xep->xe_x = cur->x;
					xep->xe_y = cur->y;
					xep->xe_time = toy;
					goto x_rint_but;
				}
			}
EDEBUG(0x08, aed_pr("x_rint enter mmotion event tail (%x)\n", xb->xioa.itail));

			/* Get current event queue structure */
			xep = &xb->ibuff[xb->xioa.itail];

			/* Add Mouse Motion event into queue */
			xep->xe_type = XE_MMOTION;
			xep->xe_device = XE_MOUSE;
			xep->xe_direction = XE_KBTUP;
			xep->xe_x = cur->x;
			xep->xe_y = cur->y;
			xep->xe_time = toy;
			xep->xe_key = (u_char) 0;
			xb->xioa.itail = next_event;

			/* Get index of next queue entry */
			if ((next_event = EVROUND(xb->xioa.itail + 1))
				== xb->xioa.ihead)
					goto x_rint_leave;

EDEBUG(0x08, aed_pr("XEVENT-MOTION x,y (%x,%x)\n", xep->xe_x, xep->xe_y));
EDEBUG(0x20, aed_pr("M"));
		}
x_rint_but:
EDEBUG(0x08, aed_pr("last_but (%x) status (%x)\n", last_but, msposp->status));
		if (last_but != (data = msposp->status)) {
			register int i, up, down;
			up  = ( last_but ^ data ) & last_but;
			down= ( last_but ^ data ) & data;

			/* queue event for up transition (if one exists) */
			if ( up ) {
EDEBUG(0x08, aed_pr("x_rint enter BU event tail (%x)\n", xb->xioa.itail));
				for (i= 0; i < 3 ; i++ )
					if ((up >> i) & 1) break;
				last_but &= ~up;
				/* Get current event queue structure */
				xep = &xb->ibuff[xb->xioa.itail];

				/* Put Button event into queue */
				xep->xe_type = XE_BUTTON;
				xep->xe_device = XE_MOUSE;
				xep->xe_direction = XE_KBTUP;
				xep->xe_x = cur->x;
				xep->xe_y = cur->y;
				xep->xe_time = toy;
				xep->xe_key = (u_char) i;
				xb->xioa.itail = next_event;
				if ((next_event = EVROUND(xb->xioa.itail + 1))
					== xb->xioa.ihead) {
					goto x_rint_leave;
				}
			}
			/* queue event for down transistion (if one exists) */
			if ( down ) {
EDEBUG(0x08, aed_pr("x_rint enter BD event tail (%x)\n", xb->xioa.itail));
				for (i= 0; i < 3; i++)
					if (( down >> i ) & 1 ) break;
				last_but |= down;
				/* Get current event queue structure */
				xep = &xb->ibuff[xb->xioa.itail];

				/* Put Button event into queue */
				xep->xe_type = XE_BUTTON;
				xep->xe_device = XE_MOUSE;
				xep->xe_direction = XE_KBTDOWN;
				xep->xe_x = cur->x;
				xep->xe_y = cur->y;
				xep->xe_time = toy;
				xep->xe_key = (u_char) i;
				xb->xioa.itail = next_event;
				if ((next_event = EVROUND(xb->xioa.itail + 1))
					== xb->xioa.ihead) {
					goto x_rint_leave;
				}
			}
		}

		/* Get index of next queue entry */
		if ((next_event = EVROUND(xb->xioa.itail + 1)) ==xb->xioa.ihead)
			goto x_rint_leave;
	    }
	    break;
	default:
EDEBUG(0x08, aed_pr("Illegal XE_RINT entry reason (%x)x\n", intr_reason));
		printf ("Illegal XE_RINT entry reason (%x)x\n", intr_reason);
		return (1);
	}

x_rint_leave:

EDEBUG(0x08, aed_pr("x_rint Check for select (%x)\n", ei->rsel));

	/* Do select if anyone is waiting for an event */
	if (xb->xioa.itail != xb->xioa.ihead)
		if (ei->rsel) {
EDEBUG(0x08, aed_pr("x_rint do selwakeup\n"));
			selwakeup(ei->rsel, 0);
			ei->rsel = 0;
		}

	return (0);
}

/* Should not get here x_emul is really just an input emulator */
x_putc(c, si)
char c;
register SCREEN_INFO *si;
{
	return (cnforward_putc(c, si));
}

x_select(dev, rw, si)
dev_t dev;
int rw;
register SCREEN_INFO *si;
{
	register EMUL_INFO *ei = &si->ine;
	register struct XBuffArea *xb = &XBuff [ei->unit];
#ifdef ATR 
	int s = spl5();
#else 
	int s = spl1();
#endif
EDEBUG (0x08, aed_pr("x_select\n"));

	switch(rw) {
	case FREAD:
		if (xb->xioa.ihead != xb->xioa.itail) {
			splx(s);
			return(1);
		}
		ei->rsel = u.u_procp;
		splx(s);
		return(0);

	case FWRITE:
		splx(s);
		return(EACCES);
	}
}
