From e29f1f1ed2128d7382a72ccb1684045fe4bc8183 Mon Sep 17 00:00:00 2001 From: Kaleb Keithley Date: Fri, 14 Nov 2003 15:54:54 +0000 Subject: [PATCH] R6.6 is the Xorg base-line --- exec.c | 364 ++++++++++++++++ handle.c | 1348 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pf.c | 118 ++++++ swap.km | 11 + wq.h | 143 +++++++ xmodmap.c | 341 +++++++++++++++ xmodmap.h | 48 +++ xmodmap.man | 302 +++++++++++++ 8 files changed, 2675 insertions(+) create mode 100644 exec.c create mode 100644 handle.c create mode 100644 pf.c create mode 100644 swap.km create mode 100644 wq.h create mode 100644 xmodmap.c create mode 100644 xmodmap.h create mode 100644 xmodmap.man 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 +#include +#include +#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 +#include +#include +#include +#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 +#include +#include +#include +#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 +#include +#include +#include +#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\fP (without the \fIXK_\fP prefix) +or the keysym database \fI/lib/X11/XKeysymDB\fP, where 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. + -- 2.7.4