Subject: lk201 improvements, X server interface for PMAG-CA
To: None <port-pmax@netbsd.org>
From: Andy Doran <ad@psn.ie>
List: port-pmax
Date: 03/19/1999 21:07:24
I've made a few improvments to the keyboard code locally. If nobody
has any objections, I'll commit these within a few days. Here's what
I wanted to accomplish:

  o Allow the use of CapsLock
  o Allow Ctrl+Shift (The undo key in 'joe' is ^_ ;)
  o Allow the use of cursor keys, function keys and friends

I changed the logic in kbdMapChar for the first two. To get the third,
kbdMapChar now returns char *. We keep a small table of strings for
the "complex" keys, matching what termcap expects to get for properties
like ku, kN, kP and so on. This required other minor modifications in
dc.c, dtop.c, scc.c to handle the 'char *',and the addition of
KEY_CAPSLOCK in dev/dec/lk201.h. It works well.

Problems: 

  o Trying to turn the caps lock light on in kbdMapChar causes a
    lockup (due to the spl and whatever the scc driver does to
    transmit?). It shouldn't be done here anyway, since kbdMapChar can 
    be called while we're running X.
  o If shift and control are both depressed, releasing either will make
    Mr. Kernel think that both are released. This isn't a big deal.

The PX driver is done, aside from formalising the rcons stuff, conversion
to KNF and a general reorganisation. I'm thinking how the X server
interface should be done. Basically, all we really need is a couple of
ioctls, and a mapping for the packet buffers/info into user space. Can
we cleanly do this by modifications to the fb driver?

Also, I was keeping the packet area Ultrix 'compatible', since I didn't
know of the Xpxpmax server. Now that we have the source, there's no
reason to use the same format as Ultrix, since most of it is either slop,
or is used for commuication with the X server, and the latter can just
be MALLOC'ed up.

Andy.

--- lk201.c.orig	Thu Mar 18 20:30:23 1999
+++ lk201.c	Fri Mar 19 20:16:23 1999
@@ -19,7 +19,7 @@
 
 
 /* Exported functions */
-extern int kbdMapChar __P((int keycode));
+extern char *kbdMapChar __P((int keycode));
 
 extern void KBDReset __P(( dev_t dev, void (*putc) (dev_t, int) ));
 
@@ -190,6 +190,27 @@
 	LK_LED_DISABLE, LED_ALL,	/* clear keyboard leds */
 };
 
+
+/*
+ * Keyboard to what the rcons termcap entry expects.
+ * XXX function keys are handled specially.
+ */
+static struct toString {
+	int	ts_keycode;
+	char	*ts_string;
+} toString[] = {			/* termcap name */
+	{ KBD_UP,	"\033[A" },	/* ku */	
+	{ KBD_DOWN,	"\033[B" },	/* kd */
+	{ KBD_RIGHT,	"\033[C" },	/* kr */
+	{ KBD_LEFT,	"\033[D" },	/* kl */
+	{ KBD_REMOVE,	"\177" },	/* kD */
+	{ KBD_NEXT,	"\033[222z" },	/* kN */
+	{ KBD_PREVIOUS,	"\033[216z" },	/* kP */
+};
+
+#define NUM_TOSTRING (sizeof(toString) / sizeof(toString[0]))
+
+
 static void (*raw_kbd_putc) __P((dev_t dev, int c)) = NULL;
 static dev_t lk_out_dev = NODEV;
 
@@ -242,42 +263,53 @@
  *	None.
  *
  * Side effects:
- *	Remember state of shift and control keys.
+ *	Remember state of shift, control and caps-lock keys.
  *
  * ----------------------------------------------------------------------------
  */
-int
+char *
 kbdMapChar(cc)
 	int cc;
 {
 	static u_char shiftDown;
 	static u_char ctrlDown;
-	static u_char lastChar;
-
+	static u_char capsLock;
+	static char buf[8];
+	static char *lastStr;
+	char *cp = NULL;	
+	
 	switch (cc) {
 	case KEY_REPEAT:
-		cc = lastChar;
+		cp = lastStr;
 		goto done;
 
 	case KEY_UP:
 		shiftDown = 0;
 		ctrlDown = 0;
-		return (-1);
-
+		return (NULL);
+		
+	case KEY_CAPSLOCK:
+		capsLock ^= 1;
+#if 0
+		/* XXX causes race due to spl??? */
+		/* XXX may interact badly with Xserver */
+		if (capsLock)
+			(*raw_kbd_putc)(lk_out_dev, LK_LED_ENABLE);
+		else
+			(*raw_kbd_putc)(lk_out_dev, LK_LED_DISABLE);
+		
+		(*raw_kbd_putc)(lk_out_dev, LED_1);
+#endif
+		return (NULL);
+			
 	case KEY_SHIFT:
 	case KEY_R_SHIFT:
-		if (ctrlDown || shiftDown)
-			shiftDown = 0;
-		else
-			shiftDown = 1;
-		return (-1);
+		shiftDown ^= 1;
+		return (NULL);
 
 	case KEY_CONTROL:
-		if (shiftDown || ctrlDown)
-			ctrlDown = 0;
-		else
-			ctrlDown = 1;
-		return (-1);
+		ctrlDown ^= 1;
+		return (NULL);
 
 	case LK_POWER_ERROR:
 	case LK_KDOWN_ERROR:
@@ -285,22 +317,58 @@
 	case LK_OUTPUT_ERROR:
 		log(LOG_WARNING,
 			"lk201: keyboard error, code=%x\n", cc);
-		return (-1);
+		return (NULL);
 	}
 	if (shiftDown)
 		cc = shiftedAscii[cc];
 	else
 		cc = unshiftedAscii[cc];
 	if (cc >= KBD_NOKEY) {
-		/*
-		 * A function key was typed - ignore it.
-		 */
-		return (-1);
+		int i;
+		
+		/* XXX slow, although keyboard interrupts aren't frequent... */
+		
+		/* Check for keys that have multi-character codes */
+		for (i = 0; i < NUM_TOSTRING; i++)
+			if (toString[i].ts_keycode == cc) {
+				cp = toString[i].ts_string;
+				break;
+			}
+		
+		/* Handle function keys specially */
+		if (cp == NULL) {
+			if (cc < KBD_F1 || cc > KBD_F20)
+				return NULL;
+		
+			/* 
+			 * All the function keys (KBD_*) are contigious,
+			 * except for the 'Help' and 'Do' keys, which we
+			 * return as F15 and F16 since that's what they
+			 * really are.
+			 * XXX termcap can only handle F0->F9. Is this right?
+			 */
+			if (cc >= KBD_F1 && cc <= KBD_F6) {
+				buf[3] = '2';
+				buf[4] = '4' + (cc - KBD_F1);
+			} else if (cc >= KBD_F7 && cc <= KBD_DO) {
+				buf[3] = '3';
+				buf[4] = '0' + (cc - KBD_F7);
+			} else /* if (cc >= KBD_F17 && cc <= KBD_F20) */ {
+				buf[3] = '4';
+				buf[4] = '0' + (cc - KBD_F17);
+			}
+		
+			buf[0] = '\033';
+			buf[1] = '[';
+			buf[2] = '2';
+			buf[5] = '\0';
+			cp = buf;
+		}
 	}
 	if (cc >= 'a' && cc <= 'z') {
 		if (ctrlDown)
 			cc = cc - 'a' + '\1'; /* ^A */
-		else if (shiftDown)
+		else if (shiftDown ^ capsLock)
 			cc = cc - 'a' + 'A';
 	} else if (ctrlDown) {
 		if (cc >= '[' && cc <= '_')
@@ -308,9 +376,15 @@
 		else if (cc == ' ' || cc == '@')
 			cc = '\0';
 	}
-	lastChar = cc;
+	
+	if (cp == NULL) {
+		buf[0] = cc;
+		buf[1] = '\0';
+		cp = buf;
+	}
+	lastStr = cp;
 done:
-	return (cc);
+	return (cp);
 }
 
 
@@ -342,6 +416,7 @@
 	dev_t dev;	/* ignored */
 {
 	register int c;
+	static char *bp;
 
 #if 0
 /*XXX*/ printf("LK-201 getc 0x%x( [%d %d]) in_dev [%d %d]\n",
@@ -356,6 +431,13 @@
 	}
 
 	for (;;) {
+		if (bp) {
+			c = *bp++;
+			if (*bp == '\0')
+				bp = NULL;
+			break;
+		}
+		
 		/* c = (*cn_tab.cn_kbdgetc)(cn_tab.cn_dev); */
 		c = (*raw_kbd_getc) (lk_in_dev);
 #if 0
@@ -363,8 +445,8 @@
 #endif
 		if (c == 0)
 			return (-1);
-		if ((c = kbdMapChar(c & 0xff)) >= 0)
-			break;
+
+		bp = kbdMapChar(c & 0xff);
 	}
 	return (c);
 }