Index: gdk/gdkcursor.h =================================================================== --- gdk/gdkcursor.h (revision 22084) +++ gdk/gdkcursor.h (working copy) @@ -120,7 +120,8 @@ GDK_WATCH = 150, GDK_XTERM = 152, GDK_LAST_CURSOR, - GDK_CURSOR_IS_PIXMAP = -1 + GDK_BLANK_CURSOR = -2, + GDK_CURSOR_IS_PIXMAP = -1 } GdkCursorType; struct _GdkCursor Index: gdk/x11/gdkdisplay-x11.c =================================================================== --- gdk/x11/gdkdisplay-x11.c (revision 22084) +++ gdk/x11/gdkdisplay-x11.c (working copy) @@ -838,6 +838,8 @@ g_free (display_x11->motif_target_lists); } + _gdk_x11_cursor_display_finalize (GDK_DISPLAY_OBJECT(display_x11)); + /* Atom Hashtable */ g_hash_table_destroy (display_x11->atom_from_virtual); g_hash_table_destroy (display_x11->atom_to_virtual); Index: gdk/x11/gdkprivate-x11.h =================================================================== --- gdk/x11/gdkprivate-x11.h (revision 22084) +++ gdk/x11/gdkprivate-x11.h (working copy) @@ -191,6 +191,7 @@ GdkGC *gc); void _gdk_x11_cursor_update_theme (GdkCursor *cursor); +void _gdk_x11_cursor_display_finalize (GdkDisplay *display); gboolean _gdk_x11_get_xft_setting (GdkScreen *screen, const gchar *name, Index: gdk/x11/gdkcursor-x11.c =================================================================== --- gdk/x11/gdkcursor-x11.c (revision 22084) +++ gdk/x11/gdkcursor-x11.c (working copy) @@ -49,6 +49,136 @@ 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 never removed. I make the assumption that since there are a small + number of GdkDisplay'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; + 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 +add_to_cache (GdkCursorPrivate* cursor) +{ + cursor_cache = g_slist_prepend (cursor_cache, cursor); + + /* Take a ref so that if the caller frees it we still have it */ + gdk_cursor_ref ((GdkCursor*) cursor); +} + +/* Returns 0 on a match + */ +static gint +cache_compare_func (gconstpointer listelem, gconstpointer target) +{ + GdkCursorPrivate* curs = (GdkCursorPrivate*)listelem; + struct cursor_cache_key* key = (struct cursor_cache_key*)target; + + if ((curs->cursor.type != key->type) || + (curs->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, curs->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 + */ +static GdkCursorPrivate* +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); + + return res?(GdkCursorPrivate*)(res->data):NULL; +} + +/* Called by gdk_display_x11_finalize to flush any cached cursors + for a dead display. + */ +void +_gdk_x11_cursor_display_finalize (GdkDisplay *display) +{ + /* Untested! There must be an easier way to do this, a foreach that + took a flag back to say whether to delete an item would be nice */ + + GSList* item; + GSList** itemp; /* Pointer to the thing to fix when we delete an item */ + item = cursor_cache; + itemp = &cursor_cache; + while (item) + { + GdkCursorPrivate* cursor = (GdkCursorPrivate*)(item->data); + if (cursor->display == display) + { + GSList* olditem; + gdk_cursor_unref ((GdkCursor*) cursor); + /* Remove this item from the list */ + *(itemp) = item->next; + olditem = item; + item = g_slist_next (item); + g_slist_free_1 (olditem); + } else { + itemp = &(item->next); + item = g_slist_next (item); + } + } +} + +static Cursor +get_blank_cursor (GdkDisplay *display) +{ + /* TODO - figure out some magic to stop XCursor doing a theme search + if we have XCursor */ + GdkScreen *screen; + GdkPixmap *pixmap; + Pixmap source_pixmap; + XColor xfg, xbg; + Cursor xcursor; + + screen = gdk_display_get_default_screen (display); + pixmap = gdk_bitmap_create_from_data (gdk_screen_get_root_window (screen), + "\0\0\0\0\0\0\0\0", 1, 1); + + source_pixmap = GDK_PIXMAP_XID(pixmap); + + xfg.pixel = 0; /* Check?! */ + xfg.red = xfg.blue = xfg.green = 0; + xbg.pixel = 0; /* Check?! */ + xbg.red = xbg.blue = xbg.green = 0; + + if (display->closed) + xcursor = None; + else + xcursor = XCreatePixmapCursor (GDK_DISPLAY_XDISPLAY (display), + source_pixmap, source_pixmap, + &xfg, &xbg, 1, 1); + + g_object_unref (pixmap); + return xcursor; +} + /** * gdk_cursor_new_for_display: * @display: the #GdkDisplay for which the cursor will be created @@ -108,11 +238,11 @@ * * #GDK_SB_V_DOUBLE_ARROW (move horizontal splitter) * + * + * #GDK_BLANK_CURSOR (Blank cursor) + * * * - * To make the cursor invisible, use gdk_cursor_new_from_pixmap() to create - * a cursor with no pixels in it. - * * Return value: a new #GdkCursor * * Since: 2.2 @@ -128,9 +258,25 @@ g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); if (display->closed) - xcursor = None; - else - xcursor = XCreateFontCursor (GDK_DISPLAY_XDISPLAY (display), cursor_type); + { + xcursor = None; + } else { + private = find_in_cache (display, cursor_type, NULL); + + if (private) + { + /* Cache had it, add a ref for this user */ + gdk_cursor_ref ((GdkCursor*) private); + + return (GdkCursor*) private; + } else { + if (cursor_type != GDK_BLANK_CURSOR) + xcursor = XCreateFontCursor (GDK_DISPLAY_XDISPLAY (display), + cursor_type); + else + xcursor = get_blank_cursor (display); + } + } private = g_new (GdkCursorPrivate, 1); private->display = display; @@ -142,6 +288,9 @@ cursor->type = cursor_type; cursor->ref_count = 1; + if (xcursor != None) + add_to_cache (private); + return cursor; } @@ -427,26 +576,21 @@ new_cursor = XcursorShapeLoadCursor (xdisplay, cursor->type); if (new_cursor != None) - XFixesChangeCursor (xdisplay, new_cursor, private->xcursor); + { + XFixesChangeCursor (xdisplay, new_cursor, private->xcursor); + private->xcursor = new_cursor; + } } } static void -update_cursor (gpointer key, - gpointer value, - gpointer data) +update_cursor (gpointer data, + gpointer user_data) { - XID *xid = key; GdkCursor *cursor; - if (*xid & XID_FONT_BIT) - return; + cursor = (GdkCursor*)(data); - if (!GDK_IS_WINDOW (value)) - return; - - cursor = _gdk_x11_window_get_cursor (GDK_WINDOW (value)); - if (!cursor) return; @@ -503,7 +647,7 @@ if (size > 0) XcursorSetDefaultSize (xdisplay, size); - g_hash_table_foreach (display_x11->xid_ht, update_cursor, NULL); + g_slist_foreach (cursor_cache, update_cursor, NULL); } #else @@ -679,6 +823,16 @@ xcursor = None; else { + private = find_in_cache (display, GDK_CURSOR_IS_PIXMAP, name); + + if (private) + { + /* Cache had it, add a ref for this user */ + gdk_cursor_ref ((GdkCursor*) private); + + return (GdkCursor*) private; + } + xdisplay = GDK_DISPLAY_XDISPLAY (display); xcursor = XcursorLibraryLoadCursor (xdisplay, name); if (xcursor == None) @@ -694,7 +848,8 @@ cursor = (GdkCursor *) private; cursor->type = GDK_CURSOR_IS_PIXMAP; cursor->ref_count = 1; - + add_to_cache (private); + return cursor; }