ddxLoad.c 15.3 KB
Newer Older
1 2 3 4 5 6 7 8
/************************************************************
Copyright (c) 1993 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
9 10
documentation, and that the name of Silicon Graphics not be
used in advertising or publicity pertaining to distribution
11
of the software without specific prior written permission.
12
Silicon Graphics makes no representation about the suitability
13 14 15
of this software for any purpose. It is provided "as is"
without any express or implied warranty.

16 17
SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18
AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19 20 21
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
22 23 24 25 26
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
THE USE OR PERFORMANCE OF THIS SOFTWARE.

********************************************************/

27 28 29 30
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif

31 32
#include <xkb-config.h>

33 34 35 36 37 38 39 40 41 42 43
#include <stdio.h>
#include <ctype.h>
#include <X11/X.h>
#include <X11/Xos.h>
#include <X11/Xproto.h>
#include <X11/keysym.h>
#include <X11/extensions/XKM.h>
#include "inputstr.h"
#include "scrnintstr.h"
#include "windowstr.h"
#define	XKBSRV_NEED_FILE_FUNCS
44
#include <xkbsrv.h>
45
#include <X11/extensions/XI.h>
46
#include "xkb.h"
47

48 49 50 51 52 53 54 55
        /*
         * If XKM_OUTPUT_DIR specifies a path without a leading slash, it is
         * relative to the top-level XKB configuration directory.
         * Making the server write to a subdirectory of that directory
         * requires some work in the general case (install procedure
         * has to create links to /var or somesuch on many machines),
         * so we just compile into /usr/tmp for now.
         */
56 57 58 59 60 61 62 63 64
#ifndef XKM_OUTPUT_DIR
#define	XKM_OUTPUT_DIR	"compiled/"
#endif

#define	PRE_ERROR_MSG "\"The XKEYBOARD keymap compiler (xkbcomp) reports:\""
#define	ERROR_PREFIX	"\"> \""
#define	POST_ERROR_MSG1 "\"Errors from xkbcomp are not fatal to the X server\""
#define	POST_ERROR_MSG2 "\"End of messages from xkbcomp\""

65
#if defined(WIN32)
66 67 68 69 70
#define PATHSEPARATOR "\\"
#else
#define PATHSEPARATOR "/"
#endif

71 72 73
static unsigned
LoadXKM(unsigned want, unsigned need, const char *keymap, XkbDescPtr *xkbRtrn);

74
static void
75
OutputDirectory(char *outdir, size_t size)
76 77
{
#ifndef WIN32
78
    /* Can we write an xkm and then open it too? */
79 80 81 82 83
    if (access(XKM_OUTPUT_DIR, W_OK | X_OK) == 0 &&
        (strlen(XKM_OUTPUT_DIR) < size)) {
        (void) strcpy(outdir, XKM_OUTPUT_DIR);
    }
    else
84
#else
85 86 87 88 89
    if (strlen(Win32TempDir()) + 1 < size) {
        (void) strcpy(outdir, Win32TempDir());
        (void) strcat(outdir, "\\");
    }
    else
90
#endif
91 92
    if (strlen("/tmp/") < size) {
        (void) strcpy(outdir, "/tmp/");
93 94 95
    }
}

96 97 98 99 100 101 102 103 104 105 106
/**
 * Callback invoked by XkbRunXkbComp. Write to out to talk to xkbcomp.
 */
typedef void (*xkbcomp_buffer_callback)(FILE *out, void *userdata);

/**
 * Start xkbcomp, let the callback write into xkbcomp's stdin. When done,
 * return a strdup'd copy of the file name we've written to.
 */
static char *
RunXkbComp(xkbcomp_buffer_callback callback, void *userdata)
107
{
108 109
    FILE *out;
    char *buf = NULL, keymap[PATH_MAX], xkm_output_dir[PATH_MAX];
110

111
    const char *emptystring = "";
112
    char *xkbbasedirflag = NULL;
113 114
    const char *xkbbindir = emptystring;
    const char *xkbbindirsep = emptystring;
115

116
#ifdef WIN32
117 118 119 120 121 122 123
    /* WIN32 has no popen. The input must be stored in a file which is
       used as input for xkbcomp. xkbcomp does not read from stdin. */
    char tmpname[PATH_MAX];
    const char *xkmfile = tmpname;
#else
    const char *xkmfile = "-";
#endif
124 125

    snprintf(keymap, sizeof(keymap), "server-%s", display);
126

127
    OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir));
128

129
#ifdef WIN32
130 131
    strcpy(tmpname, Win32TempDir());
    strcat(tmpname, "\\xkb_XXXXXX");
132 133
    (void) mktemp(tmpname);
#endif
134 135

    if (XkbBaseDirectory != NULL) {
136 137
        if (asprintf(&xkbbasedirflag, "\"-R%s\"", XkbBaseDirectory) == -1)
            xkbbasedirflag = NULL;
138
    }
139 140

    if (XkbBinDirectory != NULL) {
141 142
        int ld = strlen(XkbBinDirectory);
        int lps = strlen(PATHSEPARATOR);
143

144
        xkbbindir = XkbBinDirectory;
145

146 147 148
        if ((ld >= lps) && (strcmp(xkbbindir + ld - lps, PATHSEPARATOR) != 0)) {
            xkbbindirsep = PATHSEPARATOR;
        }
149 150
    }

151
    if (asprintf(&buf,
152 153 154 155 156 157 158 159 160
                 "\"%s%sxkbcomp\" -w %d %s -xkm \"%s\" "
                 "-em1 %s -emp %s -eml %s \"%s%s.xkm\"",
                 xkbbindir, xkbbindirsep,
                 ((xkbDebugFlags < 2) ? 1 :
                  ((xkbDebugFlags > 10) ? 10 : (int) xkbDebugFlags)),
                 xkbbasedirflag ? xkbbasedirflag : "", xkmfile,
                 PRE_ERROR_MSG, ERROR_PREFIX, POST_ERROR_MSG1,
                 xkm_output_dir, keymap) == -1)
        buf = NULL;
161

162
    free(xkbbasedirflag);
163 164

    if (!buf) {
165 166
        LogMessage(X_ERROR,
                   "XKB: Could not invoke xkbcomp: not enough memory\n");
167
        return NULL;
168
    }
169

170
#ifndef WIN32
171
    out = Popen(buf, "w");
172
#else
173
    out = fopen(tmpname, "w");
174
#endif
175 176

    if (out != NULL) {
177 178 179
        /* Now write to xkbcomp */
        (*callback)(out, userdata);

180
#ifndef WIN32
181
        if (Pclose(out) == 0)
182
#else
183
        if (fclose(out) == 0 && System(buf) >= 0)
184
#endif
185
        {
186
            if (xkbDebugFlags)
187
                DebugF("[xkb] xkb executes: %s\n", buf);
188
            free(buf);
189
#ifdef WIN32
190
            unlink(tmpname);
191
#endif
192
            return xnfstrdup(keymap);
193 194 195
        }
        else
            LogMessage(X_ERROR, "Error compiling keymap (%s)\n", keymap);
196 197 198
#ifdef WIN32
        /* remove the temporary file */
        unlink(tmpname);
199 200 201 202
#endif
    }
    else {
#ifndef WIN32
203
        LogMessage(X_ERROR, "XKB: Could not invoke xkbcomp\n");
204
#else
205
        LogMessage(X_ERROR, "Could not open file %s\n", tmpname);
206 207
#endif
    }
208
    free(buf);
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
    return NULL;
}

typedef struct {
    XkbDescPtr xkb;
    XkbComponentNamesPtr names;
    unsigned int want;
    unsigned int need;
} XkbKeymapNamesCtx;

static void
xkb_write_keymap_for_names_cb(FILE *out, void *userdata)
{
    XkbKeymapNamesCtx *ctx = userdata;
#ifdef DEBUG
    if (xkbDebugFlags) {
        ErrorF("[xkb] XkbDDXCompileKeymapByNames compiling keymap:\n");
        XkbWriteXKBKeymapForNames(stderr, ctx->names, ctx->xkb, ctx->want, ctx->need);
    }
#endif
    XkbWriteXKBKeymapForNames(out, ctx->names, ctx->xkb, ctx->want, ctx->need);
}

static Bool
XkbDDXCompileKeymapByNames(XkbDescPtr xkb,
                           XkbComponentNamesPtr names,
                           unsigned want,
                           unsigned need, char *nameRtrn, int nameRtrnLen)
{
    char *keymap;
    Bool rc = FALSE;
    XkbKeymapNamesCtx ctx = {
        .xkb = xkb,
        .names = names,
        .want = want,
        .need = need
    };

    keymap = RunXkbComp(xkb_write_keymap_for_names_cb, &ctx);

    if (keymap) {
        if(nameRtrn)
            strlcpy(nameRtrn, keymap, nameRtrnLen);

        free(keymap);
        rc = TRUE;
    } else if (nameRtrn)
        *nameRtrn = '\0';

    return rc;
259 260
}

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
typedef struct {
    const char *keymap;
    size_t len;
} XkbKeymapString;

static void
xkb_write_keymap_string_cb(FILE *out, void *userdata)
{
    XkbKeymapString *s = userdata;
    fwrite(s->keymap, s->len, 1, out);
}

static unsigned int
XkbDDXLoadKeymapFromString(DeviceIntPtr keybd,
                          const char *keymap, int keymap_length,
                          unsigned int want,
                          unsigned int need,
                          XkbDescPtr *xkbRtrn)
{
    unsigned int have;
    char *map_name;
    XkbKeymapString map = {
        .keymap = keymap,
        .len = keymap_length
    };

    *xkbRtrn = NULL;

    map_name = RunXkbComp(xkb_write_keymap_string_cb, &map);
    if (!map_name) {
        LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n");
        return 0;
    }

    have = LoadXKM(want, need, map_name, xkbRtrn);
    free(map_name);

    return have;
}

301
static FILE *
302
XkbDDXOpenConfigFile(const char *mapName, char *fileNameRtrn, int fileNameRtrnLen)
303
{
304 305
    char buf[PATH_MAX], xkm_output_dir[PATH_MAX];
    FILE *file;
306

307 308 309 310
    buf[0] = '\0';
    if (mapName != NULL) {
        OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir));
        if ((XkbBaseDirectory != NULL) && (xkm_output_dir[0] != '/')
311
#ifdef WIN32
312
            && (!isalpha(xkm_output_dir[0]) || xkm_output_dir[1] != ':')
313
#endif
314
            ) {
315 316 317
            if (snprintf(buf, PATH_MAX, "%s/%s%s.xkm", XkbBaseDirectory,
                         xkm_output_dir, mapName) >= PATH_MAX)
                buf[0] = '\0';
318 319
        }
        else {
320 321 322
            if (snprintf(buf, PATH_MAX, "%s%s.xkm", xkm_output_dir, mapName)
                >= PATH_MAX)
                buf[0] = '\0';
323 324 325 326 327
        }
        if (buf[0] != '\0')
            file = fopen(buf, "rb");
        else
            file = NULL;
328
    }
329 330 331 332
    else
        file = NULL;
    if ((fileNameRtrn != NULL) && (fileNameRtrnLen > 0)) {
        strlcpy(fileNameRtrn, buf, fileNameRtrnLen);
333 334 335 336
    }
    return file;
}

337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
static unsigned
LoadXKM(unsigned want, unsigned need, const char *keymap, XkbDescPtr *xkbRtrn)
{
    FILE *file;
    char fileName[PATH_MAX];
    unsigned missing;

    file = XkbDDXOpenConfigFile(keymap, fileName, PATH_MAX);
    if (file == NULL) {
        LogMessage(X_ERROR, "Couldn't open compiled keymap file %s\n",
                   fileName);
        return 0;
    }
    missing = XkmReadFile(file, need, want, xkbRtrn);
    if (*xkbRtrn == NULL) {
        LogMessage(X_ERROR, "Error loading keymap %s\n", fileName);
        fclose(file);
        (void) unlink(fileName);
        return 0;
    }
    else {
        DebugF("Loaded XKB keymap %s, defined=0x%x\n", fileName,
               (*xkbRtrn)->defined);
    }
    fclose(file);
    (void) unlink(fileName);
    return (need | want) & (~missing);
}

366
unsigned
367 368 369 370 371
XkbDDXLoadKeymapByNames(DeviceIntPtr keybd,
                        XkbComponentNamesPtr names,
                        unsigned want,
                        unsigned need,
                        XkbDescPtr *xkbRtrn, char *nameRtrn, int nameRtrnLen)
372
{
373
    XkbDescPtr xkb;
374

Daniel Stone's avatar
Daniel Stone committed
375
    *xkbRtrn = NULL;
376 377 378 379 380 381 382 383
    if ((keybd == NULL) || (keybd->key == NULL) ||
        (keybd->key->xkbInfo == NULL))
        xkb = NULL;
    else
        xkb = keybd->key->xkbInfo->desc;
    if ((names->keycodes == NULL) && (names->types == NULL) &&
        (names->compat == NULL) && (names->symbols == NULL) &&
        (names->geometry == NULL)) {
384
        LogMessage(X_ERROR, "XKB: No components provided for device %s\n",
385
                   keybd->name ? keybd->name : "(unnamed keyboard)");
386
        return 0;
387
    }
388 389 390 391
    else if (!XkbDDXCompileKeymapByNames(xkb, names, want, need,
                                         nameRtrn, nameRtrnLen)) {
        LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n");
        return 0;
392
    }
393 394

    return LoadXKM(want, need, nameRtrn, xkbRtrn);
395 396
}

397
Bool
398
XkbDDXNamesFromRules(DeviceIntPtr keybd,
Keith Packard's avatar
Keith Packard committed
399
                     const char *rules_name,
400
                     XkbRF_VarDefsPtr defs, XkbComponentNamesPtr names)
401
{
402 403 404 405
    char buf[PATH_MAX];
    FILE *file;
    Bool complete;
    XkbRF_RulesPtr rules;
406 407

    if (!rules_name)
408
        return FALSE;
409

410 411
    if (snprintf(buf, PATH_MAX, "%s/rules/%s", XkbBaseDirectory, rules_name)
        >= PATH_MAX) {
412
        LogMessage(X_ERROR, "XKB: Rules name is too long\n");
413
        return FALSE;
414
    }
415 416 417

    file = fopen(buf, "r");
    if (!file) {
418
        LogMessage(X_ERROR, "XKB: Couldn't open rules file %s\n", buf);
419
        return FALSE;
420 421
    }

422
    rules = XkbRF_Create();
423 424
    if (!rules) {
        LogMessage(X_ERROR, "XKB: Couldn't create rules struct\n");
425 426
        fclose(file);
        return FALSE;
427
    }
428 429 430

    if (!XkbRF_LoadRules(file, rules)) {
        LogMessage(X_ERROR, "XKB: Couldn't parse rules file %s\n", rules_name);
431 432 433
        fclose(file);
        XkbRF_Free(rules, TRUE);
        return FALSE;
434
    }
435 436

    memset(names, 0, sizeof(*names));
437
    complete = XkbRF_GetComponents(rules, defs, names);
438
    fclose(file);
439
    XkbRF_Free(rules, TRUE);
Kaleb Keithley's avatar
Kaleb Keithley committed
440

441 442
    if (!complete)
        LogMessage(X_ERROR, "XKB: Rules returned no components\n");
Kaleb Keithley's avatar
Kaleb Keithley committed
443

444 445
    return complete;
}
446

447
static Bool
448 449
XkbRMLVOtoKcCGST(DeviceIntPtr dev, XkbRMLVOSet * rmlvo,
                 XkbComponentNamesPtr kccgst)
450 451 452 453 454 455 456 457
{
    XkbRF_VarDefsRec mlvo;

    mlvo.model = rmlvo->model;
    mlvo.layout = rmlvo->layout;
    mlvo.variant = rmlvo->variant;
    mlvo.options = rmlvo->options;

458 459 460 461 462 463 464 465 466
    return XkbDDXNamesFromRules(dev, rmlvo->rules, &mlvo, kccgst);
}

/**
 * Compile the given RMLVO keymap and return it. Returns the XkbDescPtr on
 * success or NULL on failure. If the components compiled are not a superset
 * or equal to need, the compiliation is treated as failure.
 */
static XkbDescPtr
467
XkbCompileKeymapForDevice(DeviceIntPtr dev, XkbRMLVOSet * rmlvo, int need)
468
{
469
    XkbDescPtr xkb = NULL;
470
    unsigned int provided;
471
    XkbComponentNamesRec kccgst = { 0 };
472 473
    char name[PATH_MAX];

474
    if (XkbRMLVOtoKcCGST(dev, rmlvo, &kccgst)) {
475 476 477
        provided =
            XkbDDXLoadKeymapByNames(dev, &kccgst, XkmAllIndicesMask, need, &xkb,
                                    name, PATH_MAX);
478 479 480 481 482
        if ((need & provided) != need) {
            if (xkb) {
                XkbFreeKeyboard(xkb, 0, TRUE);
                xkb = NULL;
            }
483 484 485
        }
    }

486
    XkbFreeComponentNames(&kccgst, FALSE);
487 488 489
    return xkb;
}

490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
static XkbDescPtr
KeymapOrDefaults(DeviceIntPtr dev, XkbDescPtr xkb)
{
    XkbRMLVOSet dflts;

    if (xkb)
        return xkb;

    /* we didn't get what we really needed. And that will likely leave
     * us with a keyboard that doesn't work. Use the defaults instead */
    LogMessage(X_ERROR, "XKB: Failed to load keymap. Loading default "
                        "keymap instead.\n");

    XkbGetRulesDflts(&dflts);

    xkb = XkbCompileKeymapForDevice(dev, &dflts, 0);

    XkbFreeRMLVOSet(&dflts, FALSE);

    return xkb;
}


513
XkbDescPtr
514
XkbCompileKeymap(DeviceIntPtr dev, XkbRMLVOSet * rmlvo)
515 516
{
    XkbDescPtr xkb;
517
    unsigned int need;
518 519 520

    if (!dev || !rmlvo) {
        LogMessage(X_ERROR, "XKB: No device or RMLVO specified\n");
521 522 523
        return NULL;
    }

524 525
    /* These are the components we really really need */
    need = XkmSymbolsMask | XkmCompatMapMask | XkmTypesMask |
526
        XkmKeyNamesMask | XkmVirtualModsMask;
527 528 529

    xkb = XkbCompileKeymapForDevice(dev, rmlvo, need);

530
    return KeymapOrDefaults(dev, xkb);
531
}
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560

XkbDescPtr
XkbCompileKeymapFromString(DeviceIntPtr dev,
                           const char *keymap, int keymap_length)
{
    XkbDescPtr xkb;
    unsigned int need, provided;

    if (!dev || !keymap) {
        LogMessage(X_ERROR, "XKB: No device or keymap specified\n");
        return NULL;
    }

    /* These are the components we really really need */
    need = XkmSymbolsMask | XkmCompatMapMask | XkmTypesMask |
           XkmKeyNamesMask | XkmVirtualModsMask;

    provided =
        XkbDDXLoadKeymapFromString(dev, keymap, keymap_length,
                                   XkmAllIndicesMask, need, &xkb);
    if ((need & provided) != need) {
        if (xkb) {
            XkbFreeKeyboard(xkb, 0, TRUE);
            xkb = NULL;
        }
    }

    return KeymapOrDefaults(dev, xkb);
}