/* vi: set sw=4 ts=4: */
/*
 * CCE - Console Chinese Environment -
 * Copyright (C) 1998-2003 Rui He (rhe@3eti.com)
 *
 * Arthur Que <arthur@turbolinux.com.cn>
 * FreeBSD keymap patch
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * 
 */

#include	<stdio.h>
#include	<stdlib.h>
#include	<unistd.h>
#include	<string.h>
#include	<signal.h>
#include 	<fcntl.h>

#include 	"config.h"

#include	<term.h>
#include 	<vgakeyboard.h>
#include	<errors.h>
#include	<vc.h>
#include	<key.h>
#include 	<convert.h>
#include 	<hzinput.h>

#if defined(linux) 

#define CTRL_ALT   ((1 << KG_CTRL)  | (1 << KG_ALT))
#define SHIFT_ALT  ((1 << KG_SHIFT) | (1 << KG_ALT))

struct _KeymapData
{  
    int table, index, oldValue, newValue;
}KMData[] =  /* 23 */
{
  { CTRL_ALT,  SCANCODE_0,  0, CTRL_ALT_0},
  { CTRL_ALT,  SCANCODE_1,  0, CTRL_ALT_1},
  { CTRL_ALT,  SCANCODE_2,  0, CTRL_ALT_2},
  { CTRL_ALT,  SCANCODE_3,  0, CTRL_ALT_3},
  { CTRL_ALT,  SCANCODE_4,  0, CTRL_ALT_4},
  { CTRL_ALT,  SCANCODE_5,  0, CTRL_ALT_5},
  { CTRL_ALT,  SCANCODE_6,  0, CTRL_ALT_6},
  { CTRL_ALT,  SCANCODE_7,  0, CTRL_ALT_7},
  { CTRL_ALT,  SCANCODE_8,  0, CTRL_ALT_8},
  { CTRL_ALT,  SCANCODE_9,  0, CTRL_ALT_9},

  { CTRL_ALT,  SCANCODE_A,  0, CTRL_ALT_A},
  { CTRL_ALT,  SCANCODE_E,  0, CTRL_ALT_E},
  { CTRL_ALT,  SCANCODE_X,  0, CTRL_ALT_X},
  { CTRL_ALT,  SCANCODE_P,  0, CTRL_ALT_P},
  { CTRL_ALT,  SCANCODE_N,  0, CTRL_ALT_N},
  { CTRL_ALT,  SCANCODE_R,  0, CTRL_ALT_R},
  { CTRL_ALT,  SCANCODE_L,  0, CTRL_ALT_L},  /* enable/disable LianXiang */
  { CTRL_ALT,  SCANCODE_K,  0, CTRL_ALT_K},  /* switch keyboard map(pinyin, different zhuyins) */

  { 1 << KG_CTRL,  SCANCODE_LEFTSHIFT,   0, CTRL_SHIFT}, 
  { 1 << KG_CTRL,  SCANCODE_SPACE,       0, CTRL_SPACE},
  { 1 << KG_CTRL,  SCANCODE_PERIOD,      0, CTRL_PERIOD},

  { SHIFT_ALT  /* 1 << KG_SHIFT */, SCANCODE_SPACE,       0, SHIFT_SPACE},
  
  { 0,  -1, 0, 0}  /* dummy */
};

#elif defined(__SunOS__)

#define CTRL_ALT  CTRLMASK  /* CAPSMASK SHIFTMASK UPMASK ALTGRAPHMASK 0 */

struct _KeymapData
{  
    int table, index, oldValue, newValue;
}KMData[] =  
{
  { CTRL_ALT,  SCANCODE_0,  0, CTRL_ALT_0},
  { CTRL_ALT,  SCANCODE_1,  0, CTRL_ALT_1},
  { CTRL_ALT,  SCANCODE_2,  0, CTRL_ALT_2},
  { CTRL_ALT,  SCANCODE_3,  0, CTRL_ALT_3},
  { CTRL_ALT,  SCANCODE_4,  0, CTRL_ALT_4},
  { CTRL_ALT,  SCANCODE_5,  0, CTRL_ALT_5},
  { CTRL_ALT,  SCANCODE_6,  0, CTRL_ALT_6},
  { CTRL_ALT,  SCANCODE_7,  0, CTRL_ALT_7},
  { CTRL_ALT,  SCANCODE_8,  0, CTRL_ALT_8},
  { CTRL_ALT,  SCANCODE_9,  0, CTRL_ALT_9},

  { CTRL_ALT,  SCANCODE_A,  0, CTRL_ALT_A},
  { CTRL_ALT,  SCANCODE_E,  0, CTRL_ALT_E},
  { CTRL_ALT,  SCANCODE_X,  0, CTRL_ALT_X},
  { CTRL_ALT,  SCANCODE_P,  0, CTRL_ALT_P},
  { CTRL_ALT,  SCANCODE_N,  0, CTRL_ALT_N},
  { CTRL_ALT,  SCANCODE_R,  0, CTRL_ALT_R},
  { CTRL_ALT,  SCANCODE_L,  0, CTRL_ALT_L}, 
  { CTRL_ALT,  SCANCODE_K,  0, CTRL_ALT_K},  

  { CTRLMASK,  SCANCODE_SPACE,  0, CTRL_SPACE},
  { CTRLMASK,  SCANCODE_PERIOD, 0, CTRL_PERIOD},

  { SHIFTMASK, SCANCODE_SPACE,  0, SHIFT_SPACE},
  { 0, -1, 0, 0 },
};

#elif defined(__FreeBSD__) || defined(__SCO__)

#if defined(__SCOOpenServer__)
#include <sys/keyboard.h>  /* for keymap_t */
#endif

keymap_t old_keymap;

#elif defined(__Lynx__)

remote_loadable_atran_t old_atran;

#endif

static void GetOldKeymap(void)
{
#if defined(linux)  

   struct kbentry spec;
   int i; /* ,cfd; */
	
   for (i = 0; /* i < NR_SPEC_KEY */ KMData[i].index >= 0; i++)
   {
       spec.kb_table = KMData[i].table;
       spec.kb_index = KMData[i].index ;
       ioctl(0 /* cfd */, KDGKBENT, &spec);
       KMData[i].oldValue =  spec.kb_value;
    }
   
#elif defined(__SunOS__)  

   struct kiockeymap km;
   int i,cfd;

   cfd = open("/dev/kbd", O_WRONLY);
   if (cfd < 0 && (cfd = open("/dev/kbd", O_RDONLY)) < 0)
   {
        PerrorExit("/dev/kbd");
   }

   for (i = 0; KMData[i].index >= 0; i++)
   {
       km.kio_tablemask = KMData[i].table;
       km.kio_station = KMData[i].index ; /* scan code */
       ioctl(cfd, KIOCGKEY, &km);
       KMData[i].oldValue =  km.kio_entry;
    }

#elif defined(__FreeBSD__) || defined(__SCO__)
 
   if (ioctl(0, GIO_KEYMAP, &old_keymap) < 0)
   {
		PerrorExit("getting keymap");
   }

#elif defined(__Lynx__)

	if (ioctl(0, VT_KBGET, &old_atran) < 0)
   	{
		PerrorExit("Lynx VT_KBGET");
	}	
#endif

}

void SetupKeymap(void)
{
#if defined(linux) /* || defined(__SCOUnixWare__) */

   int i; /*,cfd; */
   struct kbentry spec;

   for(i = 0; KMData[i].index >= 0 /* i < NR_SPEC_KEY */ ; i++)
   {
      spec.kb_table = KMData[i].table;
      spec.kb_index = KMData[i].index ;

      spec.kb_value = K(KT_LATIN, KMData[i].newValue);
      ioctl(0 /* cfd */, KDSKBENT, &spec);
  }

#elif defined(__SunOS__)

   int i,cfd;
   struct kiockeymap km;

   cfd = open("/dev/kbd", O_WRONLY);
   if (cfd < 0 && (cfd = open("/dev/kbd", O_RDONLY)) < 0) 
   {
        PerrorExit("/dev/kbd");
   }

   for(i = 0; KMData[i].index >= 0 ; i++)
   {
      km.kio_tablemask = KMData[i].table;
      km.kio_station = KMData[i].index ;
      km.kio_entry = KMData[i].newValue;
      ioctl(cfd, KIOCSKEY, &km);
  }

#elif defined(__FreeBSD__) 
  
#define CCE_SHIFT      1
#define CCE_CTRL       2
#define CCE_ALT	       4
#define CCE_ALT_SHIFT  5
#define CCE_CTRL_ALT   6

  keymap_t new_keymap;

  memcpy(&new_keymap, &old_keymap, sizeof(keymap_t));
  
  new_keymap.key[SCANCODE_SPACE].map[CCE_CTRL] = CTRL_SPACE; /* 230 */
  new_keymap.key[SCANCODE_SPACE].map[/* 1 */ CCE_ALT_SHIFT] = SHIFT_SPACE; /* 231 */

  new_keymap.key[SCANCODE_1].map[CCE_CTRL_ALT] = CTRL_ALT_1; /* 201 */
  new_keymap.key[SCANCODE_2].map[CCE_CTRL_ALT] = CTRL_ALT_2; /* 202 */
  new_keymap.key[SCANCODE_3].map[CCE_CTRL_ALT] = CTRL_ALT_3; /* 203 */
  new_keymap.key[SCANCODE_4].map[CCE_CTRL_ALT] = CTRL_ALT_4; /* 204 */
  new_keymap.key[SCANCODE_5].map[CCE_CTRL_ALT] = CTRL_ALT_5; /* 205 */
  new_keymap.key[SCANCODE_6].map[CCE_CTRL_ALT] = CTRL_ALT_6; /* 206 */
  new_keymap.key[SCANCODE_7].map[CCE_CTRL_ALT] = CTRL_ALT_7; /* 207 */
  new_keymap.key[SCANCODE_8].map[CCE_CTRL_ALT] = CTRL_ALT_8; /* 208 */
  new_keymap.key[SCANCODE_9].map[CCE_CTRL_ALT] = CTRL_ALT_9; /* 209 */
  new_keymap.key[SCANCODE_0].map[CCE_CTRL_ALT] = CTRL_ALT_0; /* 200 */

  new_keymap.key[SCANCODE_A].map[CCE_CTRL_ALT] = CTRL_ALT_A; /* 210 */
  new_keymap.key[SCANCODE_E].map[CCE_CTRL_ALT] = CTRL_ALT_E; /* 211 */
  new_keymap.key[SCANCODE_X].map[CCE_CTRL_ALT] = CTRL_ALT_X; /* 212 */
  new_keymap.key[SCANCODE_N].map[CCE_CTRL_ALT] = CTRL_ALT_N; /* 213 */
  new_keymap.key[SCANCODE_P].map[CCE_CTRL_ALT] = CTRL_ALT_P; /* 214 */
  new_keymap.key[SCANCODE_R].map[CCE_CTRL_ALT] = CTRL_ALT_R; /* 215 */
  new_keymap.key[SCANCODE_L].map[CCE_CTRL_ALT] = CTRL_ALT_L; /* 216 */
  new_keymap.key[SCANCODE_K].map[CCE_CTRL_ALT] = CTRL_ALT_K; /* 218 */
  new_keymap.key[SCANCODE_PERIOD].map[CCE_CTRL] = CTRL_PERIOD; /* 217 */

	if (ioctl(0, PIO_KEYMAP, &new_keymap) < 0) 
	{
    	PerrorExit("setting keymap\n");
	}

#elif defined(__SCO__)  /* Hack for OpenServer/Unixware, just set a few keys */
 
	keymap_t new_keymap;

	memcpy(&new_keymap, &old_keymap, sizeof(keymap_t));
  
	new_keymap.key[SCANCODE_SPACE].map[2] = SCO_CTRL_SPACE_2; 

	if (ioctl(0, PIO_KEYMAP, &new_keymap) < 0) 
  	{
		PerrorExit("setting keymap\n");
	}

#elif defined(__NetBSD__) || defined(__OpenBSD__)
 
#elif defined(__Lynx__)

	remote_loadable_atran_t new_atran;
	
	memcpy(&new_atran, &old_atran, sizeof(remote_loadable_atran_t));
	 
    new_atran[Control][SCANCODE_SPACE] = CTRL_SPACE; /* 230 */
    new_atran[ShiftAlt/*Shift*/][SCANCODE_SPACE] = SHIFT_SPACE; /* 231 */
    new_atran[Control][SCANCODE_PERIOD] = CTRL_PERIOD;

    new_atran[ShiftAlt][SCANCODE_1] = CTRL_ALT_1; /* 201 */
    new_atran[ShiftAlt][SCANCODE_2] = CTRL_ALT_2; /* 202 */
    new_atran[ShiftAlt][SCANCODE_3] = CTRL_ALT_3; /* 203 */
    new_atran[ShiftAlt][SCANCODE_4] = CTRL_ALT_4; /* 204 */
    new_atran[ShiftAlt][SCANCODE_5] = CTRL_ALT_5; /* 205 */
    new_atran[ShiftAlt][SCANCODE_6] = CTRL_ALT_6; /* 206 */
    new_atran[ShiftAlt][SCANCODE_7] = CTRL_ALT_7; /* 207 */
    new_atran[ShiftAlt][SCANCODE_8] = CTRL_ALT_8; /* 208 */
    new_atran[ShiftAlt][SCANCODE_9] = CTRL_ALT_9; /* 209 */
    new_atran[ShiftAlt][SCANCODE_0] = CTRL_ALT_0; /* 200 */

    new_atran[ShiftAlt][SCANCODE_A] = CTRL_ALT_A; /* 210 */
	new_atran[ShiftAlt][SCANCODE_E] = CTRL_ALT_E; /* 211 */
    new_atran[ShiftAlt][SCANCODE_X] = CTRL_ALT_X; /* 212 */
    new_atran[ShiftAlt][SCANCODE_N] = CTRL_ALT_N; /* 213 */
    new_atran[ShiftAlt][SCANCODE_P] = CTRL_ALT_P; /* 214 */
    new_atran[ShiftAlt][SCANCODE_R] = CTRL_ALT_R; /* 215 */
    new_atran[ShiftAlt][SCANCODE_L] = CTRL_ALT_L;
    new_atran[ShiftAlt][SCANCODE_K] = CTRL_ALT_K;

	if (ioctl(0, VT_KBPUT, &new_atran) < 0)
		PerrorExit("LynxOS calling VT_KBPUT");
#endif
   
}

void RestoreKeymap(void)
{
#if defined(linux) /* || defined(__SCOUnixWare__) */
  
  struct kbentry spec;
  int i; /* ,cfd; */

  for (i = 0; /* i < NR_SPEC_KEY */ KMData[i].index >= 0; i++)
  {
    spec.kb_table = KMData[i].table;
    spec.kb_index = KMData[i].index;
    spec.kb_value = KMData[i].oldValue;
    ioctl(0 /* cfd */, KDSKBENT, &spec);
  }

#elif defined(__SunOS__)  
  struct kiockeymap km;
  int i,cfd;

  cfd = open("/dev/kbd", O_WRONLY);
  if (cfd < 0 && (cfd = open("/dev/kbd", O_RDONLY)) < 0) 
  {
       PerrorExit("/dev/kbd");
  }

  for (i = 0; KMData[i].index >= 0; i++)
  {
    km.kio_tablemask = KMData[i].table;
    km.kio_station = KMData[i].index;
    km.kio_entry = KMData[i].oldValue;
    ioctl(cfd, KIOCSKEY, &km);
  }

#elif defined(__FreeBSD__) || defined(__SCO__)

  if (ioctl(0, PIO_KEYMAP, &old_keymap) < 0)
    PerrorExit("restoring keymap\n");

#elif defined(__Lynx__)

  if (ioctl(0, VT_KBPUT, &old_atran) < 0)
	  PerrorExit("Lynx restoring atran");
#endif  

}

void KeymapInit(void)
{
   GetOldKeymap();
   SetupKeymap();
}

void KeymapCleanup(void)
{
   RestoreKeymap();
}

void ProcessNormalModeKey(int tty_fd,unsigned char c)
{

#ifdef USE_CCE_PREFIX_KEY    
	static unsigned char old_key = 0; /* last input key  */

	if (old_key != CCE_PREFIX_KEY
#if defined(__SCO__)
		&& (c != SCO_CTRL_SPACE_2 /* && c != SCO_SHIFT_SPACE_2 */
		   )	
#endif
	)
	{
		old_key = c;
    	HZFilter(tty_fd,c);
		return;
	}
	old_key = c;
#endif

	switch(c)
    {

#if defined(__NetBSD__) || defined(__OpenBSD__)
	    case CTRL_ALT_0: SetCurrentInputMethod(0); break;
        case CTRL_ALT_1: SetCurrentInputMethod(1); break;
        case CTRL_ALT_2: SetCurrentInputMethod(2); break;
        case CTRL_ALT_3: SetCurrentInputMethod(3); break;
        case CTRL_ALT_4: SetCurrentInputMethod(4); break;
        case CTRL_ALT_5: SetCurrentInputMethod(5); break;
        case CTRL_ALT_6: SetCurrentInputMethod(6); break;
        case CTRL_ALT_7: SetCurrentInputMethod(7); break;
        case CTRL_ALT_8: SetCurrentInputMethod(8); break;
        case CTRL_ALT_9: SetCurrentInputMethod(9); break;
#else
#if defined(__SCO__)

		case SCO_CTRL_SPACE_2: /* hack for SCO  */
			ToggleInputMethod();;
			break;	
#endif
        case CTRL_ALT_0:
        case CTRL_ALT_1:
        case CTRL_ALT_2:
        case CTRL_ALT_3:
        case CTRL_ALT_4:
        case CTRL_ALT_5:
        case CTRL_ALT_6:
        case CTRL_ALT_7:
        case CTRL_ALT_8:
        case CTRL_ALT_9:
		    SetCurrentInputMethod( c - CTRL_ALT_0);
            break;
#endif

#if !defined(__Minix__)
		case CTRL_ALT_A:
        	TermOpen();
         	TextRepaintAll(curCon);
          	break;
       	case CTRL_ALT_N:
          	ChangeTerm(1);
          	break;
       	case CTRL_ALT_P:
          	ChangeTerm(0);
          	break;
#endif

       	case CTRL_ALT_X:
#ifdef HAVE_KILL
		  	if ( curCon->terminate == 0 )
            	kill( curCon->childPid, SIGHUP);
#endif
          	break;
		case CTRL_ALT_E:
			ChangeSystemEncoding(-1);  /* switch to next available one! */
			break;
      	case CTRL_ALT_R:
        	TextRepaintAll(curCon);
          	break;
		case CTRL_ALT_L:
			ToggleAssociateMode();
			break;
		case CTRL_ALT_K:
			ChangePinyinZhuyinKeyboardMap();
			break;
		case CTRL_PERIOD:
			ToggleChinesePunct();
			break;
       	case CTRL_SPACE:
			ToggleInputMethod();;
          	break;
		case SHIFT_SPACE:
			ToggleHalfFull();
          	break;
       	case CTRL_SHIFT:
          	SwitchInputMethod();
			break;

       	default:
          	HZFilter(tty_fd,c);
          	break;
    }
}

