[gtk+] x11: Redo cursor handling completely



commit e294f7155583469ad25f2da0a3ffa1ca4ab099df
Author: Benjamin Otte <otte redhat com>
Date:   Fri Nov 3 06:01:56 2017 +0100

    x11: Redo cursor handling completely
    
    Instead of creating a GdkX11Cursor, create GdkCursors. Cache the XCursor
    in a hash table instead.
    
    Also, make use of the new fallback mechanism for fallback code: Make
    sure to provide cursors for the names that are guaranteed to exist, but
    do not do bad attempts at displaying texture surfaces.
    Black/White/transparent is not a replacement for those.

 docs/reference/gdk/gdk4-sections.txt |   12 +-
 gdk/x11/gdkcursor-x11.c              |  753 +++++++++++-----------------------
 gdk/x11/gdkdevice-core-x11.c         |    8 +-
 gdk/x11/gdkdevice-xi2.c              |    5 +-
 gdk/x11/gdkdisplay-x11.h             |    3 +
 gdk/x11/gdkprivate-x11.h             |    1 -
 gdk/x11/gdkx-autocleanups.h          |    1 -
 gdk/x11/gdkx.h                       |    1 -
 gdk/x11/gdkx11cursor.h               |   83 ----
 gdk/x11/gdkx11display.h              |    3 +
 gdk/x11/meson.build                  |    1 -
 testsuite/gtk/objects-finalize.c     |    1 -
 12 files changed, 249 insertions(+), 623 deletions(-)
---
diff --git a/docs/reference/gdk/gdk4-sections.txt b/docs/reference/gdk/gdk4-sections.txt
index 97bae3d..ebd74cd 100644
--- a/docs/reference/gdk/gdk4-sections.txt
+++ b/docs/reference/gdk/gdk4-sections.txt
@@ -835,7 +835,6 @@ gdk_cursor_get_hotspot_y
 gdk_cursor_get_texture
 
 <SUBSECTION Standard>
-GDK_TYPE_CURSOR_TYPE
 GDK_TYPE_CURSOR
 
 <SUBSECTION Private>
@@ -899,8 +898,6 @@ gdk_drag_context_get_type
 <FILE>x_interaction</FILE>
 GDK_WINDOW_XID
 GDK_DISPLAY_XDISPLAY
-GDK_CURSOR_XCURSOR
-GDK_CURSOR_XDISPLAY
 GDK_POINTER_TO_XID
 GDK_XID_TO_POINTER
 gdk_x11_lookup_xdisplay
@@ -915,6 +912,7 @@ gdk_x11_display_set_startup_notification_id
 gdk_x11_display_get_xdisplay
 gdk_x11_display_get_xscreen
 gdk_x11_display_get_xrootwindow
+gdk_x11_display_get_xcursor
 gdk_x11_display_grab
 gdk_x11_display_ungrab
 gdk_x11_display_error_trap_push
@@ -944,8 +942,6 @@ gdk_x11_window_get_desktop
 gdk_x11_window_set_utf8_property
 gdk_x11_window_set_frame_extents
 gdk_x11_window_set_frame_sync_enabled
-gdk_x11_cursor_get_xcursor
-gdk_x11_cursor_get_xdisplay
 gdk_x11_keymap_get_group_for_state
 gdk_x11_keymap_key_is_modifier
 gdk_x11_visual_get_xvisual
@@ -971,12 +967,6 @@ GDK_X11_APP_LAUNCH_CONTEXT_CLASS
 GDK_IS_X11_APP_LAUNCH_CONTEXT
 GDK_IS_X11_APP_LAUNCH_CONTEXT_CLASS
 GDK_X11_APP_LAUNCH_CONTEXT_GET_CLASS
-GDK_TYPE_X11_CURSOR
-GDK_X11_CURSOR
-GDK_X11_CURSOR_CLASS
-GDK_IS_X11_CURSOR
-GDK_IS_X11_CURSOR_CLASS
-GDK_X11_CURSOR_GET_CLASS
 GDK_TYPE_X11_DEVICE_CORE
 GDK_X11_DEVICE_CORE
 GDK_X11_DEVICE_CORE_CLASS
diff --git a/gdk/x11/gdkcursor-x11.c b/gdk/x11/gdkcursor-x11.c
index ff0ebba..a8dae1c 100644
--- a/gdk/x11/gdkcursor-x11.c
+++ b/gdk/x11/gdkcursor-x11.c
@@ -42,150 +42,32 @@
 #include <X11/extensions/Xfixes.h>
 #endif
 #include <string.h>
-#include <errno.h>
-#include <math.h>
 
-struct _GdkX11Cursor
-{
-  GdkCursor cursor;
-
-  Cursor xcursor;
-  guint serial;
-};
-
-struct _GdkX11CursorClass
-{
-  GdkCursorClass cursor_class;
-};
-
-static guint theme_serial = 0;
-
-/* cursor_cache holds a cache of non-pixmap cursors to avoid expensive 
- * 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.
- */
-static GSList* cursor_cache = NULL;
-
-struct cursor_cache_key
-{
-  GdkDisplay* display;
-  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
-add_to_cache (GdkX11Cursor* cursor)
-{
-  cursor_cache = g_slist_prepend (cursor_cache, cursor);
-
-  /* Take a ref so that if the caller frees it we still have it */
-  g_object_ref (cursor);
-}
-
-/* Returns 0 on a match
- */
-static gint
-cache_compare_func (gconstpointer listelem, 
-                    gconstpointer target)
-{
-  GdkX11Cursor* cursor = (GdkX11Cursor*)listelem;
-  struct cursor_cache_key* key = (struct cursor_cache_key*)target;
-
-  if (gdk_cursor_get_display (GDK_CURSOR (cursor)) != key->display)
-    return 1; /* No match */
-  
-  /* Elements marked as pixmap must be named cursors 
-   * (since we don't store normal pixmap cursors 
-   */
-  return strcmp (key->name, gdk_cursor_get_name (GDK_CURSOR (cursor)));
-}
-
-/* Returns the cursor if there is a match, NULL if not
- */
-static GdkX11Cursor*
-find_in_cache (GdkDisplay    *display, 
-               const char    *name)
+gdk_x11_cursor_remove_from_cache (gpointer data, GObject *cursor)
 {
-  GSList* res;
-  struct cursor_cache_key key;
-
-  key.display = display;
-  key.name = name;
-
-  res = g_slist_find_custom (cursor_cache, &key, cache_compare_func);
-
-  if (res)
-    return (GdkX11Cursor *) res->data;
+  GdkDisplay *display = data;
+  Cursor xcursor;
 
-  return NULL;
+  xcursor = GDK_POINTER_TO_XID (g_hash_table_steal (GDK_X11_DISPLAY (display)->cursors, cursor));
+  XFreeCursor (GDK_DISPLAY_XDISPLAY (display), xcursor);
 }
 
-/* Called by gdk_x11_display_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)
+  GHashTableIter iter;
+  gpointer cursor;
+
+  if (GDK_X11_DISPLAY (display)->cursors)
     {
-      GdkX11Cursor* cursor = (GdkX11Cursor*)(item->data);
-      if (gdk_cursor_get_display (GDK_CURSOR (cursor)) == display)
-        {
-          GSList* olditem;
-          g_object_unref ((GdkCursor*) cursor);
-          /* Remove this item from the list */
-          *(itemp) = item->next;
-          olditem = item;
-          item = item->next;
-          g_slist_free_1 (olditem);
-        }
-      else
-        {
-          itemp = &(item->next);
-          item = item->next;
-        }
+      g_hash_table_iter_init (&iter, GDK_X11_DISPLAY (display)->cursors);
+      while (g_hash_table_iter_next (&iter, &cursor, NULL))
+        g_object_weak_unref (G_OBJECT (cursor), gdk_x11_cursor_remove_from_cache, display);
+      g_hash_table_unref (GDK_X11_DISPLAY (display)->cursors);
     }
 }
 
-/*** GdkX11Cursor ***/
-
-G_DEFINE_TYPE (GdkX11Cursor, gdk_x11_cursor, GDK_TYPE_CURSOR)
-
-static void
-gdk_x11_cursor_finalize (GObject *object)
-{
-  GdkX11Cursor *private = GDK_X11_CURSOR (object);
-  GdkDisplay *display;
-
-  display = gdk_cursor_get_display (GDK_CURSOR (object));
-  if (private->xcursor && !gdk_display_is_closed (display))
-    XFreeCursor (GDK_DISPLAY_XDISPLAY (display), private->xcursor);
-
-  G_OBJECT_CLASS (gdk_x11_cursor_parent_class)->finalize (object);
-}
-
-static void
-gdk_x11_cursor_class_init (GdkX11CursorClass *xcursor_class)
-{
-  GObjectClass *object_class = G_OBJECT_CLASS (xcursor_class);
-
-  object_class->finalize = gdk_x11_cursor_finalize;
-}
-
-static void
-gdk_x11_cursor_init (GdkX11Cursor *cursor)
-{
-}
-
 static Cursor
 get_blank_cursor (GdkDisplay *display)
 {
@@ -218,159 +100,73 @@ get_blank_cursor (GdkDisplay *display)
   return cursor;
 }
 
-/**
- * gdk_x11_cursor_get_xdisplay:
- * @cursor: (type GdkX11Cursor): a #GdkCursor.
- * 
- * Returns the display of a #GdkCursor.
- * 
- * Returns: (transfer none): an Xlib Display*.
- **/
-Display *
-gdk_x11_cursor_get_xdisplay (GdkCursor *cursor)
-{
-  g_return_val_if_fail (cursor != NULL, NULL);
-
-  return GDK_DISPLAY_XDISPLAY (gdk_cursor_get_display (cursor));
-}
-
-/**
- * gdk_x11_cursor_get_xcursor:
- * @cursor: (type GdkX11Cursor): a #GdkCursor.
- * 
- * Returns the X cursor belonging to a #GdkCursor.
- * 
- * Returns: an Xlib Cursor.
- **/
-Cursor
-gdk_x11_cursor_get_xcursor (GdkCursor *cursor)
-{
-  g_return_val_if_fail (cursor != NULL, None);
-
-  return ((GdkX11Cursor *)cursor)->xcursor;
-}
-
-#if defined(HAVE_XCURSOR) && defined(HAVE_XFIXES) && XFIXES_MAJOR >= 2
-
-void
-_gdk_x11_cursor_update_theme (GdkCursor *cursor)
-{
-  Display *xdisplay;
-  GdkX11Cursor *private;
-  Cursor new_cursor = None;
-  GdkX11Display *display_x11;
-
-  private = (GdkX11Cursor *) cursor;
-  display_x11 = GDK_X11_DISPLAY (gdk_cursor_get_display (cursor));
-  xdisplay = GDK_DISPLAY_XDISPLAY (display_x11);
-
-  if (!display_x11->have_xfixes)
-    return;
-
-  if (private->serial == theme_serial)
-    return;
-
-  private->serial = theme_serial;
-
-  if (private->xcursor != None)
-    {
-      const char *name;
-      
-      name = gdk_cursor_get_name (cursor);
-      if (name)
-        new_cursor = XcursorLibraryLoadCursor (xdisplay, name);
-      
-      if (new_cursor != None)
-        {
-          XFixesChangeCursor (xdisplay, new_cursor, private->xcursor);
-          private->xcursor = new_cursor;
-        }
-    }
-}
-
-static void
-update_cursor (gpointer data,
-               gpointer user_data)
-{
-  GdkCursor *cursor;
-
-  cursor = (GdkCursor*)(data);
-
-  if (!cursor)
-    return;
-
-  _gdk_x11_cursor_update_theme (cursor);
-}
-
-/**
- * gdk_x11_display_set_cursor_theme:
- * @display: (type GdkX11Display): a #GdkDisplay
- * @theme: the name of the cursor theme to use, or %NULL to unset
- *         a previously set value
- * @size: the cursor size to use, or 0 to keep the previous size
- *
- * 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_from_name() are updated to reflect the theme
- * change. Custom cursors constructed with
- * gdk_cursor_new_from_pixbuf() will have to be handled
- * 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,
-                                  const gchar *theme,
-                                  const gint   size)
-{
-  Display *xdisplay;
-  gchar *old_theme;
-  gint old_size;
-
-  g_return_if_fail (GDK_IS_DISPLAY (display));
-
-  xdisplay = GDK_DISPLAY_XDISPLAY (display);
-
-  old_theme = XcursorGetTheme (xdisplay);
-  old_size = XcursorGetDefaultSize (xdisplay);
-
-  if (old_size == size &&
-      (old_theme == theme ||
-       (old_theme && theme && strcmp (old_theme, theme) == 0)))
-    return;
-
-  theme_serial++;
-
-  XcursorSetTheme (xdisplay, theme);
-  if (size > 0)
-    XcursorSetDefaultSize (xdisplay, size);
-
-  g_slist_foreach (cursor_cache, update_cursor, NULL);
-}
-
-#else
+static const struct {
+  const char *css_name;
+  const char *traditional_name;
+  int cursor_glyph;
+} name_map[] = {
+  { "default",      "left_ptr",            XC_left_ptr, },
+  { "help",         "left_ptr",            XC_question_arrow },
+  { "context-menu", "left_ptr",            XC_left_ptr },
+  { "pointer",      "hand",                XC_hand1 },
+  { "progress",     "left_ptr_watch",      XC_watch },
+  { "wait",         "watch",               XC_watch },
+  { "cell",         "crosshair",           XC_plus },
+  { "crosshair",    "cross",               XC_crosshair },
+  { "text",         "xterm",               XC_xterm },
+  { "vertical-text","xterm",               XC_xterm },
+  { "alias",        "dnd-link",            XC_target },
+  { "copy",         "dnd-copy",            XC_target },
+  { "move",         "dnd-move",            XC_target },
+  { "no-drop",      "dnd-none",            XC_pirate },
+  { "dnd-ask",      "dnd-copy",            XC_target }, /* not CSS, but we want to guarantee it anyway */
+  { "not-allowed",  "crossed_circle",      XC_pirate },
+  { "grab",         "hand2",               XC_hand2 },
+  { "grabbing",     "hand2",               XC_hand2 },
+  { "all-scroll",   "left_ptr",            XC_left_ptr },
+  { "col-resize",   "h_double_arrow",      XC_sb_h_double_arrow },
+  { "row-resize",   "v_double_arrow",      XC_sb_v_double_arrow },
+  { "n-resize",     "top_side",            XC_top_side },
+  { "e-resize",     "right_side",          XC_right_side },
+  { "s-resize",     "bottom_side",         XC_bottom_side },
+  { "w-resize",     "left_side",           XC_left_side },
+  { "ne-resize",    "top_right_corner",    XC_top_right_corner },
+  { "nw-resize",    "top_left_corner",     XC_top_left_corner },
+  { "se-resize",    "bottom_right_corner", XC_bottom_right_corner },
+  { "sw-resize",    "bottom_left_corner",  XC_bottom_left_corner },
+  { "ew-resize",    "h_double_arrow",      XC_sb_h_double_arrow },
+  { "ns-resize",    "v_double_arrow",      XC_sb_v_double_arrow },
+  { "nesw-resize",  "fd_double_arrow",     XC_X_cursor },
+  { "nwse-resize",  "bd_double_arrow",     XC_X_cursor },
+  { "zoom-in",      "left_ptr",            XC_draped_box },
+  { "zoom-out",     "left_ptr",            XC_draped_box },
+  { NULL, NULL, XC_X_cursor }
+};
 
-void
-gdk_x11_display_set_cursor_theme (GdkDisplay  *display,
-                                  const gchar *theme,
-                                  const gint   size)
+GdkCursor*
+_gdk_x11_display_get_cursor_for_name (GdkDisplay  *display,
+                                      const gchar *name)
 {
-  g_return_if_fail (GDK_IS_DISPLAY (display));
+  return g_object_new (GDK_TYPE_CURSOR,
+                       "display", display,
+                       "name", name,
+                       NULL);
 }
 
-void
-_gdk_x11_cursor_update_theme (GdkCursor *cursor)
+GdkCursor *
+_gdk_x11_display_get_cursor_for_texture (GdkDisplay *display,
+                                         GdkTexture *texture,
+                                         int         x,
+                                         int         y)
 {
-  g_return_if_fail (cursor != NULL);
+  return g_object_new (GDK_TYPE_CURSOR, 
+                       "display", display,
+                       "texture", texture,
+                       "x", x,
+                       "y", y,
+                       NULL);
 }
 
-#endif
-
 #ifdef HAVE_XCURSOR
 
 static XcursorImage*
@@ -393,89 +189,31 @@ create_cursor_image (GdkTexture *texture,
   return xcimage;
 }
 
-GdkCursor *
-_gdk_x11_display_get_cursor_for_texture (GdkDisplay *display,
-                                         GdkTexture *texture,
-                                         int         x,
-                                         int         y)
+static Cursor
+gdk_x11_cursor_create_for_texture (GdkDisplay *display,
+                                   GdkTexture *texture,
+                                   int         x,
+                                   int         y)
 {
   XcursorImage *xcimage;
   Cursor xcursor;
-  GdkX11Cursor *private;
   int target_scale;
 
-  if (gdk_display_is_closed (display))
-    {
-      xcursor = None;
-    }
-  else
-    {
-      target_scale =
-        gdk_monitor_get_scale_factor (gdk_display_get_primary_monitor (display));
-      xcimage = create_cursor_image (texture, x, y, target_scale);
-      xcursor = XcursorImageLoadCursor (GDK_DISPLAY_XDISPLAY (display), xcimage);
-      XcursorImageDestroy (xcimage);
-    }
+  target_scale =
+    gdk_monitor_get_scale_factor (gdk_display_get_primary_monitor (display));
+  xcimage = create_cursor_image (texture, x, y, target_scale);
+  xcursor = XcursorImageLoadCursor (GDK_DISPLAY_XDISPLAY (display), xcimage);
+  XcursorImageDestroy (xcimage);
 
-  private = g_object_new (GDK_TYPE_X11_CURSOR, 
-                          "display", display,
-                          "texture", texture,
-                          "x", x,
-                          "y", y,
-                          NULL);
-  private->xcursor = xcursor;
-  private->serial = theme_serial;
-
-  return GDK_CURSOR (private);
+  return xcursor;
 }
 
-static const struct {
-  const gchar *css_name, *traditional_name;
-} name_map[] = {
-  { "default",      "left_ptr" },
-  { "help",         "left_ptr" },
-  { "context-menu", "left_ptr" },
-  { "pointer",      "hand" },
-  { "progress",     "left_ptr_watch" },
-  { "wait",         "watch" },
-  { "cell",         "crosshair" },
-  { "crosshair",    "cross" },
-  { "text",         "xterm" },
-  { "vertical-text","xterm" },
-  { "alias",        "dnd-link" },
-  { "copy",         "dnd-copy" },
-  { "move",         "dnd-move" },
-  { "no-drop",      "dnd-none" },
-  { "dnd-ask",      "dnd-copy" }, /* not CSS, but we want to guarantee it anyway */
-  { "not-allowed",  "crossed_circle" },
-  { "grab",         "hand2" },
-  { "grabbing",     "hand2" },
-  { "all-scroll",   "left_ptr" },
-  { "col-resize",   "h_double_arrow" },
-  { "row-resize",   "v_double_arrow" },
-  { "n-resize",     "top_side" },
-  { "e-resize",     "right_side" },
-  { "s-resize",     "bottom_side" },
-  { "w-resize",     "left_side" },
-  { "ne-resize",    "top_right_corner" },
-  { "nw-resize",    "top_left_corner" },
-  { "se-resize",    "bottom_right_corner" },
-  { "sw-resize",    "bottom_left_corner" },
-  { "ew-resize",    "h_double_arrow" },
-  { "ns-resize",    "v_double_arrow" },
-  { "nesw-resize",  "fd_double_arrow" },
-  { "nwse-resize",  "bd_double_arrow" },
-  { "zoom-in",      "left_ptr" },
-  { "zoom-out",     "left_ptr" },
-  { NULL, NULL }
-};
-
 static const gchar *
 name_fallback (const gchar *name)
 {
   gint i;
 
-  for (i = 0; name_map[i].css_name; i++)
+  for (i = 0; i < G_N_ELEMENTS (name_map); i++)
     {
       if (g_str_equal (name_map[i].css_name, name))
         return name_map[i].traditional_name;
@@ -484,34 +222,19 @@ name_fallback (const gchar *name)
   return NULL;
 }
 
-GdkCursor*
-_gdk_x11_display_get_cursor_for_name (GdkDisplay  *display,
-                                      const gchar *name)
+static Cursor
+gdk_x11_cursor_create_for_name (GdkDisplay *display,
+                                const char *name)
 {
   Cursor xcursor;
   Display *xdisplay;
-  GdkX11Cursor *private;
 
-  if (gdk_display_is_closed (display))
-    {
-      xcursor = None;
-    }
-  else if (strcmp (name, "none") == 0)
+  if (strcmp (name, "none") == 0)
     {
       xcursor = get_blank_cursor (display);
     }
   else
     {
-      private = find_in_cache (display, name);
-
-      if (private)
-        {
-          /* Cache had it, add a ref for this user */
-          g_object_ref (private);
-
-          return (GdkCursor*) private;
-        }
-
       xdisplay = GDK_DISPLAY_XDISPLAY (display);
       xcursor = XcursorLibraryLoadCursor (xdisplay, name);
       if (xcursor == None)
@@ -520,29 +243,11 @@ _gdk_x11_display_get_cursor_for_name (GdkDisplay  *display,
 
           fallback = name_fallback (name);
           if (fallback)
-            {
-              xcursor = XcursorLibraryLoadCursor (xdisplay, fallback);
-              if (xcursor == None)
-                xcursor = XcursorLibraryLoadCursor (xdisplay, "left_ptr");
-            }
-        }
-      if (xcursor == None)
-        {
-          g_message ("Unable to load %s from the cursor theme", name);
-          return NULL;
+            xcursor = XcursorLibraryLoadCursor (xdisplay, fallback);
         }
     }
 
-  private = g_object_new (GDK_TYPE_X11_CURSOR,
-                          "display", display,
-                          "name", name,
-                          NULL);
-  private->xcursor = xcursor;
-  private->serial = theme_serial;
-
-  add_to_cache (private);
-
-  return GDK_CURSOR (private);
+  return xcursor;
 }
 
 gboolean
@@ -567,146 +272,32 @@ _gdk_x11_display_get_default_cursor_size (GdkDisplay *display,
 
 #else
 
-static GdkCursor*
-gdk_cursor_new_from_pixmap (GdkDisplay     *display,
-                            Pixmap          source_pixmap,
-                            Pixmap          mask_pixmap,
-                            const GdkRGBA  *fg,
-                            const GdkRGBA  *bg,
-                            gint            x,
-                            gint            y)
+static Cursor
+gdk_x11_cursor_create_for_texture (GdkDisplay *display,
+                                   GdkTexture *texture,
+                                   int         x,
+                                   int         y)
 {
-  GdkX11Cursor *private;
-  Cursor xcursor;
-  XColor xfg, xbg;
-
-  g_return_val_if_fail (fg != NULL, NULL);
-  g_return_val_if_fail (bg != NULL, NULL);
-
-  xfg.red = fg->red * 65535;
-  xfg.blue = fg->blue * 65535;
-  xfg.green = fg->green * 65535;
-
-  xbg.red = bg->red * 65535;
-  xbg.blue = bg->blue * 65535;
-  xbg.green = bg->green * 65535;
-
-  if (gdk_display_is_closed (display))
-    xcursor = None;
-  else
-    xcursor = XCreatePixmapCursor (GDK_DISPLAY_XDISPLAY (display),
-                                   source_pixmap, mask_pixmap, &xfg, &xbg, x, y);
-  private = g_object_new (GDK_TYPE_X11_CURSOR,
-                          "display", display,
-                          NULL);
-  private->xcursor = xcursor;
-  private->serial = theme_serial;
-
-  return GDK_CURSOR (private);
+  return None;
 }
 
-GdkCursor *
-_gdk_x11_display_get_cursor_for_surface (GdkDisplay *display,
-                                        cairo_surface_t *surface,
-                                        gdouble     x,
-                                        gdouble     y)
+static Cursor
+gdk_x11_cursor_create_for_name (GdkDisplay  *display,
+                                const gchar *name)
 {
-  GdkCursor *cursor;
-  cairo_surface_t *pixmap, *mask;
-  guint width, height, n_channels, rowstride, data_stride, i, j;
-  guint8 *data, *mask_data, *pixels;
-  GdkRGBA fg = { 0, 0, 0, 1 };
-  GdkRGBA bg = { 1, 1, 1, 1 };
-  GdkScreen *screen;
-  cairo_surface_t *image;
-  cairo_t *cr;
-  GdkPixbuf *pixbuf;
-
-  width = cairo_image_surface_get_width (surface);
-  height = cairo_image_surface_get_height (surface);
-
-  g_return_val_if_fail (0 <= x && x < width, NULL);
-  g_return_val_if_fail (0 <= y && y < height, NULL);
-
-  /* Note: This does not support scaled surfaced, if you need that you
-     want XCursor anyway */
-  pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, width, height);
-  
-  n_channels = gdk_pixbuf_get_n_channels (pixbuf);
-  rowstride = gdk_pixbuf_get_rowstride (pixbuf);
-  pixels = gdk_pixbuf_get_pixels (pixbuf);
+  gint i;
 
-  data_stride = 4 * ((width + 31) / 32);
-  data = g_new0 (guint8, data_stride * height);
-  mask_data = g_new0 (guint8, data_stride * height);
+  if (g_str_equal (name, "none"))
+    return get_blank_cursor (display);
 
-  for (j = 0; j < height; j++)
+  for (i = 0; i < G_N_ELEMENTS (name_map); i++)
     {
-      guint8 *src = pixels + j * rowstride;
-      guint8 *d = data + data_stride * j;
-      guint8 *md = mask_data + data_stride * j;
-
-      for (i = 0; i < width; i++)
-        {
-          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++;
-            }
-        }
+      if (g_str_equal (name_map[i].css_name, name) ||
+          g_str_equal (name_map[i].traditional_name, name))
+        return XCreateFontCursor (GDK_DISPLAY_XDISPLAY (display), name_map[i].cursor_glyph);
     }
 
-  g_object_unref (pixbuf);
-
-  pixmap = _gdk_x11_window_create_bitmap_surface (gdk_display_get_root_window (display),
-                                                  width, height);
-  cr = cairo_create (pixmap);
-  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);
-
-  mask = _gdk_x11_window_create_bitmap_surface (gdk_display_get_root_window (display),
-                                                width, height);
-  cr = cairo_create (mask);
-  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);
-
-  cursor = gdk_cursor_new_from_pixmap (display,
-                                       cairo_xlib_surface_get_drawable (pixmap),
-                                       cairo_xlib_surface_get_drawable (mask),
-                                       &fg, &bg,
-                                       x, y);
-
-  cairo_surface_destroy (pixmap);
-  cairo_surface_destroy (mask);
-  
-  g_free (data);
-  g_free (mask_data);
-
-  return cursor;
-}
-
-GdkCursor*
-_gdk_x11_display_get_cursor_for_name (GdkDisplay  *display,
-                                      const gchar *name)
-{
-  return NULL;
+  return None;
 }
 
 gboolean
@@ -747,3 +338,131 @@ _gdk_x11_display_get_maximal_cursor_size (GdkDisplay *display,
                     GDK_WINDOW_XID (window),
                     128, 128, width, height);
 }
+
+/**
+ * gdk_x11_display_set_cursor_theme:
+ * @display: (type GdkX11Display): a #GdkDisplay
+ * @theme: the name of the cursor theme to use, or %NULL to unset
+ *         a previously set value
+ * @size: the cursor size to use, or 0 to keep the previous size
+ *
+ * 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_from_name() are updated to reflect the theme
+ * change. Custom cursors constructed with
+ * gdk_cursor_new_from_pixbuf() will have to be handled
+ * 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,
+                                  const gchar *theme,
+                                  const gint   size)
+{
+#if defined(HAVE_XCURSOR) && defined(HAVE_XFIXES) && XFIXES_MAJOR >= 2
+  Display *xdisplay;
+  gchar *old_theme;
+  gint old_size;
+  gpointer cursor, xcursor;
+  GHashTableIter iter;
+
+  g_return_if_fail (GDK_IS_DISPLAY (display));
+
+  xdisplay = GDK_DISPLAY_XDISPLAY (display);
+
+  old_theme = XcursorGetTheme (xdisplay);
+  old_size = XcursorGetDefaultSize (xdisplay);
+
+  if (old_size == size &&
+      (old_theme == theme ||
+       (old_theme && theme && strcmp (old_theme, theme) == 0)))
+    return;
+
+  XcursorSetTheme (xdisplay, theme);
+  if (size > 0)
+    XcursorSetDefaultSize (xdisplay, size);
+
+  g_hash_table_iter_init (&iter, GDK_X11_DISPLAY (display)->cursors);
+  while (g_hash_table_iter_next (&iter, &cursor, &xcursor))
+    {
+      const char *name = gdk_cursor_get_name (cursor);
+      
+      if (name)
+        {
+          Cursor new_cursor = gdk_x11_cursor_create_for_name (display, name);
+
+          if (new_cursor != None)
+            {
+              XFixesChangeCursor (xdisplay, new_cursor, GDK_POINTER_TO_XID (xcursor));
+              g_hash_table_iter_replace (&iter, GDK_XID_TO_POINTER (new_cursor));
+            }
+          else
+            {
+              g_hash_table_iter_remove (&iter);
+            }
+        }
+    }
+#endif
+}
+
+/**
+ * gdk_x11_display_get_xcursor:
+ * @display: a #GdkDisplay
+ * @cursor: a #GdkCursor.
+ * 
+ * Returns the X cursor belonging to a #GdkCursor, potentially
+ * creating the cursor.
+ *
+ * Be aware that the returned cursor may not be unique to @cursor.
+ * It may for example be shared with its fallback cursor. On old
+ * X servers that don't support the XCursor extension, all cursors
+ * may even fall back to a few default cursors.
+ * 
+ * Returns: an Xlib Cursor.
+ **/
+Cursor
+gdk_x11_display_get_xcursor (GdkDisplay *display,
+                             GdkCursor  *cursor)
+{
+  GdkX11Display *x11_display = GDK_X11_DISPLAY (display);
+  Cursor xcursor;
+
+  g_return_val_if_fail (cursor != NULL, None);
+
+  if (gdk_display_is_closed (display))
+    return None;
+
+  if (x11_display->cursors == NULL)
+    x11_display->cursors = g_hash_table_new (gdk_cursor_hash, gdk_cursor_equal);
+
+  xcursor = GDK_POINTER_TO_XID (g_hash_table_lookup (x11_display->cursors, cursor));
+  if (xcursor)
+    return xcursor;
+
+  if (gdk_cursor_get_name (cursor))
+    xcursor = gdk_x11_cursor_create_for_name (display, gdk_cursor_get_name (cursor));
+  else
+    xcursor = gdk_x11_cursor_create_for_texture (display,
+                                                 gdk_cursor_get_texture (cursor),
+                                                 gdk_cursor_get_hotspot_x (cursor),
+                                                 gdk_cursor_get_hotspot_y (cursor));
+
+  if (xcursor != None)
+    {
+      g_object_weak_ref (G_OBJECT (cursor), gdk_x11_cursor_remove_from_cache, display);
+      g_hash_table_insert (x11_display->cursors, cursor, GDK_XID_TO_POINTER (xcursor));
+      return xcursor;
+    }
+      
+  if (gdk_cursor_get_fallback (cursor))
+    return gdk_x11_display_get_xcursor (display, gdk_cursor_get_fallback (cursor));
+
+  return None;
+}
+
diff --git a/gdk/x11/gdkdevice-core-x11.c b/gdk/x11/gdkdevice-core-x11.c
index 5737bf6..ef2aad5 100644
--- a/gdk/x11/gdkdevice-core-x11.c
+++ b/gdk/x11/gdkdevice-core-x11.c
@@ -216,14 +216,15 @@ gdk_x11_device_core_set_window_cursor (GdkDevice *device,
                                        GdkWindow *window,
                                        GdkCursor *cursor)
 {
+  GdkDisplay *display = gdk_device_get_display (device);
   Cursor xcursor;
 
   if (!cursor)
     xcursor = None;
   else
-    xcursor = gdk_x11_cursor_get_xcursor (cursor);
+    xcursor = gdk_x11_display_get_xcursor (display, cursor);
 
-  XDefineCursor (GDK_WINDOW_XDISPLAY (window),
+  XDefineCursor (GDK_DISPLAY_XDISPLAY (display),
                  GDK_WINDOW_XID (window),
                  xcursor);
 }
@@ -369,8 +370,7 @@ gdk_x11_device_core_grab (GdkDevice    *device,
         xcursor = None;
       else
         {
-          _gdk_x11_cursor_update_theme (cursor);
-          xcursor = gdk_x11_cursor_get_xcursor (cursor);
+          xcursor = gdk_x11_display_get_xcursor (display, cursor);
         }
 
       xevent_mask = 0;
diff --git a/gdk/x11/gdkdevice-xi2.c b/gdk/x11/gdkdevice-xi2.c
index 8af7f34..b94ae39 100644
--- a/gdk/x11/gdkdevice-xi2.c
+++ b/gdk/x11/gdkdevice-xi2.c
@@ -290,7 +290,7 @@ gdk_x11_device_xi2_set_window_cursor (GdkDevice *device,
     XIDefineCursor (GDK_WINDOW_XDISPLAY (window),
                     device_xi2->device_id,
                     GDK_WINDOW_XID (window),
-                    gdk_x11_cursor_get_xcursor (cursor));
+                    gdk_x11_display_get_xcursor (GDK_WINDOW_DISPLAY (window), cursor));
   else
     XIUndefineCursor (GDK_WINDOW_XDISPLAY (window),
                       device_xi2->device_id,
@@ -439,8 +439,7 @@ gdk_x11_device_xi2_grab (GdkDevice    *device,
     xcursor = None;
   else
     {
-      _gdk_x11_cursor_update_theme (cursor);
-      xcursor = gdk_x11_cursor_get_xcursor (cursor);
+      xcursor = gdk_x11_display_get_xcursor (display, cursor);
     }
 
   mask.deviceid = device_xi2->device_id;
diff --git a/gdk/x11/gdkdisplay-x11.h b/gdk/x11/gdkdisplay-x11.h
index 9f7491f..536cd68 100644
--- a/gdk/x11/gdkdisplay-x11.h
+++ b/gdk/x11/gdkdisplay-x11.h
@@ -105,6 +105,9 @@ struct _GdkX11Display
   /* input GdkWindow list */
   GList *input_windows;
 
+  /* GdkCursor => XCursor */
+  GHashTable *cursors;
+
   GPtrArray *monitors;
   int primary_monitor;
 
diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h
index 7a84fce..bffa830 100644
--- a/gdk/x11/gdkprivate-x11.h
+++ b/gdk/x11/gdkprivate-x11.h
@@ -267,7 +267,6 @@ void _gdk_x11_screen_init_root_window (GdkScreen *screen);
 void _gdk_x11_screen_init_visuals     (GdkScreen *screen,
                                        gboolean   setup_display);
 
-void _gdk_x11_cursor_update_theme (GdkCursor *cursor);
 void _gdk_x11_cursor_display_finalize (GdkDisplay *display);
 
 void _gdk_x11_window_register_dnd (GdkWindow *window);
diff --git a/gdk/x11/gdkx-autocleanups.h b/gdk/x11/gdkx-autocleanups.h
index fcc27df..90b47dc 100644
--- a/gdk/x11/gdkx-autocleanups.h
+++ b/gdk/x11/gdkx-autocleanups.h
@@ -22,7 +22,6 @@
 #ifndef __GI_SCANNER__
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkX11AppLaunchContext, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkX11Cursor, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkX11DeviceCore, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkX11DeviceManagerCore, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkX11DeviceManagerXI2, g_object_unref)
diff --git a/gdk/x11/gdkx.h b/gdk/x11/gdkx.h
index c6f5ec8..87960fd 100644
--- a/gdk/x11/gdkx.h
+++ b/gdk/x11/gdkx.h
@@ -33,7 +33,6 @@
 #define __GDKX_H_INSIDE__
 
 #include <gdk/x11/gdkx11applaunchcontext.h>
-#include <gdk/x11/gdkx11cursor.h>
 #include <gdk/x11/gdkx11device.h>
 #include <gdk/x11/gdkx11device-core.h>
 #include <gdk/x11/gdkx11device-xi2.h>
diff --git a/gdk/x11/gdkx11display.h b/gdk/x11/gdkx11display.h
index a72b9db..2203556 100644
--- a/gdk/x11/gdkx11display.h
+++ b/gdk/x11/gdkx11display.h
@@ -62,6 +62,9 @@ GDK_AVAILABLE_IN_3_94
 Screen *      gdk_x11_display_get_xscreen      (GdkDisplay  *display);
 GDK_AVAILABLE_IN_3_94
 Window        gdk_x11_display_get_xrootwindow  (GdkDisplay  *display);
+GDK_AVAILABLE_IN_3_94
+Cursor        gdk_x11_display_get_xcursor      (GdkDisplay  *display,
+                                                GdkCursor   *cursor);
 
 #define GDK_DISPLAY_XDISPLAY(display) (gdk_x11_display_get_xdisplay (display))
 
diff --git a/gdk/x11/meson.build b/gdk/x11/meson.build
index 28f0428..4afc1cb 100644
--- a/gdk/x11/meson.build
+++ b/gdk/x11/meson.build
@@ -31,7 +31,6 @@ gdk_x11_sources = files([
 gdk_x11_public_headers = files([
   'gdkx-autocleanups.h',
   'gdkx11applaunchcontext.h',
-  'gdkx11cursor.h',
   'gdkx11device-core.h',
   'gdkx11device-xi2.h',
   'gdkx11device.h',
diff --git a/testsuite/gtk/objects-finalize.c b/testsuite/gtk/objects-finalize.c
index 4b8a33d..26b0362 100644
--- a/testsuite/gtk/objects-finalize.c
+++ b/testsuite/gtk/objects-finalize.c
@@ -109,7 +109,6 @@ main (int argc, char **argv)
          !G_TYPE_IS_ABSTRACT (all_types[i]) &&
 #ifdef GDK_WINDOWING_X11
          all_types[i] != GDK_TYPE_X11_WINDOW &&
-         all_types[i] != GDK_TYPE_X11_CURSOR &&
          all_types[i] != GDK_TYPE_X11_SCREEN &&
          all_types[i] != GDK_TYPE_X11_DISPLAY &&
          all_types[i] != GDK_TYPE_X11_DEVICE_MANAGER_CORE &&


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]