]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkcursor-x11.c
Add vfuncs for a bunch of cursor functionality to GdkDisplay
[~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
29 /* needs to be first because any header might include gdk-pixbuf.h otherwise */
30 #define GDK_PIXBUF_ENABLE_BACKEND
31 #include <gdk-pixbuf/gdk-pixbuf.h>
32
33 #include "gdkcursor.h"
34
35 #include "gdkprivate-x11.h"
36 #include "gdkdisplay-x11.h"
37 #include "gdkx.h"
38
39 #include <X11/Xlib.h>
40 #include <X11/cursorfont.h>
41 #ifdef HAVE_XCURSOR
42 #include <X11/Xcursor/Xcursor.h>
43 #endif
44 #ifdef HAVE_XFIXES
45 #include <X11/extensions/Xfixes.h>
46 #endif
47 #include <string.h>
48 #include <errno.h>
49
50
51 static guint theme_serial = 0;
52
53 /* cursor_cache holds a cache of non-pixmap cursors to avoid expensive 
54  * libXcursor searches, cursors are added to it but only removed when
55  * their display is closed. We make the assumption that since there are 
56  * a small number of display's and a small number of cursor's that this 
57  * list will stay small enough not to be a problem.
58  */
59 static GSList* cursor_cache = NULL;
60
61 struct cursor_cache_key
62 {
63   GdkDisplay* display;
64   GdkCursorType type;
65   const char* name;
66 };
67
68 /* Caller should check if there is already a match first.
69  * Cursor MUST be either a typed cursor or a pixmap with 
70  * a non-NULL name.
71  */
72 static void
73 add_to_cache (GdkCursorPrivate* cursor)
74 {
75   cursor_cache = g_slist_prepend (cursor_cache, cursor);
76
77   /* Take a ref so that if the caller frees it we still have it */
78   gdk_cursor_ref ((GdkCursor*) cursor);
79 }
80
81 /* Returns 0 on a match
82  */
83 static gint
84 cache_compare_func (gconstpointer listelem, 
85                     gconstpointer target)
86 {
87   GdkCursorPrivate* cursor = (GdkCursorPrivate*)listelem;
88   struct cursor_cache_key* key = (struct cursor_cache_key*)target;
89
90   if ((cursor->cursor.type != key->type) ||
91       (cursor->display != key->display))
92     return 1; /* No match */
93   
94   /* Elements marked as pixmap must be named cursors 
95    * (since we don't store normal pixmap cursors 
96    */
97   if (key->type == GDK_CURSOR_IS_PIXMAP)
98     return strcmp (key->name, cursor->name);
99
100   return 0; /* Match */
101 }
102
103 /* Returns the cursor if there is a match, NULL if not
104  * For named cursors type shall be GDK_CURSOR_IS_PIXMAP
105  * For unnamed, typed cursors, name shall be NULL
106  */
107 static GdkCursorPrivate*
108 find_in_cache (GdkDisplay    *display, 
109                GdkCursorType  type,
110                const char    *name)
111 {
112   GSList* res;
113   struct cursor_cache_key key;
114
115   key.display = display;
116   key.type = type;
117   key.name = name;
118
119   res = g_slist_find_custom (cursor_cache, &key, cache_compare_func);
120
121   if (res)
122     return (GdkCursorPrivate *) res->data;
123
124   return NULL;
125 }
126
127 /* Called by gdk_display_x11_finalize to flush any cached cursors
128  * for a dead display.
129  */
130 void 
131 _gdk_x11_cursor_display_finalize (GdkDisplay *display)
132 {
133   GSList* item;
134   GSList** itemp; /* Pointer to the thing to fix when we delete an item */
135   item = cursor_cache;
136   itemp = &cursor_cache;
137   while (item)
138     {
139       GdkCursorPrivate* cursor = (GdkCursorPrivate*)(item->data);
140       if (cursor->display == display)
141         {
142           GSList* olditem;
143           gdk_cursor_unref ((GdkCursor*) cursor);
144           /* Remove this item from the list */
145           *(itemp) = item->next;
146           olditem = item;
147           item = g_slist_next (item);
148           g_slist_free_1 (olditem);
149         } 
150       else 
151         {
152           itemp = &(item->next);
153           item = g_slist_next (item);
154         }
155     }
156 }
157
158 static Cursor
159 get_blank_cursor (GdkDisplay *display)
160 {
161   GdkScreen *screen;
162   Pixmap pixmap;
163   XColor color;
164   Cursor cursor;
165   cairo_surface_t *surface;
166   cairo_t *cr;
167
168   screen = gdk_display_get_default_screen (display);
169   surface = _gdk_x11_window_create_bitmap_surface (gdk_screen_get_root_window (screen), 1, 1);
170   /* Clear surface */
171   cr = cairo_create (surface);
172   cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
173   cairo_paint (cr);
174   cairo_destroy (cr);
175  
176   pixmap = cairo_xlib_surface_get_drawable (surface);
177
178   color.pixel = 0; 
179   color.red = color.blue = color.green = 0;
180
181   if (gdk_display_is_closed (display))
182     cursor = None;
183   else
184     cursor = XCreatePixmapCursor (GDK_DISPLAY_XDISPLAY (display),
185                                   pixmap, pixmap,
186                                   &color, &color, 1, 1);
187   cairo_surface_destroy (surface);
188
189   return cursor;
190 }
191
192 GdkCursor*
193 _gdk_x11_display_get_cursor_for_type (GdkDisplay    *display,
194                                       GdkCursorType  cursor_type)
195 {
196   GdkCursorPrivate *private;
197   GdkCursor *cursor;
198   Cursor xcursor;
199
200   if (gdk_display_is_closed (display))
201     {
202       xcursor = None;
203     }
204   else
205     {
206       private = find_in_cache (display, cursor_type, NULL);
207
208       if (private)
209         {
210           /* Cache had it, add a ref for this user */
211           gdk_cursor_ref ((GdkCursor*) private);
212
213           return (GdkCursor*) private;
214         }
215       else
216         {
217           if (cursor_type != GDK_BLANK_CURSOR)
218             xcursor = XCreateFontCursor (GDK_DISPLAY_XDISPLAY (display),
219                                          cursor_type);
220           else
221             xcursor = get_blank_cursor (display);
222        }
223     }
224
225   private = g_new (GdkCursorPrivate, 1);
226   private->display = display;
227   private->xcursor = xcursor;
228   private->name = NULL;
229   private->serial = theme_serial;
230
231   cursor = (GdkCursor *) private;
232   cursor->type = cursor_type;
233   cursor->ref_count = 1;
234   
235   if (xcursor != None)
236     add_to_cache (private);
237
238   return cursor;
239 }
240
241 void
242 _gdk_cursor_destroy (GdkCursor *cursor)
243 {
244   GdkCursorPrivate *private;
245
246   g_return_if_fail (cursor != NULL);
247   g_return_if_fail (cursor->ref_count == 0);
248
249   private = (GdkCursorPrivate *) cursor;
250   if (private->xcursor && !gdk_display_is_closed (private->display))
251     XFreeCursor (GDK_DISPLAY_XDISPLAY (private->display), private->xcursor);
252
253   g_free (private->name);
254   g_free (private);
255 }
256
257 /**
258  * gdk_x11_cursor_get_xdisplay:
259  * @cursor: a #GdkCursor.
260  * 
261  * Returns the display of a #GdkCursor.
262  * 
263  * Return value: an Xlib <type>Display*</type>.
264  **/
265 Display *
266 gdk_x11_cursor_get_xdisplay (GdkCursor *cursor)
267 {
268   g_return_val_if_fail (cursor != NULL, NULL);
269
270   return GDK_DISPLAY_XDISPLAY(((GdkCursorPrivate *)cursor)->display);
271 }
272
273 /**
274  * gdk_x11_cursor_get_xcursor:
275  * @cursor: a #GdkCursor.
276  * 
277  * Returns the X cursor belonging to a #GdkCursor.
278  * 
279  * Return value: an Xlib <type>Cursor</type>.
280  **/
281 Cursor
282 gdk_x11_cursor_get_xcursor (GdkCursor *cursor)
283 {
284   g_return_val_if_fail (cursor != NULL, None);
285
286   return ((GdkCursorPrivate *)cursor)->xcursor;
287 }
288
289 /** 
290  * gdk_cursor_get_display:
291  * @cursor: a #GdkCursor.
292  *
293  * Returns the display on which the #GdkCursor is defined.
294  *
295  * Returns: (transfer none): the #GdkDisplay associated to @cursor
296  *
297  * Since: 2.2
298  */
299
300 GdkDisplay *
301 gdk_cursor_get_display (GdkCursor *cursor)
302 {
303   g_return_val_if_fail (cursor != NULL, NULL);
304
305   return ((GdkCursorPrivate *)cursor)->display;
306 }
307
308 #if defined(HAVE_XCURSOR) && defined(HAVE_XFIXES) && XFIXES_MAJOR >= 2
309
310 /**
311  * gdk_cursor_get_image:
312  * @cursor: a #GdkCursor
313  *
314  * Returns a #GdkPixbuf with the image used to display the cursor.
315  *
316  * Note that depending on the capabilities of the windowing system and 
317  * on the cursor, GDK may not be able to obtain the image data. In this 
318  * case, %NULL is returned.
319  *
320  * Returns: (transfer full): a #GdkPixbuf representing @cursor, or %NULL
321  *
322  * Since: 2.8
323  */
324 GdkPixbuf*  
325 gdk_cursor_get_image (GdkCursor *cursor)
326 {
327   Display *xdisplay;
328   GdkCursorPrivate *private;
329   XcursorImages *images = NULL;
330   XcursorImage *image;
331   gint size;
332   gchar buf[32];
333   guchar *data, *p, tmp;
334   GdkPixbuf *pixbuf;
335   gchar *theme;
336   
337   g_return_val_if_fail (cursor != NULL, NULL);
338
339   private = (GdkCursorPrivate *) cursor;
340     
341   xdisplay = GDK_DISPLAY_XDISPLAY (private->display);
342
343   size = XcursorGetDefaultSize (xdisplay);
344   theme = XcursorGetTheme (xdisplay);
345
346   if (cursor->type == GDK_CURSOR_IS_PIXMAP)
347     {
348       if (private->name)
349         images = XcursorLibraryLoadImages (private->name, theme, size);
350     }
351   else
352     images = XcursorShapeLoadImages (cursor->type, theme, size);
353
354   if (!images)
355     return NULL;
356
357   image = images->images[0];
358
359   data = g_malloc (4 * image->width * image->height);
360   memcpy (data, image->pixels, 4 * image->width * image->height);
361
362   for (p = data; p < data + (4 * image->width * image->height); p += 4)
363     {
364       tmp = p[0];
365       p[0] = p[2];
366       p[2] = tmp;
367     }
368
369   pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, TRUE,
370                                      8, image->width, image->height,
371                                      4 * image->width,
372                                      (GdkPixbufDestroyNotify)g_free, NULL);
373
374   if (private->name)
375     gdk_pixbuf_set_option (pixbuf, "name", private->name);
376   g_snprintf (buf, 32, "%d", image->xhot);
377   gdk_pixbuf_set_option (pixbuf, "x_hot", buf);
378   g_snprintf (buf, 32, "%d", image->yhot);
379   gdk_pixbuf_set_option (pixbuf, "y_hot", buf);
380
381   XcursorImagesDestroy (images);
382
383   return pixbuf;
384 }
385
386 void
387 _gdk_x11_cursor_update_theme (GdkCursor *cursor)
388 {
389   Display *xdisplay;
390   GdkCursorPrivate *private;
391   Cursor new_cursor = None;
392   GdkDisplayX11 *display_x11;
393
394   private = (GdkCursorPrivate *) cursor;
395   xdisplay = GDK_DISPLAY_XDISPLAY (private->display);
396   display_x11 = GDK_DISPLAY_X11 (private->display);
397
398   if (!display_x11->have_xfixes)
399     return;
400
401   if (private->serial == theme_serial)
402     return;
403
404   private->serial = theme_serial;
405
406   if (private->xcursor != None)
407     {
408       if (cursor->type == GDK_BLANK_CURSOR)
409         return;
410
411       if (cursor->type == GDK_CURSOR_IS_PIXMAP)
412         {
413           if (private->name)
414             new_cursor = XcursorLibraryLoadCursor (xdisplay, private->name);
415         }
416       else 
417         new_cursor = XcursorShapeLoadCursor (xdisplay, cursor->type);
418       
419       if (new_cursor != None)
420         {
421           XFixesChangeCursor (xdisplay, new_cursor, private->xcursor);
422           private->xcursor = new_cursor;
423         }
424     }
425 }
426
427 static void
428 update_cursor (gpointer data,
429                gpointer user_data)
430 {
431   GdkCursor *cursor;
432
433   cursor = (GdkCursor*)(data);
434
435   if (!cursor)
436     return;
437   
438   _gdk_x11_cursor_update_theme (cursor);
439 }
440
441 /**
442  * gdk_x11_display_set_cursor_theme:
443  * @display: a #GdkDisplay
444  * @theme: the name of the cursor theme to use, or %NULL to unset
445  *         a previously set value 
446  * @size: the cursor size to use, or 0 to keep the previous size
447  *
448  * Sets the cursor theme from which the images for cursor
449  * should be taken. 
450  * 
451  * If the windowing system supports it, existing cursors created 
452  * with gdk_cursor_new(), gdk_cursor_new_for_display() and 
453  * gdk_cursor_new_for_name() are updated to reflect the theme 
454  * change. Custom cursors constructed with
455  * gdk_cursor_new_from_pixbuf() will have to be handled
456  * by the application (GTK+ applications can learn about 
457  * cursor theme changes by listening for change notification
458  * for the corresponding #GtkSetting).
459  *
460  * Since: 2.8
461  */
462 void
463 gdk_x11_display_set_cursor_theme (GdkDisplay  *display,
464                                   const gchar *theme,
465                                   const gint   size)
466 {
467   GdkDisplayX11 *display_x11;
468   Display *xdisplay;
469   gchar *old_theme;
470   gint old_size;
471
472   g_return_if_fail (GDK_IS_DISPLAY (display));
473
474   display_x11 = GDK_DISPLAY_X11 (display);
475   xdisplay = GDK_DISPLAY_XDISPLAY (display);
476
477   old_theme = XcursorGetTheme (xdisplay);
478   old_size = XcursorGetDefaultSize (xdisplay);
479
480   if (old_size == size &&
481       (old_theme == theme ||
482        (old_theme && theme && strcmp (old_theme, theme) == 0)))
483     return;
484
485   theme_serial++;
486
487   XcursorSetTheme (xdisplay, theme);
488   if (size > 0)
489     XcursorSetDefaultSize (xdisplay, size);
490     
491   g_slist_foreach (cursor_cache, update_cursor, NULL);
492 }
493
494 #else
495
496 GdkPixbuf*  
497 gdk_cursor_get_image (GdkCursor *cursor)
498 {
499   g_return_val_if_fail (cursor != NULL, NULL);
500   
501   return NULL;
502 }
503
504 void
505 gdk_x11_display_set_cursor_theme (GdkDisplay  *display,
506                                   const gchar *theme,
507                                   const gint   size)
508 {
509   g_return_if_fail (GDK_IS_DISPLAY (display));
510 }
511
512 void
513 _gdk_x11_cursor_update_theme (GdkCursor *cursor)
514 {
515   g_return_if_fail (cursor != NULL);
516 }
517
518 #endif
519
520 #ifdef HAVE_XCURSOR
521
522 static XcursorImage*
523 create_cursor_image (GdkPixbuf *pixbuf,
524                      gint       x,
525                      gint       y)
526 {
527   guint width, height;
528   XcursorImage *xcimage;
529   cairo_surface_t *surface;
530   cairo_t *cr;
531
532   width = gdk_pixbuf_get_width (pixbuf);
533   height = gdk_pixbuf_get_height (pixbuf);
534
535   xcimage = XcursorImageCreate (width, height);
536
537   xcimage->xhot = x;
538   xcimage->yhot = y;
539
540   surface = cairo_image_surface_create_for_data ((guchar *) xcimage->pixels,
541                                                  CAIRO_FORMAT_ARGB32,
542                                                  width,
543                                                  height,
544                                                  width * 4);
545
546   cr = cairo_create (surface);
547   cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
548   gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
549   cairo_paint (cr);
550   cairo_destroy (cr);
551
552   cairo_surface_destroy (surface);
553
554   return xcimage;
555 }
556
557 GdkCursor *
558 _gdk_x11_display_get_cursor_for_pixbuf (GdkDisplay *display,
559                                         GdkPixbuf  *pixbuf,
560                                         gint        x,
561                                         gint        y)
562 {
563   XcursorImage *xcimage;
564   Cursor xcursor;
565   GdkCursorPrivate *private;
566   GdkCursor *cursor;
567   const char *option;
568   char *end;
569   gint64 value;
570
571   if (x == -1 && (option = gdk_pixbuf_get_option (pixbuf, "x_hot")))
572     {
573       errno = 0;
574       end = NULL;
575       value = g_ascii_strtoll (option, &end, 10);
576       if (errno == 0 &&
577           end != option &&
578           value >= 0 && value < G_MAXINT)
579         x = (gint) value;
580     }
581   if (y == -1 && (option = gdk_pixbuf_get_option (pixbuf, "y_hot")))
582     {
583       errno = 0;
584       end = NULL;
585       value = g_ascii_strtoll (option, &end, 10);
586       if (errno == 0 &&
587           end != option &&
588           value >= 0 && value < G_MAXINT)
589         y = (gint) value;
590     }
591
592   g_return_val_if_fail (0 <= x && x < gdk_pixbuf_get_width (pixbuf), NULL);
593   g_return_val_if_fail (0 <= y && y < gdk_pixbuf_get_height (pixbuf), NULL);
594
595   if (gdk_display_is_closed (display))
596     {
597       xcursor = None;
598     }
599   else
600     {
601       xcimage = create_cursor_image (pixbuf, x, y);
602       xcursor = XcursorImageLoadCursor (GDK_DISPLAY_XDISPLAY (display), xcimage);
603       XcursorImageDestroy (xcimage);
604     }
605
606   private = g_new (GdkCursorPrivate, 1);
607   private->display = display;
608   private->xcursor = xcursor;
609   private->name = NULL;
610   private->serial = theme_serial;
611
612   cursor = (GdkCursor *) private;
613   cursor->type = GDK_CURSOR_IS_PIXMAP;
614   cursor->ref_count = 1;
615
616   return cursor;
617 }
618
619 GdkCursor*
620 _gdk_x11_display_get_cursor_for_name (GdkDisplay  *display,
621                                       const gchar *name)
622 {
623   Cursor xcursor;
624   Display *xdisplay;
625   GdkCursorPrivate *private;
626   GdkCursor *cursor;
627
628   if (gdk_display_is_closed (display))
629     {
630       xcursor = None;
631     }
632   else
633     {
634       private = find_in_cache (display, GDK_CURSOR_IS_PIXMAP, name);
635
636       if (private)
637         {
638           /* Cache had it, add a ref for this user */
639           gdk_cursor_ref ((GdkCursor*) private);
640
641           return (GdkCursor*) private;
642         }
643
644       xdisplay = GDK_DISPLAY_XDISPLAY (display);
645       xcursor = XcursorLibraryLoadCursor (xdisplay, name);
646       if (xcursor == None)
647         return NULL;
648     }
649
650   private = g_new (GdkCursorPrivate, 1);
651   private->display = display;
652   private->xcursor = xcursor;
653   private->name = g_strdup (name);
654   private->serial = theme_serial;
655
656   cursor = (GdkCursor *) private;
657   cursor->type = GDK_CURSOR_IS_PIXMAP;
658   cursor->ref_count = 1;
659   add_to_cache (private);
660
661   return cursor;
662 }
663
664 gboolean
665 _gdk_x11_display_supports_cursor_alpha (GdkDisplay *display)
666 {
667   return XcursorSupportsARGB (GDK_DISPLAY_XDISPLAY (display));
668 }
669
670 gboolean
671 _gdk_x11_display_supports_cursor_color (GdkDisplay *display)
672 {
673   return XcursorSupportsARGB (GDK_DISPLAY_XDISPLAY (display));
674 }
675
676 void
677 _gdk_x11_display_get_default_cursor_size (GdkDisplay *display,
678                                           guint      *width,
679                                           guint      *height)
680 {
681   *width = *height = XcursorGetDefaultSize (GDK_DISPLAY_XDISPLAY (display));
682 }
683
684 #else
685
686 static GdkCursor*
687 gdk_cursor_new_from_pixmap (GdkDisplay     *display,
688                             Pixmap          source_pixmap,
689                             Pixmap          mask_pixmap,
690                             const GdkColor *fg,
691                             const GdkColor *bg,
692                             gint            x,
693                             gint            y)
694 {
695   GdkCursorPrivate *private;
696   GdkCursor *cursor;
697   Cursor xcursor;
698   XColor xfg, xbg;
699
700   g_return_val_if_fail (fg != NULL, NULL);
701   g_return_val_if_fail (bg != NULL, NULL);
702
703   xfg.pixel = fg->pixel;
704   xfg.red = fg->red;
705   xfg.blue = fg->blue;
706   xfg.green = fg->green;
707   xbg.pixel = bg->pixel;
708   xbg.red = bg->red;
709   xbg.blue = bg->blue;
710   xbg.green = bg->green;
711   
712   if (gdk_display_is_closed (display->closed))
713     xcursor = None;
714   else
715     xcursor = XCreatePixmapCursor (GDK_DISPLAY_XDISPLAY (display),
716                                    source_pixmap, mask_pixmap, &xfg, &xbg, x, y);
717   private = g_new (GdkCursorPrivate, 1);
718   private->display = display;
719   private->xcursor = xcursor;
720   private->name = NULL;
721   private->serial = theme_serial;
722
723   cursor = (GdkCursor *) private;
724   cursor->type = GDK_CURSOR_IS_PIXMAP;
725   cursor->ref_count = 1;
726
727   return cursor;
728 }
729
730 GdkCursor *
731 _gdk_x11_display_get_cursor_for_pixbuf (GdkDisplay *display,
732                                         GdkPixbuf  *pixbuf,
733                                         gint        x,
734                                         gint        y)
735 {
736   GdkCursor *cursor;
737   cairo_surface_t *pixmap, *mask;
738   guint width, height, n_channels, rowstride, data_stride, i, j;
739   guint8 *data, *mask_data, *pixels;
740   GdkColor fg = { 0, 0, 0, 0 };
741   GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
742   GdkScreen *screen;
743   cairo_surface_t *image;
744   cairo_t *cr;
745
746   width = gdk_pixbuf_get_width (pixbuf);
747   height = gdk_pixbuf_get_height (pixbuf);
748
749   g_return_val_if_fail (0 <= x && x < width, NULL);
750   g_return_val_if_fail (0 <= y && y < height, NULL);
751
752   n_channels = gdk_pixbuf_get_n_channels (pixbuf);
753   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
754   pixels = gdk_pixbuf_get_pixels (pixbuf);
755
756   data_stride = 4 * ((width + 31) / 32);
757   data = g_new0 (guint8, data_stride * height);
758   mask_data = g_new0 (guint8, data_stride * height);
759
760   for (j = 0; j < height; j++)
761     {
762       guint8 *src = pixels + j * rowstride;
763       guint8 *d = data + data_stride * j;
764       guint8 *md = mask_data + data_stride * j;
765
766       for (i = 0; i < width; i++)
767         {
768           if (src[1] < 0x80)
769             *d |= 1 << (i % 8);
770
771           if (n_channels == 3 || src[3] >= 0x80)
772             *md |= 1 << (i % 8);
773
774           src += n_channels;
775           if (i % 8 == 7)
776             {
777               d++;
778               md++;
779             }
780         }
781     }
782
783   screen = gdk_display_get_default_screen (display);
784
785   pixmap = _gdk_x11_window_create_bitmap_surface (gdk_screen_get_root_window (screen),
786                                                   width, height);
787   cr = cairo_create (pixmap);
788   image = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_A1,
789                                                width, height, data_stride);
790   cairo_set_source_surface (cr, image, 0, 0);
791   cairo_surface_destroy (image);
792   cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
793   cairo_paint (cr);
794   cairo_destroy (cr);
795
796   mask = _gdk_x11_window_create_bitmap_surface (gdk_screen_get_root_window (screen),
797                                                 width, height);
798   cr = cairo_create (mask);
799   image = cairo_image_surface_create_for_data (mask_data, CAIRO_FORMAT_A1,
800                                                width, height, data_stride);
801   cairo_set_source_surface (cr, image, 0, 0);
802   cairo_surface_destroy (image);
803   cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
804   cairo_paint (cr);
805   cairo_destroy (cr);
806
807   cursor = gdk_cursor_new_from_pixmap (display,
808                                        cairo_xlib_surface_get_drawable (pixmap),
809                                        cairo_xlib_surface_get_drawable (mask),
810                                        &fg, &bg,
811                                        x, y);
812
813   cairo_surface_destroy (pixmap);
814   cairo_surface_destroy (mask);
815
816   g_free (data);
817   g_free (mask_data);
818
819   return cursor;
820 }
821
822 GdkCursor*
823 _gdk_x11_display_get_cursor_for_name (GdkDisplay  *display,
824                                       const gchar *name)
825 {
826   return NULL;
827 }
828
829 gboolean
830 _gdk_x11_display_supports_cursor_alpha (GdkDisplay *display)
831 {
832   return FALSE;
833 }
834
835 gboolean
836 _gdk_x11_display_supports_cursor_color (GdkDisplay *display)
837 {
838   return FALSE;
839 }
840
841 void
842 _gdk_x11_display_get_default_cursor_size (GdkDisplay *display)
843 {
844   /* no idea, really */
845   return 20;
846 }
847
848 #endif
849
850
851 /**
852  * gdk_display_get_maximal_cursor_size:
853  * @display: a #GdkDisplay
854  * @width: (out): the return location for the maximal cursor width
855  * @height: (out): the return location for the maximal cursor height
856  *
857  * Gets the maximal size to use for cursors on @display.
858  *
859  * Since: 2.4
860  */
861 void
862 _gdk_x11_display_get_maximal_cursor_size (GdkDisplay *display,
863                                           guint       *width,
864                                           guint       *height)
865 {
866   GdkScreen *screen;
867   GdkWindow *window;
868
869   g_return_if_fail (GDK_IS_DISPLAY (display));
870
871   screen = gdk_display_get_default_screen (display);
872   window = gdk_screen_get_root_window (screen);
873   XQueryBestCursor (GDK_DISPLAY_XDISPLAY (display),
874                     GDK_WINDOW_XID (window),
875                     128, 128, width, height);
876 }