~mzanetti/unity8/modeswitchwarning

« back to all changes in this revision

Viewing changes to plugins/Cursor/3rd_party/xcursor/xcursor.c

  • Committer: Michael Zanetti
  • Date: 2015-10-26 16:47:52 UTC
  • mfrom: (1978.2.32 trunk)
  • Revision ID: michael.zanetti@canonical.com-20151026164752-8yt7ngcng344mb67
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright © 2002 Keith Packard
 
3
 *
 
4
 * Permission to use, copy, modify, distribute, and sell this software and its
 
5
 * documentation for any purpose is hereby granted without fee, provided that
 
6
 * the above copyright notice appear in all copies and that both that
 
7
 * copyright notice and this permission notice appear in supporting
 
8
 * documentation, and that the name of Keith Packard not be used in
 
9
 * advertising or publicity pertaining to distribution of the software without
 
10
 * specific, written prior permission.  Keith Packard makes no
 
11
 * representations about the suitability of this software for any purpose.  It
 
12
 * is provided "as is" without express or implied warranty.
 
13
 *
 
14
 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 
15
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 
16
 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 
17
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 
18
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 
19
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 
20
 * PERFORMANCE OF THIS SOFTWARE.
 
21
 */
 
22
 
 
23
#include "xcursor.h"
 
24
#include <stdio.h>
 
25
#include <stdlib.h>
 
26
#include <string.h>
 
27
#include <dirent.h>
 
28
 
 
29
/*
 
30
 * From libXcursor/include/X11/extensions/Xcursor.h
 
31
 */
 
32
 
 
33
#define XcursorTrue    1
 
34
#define XcursorFalse    0
 
35
 
 
36
/*
 
37
 * Cursor files start with a header.  The header
 
38
 * contains a magic number, a version number and a
 
39
 * table of contents which has type and offset information
 
40
 * for the remaining tables in the file.
 
41
 *
 
42
 * File minor versions increment for compatible changes
 
43
 * File major versions increment for incompatible changes (never, we hope)
 
44
 *
 
45
 * Chunks of the same type are always upward compatible.  Incompatible
 
46
 * changes are made with new chunk types; the old data can remain under
 
47
 * the old type.  Upward compatible changes can add header data as the
 
48
 * header lengths are specified in the file.
 
49
 *
 
50
 *  File:
 
51
 *    FileHeader
 
52
 *    LISTofChunk
 
53
 *
 
54
 *  FileHeader:
 
55
 *    CARD32        magic        magic number
 
56
 *    CARD32        header        bytes in file header
 
57
 *    CARD32        version        file version
 
58
 *    CARD32        ntoc        number of toc entries
 
59
 *    LISTofFileToc   toc        table of contents
 
60
 *
 
61
 *  FileToc:
 
62
 *    CARD32        type        entry type
 
63
 *    CARD32        subtype        entry subtype (size for images)
 
64
 *    CARD32        position    absolute file position
 
65
 */
 
66
 
 
67
#define XCURSOR_MAGIC    0x72756358  /* "Xcur" LSBFirst */
 
68
 
 
69
/*
 
70
 * Current Xcursor version number.  Will be substituted by configure
 
71
 * from the version in the libXcursor configure.ac file.
 
72
 */
 
73
 
 
74
#define XCURSOR_LIB_MAJOR 1
 
75
#define XCURSOR_LIB_MINOR 1
 
76
#define XCURSOR_LIB_REVISION 13
 
77
#define XCURSOR_LIB_VERSION    ((XCURSOR_LIB_MAJOR * 10000) + \
 
78
                 (XCURSOR_LIB_MINOR * 100) + \
 
79
                 (XCURSOR_LIB_REVISION))
 
80
 
 
81
/*
 
82
 * This version number is stored in cursor files; changes to the
 
83
 * file format require updating this version number
 
84
 */
 
85
#define XCURSOR_FILE_MAJOR    1
 
86
#define XCURSOR_FILE_MINOR    0
 
87
#define XCURSOR_FILE_VERSION    ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR))
 
88
#define XCURSOR_FILE_HEADER_LEN    (4 * 4)
 
89
#define XCURSOR_FILE_TOC_LEN    (3 * 4)
 
90
 
 
91
typedef struct _XcursorFileToc {
 
92
    XcursorUInt        type;    /* chunk type */
 
93
    XcursorUInt        subtype;    /* subtype (size for images) */
 
94
    XcursorUInt        position;    /* absolute position in file */
 
95
} XcursorFileToc;
 
96
 
 
97
typedef struct _XcursorFileHeader {
 
98
    XcursorUInt        magic;    /* magic number */
 
99
    XcursorUInt        header;    /* byte length of header */
 
100
    XcursorUInt        version;    /* file version number */
 
101
    XcursorUInt        ntoc;    /* number of toc entries */
 
102
    XcursorFileToc  *tocs;    /* table of contents */
 
103
} XcursorFileHeader;
 
104
 
 
105
/*
 
106
 * The rest of the file is a list of chunks, each tagged by type
 
107
 * and version.
 
108
 *
 
109
 *  Chunk:
 
110
 *    ChunkHeader
 
111
 *    <extra type-specific header fields>
 
112
 *    <type-specific data>
 
113
 *
 
114
 *  ChunkHeader:
 
115
 *    CARD32        header    bytes in chunk header + type header
 
116
 *    CARD32        type    chunk type
 
117
 *    CARD32        subtype    chunk subtype
 
118
 *    CARD32        version    chunk type version
 
119
 */
 
120
 
 
121
#define XCURSOR_CHUNK_HEADER_LEN    (4 * 4)
 
122
 
 
123
typedef struct _XcursorChunkHeader {
 
124
    XcursorUInt        header;    /* bytes in chunk header */
 
125
    XcursorUInt        type;    /* chunk type */
 
126
    XcursorUInt        subtype;    /* chunk subtype (size for images) */
 
127
    XcursorUInt        version;    /* version of this type */
 
128
} XcursorChunkHeader;
 
129
 
 
130
/*
 
131
 * Here's a list of the known chunk types
 
132
 */
 
133
 
 
134
/*
 
135
 * Comments consist of a 4-byte length field followed by
 
136
 * UTF-8 encoded text
 
137
 *
 
138
 *  Comment:
 
139
 *    ChunkHeader header    chunk header
 
140
 *    CARD32        length    bytes in text
 
141
 *    LISTofCARD8 text    UTF-8 encoded text
 
142
 */
 
143
 
 
144
#define XCURSOR_COMMENT_TYPE        0xfffe0001
 
145
#define XCURSOR_COMMENT_VERSION        1
 
146
#define XCURSOR_COMMENT_HEADER_LEN  (XCURSOR_CHUNK_HEADER_LEN + (1 *4))
 
147
#define XCURSOR_COMMENT_COPYRIGHT   1
 
148
#define XCURSOR_COMMENT_LICENSE        2
 
149
#define XCURSOR_COMMENT_OTHER        3
 
150
#define XCURSOR_COMMENT_MAX_LEN        0x100000
 
151
 
 
152
typedef struct _XcursorComment {
 
153
    XcursorUInt        version;
 
154
    XcursorUInt        comment_type;
 
155
    char        *comment;
 
156
} XcursorComment;
 
157
 
 
158
/*
 
159
 * Each cursor image occupies a separate image chunk.
 
160
 * The length of the image header follows the chunk header
 
161
 * so that future versions can extend the header without
 
162
 * breaking older applications
 
163
 *
 
164
 *  Image:
 
165
 *    ChunkHeader    header    chunk header
 
166
 *    CARD32        width    actual width
 
167
 *    CARD32        height    actual height
 
168
 *    CARD32        xhot    hot spot x
 
169
 *    CARD32        yhot    hot spot y
 
170
 *    CARD32        delay    animation delay
 
171
 *    LISTofCARD32    pixels    ARGB pixels
 
172
 */
 
173
 
 
174
#define XCURSOR_IMAGE_TYPE            0xfffd0002
 
175
#define XCURSOR_IMAGE_VERSION        1
 
176
#define XCURSOR_IMAGE_HEADER_LEN    (XCURSOR_CHUNK_HEADER_LEN + (5*4))
 
177
#define XCURSOR_IMAGE_MAX_SIZE        0x7fff    /* 32767x32767 max cursor size */
 
178
 
 
179
typedef struct _XcursorFile XcursorFile;
 
180
 
 
181
struct _XcursorFile {
 
182
    void    *closure;
 
183
    int        (*read)  (XcursorFile *file, unsigned char *buf, int len);
 
184
    int        (*write) (XcursorFile *file, unsigned char *buf, int len);
 
185
    int        (*seek)  (XcursorFile *file, long offset, int whence);
 
186
};
 
187
 
 
188
typedef struct _XcursorComments {
 
189
    int            ncomment;    /* number of comments */
 
190
    XcursorComment  **comments;    /* array of XcursorComment pointers */
 
191
} XcursorComments;
 
192
 
 
193
/*
 
194
 * From libXcursor/src/file.c
 
195
 */
 
196
 
 
197
static XcursorImage *
 
198
XcursorImageCreate (int width, int height)
 
199
{
 
200
    XcursorImage    *image;
 
201
 
 
202
    image = malloc (sizeof (XcursorImage) +
 
203
            width * height * sizeof (XcursorPixel));
 
204
    if (!image)
 
205
    return NULL;
 
206
    image->version = XCURSOR_IMAGE_VERSION;
 
207
    image->pixels = (XcursorPixel *) (image + 1);
 
208
    image->size = width > height ? width : height;
 
209
    image->width = width;
 
210
    image->height = height;
 
211
    image->delay = 0;
 
212
    return image;
 
213
}
 
214
 
 
215
static void
 
216
XcursorImageDestroy (XcursorImage *image)
 
217
{
 
218
    free (image);
 
219
}
 
220
 
 
221
static XcursorImages *
 
222
XcursorImagesCreate (int size)
 
223
{
 
224
    XcursorImages   *images;
 
225
 
 
226
    images = malloc (sizeof (XcursorImages) +
 
227
             size * sizeof (XcursorImage *));
 
228
    if (!images)
 
229
    return NULL;
 
230
    images->nimage = 0;
 
231
    images->images = (XcursorImage **) (images + 1);
 
232
    images->name = NULL;
 
233
    return images;
 
234
}
 
235
 
 
236
void
 
237
XcursorImagesDestroy (XcursorImages *images)
 
238
{
 
239
    int    n;
 
240
 
 
241
    if (!images)
 
242
        return;
 
243
 
 
244
    for (n = 0; n < images->nimage; n++)
 
245
    XcursorImageDestroy (images->images[n]);
 
246
    if (images->name)
 
247
    free (images->name);
 
248
    free (images);
 
249
}
 
250
 
 
251
static void
 
252
XcursorImagesSetName (XcursorImages *images, const char *name)
 
253
{
 
254
    char    *new;
 
255
 
 
256
    if (!images || !name)
 
257
        return;
 
258
 
 
259
    new = malloc (strlen (name) + 1);
 
260
 
 
261
    if (!new)
 
262
    return;
 
263
 
 
264
    strcpy (new, name);
 
265
    if (images->name)
 
266
    free (images->name);
 
267
    images->name = new;
 
268
}
 
269
 
 
270
static XcursorBool
 
271
_XcursorReadUInt (XcursorFile *file, XcursorUInt *u)
 
272
{
 
273
    unsigned char   bytes[4];
 
274
 
 
275
    if (!file || !u)
 
276
        return XcursorFalse;
 
277
 
 
278
    if ((*file->read) (file, bytes, 4) != 4)
 
279
    return XcursorFalse;
 
280
    *u = ((bytes[0] << 0) |
 
281
      (bytes[1] << 8) |
 
282
      (bytes[2] << 16) |
 
283
      (bytes[3] << 24));
 
284
    return XcursorTrue;
 
285
}
 
286
 
 
287
static void
 
288
_XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader)
 
289
{
 
290
    free (fileHeader);
 
291
}
 
292
 
 
293
static XcursorFileHeader *
 
294
_XcursorFileHeaderCreate (int ntoc)
 
295
{
 
296
    XcursorFileHeader    *fileHeader;
 
297
 
 
298
    if (ntoc > 0x10000)
 
299
    return NULL;
 
300
    fileHeader = malloc (sizeof (XcursorFileHeader) +
 
301
             ntoc * sizeof (XcursorFileToc));
 
302
    if (!fileHeader)
 
303
    return NULL;
 
304
    fileHeader->magic = XCURSOR_MAGIC;
 
305
    fileHeader->header = XCURSOR_FILE_HEADER_LEN;
 
306
    fileHeader->version = XCURSOR_FILE_VERSION;
 
307
    fileHeader->ntoc = ntoc;
 
308
    fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1);
 
309
    return fileHeader;
 
310
}
 
311
 
 
312
static XcursorFileHeader *
 
313
_XcursorReadFileHeader (XcursorFile *file)
 
314
{
 
315
    XcursorFileHeader    head, *fileHeader;
 
316
    XcursorUInt        skip;
 
317
    unsigned int    n;
 
318
 
 
319
    if (!file)
 
320
        return NULL;
 
321
 
 
322
    if (!_XcursorReadUInt (file, &head.magic))
 
323
    return NULL;
 
324
    if (head.magic != XCURSOR_MAGIC)
 
325
    return NULL;
 
326
    if (!_XcursorReadUInt (file, &head.header))
 
327
    return NULL;
 
328
    if (!_XcursorReadUInt (file, &head.version))
 
329
    return NULL;
 
330
    if (!_XcursorReadUInt (file, &head.ntoc))
 
331
    return NULL;
 
332
    skip = head.header - XCURSOR_FILE_HEADER_LEN;
 
333
    if (skip)
 
334
    if ((*file->seek) (file, skip, SEEK_CUR) == EOF)
 
335
        return NULL;
 
336
    fileHeader = _XcursorFileHeaderCreate (head.ntoc);
 
337
    if (!fileHeader)
 
338
    return NULL;
 
339
    fileHeader->magic = head.magic;
 
340
    fileHeader->header = head.header;
 
341
    fileHeader->version = head.version;
 
342
    fileHeader->ntoc = head.ntoc;
 
343
    for (n = 0; n < fileHeader->ntoc; n++)
 
344
    {
 
345
    if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type))
 
346
        break;
 
347
    if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype))
 
348
        break;
 
349
    if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position))
 
350
        break;
 
351
    }
 
352
    if (n != fileHeader->ntoc)
 
353
    {
 
354
    _XcursorFileHeaderDestroy (fileHeader);
 
355
    return NULL;
 
356
    }
 
357
    return fileHeader;
 
358
}
 
359
 
 
360
static XcursorBool
 
361
_XcursorSeekToToc (XcursorFile        *file,
 
362
           XcursorFileHeader    *fileHeader,
 
363
           int            toc)
 
364
{
 
365
    if (!file || !fileHeader || \
 
366
        (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF)
 
367
    return XcursorFalse;
 
368
    return XcursorTrue;
 
369
}
 
370
 
 
371
static XcursorBool
 
372
_XcursorFileReadChunkHeader (XcursorFile    *file,
 
373
                 XcursorFileHeader    *fileHeader,
 
374
                 int        toc,
 
375
                 XcursorChunkHeader    *chunkHeader)
 
376
{
 
377
    if (!file || !fileHeader || !chunkHeader)
 
378
        return XcursorFalse;
 
379
    if (!_XcursorSeekToToc (file, fileHeader, toc))
 
380
    return XcursorFalse;
 
381
    if (!_XcursorReadUInt (file, &chunkHeader->header))
 
382
    return XcursorFalse;
 
383
    if (!_XcursorReadUInt (file, &chunkHeader->type))
 
384
    return XcursorFalse;
 
385
    if (!_XcursorReadUInt (file, &chunkHeader->subtype))
 
386
    return XcursorFalse;
 
387
    if (!_XcursorReadUInt (file, &chunkHeader->version))
 
388
    return XcursorFalse;
 
389
    /* sanity check */
 
390
    if (chunkHeader->type != fileHeader->tocs[toc].type ||
 
391
    chunkHeader->subtype != fileHeader->tocs[toc].subtype)
 
392
    return XcursorFalse;
 
393
    return XcursorTrue;
 
394
}
 
395
 
 
396
#define dist(a,b)   ((a) > (b) ? (a) - (b) : (b) - (a))
 
397
 
 
398
static XcursorDim
 
399
_XcursorFindBestSize (XcursorFileHeader *fileHeader,
 
400
              XcursorDim    size,
 
401
              int        *nsizesp)
 
402
{
 
403
    unsigned int n;
 
404
    int        nsizes = 0;
 
405
    XcursorDim    bestSize = 0;
 
406
    XcursorDim    thisSize;
 
407
 
 
408
    if (!fileHeader || !nsizesp)
 
409
        return 0;
 
410
 
 
411
    for (n = 0; n < fileHeader->ntoc; n++)
 
412
    {
 
413
    if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE)
 
414
        continue;
 
415
    thisSize = fileHeader->tocs[n].subtype;
 
416
    if (!bestSize || dist (thisSize, size) < dist (bestSize, size))
 
417
    {
 
418
        bestSize = thisSize;
 
419
        nsizes = 1;
 
420
    }
 
421
    else if (thisSize == bestSize)
 
422
        nsizes++;
 
423
    }
 
424
    *nsizesp = nsizes;
 
425
    return bestSize;
 
426
}
 
427
 
 
428
static int
 
429
_XcursorFindImageToc (XcursorFileHeader    *fileHeader,
 
430
              XcursorDim    size,
 
431
              int        count)
 
432
{
 
433
    unsigned int    toc;
 
434
    XcursorDim        thisSize;
 
435
 
 
436
    if (!fileHeader)
 
437
        return 0;
 
438
 
 
439
    for (toc = 0; toc < fileHeader->ntoc; toc++)
 
440
    {
 
441
    if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE)
 
442
        continue;
 
443
    thisSize = fileHeader->tocs[toc].subtype;
 
444
    if (thisSize != size)
 
445
        continue;
 
446
    if (!count)
 
447
        break;
 
448
    count--;
 
449
    }
 
450
    if (toc == fileHeader->ntoc)
 
451
    return -1;
 
452
    return toc;
 
453
}
 
454
 
 
455
static XcursorImage *
 
456
_XcursorReadImage (XcursorFile        *file,
 
457
           XcursorFileHeader    *fileHeader,
 
458
           int            toc)
 
459
{
 
460
    XcursorChunkHeader    chunkHeader;
 
461
    XcursorImage    head;
 
462
    XcursorImage    *image;
 
463
    int            n;
 
464
    XcursorPixel    *p;
 
465
 
 
466
    if (!file || !fileHeader)
 
467
        return NULL;
 
468
 
 
469
    if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader))
 
470
    return NULL;
 
471
    if (!_XcursorReadUInt (file, &head.width))
 
472
    return NULL;
 
473
    if (!_XcursorReadUInt (file, &head.height))
 
474
    return NULL;
 
475
    if (!_XcursorReadUInt (file, &head.xhot))
 
476
    return NULL;
 
477
    if (!_XcursorReadUInt (file, &head.yhot))
 
478
    return NULL;
 
479
    if (!_XcursorReadUInt (file, &head.delay))
 
480
    return NULL;
 
481
    /* sanity check data */
 
482
    if (head.width >= 0x10000 || head.height > 0x10000)
 
483
    return NULL;
 
484
    if (head.width == 0 || head.height == 0)
 
485
    return NULL;
 
486
    if (head.xhot > head.width || head.yhot > head.height)
 
487
    return NULL;
 
488
 
 
489
    /* Create the image and initialize it */
 
490
    image = XcursorImageCreate (head.width, head.height);
 
491
    if (image == NULL)
 
492
        return NULL;
 
493
    if (chunkHeader.version < image->version)
 
494
    image->version = chunkHeader.version;
 
495
    image->size = chunkHeader.subtype;
 
496
    image->xhot = head.xhot;
 
497
    image->yhot = head.yhot;
 
498
    image->delay = head.delay;
 
499
    n = image->width * image->height;
 
500
    p = image->pixels;
 
501
    while (n--)
 
502
    {
 
503
    if (!_XcursorReadUInt (file, p))
 
504
    {
 
505
        XcursorImageDestroy (image);
 
506
        return NULL;
 
507
    }
 
508
    p++;
 
509
    }
 
510
    return image;
 
511
}
 
512
 
 
513
static XcursorImages *
 
514
XcursorXcFileLoadImages (XcursorFile *file, int size)
 
515
{
 
516
    XcursorFileHeader    *fileHeader;
 
517
    XcursorDim        bestSize;
 
518
    int            nsize;
 
519
    XcursorImages    *images;
 
520
    int            n;
 
521
    int            toc;
 
522
 
 
523
    if (!file || size < 0)
 
524
    return NULL;
 
525
    fileHeader = _XcursorReadFileHeader (file);
 
526
    if (!fileHeader)
 
527
    return NULL;
 
528
    bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize);
 
529
    if (!bestSize)
 
530
    {
 
531
        _XcursorFileHeaderDestroy (fileHeader);
 
532
    return NULL;
 
533
    }
 
534
    images = XcursorImagesCreate (nsize);
 
535
    if (!images)
 
536
    {
 
537
        _XcursorFileHeaderDestroy (fileHeader);
 
538
    return NULL;
 
539
    }
 
540
    for (n = 0; n < nsize; n++)
 
541
    {
 
542
    toc = _XcursorFindImageToc (fileHeader, bestSize, n);
 
543
    if (toc < 0)
 
544
        break;
 
545
    images->images[images->nimage] = _XcursorReadImage (file, fileHeader,
 
546
                                toc);
 
547
    if (!images->images[images->nimage])
 
548
        break;
 
549
    images->nimage++;
 
550
    }
 
551
    _XcursorFileHeaderDestroy (fileHeader);
 
552
    if (images->nimage != nsize)
 
553
    {
 
554
    XcursorImagesDestroy (images);
 
555
    images = NULL;
 
556
    }
 
557
    return images;
 
558
}
 
559
 
 
560
static int
 
561
_XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len)
 
562
{
 
563
    FILE    *f = file->closure;
 
564
    return fread (buf, 1, len, f);
 
565
}
 
566
 
 
567
static int
 
568
_XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len)
 
569
{
 
570
    FILE    *f = file->closure;
 
571
    return fwrite (buf, 1, len, f);
 
572
}
 
573
 
 
574
static int
 
575
_XcursorStdioFileSeek (XcursorFile *file, long offset, int whence)
 
576
{
 
577
    FILE    *f = file->closure;
 
578
    return fseek (f, offset, whence);
 
579
}
 
580
 
 
581
static void
 
582
_XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file)
 
583
{
 
584
    file->closure = stdfile;
 
585
    file->read = _XcursorStdioFileRead;
 
586
    file->write = _XcursorStdioFileWrite;
 
587
    file->seek = _XcursorStdioFileSeek;
 
588
}
 
589
 
 
590
static XcursorImages *
 
591
XcursorFileLoadImages (FILE *file, int size)
 
592
{
 
593
    XcursorFile    f;
 
594
 
 
595
    if (!file)
 
596
        return NULL;
 
597
 
 
598
    _XcursorStdioFileInitialize (file, &f);
 
599
    return XcursorXcFileLoadImages (&f, size);
 
600
}
 
601
 
 
602
/*
 
603
 * From libXcursor/src/library.c
 
604
 */
 
605
 
 
606
#ifndef ICONDIR
 
607
#define ICONDIR "/usr/X11R6/lib/X11/icons"
 
608
#endif
 
609
 
 
610
#ifndef XCURSORPATH
 
611
#define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:/usr/share/cursors/xorg-x11:"ICONDIR
 
612
#endif
 
613
 
 
614
static const char *
 
615
XcursorLibraryPath (void)
 
616
{
 
617
    static const char    *path;
 
618
 
 
619
    if (!path)
 
620
    {
 
621
    path = getenv ("XCURSOR_PATH");
 
622
    if (!path)
 
623
        path = XCURSORPATH;
 
624
    }
 
625
    return path;
 
626
}
 
627
 
 
628
static  void
 
629
_XcursorAddPathElt (char *path, const char *elt, int len)
 
630
{
 
631
    int        pathlen = strlen (path);
 
632
 
 
633
    /* append / if the path doesn't currently have one */
 
634
    if (path[0] == '\0' || path[pathlen - 1] != '/')
 
635
    {
 
636
    strcat (path, "/");
 
637
    pathlen++;
 
638
    }
 
639
    if (len == -1)
 
640
    len = strlen (elt);
 
641
    /* strip leading slashes */
 
642
    while (len && elt[0] == '/')
 
643
    {
 
644
    elt++;
 
645
    len--;
 
646
    }
 
647
    strncpy (path + pathlen, elt, len);
 
648
    path[pathlen + len] = '\0';
 
649
}
 
650
 
 
651
static char *
 
652
_XcursorBuildThemeDir (const char *dir, const char *theme)
 
653
{
 
654
    const char        *colon;
 
655
    const char        *tcolon;
 
656
    char        *full;
 
657
    char        *home;
 
658
    int            dirlen;
 
659
    int            homelen;
 
660
    int            themelen;
 
661
    int            len;
 
662
 
 
663
    if (!dir || !theme)
 
664
        return NULL;
 
665
 
 
666
    colon = strchr (dir, ':');
 
667
    if (!colon)
 
668
    colon = dir + strlen (dir);
 
669
 
 
670
    dirlen = colon - dir;
 
671
 
 
672
    tcolon = strchr (theme, ':');
 
673
    if (!tcolon)
 
674
    tcolon = theme + strlen (theme);
 
675
 
 
676
    themelen = tcolon - theme;
 
677
 
 
678
    home = NULL;
 
679
    homelen = 0;
 
680
    if (*dir == '~')
 
681
    {
 
682
    home = getenv ("HOME");
 
683
    if (!home)
 
684
        return NULL;
 
685
    homelen = strlen (home);
 
686
    dir++;
 
687
    dirlen--;
 
688
    }
 
689
 
 
690
    /*
 
691
     * add space for any needed directory separators, one per component,
 
692
     * and one for the trailing null
 
693
     */
 
694
    len = 1 + homelen + 1 + dirlen + 1 + themelen + 1;
 
695
 
 
696
    full = malloc (len);
 
697
    if (!full)
 
698
    return NULL;
 
699
    full[0] = '\0';
 
700
 
 
701
    if (home)
 
702
    _XcursorAddPathElt (full, home, -1);
 
703
    _XcursorAddPathElt (full, dir, dirlen);
 
704
    _XcursorAddPathElt (full, theme, themelen);
 
705
    return full;
 
706
}
 
707
 
 
708
static char *
 
709
_XcursorBuildFullname (const char *dir, const char *subdir, const char *file)
 
710
{
 
711
    char    *full;
 
712
 
 
713
    if (!dir || !subdir || !file)
 
714
        return NULL;
 
715
 
 
716
    full = malloc (strlen (dir) + 1 + strlen (subdir) + 1 + strlen (file) + 1);
 
717
    if (!full)
 
718
    return NULL;
 
719
    full[0] = '\0';
 
720
    _XcursorAddPathElt (full, dir, -1);
 
721
    _XcursorAddPathElt (full, subdir, -1);
 
722
    _XcursorAddPathElt (full, file, -1);
 
723
    return full;
 
724
}
 
725
 
 
726
static const char *
 
727
_XcursorNextPath (const char *path)
 
728
{
 
729
    char    *colon = strchr (path, ':');
 
730
 
 
731
    if (!colon)
 
732
    return NULL;
 
733
    return colon + 1;
 
734
}
 
735
 
 
736
#define XcursorWhite(c)    ((c) == ' ' || (c) == '\t' || (c) == '\n')
 
737
#define XcursorSep(c) ((c) == ';' || (c) == ',')
 
738
 
 
739
static char *
 
740
_XcursorThemeInherits (const char *full)
 
741
{
 
742
    char    line[8192];
 
743
    char    *result = NULL;
 
744
    FILE    *f;
 
745
 
 
746
    if (!full)
 
747
        return NULL;
 
748
 
 
749
    f = fopen (full, "r");
 
750
    if (f)
 
751
    {
 
752
    while (fgets (line, sizeof (line), f))
 
753
    {
 
754
        if (!strncmp (line, "Inherits", 8))
 
755
        {
 
756
        char    *l = line + 8;
 
757
        char    *r;
 
758
        while (*l == ' ') l++;
 
759
        if (*l != '=') continue;
 
760
        l++;
 
761
        while (*l == ' ') l++;
 
762
        result = malloc (strlen (l) + 1);
 
763
        if (result)
 
764
        {
 
765
            r = result;
 
766
            while (*l)
 
767
            {
 
768
            while (XcursorSep(*l) || XcursorWhite (*l)) l++;
 
769
            if (!*l)
 
770
                break;
 
771
            if (r != result)
 
772
                *r++ = ':';
 
773
            while (*l && !XcursorWhite(*l) &&
 
774
                   !XcursorSep(*l))
 
775
                *r++ = *l++;
 
776
            }
 
777
            *r++ = '\0';
 
778
        }
 
779
        break;
 
780
        }
 
781
    }
 
782
    fclose (f);
 
783
    }
 
784
    return result;
 
785
}
 
786
 
 
787
static FILE *
 
788
XcursorScanTheme (const char *theme, const char *name)
 
789
{
 
790
    FILE    *f = NULL;
 
791
    char    *full;
 
792
    char    *dir;
 
793
    const char  *path;
 
794
    char    *inherits = NULL;
 
795
    const char    *i;
 
796
 
 
797
    if (!theme || !name)
 
798
        return NULL;
 
799
 
 
800
    /*
 
801
     * Scan this theme
 
802
     */
 
803
    for (path = XcursorLibraryPath ();
 
804
     path && f == NULL;
 
805
     path = _XcursorNextPath (path))
 
806
    {
 
807
    dir = _XcursorBuildThemeDir (path, theme);
 
808
    if (dir)
 
809
    {
 
810
        full = _XcursorBuildFullname (dir, "cursors", name);
 
811
        if (full)
 
812
        {
 
813
        f = fopen (full, "r");
 
814
        free (full);
 
815
        }
 
816
        if (!f && !inherits)
 
817
        {
 
818
        full = _XcursorBuildFullname (dir, "", "index.theme");
 
819
        if (full)
 
820
        {
 
821
            inherits = _XcursorThemeInherits (full);
 
822
            free (full);
 
823
        }
 
824
        }
 
825
        free (dir);
 
826
    }
 
827
    }
 
828
    /*
 
829
     * Recurse to scan inherited themes
 
830
     */
 
831
    for (i = inherits; i && f == NULL; i = _XcursorNextPath (i))
 
832
    f = XcursorScanTheme (i, name);
 
833
    if (inherits != NULL)
 
834
    free (inherits);
 
835
    return f;
 
836
}
 
837
 
 
838
XcursorImages *
 
839
XcursorLibraryLoadImages (const char *file, const char *theme, int size)
 
840
{
 
841
    FILE        *f = NULL;
 
842
    XcursorImages   *images = NULL;
 
843
 
 
844
    if (!file)
 
845
        return NULL;
 
846
 
 
847
    if (theme)
 
848
    f = XcursorScanTheme (theme, file);
 
849
    if (!f)
 
850
    f = XcursorScanTheme ("default", file);
 
851
    if (f)
 
852
    {
 
853
    images = XcursorFileLoadImages (f, size);
 
854
    if (images)
 
855
        XcursorImagesSetName (images, file);
 
856
    fclose (f);
 
857
    }
 
858
    return images;
 
859
}
 
860
 
 
861
static void
 
862
load_all_cursors_from_dir(const char *path, int size,
 
863
              void (*load_callback)(XcursorImages *, void *),
 
864
              void *user_data)
 
865
{
 
866
    FILE *f;
 
867
    DIR *dir = opendir(path);
 
868
    struct dirent *ent;
 
869
    char *full;
 
870
    XcursorImages *images;
 
871
 
 
872
    if (!dir)
 
873
        return;
 
874
 
 
875
    for(ent = readdir(dir); ent; ent = readdir(dir)) {
 
876
#ifdef _DIRENT_HAVE_D_TYPE
 
877
        if (ent->d_type != DT_UNKNOWN &&
 
878
            (ent->d_type != DT_REG && ent->d_type != DT_LNK))
 
879
            continue;
 
880
#endif
 
881
 
 
882
        full = _XcursorBuildFullname(path, "", ent->d_name);
 
883
        if (!full)
 
884
            continue;
 
885
 
 
886
        f = fopen(full, "r");
 
887
        if (!f) {
 
888
            free(full);
 
889
            continue;
 
890
        }
 
891
 
 
892
        images = XcursorFileLoadImages(f, size);
 
893
 
 
894
        if (images) {
 
895
            XcursorImagesSetName(images, ent->d_name);
 
896
            load_callback(images, user_data);
 
897
        }
 
898
 
 
899
        fclose (f);
 
900
        free(full);
 
901
    }
 
902
 
 
903
    closedir(dir);
 
904
}
 
905
 
 
906
/** Load all the cursor of a theme
 
907
 *
 
908
 * This function loads all the cursor images of a given theme and its
 
909
 * inherited themes. Each cursor is loaded into an XcursorImages object
 
910
 * which is passed to the caller's load callback. If a cursor appears
 
911
 * more than once across all the inherited themes, the load callback
 
912
 * will be called multiple times, with possibly different XcursorImages
 
913
 * object which have the same name. The user is expected to destroy the
 
914
 * XcursorImages objects passed to the callback with
 
915
 * XcursorImagesDestroy().
 
916
 *
 
917
 * \param theme The name of theme that should be loaded
 
918
 * \param size The desired size of the cursor images
 
919
 * \param load_callback A callback function that will be called
 
920
 * for each cursor loaded. The first parameter is the XcursorImages
 
921
 * object representing the loaded cursor and the second is a pointer
 
922
 * to data provided by the user.
 
923
 * \param user_data The data that should be passed to the load callback
 
924
 */
 
925
void
 
926
xcursor_load_theme(const char *theme, int size,
 
927
            void (*load_callback)(XcursorImages *, void *),
 
928
            void *user_data)
 
929
{
 
930
    char *full, *dir;
 
931
    char *inherits = NULL;
 
932
    const char *path, *i;
 
933
 
 
934
    if (!theme)
 
935
        theme = "default";
 
936
 
 
937
    for (path = XcursorLibraryPath();
 
938
         path;
 
939
         path = _XcursorNextPath(path)) {
 
940
        dir = _XcursorBuildThemeDir(path, theme);
 
941
        if (!dir)
 
942
            continue;
 
943
 
 
944
        full = _XcursorBuildFullname(dir, "cursors", "");
 
945
 
 
946
        if (full) {
 
947
            load_all_cursors_from_dir(full, size, load_callback,
 
948
                          user_data);
 
949
            free(full);
 
950
        }
 
951
 
 
952
        if (!inherits) {
 
953
            full = _XcursorBuildFullname(dir, "", "index.theme");
 
954
            if (full) {
 
955
                inherits = _XcursorThemeInherits(full);
 
956
                free(full);
 
957
            }
 
958
        }
 
959
 
 
960
        free(dir);
 
961
    }
 
962
 
 
963
    for (i = inherits; i; i = _XcursorNextPath(i))
 
964
        xcursor_load_theme(i, size, load_callback, user_data);
 
965
 
 
966
    if (inherits)
 
967
        free(inherits);
 
968
}