xenocara/app/setxkbmap/setxkbmap.c

1091 lines
32 KiB
C
Raw Normal View History

2006-11-25 13:07:29 -07:00
/************************************************************
Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc.
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
notice appear in all copies and that both that copyright
notice and this permission notice appear in supporting
2012-04-07 09:24:43 -06:00
documentation, and that the name of Silicon Graphics not be
used in advertising or publicity pertaining to distribution
2006-11-25 13:07:29 -07:00
of the software without specific prior written permission.
2012-04-07 09:24:43 -06:00
Silicon Graphics makes no representation about the suitability
2006-11-25 13:07:29 -07:00
of this software for any purpose. It is provided "as is"
without any express or implied warranty.
2012-04-07 09:24:43 -06:00
SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
2006-11-25 13:07:29 -07:00
AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
2012-04-07 09:24:43 -06:00
GRAPHICS BE LIABLE 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
2006-11-25 13:07:29 -07:00
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
THE USE OR PERFORMANCE OF THIS SOFTWARE.
********************************************************/
2015-05-10 03:54:49 -06:00
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
2006-11-25 13:07:29 -07:00
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <limits.h>
#include <ctype.h>
#include <X11/Xlib.h>
#include <X11/Xos.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XKBfile.h>
#include <X11/extensions/XKBconfig.h>
#include <X11/extensions/XKBrules.h>
#ifndef PATH_MAX
#ifdef MAXPATHLEN
#define PATH_MAX MAXPATHLEN
#else
#define PATH_MAX 1024
#endif
#endif
#ifndef DFLT_XKB_CONFIG_ROOT
2012-04-07 09:24:43 -06:00
#define DFLT_XKB_CONFIG_ROOT "/usr/share/X11/xkb"
2006-11-25 13:07:29 -07:00
#endif
#ifndef DFLT_XKB_RULES_FILE
2012-04-07 09:24:43 -06:00
#define DFLT_XKB_RULES_FILE "base"
2006-11-25 13:07:29 -07:00
#endif
#ifndef DFLT_XKB_LAYOUT
2012-04-07 09:24:43 -06:00
#define DFLT_XKB_LAYOUT "us"
2006-11-25 13:07:29 -07:00
#endif
#ifndef DFLT_XKB_MODEL
2012-04-07 09:24:43 -06:00
#define DFLT_XKB_MODEL "pc105"
2006-11-25 13:07:29 -07:00
#endif
2012-04-07 09:24:43 -06:00
/* Constants to state how a value was obtained. The order of these
2009-10-10 04:33:01 -06:00
* is important, the bigger the higher the priority.
* e.g. FROM_CONFIG overrides FROM_SERVER */
2012-04-07 09:24:43 -06:00
enum source {
UNDEFINED = 0,
FROM_SERVER, /* Retrieved from server at runtime. */
FROM_RULES, /* Xkb rules file. */
FROM_CONFIG, /* Command-line specified config file. */
FROM_CMD_LINE, /* Specified at the cmdline. */
NUM_SOURCES
};
2006-11-25 13:07:29 -07:00
/***====================================================================***/
2009-10-10 04:33:01 -06:00
static Bool print = False;
2010-10-05 23:43:08 -06:00
static Bool query = False;
2009-10-10 04:33:01 -06:00
static Bool synch = False;
static int verbose = 5;
static Display *dpy;
/**
* human-readable versions of FROM_CONFIG, FROM_SERVER, etc. Used for error
* reporting.
*/
2012-04-07 09:24:43 -06:00
static const char *srcName[NUM_SOURCES] = {
2009-10-10 04:33:01 -06:00
"undefined", "X server", "rules file", "config file", "command line"
2006-11-25 13:07:29 -07:00
};
2012-04-07 09:24:43 -06:00
struct setting {
char const *name; /* Human-readable setting name. Used for error reporting. */
char *value; /* Holds the value. */
enum source src; /* Holds the source. */
};
typedef struct setting setting_t;
struct settings {
setting_t rules; /* Rules file */
setting_t config; /* Config file (if used) */
setting_t display; /* X display name */
setting_t locale; /* Machine's locale */
setting_t model;
setting_t layout;
setting_t variant;
setting_t keycodes;
setting_t types;
setting_t compat;
setting_t symbols;
setting_t geometry;
setting_t keymap;
};
typedef struct settings settings_t;
static settings_t settings = {
{ "rules file", NULL, UNDEFINED },
{ "config file", NULL, UNDEFINED },
{ "X display", NULL, UNDEFINED },
{ "locale", NULL, UNDEFINED },
{ "keyboard model", NULL, UNDEFINED },
{ "keyboard layout", NULL, UNDEFINED },
{ "layout variant", NULL, UNDEFINED },
{ "keycodes", NULL, UNDEFINED },
{ "types", NULL, UNDEFINED },
{ "compatibility map", NULL, UNDEFINED },
{ "symbols", NULL, UNDEFINED },
{ "geometry", NULL, UNDEFINED },
{ "keymap", NULL, UNDEFINED }
2006-11-25 13:07:29 -07:00
};
2009-10-10 04:33:01 -06:00
static XkbConfigRtrnRec cfgResult;
2006-11-25 13:07:29 -07:00
2009-10-10 04:33:01 -06:00
static XkbRF_VarDefsRec rdefs;
2006-11-25 13:07:29 -07:00
2009-10-10 04:33:01 -06:00
static Bool clearOptions = False;
2006-11-25 13:07:29 -07:00
2012-04-07 09:24:43 -06:00
struct list {
char **item; /* Array of items. */
int sz; /* Size of array. */
int num; /* Number of used elements. */
};
typedef struct list list_t;
static list_t options = { NULL, 0, 0 };
static list_t inclPath = { NULL, 0, 0 };
2006-11-25 13:07:29 -07:00
2009-10-10 04:33:01 -06:00
static XkbDescPtr xkb = NULL;
2006-11-25 13:07:29 -07:00
2009-10-10 04:33:01 -06:00
static int deviceSpec = XkbUseCoreKbd;
2007-09-12 05:08:59 -06:00
2006-11-25 13:07:29 -07:00
/***====================================================================***/
2012-04-07 09:24:43 -06:00
#define streq(s1,s2) (strcmp(s1,s2)==0)
#define strpfx(s1,s2) (strncmp(s1,s2,strlen(s2))==0)
#define MSG(s) printf(s)
#define MSG1(s,a) printf(s,a)
#define MSG2(s,a,b) printf(s,a,b)
#define MSG3(s,a,b,c) printf(s,a,b,c)
2006-11-25 13:07:29 -07:00
2012-04-07 09:24:43 -06:00
#define VMSG(l,s) if (verbose>(l)) printf(s)
#define VMSG1(l,s,a) if (verbose>(l)) printf(s,a)
#define VMSG2(l,s,a,b) if (verbose>(l)) printf(s,a,b)
#define VMSG3(l,s,a,b,c) if (verbose>(l)) printf(s,a,b,c)
2006-11-25 13:07:29 -07:00
2012-04-07 09:24:43 -06:00
#define ERR(s) fprintf(stderr,s)
#define ERR1(s,a) fprintf(stderr,s,a)
#define ERR2(s,a,b) fprintf(stderr,s,a,b)
#define ERR3(s,a,b,c) fprintf(stderr,s,a,b,c)
2006-11-25 13:07:29 -07:00
2012-04-07 09:24:43 -06:00
#define OOM(ptr) do { if ((ptr) == NULL) { ERR("Out of memory.\n"); exit(-1); } } while (0)
2006-11-25 13:07:29 -07:00
/***====================================================================***/
2012-04-07 09:24:43 -06:00
Bool addToList(list_t *list, const char *newVal);
2009-10-10 04:33:01 -06:00
void usage(int argc, char **argv);
void dumpNames(Bool wantRules, Bool wantCNames);
2012-04-07 09:24:43 -06:00
void trySetString(setting_t * setting, char *newVal, enum source src);
Bool setOptString(int *arg, int argc, char **argv, setting_t *setting, enum source src);
2009-10-10 04:33:01 -06:00
int parseArgs(int argc, char **argv);
Bool getDisplay(int argc, char **argv);
Bool getServerValues(void);
2012-04-07 09:24:43 -06:00
FILE *findFileInPath(char *name);
Bool addStringToOptions(char *opt_str, list_t *opts);
char *stringFromOptions(char *orig, list_t *newOpts);
2009-10-10 04:33:01 -06:00
Bool applyConfig(char *name);
2012-04-07 09:24:43 -06:00
XkbRF_RulesPtr tryLoadRules(char *name, char *locale, Bool wantDesc, Bool wantRules);
2009-10-10 04:33:01 -06:00
Bool applyRules(void);
Bool applyComponentNames(void);
void printKeymap(void);
2006-11-25 13:07:29 -07:00
/***====================================================================***/
2012-04-07 09:24:43 -06:00
/*
If newVal is NULL or empty string, the list is cleared.
Otherwise newVal is added to the end of the list (if it is not present in the list yet).
*/
2006-11-25 13:07:29 -07:00
Bool
2012-04-07 09:24:43 -06:00
addToList(list_t *list, const char *newVal)
2006-11-25 13:07:29 -07:00
{
2009-10-10 04:33:01 -06:00
register int i;
if ((!newVal) || (!newVal[0]))
{
2012-04-07 09:24:43 -06:00
list->num = 0;
2009-10-10 04:33:01 -06:00
return True;
}
2012-04-07 09:24:43 -06:00
for (i = 0; i < list->num; i++)
2009-10-10 04:33:01 -06:00
{
2012-04-07 09:24:43 -06:00
if (streq(list->item[i], newVal))
2009-10-10 04:33:01 -06:00
return True;
}
2012-04-07 09:24:43 -06:00
if ((list->item == NULL) || (list->sz < 1))
2009-10-10 04:33:01 -06:00
{
2012-04-07 09:24:43 -06:00
list->num = 0;
list->sz = 4;
list->item = (char **) calloc(list->sz, sizeof(char *));
OOM(list->item);
2009-10-10 04:33:01 -06:00
}
2012-04-07 09:24:43 -06:00
else if (list->num >= list->sz)
2009-10-10 04:33:01 -06:00
{
2012-04-07 09:24:43 -06:00
list->sz *= 2;
list->item = (char **) realloc(list->item, (list->sz) * sizeof(char *));
OOM(list->item);
2009-10-10 04:33:01 -06:00
}
2012-04-07 09:24:43 -06:00
list->item[list->num] = strdup(newVal);
OOM(list->item[list->num]);
list->num += 1;
2006-11-25 13:07:29 -07:00
return True;
}
/***====================================================================***/
void
2009-10-10 04:33:01 -06:00
usage(int argc, char **argv)
2006-11-25 13:07:29 -07:00
{
2012-04-07 09:24:43 -06:00
MSG1(
"Usage: %s [options] [<layout> [<variant> [<option> ... ]]]\n"
"Options:\n"
" -?, -help Print this message\n"
" -compat <name> Specifies compatibility map component name\n"
" -config <file> Specifies configuration file to use\n"
" -device <deviceid> Specifies the device ID to use\n"
" -display <dpy> Specifies display to use\n"
" -geometry <name> Specifies geometry component name\n"
2015-05-10 03:54:49 -06:00
" -I <dir> Add <dir> to list of directories to be used\n"
2012-04-07 09:24:43 -06:00
" -keycodes <name> Specifies keycodes component name\n"
" -keymap <name> Specifies name of keymap to load\n"
" -layout <name> Specifies layout used to choose component names\n"
" -model <name> Specifies model used to choose component names\n"
" -option <name> Adds an option used to choose component names\n"
" -print Print a complete xkb_keymap description and exit\n"
" -query Print the current layout settings and exit\n"
" -rules <name> Name of rules file to use\n"
" -symbols <name> Specifies symbols component name\n"
" -synch Synchronize request with X server\n"
" -types <name> Specifies types component name\n"
2015-05-10 03:54:49 -06:00
" -v[erbose] [<lvl>] Sets verbosity (1..10); higher values yield more messages\n"
" -version Print the program's version number\n"
2012-04-07 09:24:43 -06:00
" -variant <name> Specifies layout variant used to choose component names\n",
argv[0]
);
2006-11-25 13:07:29 -07:00
}
void
2009-10-10 04:33:01 -06:00
dumpNames(Bool wantRules, Bool wantCNames)
2006-11-25 13:07:29 -07:00
{
2009-10-10 04:33:01 -06:00
if (wantRules)
{
2012-04-07 09:24:43 -06:00
if (settings.rules.value)
MSG1("rules: %s\n", settings.rules.value);
if (settings.model.value)
MSG1("model: %s\n", settings.model.value);
if (settings.layout.value)
MSG1("layout: %s\n", settings.layout.value);
if (settings.variant.value)
MSG1("variant: %s\n", settings.variant.value);
if (options.item)
2009-10-10 04:33:01 -06:00
{
2012-04-07 09:24:43 -06:00
char *opt_str = stringFromOptions(NULL, &options);
2009-10-10 04:33:01 -06:00
MSG1("options: %s\n", opt_str);
free(opt_str);
}
}
if (wantCNames)
{
2012-04-07 09:24:43 -06:00
if (settings.keymap.value)
MSG1("keymap: %s\n", settings.keymap.value);
if (settings.keycodes.value)
MSG1("keycodes: %s\n", settings.keycodes.value);
if (settings.types.value)
MSG1("types: %s\n", settings.types.value);
if (settings.compat.value)
MSG1("compat: %s\n", settings.compat.value);
if (settings.symbols.value)
MSG1("symbols: %s\n", settings.symbols.value);
if (settings.geometry.value)
MSG1("geometry: %s\n", settings.geometry.value);
2006-11-25 13:07:29 -07:00
}
return;
}
/***====================================================================***/
2009-10-10 04:33:01 -06:00
/**
* Set the given string (obtained from src) in the svValue/svSrc globals.
* If the given item is already set, it is overridden if the original source
* is less significant than the given one.
*
* @param which What value is it (one of RULES_NDX, CONFIG_NDX, ...)
*/
2006-11-25 13:07:29 -07:00
void
2012-04-07 09:24:43 -06:00
trySetString(setting_t *setting, char *newVal, enum source src)
2006-11-25 13:07:29 -07:00
{
2012-04-07 09:24:43 -06:00
if (setting->value != NULL)
2009-10-10 04:33:01 -06:00
{
2012-04-07 09:24:43 -06:00
if (setting->src == src)
2009-10-10 04:33:01 -06:00
{
VMSG2(0, "Warning! More than one %s from %s\n",
2012-04-07 09:24:43 -06:00
setting->name, srcName[src]);
2009-10-10 04:33:01 -06:00
VMSG2(0, " Using \"%s\", ignoring \"%s\"\n",
2012-04-07 09:24:43 -06:00
setting->value, newVal);
2009-10-10 04:33:01 -06:00
return;
}
2012-04-07 09:24:43 -06:00
else if (setting->src > src)
2009-10-10 04:33:01 -06:00
{
2012-04-07 09:24:43 -06:00
VMSG1(5, "Warning! Multiple definitions of %s\n", setting->name);
2009-10-10 04:33:01 -06:00
VMSG2(5, " Using %s, ignoring %s\n",
2012-04-07 09:24:43 -06:00
srcName[setting->src], srcName[src]);
2009-10-10 04:33:01 -06:00
return;
}
}
2012-04-07 09:24:43 -06:00
setting->src = src;
setting->value = newVal;
2006-11-25 13:07:29 -07:00
return;
}
Bool
2012-04-07 09:24:43 -06:00
setOptString(int *arg, int argc, char **argv, setting_t *setting, enum source src)
2006-11-25 13:07:29 -07:00
{
2009-10-10 04:33:01 -06:00
int ndx;
char *opt;
ndx = *arg;
opt = argv[ndx];
if (ndx >= argc - 1)
{
2012-04-07 09:24:43 -06:00
VMSG1(0, "No %s specified on the command line\n", setting->name);
2009-10-10 04:33:01 -06:00
VMSG1(0, "Trailing %s option ignored\n", opt);
return True;
2006-11-25 13:07:29 -07:00
}
ndx++;
2009-10-10 04:33:01 -06:00
*arg = ndx;
2012-04-07 09:24:43 -06:00
if (setting->value != NULL)
2009-10-10 04:33:01 -06:00
{
2012-04-07 09:24:43 -06:00
if (setting->src == src)
2009-10-10 04:33:01 -06:00
{
2012-04-07 09:24:43 -06:00
VMSG2(0, "More than one %s on %s\n", setting->name, srcName[src]);
VMSG2(0, "Using \"%s\", ignoring \"%s\"\n", setting->value,
2009-10-10 04:33:01 -06:00
argv[ndx]);
return True;
}
2012-04-07 09:24:43 -06:00
else if (setting->src > src)
2009-10-10 04:33:01 -06:00
{
2012-04-07 09:24:43 -06:00
VMSG1(5, "Multiple definitions of %s\n", setting->name);
VMSG2(5, "Using %s, ignoring %s\n", srcName[setting->src],
2009-10-10 04:33:01 -06:00
srcName[src]);
return True;
}
}
2012-04-07 09:24:43 -06:00
setting->src = src;
setting->value = argv[ndx];
2006-11-25 13:07:29 -07:00
return True;
}
/***====================================================================***/
2009-10-10 04:33:01 -06:00
/**
* Parse commandline arguments.
* Return True on success or False if an unrecognized option has been
* specified.
*/
2006-11-25 13:07:29 -07:00
int
2009-10-10 04:33:01 -06:00
parseArgs(int argc, char **argv)
2006-11-25 13:07:29 -07:00
{
2009-10-10 04:33:01 -06:00
int i;
Bool ok;
unsigned present;
ok = True;
2012-04-07 09:24:43 -06:00
addToList(&inclPath, ".");
addToList(&inclPath, DFLT_XKB_CONFIG_ROOT);
2009-10-10 04:33:01 -06:00
for (i = 1; (i < argc) && ok; i++)
{
if (argv[i][0] != '-')
{
/* Allow a call like "setxkbmap us" to work. Layout is default,
if -layout is given, then try parsing variant, then options */
2012-04-07 09:24:43 -06:00
if (!settings.layout.src)
trySetString(&settings.layout, argv[i], FROM_CMD_LINE);
else if (!settings.variant.src)
trySetString(&settings.variant, argv[i], FROM_CMD_LINE);
2009-10-10 04:33:01 -06:00
else
2012-04-07 09:24:43 -06:00
ok = addToList(&options, argv[i]);
2009-10-10 04:33:01 -06:00
}
else if (streq(argv[i], "-compat"))
2012-04-07 09:24:43 -06:00
ok = setOptString(&i, argc, argv, &settings.compat, FROM_CMD_LINE);
2009-10-10 04:33:01 -06:00
else if (streq(argv[i], "-config"))
2012-04-07 09:24:43 -06:00
ok = setOptString(&i, argc, argv, &settings.config, FROM_CMD_LINE);
2009-10-10 04:33:01 -06:00
else if (streq(argv[i], "-device"))
2012-04-07 09:24:43 -06:00
{
if ( ++i < argc ) {
deviceSpec = atoi(argv[i]); /* only allow device IDs, not names */
} else {
usage(argc, argv);
exit(-1);
}
}
2009-10-10 04:33:01 -06:00
else if (streq(argv[i], "-display"))
2012-04-07 09:24:43 -06:00
ok = setOptString(&i, argc, argv, &settings.display, FROM_CMD_LINE);
2009-10-10 04:33:01 -06:00
else if (streq(argv[i], "-geometry"))
2012-04-07 09:24:43 -06:00
ok = setOptString(&i, argc, argv, &settings.geometry, FROM_CMD_LINE);
2009-10-10 04:33:01 -06:00
else if (streq(argv[i], "-help") || streq(argv[i], "-?"))
{
usage(argc, argv);
exit(0);
}
2012-04-07 09:24:43 -06:00
else if (streq(argv[i], "-I")) /* space between -I and path */
{
if ( ++i < argc )
ok = addToList(&inclPath, argv[i]);
else
VMSG(0, "No directory specified on the command line\n"
"Trailing -I option ignored\n");
}
else if (strpfx(argv[i], "-I")) /* no space between -I and path */
ok = addToList(&inclPath, &argv[i][2]);
2009-10-10 04:33:01 -06:00
else if (streq(argv[i], "-keycodes"))
2012-04-07 09:24:43 -06:00
ok = setOptString(&i, argc, argv, &settings.keycodes, FROM_CMD_LINE);
2009-10-10 04:33:01 -06:00
else if (streq(argv[i], "-keymap"))
2012-04-07 09:24:43 -06:00
ok = setOptString(&i, argc, argv, &settings.keymap, FROM_CMD_LINE);
2009-10-10 04:33:01 -06:00
else if (streq(argv[i], "-layout"))
2012-04-07 09:24:43 -06:00
ok = setOptString(&i, argc, argv, &settings.layout, FROM_CMD_LINE);
2009-10-10 04:33:01 -06:00
else if (streq(argv[i], "-model"))
2012-04-07 09:24:43 -06:00
ok = setOptString(&i, argc, argv, &settings.model, FROM_CMD_LINE);
2009-10-10 04:33:01 -06:00
else if (streq(argv[i], "-option"))
{
if ((i == argc - 1) || (argv[i + 1][0] == '\0')
|| (argv[i + 1][0] == '-'))
{
clearOptions = True;
2012-04-07 09:24:43 -06:00
ok = addToList(&options, "");
2009-10-10 04:33:01 -06:00
if (i < argc - 1 && argv[i + 1][0] == '\0')
i++;
}
else
{
2012-04-07 09:24:43 -06:00
ok = addToList(&options, argv[++i]);
2009-10-10 04:33:01 -06:00
}
}
else if (streq(argv[i], "-print"))
print = True;
2010-10-05 23:43:08 -06:00
else if (streq(argv[i], "-query"))
query = True;
2009-10-10 04:33:01 -06:00
else if (streq(argv[i], "-rules"))
2012-04-07 09:24:43 -06:00
ok = setOptString(&i, argc, argv, &settings.rules, FROM_CMD_LINE);
2009-10-10 04:33:01 -06:00
else if (streq(argv[i], "-symbols"))
2012-04-07 09:24:43 -06:00
ok = setOptString(&i, argc, argv, &settings.symbols, FROM_CMD_LINE);
2009-10-10 04:33:01 -06:00
else if (streq(argv[i], "-synch"))
synch = True;
else if (streq(argv[i], "-types"))
2012-04-07 09:24:43 -06:00
ok = setOptString(&i, argc, argv, &settings.types, FROM_CMD_LINE);
2015-05-10 03:54:49 -06:00
else if (streq(argv[i], "-version"))
{
MSG1("setxkbmap %s\n", PACKAGE_VERSION);
exit(0);
}
2009-10-10 04:33:01 -06:00
else if (streq(argv[i], "-verbose") || (streq(argv[i], "-v")))
{
if ((i < argc - 1) && (isdigit(argv[i + 1][0])))
verbose = atoi(argv[++i]);
else
verbose++;
if (verbose < 0)
{
ERR1("Illegal verbose level %d. Reset to 0\n", verbose);
verbose = 0;
}
else if (verbose > 10)
{
ERR1("Illegal verbose level %d. Reset to 10\n", verbose);
verbose = 10;
}
VMSG1(7, "Setting verbose level to %d\n", verbose);
}
else if (streq(argv[i], "-variant"))
2012-04-07 09:24:43 -06:00
ok = setOptString(&i, argc, argv, &settings.variant, FROM_CMD_LINE);
2009-10-10 04:33:01 -06:00
else
{
ERR1("Error! Option \"%s\" not recognized\n", argv[i]);
ok = False;
}
}
present = 0;
2012-04-07 09:24:43 -06:00
if (settings.types.value)
2009-10-10 04:33:01 -06:00
present++;
2012-04-07 09:24:43 -06:00
if (settings.compat.value)
2009-10-10 04:33:01 -06:00
present++;
2012-04-07 09:24:43 -06:00
if (settings.symbols.value)
2009-10-10 04:33:01 -06:00
present++;
2012-04-07 09:24:43 -06:00
if (settings.keycodes.value)
2009-10-10 04:33:01 -06:00
present++;
2012-04-07 09:24:43 -06:00
if (settings.geometry.value)
2009-10-10 04:33:01 -06:00
present++;
2012-04-07 09:24:43 -06:00
if (settings.config.value)
2009-10-10 04:33:01 -06:00
present++;
2012-04-07 09:24:43 -06:00
if (settings.model.value)
2009-10-10 04:33:01 -06:00
present++;
2012-04-07 09:24:43 -06:00
if (settings.layout.value)
2009-10-10 04:33:01 -06:00
present++;
2012-04-07 09:24:43 -06:00
if (settings.variant.value)
2009-10-10 04:33:01 -06:00
present++;
2012-04-07 09:24:43 -06:00
if (settings.keymap.value && present)
2009-10-10 04:33:01 -06:00
{
ERR("No other components can be specified when a keymap is present\n");
return False;
2006-11-25 13:07:29 -07:00
}
return ok;
}
2009-10-10 04:33:01 -06:00
/**
* Open a connection to the display and print error if it fails.
*
* @return True on success or False otherwise.
*/
2006-11-25 13:07:29 -07:00
Bool
2009-10-10 04:33:01 -06:00
getDisplay(int argc, char **argv)
2006-11-25 13:07:29 -07:00
{
2009-10-10 04:33:01 -06:00
int major, minor, why;
major = XkbMajorVersion;
minor = XkbMinorVersion;
dpy =
2012-04-07 09:24:43 -06:00
XkbOpenDisplay(settings.display.value, NULL, NULL, &major, &minor,
2009-10-10 04:33:01 -06:00
&why);
if (!dpy)
{
2012-04-07 09:24:43 -06:00
if (settings.display.value == NULL)
settings.display.value = getenv("DISPLAY");
if (settings.display.value == NULL)
settings.display.value = "default display";
2009-10-10 04:33:01 -06:00
switch (why)
{
case XkbOD_BadLibraryVersion:
ERR3("%s was compiled with XKB version %d.%02d\n", argv[0],
XkbMajorVersion, XkbMinorVersion);
ERR2("Xlib supports incompatible version %d.%02d\n",
major, minor);
break;
case XkbOD_ConnectionRefused:
2012-04-07 09:24:43 -06:00
ERR1("Cannot open display \"%s\"\n", settings.display.value);
2009-10-10 04:33:01 -06:00
break;
case XkbOD_NonXkbServer:
2012-04-07 09:24:43 -06:00
ERR1("XKB extension not present on %s\n", settings.display.value);
2009-10-10 04:33:01 -06:00
break;
case XkbOD_BadServerVersion:
ERR3("%s was compiled with XKB version %d.%02d\n", argv[0],
XkbMajorVersion, XkbMinorVersion);
ERR3("Server %s uses incompatible version %d.%02d\n",
2012-04-07 09:24:43 -06:00
settings.display.value, major, minor);
2009-10-10 04:33:01 -06:00
break;
default:
ERR1("Unknown error %d from XkbOpenDisplay\n", why);
break;
}
return False;
2006-11-25 13:07:29 -07:00
}
if (synch)
2009-10-10 04:33:01 -06:00
XSynchronize(dpy, True);
2006-11-25 13:07:29 -07:00
return True;
}
/***====================================================================***/
2009-10-10 04:33:01 -06:00
/**
2010-10-05 23:43:08 -06:00
* Retrieve xkb values from the XKB_RULES_NAMES property and store their
2009-10-10 04:33:01 -06:00
* contents in svValues.
* If the property cannot be read, the built-in defaults are used.
*
* @return True.
*/
2006-11-25 13:07:29 -07:00
Bool
getServerValues(void)
{
2009-10-10 04:33:01 -06:00
XkbRF_VarDefsRec vd;
char *tmp = NULL;
2006-11-25 13:07:29 -07:00
2009-10-10 04:33:01 -06:00
if (!XkbRF_GetNamesProp(dpy, &tmp, &vd) || !tmp)
{
VMSG1(3, "Couldn't interpret %s property\n", _XKB_RF_NAMES_PROP_ATOM);
2006-11-25 13:07:29 -07:00
tmp = DFLT_XKB_RULES_FILE;
vd.model = DFLT_XKB_MODEL;
vd.layout = DFLT_XKB_LAYOUT;
vd.variant = NULL;
vd.options = NULL;
2009-10-10 04:33:01 -06:00
VMSG3(3, "Use defaults: rules - '%s' model - '%s' layout - '%s'\n",
tmp, vd.model, vd.layout);
2006-11-25 13:07:29 -07:00
}
if (tmp)
2012-04-07 09:24:43 -06:00
trySetString(&settings.rules, tmp, FROM_SERVER);
2006-11-25 13:07:29 -07:00
if (vd.model)
2012-04-07 09:24:43 -06:00
trySetString(&settings.model, vd.model, FROM_SERVER);
2006-11-25 13:07:29 -07:00
if (vd.layout)
2012-04-07 09:24:43 -06:00
trySetString(&settings.layout, vd.layout, FROM_SERVER);
2006-11-25 13:07:29 -07:00
if (vd.variant)
2012-04-07 09:24:43 -06:00
trySetString(&settings.variant, vd.variant, FROM_SERVER);
2009-10-10 04:33:01 -06:00
if ((vd.options) && (!clearOptions))
{
2012-04-07 09:24:43 -06:00
addStringToOptions(vd.options, &options);
2009-10-10 04:33:01 -06:00
XFree(vd.options);
2006-11-25 13:07:29 -07:00
}
return True;
}
/***====================================================================***/
FILE *
2012-04-07 09:24:43 -06:00
findFileInPath(char *name)
2006-11-25 13:07:29 -07:00
{
2009-10-10 04:33:01 -06:00
register int i;
char buf[PATH_MAX];
FILE *fp;
if (name[0] == '/')
{
fp = fopen(name, "r");
if ((verbose > 7) || ((!fp) && (verbose > 0)))
MSG2("%s file %s\n", (fp ? "Found" : "Didn't find"), name);
return fp;
}
2012-04-07 09:24:43 -06:00
for (i = 0; (i < inclPath.num); i++)
2009-10-10 04:33:01 -06:00
{
2012-04-07 09:24:43 -06:00
if (snprintf(buf, PATH_MAX, "%s/%s", inclPath.item[i], name) >=
2009-10-10 04:33:01 -06:00
PATH_MAX)
{
2012-04-07 09:24:43 -06:00
VMSG2(0, "Path too long (%s/%s). Ignored.\n", inclPath.item[i],
name);
2009-10-10 04:33:01 -06:00
continue;
}
2012-04-07 09:24:43 -06:00
fp = fopen(buf, "r");
2009-10-10 04:33:01 -06:00
if ((verbose > 7) || ((!fp) && (verbose > 5)))
MSG2("%s file %s\n", (fp ? "Found" : "Didn't find"), buf);
if (fp != NULL)
return fp;
2006-11-25 13:07:29 -07:00
}
return NULL;
}
/***====================================================================***/
Bool
2012-04-07 09:24:43 -06:00
addStringToOptions(char *opt_str, list_t *opts)
2006-11-25 13:07:29 -07:00
{
2009-10-10 04:33:01 -06:00
char *tmp, *str, *next;
Bool ok = True;
2006-11-25 13:07:29 -07:00
2012-04-07 09:24:43 -06:00
str = strdup(opt_str);
OOM(str);
for (tmp = str; (tmp && *tmp != '\0') && ok; tmp = next)
2009-10-10 04:33:01 -06:00
{
next = strchr(str, ',');
if (next)
{
*next = '\0';
next++;
}
2012-04-07 09:24:43 -06:00
ok = addToList(opts, tmp) && ok;
2006-11-25 13:07:29 -07:00
}
free(str);
return ok;
}
/***====================================================================***/
char *
2012-04-07 09:24:43 -06:00
stringFromOptions(char *orig, list_t *newOpts)
2006-11-25 13:07:29 -07:00
{
2015-05-10 03:54:49 -06:00
size_t len;
int i, nOut;
2009-10-10 04:33:01 -06:00
if (orig)
len = strlen(orig) + 1;
else
len = 0;
2012-04-07 09:24:43 -06:00
for (i = 0; i < newOpts->num; i++)
2009-10-10 04:33:01 -06:00
{
2012-04-07 09:24:43 -06:00
if (newOpts->item[i])
len += strlen(newOpts->item[i]) + 1;
2009-10-10 04:33:01 -06:00
}
if (len < 1)
return NULL;
if (orig)
{
orig = (char *) realloc(orig, len);
2012-04-07 09:24:43 -06:00
OOM(orig);
2009-10-10 04:33:01 -06:00
nOut = 1;
}
else
{
orig = (char *) calloc(len, 1);
2012-04-07 09:24:43 -06:00
OOM(orig);
2009-10-10 04:33:01 -06:00
nOut = 0;
}
2012-04-07 09:24:43 -06:00
for (i = 0; i < newOpts->num; i++)
2009-10-10 04:33:01 -06:00
{
2012-04-07 09:24:43 -06:00
if (!newOpts->item[i])
2009-10-10 04:33:01 -06:00
continue;
if (nOut > 0)
{
strcat(orig, ",");
2012-04-07 09:24:43 -06:00
strcat(orig, newOpts->item[i]);
2009-10-10 04:33:01 -06:00
}
else
2012-04-07 09:24:43 -06:00
strcpy(orig, newOpts->item[i]);
2009-10-10 04:33:01 -06:00
nOut++;
2006-11-25 13:07:29 -07:00
}
return orig;
}
/***====================================================================***/
Bool
applyConfig(char *name)
{
2009-10-10 04:33:01 -06:00
FILE *fp;
Bool ok;
2012-04-07 09:24:43 -06:00
if ((fp = findFileInPath(name)) == NULL)
2019-08-15 11:03:15 -06:00
{
ERR1("Couldn't find configuration file \"%s\"\n", name);
2009-10-10 04:33:01 -06:00
return False;
2019-08-15 11:03:15 -06:00
}
2009-10-10 04:33:01 -06:00
ok = XkbCFParse(fp, XkbCFDflts, NULL, &cfgResult);
2006-11-25 13:07:29 -07:00
fclose(fp);
2009-10-10 04:33:01 -06:00
if (!ok)
{
2019-08-15 11:03:15 -06:00
ERR1("Couldn't parse configuration file \"%s\"\n", name);
2009-10-10 04:33:01 -06:00
return False;
2006-11-25 13:07:29 -07:00
}
2009-10-10 04:33:01 -06:00
if (cfgResult.rules_file)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.rules, cfgResult.rules_file, FROM_CONFIG);
2009-10-10 04:33:01 -06:00
cfgResult.rules_file = NULL;
2006-11-25 13:07:29 -07:00
}
2009-10-10 04:33:01 -06:00
if (cfgResult.model)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.model, cfgResult.model, FROM_CONFIG);
2009-10-10 04:33:01 -06:00
cfgResult.model = NULL;
2006-11-25 13:07:29 -07:00
}
2009-10-10 04:33:01 -06:00
if (cfgResult.layout)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.layout, cfgResult.layout, FROM_CONFIG);
2009-10-10 04:33:01 -06:00
cfgResult.layout = NULL;
2006-11-25 13:07:29 -07:00
}
2009-10-10 04:33:01 -06:00
if (cfgResult.variant)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.variant, cfgResult.variant, FROM_CONFIG);
2009-10-10 04:33:01 -06:00
cfgResult.variant = NULL;
2006-11-25 13:07:29 -07:00
}
2009-10-10 04:33:01 -06:00
if (cfgResult.options)
{
2012-04-07 09:24:43 -06:00
addStringToOptions(cfgResult.options, &options);
2009-10-10 04:33:01 -06:00
cfgResult.options = NULL;
2006-11-25 13:07:29 -07:00
}
2009-10-10 04:33:01 -06:00
if (cfgResult.keymap)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.keymap, cfgResult.keymap, FROM_CONFIG);
2009-10-10 04:33:01 -06:00
cfgResult.keymap = NULL;
2006-11-25 13:07:29 -07:00
}
2009-10-10 04:33:01 -06:00
if (cfgResult.keycodes)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.keycodes, cfgResult.keycodes, FROM_CONFIG);
2009-10-10 04:33:01 -06:00
cfgResult.keycodes = NULL;
2006-11-25 13:07:29 -07:00
}
2009-10-10 04:33:01 -06:00
if (cfgResult.geometry)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.geometry, cfgResult.geometry, FROM_CONFIG);
2009-10-10 04:33:01 -06:00
cfgResult.geometry = NULL;
2006-11-25 13:07:29 -07:00
}
2009-10-10 04:33:01 -06:00
if (cfgResult.symbols)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.symbols, cfgResult.symbols, FROM_CONFIG);
2009-10-10 04:33:01 -06:00
cfgResult.symbols = NULL;
2006-11-25 13:07:29 -07:00
}
2009-10-10 04:33:01 -06:00
if (cfgResult.types)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.types, cfgResult.types, FROM_CONFIG);
2009-10-10 04:33:01 -06:00
cfgResult.types = NULL;
2006-11-25 13:07:29 -07:00
}
2009-10-10 04:33:01 -06:00
if (cfgResult.compat)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.compat, cfgResult.compat, FROM_CONFIG);
2009-10-10 04:33:01 -06:00
cfgResult.compat = NULL;
2006-11-25 13:07:29 -07:00
}
2009-10-10 04:33:01 -06:00
if (verbose > 5)
{
MSG("After config file:\n");
dumpNames(True, True);
2006-11-25 13:07:29 -07:00
}
return True;
}
2012-04-07 09:24:43 -06:00
XkbRF_RulesPtr
tryLoadRules(char *name, char *locale, Bool wantDesc, Bool wantRules)
{
XkbRF_RulesPtr rules = NULL;
VMSG1(7, "Trying to load rules file %s...\n", name);
rules = XkbRF_Load(name, locale, wantDesc, wantRules);
if (rules)
{
VMSG(7, "Success.\n");
}
return rules;
}
2009-10-10 04:33:01 -06:00
/**
* If any of model, layout, variant or options is specified, then compile the
* options into the
*
* @return True on success or false otherwise.
*/
2006-11-25 13:07:29 -07:00
Bool
applyRules(void)
{
2009-10-10 04:33:01 -06:00
int i;
char *rfName;
2015-05-10 03:54:49 -06:00
XkbRF_RulesPtr rules = NULL;
2006-11-25 13:07:29 -07:00
2012-04-07 09:24:43 -06:00
if (settings.model.src || settings.layout.src || settings.variant.src
|| options.item)
2009-10-10 04:33:01 -06:00
{
char buf[PATH_MAX];
XkbComponentNamesRec rnames;
2006-11-25 13:07:29 -07:00
2012-04-07 09:24:43 -06:00
if (settings.variant.src < settings.layout.src)
settings.variant.value = NULL;
2006-11-25 13:07:29 -07:00
2012-04-07 09:24:43 -06:00
rdefs.model = settings.model.value;
rdefs.layout = settings.layout.value;
rdefs.variant = settings.variant.value;
if (options.item)
2009-10-10 04:33:01 -06:00
rdefs.options =
2012-04-07 09:24:43 -06:00
stringFromOptions(rdefs.options, &options);
2009-10-10 04:33:01 -06:00
2012-04-07 09:24:43 -06:00
if (settings.rules.src)
rfName = settings.rules.value;
2009-10-10 04:33:01 -06:00
else
rfName = DFLT_XKB_RULES_FILE;
if (rfName[0] == '/')
{
2012-04-07 09:24:43 -06:00
rules = tryLoadRules(rfName, settings.locale.value, True, True);
2009-10-10 04:33:01 -06:00
}
else
{
/* try to load rules files from all include paths until the first
* we succeed with */
2012-04-07 09:24:43 -06:00
for (i = 0; (i < inclPath.num) && (!rules); i++)
2009-10-10 04:33:01 -06:00
{
2012-04-07 09:24:43 -06:00
if (snprintf(buf, PATH_MAX, "%s/rules/%s",
inclPath.item[i], rfName) >= PATH_MAX)
2009-10-10 04:33:01 -06:00
{
VMSG2(0, "Path too long (%s/rules/%s). Ignored.\n",
2012-04-07 09:24:43 -06:00
inclPath.item[i], rfName);
2009-10-10 04:33:01 -06:00
continue;
}
2012-04-07 09:24:43 -06:00
rules = tryLoadRules(buf, settings.locale.value, True, True);
2009-10-10 04:33:01 -06:00
}
}
if (!rules)
{
2012-04-07 09:24:43 -06:00
ERR1("Couldn't find rules file (%s) \n", rfName);
2009-10-10 04:33:01 -06:00
return False;
}
/* Let the rules file to the magic, then update the svValues with
* those returned after processing the rules */
XkbRF_GetComponents(rules, &rdefs, &rnames);
if (rnames.keycodes)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.keycodes, rnames.keycodes, FROM_RULES);
2009-10-10 04:33:01 -06:00
rnames.keycodes = NULL;
}
if (rnames.symbols)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.symbols, rnames.symbols, FROM_RULES);
2009-10-10 04:33:01 -06:00
rnames.symbols = NULL;
}
if (rnames.types)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.types, rnames.types, FROM_RULES);
2009-10-10 04:33:01 -06:00
rnames.types = NULL;
}
if (rnames.compat)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.compat, rnames.compat, FROM_RULES);
2009-10-10 04:33:01 -06:00
rnames.compat = NULL;
}
if (rnames.geometry)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.geometry, rnames.geometry, FROM_RULES);
2009-10-10 04:33:01 -06:00
rnames.geometry = NULL;
}
if (rnames.keymap)
{
2012-04-07 09:24:43 -06:00
trySetString(&settings.keymap, rnames.keymap, FROM_RULES);
2009-10-10 04:33:01 -06:00
rnames.keymap = NULL;
}
if (verbose > 6)
{
2012-04-07 09:24:43 -06:00
MSG1("Applied rules from %s:\n", rfName);
2009-10-10 04:33:01 -06:00
dumpNames(True, False);
}
}
else if (verbose > 6)
{
MSG("No rules variables specified. Rules file ignored\n");
2006-11-25 13:07:29 -07:00
}
return True;
}
/* Primitive sanity check - filter out 'map names' (inside parenthesis) */
/* that can confuse xkbcomp parser */
2009-10-10 04:33:01 -06:00
static Bool
2012-04-07 09:24:43 -06:00
checkName(char *name, const char *string)
2006-11-25 13:07:29 -07:00
{
2009-10-10 04:33:01 -06:00
char *i = name, *opar = NULL;
Bool ret = True;
if (!name)
return True;
while (*i)
{
if (opar == NULL)
{
if (*i == '(')
opar = i;
}
else
{
if ((*i == '(') || (*i == '|') || (*i == '+'))
{
ret = False;
break;
}
if (*i == ')')
opar = NULL;
}
i++;
}
if (opar)
ret = False;
if (!ret)
{
char c;
int n = 1;
for (i = opar + 1; *i && n; i++)
{
if (*i == '(')
n++;
if (*i == ')')
n--;
}
if (*i)
i++;
c = *i;
*i = '\0';
ERR1("Illegal map name '%s' ", opar);
*i = c;
ERR2("in %s name '%s'\n", string, name);
}
return ret;
2006-11-25 13:07:29 -07:00
}
void
printKeymap(void)
{
MSG("xkb_keymap {\n");
2012-04-07 09:24:43 -06:00
if (settings.keycodes.value)
MSG1("\txkb_keycodes { include \"%s\"\t};\n", settings.keycodes.value);
if (settings.types.value)
MSG1("\txkb_types { include \"%s\"\t};\n", settings.types.value);
if (settings.compat.value)
MSG1("\txkb_compat { include \"%s\"\t};\n", settings.compat.value);
if (settings.symbols.value)
MSG1("\txkb_symbols { include \"%s\"\t};\n", settings.symbols.value);
if (settings.geometry.value)
MSG1("\txkb_geometry { include \"%s\"\t};\n", settings.geometry.value);
2006-11-25 13:07:29 -07:00
MSG("};\n");
}
Bool
applyComponentNames(void)
{
2012-04-07 09:24:43 -06:00
if (!checkName(settings.types.value, "types"))
2009-10-10 04:33:01 -06:00
return False;
2012-04-07 09:24:43 -06:00
if (!checkName(settings.compat.value, "compat"))
2009-10-10 04:33:01 -06:00
return False;
2012-04-07 09:24:43 -06:00
if (!checkName(settings.symbols.value, "symbols"))
2009-10-10 04:33:01 -06:00
return False;
2012-04-07 09:24:43 -06:00
if (!checkName(settings.keycodes.value, "keycodes"))
2009-10-10 04:33:01 -06:00
return False;
2012-04-07 09:24:43 -06:00
if (!checkName(settings.geometry.value, "geometry"))
2009-10-10 04:33:01 -06:00
return False;
2012-04-07 09:24:43 -06:00
if (!checkName(settings.keymap.value, "keymap"))
2009-10-10 04:33:01 -06:00
return False;
if (verbose > 5)
{
MSG("Trying to build keymap using the following components:\n");
dumpNames(False, True);
}
/* Upload the new description to the server. */
2010-10-05 23:43:08 -06:00
if (dpy && !print && !query)
2009-10-10 04:33:01 -06:00
{
2015-05-10 03:54:49 -06:00
XkbComponentNamesRec cmdNames = {
.keymap = settings.keymap.value,
.keycodes = settings.keycodes.value,
.types = settings.types.value,
.compat = settings.compat.value,
.symbols = settings.symbols.value,
.geometry = settings.geometry.value
};
2009-10-10 04:33:01 -06:00
xkb = XkbGetKeyboardByName(dpy, deviceSpec, &cmdNames,
XkbGBN_AllComponentsMask,
XkbGBN_AllComponentsMask &
(~XkbGBN_GeometryMask), True);
if (!xkb)
{
ERR("Error loading new keyboard description\n");
return False;
}
/* update the XKB root property */
2012-04-07 09:24:43 -06:00
if (settings.rules.value && (rdefs.model || rdefs.layout))
2009-10-10 04:33:01 -06:00
{
2012-04-07 09:24:43 -06:00
if (!XkbRF_SetNamesProp(dpy, settings.rules.value, &rdefs))
2009-10-10 04:33:01 -06:00
{
VMSG(0, "Error updating the XKB names property\n");
}
}
}
if (print)
{
2006-11-25 13:07:29 -07:00
printKeymap();
}
2010-10-05 23:43:08 -06:00
if (query)
{
2012-04-07 09:24:43 -06:00
dumpNames(True, False);
2010-10-05 23:43:08 -06:00
}
2006-11-25 13:07:29 -07:00
return True;
}
int
2009-10-10 04:33:01 -06:00
main(int argc, char **argv)
2006-11-25 13:07:29 -07:00
{
2009-10-10 04:33:01 -06:00
if ((!parseArgs(argc, argv)) || (!getDisplay(argc, argv)))
exit(-1);
2012-04-07 09:24:43 -06:00
settings.locale.value = setlocale(LC_ALL, settings.locale.value);
settings.locale.src = FROM_SERVER;
VMSG1(7, "locale is %s\n", settings.locale.value);
2006-11-25 13:07:29 -07:00
if (dpy)
getServerValues();
2012-04-07 09:24:43 -06:00
if (settings.config.value && (!applyConfig(settings.config.value)))
2009-10-10 04:33:01 -06:00
exit(-3);
2006-11-25 13:07:29 -07:00
if (!applyRules())
2009-10-10 04:33:01 -06:00
exit(-4);
2006-11-25 13:07:29 -07:00
if (!applyComponentNames())
2009-10-10 04:33:01 -06:00
exit(-5);
2006-11-25 13:07:29 -07:00
if (dpy)
2009-10-10 04:33:01 -06:00
XCloseDisplay(dpy);
2006-11-25 13:07:29 -07:00
exit(0);
}