xmodmap.c revision a733a5bf
11a30de1fSmrg/*
21a30de1fSmrg
31a30de1fSmrgCopyright 1988, 1998  The Open Group
41a30de1fSmrg
51a30de1fSmrgPermission to use, copy, modify, distribute, and sell this software and its
61a30de1fSmrgdocumentation for any purpose is hereby granted without fee, provided that
71a30de1fSmrgthe above copyright notice appear in all copies and that both that
81a30de1fSmrgcopyright notice and this permission notice appear in supporting
91a30de1fSmrgdocumentation.
101a30de1fSmrg
111a30de1fSmrgThe above copyright notice and this permission notice shall be included
121a30de1fSmrgin all copies or substantial portions of the Software.
131a30de1fSmrg
141a30de1fSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
151a30de1fSmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
161a30de1fSmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
171a30de1fSmrgIN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
181a30de1fSmrgOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
191a30de1fSmrgARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
201a30de1fSmrgOTHER DEALINGS IN THE SOFTWARE.
211a30de1fSmrg
221a30de1fSmrgExcept as contained in this notice, the name of The Open Group shall
231a30de1fSmrgnot be used in advertising or otherwise to promote the sale, use or
241a30de1fSmrgother dealings in this Software without prior written authorization
251a30de1fSmrgfrom The Open Group.
261a30de1fSmrg
271a30de1fSmrg*/
28a733a5bfSmrg
29a733a5bfSmrg#ifdef HAVE_CONFIG_H
30a733a5bfSmrg# include "config.h"
31a733a5bfSmrg#endif
321a30de1fSmrg
331a30de1fSmrg#include <X11/Xos.h>
341a30de1fSmrg#include <X11/Xlib.h>
351a30de1fSmrg#include <stdio.h>
361a30de1fSmrg#include <stdlib.h>
371a30de1fSmrg#include <ctype.h>
38a733a5bfSmrg#include <stdarg.h>
391a30de1fSmrg#include "xmodmap.h"
401a30de1fSmrg
411a30de1fSmrgconst char *ProgramName;
421a30de1fSmrgDisplay *dpy = NULL;
431a30de1fSmrgint min_keycode, max_keycode;
441a30de1fSmrgBool verbose = False;
451a30de1fSmrgBool dontExecute = False;
461a30de1fSmrg
47a733a5bfSmrgvoid
48a733a5bfSmrg_X_NORETURN
491a30de1fSmrgExit(int status)
501a30de1fSmrg{
511a30de1fSmrg    if (dpy) {
521a30de1fSmrg	XCloseDisplay (dpy);
531a30de1fSmrg	dpy = NULL;
541a30de1fSmrg    }
551a30de1fSmrg    exit (status);
561a30de1fSmrg}
571a30de1fSmrg
58a733a5bfSmrgstatic void _X_NORETURN
59a733a5bfSmrgFatalError(const char *message)
60a733a5bfSmrg{
61a733a5bfSmrg    fprintf(stderr, "%s: %s\n", ProgramName, message);
62a733a5bfSmrg    Exit(-1);
63a733a5bfSmrg}
64a733a5bfSmrg
65a733a5bfSmrg#ifndef HAVE_ASPRINTF
66a733a5bfSmrg/* sprintf variant found in newer libc's which allocates string to print to */
67a733a5bfSmrgstatic int _X_ATTRIBUTE_PRINTF(2,3)
68a733a5bfSmrgasprintf(char ** ret, const char *format, ...)
691a30de1fSmrg{
70a733a5bfSmrg    char buf[256];
71a733a5bfSmrg    int len;
72a733a5bfSmrg    va_list ap;
73a733a5bfSmrg
74a733a5bfSmrg    va_start(ap, format);
75a733a5bfSmrg    len = vsnprintf(buf, sizeof(buf), format, ap);
76a733a5bfSmrg    va_end(ap);
77a733a5bfSmrg
78a733a5bfSmrg    if (len < 0)
79a733a5bfSmrg	return -1;
80a733a5bfSmrg
81a733a5bfSmrg    if (len < sizeof(buf))
82a733a5bfSmrg    {
83a733a5bfSmrg	*ret = strdup(buf);
841a30de1fSmrg    }
85a733a5bfSmrg    else
86a733a5bfSmrg    {
87a733a5bfSmrg	*ret = malloc(len + 1); /* snprintf doesn't count trailing '\0' */
88a733a5bfSmrg	if (*ret != NULL)
89a733a5bfSmrg	{
90a733a5bfSmrg	    va_start(ap, format);
91a733a5bfSmrg	    len = vsnprintf(*ret, len + 1, format, ap);
92a733a5bfSmrg	    va_end(ap);
93a733a5bfSmrg	    if (len < 0) {
94a733a5bfSmrg		free(*ret);
95a733a5bfSmrg		*ret = NULL;
96a733a5bfSmrg	    }
97a733a5bfSmrg	}
98a733a5bfSmrg    }
99a733a5bfSmrg
100a733a5bfSmrg    if (*ret == NULL)
101a733a5bfSmrg	return -1;
102a733a5bfSmrg
103a733a5bfSmrg    return len;
1041a30de1fSmrg}
105a733a5bfSmrg#endif /* HAVE_ASPRINTF */
1061a30de1fSmrg
1071a30de1fSmrgstatic const char help_message[] =
1081a30de1fSmrg"\nwhere options include:\n"
1091a30de1fSmrg"    -display host:dpy            X server to use\n"
1101a30de1fSmrg"    -verbose, -quiet             turn logging on or off\n"
1111a30de1fSmrg"    -n                           don't execute changes, just show like make\n"
1121a30de1fSmrg"    -e expression                execute string\n"
1131a30de1fSmrg"    -pm                          print modifier map\n"
1141a30de1fSmrg"    -pk                          print keymap table\n"
1151a30de1fSmrg"    -pke                         print keymap table as expressions\n"
1161a30de1fSmrg"    -pp                          print pointer map\n"
117a733a5bfSmrg"    -help                        print this usage message\n"
1181a30de1fSmrg"    -grammar                     print out short help on allowable input\n"
1191a30de1fSmrg"    -                            read standard input\n"
1201a30de1fSmrg"\n";
1211a30de1fSmrg
1221a30de1fSmrg
1231a30de1fSmrgstatic void
124a733a5bfSmrg_X_NORETURN
125a733a5bfSmrgusage(int exitcode)
1261a30de1fSmrg{
1271a30de1fSmrg    fprintf (stderr, "usage:  %s [-options ...] [filename]\n", ProgramName);
1281a30de1fSmrg    fprintf (stderr, "%s\n", help_message);
129a733a5bfSmrg    Exit (exitcode);
1301a30de1fSmrg}
1311a30de1fSmrg
1321a30de1fSmrgstatic const char grammar_message[] =
1331a30de1fSmrg"    pointer = default              reset pointer buttons to default\n"
1341a30de1fSmrg"    pointer = NUMBER ...           set pointer button codes\n"
1351a30de1fSmrg"    keycode NUMBER = [KEYSYM ...]  map keycode to given keysyms\n"
1361a30de1fSmrg"    keysym KEYSYM = [KEYSYM ...]   look up keysym and do a keycode operation\n"
1371a30de1fSmrg"    clear MODIFIER                 remove all keys for this modifier\n"
1381a30de1fSmrg"    add MODIFIER = KEYSYM ...      add the keysyms to the modifier\n"
1391a30de1fSmrg"    remove MODIFIER = KEYSYM ...   remove the keysyms from the modifier\n"
1401a30de1fSmrg"\n"
1411a30de1fSmrg"where NUMBER is a decimal, octal, or hex constant; KEYSYM is a valid\n"
1421a30de1fSmrg"Key Symbol name; and MODIFIER is one of the eight modifier names:  Shift,\n"
1431a30de1fSmrg"Lock, Control, Mod1, Mod2, Mod3, Mod4, or Mod5.  Lines beginning with\n"
1441a30de1fSmrg"an exclamation mark (!) are taken as comments.  Case is significant except\n"
1451a30de1fSmrg"for MODIFIER names.\n"
1461a30de1fSmrg"\n"
1471a30de1fSmrg"Keysyms on the left hand side of the = sign are looked up before any changes\n"
1481a30de1fSmrg"are made; keysyms on the right are looked up after all of those on the left\n"
1491a30de1fSmrg"have been resolved.  This makes it possible to swap modifier keys.\n"
1501a30de1fSmrg"\n";
1511a30de1fSmrg
1521a30de1fSmrg
1531a30de1fSmrgstatic void
154a733a5bfSmrg_X_NORETURN
1551a30de1fSmrggrammar_usage(void)
1561a30de1fSmrg{
1571a30de1fSmrg    fprintf (stderr, "%s accepts the following input expressions:\n\n",
1581a30de1fSmrg	     ProgramName);
1591a30de1fSmrg    fprintf (stderr, "%s\n", grammar_message);
1601a30de1fSmrg    Exit (0);
1611a30de1fSmrg}
1621a30de1fSmrg
1631a30de1fSmrgint parse_errors = 0;
1641a30de1fSmrg
1651a30de1fSmrgint
1661a30de1fSmrgmain(int argc, char *argv[])
1671a30de1fSmrg{
1681a30de1fSmrg    int i;
1691a30de1fSmrg    char *displayname = NULL;
1701a30de1fSmrg    int status;
1711a30de1fSmrg    Bool printMap = False;
1721a30de1fSmrg    Bool printKeyTable = False;
1731a30de1fSmrg    Bool printKeyTableExprs = False;
1741a30de1fSmrg    Bool printPointerMap = False;
1751a30de1fSmrg    Bool didAnything = False;
1761a30de1fSmrg
1771a30de1fSmrg    ProgramName = argv[0];
1781a30de1fSmrg
1791a30de1fSmrg    /*
1801a30de1fSmrg     * scan the arg list once to find out which display to use
1811a30de1fSmrg     */
1821a30de1fSmrg
1831a30de1fSmrg    for (i = 1; i < argc; i++) {
184a733a5bfSmrg	const char *arg = argv[i];
185a733a5bfSmrg
186a733a5bfSmrg	if (arg[0] == '-') {
187a733a5bfSmrg	    switch (arg[1]) {
188a733a5bfSmrg	    case 'd':			/* -display host:dpy */
189a733a5bfSmrg		if (++i >= argc) usage (1);
190a733a5bfSmrg		displayname = argv[i];
191a733a5bfSmrg		break;
192a733a5bfSmrg	    case 'g':			/* -grammar */
193a733a5bfSmrg		grammar_usage ();
194a733a5bfSmrg		/*NOTREACHED*/
195a733a5bfSmrg	    case 'h':			/* -help */
196a733a5bfSmrg	    case '?':
197a733a5bfSmrg		usage(0);
198a733a5bfSmrg	    }
1991a30de1fSmrg	}
2001a30de1fSmrg    }
2011a30de1fSmrg
2021a30de1fSmrg    dpy = XOpenDisplay (displayname);
2031a30de1fSmrg    if (!dpy) {
2041a30de1fSmrg	fprintf (stderr, "%s:  unable to open display '%s'\n",
2051a30de1fSmrg		 ProgramName, XDisplayName (displayname));
2061a30de1fSmrg	Exit (1);
2071a30de1fSmrg    }
2081a30de1fSmrg
2091a30de1fSmrg    XDisplayKeycodes (dpy, &min_keycode, &max_keycode);
2101a30de1fSmrg
2111a30de1fSmrg    initialize_map ();
2121a30de1fSmrg
2131a30de1fSmrg    /*
2141a30de1fSmrg     * scan the arg list again to do the actual work (since it requires
2151a30de1fSmrg     * the display being open.
2161a30de1fSmrg     */
2171a30de1fSmrg
2181a30de1fSmrg    for (i = 1; i < argc; i++) {
2191a30de1fSmrg	char *arg = argv[i];
2201a30de1fSmrg
2211a30de1fSmrg	if (arg[0] == '-') {
2221a30de1fSmrg	    switch (arg[1]) {
2231a30de1fSmrg	      case 'd':			/* -display host:dpy */
2241a30de1fSmrg		++i;			/* handled above */
2251a30de1fSmrg		continue;
2261a30de1fSmrg	      case 'v':			/* -verbose */
2271a30de1fSmrg		verbose = True;
2281a30de1fSmrg		continue;
2291a30de1fSmrg	      case 'q':			/* -quiet */
2301a30de1fSmrg		verbose = False;
2311a30de1fSmrg		continue;
2321a30de1fSmrg	      case 'n':			/* -n (like make) */
2331a30de1fSmrg		dontExecute = True;
2341a30de1fSmrg		continue;
2351a30de1fSmrg	      case 'e':			/* -e expression */
2361a30de1fSmrg		didAnything = True;
237a733a5bfSmrg		if (++i >= argc) usage (1);
2381a30de1fSmrg		process_line (argv[i]);
2391a30de1fSmrg		continue;
2401a30de1fSmrg	      case 'p':			/* -p... */
2411a30de1fSmrg		switch (arg[2]) {
2421a30de1fSmrg		  case '\0':
2431a30de1fSmrg		  case 'm':		/* -pm */
2441a30de1fSmrg		    printMap = True;
2451a30de1fSmrg		    break;
2461a30de1fSmrg		  case 'k':		/* -pk, -pke */
2471a30de1fSmrg		    switch (arg[3]) {
2481a30de1fSmrg		    case '\0':
2491a30de1fSmrg			printKeyTable = True;
2501a30de1fSmrg			break;
2511a30de1fSmrg		    case 'e':
2521a30de1fSmrg			printKeyTableExprs = True;
2531a30de1fSmrg			break;
2541a30de1fSmrg		    default:
255a733a5bfSmrg			usage (1);
2561a30de1fSmrg		    }
2571a30de1fSmrg		    break;
2581a30de1fSmrg		  case 'p':		/* -pp */
2591a30de1fSmrg		    printPointerMap = True;
2601a30de1fSmrg		    break;
2611a30de1fSmrg		  default:
262a733a5bfSmrg		    usage (1);
2631a30de1fSmrg		    /* NOTREACHED */
2641a30de1fSmrg		}
2651a30de1fSmrg		didAnything = True;
2661a30de1fSmrg		continue;
2671a30de1fSmrg	      case 'g':			/* -grammar */
2681a30de1fSmrg		grammar_usage ();
2691a30de1fSmrg		/*NOTREACHED*/
2701a30de1fSmrg	      case '\0':		/* - (use standard input) */
2711a30de1fSmrg		didAnything = True;
2721a30de1fSmrg		process_file (NULL);
2731a30de1fSmrg		continue;
2741a30de1fSmrg
2751a30de1fSmrg	      /*
2761a30de1fSmrg	       * provide old xmodmap args
2771a30de1fSmrg	       */
2781a30de1fSmrg	      case 'S':
2791a30de1fSmrg		didAnything = True;
2801a30de1fSmrg		process_line ("clear shift");
2811a30de1fSmrg		continue;
2821a30de1fSmrg	      case 'L':
2831a30de1fSmrg		didAnything = True;
2841a30de1fSmrg		process_line ("clear lock");
2851a30de1fSmrg		continue;
2861a30de1fSmrg	      case 'C':
2871a30de1fSmrg		didAnything = True;
2881a30de1fSmrg		process_line ("clear control");
2891a30de1fSmrg		continue;
2901a30de1fSmrg	      case '1':
2911a30de1fSmrg	      case '2':
2921a30de1fSmrg	      case '3':
2931a30de1fSmrg	      case '4':
2941a30de1fSmrg	      case '5': {
2951a30de1fSmrg		  char cmd[11] = "clear modX";
2961a30de1fSmrg		  cmd[9] = arg[1];
2971a30de1fSmrg		  process_line (cmd);
2981a30de1fSmrg		  continue;
2991a30de1fSmrg	      }
3001a30de1fSmrg	      case 's':
3011a30de1fSmrg	      case 'l':
3021a30de1fSmrg	      case 'c': {
3031a30de1fSmrg		  char *cmd;
3041a30de1fSmrg		  didAnything = True;
305a733a5bfSmrg		  if (++i >= argc) usage (1);
306a733a5bfSmrg		  if (asprintf (&cmd, "remove %s = %s",
3071a30de1fSmrg				  ((arg[1] == 's') ? "shift" :
3081a30de1fSmrg				   ((arg[1] == 'l') ? "lock" :
309a733a5bfSmrg				    "control")), argv[i]) == -1)
310a733a5bfSmrg		      FatalError("Could not allocate memory for remove cmd");
3111a30de1fSmrg		  process_line (cmd);
3121a30de1fSmrg		  continue;
3131a30de1fSmrg	      }
3141a30de1fSmrg	      default:
315a733a5bfSmrg		usage (1);
3161a30de1fSmrg		/*NOTREACHED*/
3171a30de1fSmrg	    }
3181a30de1fSmrg	} else if (arg[0] == '+') {	/* old xmodmap args */
3191a30de1fSmrg	    switch (arg[1]) {
3201a30de1fSmrg	      case '1':
3211a30de1fSmrg	      case '2':
3221a30de1fSmrg	      case '3':
3231a30de1fSmrg	      case '4':
3241a30de1fSmrg	      case '5': {
3251a30de1fSmrg		  char *cmd;
3261a30de1fSmrg		  didAnything = True;
327a733a5bfSmrg		  if (++i >= argc) usage (1);
328a733a5bfSmrg		  if (asprintf (&cmd, "add mod%c = %s", arg[1], argv[i]) == -1)
329a733a5bfSmrg		      FatalError("Could not allocate memory for add cmd");
3301a30de1fSmrg		  process_line (cmd);
3311a30de1fSmrg		  continue;
3321a30de1fSmrg	      }
3331a30de1fSmrg	      case 'S':
3341a30de1fSmrg	      case 'L':
3351a30de1fSmrg	      case 'C':
3361a30de1fSmrg		arg[1] = tolower (arg[1]);
3371a30de1fSmrg		/* fall through to handler below */
3381a30de1fSmrg	      case 's':
3391a30de1fSmrg	      case 'l':
3401a30de1fSmrg	      case 'c': {
3411a30de1fSmrg		  char *cmd;
3421a30de1fSmrg		  didAnything = True;
343a733a5bfSmrg		  if (++i >= argc) usage (1);
344a733a5bfSmrg		  if (asprintf (&cmd, "add %s = %s",
3451a30de1fSmrg				  ((arg[1] == 's') ? "shift" :
3461a30de1fSmrg				   ((arg[1] == 'l') ? "lock" :
347a733a5bfSmrg				    "control")), argv[i]) == -1)
348a733a5bfSmrg		      FatalError("Could not allocate memory for remove cmd");
3491a30de1fSmrg		  process_line (cmd);
3501a30de1fSmrg		  continue;
3511a30de1fSmrg	      }
3521a30de1fSmrg	      default:
353a733a5bfSmrg		usage (1);
3541a30de1fSmrg	    }
3551a30de1fSmrg	} else {
3561a30de1fSmrg	    didAnything = True;
3571a30de1fSmrg	    process_file (arg);
3581a30de1fSmrg	    continue;
3591a30de1fSmrg	}
3601a30de1fSmrg    }					/* end for loop */
3611a30de1fSmrg
3621a30de1fSmrg    /* for compatibility */
3631a30de1fSmrg    if (!didAnything) printMap = True;
3641a30de1fSmrg
3651a30de1fSmrg    /*
3661a30de1fSmrg     * at this point, the work list has been built and we can view it or
3671a30de1fSmrg     * execute it
3681a30de1fSmrg     */
3691a30de1fSmrg
3701a30de1fSmrg    if (dontExecute) {
3711a30de1fSmrg	print_work_queue ();
3721a30de1fSmrg	Exit (0);
3731a30de1fSmrg    }
3741a30de1fSmrg
3751a30de1fSmrg    if (parse_errors != 0) {
3761a30de1fSmrg	fprintf (stderr, "%s:  %d error%s encountered, aborting.\n",
3771a30de1fSmrg		 ProgramName, parse_errors,
3781a30de1fSmrg		 (parse_errors == 1 ? "" : "s"));
3791a30de1fSmrg	status = -1;	/* return an error condition */
3801a30de1fSmrg    } else {
3811a30de1fSmrg	status = execute_work_queue ();
3821a30de1fSmrg    }
3831a30de1fSmrg
3841a30de1fSmrg    if (printMap) {
3851a30de1fSmrg	print_modifier_map ();
3861a30de1fSmrg    }
3871a30de1fSmrg
3881a30de1fSmrg    if (printKeyTable) {
3891a30de1fSmrg	print_key_table (False);
3901a30de1fSmrg    }
3911a30de1fSmrg
3921a30de1fSmrg    if (printKeyTableExprs) {
3931a30de1fSmrg	print_key_table (True);
3941a30de1fSmrg    }
3951a30de1fSmrg
3961a30de1fSmrg    if (printPointerMap) {
3971a30de1fSmrg	print_pointer_map ();
3981a30de1fSmrg    }
3991a30de1fSmrg
4001a30de1fSmrg    Exit (status < 0 ? 1 : 0);
4011a30de1fSmrg
4021a30de1fSmrg    /* Muffle gcc */
4031a30de1fSmrg    return 0;
4041a30de1fSmrg}
4051a30de1fSmrg
406