RCS_ID_C "$Id: keymap.c,v 3.3 1994/05/14 12:43:33 ppessi Exp $";
/*
 * Keyboard handling routines
 *
 * $Log: keymap.c,v $
 * Revision 3.3  1994/05/14  12:43:33  ppessi
 * Cleaned up the keyboard conversion code,
 * it fully supports CTRL8BIT option now.
 *
 * Revision 3.2  1994/05/12  10:44:49  ppessi
 * Added application keypad handling (original code by R. Knop)
 *
 */

#include <string.h>
#include <stdio.h>

#include "nifty.h"
#include "amiga.h"
#include "display.h"
#include "wimp.h"
#include "nio.h"
#include "napsaprefs.h"

#include <devices/keymap.h>
#include <intuition/intuition.h>

struct IOStdReq consolereq = { 0 };
#define ConsoleDevice (consolereq.io_Device)

#ifdef USE_PRAGMAS
#include <proto/keymap.h>
#include <clib/console_protos.h>
#include <pragmas/console_pragmas.h>
#endif
#ifdef USE_INLINE
#include <inline/proto.h>
#include <inline/console.h>
#endif
#ifdef USE_CLIB
#include <clib/keymap_protos.h>
#include <clib/console_protos.h>
#endif

struct KeyMap *default_map = NULL; /* used in `international' mode */
struct KeyMap *used_map = NULL;	   /* used in multinational/national mode */

char keyMapDir[] = "Devs:keymaps/"; 
#define MAXKEYMAPLEN 32

static struct KeyMap *findmap(const char *name)
{
  struct KeyMapNode *keymap_n;
  BPTR keySegs;
  char fullKeyMapName[sizeof(keyMapDir) + MAXKEYMAPLEN];
  struct KeyMapResource *keyMapBase =
    (struct KeyMapResource *)OpenResource("keymap.resource");

  if (!keyMapBase)
    return NULL;

  /* Is it already loaded? */	
  keymap_n = (struct KeyMapNode *)
    FindName(&(keyMapBase->kr_List), (UBYTE *)name);
  if (!keymap_n) {
    /* Not found, we load it from devs:keymaps */
    strcpy(fullKeyMapName, keyMapDir);
    strncat(fullKeyMapName, name, MAXKEYMAPLEN);
    keySegs = LoadSeg(fullKeyMapName);
    if (!keySegs) 
      return NULL;
    keymap_n = (struct KeyMapNode *)((LONG *) (keySegs << 2) + 1);
    AddHead(&(keyMapBase->kr_List), (struct Node *)keymap_n);
  }
  return &(keymap_n->kn_KeyMap);
}

/*
 * Use named keymap. If map is non-NULL, use it.
 * Default map is treated as -1
 * Return pointer to new keymap
 *
 * If named keymap can not be found from memory, load it from disk
 */
void *setmap(const char *name, void *map)
{
  if (map) {
    if (map != (void *)-1) {
      used_map = map;
    } else {
      used_map = NULL;
    }
  } else if (name) {
    used_map = findmap(name);
  } else {
    used_map = default_map;
  }

  if (used_map) 
    return used_map;
  else
    return (void *)-1;
}

/*
 * Initialize keymaps
 */
void initkeys(void)
{
  if(OpenDevice("console.device", -1L, (struct IORequest *)&consolereq, 0L))
    fatalError("Failure to open console.device");

  default_map = setmap(np.keymap, NULL);
  if (default_map == (struct KeyMap *)-1)
    default_map = NULL;
}

/*
 * Free resources allocated by this module
 */
void deinitkeys(void)
{
  if (ConsoleDevice) 
    CloseDevice((struct IORequest *)&consolereq);
  ConsoleDevice = NULL;
}

/*
 * Handle an Intuition RAWKEY message.  
 *
 * The function keys, cursor keys, and keypad may have either the
 * native Amiga values, or the vt100/vt52 values.
 */
#define ISCURSOR(c) (((c)>75) && ((c)<80))
#ifdef OWNICONIFY 
#define ICONIFY(c, q) (((q) & AMIGALEFT) && ((q) & AMIGARIGHT) && ((c) == 23))
#endif

static long keyconvert(struct IntuiMessage *msg, char *kbuffer, long kbsize);

void dokeys(struct IntuiMessage *msg)
{
  UBYTE buf[512], *convertp = buf + sizeof buf / 2, *endp, *sendp;
  int count;

  static const char applkeypad[0x60] = 
    {
      0,   0,   0,   0,   0,   0,   0,   0,
      0,   0,   0,   0,   0,   0,   0, 'p', /* 0 */
      0,   0,   0,   0,   0,   0,   0,   0,
      0,   0,   0,   0,   0, 'q', 'r', 's', /* 1 2 3 */
      0,   0,   0,   0,   0,   0,   0,   0,
      0,   0,   0,   0,   0, 't', 'u', 'v', /* 4 5 6 */
      0,   0,   0,   0,   0,   0,   0,   0,
      0,   0,   0,   0, 'n', 'w', 'x', 'y', /* . 7 8 9 */
      0,   0,   0, 'M',   0,   0,   0,   0, /* Enter */
      0,   0, 'm',   0,   0,   0,   0,   0, /* - */
      0,   0,   0,   0,   0,   0,   0,   0,
      0,   0, 'P', 'Q', 'R', 'S', 'l',   0, /* ( ) / * + */
    };

  if (np.applkeypad && msg->Code < 0x60 &&
      applkeypad[(BYTE)(msg->Code)] != 0) {
    /* Application keypad handling */
    convertp[0] = CTRL8('O');
    convertp[1] = applkeypad[(BYTE)(msg->Code)];
    count = 2;
  } else if (ISCURSOR(msg->Code)) {
    /* Cursor keys */
    count = 2;
    convertp[1] = msg->Code - 76 + 'A';

    switch (np.cursormap) {
    case CURSOR_VT100:
      convertp[0] = CTRL8('[');	/* CSI */
      break;
    case CURSOR_ANSI:
      convertp[0] = CTRL8('O');
      break;
    case CURSOR_VT52:
      convertp[0] = 033;
      break;
    default:
      count = 0;
    }
  } else {
    count = keyconvert(msg, convertp, sizeof(buf) / 2);
  }


  /* If alt is treated as meta, we add ESC before a single char */
  if (np.altismeta && (msg->Qualifier & IEQUALIFIER_LALT) && count == 1) {
    buf[0] = ESC;
    sendp = buf + 1;
  } else {
    sendp = buf;
  }

  ReplyMsg((struct Message *)msg);

  /* Convert keycodes before sending them */
  if (count != 0) {
    for (endp = convertp + count; convertp < endp; ) {
      UBYTE c = *convertp++;

      if ((c & 0xe0) == 0x80 && !np.ctrl8bit) {
	/* 8-bit control codes are converted into ESC-sequences */
	*sendp++ = ESC;
	c += '@' - 0x80;
      } else if (c == '\015' && np.ansi_LNM) {
	/* CR is converted into CRLF pair */
	*sendp++ = c;
	c = '\012';
      } else if (np.national == NAT_7BIT) {
	/* Characters are converted into national codes */
	c = national_send[c];
      }

      *sendp++ = c;
    }

    nwrite(buf, sendp - buf);
  }
}

static long keyconvert(struct IntuiMessage *msg, char *kbuffer, long kbsize)
{
  struct InputEvent ievent;
    
  if(msg->Code & IECODE_UP_PREFIX)
    return 0;

  ievent.ie_Code = msg->Code;
    
  /* swap BS and DEL */
  if (ievent.ie_Code == 70 && np.delete)
    ievent.ie_Code = 65;
  else if (ievent.ie_Code == 65 && np.backspace)
    ievent.ie_Code = 70;

  ievent.ie_NextEvent = 0;
  ievent.ie_SubClass = 0;
  ievent.ie_Class = IECLASS_RAWKEY;
  ievent.ie_Qualifier = msg->Qualifier;
  ievent.ie_position.ie_addr = *((APTR *)msg->IAddress);
  /* This waz a mysterious bug */
  if (np.altismeta) {		/* left alt is meta char */
    ievent.ie_Qualifier &= ~IEQUALIFIER_LALT;
    ievent.ie_position.ie_dead.ie_prev1DownQual &= ~IEQUALIFIER_LALT;
    ievent.ie_position.ie_dead.ie_prev2DownQual &= ~IEQUALIFIER_LALT;
  }

  return (RawKeyConvert(&ievent, kbuffer, kbsize, 
			np.national ? used_map : default_map));
}

