gdkcursor-x11.c 23.4 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* GDK - The GIMP Drawing Kit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19 20

/*
21
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22 23 24 25 26
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

27
#include "config.h"
28

29 30 31 32
/* needs to be first because any header might include gdk-pixbuf.h otherwise */
#define GDK_PIXBUF_ENABLE_BACKEND
#include <gdk-pixbuf/gdk-pixbuf.h>

33
#include "gdkcursor.h"
34
#include "gdkcursorprivate.h"
35 36 37
#include "gdkprivate-x11.h"
#include "gdkdisplay-x11.h"

Elliot Lee's avatar
Elliot Lee committed
38 39
#include <X11/Xlib.h>
#include <X11/cursorfont.h>
Matthias Clasen's avatar
Matthias Clasen committed
40 41 42
#ifdef HAVE_XCURSOR
#include <X11/Xcursor/Xcursor.h>
#endif
43 44 45 46
#ifdef HAVE_XFIXES
#include <X11/extensions/Xfixes.h>
#endif
#include <string.h>
47
#include <errno.h>
Owen Taylor's avatar
Started  
Owen Taylor committed
48

Benjamin Otte's avatar
Benjamin Otte committed
49 50 51 52 53 54
#define GDK_TYPE_X11_CURSOR              (gdk_x11_cursor_get_type ())
#define GDK_X11_CURSOR(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_X11_CURSOR, GdkX11Cursor))
#define GDK_X11_CURSOR_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_X11_CURSOR, GdkX11CursorClass))
#define GDK_IS_X11_CURSOR(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_X11_CURSOR))
#define GDK_IS_X11_CURSOR_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_X11_CURSOR))
#define GDK_X11_CURSOR_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_X11_CURSOR, GdkX11CursorClass))
55

Benjamin Otte's avatar
Benjamin Otte committed
56 57 58 59
typedef struct _GdkX11Cursor GdkX11Cursor;
typedef struct _GdkX11CursorClass GdkX11CursorClass;

struct _GdkX11Cursor
60 61
{
  GdkCursor cursor;
Benjamin Otte's avatar
Benjamin Otte committed
62

63 64 65 66 67 68
  Cursor xcursor;
  GdkDisplay *display;
  gchar *name;
  guint serial;
};

Benjamin Otte's avatar
Benjamin Otte committed
69 70 71 72
struct _GdkX11CursorClass
{
  GdkCursorClass cursor_class;
};
Matthias Clasen's avatar
Matthias Clasen committed
73

74
static guint theme_serial = 0;
Elliot Lee's avatar
Elliot Lee committed
75

76
/* cursor_cache holds a cache of non-pixmap cursors to avoid expensive 
Matthias Clasen's avatar
Matthias Clasen committed
77 78 79 80
 * libXcursor searches, cursors are added to it but only removed when
 * their display is closed. We make the assumption that since there are 
 * a small number of display's and a small number of cursor's that this 
 * list will stay small enough not to be a problem.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
 */
static GSList* cursor_cache = NULL;

struct cursor_cache_key
{
  GdkDisplay* display;
  GdkCursorType type;
  const char* name;
};

/* Caller should check if there is already a match first.
 * Cursor MUST be either a typed cursor or a pixmap with 
 * a non-NULL name.
 */
static void
Benjamin Otte's avatar
Benjamin Otte committed
96
add_to_cache (GdkX11Cursor* cursor)
97 98 99 100
{
  cursor_cache = g_slist_prepend (cursor_cache, cursor);

  /* Take a ref so that if the caller frees it we still have it */
101
  g_object_ref (cursor);
102 103 104 105 106 107 108 109
}

/* Returns 0 on a match
 */
static gint
cache_compare_func (gconstpointer listelem, 
                    gconstpointer target)
{
Benjamin Otte's avatar
Benjamin Otte committed
110
  GdkX11Cursor* cursor = (GdkX11Cursor*)listelem;
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
  struct cursor_cache_key* key = (struct cursor_cache_key*)target;

  if ((cursor->cursor.type != key->type) ||
      (cursor->display != key->display))
    return 1; /* No match */
  
  /* Elements marked as pixmap must be named cursors 
   * (since we don't store normal pixmap cursors 
   */
  if (key->type == GDK_CURSOR_IS_PIXMAP)
    return strcmp (key->name, cursor->name);

  return 0; /* Match */
}

/* Returns the cursor if there is a match, NULL if not
 * For named cursors type shall be GDK_CURSOR_IS_PIXMAP
 * For unnamed, typed cursors, name shall be NULL
 */
Benjamin Otte's avatar
Benjamin Otte committed
130
static GdkX11Cursor*
131 132 133 134 135 136 137 138 139 140 141 142 143 144
find_in_cache (GdkDisplay    *display, 
               GdkCursorType  type,
               const char    *name)
{
  GSList* res;
  struct cursor_cache_key key;

  key.display = display;
  key.type = type;
  key.name = name;

  res = g_slist_find_custom (cursor_cache, &key, cache_compare_func);

  if (res)
Benjamin Otte's avatar
Benjamin Otte committed
145
    return (GdkX11Cursor *) res->data;
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161

  return NULL;
}

/* Called by gdk_display_x11_finalize to flush any cached cursors
 * for a dead display.
 */
void 
_gdk_x11_cursor_display_finalize (GdkDisplay *display)
{
  GSList* item;
  GSList** itemp; /* Pointer to the thing to fix when we delete an item */
  item = cursor_cache;
  itemp = &cursor_cache;
  while (item)
    {
Benjamin Otte's avatar
Benjamin Otte committed
162
      GdkX11Cursor* cursor = (GdkX11Cursor*)(item->data);
163 164
      if (cursor->display == display)
        {
165
          GSList* olditem;
166
          gdk_cursor_unref ((GdkCursor*) cursor);
167 168 169 170 171
          /* Remove this item from the list */
          *(itemp) = item->next;
          olditem = item;
          item = g_slist_next (item);
          g_slist_free_1 (olditem);
172 173 174
        } 
      else 
        {
175 176 177
          itemp = &(item->next);
          item = g_slist_next (item);
        }
178 179 180
    }
}

Benjamin Otte's avatar
Benjamin Otte committed
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
/*** GdkX11Cursor ***/

G_DEFINE_TYPE (GdkX11Cursor, gdk_x11_cursor, GDK_TYPE_CURSOR)

void
gdk_x11_cursor_finalize (GObject *object)
{
  GdkX11Cursor *private = GDK_X11_CURSOR (object);

  if (private->xcursor && !gdk_display_is_closed (private->display))
    XFreeCursor (GDK_DISPLAY_XDISPLAY (private->display), private->xcursor);

  g_free (private->name);

  G_OBJECT_CLASS (gdk_x11_cursor_parent_class)->finalize (object);
}

static void
gdk_x11_cursor_class_init (GdkX11CursorClass *cursor_class)
{
  GObjectClass *object_class = G_OBJECT_CLASS (cursor_class);

  object_class->finalize = gdk_x11_cursor_finalize;
}

static void
gdk_x11_cursor_init (GdkX11Cursor *cursor)
{
}

211 212 213 214
static Cursor
get_blank_cursor (GdkDisplay *display)
{
  GdkScreen *screen;
215
  Pixmap pixmap;
216 217
  XColor color;
  Cursor cursor;
218
  cairo_surface_t *surface;
219
  cairo_t *cr;
220 221

  screen = gdk_display_get_default_screen (display);
222 223 224
  surface = _gdk_x11_window_create_bitmap_surface (gdk_screen_get_root_window (screen), 1, 1);
  /* Clear surface */
  cr = cairo_create (surface);
225 226 227
  cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
  cairo_paint (cr);
  cairo_destroy (cr);
228
 
229
  pixmap = cairo_xlib_surface_get_drawable (surface);
230 231 232

  color.pixel = 0; 
  color.red = color.blue = color.green = 0;
233 234

  if (gdk_display_is_closed (display))
235 236 237
    cursor = None;
  else
    cursor = XCreatePixmapCursor (GDK_DISPLAY_XDISPLAY (display),
238
                                  pixmap, pixmap,
239
                                  &color, &color, 1, 1);
240
  cairo_surface_destroy (surface);
241 242 243 244

  return cursor;
}

Elliot Lee's avatar
Elliot Lee committed
245
GdkCursor*
246 247
_gdk_x11_display_get_cursor_for_type (GdkDisplay    *display,
                                      GdkCursorType  cursor_type)
Elliot Lee's avatar
Elliot Lee committed
248
{
Benjamin Otte's avatar
Benjamin Otte committed
249
  GdkX11Cursor *private;
Elliot Lee's avatar
Elliot Lee committed
250 251 252
  GdkCursor *cursor;
  Cursor xcursor;

253
  if (gdk_display_is_closed (display))
254 255
    {
      xcursor = None;
256 257
    }
  else
258 259 260 261 262 263
    {
      private = find_in_cache (display, cursor_type, NULL);

      if (private)
        {
          /* 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 272
            xcursor = XCreateFontCursor (GDK_DISPLAY_XDISPLAY (display),
                                         cursor_type);
273 274
          else
            xcursor = get_blank_cursor (display);
275 276
       }
    }
277

Benjamin Otte's avatar
Benjamin Otte committed
278
  private = g_object_new (GDK_TYPE_X11_CURSOR, NULL);
279
  private->display = display;
Elliot Lee's avatar
Elliot Lee committed
280
  private->xcursor = xcursor;
281
  private->name = NULL;
282 283
  private->serial = theme_serial;

284
  cursor = (GdkCursor *) private;
Elliot Lee's avatar
Elliot Lee committed
285
  cursor->type = cursor_type;
286
  
287 288 289
  if (xcursor != None)
    add_to_cache (private);

Elliot Lee's avatar
Elliot Lee committed
290 291 292
  return cursor;
}

Matthias Clasen's avatar
Matthias Clasen committed
293 294 295 296 297 298 299 300
/**
 * gdk_x11_cursor_get_xdisplay:
 * @cursor: a #GdkCursor.
 * 
 * Returns the display of a #GdkCursor.
 * 
 * Return value: an Xlib <type>Display*</type>.
 **/
301 302 303
Display *
gdk_x11_cursor_get_xdisplay (GdkCursor *cursor)
{
304
  g_return_val_if_fail (cursor != NULL, NULL);
305

Benjamin Otte's avatar
Benjamin Otte committed
306
  return GDK_DISPLAY_XDISPLAY(((GdkX11Cursor *)cursor)->display);
307 308
}

Matthias Clasen's avatar
Matthias Clasen committed
309 310 311 312 313 314 315 316
/**
 * gdk_x11_cursor_get_xcursor:
 * @cursor: a #GdkCursor.
 * 
 * Returns the X cursor belonging to a #GdkCursor.
 * 
 * Return value: an Xlib <type>Cursor</type>.
 **/
317 318 319
Cursor
gdk_x11_cursor_get_xcursor (GdkCursor *cursor)
{
320
  g_return_val_if_fail (cursor != NULL, None);
321

Benjamin Otte's avatar
Benjamin Otte committed
322
  return ((GdkX11Cursor *)cursor)->xcursor;
323
}
324 325

/** 
326
 * gdk_cursor_get_display:
Matthias Clasen's avatar
Matthias Clasen committed
327 328 329
 * @cursor: a #GdkCursor.
 *
 * Returns the display on which the #GdkCursor is defined.
330
 *
331
 * Returns: (transfer none): the #GdkDisplay associated to @cursor
332
 *
Matthias Clasen's avatar
Matthias Clasen committed
333
 * Since: 2.2
334 335
 */

336 337
GdkDisplay *
gdk_cursor_get_display (GdkCursor *cursor)
338
{
339
  g_return_val_if_fail (cursor != NULL, NULL);
340

Benjamin Otte's avatar
Benjamin Otte committed
341
  return ((GdkX11Cursor *)cursor)->display;
342
}
Matthias Clasen's avatar
Matthias Clasen committed
343

344 345 346 347 348 349 350 351 352 353 354 355
#if defined(HAVE_XCURSOR) && defined(HAVE_XFIXES) && XFIXES_MAJOR >= 2

/**
 * gdk_cursor_get_image:
 * @cursor: a #GdkCursor
 *
 * Returns a #GdkPixbuf with the image used to display the cursor.
 *
 * Note that depending on the capabilities of the windowing system and 
 * on the cursor, GDK may not be able to obtain the image data. In this 
 * case, %NULL is returned.
 *
356
 * Returns: (transfer full): a #GdkPixbuf representing @cursor, or %NULL
357 358 359 360 361 362 363
 *
 * Since: 2.8
 */
GdkPixbuf*  
gdk_cursor_get_image (GdkCursor *cursor)
{
  Display *xdisplay;
Benjamin Otte's avatar
Benjamin Otte committed
364
  GdkX11Cursor *private;
365 366 367 368
  XcursorImages *images = NULL;
  XcursorImage *image;
  gint size;
  gchar buf[32];
Matthias Clasen's avatar
Matthias Clasen committed
369
  guchar *data, *p, tmp;
370 371
  GdkPixbuf *pixbuf;
  gchar *theme;
372
  
373
  g_return_val_if_fail (cursor != NULL, NULL);
374

Benjamin Otte's avatar
Benjamin Otte committed
375
  private = (GdkX11Cursor *) cursor;
376 377 378 379 380 381 382 383 384
    
  xdisplay = GDK_DISPLAY_XDISPLAY (private->display);

  size = XcursorGetDefaultSize (xdisplay);
  theme = XcursorGetTheme (xdisplay);

  if (cursor->type == GDK_CURSOR_IS_PIXMAP)
    {
      if (private->name)
385
        images = XcursorLibraryLoadImages (private->name, theme, size);
386
    }
387
  else
388 389 390 391
    images = XcursorShapeLoadImages (cursor->type, theme, size);

  if (!images)
    return NULL;
392

393 394 395 396 397
  image = images->images[0];

  data = g_malloc (4 * image->width * image->height);
  memcpy (data, image->pixels, 4 * image->width * image->height);

Matthias Clasen's avatar
Matthias Clasen committed
398 399 400 401 402 403 404
  for (p = data; p < data + (4 * image->width * image->height); p += 4)
    {
      tmp = p[0];
      p[0] = p[2];
      p[2] = tmp;
    }

405
  pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, TRUE,
406 407 408
                                     8, image->width, image->height,
                                     4 * image->width,
                                     (GdkPixbufDestroyNotify)g_free, NULL);
409 410 411 412 413 414 415

  if (private->name)
    gdk_pixbuf_set_option (pixbuf, "name", private->name);
  g_snprintf (buf, 32, "%d", image->xhot);
  gdk_pixbuf_set_option (pixbuf, "x_hot", buf);
  g_snprintf (buf, 32, "%d", image->yhot);
  gdk_pixbuf_set_option (pixbuf, "y_hot", buf);
416

417 418 419 420 421
  XcursorImagesDestroy (images);

  return pixbuf;
}

422 423
void
_gdk_x11_cursor_update_theme (GdkCursor *cursor)
424 425
{
  Display *xdisplay;
Benjamin Otte's avatar
Benjamin Otte committed
426
  GdkX11Cursor *private;
427
  Cursor new_cursor = None;
428
  GdkDisplayX11 *display_x11;
429

Benjamin Otte's avatar
Benjamin Otte committed
430
  private = (GdkX11Cursor *) cursor;
431
  xdisplay = GDK_DISPLAY_XDISPLAY (private->display);
432 433 434 435 436
  display_x11 = GDK_DISPLAY_X11 (private->display);

  if (!display_x11->have_xfixes)
    return;

437
  if (private->serial == theme_serial)
438 439
    return;

440
  private->serial = theme_serial;
441 442

  if (private->xcursor != None)
443
    {
Matthias Clasen's avatar
Matthias Clasen committed
444 445 446
      if (cursor->type == GDK_BLANK_CURSOR)
        return;

447
      if (cursor->type == GDK_CURSOR_IS_PIXMAP)
448 449 450 451
        {
          if (private->name)
            new_cursor = XcursorLibraryLoadCursor (xdisplay, private->name);
        }
452
      else 
453
        new_cursor = XcursorShapeLoadCursor (xdisplay, cursor->type);
454 455
      
      if (new_cursor != None)
456 457 458 459
        {
          XFixesChangeCursor (xdisplay, new_cursor, private->xcursor);
          private->xcursor = new_cursor;
        }
460
    }
461
}
462

463
static void
464
update_cursor (gpointer data,
465
               gpointer user_data)
466 467 468
{
  GdkCursor *cursor;

469
  cursor = (GdkCursor*)(data);
470 471 472 473 474 475 476

  if (!cursor)
    return;
  
  _gdk_x11_cursor_update_theme (cursor);
}

477 478 479
/**
 * gdk_x11_display_set_cursor_theme:
 * @display: a #GdkDisplay
Owen Taylor's avatar
Owen Taylor committed
480
 * @theme: the name of the cursor theme to use, or %NULL to unset
481 482
 *         a previously set value 
 * @size: the cursor size to use, or 0 to keep the previous size
483 484 485 486 487 488 489
 *
 * Sets the cursor theme from which the images for cursor
 * should be taken. 
 * 
 * If the windowing system supports it, existing cursors created 
 * with gdk_cursor_new(), gdk_cursor_new_for_display() and 
 * gdk_cursor_new_for_name() are updated to reflect the theme 
490 491
 * change. Custom cursors constructed with
 * gdk_cursor_new_from_pixbuf() will have to be handled
492 493 494 495 496 497 498 499
 * by the application (GTK+ applications can learn about 
 * cursor theme changes by listening for change notification
 * for the corresponding #GtkSetting).
 *
 * Since: 2.8
 */
void
gdk_x11_display_set_cursor_theme (GdkDisplay  *display,
500 501
                                  const gchar *theme,
                                  const gint   size)
502 503 504 505 506
{
  GdkDisplayX11 *display_x11;
  Display *xdisplay;
  gchar *old_theme;
  gint old_size;
507

508 509 510 511 512 513 514 515
  g_return_if_fail (GDK_IS_DISPLAY (display));

  display_x11 = GDK_DISPLAY_X11 (display);
  xdisplay = GDK_DISPLAY_XDISPLAY (display);

  old_theme = XcursorGetTheme (xdisplay);
  old_size = XcursorGetDefaultSize (xdisplay);

Owen Taylor's avatar
Owen Taylor committed
516 517 518
  if (old_size == size &&
      (old_theme == theme ||
       (old_theme && theme && strcmp (old_theme, theme) == 0)))
519 520
    return;

521 522
  theme_serial++;

523
  XcursorSetTheme (xdisplay, theme);
524 525
  if (size > 0)
    XcursorSetDefaultSize (xdisplay, size);
526
    
527
  g_slist_foreach (cursor_cache, update_cursor, NULL);
528 529 530 531 532 533 534
}

#else

GdkPixbuf*  
gdk_cursor_get_image (GdkCursor *cursor)
{
535
  g_return_val_if_fail (cursor != NULL, NULL);
536 537 538 539
  
  return NULL;
}

540 541
void
gdk_x11_display_set_cursor_theme (GdkDisplay  *display,
542 543
                                  const gchar *theme,
                                  const gint   size)
544 545
{
  g_return_if_fail (GDK_IS_DISPLAY (display));
546
}
547

548 549 550
void
_gdk_x11_cursor_update_theme (GdkCursor *cursor)
{
551
  g_return_if_fail (cursor != NULL);
552 553
}

554
#endif
Matthias Clasen's avatar
Matthias Clasen committed
555 556 557 558

#ifdef HAVE_XCURSOR

static XcursorImage*
Michael Natterer's avatar
Michael Natterer committed
559
create_cursor_image (GdkPixbuf *pixbuf,
560 561
                     gint       x,
                     gint       y)
Matthias Clasen's avatar
Matthias Clasen committed
562
{
563
  guint width, height;
Matthias Clasen's avatar
Matthias Clasen committed
564
  XcursorImage *xcimage;
565 566
  cairo_surface_t *surface;
  cairo_t *cr;
Matthias Clasen's avatar
Matthias Clasen committed
567 568 569 570 571

  width = gdk_pixbuf_get_width (pixbuf);
  height = gdk_pixbuf_get_height (pixbuf);

  xcimage = XcursorImageCreate (width, height);
Michael Natterer's avatar
Michael Natterer committed
572

Matthias Clasen's avatar
Matthias Clasen committed
573 574 575
  xcimage->xhot = x;
  xcimage->yhot = y;

576 577 578 579 580
  surface = cairo_image_surface_create_for_data ((guchar *) xcimage->pixels,
                                                 CAIRO_FORMAT_ARGB32,
                                                 width,
                                                 height,
                                                 width * 4);
Matthias Clasen's avatar
Matthias Clasen committed
581

582 583 584 585 586
  cr = cairo_create (surface);
  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
  gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
  cairo_paint (cr);
  cairo_destroy (cr);
Michael Natterer's avatar
Michael Natterer committed
587

588
  cairo_surface_destroy (surface);
Matthias Clasen's avatar
Matthias Clasen committed
589 590 591 592 593

  return xcimage;
}

GdkCursor *
594 595 596 597
_gdk_x11_display_get_cursor_for_pixbuf (GdkDisplay *display,
                                        GdkPixbuf  *pixbuf,
                                        gint        x,
                                        gint        y)
Matthias Clasen's avatar
Matthias Clasen committed
598 599 600
{
  XcursorImage *xcimage;
  Cursor xcursor;
Benjamin Otte's avatar
Benjamin Otte committed
601
  GdkX11Cursor *private;
Matthias Clasen's avatar
Matthias Clasen committed
602
  GdkCursor *cursor;
603 604 605
  const char *option;
  char *end;
  gint64 value;
Matthias Clasen's avatar
Matthias Clasen committed
606

607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
  if (x == -1 && (option = gdk_pixbuf_get_option (pixbuf, "x_hot")))
    {
      errno = 0;
      end = NULL;
      value = g_ascii_strtoll (option, &end, 10);
      if (errno == 0 &&
          end != option &&
          value >= 0 && value < G_MAXINT)
        x = (gint) value;
    }
  if (y == -1 && (option = gdk_pixbuf_get_option (pixbuf, "y_hot")))
    {
      errno = 0;
      end = NULL;
      value = g_ascii_strtoll (option, &end, 10);
      if (errno == 0 &&
          end != option &&
          value >= 0 && value < G_MAXINT)
        y = (gint) value;
    }

Matthias Clasen's avatar
Matthias Clasen committed
628 629 630
  g_return_val_if_fail (0 <= x && x < gdk_pixbuf_get_width (pixbuf), NULL);
  g_return_val_if_fail (0 <= y && y < gdk_pixbuf_get_height (pixbuf), NULL);

631
  if (gdk_display_is_closed (display))
632 633 634 635
    {
      xcursor = None;
    }
  else
Matthias Clasen's avatar
Matthias Clasen committed
636 637 638 639 640 641
    {
      xcimage = create_cursor_image (pixbuf, x, y);
      xcursor = XcursorImageLoadCursor (GDK_DISPLAY_XDISPLAY (display), xcimage);
      XcursorImageDestroy (xcimage);
    }

Benjamin Otte's avatar
Benjamin Otte committed
642
  private = g_object_new (GDK_TYPE_X11_CURSOR, NULL);
Matthias Clasen's avatar
Matthias Clasen committed
643 644
  private->display = display;
  private->xcursor = xcursor;
645
  private->name = NULL;
646 647
  private->serial = theme_serial;

648 649
  cursor = (GdkCursor *) private;
  cursor->type = GDK_CURSOR_IS_PIXMAP;
650

651 652 653
  return cursor;
}

654 655 656
GdkCursor*
_gdk_x11_display_get_cursor_for_name (GdkDisplay  *display,
                                      const gchar *name)
657 658 659
{
  Cursor xcursor;
  Display *xdisplay;
Benjamin Otte's avatar
Benjamin Otte committed
660
  GdkX11Cursor *private;
661 662
  GdkCursor *cursor;

663
  if (gdk_display_is_closed (display))
664 665 666 667
    {
      xcursor = None;
    }
  else
668
    {
669 670 671 672 673
      private = find_in_cache (display, GDK_CURSOR_IS_PIXMAP, name);

      if (private)
        {
          /* Cache had it, add a ref for this user */
674
          g_object_ref (private);
675 676 677 678

          return (GdkCursor*) private;
        }

679 680 681
      xdisplay = GDK_DISPLAY_XDISPLAY (display);
      xcursor = XcursorLibraryLoadCursor (xdisplay, name);
      if (xcursor == None)
682
        return NULL;
683 684
    }

Benjamin Otte's avatar
Benjamin Otte committed
685
  private = g_object_new (GDK_TYPE_X11_CURSOR, NULL);
686 687 688
  private->display = display;
  private->xcursor = xcursor;
  private->name = g_strdup (name);
689 690
  private->serial = theme_serial;

Matthias Clasen's avatar
Matthias Clasen committed
691 692
  cursor = (GdkCursor *) private;
  cursor->type = GDK_CURSOR_IS_PIXMAP;
693 694
  add_to_cache (private);

Matthias Clasen's avatar
Matthias Clasen committed
695 696 697
  return cursor;
}

698 699
gboolean
_gdk_x11_display_supports_cursor_alpha (GdkDisplay *display)
Matthias Clasen's avatar
Matthias Clasen committed
700 701 702 703
{
  return XcursorSupportsARGB (GDK_DISPLAY_XDISPLAY (display));
}

704 705
gboolean
_gdk_x11_display_supports_cursor_color (GdkDisplay *display)
Matthias Clasen's avatar
Matthias Clasen committed
706 707 708 709
{
  return XcursorSupportsARGB (GDK_DISPLAY_XDISPLAY (display));
}

710 711 712 713
void
_gdk_x11_display_get_default_cursor_size (GdkDisplay *display,
                                          guint      *width,
                                          guint      *height)
Matthias Clasen's avatar
Matthias Clasen committed
714
{
715
  *width = *height = XcursorGetDefaultSize (GDK_DISPLAY_XDISPLAY (display));
Matthias Clasen's avatar
Matthias Clasen committed
716 717 718 719
}

#else

720
static GdkCursor*
721 722
gdk_cursor_new_from_pixmap (GdkDisplay     *display,
                            Pixmap          source_pixmap,
723 724 725 726 727
                            Pixmap          mask_pixmap,
                            const GdkColor *fg,
                            const GdkColor *bg,
                            gint            x,
                            gint            y)
728
{
Benjamin Otte's avatar
Benjamin Otte committed
729
  GdkX11Cursor *private;
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
  GdkCursor *cursor;
  Cursor xcursor;
  XColor xfg, xbg;

  g_return_val_if_fail (fg != NULL, NULL);
  g_return_val_if_fail (bg != NULL, NULL);

  xfg.pixel = fg->pixel;
  xfg.red = fg->red;
  xfg.blue = fg->blue;
  xfg.green = fg->green;
  xbg.pixel = bg->pixel;
  xbg.red = bg->red;
  xbg.blue = bg->blue;
  xbg.green = bg->green;
  
746
  if (gdk_display_is_closed (display->closed))
747 748 749
    xcursor = None;
  else
    xcursor = XCreatePixmapCursor (GDK_DISPLAY_XDISPLAY (display),
750
                                   source_pixmap, mask_pixmap, &xfg, &xbg, x, y);
Benjamin Otte's avatar
Benjamin Otte committed
751
  private = g_object_new (GDK_TYPE_X11_CURSOR, NULL);
752 753 754 755 756 757 758
  private->display = display;
  private->xcursor = xcursor;
  private->name = NULL;
  private->serial = theme_serial;

  cursor = (GdkCursor *) private;
  cursor->type = GDK_CURSOR_IS_PIXMAP;
759

760 761 762
  return cursor;
}

Matthias Clasen's avatar
Matthias Clasen committed
763
GdkCursor *
764 765 766 767
_gdk_x11_display_get_cursor_for_pixbuf (GdkDisplay *display,
                                        GdkPixbuf  *pixbuf,
                                        gint        x,
                                        gint        y)
Matthias Clasen's avatar
Matthias Clasen committed
768 769
{
  GdkCursor *cursor;
770
  cairo_surface_t *pixmap, *mask;
771
  guint width, height, n_channels, rowstride, data_stride, i, j;
Matthias Clasen's avatar
Matthias Clasen committed
772 773 774 775
  guint8 *data, *mask_data, *pixels;
  GdkColor fg = { 0, 0, 0, 0 };
  GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
  GdkScreen *screen;
776 777
  cairo_surface_t *image;
  cairo_t *cr;
Matthias Clasen's avatar
Matthias Clasen committed
778 779 780 781 782 783 784 785 786 787 788

  width = gdk_pixbuf_get_width (pixbuf);
  height = gdk_pixbuf_get_height (pixbuf);

  g_return_val_if_fail (0 <= x && x < width, NULL);
  g_return_val_if_fail (0 <= y && y < height, NULL);

  n_channels = gdk_pixbuf_get_n_channels (pixbuf);
  rowstride = gdk_pixbuf_get_rowstride (pixbuf);
  pixels = gdk_pixbuf_get_pixels (pixbuf);

789 790 791
  data_stride = 4 * ((width + 31) / 32);
  data = g_new0 (guint8, data_stride * height);
  mask_data = g_new0 (guint8, data_stride * height);
Matthias Clasen's avatar
Matthias Clasen committed
792 793 794 795

  for (j = 0; j < height; j++)
    {
      guint8 *src = pixels + j * rowstride;
796 797
      guint8 *d = data + data_stride * j;
      guint8 *md = mask_data + data_stride * j;
798

Matthias Clasen's avatar
Matthias Clasen committed
799
      for (i = 0; i < width; i++)
800 801 802 803 804 805 806 807 808 809 810 811 812 813
        {
          if (src[1] < 0x80)
            *d |= 1 << (i % 8);

          if (n_channels == 3 || src[3] >= 0x80)
            *md |= 1 << (i % 8);

          src += n_channels;
          if (i % 8 == 7)
            {
              d++;
              md++;
            }
        }
Matthias Clasen's avatar
Matthias Clasen committed
814
    }
815

Matthias Clasen's avatar
Matthias Clasen committed
816
  screen = gdk_display_get_default_screen (display);
817

818 819
  pixmap = _gdk_x11_window_create_bitmap_surface (gdk_screen_get_root_window (screen),
                                                  width, height);
820
  cr = cairo_create (pixmap);
821 822 823 824 825 826 827
  image = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_A1,
                                               width, height, data_stride);
  cairo_set_source_surface (cr, image, 0, 0);
  cairo_surface_destroy (image);
  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
  cairo_paint (cr);
  cairo_destroy (cr);
828 829 830

  mask = _gdk_x11_window_create_bitmap_surface (gdk_screen_get_root_window (screen),
                                                width, height);
831
  cr = cairo_create (mask);
832 833 834 835 836 837 838
  image = cairo_image_surface_create_for_data (mask_data, CAIRO_FORMAT_A1,
                                               width, height, data_stride);
  cairo_set_source_surface (cr, image, 0, 0);
  cairo_surface_destroy (image);
  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
  cairo_paint (cr);
  cairo_destroy (cr);
839

840 841 842 843 844
  cursor = gdk_cursor_new_from_pixmap (display,
                                       cairo_xlib_surface_get_drawable (pixmap),
                                       cairo_xlib_surface_get_drawable (mask),
                                       &fg, &bg,
                                       x, y);
845

846 847
  cairo_surface_destroy (pixmap);
  cairo_surface_destroy (mask);
Matthias Clasen's avatar
Matthias Clasen committed
848 849 850

  g_free (data);
  g_free (mask_data);
851

Matthias Clasen's avatar
Matthias Clasen committed
852 853 854
  return cursor;
}

855 856 857
GdkCursor*
_gdk_x11_display_get_cursor_for_name (GdkDisplay  *display,
                                      const gchar *name)
858 859 860 861
{
  return NULL;
}

862 863
gboolean
_gdk_x11_display_supports_cursor_alpha (GdkDisplay *display)
Matthias Clasen's avatar
Matthias Clasen committed
864 865 866 867
{
  return FALSE;
}

868 869
gboolean
_gdk_x11_display_supports_cursor_color (GdkDisplay *display)
Matthias Clasen's avatar
Matthias Clasen committed
870 871 872 873
{
  return FALSE;
}

874 875
void
_gdk_x11_display_get_default_cursor_size (GdkDisplay *display)
Matthias Clasen's avatar
Matthias Clasen committed
876 877
{
  /* no idea, really */
878
  return 20;
Matthias Clasen's avatar
Matthias Clasen committed
879 880 881 882 883 884 885 886
}

#endif


/**
 * gdk_display_get_maximal_cursor_size:
 * @display: a #GdkDisplay
887 888
 * @width: (out): the return location for the maximal cursor width
 * @height: (out): the return location for the maximal cursor height
Matthias Clasen's avatar
Matthias Clasen committed
889
 *
Matthias Clasen's avatar
Matthias Clasen committed
890
 * Gets the maximal size to use for cursors on @display.
Matthias Clasen's avatar
Matthias Clasen committed
891 892 893
 *
 * Since: 2.4
 */
894 895 896 897
void
_gdk_x11_display_get_maximal_cursor_size (GdkDisplay *display,
                                          guint       *width,
                                          guint       *height)
Matthias Clasen's avatar
Matthias Clasen committed
898 899 900 901
{
  GdkScreen *screen;
  GdkWindow *window;

902
  g_return_if_fail (GDK_IS_DISPLAY (display));
903

Matthias Clasen's avatar
Matthias Clasen committed
904 905
  screen = gdk_display_get_default_screen (display);
  window = gdk_screen_get_root_window (screen);
906 907 908
  XQueryBestCursor (GDK_DISPLAY_XDISPLAY (display),
                    GDK_WINDOW_XID (window),
                    128, 128, width, height);
Matthias Clasen's avatar
Matthias Clasen committed
909
}