/*
 * Translate a raw key(scan code) to characters
 * 
 * Copyright (C) 1997 Hung-Chi Chu <hcchu@r350.ee.ntu.edu.tw>
 * 
 * This file is part of the Big5Con(Big5 Chinese Console) package.
 * 
 * Big5Con 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 of the License, or (at your option) any later
 * version.
 * 
 * Big5Con 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
 * this program; if not, write to the Free Software Foundation, Inc., 675
 * Mass Ave, Cambridge, MA 02139, USA.
 */

#include "big5con.h"

static unsigned kbd_state = 0;
static          const u_char esc_code = 27;
static int      auto_cursor = 1;
int             altmeta = 0;

struct fkeydef {
    u_char          str[6];
    u_char          len;
};

static struct fkeydef fkey_tab[64] = {
     /* 01-04 */ {"\033OP", 3}, {"\033OQ", 3}, {"\033OR", 3}, {"\033OS", 3},
     /* 05-08 */ {"\033[15~", 5}, {"\033[17~", 5}, {"\033[18~", 5}, {"\033[19~", 5},
     /* 09-12 */ {"\033[20~", 5}, {"\033[21~", 5}, {"\033[23~", 5}, {"\033[24~", 5},
     /* 13-16 */ {"", 0}, {"", 0}, {"", 0}, {"", 0},
     /* 17-20 */ {"", 0}, {"", 0}, {"", 0}, {"", 0},
     /* 21-24 */ {"", 0}, {"", 0}, {"", 0}, {"", 0},
     /* 25-28 */ {"", 0}, {"", 0}, {"", 0}, {"", 0},
     /* 29-32 */ {"", 0}, {"", 0}, {"", 0}, {"", 0},
     /* 33-36 */ {"", 0}, {"", 0}, {"", 0}, {"", 0},
     /* 37-40 */ {"", 0}, {"", 0}, {"", 0}, {"", 0},
     /* 41-44 */ {"", 0}, {"", 0}, {"", 0}, {"", 0},
     /* 45-48 */ {"", 0}, {"", 0}, {"", 0}, {"", 0},
     /* 49-52 */ {"\033[1~", 4}, {"\033OA", 3}, {"\033[5~", 4}, {"-", 1},
     /* 53-56 */ {"\033OD", 3}, {"", 0}, {"\033OC", 3}, {"+", 1},
     /* 57-60 */ {"\033[4~", 4}, {"\033OB", 3}, {"\033[6~", 4}, {"\033[2~", 4},
     /* 61-64 */ {"\033[3~", 4}, {"", 0}, {"", 0}, {"", 0},
};

int             kbd_leds;
struct keymap   vckmap;

void
trans_key(scancode)
    unsigned        scancode;
{
    static unsigned state = 0;
    unsigned        keycode = (scancode & 0x7f);
    int             press_down = ((scancode & 0x80) ? 0 : 1);
#if __FreeBSD__ < 3
    struct key_t   *key;
#else
    struct keyent_t *key;
#endif
    unsigned        num_shift = 0;
    int             spcl_key = 0, res, cfd;
    unsigned char   keychar;

    scancode &= 0xff;
    switch (state) {
    case 0x1D:
	state = 0;
	return;
    case 0xE1:
	state = (scancode == 0x1D) ? 0x1D : 0;
	return;
    case 0:
	switch (scancode) {
	case 0xE0:
	case 0xE1:
	    state = scancode;
	    return;
	case KEY_F1:
	case KEY_F2:
	case KEY_F3:
	case KEY_F4:
	case KEY_F5:
	case KEY_F6:
	case KEY_F7:
	case KEY_F8:
	case KEY_F9:
	case KEY_F10:
	    if (kbd_state & KEY_ALT_MASK) {
		if ((cfd = open("/dev/vga", 0)) >= 0) {
		    ioctl(cfd, VT_ACTIVATE, scancode - KEY_F1 + (kbd_state & 0x3) * 10 + 1);
		    /*
		     * woju: for Alt-F? fast switching kbd_state = 0;
		     */
		    close(cfd);
		}
		return;
	    }
	case KEY_F11:
	case KEY_F12:
	    if (kbd_state & KEY_ALT_MASK) {
		if ((cfd = open("/dev/vga", 0)) >= 0) {
		    ioctl(cfd, VT_ACTIVATE, scancode - KEY_F11 + 11);
		    /*
		     * woju: for Alt-F? fast switching kbd_state = 0;
		     */
		    close(cfd);
		}
		return;
	    }
	}
	break;
    case 0xE0:
	state = 0;
	switch (keycode) {
	case KEY_CTRL_L:
	    keycode = KEY_CTRL_R;
	    break;
	case KEY_ALT_L:
	    keycode = KEY_ALT_R;
	    break;
	case KEY_ENTER:
	    keycode = KEY_KP_ENTER;
	    break;
	case KEY_DIV:
	    keycode = KEY_KP_DIV;
	    break;
	case KEY_KP_DOT:
	    keycode = KEY_DEL;
	    break;
	case KEY_KP_0:
	    keycode = KEY_INS;
	    break;
	case KEY_KP_1:
	    keycode = KEY_END;
	    break;
	case KEY_KP_2:
	    keycode = KEY_DOWN;
	    break;
	case KEY_KP_3:
	    keycode = KEY_PGDN;
	    break;
	case KEY_KP_4:
	    keycode = KEY_LEFT;
	    break;
	case KEY_KP_6:
	    keycode = KEY_RIGHT;
	    break;
	case KEY_KP_7:
	    keycode = KEY_HOME;
	    break;
	case KEY_KP_8:
	    keycode = KEY_UP;
	    break;
	case KEY_KP_9:
	    keycode = KEY_PGUP;
	    break;
	default:
	    return;
	}
	break;
    }
    if (kbd_leds & LED_NUM) {
	switch (keycode) {
	case KEY_KP_DOT:
	case KEY_KP_0:
	case KEY_KP_1:
	case KEY_KP_2:
	case KEY_KP_3:
	case KEY_KP_4:
	case KEY_KP_5:
	case KEY_KP_6:
	case KEY_KP_7:
	case KEY_KP_8:
	case KEY_KP_9:
	    num_shift = KEY_SHIFT_MASK;
	}
    }
    key = vckmap.key + keycode;
    keychar = key->map[(kbd_state & KEY_SHIFT_MASK) ^ num_shift];
    if (!press_down) {		/* release key */
	if (key->spcl & (0x80 >> ((kbd_state & KEY_SHIFT_MASK) ^ num_shift))) {
	    switch (keychar) {
	    case LSH:
	    case RSH:
		kbd_state &= ~KEY_SHIFT_MASK;
		break;
	    case LCTR:
	    case RCTR:
		kbd_state &= ~KEY_CTRL_MASK;
		break;
	    case LALT:
	    case RALT:
		kbd_state &= ~KEY_ALT_MASK;
		break;
	    }
	}
    } else {			/* press down */
	if (key->spcl & (0x80 >> ((kbd_state & KEY_SHIFT_MASK) ^ num_shift))) {
	    switch (keychar) {
	    case LSH:
	    case RSH:
		kbd_state |= KEY_SHIFT_MASK;
		break;
	    case LCTR:
	    case RCTR:
		kbd_state |= KEY_CTRL_MASK;
		break;
	    case LALT:
	    case RALT:
		kbd_state |= KEY_ALT_MASK;
		break;
	    case CLK:
		kbd_leds ^= LED_CAP;
		ioctl(0, KDSETLED, kbd_leds);
		break;
	    case NLK:
		kbd_leds ^= LED_NUM;
		ioctl(0, KDSETLED, kbd_leds);
		break;
	    case SLK:
		kbd_leds ^= LED_SCR;
		ioctl(0, KDSETLED, kbd_leds);
		break;
	    }
	    res = trans_key2(keycode << 8, kbd_state | (kbd_leds << 4));
	} else {
	    switch (keycode) {
	    case KEY_KP_0:
	    case KEY_KP_1:
	    case KEY_KP_2:
	    case KEY_KP_3:
	    case KEY_KP_4:
	    case KEY_KP_5:
	    case KEY_KP_6:
	    case KEY_KP_7:
	    case KEY_KP_8:
	    case KEY_KP_9:
	    case KEY_KP_DOT:
	    case KEY_KP_ENTER:
	    case KEY_KP_PLUS:
	    case KEY_KP_MINUS:
	    case KEY_KP_MULT:
	    case KEY_KP_DIV:
		res = trans_key2((keycode << 8) | keychar, kbd_state | (kbd_leds << 4));
		break;
	    default:
		if (kbd_state == (KEY_ALT_MASK | KEY_CTRL_MASK) && keychar == 't') {
		    auto_cursor ^= 1;
		    return;
		}
		res = trans_key2(keychar, kbd_state | (kbd_leds << 4));
	    }
	}
	if (!res) {
	    keychar = key->map[kbd_state ^ num_shift];
	    if (key->spcl & (0x80 >> (kbd_state ^ num_shift))) {
		if (keychar >= F_FN && keychar < (F_FN + 64)) {	/* function key */
		    struct fkeydef *fkey;
		    fkey = fkey_tab + keychar - F_FN;
		    if (fkey->len)
			write(masterPty, fkey->str, fkey->len);
		    switch (keychar) {
		    case F_FN + 52:
			if (auto_cursor && isPrevW2())
			    write(masterPty, fkey->str, fkey->len);
			break;
		    case F_FN + 54:
			if (auto_cursor && isNextW2())
			    write(masterPty, fkey->str, fkey->len);
			break;
		    }
		}
	    } else {
		if (isalpha(keychar) && (kbd_leds & LED_CAP)) {
		    if (kbd_state & KEY_SHIFT_MASK)
			keychar = tolower(keychar);
		    else
			keychar = toupper(keychar);
		}
		if (altmeta && kbd_state & KEY_ALT_MASK)
		    write(masterPty, &esc_code, 1);
		write(masterPty, &keychar, 1);
		if (keychar == 8 && auto_cursor && isPrevW2())
		    write(masterPty, &keychar, 1);
	    }
	}
    }
}

int 
is_kp_num(key)
    unsigned        key;
{
    switch (key >> 8) {
    case KEY_KP_0:
    case KEY_KP_1:
    case KEY_KP_2:
    case KEY_KP_3:
    case KEY_KP_4:
    case KEY_KP_5:
    case KEY_KP_6:
    case KEY_KP_7:
    case KEY_KP_8:
    case KEY_KP_9:
	if (key & 0xff)
	    return (1);
    default:
	return (0);
    }
}
