]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkcursor-x11.c
Check for Xcursor.
[~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
34 #include "gdkprivate-x11.h"
35 #include "gdkcursor.h"
36 #include "gdkpixmap-x11.h"
37 #include "gdkx.h"
38 #include <gdk/gdkpixmap.h>
39 #include <gdk-pixbuf/gdk-pixbuf.h>
40
41
42 /**
43  * gdk_cursor_new_for_display:
44  * @display: the #GdkDisplay for which the cursor will be created
45  * @cursor_type: cursor to create
46  * 
47  * Creates a new cursor from the set of builtin cursors.
48  * Some useful ones are:
49  * <itemizedlist>
50  * <listitem><para>
51  *  <inlinegraphic format="PNG" fileref="right_ptr.png"></inlinegraphic> #GDK_RIGHT_PTR (right-facing arrow)
52  * </para></listitem>
53  * <listitem><para>
54  *  <inlinegraphic format="PNG" fileref="crosshair.png"></inlinegraphic> #GDK_CROSSHAIR (crosshair)
55  * </para></listitem>
56  * <listitem><para>
57  *  <inlinegraphic format="PNG" fileref="xterm.png"></inlinegraphic> #GDK_XTERM (I-beam)
58  * </para></listitem>
59  * <listitem><para>
60  * <inlinegraphic format="PNG" fileref="watch.png"></inlinegraphic> #GDK_WATCH (busy)
61  * </para></listitem>
62  * <listitem><para>
63  * <inlinegraphic format="PNG" fileref="fleur.png"></inlinegraphic> #GDK_FLEUR (for moving objects)
64  * </para></listitem>
65  * <listitem><para>
66  * <inlinegraphic format="PNG" fileref="hand1.png"></inlinegraphic> #GDK_HAND1 (a right-pointing hand)
67  * </para></listitem>
68  * <listitem><para>
69  * <inlinegraphic format="PNG" fileref="hand2.png"></inlinegraphic> #GDK_HAND2 (a left-pointing hand)
70  * </para></listitem>
71  * <listitem><para>
72  * <inlinegraphic format="PNG" fileref="left_side.png"></inlinegraphic> #GDK_LEFT_SIDE (resize left side)
73  * </para></listitem>
74  * <listitem><para>
75  * <inlinegraphic format="PNG" fileref="right_side.png"></inlinegraphic> #GDK_RIGHT_SIDE (resize right side)
76  * </para></listitem>
77  * <listitem><para>
78  * <inlinegraphic format="PNG" fileref="top_left_corner.png"></inlinegraphic> #GDK_TOP_LEFT_CORNER (resize northwest corner)
79  * </para></listitem>
80  * <listitem><para>
81  * <inlinegraphic format="PNG" fileref="top_right_corner.png"></inlinegraphic> #GDK_TOP_RIGHT_CORNER (resize northeast corner)
82  * </para></listitem>
83  * <listitem><para>
84  * <inlinegraphic format="PNG" fileref="bottom_left_corner.png"></inlinegraphic> #GDK_BOTTOM_LEFT_CORNER (resize southwest corner)
85  * </para></listitem>
86  * <listitem><para>
87  * <inlinegraphic format="PNG" fileref="bottom_right_corner.png"></inlinegraphic> #GDK_BOTTOM_RIGHT_CORNER (resize southeast corner)
88  * </para></listitem>
89  * <listitem><para>
90  * <inlinegraphic format="PNG" fileref="top_side.png"></inlinegraphic> #GDK_TOP_SIDE (resize top side)
91  * </para></listitem>
92  * <listitem><para>
93  * <inlinegraphic format="PNG" fileref="bottom_side.png"></inlinegraphic> #GDK_BOTTOM_SIDE (resize bottom side)
94  * </para></listitem>
95  * <listitem><para>
96  * <inlinegraphic format="PNG" fileref="sb_h_double_arrow.png"></inlinegraphic> #GDK_SB_H_DOUBLE_ARROW (move vertical splitter)
97  * </para></listitem>
98  * <listitem><para>
99  * <inlinegraphic format="PNG" fileref="sb_v_double_arrow.png"></inlinegraphic> #GDK_SB_V_DOUBLE_ARROW (move horizontal splitter)
100  * </para></listitem>
101  * </itemizedlist>
102  *
103  * To make the cursor invisible, use gdk_cursor_new_from_pixmap() to create
104  * a cursor with no pixels in it.
105  * 
106  * Return value: a new #GdkCursor
107  *
108  * Since: 2.2
109  **/
110 GdkCursor*
111 gdk_cursor_new_for_display (GdkDisplay    *display,
112                             GdkCursorType  cursor_type)
113 {
114   GdkCursorPrivate *private;
115   GdkCursor *cursor;
116   Cursor xcursor;
117
118   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
119
120   if (display->closed)
121     xcursor = None;
122   else
123     xcursor = XCreateFontCursor (GDK_DISPLAY_XDISPLAY (display), cursor_type);
124   
125   private = g_new (GdkCursorPrivate, 1);
126   private->display = display;
127   private->xcursor = xcursor;
128   cursor = (GdkCursor *) private;
129   cursor->type = cursor_type;
130   cursor->ref_count = 1;
131   
132   return cursor;
133 }
134
135 /**
136  * gdk_cursor_new_from_pixmap:
137  * @source: the pixmap specifying the cursor.
138  * @mask: the pixmap specifying the mask, which must be the same size as 
139  *    @source.
140  * @fg: the foreground color, used for the bits in the source which are 1.
141  *    The color does not have to be allocated first. 
142  * @bg: the background color, used for the bits in the source which are 0.
143  *    The color does not have to be allocated first.
144  * @x: the horizontal offset of the 'hotspot' of the cursor. 
145  * @y: the vertical offset of the 'hotspot' of the cursor.
146  * 
147  * Creates a new cursor from a given pixmap and mask. Both the pixmap and mask
148  * must have a depth of 1 (i.e. each pixel has only 2 values - on or off).
149  * The standard cursor size is 16 by 16 pixels. You can create a bitmap 
150  * from inline data as in the below example.
151  * 
152  * <example><title>Creating a custom cursor</title>
153  * <programlisting>
154  * /<!-- -->* This data is in X bitmap format, and can be created with the 'bitmap'
155  *    utility. *<!-- -->/
156  * &num;define cursor1_width 16
157  * &num;define cursor1_height 16
158  * static unsigned char cursor1_bits[] = {
159  *   0x80, 0x01, 0x40, 0x02, 0x20, 0x04, 0x10, 0x08, 0x08, 0x10, 0x04, 0x20,
160  *   0x82, 0x41, 0x41, 0x82, 0x41, 0x82, 0x82, 0x41, 0x04, 0x20, 0x08, 0x10,
161  *   0x10, 0x08, 0x20, 0x04, 0x40, 0x02, 0x80, 0x01};
162  *  
163  * static unsigned char cursor1mask_bits[] = {
164  *   0x80, 0x01, 0xc0, 0x03, 0x60, 0x06, 0x30, 0x0c, 0x18, 0x18, 0x8c, 0x31,
165  *   0xc6, 0x63, 0x63, 0xc6, 0x63, 0xc6, 0xc6, 0x63, 0x8c, 0x31, 0x18, 0x18,
166  *   0x30, 0x0c, 0x60, 0x06, 0xc0, 0x03, 0x80, 0x01};
167  *  
168  *  
169  *  GdkCursor *cursor;
170  *  GdkPixmap *source, *mask;
171  *  GdkColor fg = { 0, 65535, 0, 0 }; /<!-- -->* Red. *<!-- -->/
172  *  GdkColor bg = { 0, 0, 0, 65535 }; /<!-- -->* Blue. *<!-- -->/
173  *  
174  *  
175  *  source = gdk_bitmap_create_from_data (NULL, cursor1_bits,
176  *                                        cursor1_width, cursor1_height);
177  *  mask = gdk_bitmap_create_from_data (NULL, cursor1mask_bits,
178  *                                      cursor1_width, cursor1_height);
179  *  cursor = gdk_cursor_new_from_pixmap (source, mask, &amp;fg, &amp;bg, 8, 8);
180  *  gdk_pixmap_unref (source);
181  *  gdk_pixmap_unref (mask);
182  *  
183  *  
184  *  gdk_window_set_cursor (widget->window, cursor);
185  * </programlisting>
186  * </example>
187  *
188  * Return value: a new #GdkCursor.
189  **/
190 GdkCursor*
191 gdk_cursor_new_from_pixmap (GdkPixmap *source,
192                             GdkPixmap *mask,
193                             GdkColor  *fg,
194                             GdkColor  *bg,
195                             gint       x,
196                             gint       y)
197 {
198   GdkCursorPrivate *private;
199   GdkCursor *cursor;
200   Pixmap source_pixmap, mask_pixmap;
201   Cursor xcursor;
202   XColor xfg, xbg;
203   GdkDisplay *display;
204
205   g_return_val_if_fail (GDK_IS_PIXMAP (source), NULL);
206   g_return_val_if_fail (GDK_IS_PIXMAP (mask), NULL);
207   g_return_val_if_fail (fg != NULL, NULL);
208   g_return_val_if_fail (bg != NULL, NULL);
209
210   source_pixmap = GDK_PIXMAP_XID (source);
211   mask_pixmap   = GDK_PIXMAP_XID (mask);
212   display = GDK_PIXMAP_DISPLAY (source);
213
214   xfg.pixel = fg->pixel;
215   xfg.red = fg->red;
216   xfg.blue = fg->blue;
217   xfg.green = fg->green;
218   xbg.pixel = bg->pixel;
219   xbg.red = bg->red;
220   xbg.blue = bg->blue;
221   xbg.green = bg->green;
222   
223   if (display->closed)
224     xcursor = None;
225   else
226     xcursor = XCreatePixmapCursor (GDK_DISPLAY_XDISPLAY (display),
227                                    source_pixmap, mask_pixmap, &xfg, &xbg, x, y);
228   private = g_new (GdkCursorPrivate, 1);
229   private->display = display;
230   private->xcursor = xcursor;
231   cursor = (GdkCursor *) private;
232   cursor->type = GDK_CURSOR_IS_PIXMAP;
233   cursor->ref_count = 1;
234   
235   return cursor;
236 }
237
238 void
239 _gdk_cursor_destroy (GdkCursor *cursor)
240 {
241   GdkCursorPrivate *private;
242
243   g_return_if_fail (cursor != NULL);
244   g_return_if_fail (cursor->ref_count == 0);
245
246   private = (GdkCursorPrivate *) cursor;
247   if (!private->display->closed && private->xcursor)
248     XFreeCursor (GDK_DISPLAY_XDISPLAY (private->display), private->xcursor);
249
250   g_free (private);
251 }
252
253 /**
254  * gdk_x11_cursor_get_xdisplay:
255  * @cursor: a #GdkCursor.
256  * 
257  * Returns the display of a #GdkCursor.
258  * 
259  * Return value: an Xlib <type>Display*</type>.
260  **/
261 Display *
262 gdk_x11_cursor_get_xdisplay (GdkCursor *cursor)
263 {
264   g_return_val_if_fail (cursor != NULL, NULL);
265
266   return GDK_DISPLAY_XDISPLAY(((GdkCursorPrivate *)cursor)->display);
267 }
268
269 /**
270  * gdk_x11_cursor_get_xcursor:
271  * @cursor: a #GdkCursor.
272  * 
273  * Returns the X cursor belonging to a #GdkCursor.
274  * 
275  * Return value: an Xlib <type>Cursor</type>.
276  **/
277 Cursor
278 gdk_x11_cursor_get_xcursor (GdkCursor *cursor)
279 {
280   g_return_val_if_fail (cursor != NULL, None);
281
282   return ((GdkCursorPrivate *)cursor)->xcursor;
283 }
284
285 /** 
286  * gdk_cursor_get_display:
287  * @cursor: a #GdkCursor.
288  *
289  * Returns the display on which the #GdkCursor is defined.
290  *
291  * Returns: the #GdkDisplay associated to @cursor
292  *
293  * Since: 2.2
294  */
295
296 GdkDisplay *
297 gdk_cursor_get_display (GdkCursor *cursor)
298 {
299   g_return_val_if_fail (cursor != NULL, NULL);
300
301   return ((GdkCursorPrivate *)cursor)->display;
302 }
303
304
305 #ifdef HAVE_XCURSOR
306
307 static XcursorImage*
308 create_cursor_image (GdkPixbuf *pixbuf, 
309                      gint       x, 
310                      gint       y)
311 {
312   guint width, height, rowstride, n_channels;
313   guchar *pixels, *src;
314   XcursorImage *xcimage;
315   XcursorPixel *dest;
316   guchar a;
317   gint i, j;
318
319   width = gdk_pixbuf_get_width (pixbuf);
320   height = gdk_pixbuf_get_height (pixbuf);
321
322   n_channels = gdk_pixbuf_get_n_channels (pixbuf);
323   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
324   pixels = gdk_pixbuf_get_pixels (pixbuf);
325
326   xcimage = XcursorImageCreate (width, height);
327       
328   xcimage->xhot = x;
329   xcimage->yhot = y;
330
331   dest = xcimage->pixels;
332
333   for (j = 0; j < height; j++) 
334     {
335       src = pixels + j * rowstride;
336       for (i = 0; i < width; i++) 
337         {
338           if (n_channels == 3) 
339             a = 0xff;
340           else
341             a = src[3];
342           
343           *dest =  (a << 24) | (src[0] << 16) | (src[1] << 8) | src[2];
344           src += n_channels;
345           dest++;
346         }
347     }
348
349   return xcimage;
350 }
351
352
353 /**
354  * gdk_cursor_new_from_pixbuf:
355  * @display: the #GdkDisplay for which the cursor will be created
356  * @pixbuf: the #GdkPixbuf containing the cursor image
357  * @x: the horizontal offset of the 'hotspot' of the cursor. 
358  * @y: the vertical offset of the 'hotspot' of the cursor.
359  *
360  * Creates a new cursor from a pixbuf. 
361  *
362  * Not all GDK backends support RGBA cursors. If they are not 
363  * supported, a monochrome approximation will be displayed. 
364  * The functions gdk_display_supports_cursor_alpha() and 
365  * gdk_display_supports_cursor_color() can be used to determine
366  * whether RGBA cursors are supported; 
367  * gdk_display_get_default_cursor_size() and 
368  * gdk_display_get_maximal_cursor_size() give information about 
369  * cursor sizes.
370  *
371  * On the X backend, support for RGBA cursors requires a
372  * sufficently new version of the X Render extension. 
373  *
374  * Returns: a new #GdkCursor.
375  * 
376  * Since: 2.4
377  */
378 GdkCursor *
379 gdk_cursor_new_from_pixbuf (GdkDisplay *display, 
380                             GdkPixbuf  *pixbuf,
381                             gint        x,
382                             gint        y)
383 {
384   XcursorImage *xcimage;
385   Cursor xcursor;
386   GdkCursorPrivate *private;
387   GdkCursor *cursor;
388
389   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
390   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
391   g_return_val_if_fail (0 <= x && x < gdk_pixbuf_get_width (pixbuf), NULL);
392   g_return_val_if_fail (0 <= y && y < gdk_pixbuf_get_height (pixbuf), NULL);
393
394   if (display->closed)
395     xcursor = None;
396   else 
397     {
398       xcimage = create_cursor_image (pixbuf, x, y);
399       xcursor = XcursorImageLoadCursor (GDK_DISPLAY_XDISPLAY (display), xcimage);
400       XcursorImageDestroy (xcimage);
401     }
402
403   private = g_new (GdkCursorPrivate, 1);
404   private->display = display;
405   private->xcursor = xcursor;
406   cursor = (GdkCursor *) private;
407   cursor->type = GDK_CURSOR_IS_PIXMAP;
408   cursor->ref_count = 1;
409   
410   return cursor;
411 }
412
413 /**
414  * gdk_display_supports_cursor_alpha:
415  * @display: a #GdkDisplay
416  *
417  * Returns %TRUE if cursors can use an 8bit alpha channel 
418  * on @display. Otherwise, cursors are restricted to bilevel 
419  * alpha (i.e. a mask).
420  *
421  * Returns: whether cursors can have alpha channels.
422  *
423  * Since: 2.4
424  */
425 gboolean 
426 gdk_display_supports_cursor_alpha (GdkDisplay *display)
427 {
428   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
429
430   return XcursorSupportsARGB (GDK_DISPLAY_XDISPLAY (display));
431 }
432
433 /**
434  * gdk_display_supports_cursor_color:
435  * @display: a #GdkDisplay
436  *
437  * Returns %TRUE if multicolored cursors are supported
438  * on @display. Otherwise, cursors have only a forground
439  * and a background color.
440  *
441  * Returns: whether cursors can have multiple colors.
442  *
443  * Since: 2.4
444  */
445 gboolean 
446 gdk_display_supports_cursor_color (GdkDisplay *display)
447 {
448   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
449
450   return XcursorSupportsARGB (GDK_DISPLAY_XDISPLAY (display));
451 }
452
453 /**
454  * gdk_display_get_default_cursor_size:
455  * @display: a #GdkDisplay
456  *
457  * Returns the default size to use for cursors on @display.
458  *
459  * Returns: the default cursor size.
460  *
461  * Since: 2.4
462  */
463 guint     
464 gdk_display_get_default_cursor_size (GdkDisplay *display)
465 {
466   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
467
468   return XcursorGetDefaultSize (GDK_DISPLAY_XDISPLAY (display));
469 }
470
471 #else
472
473 GdkCursor *
474 gdk_cursor_new_from_pixbuf (GdkDisplay *display, 
475                             GdkPixbuf  *pixbuf,
476                             gint        x,
477                             gint        y)
478 {
479   GdkCursor *cursor;
480   GdkPixmap *pixmap, *mask;
481   guint width, height, n_channels, rowstride, i, j;
482   guint8 *data, *mask_data, *pixels;
483   GdkColor fg = { 0, 0, 0, 0 };
484   GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
485   GdkScreen *screen;
486
487   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
488   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
489
490   width = gdk_pixbuf_get_width (pixbuf);
491   height = gdk_pixbuf_get_height (pixbuf);
492
493   g_return_val_if_fail (0 <= x && x < width, NULL);
494   g_return_val_if_fail (0 <= y && y < height, NULL);
495
496   n_channels = gdk_pixbuf_get_n_channels (pixbuf);
497   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
498   pixels = gdk_pixbuf_get_pixels (pixbuf);
499
500   data = g_new0 (guint8, (width + 7) / 8 * height);
501   mask_data = g_new0 (guint8, (width + 7) / 8 * height);
502
503   for (j = 0; j < height; j++)
504     {
505       guint8 *src = pixels + j * rowstride;
506       guint8 *d = data + (width + 7) / 8 * j;
507       guint8 *md = mask_data + (width + 7) / 8 * j;
508         
509       for (i = 0; i < width; i++)
510         {
511           if (src[1] < 0x80)
512             *d |= 1 << (i % 8);
513           
514           if (n_channels == 3 || src[3] >= 0x80)
515             *md |= 1 << (i % 8);
516           
517           src += n_channels;
518           if (i % 8 == 0)
519             {
520               d++; 
521               md++;
522             }
523         }
524     }
525       
526   screen = gdk_display_get_default_screen (display);
527   pixmap = gdk_bitmap_create_from_data (gdk_screen_get_root_window (screen), 
528                                         data, width, height);
529  
530   mask = gdk_bitmap_create_from_data (gdk_screen_get_root_window (screen),
531                                       mask_data, width, height);
532    
533   cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, x, y);
534    
535   g_object_unref (pixmap);
536   g_object_unref (mask);
537
538   g_free (data);
539   g_free (mask_data);
540   
541   return cursor;
542 }
543
544 gboolean 
545 gdk_display_supports_cursor_alpha (GdkDisplay    *display)
546 {
547   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
548
549   return FALSE;
550 }
551
552 gboolean 
553 gdk_display_supports_cursor_color (GdkDisplay    *display)
554 {
555   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
556
557   return FALSE;
558 }
559
560 guint     
561 gdk_display_default_cursor_size (GdkDisplay    *display)
562 {
563   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
564   
565   /* no idea, really */
566   return 20; 
567 }
568
569 #endif
570
571
572 /**
573  * gdk_display_get_maximal_cursor_size:
574  * @display: a #GdkDisplay
575  * @width: the return location for the maximal cursor width
576  * @height: the return location for the maximal cursor height
577  *
578  * Returns the maximal size to use for cursors on @display.
579  *
580  * Since: 2.4
581  */
582 void     
583 gdk_display_get_maximal_cursor_size (GdkDisplay *display,
584                                      guint       *width,
585                                      guint       *height)
586 {
587   GdkScreen *screen;
588   GdkWindow *window;
589
590   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
591   
592   screen = gdk_display_get_default_screen (display);
593   window = gdk_screen_get_root_window (screen);
594   XQueryBestCursor (GDK_DISPLAY_XDISPLAY (display), 
595                     GDK_WINDOW_XWINDOW (window), 
596                     128, 128, width, height);
597 }
598