/* amiga_keys.c -- Event translation for AmigaDOS
   Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>

   This file is part of Jade.

   Jade is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   Jade is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with Jade; see the file COPYING.  If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "jade.h"
#include "jade_protos.h"

#define INTUI_V36_NAMES_ONLY
#include <clib/intuition_protos.h>
#include <clib/keymap_protos.h>
#include <string.h>

#define SHIFTQUALS (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
#define ALTQUALS   (IEQUALIFIER_LALT | IEQUALIFIER_RALT)
#define CMDQUALS   (IEQUALIFIER_LCOMMAND | IEQUALIFIER_RCOMMAND)

_PR void translate_event(u_long *, u_long *, struct IntuiMessage *);
_PR int  cook_key(struct IntuiMessage *, u_char *, int);
_PR bool lookup_event(u_long *, u_long *, u_char *);
_PR bool lookup_event_name(u_char *, u_long, u_long);

_PR u_long esc_code, esc_mods;
u_long esc_code = 0x45, esc_mods = EV_TYPE_KEYBD;

static u_long
translate_quals(u_long mods, u_short qual)
{
    if(qual & SHIFTQUALS)
	mods |= EV_MOD_SHIFT;
    if(qual & ALTQUALS)
	mods |= EV_MOD_META;
    if(qual & CMDQUALS)
	mods |= EV_MOD_MOD2; /* Amiga keys */
    if(qual & IEQUALIFIER_CONTROL)
	mods |= EV_MOD_CTRL;
    if(qual & IEQUALIFIER_CAPSLOCK)
	mods ^= EV_MOD_SHIFT;
    if(qual & IEQUALIFIER_LEFTBUTTON)
	mods |= EV_MOD_LMB;
    if(qual & IEQUALIFIER_MIDBUTTON)
	mods |= EV_MOD_MMB;
    if(qual & IEQUALIFIER_RBUTTON)
	mods |= EV_MOD_RMB;
    return(mods);
}

static u_long
translate_mods(u_long mods)
{
    u_long quals = 0;
    if(mods & EV_MOD_SHIFT)
	quals |= IEQUALIFIER_LSHIFT;
    if(mods & EV_MOD_CTRL)
	quals |= IEQUALIFIER_CONTROL;
    if(mods & EV_MOD_META)
	quals |= IEQUALIFIER_LALT;
    if(mods & EV_MOD_MOD2)
	quals |= IEQUALIFIER_LCOMMAND;
    if(mods & EV_MOD_LMB)
	quals |= IEQUALIFIER_LEFTBUTTON;
    if(mods & EV_MOD_MMB)
	quals |= IEQUALIFIER_MIDBUTTON;
    if(mods & EV_MOD_RMB)
	quals |= IEQUALIFIER_RBUTTON;
    return(mods);
}

/* Takes an IntuiMsg and converts it's contents to an EVENT. */
void
translate_event(u_long *code, u_long *mods, struct IntuiMessage *im)
{
    static u_long ClickMics, ClickSecs;
    *mods = translate_quals(*mods, im->Qualifier);
    switch(im->Class)
    {
    case IDCMP_RAWKEY:
	/* This magic means that we disreguard all upcodes and qualifiers.  */
	if((im->Code > 0x7f) || ((im->Code >= 0x60) && (im->Code <= 0x67)))
	    return;
	*code = (u_long)im->Code;
	*mods |= EV_TYPE_KEYBD;
	break;
    case IDCMP_MOUSEBUTTONS:
	*mods |= EV_TYPE_MOUSE;
	if(im->Code & IECODE_UP_PREFIX)
	{
	    *code = EV_CODE_MOUSE_UP;
	    switch(im->Code & ~IECODE_UP_PREFIX)
	    {
	    case IECODE_LBUTTON:
		*mods |= EV_MOD_LMB;
		break;
	    case IECODE_MBUTTON:
		*mods |= EV_MOD_MMB;
		break;
	    case IECODE_RBUTTON:
		*mods |= EV_MOD_RMB;
		break;
	    }
	}
	else
	{
	    if(DoubleClick(ClickSecs, ClickMics, im->Seconds, im->Micros))
		*code = EV_CODE_MOUSE_CLICK2;
	    else
		*code = EV_CODE_MOUSE_CLICK1;
	    ClickSecs = im->Seconds;
	    ClickMics = im->Micros;
	}
	break;
    case IDCMP_MOUSEMOVE:
	if(im->Qualifier & (IEQUALIFIER_MIDBUTTON | IEQUALIFIER_RBUTTON
			    | IEQUALIFIER_LEFTBUTTON))
	{
	    *mods |= EV_TYPE_MOUSE;
	    *code = EV_CODE_MOUSE_MOVE;
	}
	break;
    }
}

int
cook_key(struct IntuiMessage *im, u_char *buf, int buflen)
{
    struct InputEvent ie;
    ie.ie_Class = IECLASS_RAWKEY;
    ie.ie_SubClass = 0;
    ie.ie_Code = im->Code;
    ie.ie_Qualifier = im->Qualifier;
    ie.ie_EventAddress = *((APTR *)im->IAddress);
    return(MapRawKey(&ie, buf, buflen, NULL));
}

/*
 * Stuff to translate textual key descriptions into key codes
 */

typedef struct KeyDesc
{
    u_char	 *kd_Name;
    u_long	  kd_Mods;
    u_long	  kd_Code;
} KeyDesc;

static const KeyDesc KeyDescr[] =
{
    "Shift",    EV_MOD_SHIFT, 0,
    "SFT",      EV_MOD_SHIFT, 0,
    "Ctrl",     EV_MOD_CTRL, 0,
    "Control",  EV_MOD_CTRL, 0,
    "CTL",      EV_MOD_CTRL, 0,
    "Meta",     EV_MOD_META, 0,
    "Mod1",     EV_MOD_MOD1, 0,
    "Mod2",     EV_MOD_MOD2, 0,
    "Amiga",    EV_MOD_MOD2, 0,
    "Mod3",     EV_MOD_MOD3, 0,
    "Mod4",     EV_MOD_MOD4, 0,
    "LMB",      EV_MOD_LMB, 0,
    "Button1",  EV_MOD_BUTTON1, 0,
    "MMB",      EV_MOD_MMB, 0,
    "Button2",  EV_MOD_BUTTON2, 0,
    "RMB",      EV_MOD_RMB, 0,
    "Button3",  EV_MOD_BUTTON3, 0,
    "Button4",  EV_MOD_BUTTON4, 0,
    "Button5",  EV_MOD_BUTTON5, 0,

    "SPC",       EV_TYPE_KEYBD, 0x40,
    "Space",     EV_TYPE_KEYBD, 0x40,
    "Spacebar",  EV_TYPE_KEYBD, 0x40,
    "BS",        EV_TYPE_KEYBD, 0x41,
    "Backspace", EV_TYPE_KEYBD, 0x41,
    "TAB",       EV_TYPE_KEYBD, 0x42,
    "RET",       EV_TYPE_KEYBD, 0x44,
    "Return",    EV_TYPE_KEYBD, 0x44,
    "ESC",       EV_TYPE_KEYBD, 0x45,
    "Escape",    EV_TYPE_KEYBD, 0x45,
    "DEL",       EV_TYPE_KEYBD, 0x46,
    "Delete",    EV_TYPE_KEYBD, 0x46,
    "Help",      EV_TYPE_KEYBD, 0x5f,
    "Up",        EV_TYPE_KEYBD, 0x4c,
    "Down",      EV_TYPE_KEYBD, 0x4d,
    "Right",     EV_TYPE_KEYBD, 0x4e,
    "Left",      EV_TYPE_KEYBD, 0x4f,
    "F1",        EV_TYPE_KEYBD, 0x50,
    "F2",        EV_TYPE_KEYBD, 0x51,
    "F3",        EV_TYPE_KEYBD, 0x52,
    "F4",        EV_TYPE_KEYBD, 0x53,
    "F5",        EV_TYPE_KEYBD, 0x54,
    "F6",        EV_TYPE_KEYBD, 0x55,
    "F7",        EV_TYPE_KEYBD, 0x56,
    "F8",        EV_TYPE_KEYBD, 0x57,
    "F9",        EV_TYPE_KEYBD, 0x58,
    "F10",       EV_TYPE_KEYBD, 0x59,

    /* Numeric keypad */
    "KP_Enter",   EV_TYPE_KEYBD, 0x43,
    "KP_Multiply",EV_TYPE_KEYBD, 0x5d,
    "KP_Divide",  EV_TYPE_KEYBD, 0x5c,
    "KP_Minus",   EV_TYPE_KEYBD, 0x4a,
    "KP_Add",     EV_TYPE_KEYBD, 0x5e,
    "KP_Decimal", EV_TYPE_KEYBD, 0x39,
    "KP_0",       EV_TYPE_KEYBD, 0x0a,
    "KP_1",       EV_TYPE_KEYBD, 0x1d,
    "KP_2",       EV_TYPE_KEYBD, 0x1e,
    "KP_3",       EV_TYPE_KEYBD, 0x1f,
    "KP_4",       EV_TYPE_KEYBD, 0x2d,
    "KP_5",       EV_TYPE_KEYBD, 0x2e,
    "KP_6",       EV_TYPE_KEYBD, 0x2f,
    "KP_7",       EV_TYPE_KEYBD, 0x3d,
    "KP_8",       EV_TYPE_KEYBD, 0x3e,
    "KP_9",       EV_TYPE_KEYBD, 0x3f,

    /* Mouse events */
    "Click1",     EV_TYPE_MOUSE, EV_CODE_MOUSE_CLICK1,
    "Click2",     EV_TYPE_MOUSE, EV_CODE_MOUSE_CLICK2,
    "Off",        EV_TYPE_MOUSE, EV_CODE_MOUSE_UP,
    "Move",       EV_TYPE_MOUSE, EV_CODE_MOUSE_MOVE,

    NULL, 0, 0
};

/* Puts the integers defining the event described in DESC into CODE and
   MODS.  */
bool
lookup_event(u_long *code, u_long *mods, u_char *desc)
{
    bool rc = TRUE;
    u_char *str = desc;
    *code = *mods = 0;
    for(;;)
    {
	u_char buff[100];
	u_char *tmp = buff;
	u_char c = *str++;
	const KeyDesc *kd = KeyDescr;
	/* Get this one token.  The first character is read separately to
	   allow minus characters to be used to represent itself as well as
	   to terminate a token. */
	if(c != 0)
	{
	    *tmp++ = c;
	    while((c = *str) && (c != '-'))
	    {
		*tmp++ = c;
		str++;
	    }
	}
	else
	    goto error;
	*tmp = 0;
	if(*str)
	    str++;
	while(kd->kd_Name)
	{
	    if(!stricmp(kd->kd_Name, buff))
	    {
		*mods |= kd->kd_Mods;
		*code |= kd->kd_Code;
		if(*mods & EV_TYPE_MASK)
		    goto end;
		break;
	    }
	    kd++;
	}
	if(!kd->kd_Name)
	{
	    u_char coded[2];
	    if(MapANSI(buff, strlen(buff), coded, 1, NULL) == 1)
	    {
		*mods |= EV_TYPE_KEYBD;
		*mods = translate_quals(*mods, (UWORD)coded[1]);
		*code = (u_long)coded[0];
		goto end;
	    }
	    else
	    {
	    error:
		cmd_signal(sym_bad_event_desc, LIST_1(string_dup(desc)));
		rc = FALSE;
		goto end;
	    }
	}
    }
end:
    return(rc);
}

/* Constructs the name of the event defined by CODE and MODS in BUF.  */
bool
lookup_event_name(u_char *buf, u_long code, u_long mods)
{
    /* First resolve all modifiers */
    u_long tmp_mods = mods & EV_MOD_MASK;
    const KeyDesc *kd = KeyDescr;
    struct InputEvent ie;
    short actual;
    while(kd->kd_Name && (tmp_mods != 0))
    {
	if((tmp_mods & kd->kd_Mods) != 0)
	{
	    tmp_mods &= ~kd->kd_Mods;
	    buf = stpcpy(buf, kd->kd_Name);
	    *buf++ = '-';
	}
	kd++;
    }
    if(tmp_mods != 0)
	return(FALSE);
    /* Now try to find the code in our lookup table */
    tmp_mods = mods & EV_TYPE_MASK;
#if 0
    /* Since all modifiers are at the start of the table this is
       unnecessary :-) */
    kd = KeyDescr;
#endif
    while(kd->kd_Name)
    {
	if((kd->kd_Mods == tmp_mods) && (kd->kd_Code == code))
	{
	    strcpy(buf, kd->kd_Name);
	    return(TRUE);
	}
	kd++;
    }

    /* Couldn't find it here; have to go to the keymap.library */
    ie.ie_Class = IECLASS_RAWKEY;
    ie.ie_SubClass = 0;
    ie.ie_Code = code;
    ie.ie_Qualifier = 0;
    ie.ie_EventAddress = NULL;	/* is this safe? */
    actual = MapRawKey(&ie, buf, 10, NULL);
    if(actual == -1)
	return(FALSE);
    buf[actual] = 0;
    return(TRUE);
}
