ford@kenobi.UUCP (Mike Ditto) (10/26/87)
Well, thanks to Paul Fox at AT&T for pointing me in the right direction (or at the right include files). I have used the information in his posting (which was paraphrased from <sys/kbd.h>) to write the much-needed keyboard re-mapping program for the Unix PC. The command is called 'keyfix' and it lets you examine or modify the definition of any code-generating key on the console keyboard (it will not affect the shift, control, or lock keys them- selves). The usage is: keyfix <keynum> [ <norm> <shift> <ctrl> <flags> ] All arguments are numbers which are decimal unless preceded by 0 for octal or 0x for hexadecimal. If only <keynum> is given then the current definition of that keycode is listed but not changed. keynum: The hardware keycode of the key you are interested in. You can find these listed in the comments in /usr/include/sys/kbd.h. norm: The ascii code this key should generate if pressed alone. shift: The ascii code this key should generate if pressed with SHIFT held. control:The ascii code this key should generate if pressed with CONTROL held. flags: The flags for this keycode, as defined in <sys/kbd.h>. Note that the 'ascii codes' are 16-bit numbers. The low-order byte (last two digits) are the actual code. If the high-order byte is non-zero, it is used as an index into the kprefix[] table which you can see in <sys/kbd.h>. This is how one key (like HOME) can generate a multi-byte sequence (like ^[[H). For example, suppose you want what is now the 'BREAK/RESET' key to be RUBOUT (a.k.a. DEL) instead. You grep for BREAK in /usr/include/sys/kbd.h and see that it is keycode 0x25. So you type "keyfix 0x25" to see what it's current setting is: $ keyfix 0x25 Old keymap[0x25] = 0x00ff,0x00ff,0x00ff, 0 $ You see that it is currently set to generate 0x00ff regardless of whether shift or control are pressed. Apparrently, 0xff is what 'BREAK/RESET' returns by default. Now you want it to be RUBOUT, again regardless of the state of shift and control. So you type: $ keyfix 0x25 0x7f 0x7f 0x7f 1 Old keymap[0x25] = 0x00ff,0x00ff,0x00ff, 0 New keymap[0x25] = 0x007f,0x007f,0x007f, 1 $ Notice that the <flags> argument (the last one) was given as 1 instead of 0, which it had been before. In <sys/kbd.h> REPT is defined as 0x1, meaning that if that bit (bit 0, 0x0001) is set in the flags then the corresponding key will repeat if held down. The other flags, CAPLCK (0x2) and NUMLCK (0x4) determine whether the 'lock' keys will affect them. You can add any or all of the above flags together and use the result as the <flags> argument to keyfix. Here are the keyfixes that I recommend that everyone use: keyfix 0x36 0x36 0x5e 0x1e 1 # Make control-^ work keyfix 0x2d 0x2d 0x5f 0x1f 1 # Make control-_ work keyfix 0x20 0x20 0x20 0x00 1 # Make control-space be a NUL keyfix 0x25 0x7f 0x7f 0x7f 1 # Make BREAK/RESET be RUBOUT keyfix 0x1b 0x1b 0x7f 0xff 1 # Make control-escape be BREAK/RESET # (just in case it is ever needed) Note that you must be super-user in order to run keyfix. This is because it writes to internal data structures in the kernel, and because it has a global effect on the system (its effects last until the machine is booted). I suggest that your favorite keyfix commands be run after each reboot, perhaps in /etc/rc, or in a file in the /etc/daemons directory. If you really think you want to be running it all the time, you could make it set-uid to root. There is one peculiarity in the process of changing these keymaps. If you have CAPCTRL then you have a complete replacement for the normal keyboard driver and its tables. Therefore, modifying the 'keymap' table in the kernel has no effect. Don't worry, though. keyfix is smart enough to check if you have the CAPCTRL driver in the /etc/lddrv directory and use its table if it is there. Otherwise the /unix table is used. The only real problem with this is that it is possible for the driver to exist in /etc/lddrv but not be loaded into the kernel. This will only happen if you PARTIALLY de-installed CAPCTRL by some non-standard method (like typing "lddrv -d kbd"). If you have installed CAPCTRL and then removed it, make sure you remove or rename "/etc/lddrv/kbd". Thanks again to Paul Fox for contributing to this newsgroup. Now if he could only get me information (source?) for changing the extra control key into a meta-key. Then I could actually start doing work on the console instead of from the Amiga on tty000. Well, here's the source to keyfix... If somebody needs executables let me know. -=] Ford [=- "GNU does not eliminate (In Real Life: Michael Ditto) all the world's problems, ford%kenobi@crash.CTS.COM only some of them." -rms ...!crash!kenobi!ford -------------------------------- cut here -------------------------------- /************************************************************ * * This program was written by me, Mike "Ford" Ditto, and * I hereby release it into the public domain in the interest * of promoting the development of free, quality software * for the hackers and users of the world. * * Feel free to use, copy, modify, improve, and redistribute * this program, but keep in mind the spirit of this * contribution; always provide source, and always allow * free redistribution (shareware is fine with me). If * you use a significant part of this code in a program of * yours, I would appreciate being given the appropriate * amount of credit. * -=] Ford [=- * ************************************************************/ #include <stdio.h> #include <fcntl.h> #include <ctype.h> #include <errno.h> #include <pwd.h> #include <grp.h> #include <sys/types.h> #include <sys/kbd.h> #include <nlist.h> extern long lseek(), strtol(); extern void perror(), exit(); void kcopy(), kwrite(); char *progname; #define ldkeymapaddr (myldsyms[0].n_value) struct nlist myldsyms[] = { { "ldkeymap", }, { (char *)0, }, }; #define keymapaddr (mysyms[0].n_value) struct nlist mysyms[] = { { "keymap", }, { (char *)0, }, }; char buf[BUFSIZ]; int kmem; int keynum, norm, shift, ctrl, flags; void usage() { fprintf(stderr, "usage: %s <keynum> [ <norm> <shift> <ctrl> <flags> ]\n", progname); fprintf(stderr, " Numbers are decimal unless preceded by 0 or 0x\n"); exit(-1); } main(argc, argv) int argc; char *argv[]; { progname = *argv; setup(); if (argc != 2 && argc != 6) usage(); keynum = numcvt(argv[1]); if (keynum<0 || keynum>=0x80) { fprintf(stderr, "%s: invalid keynum 0x%02x\n", progname, keynum); exit(-2); } if (argc>2) { norm = numcvt(argv[2]); shift = numcvt(argv[3]); ctrl = numcvt(argv[4]); flags = numcvt(argv[5]); } return keyfix(); } int numcvt(str) char *str; { long value; char *ptr; value = strtol(str, &ptr, 0); if (*ptr) { fprintf(stderr, "%s: invalid number `%s'\n", progname, str); usage(); } return value; } /* one-time setup of main data structures from the kernel */ setup() { if ( (kmem=open("/dev/kmem", O_RDWR)) < 0 ) { sprintf(buf, "%s: can't open /dev/kmem", progname); perror(buf); exit(1); } if (nlist("/etc/lddrv/kbd", myldsyms)) { if (nlist("/unix", mysyms)) { sprintf(buf, "%s: can't nlist /unix", progname); perror(buf); exit(1); } #ifdef DEBUG fputs("Using keymap from /unix\n", stderr); #endif DEBUG } else { #ifdef DEBUG fputs("Using ldkeymap from /etc/lddrv/kbd\n", stderr); #endif DEBUG keymapaddr = ldkeymapaddr; } #ifdef DEBUG fprintf(stderr, "keymap: 0x%08lx\n", keymapaddr); #endif DEBUG } /* copy bytes from kernel address space to this process */ void kcopy(caddr, kaddr, nbytes) char *caddr; long kaddr; long nbytes; { if ( lseek(kmem, kaddr, 0)<0L || read(kmem, caddr, (unsigned)nbytes) != nbytes ) { sprintf(buf, "%s: can't read /dev/kmem", progname); perror(buf); exit(1); } } /* write bytes from this process' address space to the kernel's */ void kwrite(kaddr, caddr, nbytes) long kaddr; char *caddr; long nbytes; { #ifdef DEBUG fprintf(stderr, "Writing %ld bytes to kernel address 0x%08lx\n", nbytes, kaddr); #endif if ( lseek(kmem, kaddr, 0)<0L || write(kmem, caddr, (unsigned)nbytes) != nbytes ) { sprintf(buf, "%s: can't write /dev/kmem", progname); perror(buf); exit(1); } } /* change the keymap of key `keynum' */ keyfix() { struct keydef mydef; kcopy((char *)&mydef, (long)&((struct keydef *)keymapaddr)[keynum], (long)sizeof mydef); printf("Old keymap[0x%02x] = 0x%04x,0x%04x,0x%04x,%2d\n", keynum, mydef.kt_codes[0], mydef.kt_codes[1], mydef.kt_codes[2], mydef.kt_flags); if (!(norm||shift||ctrl||flags)) return 0; mydef.kt_codes[0] = norm; mydef.kt_codes[1] = shift; mydef.kt_codes[2] = ctrl; mydef.kt_flags = flags; printf("New keymap[0x%02x] = 0x%04x,0x%04x,0x%04x,%2d\n", keynum, mydef.kt_codes[0], mydef.kt_codes[1], mydef.kt_codes[2], mydef.kt_flags); kwrite((long)&((struct keydef *)keymapaddr)[keynum], (char *)&mydef, (long)sizeof mydef); return 0; }