[gnu.emacs] Using special keys on HP9000s300 under X11

ml@ecerl3.ncsu.edu (11/26/89)

Greetings!

A while back I posted some questions about configuring Emacs on an HP9000
series 300 box (running HP-UX) to work well with X-Windows (X11).  The basic
question was "How do I enable use of all the extra function keys on my
keyboard?".

Well, the basic answer was "You can't".  It seems that the code in emacs to
handle the keyboard under X11 still is somewhat rudimentary.  One person
suggested that I might want to patch the source to extend this.  Several
others wrote to me requesting that I fill them in on the correct answer
since they had the same problem.  

First off, apologies to all those who sent me messages and got no reply.
I reply to all messages I get, but the mail system here is not set up
correctly and so most outgoing mail bounces.  I wish the administration
would fix that, but they haven't/won't.  Oh well.  Hopefully the stuff
contained here will server as an adequate response.  So, here goes....


-----------------------------------------------------------------------------

The problem lies in the source file x11term.c, where you will need to change
some things.  Included below is a fragment from that file showing what it
looks like after the changes I made.  By comparing this to the original file
you should have no difficulties making these changes.

By way of a disclaimer, I shall note in advance that what I have done here
does not necessarily represent the best way to do things.  In particular,
I have made no attempt whatsoever to make the key sequences map to anything
that you will find on an existing terminal (ie. I haven't tried to emulate
anything).  I just did a quick and dirty hack which gave me nearly total
control over the keyboard.

First off, some documentation on what I did:


I have patched src/x11term.c for the HP 9000 s 300 workstations so as to
enable use of all the extra keys on the keyboard, as well as recognizing some 
conventional keys with unconventional modifiers (such as Ctrl-Return).  
In any case where a key is treated specially (ie, doesn't map into a 
single ASCII character), you get a five character escape sequence, 
consisting of the characters 'ESC' and '[' followed by three hex digits,  i.e.:

    ESC [ #1 #2 #3

where the #1 #2 #3 are each an ASCII hex digit (characters in the range
'0' to '9' and 'A' through 'F').

#1 = state	The character gives the state of the modifier keys (the control, 
		shift, and meta keys).
		Bit 0 will be set if the shift key is down; 
		Bit 2 will be set if the control key is down;
		Bit 3 will be set if the meta ("Extend Char") key is down.
		Bit 1 is the bit for the CapsLock key, but I always
		strip this off so you won't see it set.
		So, the possible values for #1 are:

			0	No modifier keys down
			1       Shift
			4	Control
			5	Shift Control
			8	Meta (Alt)
			9       Shift Meta
			C	Control Meta
		        D	Shift Control Meta

		Remember that this binary value is sent as an ASCII hex digit,
		not in raw binary form.

#2#3 = key	These are the lower order byte of the keysym, expressed
		as two hex digits.  Look in <X11/keysymdef.h> to see what all
		the keysyms are.  The HP "Vendor specific" keys, which don't
		have standard keysyms, are remapped into standard keysyms,
		mostly F35, F34 ...

For example, if I hold the control key down and press the "Select" button,
this will be translated into the key sequence:

		ESC [ 4 6 0
                 |  | | | |
prefix ----------+--+ | +-+---------  Low order byte of keysym.  Keysym
                      |               for "Select" button is XK_Select,
                      |               which is 0xFF60.
                      |
Control down ---------+



Quick reference of all the special keys on the HP 9000s300 keyboard, and how I 
map them.

Key label:     #2#3		Associated X11 keysym	    Notes
------------   ----		---------------------	    ------------------

Cursor control, editing keys, miscellany:

BackSpace	08		XK_BackSpace		    1
Tab/BackTab	09		XK_Tab			    1,2
Return		0D		XK_Return		    1
Escape		1B		XK_Escape		    1,3
Delete		FF		XK_Delete		    1,3
Menu		67		XK_Menu
Stop		69		XK_Cancel
Reset/Break	6B		XK_Break

Home		50		XK_Home
LeftArrow	51		XK_Left
UpArrow		52		XK_Up
RightArrow	53		XK_Right
DownArrow	54		XK_Down
Prev		55		XK_Prior
Next		56		XK_Next
Select		60		XK_Select
Print/Enter	62		XK_Execute

User/System	E0		XK_F35
ClearLine	DF		XK_F34
ClearDisplay	DE		XK_F33
InsertLine	DD		XK_F32
DeleteLine	DC		XK_F31
InsertChar	DB		XK_F30
DeleteChar	DA		XK_F29


The function keys:

F1 		BE		XK_F1
F2 		BF		XK_F2
F3 		C0		XK_F3
F4 		C1		XK_F4
F5 		C2		XK_F5
F6 		C3		XK_F6
F7 		C4		XK_F7
F8 		C5		XK_F8

The keypad keys (note 4 applies for all):

0		B0		XK_KP_0
1		B1		XK_KP_1
2		B2		XK_KP_2
3		B3		XK_KP_3
4		B4		XK_KP_4
5		B5		XK_KP_5
6		B6		XK_KP_6
7		B7		XK_KP_7
8		B8		XK_KP_8
9		B9		XK_KP_9
*		AA		XK_KP_Multiply
+		AB		XK_KP_Add
,		AC		XK_KP_Separator
-		AD		XK_KP_Subtract
.		AE		XK_KP_Decimal
/		AF		XK_KP_Divide
Enter		8D		XK_KP_Enter
Tab/BackTab	89		XK_KP_Tab		    2


The PF keys (4 keys above the numeric keypad):

PF1		91		XK_KP_F1
PF2		92		XK_KP_F2
PF3		93		XK_KP_F3
PF4		94		XK_KP_F4



Notes:

1.  Backspace, return, etc. just map into ASCII code unless Control or Shift key
    used in conjunction with them.
2.  Backtab always sent as escape sequence, even though Tab may not always be.
    Backtab is sent as "Shifted Tab" esc. sequence rather than being mapped into
    XK_BackTab.  Similarly for Keypad Tab key.
3.  Although Delete is Shift+Esc on HP keyboard, these are treated as if they
    were separate keys.  Shift bit in state byte will always be clear for both
    of these (shifted Esc is remapped into unshifted Delete).
4.  If none of shift/control/alt are pressed, the keypad keys will just send 
    appropriate ASCII code, not an escape sequence.


======================================================================

Here are some elisp code fragments showing how you can make use of these
key mappings:

These lines are at the end of my .emacs file:

	(if (and (eq window-system 'x)
		 (eq system-type   'hpux) )
	    (load "Xkeys") )

and here is my file Xkeys.el (note: the function "define-keys" used below
is not part of distributed emacs, it's something in my .emacs file.  You
should be able to get the gist of what's going on though):

====== begin XKeys.el ========

(message "loading HPUX X11 key-bindings")


(defvar FCN-map    (make-keymap) "Keymap for function-key sequences")
(defvar SFCN-map   (make-keymap) "Keymap for shift-function-key sequences")
(defvar CFCN-map   (make-keymap) "Keymap for control-function-key-sequences")
(defvar MFCN-map   (make-keymap) "Keymap for meta-function-key-sequences")
(defvar CSFCN-map  (make-keymap) "Keymap for control-shift-function-key sequences")
(defvar MSFCN-map  (make-keymap) "Keymap for meta-shift-function-key sequences")
(defvar MCFCN-map  (make-keymap) "Keymap for meta-control-function-key sequences")
(defvar MCSFCN-map (make-keymap) "Keymap for meta-shift-control-function-key sequences")

(define-key global-map "\e["   nil)
(define-key global-map "\e[0"  FCN-map)
(define-key global-map "\e[1"  SFCN-map)
(define-key global-map "\e[4"  CFCN-map)
(define-key global-map "\e[5"  CSFCN-map)
(define-key global-map "\e[8"  MFCN-map)
(define-key global-map "\e[9"  MSFCN-map)
(define-key global-map "\e[C"  MCFCN-map)
(define-key global-map "\e[D"  MCSFCN-map)

(define-keys FCN-map '(
    ("50"	'other-window)
    ("51"	'backward-char)
    ("52"	'previous-line)
    ("53"	'forward-char)
    ("54"	'down-arrow)
    ("55"	'scroll-down)
    ("56"	'scroll-up)
    ("60"	'set-mark-command)
    ("67"	'help-command)
    ("BE"	 rect-map)
    ("DA"	'delete-char)
    ("DC"	'kill-whole-line)
    ("DB"	'quoted-insert)
))

(define-keys SFCN-map '(
    ("50"	'other-window)
    ("51"	'beginning-of-line)
    ("52"	'previous-line)
    ("53"	'end-of-line)
    ("54"	'down-arrow)
    ("55"	'scroll-down)
    ("56"	'scroll-up)
    ("60"	'set-mark-command)
    ("67"	'help-command)
    ("BE"	 rect-map)
    ("DA"	'delete-char)
    ("DC"	'kill-whole-line)
    ("DB"	'quoted-insert)
))

====== end of XKeys.el ========


And finally, here is the fragment from x11term.c which contains the
modifications I made:

====== begin x11term.c.changes ======

/*
 * Interpreting incoming keycodes. Should have table modifiable as needed
 * from elisp.
 */

#ifdef sun
char *
stringFuncVal(keycode)
    KeySym keycode;
    {
    switch (keycode)
        {
    case XK_L1:
	return ("192");
    case XK_L2:
	return ("193");

	 ...
	 ...
	 ...

    default:
	return ("-1");
        }
    }

#else 
#ifndef HPUX				/* Add this line */
char *
stringFuncVal(keycode)
    KeySym keycode;
    {
    switch (keycode)
        {
    case XK_F1:
	return ("11");
    case XK_F2:
	return ("12");

	...
	...
	...

    default:
	return ("-1");
        }
    }
#endif					/* Add this line */
#endif				/* not sun */


#ifdef HPUX				/* Add this block */

#define IsTTYKey(K)  ( ((K) >= XK_BackSpace) && ((K) <= XK_Escape) && \
	((K) != XK_Clear) ) 

char hexdigit[] = "0123456789ABCDEF";

int
TranslateKey(keysym, xbuf, state)
    int keysym;
    char * xbuf;
    int state;
{
    register int nbytes;
    register char * ptr;

    state &= ShiftMask | ControlMask | Mod1Mask;
    ptr = xbuf;
    nbytes = 0;

    /*  As long as they are not holding down the shift or control or meta
     *  keys, we send keypad keys and TTY keys (backspace, return, et al)
     *  as regular ASCII characters rather than special escape codes.
     *  For TTY keys, Meta key does the expected thing (setting high bit).
     *
     *  Holding shift/control/meta keys down in conjunction with these
     *  special keys (except meta with TTY keys) causes escape sequences
     *  to be sent, as per our normal "Function key" code.
     */

    if ( (  (IsTTYKey(keysym))	  && ! (state & (ShiftMask|ControlMask)) )
    ||   (  (IsKeypadKey(keysym)) && ! (state) && ! (IsPFKey(keysym))    )
    ||   (  (keysym == XK_Delete) && ! (state & ControlMask)             ) )
	/* XK_Delete is singled out from the rest of the TTY type keys
	 * because on HP keyboard it is gotten by Shift-Esc, which means
	 * that the shift bit will always be set in the 'state' variable.
         */
	{
	keysym &= 0x7F;				/* Convert to ASCII */
	if (state & Mod1Mask) keysym |= 0x80;	/* if Metakey...    */
	*ptr++ = keysym;
	*ptr = 0;
	return(1);
	}

	/* Remap some special keys */
#	define Remap(old,new)		    \
	    case old:  keysym = new; break;
	switch(keysym)
	    {
	    Remap(XK_KP_BackTab,    XK_KP_Tab);
	    Remap(XK_BackTab,	    XK_Tab);
	    Remap(XK_Print,	    XK_Execute);
	    Remap(XK_Reset,	    XK_Break);
	    Remap(XK_System,	    XK_F35);
	    Remap(XK_User,	    XK_F35);	/* Same key as XK_System */
	    Remap(XK_ClearLine,	    XK_F34);
	    Remap(XK_Clear,	    XK_F33);
	    Remap(XK_InsertLine,    XK_F32);
	    Remap(XK_DeleteLine,    XK_F31);
	    Remap(XK_InsertChar,    XK_F30);
	    Remap(XK_DeleteChar,    XK_F29);
	    Remap(XK_Muhenkan,	    XK_F28);	/* What the heck are these? */
	    Remap(XK_Henkan,	    XK_F27);
	    }
#	undef Remap
	       
    *ptr++ = 033;		/* Esc */
    *ptr++ = '[';
    *ptr++ = hexdigit[state];
    *ptr++ = hexdigit[ (keysym>>4) & 0xF ];
    *ptr++ = hexdigit[ keysym & 0xF ];
    *ptr   = 0;
    return(ptr-xbuf);
}
#endif

internal_socket_read(bufp, numchars)
    register unsigned char *bufp;
    register int numchars;
    {
    /* Number of keyboard chars we have produced so far.  */
    int count = 0;
    int nbytes, rows, cols;
    char mapping_buf[20];

	...
	...
	...

	case LeaveNotify:
	    CursorToggle();
	    CursorOutline = 1;
	    CursorToggle();
	    break;

#ifdef HPUX
/*
 * 01 Nov 89		Mark Lanzo
 *	Complete replacement of KeyPress code for HP9000s300/HPUX.
 *	Ideal treatment would be to have XLookupString do our work
 *	for us, plus elisp hooks for changing things.  Here though
 * 	I'm using the same methodology that original code used, but
 *	have modified how all special keys are handled.
 */

	case KeyPress:
	    nbytes = XLookupString(&event, mapping_buf, 20, &keysym, 0);
	    /* 
             * All special keys (and a few regular keys) have keysym
	     * codes which are more than 1 byte, so I check for nonzero
	     * bits beyond l.s.byte.  Regular ASCII characters for most
             * part have keysyms which match, so if upper bytes of keysym
	     * are clear I just accept them as they are (including
	     * meta characters which I don't feel should be treated
             * specially).
	     */

	    if (IsModifierKey(keysym)) break;
    
	    /* 
             * Discard XLookupString's result if special key pressed.
	     * In effect, we use our own LookupString fcn in this case,
	     * although we accept XLookupString's value for keysym.
	     */
	    if (keysym & ~0xFF)		
		nbytes = TranslateKey(keysym, mapping_buf, event.xkey.state);
	    else if ( (nbytes==1) && (event.xkey.state & Mod1Mask) )
		*mapping_buf |= 0x80;

	    /* We now return you to your regularly scheduled code ... :-) */

	    if (nbytes && (nbytes < numchars))
		    {
		    bcopy(mapping_buf, bufp, nbytes);
		    bufp += nbytes;
		    count += nbytes;
		    numchars -= nbytes;
		    }
	    break;
#else
	case KeyPress:
	    nbytes = XLookupString(&event, mapping_buf, 20, &keysym, 0);

	    /*
	     * Someday this will be unnecessary as we will be able to use
	     * XRebindKeysym so XLookupString will have already given us the
	     * string we want.
	     */
	    if (IsFunctionKey(keysym) ||
		IsMiscFunctionKey(keysym))
	        {
		strcpy(mapping_buf, "[");
		strcat(mapping_buf, stringFuncVal(keysym));
#ifdef sun
		strcat(mapping_buf, "z");
#else
		strcat(mapping_buf, "~");
#endif				/* sun */
		nbytes = strlen(mapping_buf);
	        }
	    else
	        {
		switch (keysym)
		    {
		case XK_Left:
		    strcpy(mapping_buf, "\033[D");
		    nbytes = 1;
		    break;
		case XK_Right:
		    strcpy(mapping_buf, "\006");
		    nbytes = 1;
		    break;
		case XK_Up:
		    strcpy(mapping_buf, "\020");
		    nbytes = 1;
		    break;
		case XK_Down:
		    strcpy(mapping_buf, "\033[B");
		    nbytes = 1;
		    break;
		    }
	        }
	    if (nbytes)
	        {
		if (event.xkey.state & Mod1Mask)
		    *mapping_buf |= METABIT;
		if (numchars - nbytes > 0)
		    {
		    bcopy(mapping_buf, bufp, nbytes);
		    bufp += nbytes;
		    count += nbytes;
		    numchars -= nbytes;
		    }
	        }
	    break;
#endif		/* HPUX */


	case ButtonPress:
	case ButtonRelease:
	    *bufp++ = (char) 'X' & 037;
	    ++count;

====== end of x11term.c.changes ======

             ==[ ml@eceris.ncsu.edu (128.109.135.109) ]==