summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--exec.c364
-rw-r--r--handle.c1348
-rw-r--r--pf.c118
-rw-r--r--swap.km11
-rw-r--r--wq.h143
-rw-r--r--xmodmap.c341
-rw-r--r--xmodmap.h48
-rw-r--r--xmodmap.man302
8 files changed, 2675 insertions, 0 deletions
diff --git a/exec.c b/exec.c
new file mode 100644
index 0000000..612ec85
--- /dev/null
+++ b/exec.c
@@ -0,0 +1,364 @@
+/* $Xorg: exec.c,v 1.4 2001/02/09 02:05:56 xorgcvs Exp $ */
+/*
+
+Copyright 1988, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+
+*/
+
+/*
+ * Copyright 1987 by Sun Microsystems, Inc. Mountain View, CA.
+ *
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright no-
+ * tice appear in all copies and that both that copyright no-
+ * tice and this permission notice appear in supporting docu-
+ * mentation, and that the name of Sun not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific prior written permission. Sun
+ * makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without any
+ * express or implied warranty.
+ *
+ * SUN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
+ * NESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SUN BE LI-
+ * ABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Jim Fulton, MIT X Consortium; derived from parts of the
+ * original xmodmap, written by David Rosenthal, of Sun Microsystems.
+ */
+
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <stdio.h>
+#include "xmodmap.h"
+#include "wq.h"
+
+static mapping_busy_key (timeout)
+ int timeout;
+{
+ int i;
+ unsigned char keymap[32];
+ static unsigned int masktable[8] = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+
+ XQueryKeymap (dpy, (char *) keymap);
+
+ fprintf (stderr,
+ "%s: please release the following keys within %d seconds:\n",
+ ProgramName, timeout);
+ for (i = 0; i < 256; i++) {
+ if (keymap[i >> 3] & masktable[i & 7]) {
+ KeySym ks = XKeycodeToKeysym (dpy, (KeyCode) i, 0);
+ char *cp = XKeysymToString (ks);
+ fprintf (stderr, " %s (keysym 0x%x, keycode %d)\n",
+ cp ? cp : "UNNAMED", ks, i);
+ }
+ }
+ sleep (timeout);
+ return;
+}
+
+static mapping_busy_pointer (timeout)
+ int timeout;
+{
+ int i;
+ Window root, child; /* dummy variables */
+ int rx, ry, wx, wy; /* dummy variables */
+ unsigned int mask;
+ static unsigned int masks[5] = {
+ Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask };
+
+ if (!XQueryPointer (dpy, RootWindow(dpy,DefaultScreen(dpy)),
+ &root, &child, &rx, &ry, &wx, &wy, &mask))
+ mask = 0;
+
+ fprintf (stderr,
+ "%s: please release the following buttons within %d seconds:\n",
+ ProgramName, timeout);
+ for (i = 0; i < 5; i++) {
+ if (mask & masks[i])
+ fprintf (stderr, " Button%d\n", i+1);
+ }
+ sleep (timeout);
+ return;
+}
+
+
+/*
+ * UpdateModifierMapping - this sends the modifier map to the server
+ * and deals with retransmissions due to the keyboard being busy.
+ */
+
+int UpdateModifierMapping (map)
+ XModifierKeymap *map;
+{
+ int retries, timeout;
+
+ for (retries = 5, timeout = 2; retries > 0; retries--, timeout *= 2) {
+ int result;
+
+ result = XSetModifierMapping (dpy, map);
+ switch (result) {
+ case MappingSuccess: /* Success */
+ return (0);
+ case MappingBusy: /* Busy */
+ mapping_busy_key (timeout);
+ continue;
+ case MappingFailed:
+ fprintf (stderr, "%s: bad set modifier mapping.\n",
+ ProgramName);
+ return (-1);
+ default:
+ fprintf (stderr, "%s: bad return %d from XSetModifierMapping\n",
+ ProgramName, result);
+ return (-1);
+ }
+ }
+ fprintf (stderr,
+ "%s: unable to set modifier mapping, keyboard problem\n",
+ ProgramName);
+ return (-1);
+}
+
+
+/*
+ * AddModifier - this adds a keycode to the modifier list
+ */
+
+int AddModifier (mapp, keycode, modifier)
+ XModifierKeymap **mapp;
+ KeyCode keycode;
+ int modifier;
+{
+ if (keycode) {
+ *mapp = XInsertModifiermapEntry (*mapp, keycode, modifier);
+ return (0);
+ } else {
+ return (-1);
+ }
+ /*NOTREACHED*/
+}
+
+
+/*
+ * DeleteModifier - this removes a keycode from the modifier list
+ */
+
+int RemoveModifier (mapp, keycode, modifier)
+ XModifierKeymap **mapp;
+ KeyCode keycode;
+ int modifier;
+{
+ if (keycode) {
+ *mapp = XDeleteModifiermapEntry (*mapp, keycode, modifier);
+ return (0);
+ } else {
+ return (-1);
+ }
+ /*NOTREACHED*/
+}
+
+
+/*
+ * ClearModifier - this removes all entries from the modifier list
+ */
+
+int ClearModifier (mapp, modifier)
+ XModifierKeymap **mapp;
+ int modifier;
+{
+ int i;
+ XModifierKeymap *map = *mapp;
+ KeyCode *kcp;
+
+ kcp = &map->modifiermap[modifier * map->max_keypermod];
+ for (i = 0; i < map->max_keypermod; i++) {
+ *kcp++ = (KeyCode) 0;
+ }
+ return (0);
+}
+
+
+/*
+ * print the contents of the map
+ */
+
+PrintModifierMapping (map, fp)
+ XModifierKeymap *map;
+ FILE *fp;
+{
+ int i, k = 0;
+
+ fprintf (fp,
+ "%s: up to %d keys per modifier, (keycodes in parentheses):\n\n",
+ ProgramName, map->max_keypermod);
+ for (i = 0; i < 8; i++) {
+ int j;
+
+ fprintf(fp, "%-10s", modifier_table[i].name);
+ for (j = 0; j < map->max_keypermod; j++) {
+ if (map->modifiermap[k]) {
+ KeySym ks = XKeycodeToKeysym(dpy, map->modifiermap[k], 0);
+ char *nm = XKeysymToString(ks);
+
+ fprintf (fp, "%s %s (0x%0x)", (j > 0 ? "," : ""),
+ (nm ? nm : "BadKey"), map->modifiermap[k]);
+ }
+ k++;
+ }
+ fprintf(fp, "\n");
+ }
+ fprintf (fp, "\n");
+ return;
+}
+
+
+PrintKeyTable (exprs, fp)
+ Bool exprs;
+ FILE *fp;
+{
+ int i;
+ int min_keycode, max_keycode, keysyms_per_keycode;
+ KeySym *keymap, *origkeymap;
+
+ XDisplayKeycodes (dpy, &min_keycode, &max_keycode);
+ origkeymap = XGetKeyboardMapping (dpy, min_keycode,
+ (max_keycode - min_keycode + 1),
+ &keysyms_per_keycode);
+
+ if (!origkeymap) {
+ fprintf (stderr, "%s: unable to get keyboard mapping table.\n",
+ ProgramName);
+ return;
+ }
+ if (!exprs) {
+ fprintf (fp,
+ "There are %d KeySyms per KeyCode; KeyCodes range from %d to %d.\n\n",
+ keysyms_per_keycode, min_keycode, max_keycode);
+ fprintf (fp, " KeyCode\tKeysym (Keysym)\t...\n");
+ fprintf (fp, " Value \tValue (Name) \t...\n\n");
+ }
+ keymap = origkeymap;
+ for (i = min_keycode; i <= max_keycode; i++) {
+ int j, max;
+
+ if (exprs)
+ fprintf(fp, "keycode %3d =", i);
+ else
+ fprintf(fp, " %3d \t", i);
+ max = keysyms_per_keycode - 1;
+ while ((max >= 0) && (keymap[max] == NoSymbol))
+ max--;
+ for (j = 0; j <= max; j++) {
+ register KeySym ks = keymap[j];
+ char *s;
+ if (ks != NoSymbol)
+ s = XKeysymToString (ks);
+ else
+ s = "NoSymbol";
+ if (!exprs)
+ fprintf (fp, "0x%04x (%s)\t", ks, s ? s : "no name");
+ else if (s)
+ fprintf (fp, " %s", s);
+ else
+ fprintf (fp, " 0x%04x", ks);
+ }
+ keymap += keysyms_per_keycode;
+ fprintf (fp, "\n");
+ }
+
+ XFree ((char *) origkeymap);
+ return;
+}
+
+
+PrintPointerMap (fp)
+ FILE *fp;
+{
+ unsigned char pmap[256]; /* there are 8 bits of buttons */
+ int count, i;
+
+ count = XGetPointerMapping (dpy, pmap, 256);
+
+ fprintf (fp, "There are %d pointer buttons defined.\n\n", count);
+ fprintf (fp, " Physical Button\n");
+ fprintf (fp, " Button Code\n");
+/* " ### ###\n" */
+ for (i = 0; i < count; i++) {
+ fprintf (fp, " %3u %3u\n",
+ i+1, (unsigned int) pmap[i]);
+ }
+ fprintf (fp, "\n");
+ return;
+}
+
+
+/*
+ * SetPointerMap - set the pointer map
+ */
+
+int SetPointerMap (map, n)
+ unsigned char *map;
+ int n;
+{
+ unsigned char defmap[MAXBUTTONCODES];
+ int j;
+ int retries, timeout;
+
+ if (n == 0) { /* reset to default */
+ n = XGetPointerMapping (dpy, defmap, MAXBUTTONCODES);
+ for (j = 0; j < n; j++) defmap[j] = (unsigned char) (j + 1);
+ map = defmap;
+ }
+
+ for (retries = 5, timeout = 2; retries > 0; retries--, timeout *= 2) {
+ int result;
+
+ switch (result = XSetPointerMapping (dpy, map, n)) {
+ case MappingSuccess:
+ return 0;
+ case MappingBusy:
+ mapping_busy_pointer (timeout);
+ continue;
+ case MappingFailed:
+ fprintf (stderr, "%s: bad pointer mapping\n", ProgramName);
+ return -1;
+ default:
+ fprintf (stderr, "%s: bad return %d from XSetPointerMapping\n",
+ ProgramName, result);
+ return -1;
+ }
+ }
+ fprintf (stderr, "%s: unable to set pointer mapping\n", ProgramName);
+ return -1;
+}
diff --git a/handle.c b/handle.c
new file mode 100644
index 0000000..df2e82a
--- /dev/null
+++ b/handle.c
@@ -0,0 +1,1348 @@
+/* $Xorg: handle.c,v 1.6 2001/02/09 02:05:56 xorgcvs Exp $ */
+/*
+
+Copyright 1988, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+
+*/
+
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "xmodmap.h"
+#include "wq.h"
+
+static XModifierKeymap *map = NULL;
+
+
+/*
+ * The routines in this file manipulate a queue of intructions. Instead of
+ * executing each line as it is entered, we build up a list of actions to
+ * take and execute them all at the end. This allows us to find all errors
+ * at once, and to preserve the context in which we are looking up keysyms.
+ */
+
+struct wq work_queue = {NULL, NULL};
+
+
+/*
+ * common utility routines
+ */
+
+KeyCode *KeysymToKeycodes(dpy, keysym, pnum_kcs)
+ Display *dpy;
+ KeySym keysym;
+ int *pnum_kcs;
+{
+ KeyCode *kcs = NULL;
+ int i, j;
+
+ *pnum_kcs = 0;
+ for (i = min_keycode; i <= max_keycode; i++) {
+ for (j = 0; j < 8; j++) {
+ if (XKeycodeToKeysym(dpy, (KeyCode) i, j) == keysym) {
+ if (!kcs)
+ kcs = (KeyCode *)malloc(sizeof(KeyCode));
+ else
+ kcs = (KeyCode *)realloc((char *)kcs,
+ sizeof(KeyCode) * (*pnum_kcs + 1));
+ kcs[*pnum_kcs] = i;
+ *pnum_kcs += 1;
+ break;
+ }
+ }
+ }
+ return kcs;
+}
+
+char *copy_to_scratch (s, len)
+ char *s;
+ int len;
+{
+ static char *buf = NULL;
+ static int buflen = 0;
+
+ if (len > buflen) {
+ if (buf) free (buf);
+ buflen = (len < 40) ? 80 : (len * 2);
+ buf = (char *) malloc (buflen+1);
+ }
+ if (len > 0)
+ strncpy (buf, s, len);
+ else
+ len = 0;
+
+ buf[len] = '\0';
+ return (buf);
+}
+
+static badheader ()
+{
+ fprintf (stderr, "%s: %s:%d: bad ", ProgramName, inputFilename, lineno);
+ parse_errors++;
+}
+
+#define badmsg(what,arg) { badheader(); fprintf (stderr, what, arg); \
+ putc ('\n', stderr); }
+
+#define badmsgn(what,s,len) badmsg (what, copy_to_scratch (s, len))
+
+void initialize_map ()
+{
+ map = XGetModifierMapping (dpy);
+ return;
+}
+
+static void do_keycode(), do_keysym(), finish_keycodes();
+static void do_add(), do_remove(), do_clear(), do_pointer();
+static int get_keysym_list();
+
+int skip_word(), skip_space(), skip_chars();
+
+static struct dt {
+ char *command; /* name of input command */
+ int length; /* length of command */
+ void (*proc)(); /* handler */
+} dispatch_table[] = {
+ { "keycode", 7, do_keycode },
+ { "keysym", 6, do_keysym },
+ { "add", 3, do_add },
+ { "remove", 6, do_remove },
+ { "clear", 5, do_clear },
+ { "pointer", 7, do_pointer },
+ { NULL, 0, NULL }};
+
+/*
+ * handle_line - this routine parses the input line (which has had all leading
+ * and trailing whitespace removed) and builds up the work queue.
+ */
+
+void handle_line (line, len)
+ char *line; /* string to parse */
+ int len; /* length of line */
+{
+ int n;
+ struct dt *dtp;
+
+ n = skip_chars (line, len);
+ if (n < 0) {
+ badmsg ("input line '%s'", line);
+ return;
+ }
+
+ for (dtp = dispatch_table; dtp->command != NULL; dtp++) {
+ if (n == dtp->length &&
+ strncmp (line, dtp->command, dtp->length) == 0) {
+
+ n += skip_space (line+n, len-n);
+ line += n, len -= n;
+
+ (*(dtp->proc)) (line, len);
+ return;
+ }
+ }
+
+ fprintf (stderr, "%s: unknown command on line %s:%d\n",
+ ProgramName, inputFilename, lineno);
+ parse_errors++;
+}
+
+/*
+ * the following routines are useful for parsing
+ */
+
+int skip_word (s, len)
+ register char *s;
+ register int len;
+{
+ register int n;
+
+ n = skip_chars (s, len);
+ return (n + skip_space (s+n, len-n));
+}
+
+int skip_chars (s, len)
+ register char *s;
+ register int len;
+{
+ register int i;
+
+ if (len <= 0 || !s || *s == '\0') return (0);
+
+ for (i = 0; i < len; i++) {
+ if (isspace(s[i])) break;
+ }
+ return (i);
+}
+
+int skip_space (s, len)
+ register char *s;
+ register int len;
+{
+ register int i;
+
+ if (len <= 0 || !s || *s == '\0') return (0);
+
+ for (i = 0; i < len; i++) {
+ if (!s[i] || !isspace(s[i])) break;
+ }
+ return (i);
+}
+
+
+int skip_until_char (s, len, c)
+ register char *s;
+ register int len;
+ register char c;
+{
+ register int i;
+
+ for (i = 0; i < len; i++) {
+ if (s[i] == c) break;
+ }
+ return (i);
+}
+
+int skip_until_chars (s, len, cs, cslen)
+ char *s;
+ int len;
+ register char *cs;
+ register int cslen;
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ register int j;
+ register char c = s[i];
+
+ for (j = 0; j < cslen; j++) {
+ if (c == cs[j]) goto done;
+ }
+ }
+ done:
+ return (i);
+}
+
+/*
+ * The action routines.
+ *
+ * This is where the real work gets done. Each routine is responsible for
+ * parsing its input (note that the command keyword has been stripped off)
+ * and adding to the work queue. They are also in charge of outputting
+ * error messages and returning non-zero if there is a problem.
+ *
+ * The following global variables are available:
+ * dpy the display descriptor
+ * work_queue linked list of opcodes
+ * inputFilename name of the file being processed
+ * lineno line number of current line in input file
+ */
+
+add_to_work_queue (p) /* this can become a macro someday */
+ union op *p;
+{
+ if (work_queue.head == NULL) { /* nothing on the list */
+ work_queue.head = work_queue.tail = p; /* head, tail, no prev */
+ } else {
+ work_queue.tail->generic.next = p; /* head okay, prev */
+ work_queue.tail = p; /* tail */
+ }
+ p->generic.next = NULL;
+
+ if (verbose) {
+ print_opcode (p);
+ }
+ return;
+}
+
+char *copystring (s, len)
+ char *s;
+ int len;
+{
+ char *retval;
+
+ retval = (char *) malloc (len+1);
+ if (retval) {
+ strncpy (retval, s, len);
+ retval[len] = '\0';
+ }
+ return (retval);
+}
+
+static Bool parse_number (str, val)
+ char *str;
+ unsigned long *val;
+{
+ char *fmt = "%ld";
+
+ if (*str == '0') {
+ str++;
+ fmt = "%lo";
+ if (*str == '\0') {
+ *val = 0;
+ return 1;
+ }
+ if (*str == 'x' || *str == 'X') {
+ str++;
+ fmt = "%lx";
+ }
+ }
+ return (sscanf (str, fmt, val) == 1);
+}
+
+static Bool parse_keysym (line, n, name, keysym)
+ char *line;
+ int n;
+ char **name;
+ KeySym *keysym;
+{
+ *name = copy_to_scratch (line, n);
+ if (!strcmp(*name, "NoSymbol")) {
+ *keysym = NoSymbol;
+ return (True);
+ }
+ *keysym = XStringToKeysym (*name);
+ if (*keysym == NoSymbol && '0' <= **name && **name <= '9')
+ return parse_number(*name, keysym);
+ return (*keysym != NoSymbol);
+}
+
+/*
+ * do_keycode - parse off lines of the form
+ *
+ * "keycode" number "=" [keysym ...]
+ * ^
+ *
+ * where number is in decimal, hex, or octal. Any number of keysyms may be
+ * listed.
+ */
+
+static void do_keycode (line, len)
+ char *line;
+ int len;
+{
+ int dummy;
+ char *fmt = "%d";
+ KeyCode keycode;
+
+ if (len < 3 || !line || *line == '\0') { /* 5=a minimum */
+ badmsg ("keycode input line", NULL);
+ return;
+ }
+
+ if (!strncmp("any", line, 3)) {
+ keycode = 0;
+ len += 3;
+ } else {
+ if (*line == '0') line++, len--, fmt = "%o";
+ if (*line == 'x' || *line == 'X') line++, len--, fmt = "%x";
+
+ dummy = 0;
+ if (sscanf (line, fmt, &dummy) != 1 || dummy == 0) {
+ badmsg ("keycode value", NULL);
+ return;
+ }
+ keycode = (KeyCode) dummy;
+ if ((int)keycode < min_keycode || (int)keycode > max_keycode) {
+ badmsg ("keycode value (out of range)", NULL);
+ return;
+ }
+ }
+
+ finish_keycodes (line, len, &keycode, 1);
+}
+
+
+/*
+ * do_keysym - parse off lines of the form
+ *
+ * "keysym" keysym "=" [keysym ...]
+ * ^
+ *
+ * The left keysyms has to be checked for validity and evaluated.
+ */
+
+static void do_keysym (line, len)
+ char *line;
+ int len;
+{
+ int n;
+ KeyCode *keycodes;
+ KeySym keysym;
+ char *tmpname;
+
+ if (len < 3 || !line || *line == '\0') { /* a=b minimum */
+ badmsg ("keysym input line", NULL);
+ return;
+ }
+
+ n = skip_chars (line, len);
+ if (n < 1) {
+ badmsg ("target keysym name", NULL);
+ return;
+ }
+ if (!parse_keysym(line, n, &tmpname, &keysym)) {
+ badmsg ("keysym target key symbol '%s'", tmpname);
+ return;
+ }
+
+ keycodes = KeysymToKeycodes (dpy, keysym, &n);
+ if (n == 0) {
+ badmsg ("keysym target keysym '%s', no corresponding keycodes",
+ tmpname);
+ return;
+ }
+ if (verbose) {
+ int i;
+ printf ("! Keysym %s (0x%lx) corresponds to keycode(s)",
+ tmpname, (long) keysym);
+ for (i = 0; i < n; i++)
+ printf (" 0x%x", keycodes[i]);
+ printf("\n");
+ }
+
+ finish_keycodes (line, len, keycodes, n);
+}
+
+static void finish_keycodes (line, len, keycodes, count)
+ char *line;
+ int len;
+ KeyCode *keycodes;
+ int count;
+{
+ int n;
+ KeySym *kslist;
+ union op *uop;
+ struct op_keycode *opk;
+
+ n = skip_until_char (line, len, '=');
+ line += n, len -= n;
+
+ if (len < 1 || *line != '=') { /* = minimum */
+ badmsg ("keycode command (missing keysym list),", NULL);
+ return;
+ }
+ line++, len--; /* skip past the = */
+
+ n = skip_space (line, len);
+ line += n, len -= n;
+
+ /* allow empty list */
+ if (get_keysym_list (line, len, &n, &kslist) < 0)
+ return;
+
+ while (--count >= 0) {
+ uop = AllocStruct (union op);
+ if (!uop) {
+ badmsg ("attempt to allocate a %ld byte keycode opcode",
+ (long) sizeof (struct op_keycode));
+ return;
+ }
+ opk = &uop->keycode;
+
+ opk->type = doKeycode;
+ opk->target_keycode = keycodes[count];
+ opk->count = n;
+ opk->keysyms = kslist;
+
+ add_to_work_queue (uop);
+ }
+
+#ifdef later
+ /* make sure we handle any special keys */
+ check_special_keys (keycode, n, kslist);
+#endif
+}
+
+
+/*
+ * parse_modifier - convert a modifier string name to its index
+ */
+
+struct modtab modifier_table[] = { /* keep in order so it can be index */
+ { "shift", 5, 0 },
+ { "lock", 4, 1 },
+ { "control", 7, 2 },
+ { "mod1", 4, 3 },
+ { "mod2", 4, 4 },
+ { "mod3", 4, 5 },
+ { "mod4", 4, 6 },
+ { "mod5", 4, 7 },
+ { "ctrl", 4, 2 },
+ { NULL, 0, 0 }};
+
+static int parse_modifier (line, n)
+ register char *line;
+ register int n;
+{
+ register int i;
+ struct modtab *mt;
+
+ /* lowercase for comparison against table */
+ for (i = 0; i < n; i++) {
+ if (isupper (line[i])) line[i] = tolower (line[i]);
+ }
+
+ for (mt = modifier_table; mt->name; mt++) {
+ if (n == mt->length && strncmp (line, mt->name, n) == 0)
+ return (mt->value);
+ }
+ return (-1);
+}
+
+
+/*
+ * do_add - parse off lines of the form
+ *
+ * add MODIFIER = keysym ...
+ * ^
+ * where the MODIFIER is one of Shift, Lock, Control, Mod[1-5] where case
+ * is not important. There should also be an alias Ctrl for control.
+ */
+
+static void do_add (line, len)
+ char *line;
+ int len;
+{
+ int n;
+ int modifier;
+ KeySym *kslist;
+ union op *uop;
+ struct op_addmodifier *opam;
+
+ if (len < 6 || !line || *line == '\0') { /* Lock=a minimum */
+ badmsg ("add modifier input line", NULL);
+ return;
+ }
+
+ n = skip_chars (line, len);
+ if (n < 1) {
+ badmsg ("add modifier name %s", line);
+ return;
+ }
+
+ modifier = parse_modifier (line, n);
+ if (modifier < 0) {
+ badmsgn ("add modifier name '%s', not allowed", line, n);
+ return;
+ }
+
+ line += n, len -= n;
+ n = skip_until_char (line, len, '=');
+ if (n < 0) {
+ badmsg ("add modifier = keysym", NULL);
+ return;
+ }
+
+ n++; /* skip = */
+ n += skip_space (line+n, len-n);
+ line += n, len -= n;
+
+ if (get_keysym_list (line, len, &n, &kslist) < 0)
+ return;
+ if (n == 0) {
+ badmsg ("add modifier keysym list (empty)", NULL);
+ return;
+ }
+
+ uop = AllocStruct (union op);
+ if (!uop) {
+ badmsg ("attempt to allocate %ld byte addmodifier opcode",
+ (long) sizeof (struct op_addmodifier));
+ return;
+ }
+ opam = &uop->addmodifier;
+
+ opam->type = doAddModifier;
+ opam->modifier = modifier;
+ opam->count = n;
+ opam->keysyms = kslist;
+
+ add_to_work_queue (uop);
+}
+
+#ifdef AUTO_ADD_REMOVE
+/*
+ * make_add - stick a single add onto the queue
+ */
+static void make_add (modifier, keysym)
+ int modifier;
+ KeySym keysym;
+{
+ union op *uop;
+ struct op_addmodifier *opam;
+
+ uop = AllocStruct (union op);
+ if (!uop) {
+ badmsg ("attempt to allocate %ld byte addmodifier opcode",
+ (long) sizeof (struct op_addmodifier));
+ return;
+ }
+ opam = &uop->addmodifier;
+
+ opam->type = doAddModifier;
+ opam->modifier = modifier;
+ opam->count = 1;
+ opam->keysyms = (KeySym *) malloc (sizeof (KeySym));
+ if (!opam->keysyms) {
+ badmsg ("attempt to allocate %ld byte KeySym", (long) sizeof (KeySym));
+ free ((char *) opam);
+ return;
+ }
+ opam->keysyms[0] = keysym;
+
+ add_to_work_queue (uop);
+ return;
+}
+#endif /* AUTO_ADD_REMOVE */
+
+
+/*
+ * do_remove - parse off lines of the form
+ *
+ * remove MODIFIER = oldkeysym ...
+ * ^
+ * where the MODIFIER is one of Shift, Lock, Control, Mod[1-5] where case
+ * is not important. There should also be an alias Ctrl for control.
+ */
+
+static void do_remove (line, len)
+ char *line;
+ int len;
+{
+ int n;
+ int nc;
+ int i;
+ int tot;
+ int modifier;
+ KeySym *kslist;
+ KeyCode *kclist;
+ union op *uop;
+ struct op_removemodifier *oprm;
+
+ if (len < 6 || !line || *line == '\0') { /* Lock=a minimum */
+ badmsg ("remove modifier input line", NULL);
+ return;
+ }
+
+ n = skip_chars (line, len);
+ if (n < 1) {
+ badmsg ("remove modifier name %s", line);
+ return;
+ }
+
+ modifier = parse_modifier (line, n);
+ if (modifier < 0) {
+ badmsgn ("remove modifier name '%s', not allowed", line, n);
+ return;
+ }
+
+ line += n, len -= n;
+ n = skip_until_char (line, len, '=');
+ if (n < 0) {
+ badmsg ("remove modifier = keysym", NULL);
+ return;
+ }
+
+ n++;
+ n += skip_space (line+n, len-n);
+ line += n, len -= n;
+
+ if (get_keysym_list (line, len, &n, &kslist) < 0)
+ return;
+ if (n == 0) {
+ badmsg ("remove modifier keysym list (empty)", NULL);
+ return;
+ }
+
+ /*
+ * unlike the add command, we have to now evaluate the keysyms
+ */
+
+ kclist = (KeyCode *) malloc (n * sizeof (KeyCode));
+ if (!kclist) {
+ badmsg ("attempt to allocate %ld byte keycode list",
+ (long) (n * sizeof (KeyCode)));
+ free ((char *) kslist);
+ return;
+ }
+
+ tot = n;
+ nc = 0;
+ for (i = 0; i < n; i++) {
+ int num_kcs;
+ KeyCode *kcs;
+ kcs = KeysymToKeycodes (dpy, kslist[i], &num_kcs);
+ if (num_kcs == 0) {
+ char *tmpname = XKeysymToString (kslist[i]);
+ badmsg ("keysym in remove modifier list '%s', no corresponding keycodes",
+ tmpname ? tmpname : "?");
+ continue;
+ }
+ if (verbose) {
+ int j;
+ char *tmpname = XKeysymToString (kslist[i]);
+ printf ("! Keysym %s (0x%lx) corresponds to keycode(s)",
+ tmpname ? tmpname : "?", (long) kslist[i]);
+ for (j = 0; j < num_kcs; j++)
+ printf(" 0x%x", kcs[j]);
+ printf("\n");
+ }
+ if (nc + num_kcs > tot) {
+ tot = nc + num_kcs;
+ kclist = (KeyCode *)realloc((char *)kclist, tot * sizeof(KeyCode));
+ if (!kclist) {
+ badmsg ("attempt to allocate %ld byte keycode list",
+ (long) (tot * sizeof (KeyCode)));
+ free ((char *) kslist);
+ return;
+ }
+ }
+ while (--num_kcs >= 0)
+ kclist[nc++] = *kcs++; /* okay, add it to list */
+ }
+
+ free ((char *) kslist); /* all done with it */
+
+ uop = AllocStruct (union op);
+ if (!uop) {
+ badmsg ("attempt to allocate %ld byte removemodifier opcode",
+ (long) sizeof (struct op_removemodifier));
+ return;
+ }
+ oprm = &uop->removemodifier;
+
+ oprm->type = doRemoveModifier;
+ oprm->modifier = modifier;
+ oprm->count = nc;
+ oprm->keycodes = kclist;
+
+ add_to_work_queue (uop);
+}
+
+#ifdef AUTO_ADD_REMOVE
+/*
+ * make_remove - stick a single remove onto the queue
+ */
+static void make_remove (modifier, keycode)
+ int modifier;
+ KeyCode keycode;
+{
+ union op *uop;
+ struct op_removemodifier *oprm;
+
+ uop = AllocStruct (union op);
+ if (!uop) {
+ badmsg ("attempt to allocate %ld byte removemodifier opcode",
+ (long) sizeof (struct op_removemodifier));
+ return;
+ }
+ oprm = &uop->removemodifier;
+
+ oprm->type = doRemoveModifier;
+ oprm->modifier = modifier;
+ oprm->count = 1;
+ oprm->keycodes = (KeyCode *) malloc (sizeof (KeyCode));
+ if (!oprm->keycodes) {
+ badmsg ("attempt to allocate %ld byte KeyCode",
+ (long) sizeof (KeyCode));
+ free ((char *) oprm);
+ return;
+ }
+ oprm->keycodes[0] = keycode;
+
+ add_to_work_queue (uop);
+ return;
+}
+#endif /* AUTO_ADD_REMOVE */
+
+
+/*
+ * do_clear - parse off lines of the form
+ *
+ * clear MODIFIER
+ * ^
+ */
+
+static void do_clear (line, len)
+ char *line;
+ int len;
+{
+ int n;
+ int modifier;
+ union op *uop;
+ struct op_clearmodifier *opcm;
+
+ if (len < 4 || !line || *line == '\0') { /* Lock minimum */
+ badmsg ("clear modifier input line", NULL);
+ return;
+ }
+
+ n = skip_chars (line, len);
+
+ modifier = parse_modifier (line, n);
+ if (modifier < 0) {
+ badmsgn ("clear modifier name '%s'", line, n);
+ return;
+ }
+ n += skip_space (line+n, len-n);
+ if (n != len) {
+ badmsgn ("extra argument '%s' to clear modifier", line+n, len-n);
+ /* okay to continue */
+ }
+
+ uop = AllocStruct (union op);
+ if (!uop) {
+ badmsg ("attempt to allocate %d byte clearmodifier opcode",
+ (long) sizeof (struct op_clearmodifier));
+ return;
+ }
+ opcm = &uop->clearmodifier;
+
+ opcm->type = doClearModifier;
+ opcm->modifier = modifier;
+
+ add_to_work_queue (uop);
+}
+
+static int strncmp_nocase (a, b, n)
+ char *a, *b;
+ int n;
+{
+ int i;
+ int a1, b1;
+
+ for (i = 0; i < n; i++, a++, b++) {
+ if (!*a) return -1;
+ if (!*b) return 1;
+
+ if (*a != *b) {
+ a1 = (isascii(*a) && isupper(*a)) ? tolower(*a) : *a;
+ b1 = (isascii(*b) && isupper(*b)) ? tolower(*b) : *b;
+ if (a1 != b1) return b1 - a1;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * do_pointer = get list of numbers of the form
+ *
+ * buttons = NUMBER ...
+ * ^
+ */
+
+static void do_pointer (line, len)
+ char *line;
+ int len;
+{
+ int n;
+ int i;
+ unsigned long val;
+ union op *uop;
+ struct op_pointer *opp;
+ unsigned char buttons[MAXBUTTONCODES];
+ int nbuttons;
+ char *strval;
+ Bool ok;
+
+ if (len < 2 || !line || *line == '\0') { /* =1 minimum */
+ badmsg ("buttons input line", NULL);
+ return;
+ }
+
+ nbuttons = XGetPointerMapping (dpy, buttons, MAXBUTTONCODES);
+
+ n = skip_space (line, len);
+ line += n, len -= n;
+
+ if (line[0] != '=') {
+ badmsg ("buttons pointer code list, missing equal sign", NULL);
+ return;
+ }
+
+ line++, len--; /* skip = */
+ n = skip_space (line, len);
+ line += n, len -= n;
+
+ i = 0;
+ if (len < 7 || strncmp_nocase (line, "default", 7) != 0) {
+ while (len > 0) {
+ n = skip_space (line, len);
+ line += n, len -= n;
+ if (line[0] == '\0') break;
+ n = skip_word (line, len);
+ if (n < 1) {
+ badmsg ("skip of word in buttons line: %s", line);
+ return;
+ }
+ strval = copy_to_scratch(line, n);
+ ok = parse_number (strval, &val);
+ if (!ok || val >= MAXBUTTONCODES) {
+ badmsg ("value %s given for buttons list", strval);
+ return;
+ }
+ buttons[i++] = (unsigned char) val;
+ line += n, len -= n;
+ }
+ }
+
+ if (i > 0 && i != nbuttons) {
+ badheader ();
+ fprintf (stderr, "number of buttons, must have %d instead of %d\n",
+ nbuttons, i);
+ return;
+ }
+
+ uop = AllocStruct (union op);
+ if (!uop) {
+ badmsg ("attempt to allocate a %ld byte pointer opcode",
+ (long) sizeof (struct op_pointer));
+ return;
+ }
+ opp = &uop->pointer;
+
+ opp->type = doPointer;
+ opp->count = i;
+ for (i = 0; i < opp->count; i++) {
+ opp->button_codes[i] = buttons[i];
+ }
+
+ add_to_work_queue (uop);
+}
+
+
+/*
+ * get_keysym_list - parses the rest of the line into a keysyms assumes
+ * that the = sign has been parsed off but there may be leading whitespace
+ *
+ * keysym ...
+ * ^
+ *
+ * this involves getting the word containing the keysym, checking its range,
+ * and adding it to the list.
+ */
+
+static int get_keysym_list (line, len, np, kslistp)
+ char *line;
+ int len;
+ int *np;
+ KeySym **kslistp;
+{
+ int havesofar, maxcanhave;
+ KeySym *keysymlist;
+
+ *np = 0;
+ *kslistp = NULL;
+
+ if (len == 0) return (0); /* empty list */
+
+ havesofar = 0;
+ maxcanhave = 4; /* most lists are small */
+ keysymlist = (KeySym *) malloc (maxcanhave * sizeof (KeySym));
+ if (!keysymlist) {
+ badmsg ("attempt to allocate %ld byte initial keysymlist",
+ (long) (maxcanhave * sizeof (KeySym)));
+ return (-1);
+ }
+
+ while (len > 0) {
+ KeySym keysym;
+ int n;
+ char *tmpname;
+ Bool ok;
+
+ n = skip_space (line, len);
+ line += n, len -= n;
+
+ n = skip_chars (line, len);
+ if (n < 0) {
+ badmsg ("keysym name list", NULL);
+ return (-1);
+ }
+
+ ok = parse_keysym (line, n, &tmpname, &keysym);
+ line += n, len -= n;
+ if (!ok) {
+ badmsg ("keysym name '%s' in keysym list", tmpname);
+ /* do NOT return here, look for others */
+ continue;
+ }
+
+ /*
+ * Do NOT test to see if the keysym translates to a keycode or you
+ * won't be able to assign new ones....
+ */
+
+ /* grow the list bigger if necessary */
+ if (havesofar >= maxcanhave) {
+ maxcanhave *= 2;
+ keysymlist = (KeySym *) realloc (keysymlist,
+ maxcanhave * sizeof (KeySym));
+ if (!keysymlist) {
+ badmsg ("attempt to grow keysym list to %ld bytes",
+ (long) (maxcanhave * sizeof (KeySym)));
+ return (-1);
+ }
+ }
+
+ /* and add it to the list */
+ keysymlist[havesofar++] = keysym;
+ }
+
+ *kslistp = keysymlist;
+ *np = havesofar;
+ return (0);
+}
+
+
+#ifdef later
+/*
+ * check_special_keys - run through list of keysyms and generate "add" or
+ * "remove" commands for for any of the key syms that appear in the modifier
+ * list. this involves running down the modifier map which is an array of
+ * 8 by map->max_keypermod keycodes.
+ */
+
+static void check_special_keys (keycode, n, kslist)
+ KeyCode keycode;
+ int n;
+ KeySym *kslist;
+{
+ int i; /* iterator variable */
+ KeyCode *kcp; /* keycode pointer */
+
+ /*
+ * walk the modifiermap array. since its dimensions are not known at
+ * compile time, we have to walk it by hand instead of indexing. this
+ * is why it is initialized outside the loop, but incremented inside the
+ * second loop.
+ */
+
+ kcp = map->modifiermap; /* start at beginning and iterate */
+ for (i = 0; i < 8; i++) { /* there are 8 modifier keys */
+ int j;
+
+ for (j = 0; j < map->max_keypermod; j++, kcp++) {
+ KeySym keysym;
+ int k;
+
+ if (!*kcp) continue; /* only non-zero entries significant */
+
+ /*
+ * check to see if the target keycode is already a modifier; if so,
+ * then we have to remove it
+ */
+ if (keycode == *kcp) {
+ make_remove (i, keycode);
+ }
+
+ /*
+ * now, check to see if any of the keysyms map to keycodes
+ * that are in the modifier list
+ */
+ for (k = 0; k < n; k++) {
+ KeyCodes kc;
+
+ kc = XKeysymToKeycode (dpy, kslist[k]);
+ if (kc == *kcp) { /* yup, found one */
+ /*
+ * have to generate a remove of the CURRENT keycode
+ * and then an add of the new KEYCODE
+ */
+ make_remove (i, kc); /* modifier, keycode */
+ make_add (i, kslist[k]); /* modifier, keysym */
+ }
+ }
+ }
+ }
+ return;
+}
+#endif
+
+/*
+ * print_work_queue - disassemble the work queue and print it on stdout
+ */
+
+void print_work_queue ()
+{
+ union op *op;
+
+ printf ("! dump of work queue\n");
+ for (op = work_queue.head; op; op = op->generic.next) {
+ print_opcode (op);
+ }
+ return;
+}
+
+void print_opcode (op)
+ union op *op;
+{
+ int i;
+
+ printf (" ");
+ switch (op->generic.type) {
+ case doKeycode:
+ if (op->keycode.target_keycode)
+ printf ("keycode 0x%lx =", (long) op->keycode.target_keycode);
+ else
+ printf ("keycode any =");
+ for (i = 0; i < op->keycode.count; i++) {
+ char *name = XKeysymToString (op->keycode.keysyms[i]);
+
+ printf (" %s", name ? name : "BADKEYSYM");
+ }
+ printf ("\n");
+ break;
+ case doAddModifier:
+ printf ("add %s =", modifier_table[op->addmodifier.modifier].name);
+ for (i = 0; i < op->addmodifier.count; i++) {
+ char *name = XKeysymToString (op->addmodifier.keysyms[i]);
+ printf (" %s", name ? name : "BADKEYSYM");
+ }
+ printf ("\n");
+ break;
+ case doRemoveModifier:
+ printf ("remove %s = ",
+ modifier_table[op->removemodifier.modifier].name);
+ for (i = 0; i < op->removemodifier.count; i++) {
+ printf (" 0x%lx", (long) op->removemodifier.keycodes[i]);
+ }
+ printf ("\n");
+ break;
+ case doClearModifier:
+ printf ("clear %s\n", modifier_table[op->clearmodifier.modifier].name);
+ break;
+ case doPointer:
+ printf ("pointer = ");
+ if (op->pointer.count == 0)
+ printf(" default");
+ else for (i=0; i < op->pointer.count; i++)
+ printf(" %d", op->pointer.button_codes[i]);
+ printf ("\n");
+ break;
+ default:
+ printf ("! unknown opcode %d\n", op->generic.type);
+ break;
+ } /* end switch */
+ return;
+}
+
+/*
+ * execute_work_queue - do the real meat and potatoes now that we know what
+ * we need to do and that all of the input is correct.
+ */
+
+static int exec_keycode(), exec_add(), exec_remove(), exec_clear();
+static int exec_pointer();
+
+int execute_work_queue ()
+{
+ union op *op;
+ int errors;
+ Bool update_map = False;
+ int dosync;
+
+ if (verbose) {
+ printf ("!\n");
+ printf ("! executing work queue\n");
+ printf ("!\n");
+ }
+
+ errors = 0;
+ dosync = 0;
+
+ for (op = work_queue.head; op; op = op->generic.next) {
+ if (verbose) print_opcode (op);
+
+ /* check to see if we have to update the keyboard mapping */
+ if (dosync &&
+ (dosync < 0 ||
+ op->generic.type != doKeycode ||
+ !op->keycode.target_keycode)) {
+ XSync (dpy, 0);
+ while (XEventsQueued (dpy, QueuedAlready) > 0) {
+ XEvent event;
+ XNextEvent (dpy, &event);
+ if (event.type == MappingNotify) {
+ /* read all MappingNotify events */
+ while (XCheckTypedEvent (dpy, MappingNotify, &event)) ;
+ XRefreshKeyboardMapping (&event.xmapping);
+ } else {
+ fprintf (stderr, "%s: unknown event %ld\n",
+ ProgramName, (long) event.type);
+ }
+ }
+ }
+ dosync = 0;
+ switch (op->generic.type) {
+ case doKeycode:
+ if (exec_keycode (op) < 0) errors++;
+ if (op->keycode.target_keycode)
+ dosync = 1;
+ else
+ dosync = -1;
+ break;
+ case doAddModifier:
+ if (exec_add (op) < 0) errors++;
+ else update_map = True;
+ break;
+ case doRemoveModifier:
+ if (exec_remove (op) < 0) errors++;
+ else update_map = True;
+ break;
+ case doClearModifier:
+ if (exec_clear (op) < 0) errors++;
+ else update_map = True;
+ break;
+ case doPointer:
+ if (exec_pointer (op) < 0) errors++;
+ break;
+ default:
+ fprintf (stderr, "%s: unknown opcode %d\n",
+ ProgramName, op->generic.type);
+ break;
+ }
+ }
+
+ if (update_map) {
+ if (UpdateModifierMapping (map) < 0) errors++;
+ }
+
+ return (errors > 0 ? -1 : 0);
+}
+
+static int exec_keycode (opk)
+ struct op_keycode *opk;
+{
+ if (!opk->target_keycode) {
+ int i, j;
+ KeyCode free;
+ if (!opk->count)
+ return (0);
+ free = 0;
+ for (i = min_keycode; i <= max_keycode; i++) {
+ for (j = 0; j < opk->count; j++) {
+ if (XKeycodeToKeysym(dpy, (KeyCode) i, j) != opk->keysyms[j])
+ break;
+ }
+ if (j >= opk->count)
+ return (0);
+ if (free)
+ continue;
+ for (j = 0; j < 8; j++) {
+ if (XKeycodeToKeysym(dpy, (KeyCode) i, j) != None)
+ break;
+ }
+ if (j >= 8)
+ free = i;
+ }
+ if (!free) {
+ fprintf(stderr, "%s: no available keycode for assignment\n",
+ ProgramName);
+ return (-1);
+ }
+ XChangeKeyboardMapping (dpy, free, opk->count, opk->keysyms, 1);
+ } else if (opk->count == 0) {
+ KeySym dummy = NoSymbol;
+ XChangeKeyboardMapping (dpy, opk->target_keycode, 1,
+ &dummy, 1);
+ } else {
+ XChangeKeyboardMapping (dpy, opk->target_keycode, opk->count,
+ opk->keysyms, 1);
+ }
+ return (0);
+}
+
+static int exec_add (opam)
+ struct op_addmodifier *opam;
+{
+ int i;
+ int status;
+
+ status = 0;
+ for (i = 0; i < opam->count; i++) {
+ int num_kcs;
+ KeyCode *kcs;
+
+ kcs = KeysymToKeycodes (dpy, opam->keysyms[i], &num_kcs);
+ if (num_kcs == 0)
+ status = -1;
+ while (--num_kcs >= 0) {
+ if (AddModifier (&map, *kcs++, opam->modifier) < 0)
+ status = -1;
+ }
+ }
+ return (status);
+}
+
+static int exec_remove (oprm)
+ struct op_removemodifier *oprm;
+{
+ int i;
+ int status;
+
+ status = 0;
+ for (i = 0; i < oprm->count; i++) {
+ if (RemoveModifier (&map, oprm->keycodes[i], oprm->modifier) < 0)
+ status = -1;
+ }
+ return (status);
+}
+
+static int exec_clear (opcm)
+ struct op_clearmodifier *opcm;
+{
+ return (ClearModifier (&map, opcm->modifier));
+}
+
+
+static int exec_pointer (opp)
+ struct op_pointer *opp;
+{
+ return (SetPointerMap (opp->button_codes, opp->count));
+}
+
+void print_modifier_map ()
+{
+ PrintModifierMapping (map, stdout);
+ return;
+}
+
+void print_key_table (exprs)
+ Bool exprs;
+{
+ PrintKeyTable (exprs, stdout);
+ return;
+}
+
+void print_pointer_map ()
+{
+ PrintPointerMap (stdout);
+ return;
+}
+
+
diff --git a/pf.c b/pf.c
new file mode 100644
index 0000000..2cacbdc
--- /dev/null
+++ b/pf.c
@@ -0,0 +1,118 @@
+/* $Xorg: pf.c,v 1.4 2001/02/09 02:05:56 xorgcvs Exp $ */
+/*
+
+Copyright 1988, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+
+*/
+
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "xmodmap.h"
+
+#define NOTINFILEFILENAME "commandline"
+char *inputFilename = NOTINFILEFILENAME;
+int lineno = 0;
+
+void process_file (filename)
+ char *filename; /* NULL means use stdin */
+{
+ FILE *fp;
+ char buffer[BUFSIZ];
+
+ /* open the file, eventually we'll want to pipe through cpp */
+
+ if (!filename) {
+ fp = stdin;
+ inputFilename = "stdin";
+ } else {
+ fp = fopen (filename, "r");
+ if (!fp) {
+ fprintf (stderr, "%s: unable to open file '%s' for reading\n",
+ ProgramName, filename);
+ parse_errors++;
+ return;
+ }
+ inputFilename = filename;
+ }
+
+
+ /* read the input and filter */
+
+ if (verbose) {
+ printf ("! %s:\n", inputFilename);
+ }
+
+ for (lineno = 0; ; lineno++) {
+ buffer[0] = '\0';
+ if (fgets (buffer, BUFSIZ, fp) == NULL)
+ break;
+
+ process_line (buffer);
+ }
+
+ inputFilename = NOTINFILEFILENAME;
+ lineno = 0;
+ (void) fclose (fp);
+}
+
+
+void process_line (buffer)
+ char *buffer;
+{
+ int len;
+ int i;
+ char *cp;
+
+ len = strlen (buffer);
+
+ for (i = 0; i < len; i++) { /* look for blank lines */
+ register char c = buffer[i];
+ if (!(isspace(c) || c == '\n')) break;
+ }
+ if (i == len) return;
+
+ cp = &buffer[i];
+
+ if (*cp == '!') return; /* look for comments */
+ len -= (cp - buffer); /* adjust len by how much we skipped */
+
+ /* pipe through cpp */
+
+ /* strip trailing space */
+ for (i = len-1; i >= 0; i--) {
+ register char c = cp[i];
+ if (!(isspace(c) || c == '\n')) break;
+ }
+ if (i >= 0) cp[len = (i+1)] = '\0'; /* nul terminate */
+
+ if (verbose) {
+ printf ("! %d: %s\n", lineno, cp);
+ }
+
+ /* handle input */
+ handle_line (cp, len);
+}
diff --git a/swap.km b/swap.km
new file mode 100644
index 0000000..64c55e5
--- /dev/null
+++ b/swap.km
@@ -0,0 +1,11 @@
+!
+! Swap Caps_Lock and Control_L
+!
+
+remove Lock = Caps_Lock
+remove Control = Control_L
+keysym Control_L = Caps_Lock
+keysym Caps_Lock = Control_L
+add Lock = Caps_Lock
+add Control = Control_L
+
diff --git a/wq.h b/wq.h
new file mode 100644
index 0000000..e8e4b41
--- /dev/null
+++ b/wq.h
@@ -0,0 +1,143 @@
+/* $Xorg: wq.h,v 1.4 2001/02/09 02:05:56 xorgcvs Exp $ */
+/*
+
+Copyright 1988, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+
+*/
+
+/*
+ * Input is parsed and a work queue is built that is executed later. This
+ * allows us to swap keys as well as ensure that we don't mess up the keyboard
+ * by doing a partial rebind.
+ */
+
+enum opcode { doKeycode, doAddModifier, doRemoveModifier, doClearModifier,
+ doPointer };
+
+struct op_generic {
+ enum opcode type; /* oneof enum opcode */
+ union op *next; /* next element in list or NULL */
+};
+
+
+/*
+ * keycode KEYCODE = KEYSYM
+ * keysym OLDKEYSYM = NEWKEYSYM
+ *
+ * want to eval the OLDKEYSYM before executing the work list so that it isn't
+ * effected by any assignments.
+ */
+
+struct op_keycode {
+ enum opcode type; /* doKeycode */
+ union op *next; /* next element in list or NULL */
+ KeyCode target_keycode; /* key to which we are assigning */
+ int count; /* number of new keysyms */
+ KeySym *keysyms; /* new values to insert */
+};
+
+
+/*
+ * add MODIFIER = KEYSYM ...
+ */
+
+struct op_addmodifier {
+ enum opcode type; /* doAddModifier */
+ union op *next; /* next element in list or NULL */
+ int modifier; /* index into modifier list */
+ int count; /* number of keysyms */
+ KeySym *keysyms; /* new values to insert */
+};
+
+
+/*
+ * remove MODIFIER = OLDKEYSYM ...
+ *
+ * want to eval the OLDKEYSYM before executing the work list so that it isn't
+ * effected by any assignments.
+ */
+
+struct op_removemodifier {
+ enum opcode type; /* doRemoveModifier */
+ union op *next; /* next element in list or NULL */
+ int modifier; /* index into modifier list */
+ int count; /* number of keysyms */
+ KeyCode *keycodes; /* old values to remove */
+};
+
+
+/*
+ * clear MODIFIER
+ */
+
+struct op_clearmodifier {
+ enum opcode type; /* doClearModifier */
+ union op *next; /* next element in list or NULL */
+ int modifier; /* index into modifier list */
+};
+
+/*
+ * pointer = NUMBER ...
+ *
+ * set pointer map to the positive numbers given on the right hand side
+ */
+
+#define MAXBUTTONCODES 256 /* there are eight bits of buttons */
+
+struct op_pointer {
+ enum opcode type; /* doPointer */
+ union op *next; /* next element in list or NULL */
+ int count; /* number of new button codes */
+ unsigned char button_codes[MAXBUTTONCODES];
+};
+
+
+/*
+ * all together now
+ */
+union op {
+ struct op_generic generic;
+ struct op_keycode keycode;
+ struct op_addmodifier addmodifier;
+ struct op_removemodifier removemodifier;
+ struct op_clearmodifier clearmodifier;
+ struct op_pointer pointer;
+};
+
+extern struct wq {
+ union op *head;
+ union op *tail;
+} work_queue;
+
+
+extern struct modtab {
+ char *name;
+ int length;
+ int value;
+} modifier_table[];
+
+#define AllocStruct(s) ((s *) malloc (sizeof (s)))
+
+#define MAXKEYSYMNAMESIZE 80 /* absurdly large */
diff --git a/xmodmap.c b/xmodmap.c
new file mode 100644
index 0000000..b5ffa00
--- /dev/null
+++ b/xmodmap.c
@@ -0,0 +1,341 @@
+/* $Xorg: xmodmap.c,v 1.4 2001/02/09 02:05:56 xorgcvs Exp $ */
+/*
+
+Copyright 1988, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+
+*/
+
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "xmodmap.h"
+
+char *ProgramName;
+Display *dpy = NULL;
+int min_keycode, max_keycode;
+Bool verbose = False;
+Bool dontExecute = False;
+
+void Exit (status)
+ int status;
+{
+ if (dpy) {
+ XCloseDisplay (dpy);
+ dpy = NULL;
+ }
+ exit (status);
+}
+
+
+static char *help_message[] = {
+"\nwhere options include:",
+" -display host:dpy X server to use",
+" -verbose, -quiet turn logging on or off",
+" -n don't execute changes, just show like make",
+" -e expression execute string",
+" -pm print modifier map",
+" -pk print keymap table",
+" -pke print keymap table as expressions",
+" -pp print pointer map",
+" -grammar print out short help on allowable input",
+" - read standard input",
+"",
+NULL};
+
+
+void usage ()
+{
+ char **cpp;
+
+ fprintf (stderr, "usage: %s [-options ...] [filename]\n", ProgramName);
+ for (cpp = help_message; *cpp; cpp++) {
+ fprintf (stderr, "%s\n", *cpp);
+ }
+ Exit (1);
+}
+
+static char *grammar_message[] = {
+" pointer = default reset pointer buttons to default",
+" pointer = NUMBER ... set pointer button codes",
+" keycode NUMBER = [KEYSYM ...] map keycode to given keysyms",
+" keysym KEYSYM = [KEYSYM ...] look up keysym and do a keycode operation",
+" clear MODIFIER remove all keys for this modifier",
+" add MODIFIER = KEYSYM ... add the keysyms to the modifier",
+" remove MODIFIER = KEYSYM ... remove the keysyms from the modifier",
+"",
+"where NUMBER is a decimal, octal, or hex constant; KEYSYM is a valid",
+"Key Symbol name; and MODIFIER is one of the eight modifier names: Shift,",
+"Lock, Control, Mod1, Mod2, Mod3, Mod4, or Mod5. Lines beginning with",
+"an exclamation mark (!) are taken as comments. Case is significant except",
+"for MODIFIER names.",
+"",
+"Keysyms on the left hand side of the = sign are looked up before any changes",
+"are made; keysyms on the right are looked up after all of those on the left",
+"have been resolved. This makes it possible to swap modifier keys.",
+"",
+NULL };
+
+
+void grammar_usage ()
+{
+ char **cpp;
+
+ fprintf (stderr, "%s accepts the following input expressions:\n\n",
+ ProgramName);
+ for (cpp = grammar_message; *cpp; cpp++) {
+ fprintf (stderr, "%s\n", *cpp);
+ }
+ Exit (0);
+}
+
+int parse_errors = 0;
+
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+ char *displayname = NULL;
+ int status;
+ Bool printMap = False;
+ Bool printKeyTable = False;
+ Bool printKeyTableExprs = False;
+ Bool printPointerMap = False;
+ Bool didAnything = False;
+
+ ProgramName = argv[0];
+
+ /*
+ * scan the arg list once to find out which display to use
+ */
+
+ for (i = 1; i < argc; i++) {
+ if (strncmp (argv[i], "-d", 2) == 0) {
+ if (++i >= argc) usage ();
+ displayname = argv[i];
+ }
+ }
+
+ dpy = XOpenDisplay (displayname);
+ if (!dpy) {
+ fprintf (stderr, "%s: unable to open display '%s'\n",
+ ProgramName, XDisplayName (displayname));
+ Exit (1);
+ }
+
+ XDisplayKeycodes (dpy, &min_keycode, &max_keycode);
+
+ initialize_map ();
+
+ /*
+ * scan the arg list again to do the actual work (since it requires
+ * the display being open.
+ */
+
+ status = 0;
+ for (i = 1; i < argc; i++) {
+ char *arg = argv[i];
+
+ if (arg[0] == '-') {
+ switch (arg[1]) {
+ case 'd': /* -display host:dpy */
+ ++i; /* handled above */
+ continue;
+ case 'v': /* -verbose */
+ verbose = True;
+ continue;
+ case 'q': /* -quiet */
+ verbose = False;
+ continue;
+ case 'n': /* -n (like make) */
+ dontExecute = True;
+ continue;
+ case 'e': /* -e expression */
+ didAnything = True;
+ if (++i >= argc) usage ();
+ process_line (argv[i]);
+ continue;
+ case 'p': /* -p... */
+ switch (arg[2]) {
+ case '\0':
+ case 'm': /* -pm */
+ printMap = True;
+ break;
+ case 'k': /* -pk, -pke */
+ switch (arg[3]) {
+ case '\0':
+ printKeyTable = True;
+ break;
+ case 'e':
+ printKeyTableExprs = True;
+ break;
+ default:
+ usage ();
+ }
+ break;
+ case 'p': /* -pp */
+ printPointerMap = True;
+ break;
+ default:
+ usage ();
+ /* NOTREACHED */
+ }
+ didAnything = True;
+ continue;
+ case 'g': /* -grammar */
+ grammar_usage ();
+ /*NOTREACHED*/
+ case '\0': /* - (use standard input) */
+ didAnything = True;
+ process_file (NULL);
+ continue;
+
+ /*
+ * provide old xmodmap args
+ */
+ case 'S':
+ didAnything = True;
+ process_line ("clear shift");
+ continue;
+ case 'L':
+ didAnything = True;
+ process_line ("clear lock");
+ continue;
+ case 'C':
+ didAnything = True;
+ process_line ("clear control");
+ continue;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5': {
+ char *cmd = "clear modX";
+ cmd[9] = arg[1];
+ process_line (cmd);
+ continue;
+ }
+ case 's':
+ case 'l':
+ case 'c': {
+ char cmd[80]; /* big enough to hold line */
+ didAnything = True;
+ if (++i >= argc) usage ();
+ (void) sprintf (cmd, "remove %s = %s",
+ ((arg[1] == 's') ? "shift" :
+ ((arg[1] == 'l') ? "lock" :
+ "control")), argv[i]);
+ process_line (cmd);
+ continue;
+ }
+ default:
+ usage ();
+ /*NOTREACHED*/
+ }
+ } else if (arg[0] == '+') { /* old xmodmap args */
+ switch (arg[1]) {
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5': {
+ char cmd[80]; /* big enough to hold line */
+ didAnything = True;
+ if (++i >= argc) usage ();
+
+ (void) sprintf (cmd, "add mod%c = %s", arg[1], argv[i]);
+ process_line (cmd);
+ continue;
+ }
+ case 'S':
+ case 'L':
+ case 'C':
+ arg[1] = tolower (arg[1]);
+ /* fall through to handler below */
+ case 's':
+ case 'l':
+ case 'c': {
+ char cmd[80]; /* big enough to hold line */
+ didAnything = True;
+ if (++i >= argc) usage ();
+ (void) sprintf (cmd, "add %s = %s",
+ ((arg[1] == 's') ? "shift" :
+ ((arg[1] == 'l') ? "lock" :
+ "control")), argv[i]);
+ process_line (cmd);
+ continue;
+ }
+ default:
+ usage ();
+ }
+ } else {
+ didAnything = True;
+ process_file (arg);
+ continue;
+ }
+ } /* end for loop */
+
+ /* for compatibility */
+ if (!didAnything) printMap = True;
+
+ /*
+ * at this point, the work list has been built and we can view it or
+ * execute it
+ */
+
+ if (dontExecute) {
+ print_work_queue ();
+ Exit (0);
+ }
+
+ if (parse_errors != 0) {
+ fprintf (stderr, "%s: %d error%s encountered, aborting.\n",
+ ProgramName, parse_errors,
+ (parse_errors == 1 ? "" : "s"));
+ status = -1; /* return an error condition */
+ } else {
+ status = execute_work_queue ();
+ }
+
+ if (printMap) {
+ print_modifier_map ();
+ }
+
+ if (printKeyTable) {
+ print_key_table (False);
+ }
+
+ if (printKeyTableExprs) {
+ print_key_table (True);
+ }
+
+ if (printPointerMap) {
+ print_pointer_map ();
+ }
+
+ Exit (status < 0 ? 1 : 0);
+}
+
diff --git a/xmodmap.h b/xmodmap.h
new file mode 100644
index 0000000..96b4f66
--- /dev/null
+++ b/xmodmap.h
@@ -0,0 +1,48 @@
+/* $Xorg: xmodmap.h,v 1.4 2001/02/09 02:05:56 xorgcvs Exp $ */
+/*
+
+Copyright 1988, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+
+*/
+
+extern char *ProgramName;
+extern Display *dpy;
+extern int min_keycode, max_keycode;
+extern Bool verbose;
+extern Bool dontExecute;
+extern char *inputFilename;
+extern int lineno;
+extern int parse_errors;
+
+extern void initialize_map ();
+extern void process_file ();
+extern void process_line ();
+extern void handle_line ();
+extern void print_opcode ();
+extern void print_work_queue ();
+extern int execute_work_queue ();
+extern void print_modifier_map ();
+extern void print_key_table ();
+extern void print_pointer_map ();
diff --git a/xmodmap.man b/xmodmap.man
new file mode 100644
index 0000000..d23daca
--- /dev/null
+++ b/xmodmap.man
@@ -0,0 +1,302 @@
+.\" $Xorg: xmodmap.man,v 1.4 2001/02/09 02:05:56 xorgcvs Exp $
+.\" Copyright 1988, 1989, 1990, 1998 The Open Group
+.\" Copyright 1987 Sun Microsystems, Inc.
+.\"
+.\" Permission to use, copy, modify, distribute, and sell this software and its
+.\" documentation for any purpose is hereby granted without fee, provided that
+.\" the above copyright notice appear in all copies and that both that
+.\" copyright notice and this permission notice appear in supporting
+.\" documentation.
+.\"
+.\" The above copyright notice and this permission notice shall be included
+.\" in all copies or substantial portions of the Software.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+.\" IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+.\" OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+.\" ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+.\" OTHER DEALINGS IN THE SOFTWARE.
+.\"
+.\" Except as contained in this notice, the name of The Open Group shall
+.\" not be used in advertising or otherwise to promote the sale, use or
+.\" other dealings in this Software without prior written authorization
+.\" from The Open Group.
+.de EX \"Begin example
+.ne 5
+.if n .sp 1
+.if t .sp .5
+.nf
+.in +.5i
+..
+.de EE
+.fi
+.in -.5i
+.if n .sp 1
+.if t .sp .5
+..
+.TH XMODMAP 1 "Release 6.4" "X Version 11"
+.SH NAME
+xmodmap - utility for modifying keymaps (and pointer buttons) in X
+.SH SYNOPSIS
+.B xmodmap
+[-options ...] [filename]
+.SH DESCRIPTION
+.PP
+The \fIxmodmap\fP program is used to edit and display the
+keyboard \fImodifier map\fP and \fIkeymap table\fP that are used by client
+applications to convert event keycodes into keysyms. It is usually run from
+the user's session startup script to configure the keyboard according to
+personal tastes.
+.SH OPTIONS
+.PP
+The following options may be used with \fIxmodmap\fP:
+.TP 8
+.B \-display \fIdisplay\fP
+This option specifies the host and display to use.
+.TP 8
+.B \-help
+This option indicates that a brief description of the command line arguments
+should be printed on the standard error channel. This will be done whenever an
+unhandled argument is given to
+.I xmodmap.
+.TP 8
+.B \-grammar
+This option indicates that a help message describing the expression grammar
+used in files and with \-e expressions should be printed on the standard error.
+.TP 8
+.B \-verbose
+This option indicates that
+.I xmodmap
+should print logging information as it parses its input.
+.TP 8
+.B \-quiet
+This option turns off the verbose logging. This is the default.
+.TP 8
+.B \-n
+This option indicates that
+.I xmodmap
+should not change the mappings, but should display what it would do, like
+\fImake(1)\fP does when given this option.
+.TP 8
+.B \-e \fIexpression\fB
+This option specifies an expression to be executed. Any number of expressions
+may be specified from the command line.
+.TP 8
+.B \-pm
+This option indicates that the current modifier map should be printed on the
+standard output.
+.TP 8
+.B \-pk
+This option indicates that the current keymap table should be printed on the
+standard output.
+.TP 8
+.B \-pke
+This option indicates that the current keymap table should be printed on the
+standard output in the form of expressions that can be fed back to
+\fIxmodmap\fP.
+.TP 8
+.B \-pp
+This option indicates that the current pointer map should be printed on the
+standard output.
+.TP 8
+.B \-
+A lone dash means that the standard input should be used as the input file.
+.PP
+The \fIfilename\fP specifies a file containing \fIxmodmap\fP expressions
+to be executed. This file is usually kept in the user's home directory with
+a name like \fI.xmodmaprc\fP.
+.SH EXPRESSION GRAMMAR
+.PP
+The
+.I xmodmap
+program reads a list of expressions and parses them all before attempting
+to execute any of them. This makes it possible to refer to keysyms that are
+being redefined in a natural way without having to worry as much about name
+conflicts.
+.TP 8
+.B keycode \fINUMBER\fP = \fIKEYSYMNAME ...\fP
+The list of keysyms is assigned to the indicated keycode
+(which may be specified in decimal, hex or octal and can be determined by
+running the
+.I xev
+program.
+.TP 8
+.B keycode any = \fIKEYSYMNAME ...\fP
+If no existing key has the specified list of keysyms assigned to it,
+a spare key on the keyboard is selected and the keysyms are assigned to it.
+The list of keysyms may be specified in decimal, hex or octal.
+.TP 8
+.B keysym \fIKEYSYMNAME\fP = \fIKEYSYMNAME ...\fP
+The \fIKEYSYMNAME\fP on the left hand side is translated into matching keycodes
+used to perform the corresponding set of \fBkeycode\fP expressions.
+The list of keysym names may be
+found in the header file \fI<X11/keysymdef.h>\fP (without the \fIXK_\fP prefix)
+or the keysym database \fI<XRoot>/lib/X11/XKeysymDB\fP, where <XRoot> refers
+to the root of the X11 install tree.
+Note that if the same keysym is bound to multiple keys, the expression is
+executed for each matching keycode.
+.TP 8
+.B clear \fIMODIFIERNAME\fP
+This removes all entries in the modifier map for the given modifier, where
+valid name are:
+.BR Shift ,
+.BR Lock ,
+.BR Control ,
+.BR Mod1 ,
+.BR Mod2 ,
+.BR Mod3 ,
+.BR Mod4 ,
+and \fBMod5\fP (case
+does not matter in modifier names, although it does matter for all other
+names). For example, ``clear Lock'' will remove
+all any keys that were bound to the shift lock modifier.
+.TP 8
+.B add \fIMODIFIERNAME\fP = \fIKEYSYMNAME ...\fP
+This adds all keys containing the given keysyms to the indicated modifier map.
+The keysym names
+are evaluated after all input expressions are read to make it easy to write
+expressions to swap keys (see the EXAMPLES section).
+.TP 8
+.B remove \fIMODIFIERNAME\fP = \fIKEYSYMNAME ...\fP
+This removes all keys containing the given keysyms from the indicated
+modifier map. Unlike
+.B add,
+the keysym names are evaluated as the line is read in. This allows you to
+remove keys from a modifier without having to worry about whether or not they
+have been reassigned.
+.TP 8
+.B "pointer = default"
+This sets the pointer map back to its default settings (button 1 generates a
+code of 1, button 2 generates a 2, etc.).
+.TP 8
+.B pointer = \fINUMBER ...\fP
+This sets to pointer map to contain the indicated button codes. The list
+always starts with the first physical button.
+.PP
+Lines that begin with an exclamation point (!) are taken as comments.
+.PP
+If you want to change the binding of a modifier key, you must also remove it
+from the appropriate modifier map.
+.SH EXAMPLES
+.PP
+Many pointers are designed such that the first button is pressed using the
+index finger of the right hand. People who are left-handed frequently find
+that it is more comfortable to reverse the button codes that get generated
+so that the primary button is pressed using the index finger of the left hand.
+This could be done on a 3 button pointer as follows:
+.EX
+% xmodmap -e "pointer = 3 2 1"
+.EE
+.PP
+Many applications support the notion of Meta keys (similar to Control
+keys except that Meta is held down instead of Control). However,
+some servers do not have a Meta keysym in the default keymap table, so one
+needs to be added by hand.
+The following command will attach Meta to the Multi-language key (sometimes
+labeled Compose Character). It also takes advantage of the fact that
+applications that need a Meta key simply need to get the keycode and don't
+require the keysym to be in the first column of the keymap table. This
+means that applications that are looking for a Multi_key (including the
+default modifier map) won't notice any change.
+.EX
+% xmodmap -e "keysym Multi_key = Multi_key Meta_L"
+.EE
+.PP
+Similarly, some keyboards have an Alt key but no Meta key.
+In that case the following may be useful:
+.EX
+% xmodmap -e "keysym Alt_L = Meta_L Alt_L"
+.EE
+.PP
+One of the more simple, yet convenient, uses of \fIxmodmap\fP is to set the
+keyboard's "rubout" key to generate an alternate keysym. This frequently
+involves exchanging Backspace with Delete to be more comfortable to the user.
+If the \fIttyModes\fP resource in \fIxterm\fP is set as well, all terminal
+emulator windows will use the same key for erasing characters:
+.EX
+% xmodmap -e "keysym BackSpace = Delete"
+% echo "XTerm*ttyModes: erase ^?" | xrdb -merge
+.EE
+.PP
+Some keyboards do not automatically generate less than and greater than
+characters when the comma and period keys are shifted. This can be remedied
+with \fIxmodmap\fP by resetting the bindings for the comma and period with
+the following scripts:
+.EX
+!
+! make shift-, be < and shift-. be >
+!
+keysym comma = comma less
+keysym period = period greater
+.EE
+.PP
+One of the more irritating differences between keyboards is the location of the
+Control and Shift Lock keys. A common use of \fIxmodmap\fP is to swap these
+two keys as follows:
+.EX
+!
+! Swap Caps_Lock and Control_L
+!
+remove Lock = Caps_Lock
+remove Control = Control_L
+keysym Control_L = Caps_Lock
+keysym Caps_Lock = Control_L
+add Lock = Caps_Lock
+add Control = Control_L
+.EE
+.PP
+The \fIkeycode\fP command is useful for assigning the same keysym to
+multiple keycodes. Although unportable, it also makes it possible to write
+scripts that can reset the keyboard to a known state. The following script
+sets the backspace key to generate Delete (as shown above), flushes all
+existing caps lock bindings, makes the CapsLock
+key be a control key, make F5 generate Escape, and makes Break/Reset be a
+shift lock.
+.EX
+!
+! On the HP, the following keycodes have key caps as listed:
+!
+! 101 Backspace
+! 55 Caps
+! 14 Ctrl
+! 15 Break/Reset
+! 86 Stop
+! 89 F5
+!
+keycode 101 = Delete
+keycode 55 = Control_R
+clear Lock
+add Control = Control_R
+keycode 89 = Escape
+keycode 15 = Caps_Lock
+add Lock = Caps_Lock
+.EE
+.SH ENVIRONMENT
+.PP
+.TP 8
+.B DISPLAY
+to get default host and display number.
+.SH SEE ALSO
+X(1), xev(1), \fIXlib\fP documentation on key and pointer events
+.SH BUGS
+.PP
+Every time a \fBkeycode\fP expression is evaluated, the server generates
+a \fIMappingNotify\fP event on every client. This can cause some thrashing.
+All of the changes should be batched together and done at once.
+Clients that receive keyboard input and ignore \fIMappingNotify\fP events
+will not notice any changes made to keyboard mappings.
+.PP
+.I Xmodmap
+should generate "add" and "remove" expressions automatically
+whenever a keycode that is already bound to a modifier is changed.
+.PP
+There should be a way to have the
+.I remove
+expression accept keycodes as well as keysyms for those times when you really
+mess up your mappings.
+.SH AUTHOR
+Jim Fulton, MIT X Consortium, rewritten from an earlier version by
+David Rosenthal of Sun Microsystems.
+