/* Copyright (C) 1996,1997 Robert H”hne, see COPYING.RH for details */
/* This file is part of RHIDE. */
#ifdef __linux__

#define Uses_TKeys
#define Uses_TEvent
#include <tv.h>
#include <stdio.h>

#include <sys/time.h>

#include <unistd.h>
#include <ctype.h>
#ifdef __FreeBSD__
#include <ncurses.h>
#else
#include <curses.h>
#endif
#include <term.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/kd.h>
#include <sys/vt.h>
#include <signal.h>

int use_real_keyboard_bios = 0;
int convert_num_pad = 0;

#if 0
static int timeout_esc = -1;
#endif
extern int timer_value;

/*
 * This is the time limit in ms within Esc-key sequences are detected as
 * Alt-letter sequences.  Useful when we can't generate Alt-letter sequences
 * directly.
 */
#define DELAY_ESCAPE		400

/* key modifiers */

#define MALT		(kbLeftAlt | kbRightAlt)
#define MCTRL		(kbLeftCtrl | kbRightCtrl)
#define MSHIFT		(kbLeftShift | kbRightShift)

/* key types */

#define TALT		0x01		/* alt-letter key */

typedef struct
{
	int in;
	char type;
	char modifiers;
	short out;
}
keymap_t;

#define _kbCtrlA 0x1e01
#define _kbCtrlB 0x3002
#define _kbCtrlC 0x2e03
#define _kbCtrlD 0x2004
#define _kbCtrlE 0x1205
#define _kbCtrlF 0x2106
#define _kbCtrlG 0x2207
#define _kbCtrlH 0x2308
#define _kbCtrlI 0x1709
#define _kbCtrlJ 0x240a
#define _kbCtrlK 0x250b
#define _kbCtrlL 0x260c
#define _kbCtrlM 0x320d
#define _kbCtrlN 0x310e
#define _kbCtrlO 0x180f
#define _kbCtrlP 0x1910
#define _kbCtrlQ 0x1011
#define _kbCtrlR 0x1312
#define _kbCtrlS 0x1f13
#define _kbCtrlT 0x1414
#define _kbCtrlU 0x1615
#define _kbCtrlV 0x2f16
#define _kbCtrlW 0x1117
#define _kbCtrlX 0x2d18
#define _kbCtrlY 0x2c19
#define _kbCtrlZ 0x151a

#define _kbA _kbCtrlA+0x0060
#define _kbB _kbCtrlB+0x0060
#define _kbC _kbCtrlC+0x0060
#define _kbD _kbCtrlD+0x0060
#define _kbE _kbCtrlE+0x0060
#define _kbF _kbCtrlF+0x0060
#define _kbG _kbCtrlG+0x0060
#define _kbH _kbCtrlH+0x0060
#define _kbI _kbCtrlI+0x0060
#define _kbJ _kbCtrlJ+0x0060
#define _kbK _kbCtrlK+0x0060
#define _kbL _kbCtrlL+0x0060
#define _kbM _kbCtrlM+0x0060
#define _kbN _kbCtrlN+0x0060
#define _kbO _kbCtrlO+0x0060
#define _kbP _kbCtrlP+0x0060
#define _kbQ _kbCtrlQ+0x0060
#define _kbR _kbCtrlR+0x0060
#define _kbS _kbCtrlS+0x0060
#define _kbT _kbCtrlT+0x0060
#define _kbU _kbCtrlU+0x0060
#define _kbV _kbCtrlV+0x0060
#define _kbW _kbCtrlW+0x0060
#define _kbX _kbCtrlX+0x0060
#define _kbY _kbCtrlY+0x0060
#define _kbZ _kbCtrlZ+0x0060

#define _kb0 0x0b30
#define _kb1 0x0231
#define _kb2 0x0332
#define _kb3 0x0433
#define _kb4 0x0534
#define _kb5 0x0635
#define _kb6 0x0736
#define _kb7 0x0837
#define _kb8 0x0938
#define _kb9 0x0a39


static keymap_t keymap[] =
{
	/* ascii codes */

   {1, 0, MCTRL, _kbCtrlA}, {2, 0, MCTRL, _kbCtrlB}, {3, 0, MCTRL, _kbCtrlC},
	{4, 0, MCTRL, _kbCtrlD}, {5, 0, MCTRL, _kbCtrlE}, {6, 0, MCTRL, _kbCtrlF},
	{7, 0, MCTRL, _kbCtrlG}, {8, 0, MCTRL, _kbCtrlH}, {9, 0, MCTRL, _kbCtrlI},
	{10, 0, 0, kbCtrlJ}, {11, 0, MCTRL, _kbCtrlK}, {12, 0, MCTRL, _kbCtrlL},
	{13, 0, 0, kbCtrlM}, {14, 0, MCTRL, _kbCtrlN}, {15, 0, MCTRL, _kbCtrlO},
	{16, 0, MCTRL, _kbCtrlP}, {17, 0, MCTRL, _kbCtrlQ}, {18, 0, MCTRL, _kbCtrlR},
	{19, 0, MCTRL, _kbCtrlS}, {20, 0, MCTRL, _kbCtrlT}, {21, 0, MCTRL, _kbCtrlU},
	{22, 0, MCTRL, _kbCtrlV}, {23, 0, MCTRL, _kbCtrlW}, {24, 0, MCTRL, _kbCtrlX},
	{25, 0, MCTRL, _kbCtrlY}, {26, 0, MCTRL, _kbCtrlZ}, {9, 0, 0, kbTab},
	{10, 0, 0, kbEnter}, {27, 0, 0, kbEsc}, {31, 0, 0, kbCtrlBack},
	{127, 0, 0, kbBack},

   {10, 0, MCTRL, kbCtrlEnter},

   {'q', 0, MCTRL, _kbCtrlQ}, {'s', 0, MCTRL, _kbCtrlS}, {'j', 0, MCTRL, _kbCtrlJ},
   {'m', 0, MCTRL, _kbCtrlM},

   {'a', 0, 0, _kbA}, {'b', 0, 0, _kbB}, {'c', 0, 0, _kbC},
   {'d', 0, 0, _kbD}, {'e', 0, 0, _kbE}, {'f', 0, 0, _kbF},
   {'g', 0, 0, _kbG}, {'h', 0, 0, _kbH}, {'i', 0, 0, _kbI},
   {'j', 0, 0, _kbJ}, {'k', 0, 0, _kbK}, {'l', 0, 0, _kbL},
   {'m', 0, 0, _kbM}, {'n', 0, 0, _kbN}, {'o', 0, 0, _kbO},
   {'p', 0, 0, _kbP}, {'q', 0, 0, _kbQ}, {'r', 0, 0, _kbR},
   {'s', 0, 0, _kbS}, {'t', 0, 0, _kbT}, {'u', 0, 0, _kbU},
   {'v', 0, 0, _kbV}, {'w', 0, 0, _kbW}, {'x', 0, 0, _kbX},
   {'y', 0, 0, _kbY}, {'z', 0, 0, _kbZ},

   {'0', 0, 0, _kb0}, {'1', 0, 0, _kb1}, {'2', 0, 0, _kb2},
   {'3', 0, 0, _kb3}, {'4', 0, 0, _kb4}, {'5', 0, 0, _kb5},
   {'6', 0, 0, _kb6}, {'7', 0, 0, _kb7}, {'8', 0, 0, _kb8},
   {'9', 0, 0, _kb9},

	{9, 0, MSHIFT, kbShiftTab},

	/* alt-letter codes */

	{' ', TALT, 0, kbAltSpace},
	{'0', TALT, 0, kbAlt0}, {'1', TALT, 0, kbAlt1}, {'2', TALT, 0, kbAlt2},
	{'3', TALT, 0, kbAlt3}, {'4', TALT, 0, kbAlt4}, {'5', TALT, 0, kbAlt5},
	{'6', TALT, 0, kbAlt6}, {'7', TALT, 0, kbAlt7}, {'8', TALT, 0, kbAlt8},
	{'9', TALT, 0, kbAlt9},
	{'A', TALT, 0, kbAltA}, {'B', TALT, 0, kbAltB}, {'C', TALT, 0, kbAltC},
	{'D', TALT, 0, kbAltD}, {'E', TALT, 0, kbAltE}, {'F', TALT, 0, kbAltF},
	{'G', TALT, 0, kbAltG}, {'H', TALT, 0, kbAltH}, {'I', TALT, 0, kbAltI},
	{'J', TALT, 0, kbAltJ}, {'K', TALT, 0, kbAltK}, {'L', TALT, 0, kbAltL},
	{'M', TALT, 0, kbAltM}, {'N', TALT, 0, kbAltN}, {'O', TALT, 0, kbAltO},
	{'P', TALT, 0, kbAltP}, {'Q', TALT, 0, kbAltQ}, {'R', TALT, 0, kbAltR},
	{'S', TALT, 0, kbAltS}, {'T', TALT, 0, kbAltT}, {'U', TALT, 0, kbAltU},
	{'V', TALT, 0, kbAltV}, {'W', TALT, 0, kbAltW}, {'X', TALT, 0, kbAltX},
	{'Y', TALT, 0, kbAltY}, {'Z', TALT, 0, kbAltZ}, {127, TALT, 0, kbAltBack},

	/* escape codes */

	{KEY_DOWN, 0, 0, kbDown}, {KEY_UP, 0, 0, kbUp}, {KEY_LEFT, 0, 0, kbLeft},
	{KEY_RIGHT, 0, 0, kbRight}, {KEY_HOME, 0, 0, kbHome},
	{KEY_BACKSPACE, 0, 0, kbBack}, {KEY_F(1), 0, 0, kbF1},
	{KEY_F(2), 0, 0, kbF2}, {KEY_F(3), 0, 0, kbF3}, {KEY_F(4), 0, 0, kbF4},
	{KEY_F(5), 0, 0, kbF5}, {KEY_F(6), 0, 0, kbF6}, {KEY_F(7), 0, 0, kbF7},
	{KEY_F(8), 0, 0, kbF8}, {KEY_F(9), 0, 0, kbF9}, {KEY_F(10), 0, 0, kbF10},
	{KEY_DC, 0, 0, kbDel}, {KEY_IC, 0, 0, kbIns}, {KEY_NPAGE, 0, 0, kbPgDn},
	{KEY_PPAGE, 0, 0, kbPgUp}, {KEY_END, 0, 0, kbEnd},

	{KEY_F(1), 0, MALT, kbAltF1},
	{KEY_F(2), 0, MALT, kbAltF2}, {KEY_F(3), 0, MALT, kbAltF3}, {KEY_F(4), 0, MALT, kbAltF4},
	{KEY_F(5), 0, MALT, kbAltF5}, {KEY_F(6), 0, MALT, kbAltF6}, {KEY_F(7), 0, MALT, kbAltF7},
	{KEY_F(8), 0, MALT, kbAltF8}, {KEY_F(9), 0, MALT, kbAltF9}, {KEY_F(10), 0, MALT, kbAltF10},

	{KEY_LEFT, 0, MCTRL, kbCtrlLeft}, {KEY_RIGHT, 0, MCTRL, kbCtrlRight},
	{KEY_HOME, 0, MCTRL, kbCtrlHome}, {KEY_F(1), 0, MCTRL, kbCtrlF1},
	{KEY_F(2), 0, MCTRL, kbCtrlF2}, {KEY_F(3), 0, MCTRL, kbCtrlF3},
	{KEY_F(4), 0, MCTRL, kbCtrlF4}, {KEY_F(5), 0, MCTRL, kbCtrlF5},
	{KEY_F(6), 0, MCTRL, kbCtrlF6}, {KEY_F(7), 0, MCTRL, kbCtrlF7},
	{KEY_F(8), 0, MCTRL, kbCtrlF8}, {KEY_F(9), 0, MCTRL, kbCtrlF9},
	{KEY_F(10), 0, MCTRL, kbCtrlF10}, {KEY_DC, 0, MCTRL, kbCtrlDel}, 
	{KEY_IC, 0, MCTRL, kbCtrlIns}, {KEY_NPAGE, 0, MCTRL, kbCtrlPgDn},
	{KEY_PPAGE, 0, MCTRL, kbCtrlPgUp}, {KEY_END, 0, MCTRL, kbCtrlEnd},

	{KEY_F(11), 0, MSHIFT, kbShiftF1}, {KEY_F(12), 0, MSHIFT, kbShiftF2},
	{KEY_F(13), 0, MSHIFT, kbShiftF3}, {KEY_F(14), 0, MSHIFT, kbShiftF4},
	{KEY_F(15), 0, MSHIFT, kbShiftF5}, {KEY_F(16), 0, MSHIFT, kbShiftF6},
	{KEY_F(17), 0, MSHIFT, kbShiftF7}, {KEY_F(18), 0, MSHIFT, kbShiftF8},
	{KEY_F(19), 0, MSHIFT, kbShiftF9}, {KEY_F(20), 0, MSHIFT, kbShiftF10},
	{KEY_DC, 0, MSHIFT, kbShiftDel}, {KEY_IC, 0, MSHIFT, kbShiftIns}
};

/*
 * Builds a keycode from code and modifiers.
 */

static int kbMapKey(int code, int type, unsigned short & modifiers)
{
	keymap_t *best = NULL, *p;

	for (p = keymap; p < keymap + sizeof(keymap) / sizeof(keymap_t); p++)
	{
		/* code and type must match */

		if (p->in == code && p->type == type)
		{
			/*
			 * now get the best keycode we have, modifier keys
			 * may differ
			 */
			if (best == NULL || p->modifiers == modifiers)
			{
				best = p;
			}
		}
	}
	if (best != NULL)
	{
     /* That's now a little hack, to simulate the Ctrl key (for SET's editor) */
     if (code < 27 && code != 10 && code != 9)
       modifiers |= kbCtrlShift;
	  return best->out;	/* keycode found */
   }
	if (code <= 255) return code;	/* it is an ascii character */
	return kbNoKey;
}

/*
 * Gets information about modifier keys (Alt, Ctrl and Shift).  This can
 * be done only if the program runs on the system console.
 */

#define IN_FD fileno(stdin)

static int kbReadShiftState()
{
#ifdef __linux__
	int arg = 6;	/* TIOCLINUX function #6 */
	int shift = 0;

	if (ioctl(IN_FD, TIOCLINUX, &arg) != -1)
	{
		if (arg & (2 | 8)) shift |= kbLeftAlt | kbRightAlt;
		if (arg & 4) shift |= kbLeftCtrl | kbRightCtrl;
		if (arg & 1) shift |= kbLeftShift | kbRightShift;
	}
	return shift;
#else
	return 0;
#endif
}

ushort getshiftstate()
{
  return kbReadShiftState();
}

unsigned short gkey_shifts_flags;
unsigned char gkey_raw_value;
unsigned short gkey_raw_code;
static unsigned short gkey_key_code;

void clear_keybuf()
{
  tcflush(IN_FD,TCIFLUSH);
}

#if 0

static unsigned char key_buf[100];
static int key_buf_ptr = 0;

static int _getch_()
{
  if (key_buf_ptr)
    return key_buf[--key_buf_ptr];
  unsigned char c;
  return (read(IN_FD,&c,1) == 1 ? c : ERR);
}

static void _ungetch_(unsigned char c)
{
  key_buf[key_buf_ptr++] = c;
}

#else

#define _getch_ getch
#define _ungetch_ ungetch

#endif

int rh_kbhit()
{
  int c = _getch_();
  if (c != ERR) _ungetch_(c);
  return c != ERR;
}

/*
 * Reads a key from the keyboard.
 */

unsigned short gkey()
{
  int code, type = 0;

  /* see if there is data available */

  if ((code = _getch_()) != ERR)
  {
    if (code == 27)
    {
      if (rh_kbhit())
      {
        int code2 = _getch_();
        if (code2 != 27)
        {
          code = toupper(code2);
          type = TALT;
        }
      }
    } 
  }
  else return 0;
  gkey_shifts_flags = kbReadShiftState();
  gkey_raw_code = code;
  gkey_key_code = kbMapKey(code, type, gkey_shifts_flags);
  gkey_raw_value = gkey_key_code >> 8;
  if (gkey_key_code == kbNoKey) return 0;
  return gkey_key_code;
}

typedef struct
{
  uchar change_table;
  uchar change_index;
  uchar old_table;
  uchar old_index;
  ushort old_val;
  ushort new_val;
} change_entry;

#define SCAN_F1 0x3b
#define SCAN_F2 0x3c
#define SCAN_F3 0x3d
#define SCAN_F4 0x3e
#define SCAN_F5 0x3f
#define SCAN_F6 0x40
#define SCAN_F7 0x41
#define SCAN_F8 0x42
#define SCAN_F9 0x43
#define SCAN_F10 0x44
#define SCAN_Q 0x10
#define SCAN_S 0x1f
#define SCAN_J 0x24
#define SCAN_M 0x32

#define _K_ALTTAB 8
#define _K_NORMTAB 0
#define _K_CTRLTAB 4

change_entry changes[] = {
  { _K_ALTTAB, SCAN_F1, _K_NORMTAB, SCAN_F1, 0, 0 },
  { _K_ALTTAB, SCAN_F2, _K_NORMTAB, SCAN_F2, 0, 0  },
  { _K_ALTTAB, SCAN_F3, _K_NORMTAB, SCAN_F3, 0, 0 },
  { _K_ALTTAB, SCAN_F4, _K_NORMTAB, SCAN_F4, 0, 0 },
  { _K_ALTTAB, SCAN_F5, _K_NORMTAB, SCAN_F5, 0, 0 },
  { _K_ALTTAB, SCAN_F6, _K_NORMTAB, SCAN_F6, 0, 0 },
  { _K_ALTTAB, SCAN_F7, _K_NORMTAB, SCAN_F7, 0, 0 },
  { _K_ALTTAB, SCAN_F8, _K_NORMTAB, SCAN_F8, 0, 0 },
  { _K_ALTTAB, SCAN_F9, _K_NORMTAB, SCAN_F9, 0, 0 },
  { _K_ALTTAB, SCAN_F10, _K_NORMTAB, SCAN_F10, 0, 0 },
  { _K_CTRLTAB, SCAN_Q, _K_NORMTAB, SCAN_Q, 0, 0},
  { _K_CTRLTAB, SCAN_S, _K_NORMTAB, SCAN_S, 0, 0},
  { _K_CTRLTAB, SCAN_J, _K_NORMTAB, SCAN_J, 0, 0},
  { _K_CTRLTAB, SCAN_M, _K_NORMTAB, SCAN_M, 0, 0}
};

#define change_size (sizeof(changes)/sizeof(change_entry))

static struct termios saved_attributes;

static int keyboard_patch_set = 0;
static int keyboard_patch_available=0;
static struct kbentry entry;

static struct vt_mode oldvtmode;
static int our_vt;
static int console_sigs_set = 0;
static int atexit_done_console_sigs = 0;
int install_console_sigs = 1;

static void done_console_sigs()
{
  struct sigaction sig;

  if (!install_console_sigs)
    return;

  if (!console_sigs_set)
    return;
  sigemptyset(&sig.sa_mask);
  sigaddset(&sig.sa_mask,SIGUSR1);
  sigaddset(&sig.sa_mask,SIGUSR2);
  sig.sa_flags   = SA_RESTART;
  sigprocmask(SIG_BLOCK,&sig.sa_mask,NULL); // No switches now, we are not
                                            // initialized yet
  sig.sa_handler = SIG_DFL;
  sigaction(SIGUSR1,&sig,NULL);
  sig.sa_handler = SIG_DFL;
  sigaction(SIGUSR2,&sig,NULL);

  ioctl(STDIN_FILENO,VT_SETMODE,&oldvtmode);

  sigprocmask(SIG_UNBLOCK,&sig.sa_mask,NULL);

  console_sigs_set = 0;

}

static void _patch_keyboard();
static void _unpatch_keyboard();

static void releasevt_handler(int)
{
  _unpatch_keyboard();
  TMouse::suspend();
  ioctl(STDIN_FILENO,VT_RELDISP,1);
}

static void acquirevt_handler(int)
{
  ioctl(STDIN_FILENO,VT_RELDISP,VT_ACKACQ);
  ioctl(STDIN_FILENO,VT_WAITACTIVE,our_vt);
  _patch_keyboard();
  TMouse::resume();
}

static void init_console_sigs()
{
  if (!install_console_sigs)
    return;

  // -------- Get our console number
  if (sscanf(ttyname(STDOUT_FILENO),"/dev/tty%2d",&our_vt) != 1)
  {
    return;
  }

  // -------- Tell our console to inform us about switches
  struct vt_mode newvtmode;
  if (ioctl(STDIN_FILENO,VT_GETMODE,&newvtmode))
  {
    return;
  }

  // -------- set up signal handlers to know about console switches
  struct sigaction sig;
  sigemptyset(&sig.sa_mask);
  sigaddset(&sig.sa_mask,SIGUSR1);
  sigaddset(&sig.sa_mask,SIGUSR2);
  sig.sa_flags   = SA_RESTART;
  sigprocmask(SIG_BLOCK,&sig.sa_mask,NULL); // No switches now, we are not
                                            // initialized yet
  sig.sa_handler = releasevt_handler;
  sigaction(SIGUSR1,&sig,NULL);
  sig.sa_handler = acquirevt_handler;
  sigaction(SIGUSR2,&sig,NULL);

  oldvtmode = newvtmode;
  newvtmode.mode   = VT_PROCESS;
  newvtmode.relsig = SIGUSR1;
  newvtmode.acqsig = SIGUSR2;
  if (ioctl(STDIN_FILENO,VT_SETMODE,&newvtmode))
  {
    return;
  }

  if (atexit_done_console_sigs)
    atexit(done_console_sigs);
  atexit_done_console_sigs = 1;

  sigprocmask(SIG_UNBLOCK,&sig.sa_mask,NULL);

  console_sigs_set = 1;
}

static int atexit_unpatch_keyboard = 0;

static void patch_keyboard_init()
{
  unsigned i;
  if (use_real_keyboard_bios)
  {
    keyboard_patch_available = 0;
    return;
  }
  for (i=0;i<change_size;i++)
  {
    change_entry *e = &changes[i];
    entry.kb_table = e->change_table;
    entry.kb_index = e->change_index;
    if (ioctl(STDIN_FILENO,KDGKBENT,&entry) != 0)
    {
      keyboard_patch_available = 0;
      return;
    }
    e->old_val = entry.kb_value;
    entry.kb_table = e->old_table;
    entry.kb_index = e->old_index;
    ioctl(STDIN_FILENO,KDGKBENT,&entry);
    e->new_val = entry.kb_value;
  }
  keyboard_patch_available = 1;
  if (!atexit_unpatch_keyboard)
    atexit(_unpatch_keyboard);
  atexit_unpatch_keyboard = 1;
}

static void _patch_keyboard()
{
  unsigned i;
  if (keyboard_patch_set)
    return;
  patch_keyboard_init();
  if (!keyboard_patch_available)
    return;
  for (i=0;i<change_size;i++)
  {
    change_entry *e = &changes[i];
    entry.kb_table = e->change_table;
    entry.kb_index = e->change_index;
    entry.kb_value = e->new_val;
    if (ioctl(STDIN_FILENO,KDSKBENT,&entry) != 0)
    {
      keyboard_patch_available = 0;
      return;
    }
  }
  keyboard_patch_set = 1;
}

static void patch_keyboard()
{
  _patch_keyboard();
  if (keyboard_patch_set)
    init_console_sigs();
}

static void _unpatch_keyboard()
{
  unsigned i;
  if (!keyboard_patch_available)
    return;
  if (!keyboard_patch_set)
    return;
  for (i=0;i<change_size;i++)
  {
    change_entry *e = &changes[i];
    entry.kb_table = e->change_table;
    entry.kb_index = e->change_index;
    entry.kb_value = e->old_val;
    ioctl(STDIN_FILENO,KDSKBENT,&entry);
  }
  keyboard_patch_set = 0;
}

static void unpatch_keyboard()
{
  _unpatch_keyboard();
  if (keyboard_patch_available)
    done_console_sigs();
}

void resume_keyboard()
{
  tcgetattr (STDIN_FILENO, &saved_attributes);
  patch_keyboard();
}

void suspend_keyboard()
{
  tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
  unpatch_keyboard();
}

#endif // __linux__

