Edit

IABSD.fr/xenocara/lib/libXcursor/src/cursor.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2006-11-25 16:46:32
    Hash : 606ceaa6
    Message : import from X.Org 7.2RC1

  • lib/libXcursor/src/cursor.c
  • /*
     * $Id: cursor.c,v 1.1.1.1 2006/11/25 17:00:28 matthieu Exp $
     *
     * Copyright © 2002 Keith Packard
     *
     * Permission to use, copy, modify, distribute, and sell this software and its
     * documentation for any purpose is hereby granted without fee, provided that
     * the above copyright notice appear in all copies and that both that
     * copyright notice and this permission notice appear in supporting
     * documentation, and that the name of Keith Packard not be used in
     * advertising or publicity pertaining to distribution of the software without
     * specific, written prior permission.  Keith Packard makes no
     * representations about the suitability of this software for any purpose.  It
     * is provided "as is" without express or implied warranty.
     *
     * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
     * EVENT SHALL KEITH PACKARD 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 OR OTHER
     * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
     * PERFORMANCE OF THIS SOFTWARE.
     */
    
    #include "xcursorint.h"
    #include <X11/Xlibint.h>
    #include <X11/Xutil.h>
    
    XcursorCursors *
    XcursorCursorsCreate (Display *dpy, int size)
    {
        XcursorCursors  *cursors;
    
        cursors = malloc (sizeof (XcursorCursors) +
    		      size * sizeof (Cursor));
        if (!cursors)
    	return NULL;
        cursors->ref = 1;
        cursors->dpy = dpy;
        cursors->ncursor = 0;
        cursors->cursors = (Cursor *) (cursors + 1);
        return cursors;
    }
    
    void
    XcursorCursorsDestroy (XcursorCursors *cursors)
    {
        int	    n;
    
        if (!cursors)
          return;
    
        --cursors->ref;
        if (cursors->ref > 0)
    	return;
        
        for (n = 0; n < cursors->ncursor; n++)
    	XFreeCursor (cursors->dpy, cursors->cursors[n]);
        free (cursors);
    }
    
    XcursorAnimate *
    XcursorAnimateCreate (XcursorCursors *cursors)
    {
        XcursorAnimate  *animate;
    
        animate = malloc (sizeof (XcursorAnimate));
        if (!animate)
    	return NULL;
        animate->cursors = cursors;
        cursors->ref++;
        animate->sequence = 0;
        return animate;
    }
    
    void
    XcursorAnimateDestroy (XcursorAnimate *animate)
    {
        if (!animate)
          return;
    
        XcursorCursorsDestroy (animate->cursors);
        free (animate);
    }
    
    Cursor
    XcursorAnimateNext (XcursorAnimate *animate)
    {
        Cursor  cursor = animate->cursors->cursors[animate->sequence++];
    
        if (animate->sequence >= animate->cursors->ncursor)
    	animate->sequence = 0;
        return cursor;
    }
    
    static int
    nativeByteOrder (void)
    {
        int	x = 1;
    
        return (*((char *) &x) == 1) ? LSBFirst : MSBFirst;
    }
    
    static XcursorUInt
    _XcursorPixelBrightness (XcursorPixel p)
    {
        XcursorPixel    alpha = p >> 24;
        XcursorPixel    r, g, b;
    
        if (!alpha)
    	return 0;
        r = ((p >> 8) & 0xff00) / alpha;
        if (r > 0xff) r = 0xff;
        g = ((p >> 0) & 0xff00) / alpha;
        if (g > 0xff) g = 0xff;
        b = ((p << 8) & 0xff00) / alpha;
        if (b > 0xff) b = 0xff;
        return (r * 153 + g * 301 + b * 58) >> 9;
    }
    
    static unsigned short
    _XcursorDivideAlpha (XcursorUInt value, XcursorUInt alpha)
    {
        if (!alpha)
    	return 0;
        value = value * 255 / alpha;
        if (value > 255)
    	value = 255;
        return value | (value << 8);
    }
    
    static void
    _XcursorPixelToColor (XcursorPixel p, XColor *color)
    {
        XcursorPixel    alpha = p >> 24;
    
        color->pixel = 0;
        color->red =   _XcursorDivideAlpha ((p >> 16) & 0xff, alpha);
        color->green = _XcursorDivideAlpha ((p >>  8) & 0xff, alpha);
        color->blue =  _XcursorDivideAlpha ((p >>  0) & 0xff, alpha);
        color->flags = DoRed|DoGreen|DoBlue;
    }
    
    #undef DEBUG_IMAGE
    #ifdef DEBUG_IMAGE
    static void
    _XcursorDumpImage (XImage *image)
    {
        FILE    *f = fopen ("/tmp/images", "a");
        int	    x, y;
        if (!f)
    	return;
        fprintf (f, "%d x %x\n", image->width, image->height);
        for (y = 0; y < image->height; y++)
        {
    	for (x = 0; x < image->width; x++)
    	    fprintf (f, "%c", XGetPixel (image, x, y) ? '*' : ' ');
    	fprintf (f, "\n");
        }
        fflush (f);
        fclose (f);
    }
    
    static void
    _XcursorDumpColor (XColor *color, char *name)
    {
        FILE    *f = fopen ("/tmp/images", "a");
        fprintf (f, "%s: %x %x %x\n", name,
    	     color->red, color->green, color->blue);
        fflush (f);
        fclose (f);
    }
    #endif
    
    static int
    _XcursorCompareRed (const void *a, const void *b)
    {
        const XcursorPixel    *ap = a, *bp = b;
    
        return (int) (((*ap >> 16) & 0xff) - ((*bp >> 16) & 0xff));
    }
    
    static int
    _XcursorCompareGreen (const void *a, const void *b)
    {
        const XcursorPixel    *ap = a, *bp = b;
    
        return (int) (((*ap >> 8) & 0xff) - ((*bp >> 8) & 0xff));
    }
    
    static int
    _XcursorCompareBlue (const void *a, const void *b)
    {
        const XcursorPixel    *ap = a, *bp = b;
    
        return (int) (((*ap >> 0) & 0xff) - ((*bp >> 0) & 0xff));
    }
    
    static XcursorPixel
    _XcursorAverageColor (XcursorPixel *pixels, int npixels)
    {
        XcursorPixel    p;
        XcursorPixel    red, green, blue;
        int		    n = npixels;
    
        blue = green = red = 0;
        while (n--)
        {
    	p = *pixels++;
    	red += (p >> 16) & 0xff;
    	green += (p >> 8) & 0xff;
    	blue += (p >> 0) & 0xff;
        }
        if (!n)
    	return 0;
        return (0xff << 24) | ((red/npixels) << 16) | ((green/npixels) << 8) | (blue/npixels);
    }
    
    typedef struct XcursorCoreCursor {
        XImage  *src_image;
        XImage  *msk_image;
        XColor  on_color;
        XColor  off_color;
    } XcursorCoreCursor;
    
    static Bool
    _XcursorHeckbertMedianCut (const XcursorImage *image, XcursorCoreCursor *core)
    {
        XImage	    *src_image = core->src_image, *msk_image = core->msk_image;
        int		    npixels = image->width * image->height;
        int		    ncolors;
        int		    n;
        XcursorPixel    *po, *pn, *pc;
        XcursorPixel    p;
        XcursorPixel    red, green, blue, alpha;
        XcursorPixel    max_red, min_red, max_green, min_green, max_blue, min_blue;
        XcursorPixel    *temp, *pixels, *colors;
        int		    split;
        XcursorPixel    leftColor, centerColor, rightColor;
        int		    (*compare) (const void *, const void *);
        int		    x, y;
        
        /*
         * Temp space for converted image and converted colors
         */
        temp = malloc (npixels * sizeof (XcursorPixel) * 2);
        if (!temp)
    	return False;
        
        pixels = temp;
        colors = pixels + npixels;
        
        /*
         * Convert to 2-value alpha and build
         * array of opaque color values and an
         */
        po = image->pixels;
        pn = pixels;
        pc = colors;
        max_blue = max_green = max_red = 0;
        min_blue = min_green = min_red = 255;
        n = npixels;
        while (n--)
        {
    	p = *po++;
    	alpha = (p >> 24) & 0xff;
    	red = (p >> 16) & 0xff;
    	green = (p >> 8) & 0xff;
    	blue = (p >> 0) & 0xff;
    	if (alpha >= 0x80)
    	{
    	    red = red * 255 / alpha;
    	    green = green * 255 / alpha;
    	    blue = blue * 255 / alpha;
    	    if (red < min_red) min_red = red;
    	    if (red > max_red) max_red = red;
    	    if (green < min_green) min_green = green;
    	    if (green > max_green) max_green = green;
    	    if (blue < min_blue) min_blue = blue;
    	    if (blue > max_blue) max_blue = blue;
    	    p = ((0xff << 24) | (red << 16) | 
    		 (green << 8) | (blue << 0));
    	    *pc++ = p;
    	}
    	else
    	    p = 0;
    	*pn++ = p;
        }
        ncolors = pc - colors;
        
        /*
         * Compute longest dimension and sort
         */
        if ((max_green - min_green) >= (max_red - min_red) &&
    	(max_green - min_green) >= (max_blue - min_blue))
    	compare = _XcursorCompareGreen;
        else if ((max_red - min_red) >= (max_blue - min_blue))
    	compare = _XcursorCompareRed;
        else
    	compare = _XcursorCompareBlue;
        qsort (colors, ncolors, sizeof (XcursorPixel), compare);
        /*
         * Compute average colors on both sides of the cut
         */
        split = ncolors >> 1;
        leftColor  = _XcursorAverageColor (colors, split);
        centerColor = colors[split];
        rightColor = _XcursorAverageColor (colors + split, ncolors - split);
        /*
         * Select best color for each pixel
         */
        pn = pixels;
        for (y = 0; y < image->height; y++)
    	for (x = 0; x < image->width; x++)
    	{
    	    p = *pn++;
    	    if (p & 0xff000000)
    	    {
    		XPutPixel (msk_image, x, y, 1);
    		if ((*compare) (&p, &centerColor) >= 0)
    		    XPutPixel (src_image, x, y, 0);
    		else
    		    XPutPixel (src_image, x, y, 1);
    	    }
    	    else
    	    {
    		XPutPixel (msk_image, x, y, 0);
    		XPutPixel (src_image, x, y, 0);
    	    }
    	}
        free (temp);
        _XcursorPixelToColor (rightColor, &core->off_color);
        _XcursorPixelToColor (leftColor, &core->on_color);
        return True;
    }
    
    #if 0
    #define DITHER_DIM  4
    static XcursorPixel orderedDither[4][4] = {
        {  1,  9,  3, 11 },
        { 13,  5, 15,  7 },
        {  4, 12,  2, 10 },
        { 16,  8, 14,  6 }
    };
    #else
    #define DITHER_DIM 2
    static XcursorPixel orderedDither[2][2] = {
        {  1,  3,  },
        {  4,  2,  },
    };
    #endif
    
    #define DITHER_SIZE  ((sizeof orderedDither / sizeof orderedDither[0][0]) + 1)
    
    static Bool
    _XcursorBayerOrderedDither (const XcursorImage *image, XcursorCoreCursor *core)
    {
        int		    x, y;
        XcursorPixel    *pixel, p;
        XcursorPixel    a, i, d;
    
        pixel = image->pixels;
        for (y = 0; y < image->height; y++)
    	for (x = 0; x < image->width; x++)
    	{
    	    p = *pixel++;
    	    a = ((p >> 24) * DITHER_SIZE + 127) / 255;
    	    i = (_XcursorPixelBrightness (p) * DITHER_SIZE + 127) / 255;
    	    d = orderedDither[y&(DITHER_DIM-1)][x&(DITHER_DIM-1)];
    	    if (a > d)
    	    {
    		XPutPixel (core->msk_image, x, y, 1);
    		if (i > d)
    		    XPutPixel (core->src_image, x, y, 0);   /* white */
    		else
    		    XPutPixel (core->src_image, x, y, 1);   /* black */
    	    }
    	    else
    	    {
    		XPutPixel (core->msk_image, x, y, 0);
    		XPutPixel (core->src_image, x, y, 0);
    	    }
    	}
        core->on_color.red = 0;
        core->on_color.green = 0;
        core->on_color.blue = 0;
        core->off_color.red = 0xffff;
        core->off_color.green = 0xffff;
        core->off_color.blue = 0xffff;
        return True;
    }
    
    static Bool
    _XcursorFloydSteinberg (const XcursorImage *image, XcursorCoreCursor *core)
    {
        int		    *aPicture, *iPicture, *aP, *iP;
        XcursorPixel    *pixel, p;
        int		    aR, iR, aA, iA;
        int		    npixels = image->width * image->height;
        int		    n;
        int		    right = 1;
        int		    belowLeft = image->width - 1;
        int		    below = image->width;
        int		    belowRight = image->width + 1;
        int		    iError, aError;
        int		    iErrorRight, aErrorRight;
        int		    iErrorBelowLeft, aErrorBelowLeft;
        int		    iErrorBelow, aErrorBelow;
        int		    iErrorBelowRight, aErrorBelowRight;
        int		    x, y;
        int		    max_inten, min_inten, mean_inten;
    
        iPicture = malloc (npixels * sizeof (int) * 2);
        if (!iPicture)
    	return False;
        aPicture = iPicture + npixels;
    
        /*
         * Compute raw gray and alpha arrays
         */
        pixel = image->pixels;
        iP = iPicture;
        aP = aPicture;
        n = npixels;
        max_inten = 0;
        min_inten = 0xff;
        while (n--)
        {
    	p = *pixel++;
    	*aP++ = (int) (p >> 24);
    	iR = (int) _XcursorPixelBrightness (p);
    	if (iR > max_inten) max_inten = iR;
    	if (iR < min_inten) min_inten = iR;
    	*iP++ = iR;
        }
        /*
         * Draw the image while diffusing the error
         */
        iP = iPicture;
        aP = aPicture;
        mean_inten = (max_inten + min_inten + 1) >> 1;
        for (y = 0; y < image->height; y++)
    	for (x = 0; x < image->width; x++)
    	{
    	    aR = *aP;
    	    iR = *iP;
    	    if (aR >= 0x80)
    	    {
    		XPutPixel (core->msk_image, x, y, 1);
    		aA = 0xff;
    	    }
    	    else
    	    {
    		XPutPixel (core->msk_image, x, y, 0);
    		aA = 0x00;
    	    }
    	    if (iR >= mean_inten)
    	    {
    		XPutPixel (core->src_image, x, y, 0);
    		iA = max_inten;
    	    }
    	    else
    	    {
    		XPutPixel (core->src_image, x, y, 1);
    		iA = min_inten;
    	    }
    	    iError = iR - iA;
    	    aError = aR - aA;
    	    iErrorRight = (iError * 7) >> 4;
    	    iErrorBelowLeft = (iError * 3) >> 4;
    	    iErrorBelow = (iError * 5) >> 4;
    	    iErrorBelowRight = (iError - iErrorRight - 
    				iErrorBelowLeft - iErrorBelow);
    	    aErrorRight = (aError * 7) >> 4;
    	    aErrorBelowLeft = (aError * 3) >> 4;
    	    aErrorBelow = (aError * 5) >> 4;
    	    aErrorBelowRight = (aError - aErrorRight - 
    				aErrorBelowLeft - aErrorBelow);
    	    if (x < image->width - 1)
    	    {
    		iP[right] += iErrorRight; 
    		aP[right] += aErrorRight;
    	    }
    	    if (y < image->height - 1)
    	    {
    		if (x)
    		{
    		    iP[belowLeft] += iErrorBelowLeft;
    		    aP[belowLeft] += aErrorBelowLeft;
    		}
    		iP[below] += iErrorBelow;
    		aP[below] += aErrorBelow;
    		if (x < image->width - 1)
    		{
    		    iP[belowRight] += iErrorBelowRight;
    		    aP[belowRight] += aErrorBelowRight;
    		}
    	    }
    	    aP++;
    	    iP++;
    	}
        free (iPicture);
        core->on_color.red =
        core->on_color.green = 
        core->on_color.blue = (min_inten | min_inten << 8);
        core->off_color.red = 
        core->off_color.green =
        core->off_color.blue = (max_inten | max_inten << 8);
        return True;
    }
    
    static Bool
    _XcursorThreshold (const XcursorImage *image, XcursorCoreCursor *core)
    {
        XcursorPixel    *pixel, p;
        int		    x, y;
    
        /*
         * Draw the image, picking black for dark pixels and white for light
         */
        pixel = image->pixels;
        for (y = 0; y < image->height; y++)
    	for (x = 0; x < image->width; x++)
    	{
    	    p = *pixel++;
    	    if ((p >> 24) >= 0x80)
    	    {
    		XPutPixel (core->msk_image, x, y, 1);
    		if (_XcursorPixelBrightness (p) > 0x80)
    		    XPutPixel (core->src_image, x, y, 0);
    		else
    		    XPutPixel (core->src_image, x, y, 1);
    	    }
    	    else
    	    {
    		XPutPixel (core->msk_image, x, y, 0);
    		XPutPixel (core->src_image, x, y, 0);
    	    }
    	}
        core->on_color.red =
        core->on_color.green = 
        core->on_color.blue = 0;
        core->off_color.red = 
        core->off_color.green =
        core->off_color.blue = 0xffff;
        return True;
    }
    
    Cursor
    XcursorImageLoadCursor (Display *dpy, const XcursorImage *image)
    {
        Cursor  cursor;
        
    #if RENDER_MAJOR > 0 || RENDER_MINOR >= 5
        if (XcursorSupportsARGB (dpy))
        {
    	XImage		    ximage;
    	int		    screen = DefaultScreen (dpy);
    	Pixmap		    pixmap;
    	Picture		    picture;
    	GC		    gc;
    	XRenderPictFormat   *format;
    
    	ximage.width = image->width;
    	ximage.height = image->height;
    	ximage.xoffset = 0;
    	ximage.format = ZPixmap;
    	ximage.data = (char *) image->pixels;
    	ximage.byte_order = nativeByteOrder ();
    	ximage.bitmap_unit = 32;
    	ximage.bitmap_bit_order = ximage.byte_order;
    	ximage.bitmap_pad = 32;
    	ximage.depth = 32;
    	ximage.bits_per_pixel = 32;
    	ximage.bytes_per_line = image->width * 4;
    	ximage.red_mask = 0xff0000;
    	ximage.green_mask = 0x00ff00;
    	ximage.blue_mask = 0x0000ff;
    	ximage.obdata = NULL;
    	if (!XInitImage (&ximage))
    	    return None;
    	pixmap = XCreatePixmap (dpy, RootWindow (dpy, screen),
    				image->width, image->height, 32);
    	gc = XCreateGC (dpy, pixmap, 0, NULL);
    	XPutImage (dpy, pixmap, gc, &ximage, 
    		   0, 0, 0, 0, image->width, image->height);
    	XFreeGC (dpy, gc);
    	format = XRenderFindStandardFormat (dpy, PictStandardARGB32);
    	picture = XRenderCreatePicture (dpy, pixmap, format, 0, NULL);
    	XFreePixmap (dpy, pixmap);
    	cursor = XRenderCreateCursor (dpy, picture, 
    				      image->xhot, image->yhot);
    	XRenderFreePicture (dpy, picture);
        }
        else
    #endif
        {
    	XcursorDisplayInfo  *info = _XcursorGetDisplayInfo (dpy);
    	int		    screen = DefaultScreen (dpy);
    	XcursorCoreCursor   core;
    	Pixmap		    src_pixmap, msk_pixmap;
    	GC		    gc;
    	XGCValues	    gcv;
    
    	core.src_image = XCreateImage (dpy, NULL, 1, ZPixmap,
    				       0, NULL, image->width, image->height,
    				       32, 0);
    	core.src_image->data = Xmalloc (image->height * 
    					core.src_image->bytes_per_line);
    	core.msk_image = XCreateImage (dpy, NULL, 1, ZPixmap,
    				       0, NULL, image->width, image->height,
    				       32, 0);
    	core.msk_image->data = Xmalloc (image->height * 
    					core.msk_image->bytes_per_line);
    
    	switch (info->dither) {
    	case XcursorDitherThreshold:
    	    if (!_XcursorThreshold (image, &core))
    		return 0;
    	    break;
    	case XcursorDitherMedian:
    	    if (!_XcursorHeckbertMedianCut (image, &core))
    		return 0;
    	    break;
    	case XcursorDitherOrdered:
    	    if (!_XcursorBayerOrderedDither (image, &core))
    		return 0;
    	    break;
    	case XcursorDitherDiffuse:
    	    if (!_XcursorFloydSteinberg (image, &core))
    		return 0;
    	    break;
    	default:
    	    return 0;
    	}
    
    	/*
    	 * Create the cursor
    	 */
    	src_pixmap = XCreatePixmap (dpy, RootWindow (dpy, screen),
    				    image->width, image->height, 1);
    	msk_pixmap = XCreatePixmap (dpy, RootWindow (dpy, screen),
    				    image->width, image->height, 1);
    	gcv.foreground = 1;
    	gcv.background = 0;
    	gc = XCreateGC (dpy, src_pixmap, 
    			GCForeground|GCBackground,
    			&gcv);
    	XPutImage (dpy, src_pixmap, gc, core.src_image,
    		   0, 0, 0, 0, image->width, image->height);
    	
    	XPutImage (dpy, msk_pixmap, gc, core.msk_image,
    		   0, 0, 0, 0, image->width, image->height);
    	XFreeGC (dpy, gc);
    	
    #ifdef DEBUG_IMAGE
    	_XcursorDumpColor (&core.on_color, "on_color");
    	_XcursorDumpColor (&core.off_color, "off_color");
    	_XcursorDumpImage (core.src_image);
    	_XcursorDumpImage (core.msk_image);
    #endif
    	XDestroyImage (core.src_image);
    	XDestroyImage (core.msk_image);
    
    	cursor = XCreatePixmapCursor (dpy, src_pixmap, msk_pixmap,
    				      &core.on_color, &core.off_color,
    				      image->xhot, image->yhot);
    	XFreePixmap (dpy, src_pixmap);
    	XFreePixmap (dpy, msk_pixmap);
        }
        return cursor;
    }
    
    XcursorCursors *
    XcursorImagesLoadCursors (Display *dpy, const XcursorImages *images)
    {
        XcursorCursors  *cursors = XcursorCursorsCreate (dpy, images->nimage);
        int		    n;
    
        if (!cursors)
    	return NULL;
        for (n = 0; n < images->nimage; n++)
        {
    	cursors->cursors[n] = XcursorImageLoadCursor (dpy, images->images[n]);
    	if (!cursors->cursors[n])
    	{
    	    XcursorCursorsDestroy (cursors);
    	    return NULL;
    	}
    	cursors->ncursor++;
        }
        return cursors;
    }
    
    Cursor
    XcursorImagesLoadCursor (Display *dpy, const XcursorImages *images)
    {
        Cursor  cursor;
        if (images->nimage == 1 || !XcursorSupportsAnim (dpy))
    	cursor = XcursorImageLoadCursor (dpy, images->images[0]);
        else
        {
    	XcursorCursors	*cursors = XcursorImagesLoadCursors (dpy, images);
    	XAnimCursor	*anim;
    	int		n;
    	
    	if (!cursors)
    	    return 0;
    	anim = malloc (cursors->ncursor * sizeof (XAnimCursor));
    	if (!anim)
    	{
    	    XcursorCursorsDestroy (cursors);
    	    return 0;
    	}
    	for (n = 0; n < cursors->ncursor; n++)
    	{
    	    anim[n].cursor = cursors->cursors[n];
    	    anim[n].delay = images->images[n]->delay;
    	}
    	cursor = XRenderCreateAnimCursor (dpy, cursors->ncursor, anim);
    	XcursorCursorsDestroy(cursors);
    	free (anim);
        }
    #if defined HAVE_XFIXES && XFIXES_MAJOR >= 2
        if (images->name)
    	XFixesSetCursorName (dpy, cursor, images->name);
    #endif
        return cursor;
    }
    
    
    Cursor
    XcursorFilenameLoadCursor (Display *dpy, const char *file)
    {
        int		    size = XcursorGetDefaultSize (dpy);
        XcursorImages   *images = XcursorFilenameLoadImages (file, size);
        Cursor	    cursor;
        
        if (!images)
    	return None;
        cursor = XcursorImagesLoadCursor (dpy, images);
        XcursorImagesDestroy (images);
        return cursor;
    }
    
    XcursorCursors *
    XcursorFilenameLoadCursors (Display *dpy, const char *file)
    {
        int		    size = XcursorGetDefaultSize (dpy);
        XcursorImages   *images = XcursorFilenameLoadImages (file, size);
        XcursorCursors  *cursors;
        
        if (!images)
    	return NULL;
        cursors = XcursorImagesLoadCursors (dpy, images);
        XcursorImagesDestroy (images);
        return cursors;
    }
    
    /*
     * Stolen from XCreateGlyphCursor (which we cruelly override)
     */
    
    Cursor
    _XcursorCreateGlyphCursor(Display	    *dpy,
    			  Font		    source_font,
    			  Font		    mask_font,
    			  unsigned int	    source_char,
    			  unsigned int	    mask_char,
    			  XColor _Xconst    *foreground,
    			  XColor _Xconst    *background)
    {       
        Cursor cid;
        register xCreateGlyphCursorReq *req;
    
        LockDisplay(dpy);
        GetReq(CreateGlyphCursor, req);
        cid = req->cid = XAllocID(dpy);
        req->source = source_font;
        req->mask = mask_font;
        req->sourceChar = source_char;
        req->maskChar = mask_char;
        req->foreRed = foreground->red;
        req->foreGreen = foreground->green;
        req->foreBlue = foreground->blue;
        req->backRed = background->red;
        req->backGreen = background->green;
        req->backBlue = background->blue;
        UnlockDisplay(dpy);
        SyncHandle();
        return (cid);
    }
    
    /*
     * Stolen from XCreateFontCursor (which we cruelly override)
     */
    
    Cursor
    _XcursorCreateFontCursor (Display *dpy, unsigned int shape)
    {
        static XColor _Xconst foreground = { 0,    0,     0,     0  };  /* black */
        static XColor _Xconst background = { 0, 65535, 65535, 65535 };  /* white */
    
        /* 
         * the cursor font contains the shape glyph followed by the mask
         * glyph; so character position 0 contains a shape, 1 the mask for 0,
         * 2 a shape, etc.  <X11/cursorfont.h> contains hash define names
         * for all of these.
         */
    
        if (dpy->cursor_font == None) 
        {
    	dpy->cursor_font = XLoadFont (dpy, CURSORFONT);
    	if (dpy->cursor_font == None)
    	    return None;
        }
    
        return _XcursorCreateGlyphCursor (dpy, dpy->cursor_font, dpy->cursor_font, 
    				      shape, shape + 1, &foreground, &background);
    }