FreeBSD-5.3/usr.sbin/pcvt/kcon/kcon.c

/*
 * Copyright (c) 1992, 2000 Hellmuth Michaelis
 *
 * Copyright (c) 1992, 1993 Holger Veit.
 *
 * All rights reserved.
 *
 * This code is derived from software contributed to 386BSD by
 * Holger Veit
 *
 * 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by
 *	Hellmuth Michaelis and Holger Veit
 * 4. The name authors may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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.
 */

/*---------------------------------------------------------------------------*
 *
 *	kcon.c		Keyboard control and remapping
 *	----------------------------------------------
 *
 *	based on "keymap" which was written by
 *	Holger Veit (veit@du9ds3.uni-duisburg.de)
 *
 *	Last Edit-Date: [Mon Mar 27 17:03:50 2000]";
 *
 * $FreeBSD: src/usr.sbin/pcvt/kcon/kcon.c,v 1.7 2001/03/01 06:22:44 imp Exp $
 *
 *---------------------------------------------------------------------------*/

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <machine/pcvt_ioctl.h>

#include "keycap.h"

int Rf = 0;
int df = 0;
int lf = 0;
int mf = 0;
int of = 0;
int pf = 0;
int rf = 0;
int tf = 0;
int xf = 0;
int sf = 0;

/*---------------------------------------------------------------------------*
 *	main entry
 *---------------------------------------------------------------------------*/
main(argc, argv)
int argc;
char *argv[];
{
	int c = 0;

	int errf = 0;

	int rate = -1;
	int delay = -1;
	char *map;
	int kbfd;

	while((c = getopt(argc, argv, "Rd:lm:opr:st:x")) != -1)
	{
		switch(c)
		{
			case 'R':
				Rf = 1;
				break;

			case 'd':
				df = 1;
				delay = atoi(optarg);
				break;

			case 'l':
				lf = 1;
				break;

			case 'm':
				mf = 1;
				map = optarg;
				break;

			case 'o':
				if(xf)
					errf = 1;
				else
					of = 1;
				break;

			case 'p':
				pf = 1;
				break;

			case 'r':
				rf = 1;
				rate = atoi(optarg);
				break;

			case 's':
				sf = 1;
				break;

			case 't':
				if(*optarg == '+')
					tf = 1;
				else if(*optarg == '-')
					tf = -1;
				else
					errf = 1;
				break;

			case 'x':
				if(of)
					errf = 1;
				else
					xf = 1;
				break;

			default:
				usage();
		}
	}

	if((Rf == 0 && df == 0 && lf == 0 && tf == 0 && sf == 0 &&
	    rf == 0 && mf == 0 ) || errf)
	{
		usage();
	}

	if((kbfd = open(KEYB_DEVICE, 0)) < 0)
	{
		perror("kcon: keyboard open failiure");
		exit(1);
	}

	if(sf)
	{
		showtypeamatic(kbfd);
		exit(0);
	}

	if(lf)
	{
		listcurrent(kbfd);
		exit(0);
	}

	if (Rf)
	{
		if (ioctl(kbfd, KBDRESET, 0) < 0) {
			perror ("kcon: ioctl KBDRESET failed");
			exit (1);
		}
	}

	if(tf)
	{
		setrepeat(kbfd, tf);
	}

	if(df || rf)
	{
		if(delay > 3)
		{
			fprintf(stderr,"Delay value (%d) out of range, possible values are 0..3!\n",delay);
			exit(1);
		}
		if(rate > 31)
		{
			fprintf(stderr,"Rate value (%d) out of range, possible values are 0..31!\n",rate);
			exit(1);
		}
		settypeam(kbfd, delay, rate);
	}

	if(mf)
	{
		remapkeys(kbfd, map);
	}

	close(kbfd);
	exit(0);
}

/*---------------------------------------------------------------------------*
 *	display usage info & exit
 *---------------------------------------------------------------------------*/
usage()
{
	fprintf(stderr, "\nkcon: keyboard control and remapping utility for pcvt video driver\n");
	fprintf(stderr, "usage: [-R] [-d delay] [-l] [-m map] [-o] [-p] [-r rate] [-t +/-] [-x]\n");
	fprintf(stderr, "       -R   full reset of keyboard\n");
	fprintf(stderr, "       -d   delay until a key is repeated (range: 0...3 => 250...1000ms)\n");
	fprintf(stderr, "       -l   produce listing of current keyboard mapping\n");
	fprintf(stderr, "       -m   set keyboard remapping from a keycap entry\n");
	fprintf(stderr, "       -o   set octal output for listing\n");
	fprintf(stderr, "       -p   pure, don't display escape as 'ESC' for listing\n");
	fprintf(stderr, "       -r   chars/second repeat value (range: 0...31 => 30...2 chars/sec)\n");
	fprintf(stderr, "       -s   show, display the current keyboard typematic values\n");
	fprintf(stderr, "       -t   switch repeat on(+) or off(-)\n");
	fprintf(stderr, "       -x   set hexadecimal output for listing\n\n");
	exit(1);
}

/*---------------------------------------------------------------------------*
 *	convert control char in string to printable values
 *---------------------------------------------------------------------------*/
char *showcntrl(s)
u_char *s;
{
	static char res_str[80];
	static char conv_buf[80];
	int i;

	res_str[0] = '\0';

	for(i = 0; s[i]; i++)
	{
		if(((s[i] > 0x20) && (s[i] <= 0x7e)) || ((s[i] >= 0xa0) && (s[i] <= 0xff)))
		{
			conv_buf[0] = s[i];
			conv_buf[1] = '\0';
		}
		else if((s[i] == 0x1b) && (pf == 0))
		{
			strcpy(conv_buf,"ESC ");
		}
		else if(of)
		{
			sprintf(conv_buf,"\\%03.3o ", s[i]);
		}
		else
		{
			sprintf(conv_buf,"0x%02.2X ", s[i]);
		}
		strcat(res_str, conv_buf);
	}
	return(res_str);
}

/*---------------------------------------------------------------------------*
 *	list the current keyboard mapping
 *---------------------------------------------------------------------------*/
listcurrent(kbfd)
int kbfd;
{
	static char *keytypetab[] = {
		"NONE     ",
		"SHIFT    ",
		"ALT/META ",
		"NUMLOCK  ",
		"CONTROL  ",
		"CAPSLOCK ",
		"ASCII    ",
		"SCROLL   ",
		"FUNCTION ",
		"KEYPAD   ",
		"BREAK    ",
		"ALTGR    ",
		"SHIFTLOCK",
		"CURSOR   ",
		"RETURN   "
	};

	struct kbd_ovlkey keyboardmap[KBDMAXKEYS];
	struct kbd_ovlkey *kbmapp;
	int keytype;
	int altgr_defined;
	int i;

	altgr_defined = 0;
	kbmapp = keyboardmap;

	for (i = 0; i < KBDMAXKEYS; i++)
	{
		kbmapp->keynum = i;

		if(ioctl(kbfd, KBDGCKEY, kbmapp) < 0)
		{
			perror("kcon: ioctl KBDGCKEY failed");
			exit(1);
		}

		if((kbmapp->type & KBD_MASK) == KBD_ALTGR)
			altgr_defined = i;

		kbmapp++;
	}

	if(altgr_defined)
	{
		printf("S Key KeyType   Normal          Shift           Control         Altgr          \n");
		printf("- --- --------- --------------- --------------- --------------- ---------------\n");
	}
	else
	{
		printf("S Key KeyType   Normal          Shift           Control        \n");
		printf("- --- --------- --------------- --------------- ---------------\n");
	}

	kbmapp = &keyboardmap[1];

	for(i = 1; i < KBDMAXKEYS; i++)
	{
		keytype = kbmapp->type;

		if(keytype)
		{
			if(keytype & KBD_OVERLOAD)
				printf("! %3.3d %9.9s ", i, keytypetab[keytype & KBD_MASK]);
			else
				printf("- %3.3d %9.9s ", i, keytypetab[keytype & KBD_MASK]);

			switch(keytype & KBD_MASK)
			{

				case KBD_NUM:
				case KBD_ASCII:
				case KBD_FUNC:
				case KBD_KP:
				case KBD_CURSOR:
				case KBD_RETURN: /* ??? */

					if(kbmapp->subu == KBD_SUBT_STR)
						printf("%-15s ",showcntrl(kbmapp->unshift));
					else
						printf("Function()      ");

					if(kbmapp->subs == KBD_SUBT_STR)
						printf("%-15s ",showcntrl(kbmapp->shift));
					else
						printf("Function()      ");

					if(kbmapp->subc == KBD_SUBT_STR)
						printf("%-15s ",showcntrl(kbmapp->ctrl));
					else
						printf("Function()      ");

					if(altgr_defined)
					{
						if(kbmapp->suba == KBD_SUBT_STR)
							printf("%-15s ",showcntrl(kbmapp->altgr));
						else
							printf("Function()      ");
					}
					break;
			}
			putchar('\n');
		}
		kbmapp++;
	}
}

/*---------------------------------------------------------------------------*
 *	show delay and rate values for keyboard
 *---------------------------------------------------------------------------*/
showtypeamatic(kbfd)
int kbfd;
{
	static char *delaytab[] = {
		"250",
		"500",
		"750",
		"1000"
	};

	static char *ratetab[] = {
		"30.0",
		"26.7",
		"24.0",
		"21.8",
		"20.0",
		"18.5",
		"17.1",
		"16.0",
		"15.0",
		"13.3",
		"12.0",
		"10.9",
		"10.0",
		"9.2",
		"8.6",
		"8.0",
		"7.5",
		"6.7",
		"6.0",
		"5.5",
		"5.0",
		"4.6",
		"4.3",
		"4.0",
		"3.7",
		"3.3",
		"3.0",
		"2.7",
		"2.5",
		"2.3",
		"2.1",
		"2.0"
	};

	int cur_typemat_val;
	int delay, rate;

	if((ioctl(kbfd, KBDGTPMAT, &cur_typemat_val)) < 0)
	{
		perror("kcon: ioctl KBDGTPMAT failed");
		exit(1);
	}

	delay = ((cur_typemat_val & 0x60) >> 5);
	rate = cur_typemat_val & 0x1f;

	printf("\nDisplaying the current keyboard typematic values:\n\n");
	printf("The delay-until-repeat time is [ %s ] milliseconds\n",delaytab[delay]);
	printf("The repeat-rate is [ %s ] characters per second\n\n",ratetab[rate]);
}

/*---------------------------------------------------------------------------*
 *	set repeat feature on/off
 *---------------------------------------------------------------------------*/
setrepeat(kbfd, tf)
int kbfd;
int tf;
{
	int	srepsw_val;

	if(tf == 1)
		srepsw_val = KBD_REPEATON;
	else
		srepsw_val = KBD_REPEATOFF;

	if(ioctl(kbfd, KBDSREPSW, &srepsw_val) < 0)
	{
		perror("kcon: ioctl KBDREPSW failed");
		exit(1);
	}
}

/*---------------------------------------------------------------------------*
 *	set delay and rate values for keyboard
 *---------------------------------------------------------------------------*/
settypeam(kbfd, delay, rate)
int kbfd;
int delay;
int rate;
{
	int cur_typemat_val;
	int new_typemat_val;

	if((ioctl(kbfd, KBDGTPMAT, &cur_typemat_val)) < 0)
	{
		perror("kcon: ioctl KBDGTPMAT failed");
		exit(1);
	}

	if(delay == -1)
		delay = (cur_typemat_val & 0x60);
	else
		delay = ((delay << 5) & 0x60);

	if(rate == -1)
		rate = (cur_typemat_val & 0x1f);
	else
		rate &= 0x1f;

	new_typemat_val = delay | rate;

	if((ioctl(kbfd, KBDSTPMAT, &new_typemat_val)) < 0)
	{
		perror("kcon: ioctl KBDSTPMAT failed");
		exit(1);
	}
}

/*---------------------------------------------------------------------------*
 *	remap keyboard from keycap entry
 *---------------------------------------------------------------------------*/
remapkeys(kbfd, map)
int kbfd;
char *map;
{
	char cap_entry[1024];
	int ret;
	char keyflag[128];
	int i;

	/* try to find the entry */

	ret = kgetent(cap_entry, map);

	if(ret == -1)
	{
		fprintf(stderr, "kcon: keycap database not found or not accessible!\n");
		exit(1);
	}
	else if(ret == 0)
	{
		fprintf(stderr, "kcon: keycap entry [%s] not found in database!\n", map);
		exit(1);
	}

	/* set default mapping */

	if((ioctl(kbfd, KBDDEFAULT)) < 0)
	{
		perror("kcon: ioctl KBDDEFAULT failed");
		exit(1);
	}

	/* DE flag present? */

	if(kgetflag("de"))
		return;

	for(i = 0; i < KBDMAXKEYS; i++)
		keyflag[i] = 0;

	set_lock(keyflag, kbfd);

	set_shift(keyflag, kbfd);

	set_char(keyflag, kbfd);
}

/*---------------------------------------------------------------------------*
 *	care for lock keys
 *---------------------------------------------------------------------------*/
set_lock(keyflag, kbfd)
char keyflag[];
int kbfd;
{
	int i, j;
	char cap[16];
	struct kbd_ovlkey entry;

	struct	{
		char	*ch;
		u_short	typ;
	} lock[] =
	{
		"ca",	KBD_CAPS,
		"sh",	KBD_SHFTLOCK,
		"nl",	KBD_NUMLOCK,
		"sc",	KBD_SCROLL
	};


	for(i = 0; i < 4; i++)
	{
		int n;

		sprintf(cap, "%s", lock[i].ch);

		n = kgetnum(cap);

		if(n > 0)
		{
			if (keyflag[n])
			{
				fprintf(stderr,"kcon: duplicate key definition for key [%d]!\n",n);
				exit(1);
			}
			keyflag[n] = 1;

			entry.keynum = n;
			entry.type = lock[i].typ;

			if((ioctl(kbfd, KBDSCKEY, &entry)) < 0)
			{
				perror("kcon: ioctl KBDSCKEY failed");
				exit(1);
			}
		}
	}
}

/*---------------------------------------------------------------------------*
 *	care for shifting keys
 *---------------------------------------------------------------------------*/
set_shift(keyflag, kbfd)
char keyflag[];
int kbfd;
{
	int i, j;
	char cap[16];
	struct kbd_ovlkey entry;

	struct {
		char	ch;
		u_short	typ;
	} shift[] =
	{
		'm',	KBD_META,
		'l',	KBD_ALTGR,
		'h',	KBD_SHIFT,
		't',	KBD_CTL
	};

	for(i = 0; i < 4; i++)
	{
		for(j = 1; j < 10; j++)
		{
			int n;

			sprintf(cap, "%c%d", shift[i].ch,j);

			n = kgetnum(cap);

			if (n >= 0)
			{
				if (keyflag[n])
				{
					fprintf(stderr,"kcon: duplicate key definition for key [%d]!\n",n);
					exit(1);
				}
				keyflag[n] = 1;

				entry.keynum = n;
				entry.type = shift[i].typ;
				if((ioctl(kbfd, KBDSCKEY, &entry)) < 0)
				{
					perror("kcon: ioctl KBDSCKEY failed");
					exit(1);
				}
			}
		}
	}
}

/*---------------------------------------------------------------------------*
 *	care for normal keys
 *---------------------------------------------------------------------------*/
set_char(keyflag, kbfd)
char keyflag[];
int kbfd;
{
	int i, j;
	char cap[16];
	int setflag;
	char *addr_str;
	char *new_str;
	struct kbd_ovlkey entry;

	struct {
		char	*addr;
		char	ch;
	} standard[] = {
		0,			'D',
		&entry.unshift[0],	'K',
		&entry.shift[0],	'S',
		&entry.ctrl[0],		'C',
		&entry.altgr[0],	'A'
	};

	for(i = 1; i < KBDMAXKEYS; i++)
	{
		setflag = 0;

		entry.keynum = i;

		if((ioctl(kbfd, KBDGOKEY, &entry)) < 0)
		{
			perror("kcon: ioctl KBDGOKEY failed");
			exit(1);
		}

		entry.type = KBD_ASCII;

		for(j = 0; j < 5; j++)
		{
			sprintf(cap, "%c%d", standard[j].ch,i);

			if((j == 0) && (kgetflag(cap)))
			{
				/* delete a key */

				entry.type = KBD_NONE;
				setflag = 1;
				goto setit;

			}
			else
			{
				addr_str = standard[j].addr;
				if(new_str = kgetstr(cap, &addr_str))
				{
					if(strlen(new_str) > KBDMAXOVLKEYSIZE)
					{
						fprintf(stderr, "kcon: database entry string [%s] longer than max [%d]!\n",new_str,KBDMAXOVLKEYSIZE);
						exit(1);
					}
					setflag = 1;
				}
			}
		}

setit:		if (setflag)
		{
			if (keyflag[i])
			{
				fprintf(stderr,"kcon: duplicate key definition for key [%d]!\n",i);
				exit(1);
			}
			keyflag[i] = 1;

			if((ioctl(kbfd, KBDSCKEY, &entry)) < 0)
			{
				perror("kcon: ioctl KBDSCKEY failed");
				exit(1);
			}
		}
	}
}

/*------------------- EOF ------------------------------------------------*/