]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkcursor-x11.c
Add a stub for the no-Xcursor case. (noticed by Luis Villa)
[~andy/gtk] / gdk / x11 / gdkcursor-x11.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <config.h>
28 #include <X11/Xlib.h>
29 #include <X11/cursorfont.h>
30 #ifdef HAVE_XCURSOR
31 #include <X11/Xcursor/Xcursor.h>
32 #endif
33 #ifdef HAVE_XFIXES
34 #include <X11/extensions/Xfixes.h>
35 #endif
36 #include <string.h>
37
38 #include "gdkprivate-x11.h"
39 #include "gdkcursor.h"
40 #include "gdkpixmap-x11.h"
41 #include "gdkx.h"
42 #include <gdk/gdkpixmap.h>
43 #define GDK_PIXBUF_ENABLE_BACKEND
44 #include <gdk-pixbuf/gdk-pixbuf.h>
45 #include "gdkalias.h"
46
47
48 /**
49  * gdk_cursor_new_for_display:
50  * @display: the #GdkDisplay for which the cursor will be created
51  * @cursor_type: cursor to create
52  * 
53  * Creates a new cursor from the set of builtin cursors.
54  * Some useful ones are:
55  * <itemizedlist>
56  * <listitem><para>
57  *  <inlinegraphic format="PNG" fileref="right_ptr.png"></inlinegraphic> #GDK_RIGHT_PTR (right-facing arrow)
58  * </para></listitem>
59  * <listitem><para>
60  *  <inlinegraphic format="PNG" fileref="crosshair.png"></inlinegraphic> #GDK_CROSSHAIR (crosshair)
61  * </para></listitem>
62  * <listitem><para>
63  *  <inlinegraphic format="PNG" fileref="xterm.png"></inlinegraphic> #GDK_XTERM (I-beam)
64  * </para></listitem>
65  * <listitem><para>
66  * <inlinegraphic format="PNG" fileref="watch.png"></inlinegraphic> #GDK_WATCH (busy)
67  * </para></listitem>
68  * <listitem><para>
69  * <inlinegraphic format="PNG" fileref="fleur.png"></inlinegraphic> #GDK_FLEUR (for moving objects)
70  * </para></listitem>
71  * <listitem><para>
72  * <inlinegraphic format="PNG" fileref="hand1.png"></inlinegraphic> #GDK_HAND1 (a right-pointing hand)
73  * </para></listitem>
74  * <listitem><para>
75  * <inlinegraphic format="PNG" fileref="hand2.png"></inlinegraphic> #GDK_HAND2 (a left-pointing hand)
76  * </para></listitem>
77  * <listitem><para>
78  * <inlinegraphic format="PNG" fileref="left_side.png"></inlinegraphic> #GDK_LEFT_SIDE (resize left side)
79  * </para></listitem>
80  * <listitem><para>
81  * <inlinegraphic format="PNG" fileref="right_side.png"></inlinegraphic> #GDK_RIGHT_SIDE (resize right side)
82  * </para></listitem>
83  * <listitem><para>
84  * <inlinegraphic format="PNG" fileref="top_left_corner.png"></inlinegraphic> #GDK_TOP_LEFT_CORNER (resize northwest corner)
85  * </para></listitem>
86  * <listitem><para>
87  * <inlinegraphic format="PNG" fileref="top_right_corner.png"></inlinegraphic> #GDK_TOP_RIGHT_CORNER (resize northeast corner)
88  * </para></listitem>
89  * <listitem><para>
90  * <inlinegraphic format="PNG" fileref="bottom_left_corner.png"></inlinegraphic> #GDK_BOTTOM_LEFT_CORNER (resize southwest corner)
91  * </para></listitem>
92  * <listitem><para>
93  * <inlinegraphic format="PNG" fileref="bottom_right_corner.png"></inlinegraphic> #GDK_BOTTOM_RIGHT_CORNER (resize southeast corner)
94  * </para></listitem>
95  * <listitem><para>
96  * <inlinegraphic format="PNG" fileref="top_side.png"></inlinegraphic> #GDK_TOP_SIDE (resize top side)
97  * </para></listitem>
98  * <listitem><para>
99  * <inlinegraphic format="PNG" fileref="bottom_side.png"></inlinegraphic> #GDK_BOTTOM_SIDE (resize bottom side)
100  * </para></listitem>
101  * <listitem><para>
102  * <inlinegraphic format="PNG" fileref="sb_h_double_arrow.png"></inlinegraphic> #GDK_SB_H_DOUBLE_ARROW (move vertical splitter)
103  * </para></listitem>
104  * <listitem><para>
105  * <inlinegraphic format="PNG" fileref="sb_v_double_arrow.png"></inlinegraphic> #GDK_SB_V_DOUBLE_ARROW (move horizontal splitter)
106  * </para></listitem>
107  * </itemizedlist>
108  *
109  * To make the cursor invisible, use gdk_cursor_new_from_pixmap() to create
110  * a cursor with no pixels in it.
111  * 
112  * Return value: a new #GdkCursor
113  *
114  * Since: 2.2
115  **/
116 GdkCursor*
117 gdk_cursor_new_for_display (GdkDisplay    *display,
118                             GdkCursorType  cursor_type)
119 {
120   GdkCursorPrivate *private;
121   GdkCursor *cursor;
122   Cursor xcursor;
123
124   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
125
126   if (display->closed)
127     xcursor = None;
128   else
129     xcursor = XCreateFontCursor (GDK_DISPLAY_XDISPLAY (display), cursor_type);
130   
131   private = g_new (GdkCursorPrivate, 1);
132   private->display = display;
133   private->xcursor = xcursor;
134   private->name = NULL;
135   cursor = (GdkCursor *) private;
136   cursor->type = cursor_type;
137   cursor->ref_count = 1;
138   
139   return cursor;
140 }
141
142 /**
143  * gdk_cursor_new_from_pixmap:
144  * @source: the pixmap specifying the cursor.
145  * @mask: the pixmap specifying the mask, which must be the same size as 
146  *    @source.
147  * @fg: the foreground color, used for the bits in the source which are 1.
148  *    The color does not have to be allocated first. 
149  * @bg: the background color, used for the bits in the source which are 0.
150  *    The color does not have to be allocated first.
151  * @x: the horizontal offset of the 'hotspot' of the cursor. 
152  * @y: the vertical offset of the 'hotspot' of the cursor.
153  * 
154  * Creates a new cursor from a given pixmap and mask. Both the pixmap and mask
155  * must have a depth of 1 (i.e. each pixel has only 2 values - on or off).
156  * The standard cursor size is 16 by 16 pixels. You can create a bitmap 
157  * from inline data as in the below example.
158  * 
159  * <example><title>Creating a custom cursor</title>
160  * <programlisting>
161  * /<!-- -->* This data is in X bitmap format, and can be created with the 'bitmap'
162  *    utility. *<!-- -->/
163  * &num;define cursor1_width 16
164  * &num;define cursor1_height 16
165  * static unsigned char cursor1_bits[] = {
166  *   0x80, 0x01, 0x40, 0x02, 0x20, 0x04, 0x10, 0x08, 0x08, 0x10, 0x04, 0x20,
167  *   0x82, 0x41, 0x41, 0x82, 0x41, 0x82, 0x82, 0x41, 0x04, 0x20, 0x08, 0x10,
168  *   0x10, 0x08, 0x20, 0x04, 0x40, 0x02, 0x80, 0x01};
169  *  
170  * static unsigned char cursor1mask_bits[] = {
171  *   0x80, 0x01, 0xc0, 0x03, 0x60, 0x06, 0x30, 0x0c, 0x18, 0x18, 0x8c, 0x31,
172  *   0xc6, 0x63, 0x63, 0xc6, 0x63, 0xc6, 0xc6, 0x63, 0x8c, 0x31, 0x18, 0x18,
173  *   0x30, 0x0c, 0x60, 0x06, 0xc0, 0x03, 0x80, 0x01};
174  *  
175  *  
176  *  GdkCursor *cursor;
177  *  GdkPixmap *source, *mask;
178  *  GdkColor fg = { 0, 65535, 0, 0 }; /<!-- -->* Red. *<!-- -->/
179  *  GdkColor bg = { 0, 0, 0, 65535 }; /<!-- -->* Blue. *<!-- -->/
180  *  
181  *  
182  *  source = gdk_bitmap_create_from_data (NULL, cursor1_bits,
183  *                                        cursor1_width, cursor1_height);
184  *  mask = gdk_bitmap_create_from_data (NULL, cursor1mask_bits,
185  *                                      cursor1_width, cursor1_height);
186  *  cursor = gdk_cursor_new_from_pixmap (source, mask, &amp;fg, &amp;bg, 8, 8);
187  *  gdk_pixmap_unref (source);
188  *  gdk_pixmap_unref (mask);
189  *  
190  *  
191  *  gdk_window_set_cursor (widget->window, cursor);
192  * </programlisting>
193  * </example>
194  *
195  * Return value: a new #GdkCursor.
196  **/
197 GdkCursor*
198 gdk_cursor_new_from_pixmap (GdkPixmap      *source,
199                             GdkPixmap      *mask,
200                             const GdkColor *fg,
201                             const GdkColor *bg,
202                             gint            x,
203                             gint            y)
204 {
205   GdkCursorPrivate *private;
206   GdkCursor *cursor;
207   Pixmap source_pixmap, mask_pixmap;
208   Cursor xcursor;
209   XColor xfg, xbg;
210   GdkDisplay *display;
211
212   g_return_val_if_fail (GDK_IS_PIXMAP (source), NULL);
213   g_return_val_if_fail (GDK_IS_PIXMAP (mask), NULL);
214   g_return_val_if_fail (fg != NULL, NULL);
215   g_return_val_if_fail (bg != NULL, NULL);
216
217   source_pixmap = GDK_PIXMAP_XID (source);
218   mask_pixmap   = GDK_PIXMAP_XID (mask);
219   display = GDK_PIXMAP_DISPLAY (source);
220
221   xfg.pixel = fg->pixel;
222   xfg.red = fg->red;
223   xfg.blue = fg->blue;
224   xfg.green = fg->green;
225   xbg.pixel = bg->pixel;
226   xbg.red = bg->red;
227   xbg.blue = bg->blue;
228   xbg.green = bg->green;
229   
230   if (display->closed)
231     xcursor = None;
232   else
233     xcursor = XCreatePixmapCursor (GDK_DISPLAY_XDISPLAY (display),
234                                    source_pixmap, mask_pixmap, &xfg, &xbg, x, y);
235   private = g_new (GdkCursorPrivate, 1);
236   private->display = display;
237   private->xcursor = xcursor;
238   private->name = NULL;
239   cursor = (GdkCursor *) private;
240   cursor->type = GDK_CURSOR_IS_PIXMAP;
241   cursor->ref_count = 1;
242   
243   return cursor;
244 }
245
246 void
247 _gdk_cursor_destroy (GdkCursor *cursor)
248 {
249   GdkCursorPrivate *private;
250
251   g_return_if_fail (cursor != NULL);
252   g_return_if_fail (cursor->ref_count == 0);
253
254   private = (GdkCursorPrivate *) cursor;
255   if (!private->display->closed && private->xcursor)
256     XFreeCursor (GDK_DISPLAY_XDISPLAY (private->display), private->xcursor);
257
258   g_free (private->name);
259   g_free (private);
260 }
261
262 /**
263  * gdk_x11_cursor_get_xdisplay:
264  * @cursor: a #GdkCursor.
265  * 
266  * Returns the display of a #GdkCursor.
267  * 
268  * Return value: an Xlib <type>Display*</type>.
269  **/
270 Display *
271 gdk_x11_cursor_get_xdisplay (GdkCursor *cursor)
272 {
273   g_return_val_if_fail (cursor != NULL, NULL);
274
275   return GDK_DISPLAY_XDISPLAY(((GdkCursorPrivate *)cursor)->display);
276 }
277
278 /**
279  * gdk_x11_cursor_get_xcursor:
280  * @cursor: a #GdkCursor.
281  * 
282  * Returns the X cursor belonging to a #GdkCursor.
283  * 
284  * Return value: an Xlib <type>Cursor</type>.
285  **/
286 Cursor
287 gdk_x11_cursor_get_xcursor (GdkCursor *cursor)
288 {
289   g_return_val_if_fail (cursor != NULL, None);
290
291   return ((GdkCursorPrivate *)cursor)->xcursor;
292 }
293
294 /** 
295  * gdk_cursor_get_display:
296  * @cursor: a #GdkCursor.
297  *
298  * Returns the display on which the #GdkCursor is defined.
299  *
300  * Returns: the #GdkDisplay associated to @cursor
301  *
302  * Since: 2.2
303  */
304
305 GdkDisplay *
306 gdk_cursor_get_display (GdkCursor *cursor)
307 {
308   g_return_val_if_fail (cursor != NULL, NULL);
309
310   return ((GdkCursorPrivate *)cursor)->display;
311 }
312
313 #if defined(HAVE_XCURSOR) && defined(HAVE_XFIXES) && XFIXES_MAJOR >= 2
314
315 #if 0
316 XcursorComments *
317 load_comments (const char       *file, 
318                const char       *theme)
319 {
320     FILE            *f = 0;
321     XcursorImages   *images = 0;
322     XcursorComments *comments = 0;
323
324     if (theme)
325         f = XcursorScanTheme (theme, file);
326     if (!f)
327       f = XcursorScanTheme ("default", file);
328     if (f == XCURSOR_SCAN_CORE)
329       return 0;
330     if (f)
331       {
332         XcursorFileLoad (f, &comments, &images);
333         fclose (f);
334
335         if (images)
336           XcursorImagesDestroy (images);
337       }
338
339     return comments;
340 }
341 #endif
342
343 /**
344  * gdk_cursor_get_image:
345  * @cursor: a #GdkCursor
346  *
347  * Returns a #GdkPixbuf with the image used to display the cursor.
348  *
349  * Note that depending on the capabilities of the windowing system and 
350  * on the cursor, GDK may not be able to obtain the image data. In this 
351  * case, %NULL is returned.
352  *
353  * Returns: a #GdkPixbuf representing @cursor, or %NULL
354  *
355  * Since: 2.8
356  */
357 GdkPixbuf*  
358 gdk_cursor_get_image (GdkCursor *cursor)
359 {
360   Display *xdisplay;
361   GdkCursorPrivate *private;
362   XcursorImages *images = NULL;
363   XcursorImage *image;
364   XcursorComments *comments;
365   Atom atom;
366   gint size;
367   gchar buf[32];
368   guchar *data;
369   GdkPixbuf *pixbuf;
370   gchar *theme;
371   gint i, j;
372   
373   g_return_val_if_fail (cursor != NULL, NULL);
374
375   private = (GdkCursorPrivate *) cursor;
376     
377   xdisplay = GDK_DISPLAY_XDISPLAY (private->display);
378
379   size = XcursorGetDefaultSize (xdisplay);
380   theme = XcursorGetTheme (xdisplay);
381
382   if (cursor->type == GDK_CURSOR_IS_PIXMAP)
383     {
384       if (private->name)
385         images = XcursorLibraryLoadImages (private->name, theme, size);
386     }
387   else 
388     images = XcursorShapeLoadImages (cursor->type, theme, size);
389
390   if (!images)
391     return NULL;
392   
393   image = images->images[0];
394
395   data = g_malloc (4 * image->width * image->height);
396   memcpy (data, image->pixels, 4 * image->width * image->height);
397
398   pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, TRUE,
399                                      8, image->width, image->height,
400                                      4 * image->width, 
401                                      (GdkPixbufDestroyNotify)g_free, NULL);
402
403   if (private->name)
404     gdk_pixbuf_set_option (pixbuf, "name", private->name);
405   g_snprintf (buf, 32, "%d", image->xhot);
406   gdk_pixbuf_set_option (pixbuf, "x_hot", buf);
407   g_snprintf (buf, 32, "%d", image->yhot);
408   gdk_pixbuf_set_option (pixbuf, "y_hot", buf);
409
410 #if 0
411   comments = load_comments (images->name, theme);
412
413   j = 0;
414   for (i = 0; i < comments->ncomment; i++)
415     {
416       switch (comments->comments[i].comment_type)
417         {
418         case XCURSOR_COMMENT_COPYRIGHT:
419           gdk_pixbuf_set_option (pixbuf, "copyright", comments->comments[i].comment);
420           break;
421         case XCURSOR_COMMENT_LICENSE:
422           gdk_pixbuf_set_option (pixbuf, "license", comments->comments[i].comment);
423           break;
424         default:
425           g_snprintf (buf, 32, "comment%d", j++);
426           gdk_pixbuf_set_option (pixbuf, buf, comments->comments[i].comment);
427           break;
428         }
429     }
430   XcursorCommentsDestroy (comments);
431 #endif
432
433   XcursorImagesDestroy (images);
434
435   return pixbuf;
436 }
437
438 #else
439
440 GdkPixbuf*  
441 gdk_cursor_get_image (GdkCursor *cursor)
442 {
443   g_return_val_if_fail (cursor != NULL, NULL);
444   
445   return NULL;
446 }
447
448 #endif
449
450 #ifdef HAVE_XCURSOR
451
452 static XcursorImage*
453 create_cursor_image (GdkPixbuf *pixbuf,
454                      gint       x,
455                      gint       y)
456 {
457   guint width, height, rowstride, n_channels;
458   guchar *pixels, *src;
459   XcursorImage *xcimage;
460   XcursorPixel *dest;
461
462   width = gdk_pixbuf_get_width (pixbuf);
463   height = gdk_pixbuf_get_height (pixbuf);
464
465   n_channels = gdk_pixbuf_get_n_channels (pixbuf);
466   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
467   pixels = gdk_pixbuf_get_pixels (pixbuf);
468
469   xcimage = XcursorImageCreate (width, height);
470
471   xcimage->xhot = x;
472   xcimage->yhot = y;
473
474   dest = xcimage->pixels;
475
476   if (n_channels == 3)
477     {
478       gint i, j;
479
480       for (j = 0; j < height; j++)
481         {
482           src = pixels + j * rowstride;
483           for (i = 0; i < width; i++)
484             {
485               *dest = (0xff << 24) | (src[0] << 16) | (src[1] << 8) | src[2];
486             }
487
488           src += n_channels;
489           dest++;
490         }
491     }
492   else
493     {
494       _gdk_x11_convert_to_format (pixels, rowstride,
495                                   (guchar *) dest, 4 * width,
496                                   GDK_X11_FORMAT_ARGB,
497                                   (G_BYTE_ORDER == G_BIG_ENDIAN) ?
498                                   GDK_MSB_FIRST : GDK_LSB_FIRST,
499                                   width, height);
500     }
501
502   return xcimage;
503 }
504
505
506 /**
507  * gdk_cursor_new_from_pixbuf:
508  * @display: the #GdkDisplay for which the cursor will be created
509  * @pixbuf: the #GdkPixbuf containing the cursor image
510  * @x: the horizontal offset of the 'hotspot' of the cursor. 
511  * @y: the vertical offset of the 'hotspot' of the cursor.
512  *
513  * Creates a new cursor from a pixbuf. 
514  *
515  * Not all GDK backends support RGBA cursors. If they are not 
516  * supported, a monochrome approximation will be displayed. 
517  * The functions gdk_display_supports_cursor_alpha() and 
518  * gdk_display_supports_cursor_color() can be used to determine
519  * whether RGBA cursors are supported; 
520  * gdk_display_get_default_cursor_size() and 
521  * gdk_display_get_maximal_cursor_size() give information about 
522  * cursor sizes.
523  *
524  * On the X backend, support for RGBA cursors requires a
525  * sufficently new version of the X Render extension. 
526  *
527  * Returns: a new #GdkCursor.
528  * 
529  * Since: 2.4
530  */
531 GdkCursor *
532 gdk_cursor_new_from_pixbuf (GdkDisplay *display, 
533                             GdkPixbuf  *pixbuf,
534                             gint        x,
535                             gint        y)
536 {
537   XcursorImage *xcimage;
538   Cursor xcursor;
539   GdkCursorPrivate *private;
540   GdkCursor *cursor;
541
542   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
543   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
544   g_return_val_if_fail (0 <= x && x < gdk_pixbuf_get_width (pixbuf), NULL);
545   g_return_val_if_fail (0 <= y && y < gdk_pixbuf_get_height (pixbuf), NULL);
546
547   if (display->closed)
548     xcursor = None;
549   else 
550     {
551       xcimage = create_cursor_image (pixbuf, x, y);
552       xcursor = XcursorImageLoadCursor (GDK_DISPLAY_XDISPLAY (display), xcimage);
553       XcursorImageDestroy (xcimage);
554     }
555
556   private = g_new (GdkCursorPrivate, 1);
557   private->display = display;
558   private->xcursor = xcursor;
559   private->name = NULL;
560   cursor = (GdkCursor *) private;
561   cursor->type = GDK_CURSOR_IS_PIXMAP;
562   cursor->ref_count = 1;
563   
564   return cursor;
565 }
566
567 /**
568  * gdk_cursor_new_from_name:
569  * @display: the #GdkDisplay for which the cursor will be created
570  * @name: the name of the cursor
571  *
572  * Creates a new cursor by looking up @name in the current cursor
573  * theme. 
574  * 
575  * Returns: a new #GdkCursor, or %NULL if there is no cursor with 
576  *   the given name 
577  *
578  * Since: 2.8
579  */
580 GdkCursor*  
581 gdk_cursor_new_from_name (GdkDisplay  *display,
582                           const gchar *name)
583 {
584   Cursor xcursor;
585   Display *xdisplay;
586   GdkCursorPrivate *private;
587   GdkCursor *cursor;
588
589   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
590
591   if (display->closed)
592     xcursor = None;
593   else 
594     {
595       xdisplay = GDK_DISPLAY_XDISPLAY (display);
596       xcursor = XcursorLibraryLoadCursor (xdisplay, name);
597       if (xcursor == None)
598         return NULL;
599     }
600
601   private = g_new (GdkCursorPrivate, 1);
602   private->display = display;
603   private->xcursor = xcursor;
604   private->name = g_strdup (name);
605   cursor = (GdkCursor *) private;
606   cursor->type = GDK_CURSOR_IS_PIXMAP;
607   cursor->ref_count = 1;
608   
609   return cursor;
610 }
611
612 /**
613  * gdk_display_supports_cursor_alpha:
614  * @display: a #GdkDisplay
615  *
616  * Returns %TRUE if cursors can use an 8bit alpha channel 
617  * on @display. Otherwise, cursors are restricted to bilevel 
618  * alpha (i.e. a mask).
619  *
620  * Returns: whether cursors can have alpha channels.
621  *
622  * Since: 2.4
623  */
624 gboolean 
625 gdk_display_supports_cursor_alpha (GdkDisplay *display)
626 {
627   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
628
629   return XcursorSupportsARGB (GDK_DISPLAY_XDISPLAY (display));
630 }
631
632 /**
633  * gdk_display_supports_cursor_color:
634  * @display: a #GdkDisplay
635  *
636  * Returns %TRUE if multicolored cursors are supported
637  * on @display. Otherwise, cursors have only a forground
638  * and a background color.
639  *
640  * Returns: whether cursors can have multiple colors.
641  *
642  * Since: 2.4
643  */
644 gboolean 
645 gdk_display_supports_cursor_color (GdkDisplay *display)
646 {
647   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
648
649   return XcursorSupportsARGB (GDK_DISPLAY_XDISPLAY (display));
650 }
651
652 /**
653  * gdk_display_get_default_cursor_size:
654  * @display: a #GdkDisplay
655  *
656  * Returns the default size to use for cursors on @display.
657  *
658  * Returns: the default cursor size.
659  *
660  * Since: 2.4
661  */
662 guint     
663 gdk_display_get_default_cursor_size (GdkDisplay *display)
664 {
665   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
666
667   return XcursorGetDefaultSize (GDK_DISPLAY_XDISPLAY (display));
668 }
669
670 #else
671
672 GdkCursor *
673 gdk_cursor_new_from_pixbuf (GdkDisplay *display, 
674                             GdkPixbuf  *pixbuf,
675                             gint        x,
676                             gint        y)
677 {
678   GdkCursor *cursor;
679   GdkPixmap *pixmap, *mask;
680   guint width, height, n_channels, rowstride, i, j;
681   guint8 *data, *mask_data, *pixels;
682   GdkColor fg = { 0, 0, 0, 0 };
683   GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
684   GdkScreen *screen;
685
686   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
687   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
688
689   width = gdk_pixbuf_get_width (pixbuf);
690   height = gdk_pixbuf_get_height (pixbuf);
691
692   g_return_val_if_fail (0 <= x && x < width, NULL);
693   g_return_val_if_fail (0 <= y && y < height, NULL);
694
695   n_channels = gdk_pixbuf_get_n_channels (pixbuf);
696   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
697   pixels = gdk_pixbuf_get_pixels (pixbuf);
698
699   data = g_new0 (guint8, (width + 7) / 8 * height);
700   mask_data = g_new0 (guint8, (width + 7) / 8 * height);
701
702   for (j = 0; j < height; j++)
703     {
704       guint8 *src = pixels + j * rowstride;
705       guint8 *d = data + (width + 7) / 8 * j;
706       guint8 *md = mask_data + (width + 7) / 8 * j;
707         
708       for (i = 0; i < width; i++)
709         {
710           if (src[1] < 0x80)
711             *d |= 1 << (i % 8);
712           
713           if (n_channels == 3 || src[3] >= 0x80)
714             *md |= 1 << (i % 8);
715           
716           src += n_channels;
717           if (i % 8 == 7)
718             {
719               d++; 
720               md++;
721             }
722         }
723     }
724       
725   screen = gdk_display_get_default_screen (display);
726   pixmap = gdk_bitmap_create_from_data (gdk_screen_get_root_window (screen), 
727                                         data, width, height);
728  
729   mask = gdk_bitmap_create_from_data (gdk_screen_get_root_window (screen),
730                                       mask_data, width, height);
731    
732   cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, x, y);
733    
734   g_object_unref (pixmap);
735   g_object_unref (mask);
736
737   g_free (data);
738   g_free (mask_data);
739   
740   return cursor;
741 }
742
743 GdkCursor*  
744 gdk_cursor_new_from_name (GdkDisplay  *display,
745                           const gchar *name)
746 {
747   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
748
749   return NULL;
750 }
751
752 gboolean 
753 gdk_display_supports_cursor_alpha (GdkDisplay    *display)
754 {
755   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
756
757   return FALSE;
758 }
759
760 gboolean 
761 gdk_display_supports_cursor_color (GdkDisplay    *display)
762 {
763   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
764
765   return FALSE;
766 }
767
768 guint     
769 gdk_display_get_default_cursor_size (GdkDisplay    *display)
770 {
771   g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
772   
773   /* no idea, really */
774   return 20; 
775 }
776
777 #endif
778
779
780 /**
781  * gdk_display_get_maximal_cursor_size:
782  * @display: a #GdkDisplay
783  * @width: the return location for the maximal cursor width
784  * @height: the return location for the maximal cursor height
785  *
786  * Gets the maximal size to use for cursors on @display.
787  *
788  * Since: 2.4
789  */
790 void     
791 gdk_display_get_maximal_cursor_size (GdkDisplay *display,
792                                      guint       *width,
793                                      guint       *height)
794 {
795   GdkScreen *screen;
796   GdkWindow *window;
797
798   g_return_if_fail (GDK_IS_DISPLAY (display));
799   
800   screen = gdk_display_get_default_screen (display);
801   window = gdk_screen_get_root_window (screen);
802   XQueryBestCursor (GDK_DISPLAY_XDISPLAY (display), 
803                     GDK_WINDOW_XWINDOW (window), 
804                     128, 128, width, height);
805 }
806
807 #define __GDK_CURSOR_X11_C__
808 #include "gdkaliasdef.c"