ddxLoad.c 14.8 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

#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\""

53
#if defined(WIN32)
54 55 56 57 58
#define PATHSEPARATOR "\\"
#else
#define PATHSEPARATOR "/"
#endif

59 60 61
static unsigned
LoadXKM(unsigned want, unsigned need, const char *keymap, XkbDescPtr *xkbRtrn);

62
static void
63
OutputDirectory(char *outdir, size_t size)
64 65
{
#ifndef WIN32
66
    /* Can we write an xkm and then open it too? */
67 68 69 70 71
    if (access(XKM_OUTPUT_DIR, W_OK | X_OK) == 0 &&
        (strlen(XKM_OUTPUT_DIR) < size)) {
        (void) strcpy(outdir, XKM_OUTPUT_DIR);
    }
    else
72
#else
73 74 75 76 77
    if (strlen(Win32TempDir()) + 1 < size) {
        (void) strcpy(outdir, Win32TempDir());
        (void) strcat(outdir, "\\");
    }
    else
78
#endif
79 80
    if (strlen("/tmp/") < size) {
        (void) strcpy(outdir, "/tmp/");
81 82 83
    }
}

84 85 86 87 88 89 90 91 92 93 94
/**
 * 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)
95
{
96 97
    FILE *out;
    char *buf = NULL, keymap[PATH_MAX], xkm_output_dir[PATH_MAX];
98

99
    const char *emptystring = "";
100
    char *xkbbasedirflag = NULL;
101 102
    const char *xkbbindir = emptystring;
    const char *xkbbindirsep = emptystring;
103

104
#ifdef WIN32
105 106 107 108 109 110 111
    /* 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
112 113

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

115
    OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir));
116

117
#ifdef WIN32
118 119
    strcpy(tmpname, Win32TempDir());
    strcat(tmpname, "\\xkb_XXXXXX");
120 121
    (void) mktemp(tmpname);
#endif
122 123

    if (XkbBaseDirectory != NULL) {
124 125
        if (asprintf(&xkbbasedirflag, "\"-R%s\"", XkbBaseDirectory) == -1)
            xkbbasedirflag = NULL;
126
    }
127 128

    if (XkbBinDirectory != NULL) {
129 130
        int ld = strlen(XkbBinDirectory);
        int lps = strlen(PATHSEPARATOR);
131

132
        xkbbindir = XkbBinDirectory;
133

134 135 136
        if ((ld >= lps) && (strcmp(xkbbindir + ld - lps, PATHSEPARATOR) != 0)) {
            xkbbindirsep = PATHSEPARATOR;
        }
137 138
    }

139
    if (asprintf(&buf,
140 141 142 143 144 145 146 147 148
                 "\"%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;
149

150
    free(xkbbasedirflag);
151 152

    if (!buf) {
153 154
        LogMessage(X_ERROR,
                   "XKB: Could not invoke xkbcomp: not enough memory\n");
155
        return NULL;
156
    }
157

158
#ifndef WIN32
159
    out = Popen(buf, "w");
160
#else
161
    out = fopen(tmpname, "w");
162
#endif
163 164

    if (out != NULL) {
165 166 167
        /* Now write to xkbcomp */
        (*callback)(out, userdata);

168
#ifndef WIN32
169
        if (Pclose(out) == 0)
170
#else
171
        if (fclose(out) == 0 && System(buf) >= 0)
172
#endif
173
        {
174
            if (xkbDebugFlags)
175
                DebugF("[xkb] xkb executes: %s\n", buf);
176
            free(buf);
177
#ifdef WIN32
178
            unlink(tmpname);
179
#endif
180
            return xnfstrdup(keymap);
181
        }
182 183 184 185
        else {
            LogMessage(X_ERROR, "Error compiling keymap (%s) executing '%s'\n",
                       keymap, buf);
        }
186 187 188
#ifdef WIN32
        /* remove the temporary file */
        unlink(tmpname);
189 190 191 192
#endif
    }
    else {
#ifndef WIN32
193
        LogMessage(X_ERROR, "XKB: Could not invoke xkbcomp\n");
194
#else
195
        LogMessage(X_ERROR, "Could not open file %s\n", tmpname);
196 197
#endif
    }
198
    free(buf);
199 200 201 202 203 204 205 206 207 208 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
    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;
249 250
}

251 252 253 254 255 256 257 258 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
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;
}

291
static FILE *
292
XkbDDXOpenConfigFile(const char *mapName, char *fileNameRtrn, int fileNameRtrnLen)
293
{
294 295
    char buf[PATH_MAX], xkm_output_dir[PATH_MAX];
    FILE *file;
296

297 298 299 300
    buf[0] = '\0';
    if (mapName != NULL) {
        OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir));
        if ((XkbBaseDirectory != NULL) && (xkm_output_dir[0] != '/')
301
#ifdef WIN32
302
            && (!isalpha(xkm_output_dir[0]) || xkm_output_dir[1] != ':')
303
#endif
304
            ) {
305 306 307
            if (snprintf(buf, PATH_MAX, "%s/%s%s.xkm", XkbBaseDirectory,
                         xkm_output_dir, mapName) >= PATH_MAX)
                buf[0] = '\0';
308 309
        }
        else {
310 311 312
            if (snprintf(buf, PATH_MAX, "%s%s.xkm", xkm_output_dir, mapName)
                >= PATH_MAX)
                buf[0] = '\0';
313 314 315 316 317
        }
        if (buf[0] != '\0')
            file = fopen(buf, "rb");
        else
            file = NULL;
318
    }
319 320 321 322
    else
        file = NULL;
    if ((fileNameRtrn != NULL) && (fileNameRtrnLen > 0)) {
        strlcpy(fileNameRtrn, buf, fileNameRtrnLen);
323 324 325 326
    }
    return file;
}

327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
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);
}

356
unsigned
357 358 359 360 361
XkbDDXLoadKeymapByNames(DeviceIntPtr keybd,
                        XkbComponentNamesPtr names,
                        unsigned want,
                        unsigned need,
                        XkbDescPtr *xkbRtrn, char *nameRtrn, int nameRtrnLen)
362
{
363
    XkbDescPtr xkb;
364

Daniel Stone's avatar
Daniel Stone committed
365
    *xkbRtrn = NULL;
366 367 368 369 370 371 372 373
    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)) {
374
        LogMessage(X_ERROR, "XKB: No components provided for device %s\n",
375
                   keybd->name ? keybd->name : "(unnamed keyboard)");
376
        return 0;
377
    }
378 379 380 381
    else if (!XkbDDXCompileKeymapByNames(xkb, names, want, need,
                                         nameRtrn, nameRtrnLen)) {
        LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n");
        return 0;
382
    }
383 384

    return LoadXKM(want, need, nameRtrn, xkbRtrn);
385 386
}

387
Bool
388
XkbDDXNamesFromRules(DeviceIntPtr keybd,
Keith Packard's avatar
Keith Packard committed
389
                     const char *rules_name,
390
                     XkbRF_VarDefsPtr defs, XkbComponentNamesPtr names)
391
{
392 393 394 395
    char buf[PATH_MAX];
    FILE *file;
    Bool complete;
    XkbRF_RulesPtr rules;
396 397

    if (!rules_name)
398
        return FALSE;
399

400 401
    if (snprintf(buf, PATH_MAX, "%s/rules/%s", XkbBaseDirectory, rules_name)
        >= PATH_MAX) {
402
        LogMessage(X_ERROR, "XKB: Rules name is too long\n");
403
        return FALSE;
404
    }
405 406 407

    file = fopen(buf, "r");
    if (!file) {
408
        LogMessage(X_ERROR, "XKB: Couldn't open rules file %s\n", buf);
409
        return FALSE;
410 411
    }

412
    rules = XkbRF_Create();
413 414
    if (!rules) {
        LogMessage(X_ERROR, "XKB: Couldn't create rules struct\n");
415 416
        fclose(file);
        return FALSE;
417
    }
418 419 420

    if (!XkbRF_LoadRules(file, rules)) {
        LogMessage(X_ERROR, "XKB: Couldn't parse rules file %s\n", rules_name);
421 422 423
        fclose(file);
        XkbRF_Free(rules, TRUE);
        return FALSE;
424
    }
425 426

    memset(names, 0, sizeof(*names));
427
    complete = XkbRF_GetComponents(rules, defs, names);
428
    fclose(file);
429
    XkbRF_Free(rules, TRUE);
Kaleb Keithley's avatar
Kaleb Keithley committed
430

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

434 435
    return complete;
}
436

437
static Bool
438 439
XkbRMLVOtoKcCGST(DeviceIntPtr dev, XkbRMLVOSet * rmlvo,
                 XkbComponentNamesPtr kccgst)
440 441 442 443 444 445 446 447
{
    XkbRF_VarDefsRec mlvo;

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

448 449 450 451 452 453 454 455 456
    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
457
XkbCompileKeymapForDevice(DeviceIntPtr dev, XkbRMLVOSet * rmlvo, int need)
458
{
459
    XkbDescPtr xkb = NULL;
460
    unsigned int provided;
461
    XkbComponentNamesRec kccgst = { 0 };
462 463
    char name[PATH_MAX];

464
    if (XkbRMLVOtoKcCGST(dev, rmlvo, &kccgst)) {
465 466 467
        provided =
            XkbDDXLoadKeymapByNames(dev, &kccgst, XkmAllIndicesMask, need, &xkb,
                                    name, PATH_MAX);
468 469 470 471 472
        if ((need & provided) != need) {
            if (xkb) {
                XkbFreeKeyboard(xkb, 0, TRUE);
                xkb = NULL;
            }
473 474 475
        }
    }

476
    XkbFreeComponentNames(&kccgst, FALSE);
477 478 479
    return xkb;
}

480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
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;
}


503
XkbDescPtr
504
XkbCompileKeymap(DeviceIntPtr dev, XkbRMLVOSet * rmlvo)
505 506
{
    XkbDescPtr xkb;
507
    unsigned int need;
508 509 510

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

514 515
    /* These are the components we really really need */
    need = XkmSymbolsMask | XkmCompatMapMask | XkmTypesMask |
516
        XkmKeyNamesMask | XkmVirtualModsMask;
517 518 519

    xkb = XkbCompileKeymapForDevice(dev, rmlvo, need);

520
    return KeymapOrDefaults(dev, xkb);
521
}
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550

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);
}