/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:kbd_emul.c 12.0$ */
/* $ACIS:kbd_emul.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/cacons/RCS/kbd_emul.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:kbd_emul.c 12.0$";
#endif

/*
 * IBM RT PC Workstation keyboard (8051, 8255) driver.
 * also includes interrupt dispatch routine to mouse as well.
 */
#include "../h/param.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/ioctl.h"
#include "../h/tty.h"
#include "../h/systm.h"
#include "../h/uio.h"
#include "../h/file.h"
#include "../h/kernel.h"
#include "../machine/debug.h"
#ifdef SECURE
#include "../machine/nvram.h"
#endif SECURE

#include "../machinecons/screen_conf.h"
#include "../machinecons/consdefs.h"
#include "../machinecons/consvars.h"
#include "../machinecons/kbd_emul.h"
#ifdef IBMRTPC
#include "../machinecons/kls.h"
#include "ms.h"
#endif IBMRTPC
#ifdef ATR
#include "../ca_atr/kls.h"
#include "../ca_atr/pcif.h"
#endif ATR
#include "../machinecons/keyboard.h"
#include "../machine/io.h"

char *kbd_set_ptr;
int kbd_set_cnt, kbd_set_out;
extern int click;
extern int stdclick;
extern char kbd_leds_state;

extern struct kbdq	kbd_init_buf;
extern int set_kbd_leds();

int click_freqh = CLICK_FREQ_HIGH;
int click_freql = CLICK_FREQ_LOW;
int click_vol = CLICKVOL;
int click_time = CLICK_TIME;
int kbd_last_swap;		/* the previous key hit in swap mode */
int cons_display;
int old_click;

/*
 * Normal	a	1	NORMAL
 * Shift	A	!	SHIFT
 * Caps-Lock	A	1	CAPS
 * Caps + Shift a	!	(NORMAL != CAPS) ? NORMAL : SHIFT
 */
#define CAPS_PLUS_SHIFT(code)	(*kbde_codes[code][KBD_INDEX_NORMAL] != \
		 		 *kbde_codes[code][KBD_INDEX_CAPS]) ? \
		  		 KBD_INDEX_NORMAL : KBD_INDEX_SHIFT

#define BIT(a) (1<<(a-1))


kbde_open(dev, tp, si)
dev_t dev;
register struct tty *tp;
register SCREEN_INFO *si;
{
	register EMUL_INFO *ei = &si->ine;
	register int i;

	if (ei->flag & KBDE_OPEN)
		return (0);
	for (i=0; i < NUMBER_CONS; i++) {
		si = &cons_info[i];
		if ((si->ine.emulator == E_KBDINPUT ) && (si->ine.flag & KBDE_OPEN)) {
			ei->flag |= KBDE_OPEN;
			return(0);
		}
	}
	ei->flag = KBDE_OPEN;

	(void) kbdcreset();		/* reset to standard */

	/* non-meta keys (repeat make/break) & meta keys (make/break) */
	set_default_key_types(KALLRPT, HAVE_REPEAT_MKBRK);
	make_break = kbd_state = 0;
	return (0);
}

kbde_close(tp, si)
register struct tty *tp;
register SCREEN_INFO *si;
{
	register EMUL_INFO *ei = &si->ine;

	ei->flag = 0;
	ei->unit = 0;
	return (0);
}

kbde_read(tp, uio, si)
register struct tty *tp;
struct uio *uio;
register SCREEN_INFO *si;
{
        return ((*linesw[tp->t_line].l_read)(tp, uio));
}

kbde_rint(data, tp, si)
register int data;
struct tty *tp;
register SCREEN_INFO *si;
{
	register int c = data;
	int intr_reason = si->ine.intr_reason;

	si->ine.intr_reason = 0;

	if (intr_reason == E_INT_KBD) {

		c = kbde_key_scan(si, data);

		if ((kbd_state ^ tp->t_state) & TS_TTSTOP) {
			if (tp->t_state & TS_TTSTOP)
				kbd_state |= TS_TTSTOP;
			else
				kbd_state &= ~TS_TTSTOP;
			set_kbd_led((kbd_state & TS_TTSTOP) ? SCROLL_LED : ~SCROLL_LED);
		}
		return(c == NONE);

	} else if (intr_reason == E_INT_MOUSE) {
		if ((tp->t_state & TS_ISOPEN) == 0)
			return (-1);
		ttwakeup(tp);

	}

	return (0);
}

kbde_key_scan(si, scancode)
register SCREEN_INFO *si;
register int scancode;
{
	register int c = NONE;
	register int code;
	register int old_state = kbd_state;
	register SCREEN_INFO *sio = &cons_info[cons_display];


	code = ((kbd_state & NUM_LOCK_MODE) && ((kbd_state & SET_MODE) == 0)) ?
		std_code_map[scancode] : code_map[scancode];
	if (make_break) {

		make_break = 0;
		switch (code) {
		case FN_CAPS_LOCK:		/* caps-lock key let go */
			break;
		case FN_NUM_LOCK:		/* num-lock key let go */
			break;
		case FN_ALT1:
			kbd_state &= ~ALT1_MODE; /* alt key released */
			break;
		case FN_ALT2:
			kbd_state &= ~ALT2_MODE; /* alt key released */
			break;
		case FN_ACTION:
			kbd_state &= ~ACTION_MODE; /* action key released */
			break;
		case FN_SHIFT1:
			kbd_state &= ~SHIFT1_MODE; /* shift1 key released */
			break;
		case FN_SHIFT2:
			kbd_state &= ~SHIFT2_MODE; /* shift2 key released */
			break;
		case FN_CONTROL:
			kbd_state &= ~CTL_MODE; /* control key released */
			break;
		}
	}
	else if (code == FN_BREAK) {		/* handle break code early */
		make_break = 1;
	}
	else if (kbd_state&SWAP_MODE) {
		if (kbd_last_swap==0) {
			kbd_last_swap = scancode;
		}
		else {
			register int temp = code_map[kbd_last_swap];

			code_map[kbd_last_swap] = code_map[scancode];
			code_map[scancode] = temp;
#ifndef ATR
			kbd_swap_attribute(kbd_last_swap, scancode);
#endif
			kbd_state &= ~SWAP_MODE;
			kbd_last_swap = 0;
		}
		old_state = -1;
	}
	else if (code & META)
		kbd_meta(si,code);
	else {			/* normal character */
		register int n;
		register char *p;

#ifdef IBMRTPC
		if (kbd_state & ACTION_MODE)
			n = KBD_INDEX_ACTION;
#endif IBMRTPC
#ifdef ATR
#ifdef ENH_AT_KEYBOARD
		if (kbd_state & ACTION_MODE)
			n = KBD_INDEX_ACTION;
#endif ENH_AT_KEYBOARD
#ifdef AT_KEYBOARD
		if (kbd_state & NUM_LOCK_MODE)
			n = KBD_INDEX_NUMLOCK;
#endif AT_KEYBOARD
#endif ATR
		else if (kbd_state & (ALT1_MODE | ALT2_MODE))
			n = KBD_INDEX_ALT;
		else if (kbd_state & CTL_MODE)
			n = KBD_INDEX_CONTROL;
		else if ((kbd_state & CAPS_MODE) &&
			( kbd_state & (SHIFT1_MODE | SHIFT2_MODE)))
			n = CAPS_PLUS_SHIFT(code);
		else if (kbd_state & CAPS_MODE)
			n = KBD_INDEX_CAPS;
		else if (kbd_state & (SHIFT1_MODE | SHIFT2_MODE))
			n = KBD_INDEX_SHIFT;
		else
			n = KBD_INDEX_NORMAL;
		if ((kbd_state & NUM_LOCK_MODE) &&
		   ((kbd_state & SET_MODE) == 0)) 
			p = std_codes[code][n];
		else
			p = kbde_codes[code][n];
		c = *p;		/* get first character */
		if ((kbd_state & SET_MODE) && kbd_set(si, p))
			old_state = -1;		/* force redisplay */
		else if (c & META) {
			kbd_meta(si,p[1]);
			c = NONE;	/* for gets */
		} else if (c & META_STRING) {
			c = (p[1] << 8) + p[2];
			kbd_send(si,&kbd_strings[c],KBD_STRING_LENGTH,1);
			c = NONE;	/* for gets */
		} else {
			kbd_send(si,p+1,MAX_CHAR_LENGTH,1); /* send the string */
			c = *(p+1);
        }
	}
#ifndef ATR
	if (c != NONE && (click & 2))
		speaker(click_vol,click_freqh, click_freql, click_time);
#endif ATR
	if (kbd_state != old_state)
		kbd_status(sio, kbd_state);  /* reflect kbd_state change */
	DEBUGF(cndebug & 0x80, printf("kbde_key_scan: return %x\n", c));
	return(c);			/* for gets */
}

/*
 * process a meta key
 * code2 is used for string defined keys
 */
kbd_meta(si,code)
register SCREEN_INFO *si;
register int code;
{
	register SCREEN_INFO *sio = &cons_info[cons_display];

	switch (code) {
	case FN_CAPS_LOCK:       /* caps lock key pressed */
		kbd_state ^=CAPS_MODE; /* invert caps kbd_state each time */
		set_kbd_led(kbd_state & CAPS_MODE ? CAPS_LED : ~CAPS_LED);
		break;
	case FN_NUM_LOCK:
		kbd_state ^= NUM_LOCK_MODE; 	/* key held down */
		if (kbd_state & NUM_LOCK_MODE) {
			old_click = click;
			set_click(stdclick);
		} else
			set_click(old_click);
		timeout(set_kbd_led,	
			(caddr_t) (kbd_state&NUM_LOCK_MODE ?
			 NUM_LED : ~NUM_LED), 1);
		break;
	case FN_ACTION:
		kbd_state |= ACTION_MODE; /* key held down */
		break;
	case FN_ALT1:
		kbd_state |= ALT1_MODE; /* key held down */
		break;
	case FN_ALT2:
		kbd_state |= ALT2_MODE; /* key held down */
		break;
	case FN_SHIFT1:
		kbd_state |= SHIFT1_MODE; /* key held down */
		break;
	case FN_SHIFT2:
		kbd_state |= SHIFT2_MODE; /* key held down */
		break;
	case FN_CONTROL:
		kbd_state |= CTL_MODE; /* key held down */
		break;
	case FN_DEBUG:
		debug_key = !debug_key;
		kbd_status(sio, kbd_state);
		break;
	case FN_SWAP:			/* swap two meta-keys */
		kbd_state |= SWAP_MODE;	/* mark swap mode */
		kbd_last_swap = 0;		/* haven't gotten it yet */
		break;
	case FN_KILL:
		cnhangup();
		break;
	case FN_BREAK:
		make_break = 1;		/* released a make/break key */
		break;
	case FN_SWRESET:			/* switch screens with reset */
		kbd_status(sio, 0);		/* clear status indicators */
		cons_focus(SCREEN_SWITCH_RELOAD);	/* switch & reload */
		kbd_status(si, kbd_state);	/* set status indicators */
		break;
	case FN_SWITCH:			/* switch screens */
		kbd_status(sio, 0);		/* clear status indicators */
		cons_focus(SCREEN_SWITCH); /* to next screen */
		sio = &cons_info[cons_display];
		kbd_status(sio, kbd_state);	/* set status indicators */
		break;
	case FN_SCROLL:
		cnscroll();	/* send xon or xoff */
		break;
	case FN_PRINT:
		print_screen(si, 0);	/* print it */
		break;
	case FN_LOG:
		print_screen(si, 1);	/* print log it */
		break;
	case FN_CLICK:
#ifndef ATR
		click = (click + 1) & 03;
		set_click(click);
#endif ATR
		break;
	case FN_IGNORE:
		break;
	case FN_BEEP:
		beep();
		break;
	case FN_RESET:
		kbdcreset();		/* reset to standard */
		break;
	case FN_SET:
		kbd_state ^= SET_MODE;
		if ((kbd_state & SET_MODE) == 0)
			kbd_set(si, (char *) 0);	/* cancel definition in progress */
		break;
	default:
		break;
	}
}


/* LOCAL */
kbd_status(si,state)
	register SCREEN_INFO *si;
	register int state;
{
	static int xdebug_key = -1;

	if (xdebug_key != debug_key) {
debug_key_loop:
		put_status(si, 60, debug_key ? "DEBUG" : "     ");
		xdebug_key = debug_key;
#if defined(KERNEL) && defined(RDB)
		if (debug_key) {
			debug_key = 0;
			/* asm(" setsb r12,16-16 "); level 0 interrupt */
			setint0();
			goto debug_key_loop; /* redisplay it */
		}
#endif
	}
	put_status(si, 44, (state & KBD_SECURE) ? "KBDLOCK" : "       ");
	put_status(si, 53, (state & SET_MODE) ? 
		(kbd_set_ptr ? "DEF " : "KEY?") : "    ");
	put_status(si, 57, (state & SWAP_MODE) ? 
		(kbd_last_swap ? "SWAP" : "swap") : "     ");
#ifdef ATR
#ifdef AT_KEYBOARD
	put_status(si, 61, (state & NUM_LOCK_MODE) ? "NUMLCK" : "      ");
#endif AT_KEYBOARD
#ifdef ENH_AT_KEYBOARD
	put_status(si, 61, (state & ACTION_MODE) ? "ACTION" : "      ");
#endif ENH_AT_KEYBOARD
#else
	put_status(si, 61, (state & ACTION_MODE) ? "ACTION" : "      ");
#endif ATR
	put_status(si, 67, (state & (ALT1_MODE | ALT2_MODE)) ? "ALT" : "   ");
	if ((kbd_state & CAPS_MODE) && 
	   ( kbd_state & (SHIFT1_MODE | SHIFT2_MODE)) == 0 )
		put_status(si, 70, "CAPS  ");
	else if (kbd_state & (SHIFT1_MODE | SHIFT2_MODE) && 
		((kbd_state & CAPS_MODE) == 0))
		put_status(si, 70, "SHIFT");
	else
		put_status(si, 70, "     ");
	put_status(si, 75, (state & CTL_MODE) ? "CTRL" : "    ");
}

/*
 * send a string to the line discipline 
 * a character at a time.
 */
kbd_send(si, ptr,length,flag)
register SCREEN_INFO *si;
register char *ptr;
register int length;
register int flag;
{
	register int c;
	register struct tty *tp = &cons[cons_if];

	if (tp->t_state == 0)
		return;		/* not open or anything - ignore input */
	for  (; (c = *ptr) != NONE && --length >= 0; ++ptr) {
		if (c == 0 && flag == 0)
			break;		/* only allow null as first byte */
		DEBUGF(cndebug & 0x80,
		    printf("got character %x\n", c));
		if (kbd_state & SET_MODE) {
			if (kbd_set_char(si, c))
				break;
		} else
			(*linesw[tp->t_line].l_rint)(c, tp);
		flag = 0;
	}
}

/*
 * keyboard ioctl 
 */
/*ARGSUSED*/
kbde_ioctl(tp, cmd, addr, flag)
	register struct tty *tp;
	caddr_t addr;
{
	register int error = -1;
#ifndef ATR
	int x;
#endif ATR
	switch (cmd) {

	case KBDCRESET:
	case KBDCRST:				/* reset string table */
	case KBDCSSTD:
	case KBDCGET:
	case KBDCSET:
	case KBDCOGET:
	case KBDCOSET:
	case KBDSGET:		/* calculate space available */
	case KBDSCLICK:		/* set click */
	case KBDGCLICK:		/* get current click status */
	case KBDSSECURE:	/* secure tke keyboard */
		if (u.u_uid && u.u_ttyp != tp)
			return (EACCES);
		while (tp->t_line == NTTYDISC &&
		   u.u_procp->p_pgrp != tp->t_pgrp && tp == u.u_ttyp &&
		   (u.u_procp->p_flag&SVFORK) == 0 &&
		   !(u.u_procp->p_sigignore & BIT(SIGTTOU)) &&
		   !(u.u_procp->p_sigmask & BIT(SIGTTOU))) {
			gsignal(u.u_procp->p_pgrp, SIGTTOU);
			sleep((caddr_t)&lbolt, TTOPRI);
		}
		break;
	}
#undef	bit

	switch(cmd) {
	case KBDCRESET:
		error = kbdcreset();		/* reset to standard */
		break;
	case KBDCRST:				/* reset string table */
		error = kbdcrst();
		break;
	case KBDCSSTD:
		if (suser())
			error = kbdcsstd();
		else
			error = EPERM;
		break;
	case KBDCGET:
		error = kbdcget((struct kbdarg *) addr);
		break;
	case KBDCSET:
		error = kbdcset((struct kbdarg *) addr);
		break;
	case KBDCOGET:
		error = kbdcoget((struct kbdoarg *) addr);
		break;
	case KBDCOSET:
		error = kbdcoset((struct kbdoarg *) addr);
		break;
	case KBDSGET:		/* calculate space available */
		* (int *) addr = KBD_STRING_SPACE - kbd_string_ptr;
		error = 0;
		break;
	case KBDSCLICK:		/* set click */
		click = * (int *) addr & 03;
		set_click(click);
		error = 0;
		break;
	case KBDGCLICK:		/* get current click status */
		* (int *) addr = click;
		error = 0;
		break;
	case KBDSSECURE:
#ifdef SECURE
#ifdef IBMRTPC
		if (* (int *) addr) {
			if (!nvr_is_secure())
				nvr_secure(*(int *)addr);
			x=crc_gen(_nvr,sizeof(struct nvram)-NVR_CRC_CHECK_BYTES);
			nvr_set_crc(x);
			klscmd(EXTCMD,KBDLOCK,0);
			kbd_init_buf.cmd[0].qp_cmd = NKSCAN; 
			kbd_init_buf.count = 1;
			kbdstrategy(&kbd_init_buf);
			kbd_state |= KBD_SECURE;
			kbd_status(&cons_info[cons_display],kbd_state);
		} else {
			if (nvr_is_secure())
				kbd_lock_chg();		/* release it */
		}
		error = 0;
#else IBRTPC
		printf("IOCTL KBDSECURE unimplemented on ATR!\n");
#endif IBRTPC
#endif SECURE
		break;
	}
	return(error);
}

kbdcget(arg)
register struct kbdarg *arg;
{
	register unsigned scancode = arg->kbd_scan;
	register unsigned type = arg->kbd_index;
	register unsigned length = arg->kbd_length;
	register char *p;
	register int code;
	register int l;

	DEBUGF(cndebug&SHOW_PF,
	printf("kbdcget: arg=%x scancode=%d type=%d length=%d\n",arg,scancode,type,length));
	if (scancode >= 255 || type >= MAX_TYPES)
		return(EINVAL);
	if (length > KBD_STRING_LENGTH)
		length = KBD_STRING_LENGTH;
	bzero(arg->kbd_text, length);	/* clear to requested length */
	code = code_map[scancode];
	if (code&META) {
		if (type != 0)
			return(EINVAL);		/* only one value allowed */
		length = 1;
		arg->kbd_text[0] = code;
		arg->kbd_type = META;
	} else {
		p = kbde_codes[code][type];
		if (*p & META) {
			arg->kbd_type = META;
		} else {
			arg->kbd_type = 0;
		}
		if (ISSTRING(p[0])) {
			p = kbd_strings + (p[1] << 8) + p[2];
			l = strlen(p);
			if (length > l)
				length = l;
		} else {
			p++;
			if (length > MAX_CHAR_LENGTH)
				length = MAX_CHAR_LENGTH;
		}
		bcopy(p, arg->kbd_text, length);
	}
	arg->kbd_length = length;		/* actual length */
	return(0);
}

kbdcset(arg)
register struct kbdarg *arg;
{
	register unsigned scancode = arg->kbd_scan;
	register unsigned type = arg->kbd_index;
	register unsigned length = arg->kbd_length;
	register int code;

	DEBUGF(cndebug & SHOW_PF,
	printf("kbdcset: arg=%x scancode=%d type=%d length=%d flags=%x\n",arg,scancode,type,length,arg->kbd_type));
	if (scancode >= 255 || type >= MAX_TYPES)
		return(EINVAL);
	if (length > KBD_STRING_LENGTH)
		length = KBD_STRING_LENGTH;
	code = code_map[scancode];
	if (code&META) {
		if ((arg->kbd_text[0]&META) == 0 || length != 1)
			return(EINVAL);	/* invalid replacement */
		code_map[scancode] = arg->kbd_text[0];
	} else if (arg->kbd_type & META) {
		kbde_codes[code][type][0] = META;
		kbde_codes[code][type][1] = arg->kbd_text[0];
	} else 
		return(kbd_define(kbde_codes[code][type], length, arg->kbd_text));
	return(0);
}

kbdcoget(arg)
register struct kbdoarg *arg;
{
	register unsigned scancode = arg->kbd_scan;
	register unsigned type = arg->kbd_index;
	register unsigned length = arg->kbd_length;
	register char *p;
	register int code;
	register int l;

	DEBUGF(cndebug&SHOW_PF,
	printf("kbdcoget: arg=%x scancode=%d type=%d length=%d\n",arg,scancode,type,length));
	if (scancode >= 255 || type >= MAX_TYPES)
		return(EINVAL);
	if (length > KBD_STRING_LENGTH)
		length = KBD_STRING_LENGTH;
	bzero(arg->kbd_text, length);	/* clear to requested length */
	code = code_map[scancode];
	if (code&META) {
		if (type != 0)
			return(EINVAL);		/* only one value allowed */
		length = 1;
		arg->kbd_text[0] = code;
	} else {
		p = kbde_codes[code][type];
		if (ISSTRING(p[0])) {
			p = kbd_strings + (p[1] << 8) + p[2];
			l = strlen(p);
			if (length > l)
				length = l;
		} else {
			p++;
			if (length > MAX_CHAR_LENGTH)
				length = MAX_CHAR_LENGTH;
		}
		bcopy(p, arg->kbd_text, length);
	}
	arg->kbd_length = length;		/* actual length */
	return(0);
}


kbdcoset(arg)
register struct kbdoarg *arg;
{
	register unsigned scancode = arg->kbd_scan;
	register unsigned type = arg->kbd_index;
	register unsigned length = arg->kbd_length;
	register int code;

	DEBUGF(cndebug & SHOW_PF,
	printf("kbdcoset: arg=%x scancode=%d type=%d length=%d\n",arg,scancode,type,length));
	if (scancode >= 255 || type >= MAX_TYPES)
		return(EINVAL);
	if (length > KBD_STRING_LENGTH)
		length = KBD_STRING_LENGTH;
	code = code_map[scancode];
	if (code&META) {
		if ((arg->kbd_text[0]&META) == 0 || length != 1)
			return(EINVAL);	/* invalid replacement */
		code_map[scancode] = arg->kbd_text[0];
	} else if (arg->kbd_text[0] & META) {
		kbde_codes[code][type][0] = META;
		kbde_codes[code][type][1] = arg->kbd_text[0];
	} else 
		return(kbd_define(kbde_codes[code][type], length, arg->kbd_text));
	return(0);
}

/*
 * allocate space for string of length 'length' 
 * return -1 if we run out of space.
 */
kbd_alloc(length)
register int length;
{
	register int ptr = kbd_string_ptr;
	register int new = ptr + length;

	if (ptr >= KBD_STRING_SPACE)
		return(-1);
	kbd_string_ptr = new;
	kbd_string_cnt ++;
	return(ptr);
}


/*
 * reset the string table 
 * scan thru the key settings and reset anything that points 
 * into the string pool to be ignored.
 */
kbdcrst()
{
	register char *p = kbde_codes[0][0];
	register int i, j;

	for (i=0; i<MAX_CODES; ++i) {
		for (j=0; j<MAX_TYPES; ++j) {
			if (ISSTRING(*p)) {
				p[0] = META;
				p[1] = FN_IGNORE;
				p[2] = 0;
			}
			p += MAX_CODE_LENGTH;
		}
	}
	kbd_string_ptr = 0;
	kbd_string_cnt = 0;
	return(0);
}

/*
 * reset the keyboard scan code to character map 
 * by copying in the standard versions
 * we do a kbdcrst to clear any strings that happen to
 * be defined.
 */
kbdcreset()
{
	bcopy((caddr_t) std_codes, (caddr_t) kbde_codes, sizeof kbde_codes);
	bcopy((caddr_t) std_code_map, (caddr_t) code_map, sizeof code_map);
	kbdcrst();		/* clear any string definitions */
	if(_init_kbd)		/* don't set click if kbd not inited */
		set_click(stdclick);	/* set click as well */
	return(0);
}

/*
 * copy the current (working) map into the standard one
 * also copy current click status.
 */
kbdcsstd()
{
	bcopy((caddr_t) kbde_codes, (caddr_t) std_codes, sizeof kbde_codes);
	bcopy((caddr_t) code_map, (caddr_t) std_code_map, sizeof code_map);
	stdclick = click;
	return(0);
}

/*
 * set up a defined string:
 * on first entry remember the table pointer;
 * on last entry (when called with original table pointer)
 * we complete storing of the string.
 * when called during the actual definition process we return 
 * 0 so that the caller knows to call kbd_send. kbd_send 
 * will call kbd_set_char with a character to be stored.
 * when we are all finished we clear the status line 
 * where we echoed the characters.
 * note one special case: if during a definition we have a second
 * FN_SET then we cancel any definition in progress.
 */
kbd_set(si, p)
register SCREEN_INFO *si;
register char *p;
{
	char buff[KBD_STRING_LENGTH*2+5];	/* room for enough blanks */
	register int i;

	if (p == 0 || kbd_set_ptr == p) {
	/*
	 * finished defining  (if p==0 then cancel definition)
	 */
		if (p && kbd_define(p, kbd_set_cnt, kbd_strings + kbd_string_ptr))
			beep();			/* problem found */
		kbd_state &= ~SET_MODE;
		kbd_set_ptr = 0;
		for (i=0; i<kbd_set_out+2; ++i)
			buff[i] = ' ';
		buff[i] = 0;
		put_status(si, 0,buff);		/* blank the status line */
		kbd_set_out = 0;
		return(1);
	}
	/*
	 * remember the key we are setting 
	 */
	if (kbd_set_ptr == 0 && (*p != FN_SET)) {
		kbd_set_ptr = p;
		kbd_set_cnt = 0;
		kbd_set_out = 0;
		put_status(si, 0, "<-");
		return(1);
	}
	return(0);		/* call kbd_set_char below with characters */
}

/*
 * called with a character to be added to the definition of the 
 * current string.
 * characters are echoed as entered in the status line 
 * control characters are echoed as ^letter
 */

kbd_set_char(si, c)
register SCREEN_INFO *si;
register int c;
{
	register int l;
	char text[5];
	if (kbd_set_cnt >= KBD_STRING_LENGTH) {
		kbd_set(si, kbd_set_ptr);		/* finished defining */
		return(E2BIG);			/* too long */
	}
	if (c < ' ') {
		text[0] = '^';
		text[1] = c + '@';
		l = 2;
	}
	else {
		text[0] = c;
		l = 1;
	}
	text[l] = '<';
	text[l+1] = '-';
	text[l+2] = 0;
	kbd_strings[kbd_string_ptr + kbd_set_cnt] = c;
	put_status(si, kbd_set_out, text);
	kbd_set_out += l;
	kbd_set_cnt++;
	return(0);
}

/*
 * define (at position "p" of the codes table)
 * a string of length "length" from "text"
 */
kbd_define(p, length, text)
register char *p, *text;
register int length;
{
	if (length > MAX_CHAR_LENGTH) {
		register int ptr = kbd_alloc(length+1);
		if (ptr < 0)
			return(E2BIG);
		p[0] =  META_STRING;
		p[1] = (ptr >> 8);
		p[2] = ptr;		/* implied (ptr & 0xff) */
		p = kbd_strings + ptr;
		bcopy(text, p, length);
		p[length] = 0;
	} else {
		p[0] = 0;
		bzero(p+1, MAX_CHAR_LENGTH);	/* clear to max length */
		if (length == 0) {
			p[0] = META;	/* zero length = ignore it */
			p[1] = FN_IGNORE;	/* zero length = ignore it */
		} else
			bcopy(text, p+1, length);
	}
	return(0);		/* normal return code */
}

set_click(newclick)
register int newclick;
{
#ifdef IBMRTPC
	register int s = KLSSPL();
#endif IBMRTPC
	click = newclick;
#ifdef IBMRTPC
	KEYCLICK_AUTO(click  & 1);
	splx(s);
#endif IBMRTPC
}
