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