diff options
-rw-r--r-- | exec.c | 364 | ||||
-rw-r--r-- | handle.c | 1348 | ||||
-rw-r--r-- | pf.c | 118 | ||||
-rw-r--r-- | swap.km | 11 | ||||
-rw-r--r-- | wq.h | 143 | ||||
-rw-r--r-- | xmodmap.c | 341 | ||||
-rw-r--r-- | xmodmap.h | 48 | ||||
-rw-r--r-- | xmodmap.man | 302 |
8 files changed, 2675 insertions, 0 deletions
@@ -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; +} + + @@ -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); +} @@ -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 + @@ -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. + |