The diffs below are patches to the MIT X11R5 server source that allow you to use the spacebar of your keyboard both as a spacebar and as a control key--if you press and release it you get a space, but if you press it and hold it down while pressing other keys, it acts like the Ctrl key. The patches are short; they are at the end of this message. The following paragraphs explain the patches. I've also placed this file on export.lcs.mit.edu. It is available for anonymous ftp as contrib/spacebar_hack.patches. BUT WHY? I've begun to have discomfort typing; it is worst twisting my wrist side to side to hit Return, Backspace, and Ctrl. I've been training myself to use Ctrl-H instead of Backspace and Ctrl-J instead of Return, but that still leaves the Ctrl key to contend with (I'm an emacs user, so I have to use Ctrl a lot). I've been researching alternative keyboards, and was impressed with the Kinesis keyboard which has a number of thumb buttons other than a space bar. By default these buttons are bound to Return, Backspace, and other frequently used keys that require stretching. I decided to see if I could simulate some of this in software. Now that I can use my spacebar as a Ctrl key, I rarely have to stretch my wrists from side to side. I'm releasing the patches for anyone else who finds them useful. HOW WELL DOES IT WORK? It reduces the discomfort I feel while typing, which was my primary goal. It took me only a day or so to train myself to use this new Ctrl key. A bigger problem, though is learning that when I want a Space character I've got to fully release the spacebar before typing the next character, or I'll get a control character instead. For example, in emacs, if I try to type the words "about the" too quickly I get "abotuhe"--the " t" turns into a Ctrl-t which transposes the last two charaters of "about", and then I type "he". It took me three or four days to get used to this, and I still do it sometime. I don't know if this is a typing habit that other people would have as well, or if it is just something about the way I type. This patch does seem to introduce a bug: with this server running, I can still select text in xterm, but once selected, the text does not remain highlighted. It can still be pasted, but you just don't get a visual indication of what text is selected in xterm. I've got no clue why this is happening, and haven't tried to track it down; the bug doesn't bother me enough to worry about it. I've only tried building this patch on a Sun 3 with SunOS 4.1. It is a very short patch, and does not touch any of the device-dependent code in the server, so I expect that it should be portable to most architectures. I hope that you find this patch useful, but please don't ask me to provide support for it--I'm not an X server guru, and have too many other things to be working on. The patch is provides as-is, of course, and without warranties of any kind. HOW DO I PATCH MY SERVER? To apply the diffs in this message to your server sources, run them through the patch program. Do something like this: cd /usr/X11R5/mit patch < keyhack.patches Only two files are changed: server/dix/events.c and server/os/utils.c If your build tree hasn't been cleaned up, you may be able to rebuild the server by doing the following: cd server make But probably you'll have to do something like: make Makefiles make includes cd server make If you can't figure this out, talk to whoever it was that built your server to begin with. Before installing the new server, you probably want to mv your old server to some other name. Don't overwrite it, in case you don't like using these patches. HOW DO I USE IT? The new server you have just built will behave just like your old server, unless you start it with the -keyhack option. The keyhack option takes two integer arguments: the keycode of the spacebar and the keycode of the ctrl key. There is probably some portable way to figure this out within the X server, but I didn't know what it was, and since I didn't want to hardcode these numbers into the patch, you have to supply them on the command line. You can figure out the keycodes for these keys with the following command which prints the keycodes for each key on your keyboard. xmodmap -pk | more So, for example, I start up my server with the command: Xsun -zaphod -keyhack 128 83 (-zaphod is a Sun-specific option; nothing to do with this patch.) I use xinit to start up my server, and since I don't want to type these options every time, I've created a ~/.xserverrc file with this line in it: /usr/bin/X11/Xsun -zaphod -keyhack 128 83 If you use xdm to start up your server, there is presumably a similar configuration file you can use, but I don't know what it is. Once you've started up the server, the spacebar hack will be active. If someone else sits down at your machine, however, you may want to turn it off for them. Part of this patch is to make the server respond to the SIGUSR2 signal. If you do: kill -USR2 you can turn this spacebar feature on and off. in the above refers to the process id of the server. You'll have to do a 'ps a' to find it, or a 'ps aux' if the server was started by xdm rather than by you. Note that since the -keyhack argument takes two arbitrary keycodes, you could use this patch to make your Return key act like Return and Meta, or whatever else. TODO The right way to have introduced this functionality would really have been as a server extension, probably integrated with the X Input extension in some way. Then there could be extended protocol requests to turn the feature on and off, instead of relying on a SIGUSR2, and there would presumably be a way to not have to pass keycodes on the command line. I'll leave these changes to someone who is more familiar with the X server internals than I am. Share and enjoy! David Flanagan david@ora.com Patches are below this line. ---------------------------------------------------------------------- *** server/dix/events.c.orig Wed Jun 2 22:27:40 1993 --- server/dix/events.c Sun Jul 4 18:42:53 1993 *************** *** 1813,1818 **** --- 1813,1822 ---- } } + int hack_space_keycode; /* set from the command line */ + int hack_ctrl_keycode; + int keyhack_on; + void ProcessKeyboardEvent (xE, keybd, count) register xEvent *xE; *************** *** 1827,1832 **** --- 1831,1838 ---- GrabPtr grab = keybd->grab; Bool deactivateGrab = FALSE; register KeyClassPtr keyc = keybd->key; + static int state; /* state for a simple FSM */ + int oldkey; if (!syncEvents.playingEvents) NoticeTime(xE) *************** *** 1841,1846 **** --- 1847,1901 ---- switch (xE->u.u.type) { case KeyPress: + if (keyhack_on) { + switch (state) { + case 0: + /* eat space down events and go to state 1. + * process all other key presses normally. + * If Ctrl is already down, treat space normally. + * this is a special case for Ctrl-Space, used in emacs. + */ + if ((key == hack_space_keycode) && + !(keyc->state & ControlMask)) + { + state = 1; + return; + } + break; + + case 1: + /* send a Ctrl-down event, go to state 2 and + * fall through to the next case. Set state + * to a meaningless value while recursing so we + * don't get any special processing for the + * synthesized events. + */ + state = -1; + oldkey = xE->u.u.detail; + xE->u.u.detail = hack_ctrl_keycode; + ProcessKeyboardEvent(xE, keybd, count); + xE->u.u.detail = oldkey; + state = 2; + + case 2: + /* + * pass any key down event through, but set the Ctrl + * modifier on it. We can set the Ctrl modifier by just + * recomputing the state field of the event as we did + * above. This is in fact more general, and will work + * with other modifier keys as well. This works because + * when we sent the fake Ctrl down event above, the + * recursive call to ProcessKeyboardEvent will change + * keyc->state. + * The only time we leave this state is on a key up + * event below. + */ + xE->u.keyButtonPointer.state = + (keyc->state | inputInfo.pointer->button->state); + break; + } + } + if (*kptr & bit) /* allow ddx to generate multiple downs */ { if (!modifiers) *************** *** 1872,1877 **** --- 1927,1971 ---- } break; case KeyRelease: + if (keyhack_on) { + switch (state) { + case 0: + /* key releases are handled normally in this state */ + break; + case 1: + /* + * if we get a space up before any other key down + * keystroke, then we send space up, down and return to + * state 0. Set state to a meaningless value while + * recursing so we don't process our own fake events. + */ + if (key == hack_space_keycode) { + state = -1; + xE->u.u.type = KeyPress; + ProcessKeyboardEvent(xE, keybd, count); + xE->u.u.type = KeyRelease; + ProcessKeyboardEvent(xE, keybd, count); + state = 0; + return; + } + break; + case 2: + /* + * if this is a space up, convert it to a Ctrl up, + * send it, and revert to state 0. Otherwise, just + * send it as is and stay in this state. + */ + if (key == hack_space_keycode) { + state = -1; + xE->u.u.detail = hack_ctrl_keycode; + ProcessKeyboardEvent(xE, keybd, count); + state = 0; + return; + } + break; + } + } + if (!(*kptr & bit)) /* guard against duplicates */ return; inputInfo.pointer->valuator->motionHintWindow = NullWindow; *** server/os/utils.c.orig Thu Jun 3 14:28:34 1993 --- server/os/utils.c Thu Jun 3 12:34:53 1993 *************** *** 240,245 **** --- 240,252 ---- ddxUseMsg(); } + SIGVAL + hacktoggle() + { + extern int keyhack_on; + keyhack_on = !keyhack_on; + } + /* * This function parses the command line. Handles device-independent fields * and allows ddx to handle additional fields. It is not allowed to modify *************** *** 521,526 **** --- 528,550 ---- SyncOn++; } #endif + else if (strcmp(argv[i], "-keyhack") == 0) { + extern int hack_space_keycode; + extern int hack_ctrl_keycode; + extern int keyhack_on; + + if (i < argc-1) { + hack_space_keycode = atoi(argv[++i]); + hack_ctrl_keycode = atoi(argv[++i]); + keyhack_on = 1; + signal(SIGUSR2, hacktoggle); + } + else { + fprintf(stderr, + "-keyhack argument needs two integer values.\n"); + keyhack_on = 0; + } + } else { UseMsg();