// AmiVNC - Amiga experimental VNC server - Protocol ORL version 3.3
// (c) Stephane Guillard - stephane.guillard@steria.com
// This module is the mouse / key event handler.
// It is largely based on another module written by my friend Denis Spach for his own AVNC server,
// and as it solved a NEWPOINTERPOS problem, it went into AmiVNC as well.

#include <intuition/intuition.h>
#include <clib/exec_protos.h>
#include <clib/commodities_protos.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <libraries/commodities.h>

#include "rfbproto.h"                   // Structures & constants for VNC protocol

int
    ox = -1,
    oy = -1,
    butn = 0,
    qual = 0;

/*
 * mouse buttons events preset
 */
static struct InputEvent btevent =
{
	NULL,				/* NextEvent			*/
	IECLASS_RAWMOUSE,		/* Class			*/
	NULL,				/* SubClass			*/
	NULL,				/* Code 		event	*/
	NULL,				/* Qualifier		event	*/

	{ NULL, NULL }, 		/* Position			*/
					/* position qualifier will be	*/
					/* always RELATIVE, with delta	*/
					/* sets to 0 (no need to know	*/
					/* the screen dimensions)	*/

	{ 0L, 0L }			/* TimeStamp		event	*/
};

/*
 * mouse move events preset
 */
static struct {
	struct InputEvent	ie;
	struct IEPointerPixel	pp;
} ppevent =
{
	{
	    NULL,			/* NextEvent			*/
	    IECLASS_NEWPOINTERPOS,	/* Class			*/
	    IESUBCLASS_PIXEL,		/* SubClass			*/
	    IECODE_NOBUTTON,		/* Code 			*/
	    NULL,			/* Qualifier		event	*/
	    { NULL, NULL },		/* APTR on pp		init	*/
	    { 0L, 0L }			/* TimeStamp		event	*/
	},
	{
	    NULL,			/* Screen		event	*/
	    {0, 0}			/* position		event	*/
	}
};

/*
 * key events preset
 */
static struct InputEvent kevent =
{
	NULL,				/* NextEvent			*/
	IECLASS_RAWKEY, 		/* Class			*/
	NULL,				/* SubClass			*/
	NULL,				/* Code 		event	*/
	NULL,				/* Qualifier		event	*/

	{ NULL, NULL }, 		/* Position			*/
					/* position qualifier will be	*/
					/* always RELATIVE, with delta	*/
					/* sets to 0 (no need to know	*/
					/* the screen dimensions)	*/

	{ 0L, 0L }			/* TimeStamp		event	*/
};

#define K(k)	case XK_ ## k: code = AK_ ## k; break

#define XK_Shift_L      0xFFE1
#define XK_Shift_R      0xFFE2
#define XK_Control_L    0xFFE3
#define XK_Control_R    0xFFE4
#define XK_Alt_L        0xFFE9
#define XK_Alt_R        0xFFEA
#define XK_Home         0xFF50
#define XK_Left         0xFF51
#define XK_Up           0xFF52
#define XK_Right        0xFF53
#define XK_Down         0xFF54
#define XK_Page_Up      0xFF55
#define XK_Page_Down    0xFF56
#define XK_End          0xFF57

#define AK_Home         0x3D
#define AK_Left         0x4F
#define AK_Up           0x4C
#define AK_Right        0x4E
#define AK_Down         0x4D
#define AK_Page_Up      0x3F
#define AK_Page_Down    0x1F
#define AK_End          0x1D

void	do_key(struct IOStdReq *inputReqBlk, int dn, int key)
{
	int	code = 0;

	kevent.ie_NextEvent = NULL;
	kevent.ie_Class     = IECLASS_RAWKEY;
	kevent.ie_SubClass  = IESUBCLASS_COMPATIBLE;

	switch(key)
	{
/*
	K(BackSpace);
	K(Tab);
	K(Return);
	K(Escape);
	K(Delete);
*/
	K(Home);
	K(Left);
	K(Up);
	K(Right);
	K(Down);
	K(Page_Up);
	K(Page_Down);
	K(End);

/*
	K(Insert);
	K(Help);

	K(KP_Multiply);
	K(KP_Add);
	K(KP_Separator);
	K(KP_Subtract);
	K(KP_Divide);
	K(KP_0);
	K(KP_1);
	K(KP_2);
	K(KP_3);
	K(KP_4);
	K(KP_5);
	K(KP_6);
	K(KP_7);
	K(KP_8);
	K(KP_9);

	K(F1);
	K(F2);
	K(F3);
	K(F4);
	K(F5);
	K(F6);
	K(F7);
	K(F8);
	K(F9);
	K(F10);
*/

	case XK_Shift_L:
	    if (dn) qual |=  IEQUALIFIER_LSHIFT;
	       else qual &= ~IEQUALIFIER_LSHIFT;
	    break;
	case XK_Shift_R:
	    if (dn) qual |=  IEQUALIFIER_RSHIFT;
	       else qual &= ~IEQUALIFIER_RSHIFT;
	    break;
	case XK_Control_L:
	case XK_Control_R:
	    if (dn) qual |=  IEQUALIFIER_CONTROL;
	       else qual &= ~IEQUALIFIER_CONTROL;
	    break;
	case XK_Alt_L:
	    if (dn) qual |=  IEQUALIFIER_LALT;
	       else qual &= ~IEQUALIFIER_LALT;
	    break;
   case XK_Alt_R:
	    if (dn) qual |=  IEQUALIFIER_RALT;
	       else qual &= ~IEQUALIFIER_RALT;
	    break;

	default:
	    code = -1;

        // Map out other 0xFF'ed keys
        key &= 0xFF;

	    // if ((unsigned) key > 0xff)
		// return;

	    if (qual & IEQUALIFIER_CONTROL)
	    {
		key &= 0x1f;
	    }

	    if (InvertKeyMap((unsigned) key, &kevent, NULL) != TRUE)
		return;
	}

	if (code == 0 || !dn)
	    return;

	if (code > 0)
	{
	    kevent.ie_Code	= code & 0xff;
	    kevent.ie_Qualifier = qual | ((code > 0xff) ? IEQUALIFIER_LSHIFT : 0);
	}

	inputReqBlk -> io_Length  = sizeof(kevent);
	inputReqBlk -> io_Data    = (APTR) &kevent;
	inputReqBlk -> io_Command = IND_WRITEEVENT;
	inputReqBlk -> io_Flags   = 0;

	DoIO((struct IORequest *) inputReqBlk);
}

void	do_pointer(struct IOStdReq *inputReqBlk, int buttons, int x, int y, int fbw, int fbh, struct Screen *screen)
{
	if (x != ox || y != oy)
	{
	    ppevent.ie.ie_NextEvent = NULL;
	    ppevent.ie.ie_Code	    = IECODE_NOBUTTON;
	    ppevent.ie.ie_Qualifier = NULL;

	    inputReqBlk -> io_Data    = (APTR) &ppevent.ie;
	    inputReqBlk -> io_Command = IND_WRITEEVENT;
	    inputReqBlk -> io_Flags   = 0;

    	/*
		 * Send mouse move to a dedicated screen
		 * WARNING: for cases where screen has been closed
		 */

		ppevent.ie.ie_Class	   = IECLASS_NEWPOINTERPOS;
		ppevent.ie.ie_SubClass	   = IESUBCLASS_PIXEL;

		ppevent.ie.ie_EventAddress = (APTR) &ppevent.pp;

		ppevent.pp.iepp_Position.X = x;
		ppevent.pp.iepp_Position.Y = y;
		ppevent.pp.iepp_Screen	   = screen;

		inputReqBlk -> io_Length = sizeof(ppevent);

	    ox = x;
	    oy = y;

	    ppevent.ie.ie_Qualifier |= qual;

	    DoIO((struct IORequest *) inputReqBlk);
	}

	/*
	 * do mouse buttons
	 */

do_buttons:

	buttons &= (rfbButton1Mask | rfbButton2Mask | rfbButton3Mask);

	if (buttons != butn)
	{
	    /* check left button change */
	    if ((buttons ^ butn) & rfbButton1Mask)
	    {
		btevent.ie_NextEvent = NULL;
		btevent.ie_Class     = IECLASS_RAWMOUSE;
		btevent.ie_SubClass  = IESUBCLASS_COMPATIBLE;
		btevent.ie_Code      = IECODE_LBUTTON;
		btevent.ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE
				     // | IEQUALIFIER_LEFTBUTTON
				     ;
		btevent.ie_X	     = 0;
		btevent.ie_Y	     = 0;

		if (buttons & rfbButton1Mask)
		{
		    /* button down */
		    qual		 |= IEQUALIFIER_LEFTBUTTON;
		}
		else
		{
		    /* button up */
		    btevent.ie_Code	 |= IECODE_UP_PREFIX;
		    qual		 &= ~IEQUALIFIER_LEFTBUTTON;
		}

		btevent.ie_Qualifier |= qual;

		inputReqBlk -> io_Length    = sizeof(btevent);
		inputReqBlk -> io_Data      = (APTR) &btevent;
		inputReqBlk -> io_Command   = IND_WRITEEVENT;
		inputReqBlk -> io_Flags     = 0;

		DoIO((struct IORequest *) inputReqBlk);
	    }

	    /* check middle button change */
	    if ((buttons ^ butn) & rfbButton2Mask)
	    {
		btevent.ie_NextEvent = NULL;
		btevent.ie_Class     = IECLASS_RAWMOUSE;
		btevent.ie_SubClass  = IESUBCLASS_COMPATIBLE;
		btevent.ie_Code      = IECODE_MBUTTON;
		btevent.ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE
				     // | IEQUALIFIER_MIDBUTTON
				     ;
		btevent.ie_X	     = 0;
		btevent.ie_Y	     = 0;

		if (buttons & rfbButton2Mask)
		{
		    /* button down */
		    qual		 |= IEQUALIFIER_MIDBUTTON;
		}
		else
		{
		    /* button up */
		    btevent.ie_Code	 |= IECODE_UP_PREFIX;
		    qual		 &= ~IEQUALIFIER_MIDBUTTON;
		}

		btevent.ie_Qualifier |= qual;

		inputReqBlk -> io_Length    = sizeof(btevent);
		inputReqBlk -> io_Data      = (APTR) &btevent;
		inputReqBlk -> io_Command   = IND_WRITEEVENT;
		inputReqBlk -> io_Flags     = 0;

		DoIO((struct IORequest *) inputReqBlk);
	    }

	    /* check right button change */
	    if ((buttons ^ butn) & rfbButton3Mask)
	    {
		btevent.ie_NextEvent = NULL;
		btevent.ie_Class     = IECLASS_RAWMOUSE;
		btevent.ie_SubClass  = IESUBCLASS_COMPATIBLE;
		btevent.ie_Code      = IECODE_RBUTTON;
		btevent.ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE
				     // | IEQUALIFIER_RBUTTON
				     ;
		btevent.ie_X	     = 0;
		btevent.ie_Y	     = 0;

		if (buttons & rfbButton3Mask)
		{
		    /* button down */
		    qual		 |= IEQUALIFIER_RBUTTON;
		}
		else
		{
		    /* button up */
		    btevent.ie_Code	 |= IECODE_UP_PREFIX;
		    qual		 &= ~IEQUALIFIER_RBUTTON;
		}

		btevent.ie_Qualifier |= qual;

		inputReqBlk -> io_Length    = sizeof(btevent);
		inputReqBlk -> io_Data      = (APTR) &btevent;
		inputReqBlk -> io_Command   = IND_WRITEEVENT;
		inputReqBlk -> io_Flags     = 0;

		DoIO((struct IORequest *) inputReqBlk);
	    }

	    butn = buttons;
	}
}
