diff options
author | LRN <lrn1986@gmail.com> | 2018-04-01 10:33:43 +0000 |
---|---|---|
committer | LRN <lrn1986@gmail.com> | 2018-04-01 10:33:43 +0000 |
commit | eec0bd2fa9cee5eff26aa3136f1d4f616347d852 (patch) | |
tree | 11f658f652b74076c2b9e191f6fa3ef81a3fbecc /gdk | |
parent | f2937f8c453b58fd91c3f35b152dd736f4a3edf2 (diff) | |
parent | 795572710c99d7612bd7d71d538e8dab60d84309 (diff) | |
download | gtk+-eec0bd2fa9cee5eff26aa3136f1d4f616347d852.tar.gz |
Merge branch 'lrn/issue-147' into 'master'
Fix cursor handling in GTK4 on W32
Closes #147
See merge request GNOME/gtk!92
Diffstat (limited to 'gdk')
-rw-r--r-- | gdk/win32/gdkcursor-win32.c | 691 | ||||
-rw-r--r-- | gdk/win32/gdkdevice-virtual.c | 51 | ||||
-rw-r--r-- | gdk/win32/gdkdisplay-win32.h | 13 | ||||
-rw-r--r-- | gdk/win32/gdkevents-win32.c | 46 | ||||
-rw-r--r-- | gdk/win32/gdkprivate-win32.h | 8 | ||||
-rw-r--r-- | gdk/win32/gdksurface-win32.c | 18 | ||||
-rw-r--r-- | gdk/win32/gdksurface-win32.h | 4 | ||||
-rw-r--r-- | gdk/win32/gdkwin32cursor.h | 53 | ||||
-rw-r--r-- | gdk/win32/gdkwin32display.h | 4 |
9 files changed, 624 insertions, 264 deletions
diff --git a/gdk/win32/gdkcursor-win32.c b/gdk/win32/gdkcursor-win32.c index 791ec00e9b..95bcb334a8 100644 --- a/gdk/win32/gdkcursor-win32.c +++ b/gdk/win32/gdkcursor-win32.c @@ -22,6 +22,7 @@ #include "gdkcursor.h" #include "gdkwin32.h" #include "gdktextureprivate.h" +#include "gdkintl.h" #include "gdkdisplay-win32.h" @@ -76,6 +77,309 @@ static DefaultCursor default_cursors[] = { { "se-resize", IDC_SIZENWSE } }; +typedef struct _GdkWin32HCursorTableEntry GdkWin32HCursorTableEntry; + +struct _GdkWin32HCursorTableEntry +{ + HCURSOR handle; + guint64 refcount; + gboolean destroyable; +}; + +struct _GdkWin32HCursor +{ + GObject parent_instance; + + /* Do not do any modifications to the handle + * (i.e. do not call DestroyCursor() on it). + * It's a "read-only" copy, the original is stored + * in the display instance. + */ + HANDLE readonly_handle; + + /* This is a way to access the real handle stored + * in the display. + * TODO: make it a weak reference + */ + GdkWin32Display *display; + + /* A copy of the "destoyable" attribute of the handle */ + gboolean readonly_destroyable; +}; + +struct _GdkWin32HCursorClass +{ + GObjectClass parent_class; +}; + +enum +{ + PROP_0, + PROP_DISPLAY, + PROP_HANDLE, + PROP_DESTROYABLE, + NUM_PROPERTIES +}; + +G_DEFINE_TYPE (GdkWin32HCursor, gdk_win32_hcursor, G_TYPE_OBJECT) + +static void +gdk_win32_hcursor_init (GdkWin32HCursor *win32_hcursor) +{ +} + +static void +gdk_win32_hcursor_finalize (GObject *gobject) +{ + GdkWin32HCursor *win32_hcursor = GDK_WIN32_HCURSOR (gobject); + + if (win32_hcursor->display) + _gdk_win32_display_hcursor_unref (win32_hcursor->display, win32_hcursor->readonly_handle); + + g_clear_object (&win32_hcursor->display); + + G_OBJECT_CLASS (gdk_win32_hcursor_parent_class)->finalize (G_OBJECT (win32_hcursor)); +} + +static void +gdk_win32_hcursor_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdkWin32HCursor *win32_hcursor; + + win32_hcursor = GDK_WIN32_HCURSOR (object); + + switch (prop_id) + { + case PROP_DISPLAY: + g_set_object (&win32_hcursor->display, g_value_get_object (value)); + break; + + case PROP_DESTROYABLE: + win32_hcursor->readonly_destroyable = g_value_get_boolean (value); + break; + + case PROP_HANDLE: + win32_hcursor->readonly_handle = g_value_get_pointer (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + +} + +static void +gdk_win32_hcursor_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdkWin32HCursor *win32_hcursor; + + win32_hcursor = GDK_WIN32_HCURSOR (object); + + switch (prop_id) + { + case PROP_DISPLAY: + g_value_set_object (value, win32_hcursor->display); + break; + + case PROP_DESTROYABLE: + g_value_set_boolean (value, win32_hcursor->readonly_destroyable); + break; + + case PROP_HANDLE: + g_value_set_pointer (value, win32_hcursor->readonly_handle); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdk_win32_hcursor_constructed (GObject *object) +{ + GdkWin32HCursor *win32_hcursor; + + win32_hcursor = GDK_WIN32_HCURSOR (object); + + g_assert_nonnull (win32_hcursor->display); + g_assert_nonnull (win32_hcursor->readonly_handle); + + _gdk_win32_display_hcursor_ref (win32_hcursor->display, + win32_hcursor->readonly_handle, + win32_hcursor->readonly_destroyable); +} + +static GParamSpec *hcursor_props[NUM_PROPERTIES] = { NULL, }; + +static void +gdk_win32_hcursor_class_init (GdkWin32HCursorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gdk_win32_hcursor_finalize; + object_class->constructed = gdk_win32_hcursor_constructed; + object_class->get_property = gdk_win32_hcursor_get_property; + object_class->set_property = gdk_win32_hcursor_set_property; + + hcursor_props[PROP_DISPLAY] = + g_param_spec_object ("display", + P_("Display"), + P_("The display that will use this cursor"), + GDK_TYPE_DISPLAY, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + + hcursor_props[PROP_HANDLE] = + g_param_spec_pointer ("handle", + P_("Handle"), + P_("The HCURSOR handle for this cursor"), + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + + hcursor_props[PROP_DESTROYABLE] = + g_param_spec_boolean ("destroyable", + P_("Destroyable"), + P_("Whether calling DestroyCursor() is allowed on this cursor"), + TRUE, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + + g_object_class_install_properties (object_class, NUM_PROPERTIES, hcursor_props); +} + +GdkWin32HCursor * +gdk_win32_hcursor_new (GdkWin32Display *display, + HCURSOR handle, + gboolean destroyable) +{ + return g_object_new (GDK_TYPE_WIN32_HCURSOR, + "display", display, + "handle", handle, + "destroyable", destroyable, + NULL); +} + +void +_gdk_win32_display_hcursor_ref (GdkWin32Display *display, + HCURSOR handle, + gboolean destroyable) +{ + GdkWin32HCursorTableEntry *entry; + + entry = g_hash_table_lookup (display->cursor_reftable, handle); + + if (entry) + { + if (entry->destroyable != destroyable) + g_warning ("Destroyability metadata for cursor handle 0x%p does not match", handle); + + entry->refcount += 1; + + return; + } + + entry = g_new0 (GdkWin32HCursorTableEntry, 1); + entry->handle = handle; + entry->destroyable = destroyable; + entry->refcount = 1; + + g_hash_table_insert (display->cursor_reftable, handle, entry); + display->cursors_for_destruction = g_list_remove_all (display->cursors_for_destruction, handle); +} + +static gboolean +delayed_cursor_destruction (gpointer user_data) +{ + GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (user_data); + HANDLE current_hcursor = GetCursor (); + GList *p; + + win32_display->idle_cursor_destructor_id = 0; + + for (p = win32_display->cursors_for_destruction; p; p = p->next) + { + HCURSOR handle = (HCURSOR) p->data; + + if (handle == NULL) + continue; + + if (current_hcursor == handle) + { + SetCursor (NULL); + current_hcursor = NULL; + } + + if (!DestroyCursor (handle)) + g_warning (G_STRLOC ": DestroyCursor (%p) failed: %lu", handle, GetLastError ()); + } + + g_list_free (win32_display->cursors_for_destruction); + win32_display->cursors_for_destruction = NULL; + + return G_SOURCE_REMOVE; +} + +void +_gdk_win32_display_hcursor_unref (GdkWin32Display *display, + HCURSOR handle) +{ + GdkWin32HCursorTableEntry *entry; + gboolean destroyable; + + entry = g_hash_table_lookup (display->cursor_reftable, handle); + + if (!entry) + { + g_warning ("Trying to forget cursor handle 0x%p that is not in the table", handle); + + return; + } + + entry->refcount -= 1; + + if (entry->refcount > 0) + return; + + destroyable = entry->destroyable; + + g_hash_table_remove (display->cursor_reftable, handle); + g_free (entry); + + if (!destroyable) + return; + + /* GDK tends to destroy a cursor first, then set a new one. + * This results in repeated oscillations between SetCursor(NULL) + * and SetCursor(hcursor). To avoid that, delay cursor destruction a bit + * to let GDK set a new one first. That way cursors are switched + * seamlessly, without a NULL cursor between them. + * If GDK sets the new cursor to the same handle the old cursor had, + * the cursor handle is taken off the destruction list. + */ + if (g_list_find (display->cursors_for_destruction, handle) == NULL) + { + display->cursors_for_destruction = g_list_prepend (display->cursors_for_destruction, handle); + + if (display->idle_cursor_destructor_id == 0) + display->idle_cursor_destructor_id = g_idle_add (delayed_cursor_destruction, display); + } +} + +#ifdef gdk_win32_hcursor_get_handle +#undef gdk_win32_hcursor_get_handle +#endif + +HCURSOR +gdk_win32_hcursor_get_handle (GdkWin32HCursor *cursor) +{ + return cursor->readonly_handle; +} + static HCURSOR hcursor_from_x_cursor (gint i, const gchar *name) @@ -141,41 +445,60 @@ hcursor_from_x_cursor (gint i, return rv; } -static HCURSOR -win32_cursor_create_hcursor (Win32Cursor *cursor, - const gchar *name) +static GdkWin32HCursor * +win32_cursor_create_win32hcursor (GdkWin32Display *display, + Win32Cursor *cursor, + const gchar *name) { - HCURSOR result; + GdkWin32HCursor *result; switch (cursor->load_type) { case GDK_WIN32_CURSOR_LOAD_FROM_FILE: - result = LoadImageW (NULL, - cursor->resource_name, - IMAGE_CURSOR, - cursor->width, - cursor->height, - cursor->load_flags); + result = gdk_win32_hcursor_new (display, + LoadImageW (NULL, + cursor->resource_name, + IMAGE_CURSOR, + cursor->width, + cursor->height, + cursor->load_flags), + cursor->load_flags & LR_SHARED ? FALSE : TRUE); break; case GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_NULL: - result = LoadImageA (NULL, - (const gchar *) cursor->resource_name, - IMAGE_CURSOR, - cursor->width, - cursor->height, - cursor->load_flags); + result = gdk_win32_hcursor_new (display, + LoadImageA (NULL, + (const gchar *) cursor->resource_name, + IMAGE_CURSOR, + cursor->width, + cursor->height, + cursor->load_flags), + cursor->load_flags & LR_SHARED ? FALSE : TRUE); break; case GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_THIS: - result = LoadImageA (_gdk_app_hmodule, - (const gchar *) cursor->resource_name, - IMAGE_CURSOR, - cursor->width, - cursor->height, - cursor->load_flags); + result = gdk_win32_hcursor_new (display, + LoadImageA (_gdk_app_hmodule, + (const gchar *) cursor->resource_name, + IMAGE_CURSOR, + cursor->width, + cursor->height, + cursor->load_flags), + cursor->load_flags & LR_SHARED ? FALSE : TRUE); + break; + case GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_GTK: + result = gdk_win32_hcursor_new (display, + LoadImageA (_gdk_dll_hinstance, + (const gchar *) cursor->resource_name, + IMAGE_CURSOR, + cursor->width, + cursor->height, + cursor->load_flags), + cursor->load_flags & LR_SHARED ? FALSE : TRUE); break; case GDK_WIN32_CURSOR_CREATE: - result = hcursor_from_x_cursor (cursor->xcursor_number, - name); + result = gdk_win32_hcursor_new (display, + hcursor_from_x_cursor (cursor->xcursor_number, + name), + TRUE); break; default: result = NULL; @@ -303,38 +626,40 @@ static void win32_cursor_theme_load_system (Win32CursorTheme *theme, gint size) { - gint i; - HCURSOR hcursor; - Win32Cursor *cursor; + gint i; + HCURSOR shared_hcursor; + HCURSOR x_hcursor; + Win32Cursor *cursor; for (i = 0; i < G_N_ELEMENTS (cursors); i++) { - if (cursors[i].name == NULL) break; - hcursor = NULL; + shared_hcursor = NULL; + x_hcursor = NULL; /* Prefer W32 cursors */ if (cursors[i].builtin) - hcursor = LoadImageA (NULL, cursors[i].builtin, IMAGE_CURSOR, - size, size, - LR_SHARED | (size == 0 ? LR_DEFAULTSIZE : 0)); + shared_hcursor = LoadImageA (NULL, cursors[i].builtin, IMAGE_CURSOR, + size, size, + LR_SHARED | (size == 0 ? LR_DEFAULTSIZE : 0)); /* Fall back to X cursors, but only if we've got no theme cursor */ - if (hcursor == NULL && g_hash_table_lookup (theme->named_cursors, cursors[i].name) == NULL) - hcursor = hcursor_from_x_cursor (i, cursors[i].name); + if (shared_hcursor == NULL && g_hash_table_lookup (theme->named_cursors, cursors[i].name) == NULL) + x_hcursor = hcursor_from_x_cursor (i, cursors[i].name); - if (hcursor == NULL) + if (shared_hcursor == NULL && x_hcursor == NULL) continue; + else if (x_hcursor != NULL) + DestroyCursor (x_hcursor); - DestroyCursor (hcursor); - cursor = win32_cursor_new (GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_NULL, + cursor = win32_cursor_new (shared_hcursor ? GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_NULL : GDK_WIN32_CURSOR_CREATE, (gpointer) cursors[i].builtin, size, size, LR_SHARED | (size == 0 ? LR_DEFAULTSIZE : 0), - 0); + x_hcursor ? i : 0); g_hash_table_insert (theme->named_cursors, g_strdup (cursors[i].name), cursor); @@ -345,13 +670,12 @@ win32_cursor_theme_load_system (Win32CursorTheme *theme, if (default_cursors[i].name == NULL) break; - hcursor = LoadImageA (NULL, default_cursors[i].id, IMAGE_CURSOR, size, size, - LR_SHARED | (size == 0 ? LR_DEFAULTSIZE : 0)); + shared_hcursor = LoadImageA (NULL, default_cursors[i].id, IMAGE_CURSOR, size, size, + LR_SHARED | (size == 0 ? LR_DEFAULTSIZE : 0)); - if (hcursor == NULL) + if (shared_hcursor == NULL) continue; - DestroyCursor (hcursor); cursor = win32_cursor_new (GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_NULL, (gpointer) default_cursors[i].id, size, @@ -410,15 +734,10 @@ static void gdk_win32_cursor_remove_from_cache (gpointer data, GObject *cursor) { GdkDisplay *display = data; - HCURSOR hcursor; - - hcursor = g_hash_table_lookup (GDK_WIN32_DISPLAY (display)->cursors, cursor); - - if (GetCursor () == hcursor) - SetCursor (NULL); + GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display); - if (!DestroyCursor (hcursor)) - g_warning (G_STRLOC ": DestroyCursor (%p) failed: %lu", hcursor, GetLastError ()); + /* Unrefs the GdkWin32HCursor value object automatically */ + g_hash_table_remove (win32_display->cursors, cursor); } void @@ -439,6 +758,9 @@ _gdk_win32_display_finalize_cursors (GdkWin32Display *display) g_free (display->cursor_theme_name); + g_list_free (display->cursors_for_destruction); + display->cursors_for_destruction = NULL; + if (display->cursor_theme) win32_cursor_theme_destroy (display->cursor_theme); } @@ -446,14 +768,18 @@ _gdk_win32_display_finalize_cursors (GdkWin32Display *display) void _gdk_win32_display_init_cursors (GdkWin32Display *display) { - display->cursors = g_hash_table_new (gdk_cursor_hash, - gdk_cursor_equal); + display->cursors = g_hash_table_new_full (gdk_cursor_hash, + gdk_cursor_equal, + NULL, + g_object_unref); + display->cursor_reftable = g_hash_table_new (NULL, NULL); display->cursor_theme_name = g_strdup ("system"); } -/* This is where we use the names mapped to the equivilants that Windows define by default */ -static HCURSOR -hcursor_idc_from_name (const gchar *name) +/* This is where we use the names mapped to the equivalents that Windows defines by default */ +static GdkWin32HCursor * +win32hcursor_idc_from_name (GdkWin32Display *display, + const gchar *name) { int i; @@ -462,28 +788,31 @@ hcursor_idc_from_name (const gchar *name) if (strcmp (default_cursors[i].name, name) != 0) continue; - return LoadImageA (NULL, default_cursors[i].id, IMAGE_CURSOR, 0, 0, - LR_SHARED | LR_DEFAULTSIZE); + return gdk_win32_hcursor_new (display, + LoadImageA (NULL, default_cursors[i].id, IMAGE_CURSOR, 0, 0, + LR_SHARED | LR_DEFAULTSIZE), + FALSE); } return NULL; } -static HCURSOR -hcursor_x_from_name (const gchar *name) +static GdkWin32HCursor * +win32hcursor_x_from_name (GdkWin32Display *display, + const gchar *name) { gint i; for (i = 0; i < G_N_ELEMENTS (cursors); i++) if (cursors[i].name == NULL || strcmp (cursors[i].name, name) == 0) - return hcursor_from_x_cursor (i, name); + return gdk_win32_hcursor_new (display, hcursor_from_x_cursor (i, name), TRUE); return NULL; } -static HCURSOR -hcursor_from_theme (GdkDisplay *display, - const gchar *name) +static GdkWin32HCursor * +win32hcursor_from_theme (GdkWin32Display *display, + const gchar *name) { Win32CursorTheme *theme; Win32Cursor *theme_cursor; @@ -498,34 +827,34 @@ hcursor_from_theme (GdkDisplay *display, if (theme_cursor == NULL) return NULL; - return win32_cursor_create_hcursor (theme_cursor, name); + return win32_cursor_create_win32hcursor (win32_display, theme_cursor, name); } -static HCURSOR -hcursor_from_name (GdkDisplay *display, - const gchar *name) +static GdkWin32HCursor * +win32hcursor_from_name (GdkWin32Display *display, + const gchar *name) { - HCURSOR hcursor; + GdkWin32HCursor *win32hcursor; /* Try current theme first */ - hcursor = hcursor_from_theme (display, name); + win32hcursor = win32hcursor_from_theme (display, name); - if (hcursor != NULL) - return hcursor; + if (win32hcursor != NULL) + return win32hcursor; - hcursor = hcursor_idc_from_name (name); + win32hcursor = win32hcursor_idc_from_name (display, name); - if (hcursor != NULL) - return hcursor; + if (win32hcursor != NULL) + return win32hcursor; - hcursor = hcursor_x_from_name (name); + win32hcursor = win32hcursor_x_from_name (display, name); - return hcursor; + return win32hcursor; } /* Create a blank cursor */ -static HCURSOR -create_blank_cursor (void) +static GdkWin32HCursor * +create_blank_win32hcursor (GdkWin32Display *display) { gint w, h; guchar *and_plane, *xor_plane; @@ -545,31 +874,28 @@ create_blank_cursor (void) if (rv == NULL) WIN32_API_FAILED ("CreateCursor"); - return rv; + return gdk_win32_hcursor_new (display, rv, TRUE); } -static HCURSOR -gdk_win32_cursor_create_for_name (GdkDisplay *display, +static GdkWin32HCursor * +gdk_win32hcursor_create_for_name (GdkWin32Display *display, const gchar *name) { - HCURSOR hcursor = NULL; - GdkCursor *result; - GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display); + GdkWin32HCursor *win32hcursor = NULL; /* Blank cursor case */ if (strcmp (name, "none") == 0) - return create_blank_cursor (); - - hcursor = hcursor_from_name (display, name); + return create_blank_win32hcursor (display); - /* allow to load named cursor resources linked into the executable */ - if (!hcursor) - hcursor = LoadCursor (_gdk_app_hmodule, name); + win32hcursor = win32hcursor_from_name (display, name); - if (hcursor == NULL) - return NULL; + if (win32hcursor) + return win32hcursor; - return hcursor; + /* Allow to load named cursor resources linked into the executable. + * Cursors obtained with LoadCursor() cannot be destroyed. + */ + return gdk_win32_hcursor_new (display, LoadCursor (_gdk_app_hmodule, name), FALSE); } static HICON @@ -578,11 +904,11 @@ pixbuf_to_hicon (GdkPixbuf *pixbuf, gint x, gint y); -static HCURSOR -gdk_win32_cursor_create_for_texture (GdkDisplay *display, - GdkTexture *texture, - int x, - int y) +static GdkWin32HCursor * +gdk_win32hcursor_create_for_texture (GdkWin32Display *display, + GdkTexture *texture, + int x, + int y) { cairo_surface_t *surface; GdkPixbuf *pixbuf; @@ -599,72 +925,20 @@ gdk_win32_cursor_create_for_texture (GdkDisplay *display, g_object_unref (pixbuf); - return (HCURSOR)icon; -} - -GdkCursor * -gdk_win32_display_cursor_from_hcursor (GdkDisplay *display, - HCURSOR hcursor) -{ - GHashTableIter iter; - gpointer cursor_current, hcursor_current; - - GdkCursor *cursor = NULL; - GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display); - - if (win32_display->cursors) - { - g_hash_table_iter_init (&iter, win32_display->cursors); - while (g_hash_table_iter_next (&iter, &cursor_current, &hcursor_current)) - if ((HCURSOR)hcursor_current == hcursor) - { - cursor = (GdkCursor*) cursor_current; - break; - } - } - - return cursor; + return gdk_win32_hcursor_new (display, (HCURSOR) icon, TRUE); } -HCURSOR -_gdk_win32_display_get_cursor_for_surface (GdkDisplay *display, - cairo_surface_t *surface, - gdouble x, - gdouble y) -{ - HCURSOR hcursor; - GdkPixbuf *pixbuf; - gint width, height; - - g_return_val_if_fail (surface != NULL, NULL); - - width = cairo_image_surface_get_width (surface); - height = cairo_image_surface_get_height (surface); - pixbuf = gdk_pixbuf_get_from_surface (surface, - 0, - 0, - width, - height); - - g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); - 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); - - hcursor = _gdk_win32_pixbuf_to_hcursor (pixbuf, x, y); - - g_object_unref (pixbuf); - - return hcursor; -} static gboolean -_gdk_win32_cursor_update (GdkWin32Display *win32_display, - GdkCursor *cursor, - HCURSOR hcursor) +_gdk_win32_cursor_update (GdkWin32Display *win32_display, + GdkCursor *cursor, + GdkWin32HCursor *win32_hcursor, + GList **update_cursors, + GList **update_win32hcursors) { - HCURSOR hcursor_new = NULL; + GdkWin32HCursor *win32hcursor_new = NULL; Win32CursorTheme *theme; - Win32Cursor *theme_cursor; + Win32Cursor *theme_cursor; const gchar *name = gdk_cursor_get_name (cursor); @@ -676,28 +950,29 @@ _gdk_win32_cursor_update (GdkWin32Display *win32_display, theme_cursor = win32_cursor_theme_get_cursor (theme, name); if (theme_cursor != NULL) - hcursor_new = win32_cursor_create_hcursor (theme_cursor, name); + win32hcursor_new = win32_cursor_create_win32hcursor (win32_display, theme_cursor, name); - if (hcursor_new == NULL) + if (win32hcursor_new == NULL) { g_warning (G_STRLOC ": Unable to load %s from the cursor theme", name); - hcursor_new = hcursor_idc_from_name (name); + win32hcursor_new = win32hcursor_idc_from_name (win32_display, name); - if (hcursor_new == NULL) - hcursor_new = hcursor_x_from_name (name); + if (win32hcursor_new == NULL) + win32hcursor_new = win32hcursor_x_from_name (win32_display, name); - if (hcursor_new == NULL) + if (win32hcursor_new == NULL) return FALSE; } - if (GetCursor () == hcursor) - SetCursor (hcursor_new); - - if (!DestroyCursor (hcursor)) - g_warning (G_STRLOC ": DestroyCursor (%p) failed: %lu", hcursor, GetLastError ()); + if (GetCursor () == win32_hcursor->readonly_handle) + SetCursor (win32hcursor_new->readonly_handle); - g_hash_table_replace (win32_display->cursors, cursor, hcursor_new); + /* Don't modify the hash table mid-iteration, put everything into a list + * and update the table later on. + */ + *update_cursors = g_list_prepend (*update_cursors, cursor); + *update_win32hcursors = g_list_prepend (*update_win32hcursors, win32hcursor_new); return TRUE; } @@ -707,12 +982,23 @@ _gdk_win32_display_update_cursors (GdkWin32Display *display) { GHashTableIter iter; GdkCursor *cursor; - HCURSOR hcursor; + GdkWin32HCursor *win32hcursor; + GList *update_cursors = NULL; + GList *update_win32hcursors = NULL; g_hash_table_iter_init (&iter, display->cursors); - while (g_hash_table_iter_next (&iter, (gpointer *) &cursor, &hcursor)) - _gdk_win32_cursor_update (display, cursor, hcursor); + while (g_hash_table_iter_next (&iter, (gpointer *) &cursor, (gpointer *) &win32hcursor)) + _gdk_win32_cursor_update (display, cursor, win32hcursor, &update_cursors, &update_win32hcursors); + + while (update_cursors != NULL && update_win32hcursors != NULL) + { + g_hash_table_replace (display->cursors, update_cursors->data, update_win32hcursors->data); + update_cursors = g_list_delete_link (update_cursors, update_cursors); + update_win32hcursors = g_list_delete_link (update_win32hcursors, update_win32hcursors); + } + + g_assert (update_cursors == NULL && update_win32hcursors == NULL); } GdkPixbuf * @@ -1205,51 +1491,58 @@ _gdk_win32_pixbuf_to_hcursor (GdkPixbuf *pixbuf, /** - * gdk_win32_display_get_hcursor: + * gdk_win32_display_get_win32hcursor: * @display: (type GdkWin32Display): a #GdkDisplay * @cursor: a #GdkCursor. * - * Returns the Win32 HCURSOR belonging to a #GdkCursor, potentially - * creating the cursor. + * Returns the Win32 HCURSOR wrapper object belonging to a #GdkCursor, + * potentially creating the cursor object. * * Be aware that the returned cursor may not be unique to @cursor. * It may for example be shared with its fallback cursor. * - * Returns: a Win32 HCURSOR. + * Returns: a GdkWin32HCursor. **/ -HCURSOR -gdk_win32_display_get_hcursor (GdkDisplay *display, - GdkCursor *cursor) +GdkWin32HCursor * +gdk_win32_display_get_win32hcursor (GdkWin32Display *display, + GdkCursor *cursor) { GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display); - HCURSOR hcursor; + GdkWin32HCursor *win32hcursor; + const gchar *cursor_name; + GdkCursor *fallback; g_return_val_if_fail (cursor != NULL, NULL); - if (gdk_display_is_closed (display)) + if (gdk_display_is_closed (GDK_DISPLAY (display))) return NULL; - - hcursor = g_hash_table_lookup (win32_display->cursors, cursor); - if (hcursor != NULL) - return hcursor; - if (gdk_cursor_get_name (cursor)) - hcursor = gdk_win32_cursor_create_for_name (display, gdk_cursor_get_name (cursor)); + win32hcursor = g_hash_table_lookup (win32_display->cursors, cursor); + + if (win32hcursor != NULL) + return win32hcursor; + + cursor_name = gdk_cursor_get_name (cursor); + + if (cursor_name) + win32hcursor = gdk_win32hcursor_create_for_name (display, cursor_name); else - hcursor = gdk_win32_cursor_create_for_texture (display, - gdk_cursor_get_texture (cursor), - gdk_cursor_get_hotspot_x (cursor), - gdk_cursor_get_hotspot_y (cursor)); + win32hcursor = gdk_win32hcursor_create_for_texture (display, + gdk_cursor_get_texture (cursor), + gdk_cursor_get_hotspot_x (cursor), + gdk_cursor_get_hotspot_y (cursor)); - if (hcursor != NULL) + if (win32hcursor != NULL) { - g_object_weak_ref (G_OBJECT (cursor), gdk_win32_cursor_remove_from_cache, display); - g_hash_table_insert (win32_display->cursors, cursor, hcursor); - return hcursor; + g_hash_table_insert (win32_display->cursors, cursor, win32hcursor); + + return win32hcursor; } - - if (gdk_cursor_get_fallback (cursor)) - return gdk_win32_display_get_hcursor (display, gdk_cursor_get_fallback (cursor)); + + fallback = gdk_cursor_get_fallback (cursor); + + if (fallback) + return gdk_win32_display_get_win32hcursor (display, fallback); return NULL; } diff --git a/gdk/win32/gdkdevice-virtual.c b/gdk/win32/gdkdevice-virtual.c index ce1e0ab5c5..0826ee3013 100644 --- a/gdk/win32/gdkdevice-virtual.c +++ b/gdk/win32/gdkdevice-virtual.c @@ -89,21 +89,27 @@ gdk_device_virtual_get_state (GdkDevice *device, } static void -gdk_device_virtual_set_surface_cursor (GdkDevice *device, - GdkSurface *window, - GdkCursor *cursor) +gdk_device_virtual_set_surface_cursor (GdkDevice *device, + GdkSurface *window, + GdkCursor *cursor) { - if (cursor != NULL) - { - GdkDisplay *display = gdk_surface_get_display (window); - HCURSOR hcursor = NULL; + GdkDisplay *display = gdk_surface_get_display (window); + GdkWin32HCursor *win32_hcursor = NULL; - if (display != NULL) - hcursor = gdk_win32_display_get_hcursor (display, cursor); + if (cursor == NULL) + cursor = gdk_cursor_new_from_name ("default", NULL); - if (hcursor != NULL) - SetCursor (hcursor); - } + if (display != NULL) + win32_hcursor = gdk_win32_display_get_win32hcursor (GDK_WIN32_DISPLAY (display), cursor); + + /* This is correct because the code up the stack already + * checked that cursor is currently inside this window, + * and wouldn't have called this function otherwise. + */ + if (win32_hcursor != NULL) + SetCursor (gdk_win32_hcursor_get_handle (win32_hcursor)); + + g_set_object (&GDK_SURFACE_IMPL_WIN32 (window->impl)->cursor, win32_hcursor); } static void @@ -142,21 +148,15 @@ gdk_device_virtual_grab (GdkDevice *device, GdkCursor *cursor, guint32 time_) { - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD) { + GdkWin32HCursor *win32_hcursor; GdkWin32Display *display = GDK_WIN32_DISPLAY (gdk_device_get_display (device)); - if (display->grab_cursor != NULL) - { - if (GetCursor () == g_hash_table_lookup (display->cursors, display->grab_cursor)) - SetCursor (NULL); - } - - g_set_object (&display->grab_cursor, cursor); + win32_hcursor = gdk_win32_display_get_win32hcursor (display, cursor); + g_set_object (&display->grab_cursor, win32_hcursor); if (display->grab_cursor != NULL) - SetCursor (g_hash_table_lookup (display->cursors, display->grab_cursor)); + SetCursor (gdk_win32_hcursor_get_handle (display->grab_cursor)); else SetCursor (LoadCursor (NULL, IDC_ARROW)); @@ -183,14 +183,7 @@ gdk_device_virtual_ungrab (GdkDevice *device, if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD) { - if (win32_display->grab_cursor != NULL) - { - if (GetCursor () == g_hash_table_lookup (win32_display->cursors, win32_display->grab_cursor)) - SetCursor (NULL); - } - g_clear_object (&win32_display->grab_cursor); - ReleaseCapture (); } diff --git a/gdk/win32/gdkdisplay-win32.h b/gdk/win32/gdkdisplay-win32.h index 76e4544981..a4d9cc7378 100644 --- a/gdk/win32/gdkdisplay-win32.h +++ b/gdk/win32/gdkdisplay-win32.h @@ -23,6 +23,7 @@ #define __GDK_DISPLAY__WIN32_H__ #include "gdkwin32screen.h" +#include "gdkwin32cursor.h" /* Define values used to set DPI-awareness */ typedef enum _GdkWin32ProcessDpiAwareness { @@ -98,9 +99,17 @@ struct _GdkWin32Display GdkWin32ShcoreFuncs shcore_funcs; GdkWin32User32DPIFuncs user32_dpi_funcs; - /* Cursor Items (GdkCursor->HCURSOR) */ + /* Cursor Items (GdkCursor->GdkWin32HCursor) */ GHashTable *cursors; - GdkCursor *grab_cursor; + /* The cursor that is used by current grab (if any) */ + GdkWin32HCursor *grab_cursor; + /* HCURSOR -> GdkWin32HCursorTableEntry */ + GHashTable *cursor_reftable; + /* ID of the idle callback scheduled to destroy cursors */ + guint idle_cursor_destructor_id; + + /* A list of cursor handles slated for destruction. */ + GList *cursors_for_destruction; /* Message filters */ GList *filters; diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index 733a533888..685c5df185 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -126,6 +126,15 @@ static GSourceFuncs event_funcs = { NULL }; +/* Whenever we do an implicit grab (call SetCapture() after + * a mouse button is held down), we ref the capturing surface + * and keep that ref here. When mouse buttons are released, + * we remove the implicit grab and synthesize a crossing + * event from the grab surface to whatever surface is now + * under cursor. + */ +static GdkSurface *implicit_grab_surface = NULL; + static GdkSurface *mouse_window = NULL; static GdkSurface *mouse_window_ignored_leave = NULL; static gint current_x, current_y; @@ -2224,7 +2233,6 @@ gdk_event_translate (MSG *msg, POINT point; MINMAXINFO *mmi; HWND hwnd; - GdkCursor *cursor; BYTE key_state[256]; HIMC himc; WINDOWPOS *windowpos; @@ -2661,7 +2669,10 @@ gdk_event_translate (MSG *msg, if (pointer_grab == NULL) { SetCapture (GDK_SURFACE_HWND (window)); + g_set_object (&implicit_grab_surface, g_object_ref (window)); } + else + g_set_object (&implicit_grab_surface, NULL); generate_button_event (GDK_BUTTON_PRESS, button, window, msg); @@ -2694,15 +2705,13 @@ gdk_event_translate (MSG *msg, g_set_object (&window, find_window_for_mouse_event (window, msg)); - if (pointer_grab != NULL && pointer_grab->implicit) + if (pointer_grab == NULL && implicit_grab_surface != NULL) { gint state = build_pointer_event_state (msg); /* We keep the implicit grab until no buttons at all are held down */ if ((state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (button - 1))) == 0) { - GdkSurface *native_surface = pointer_grab->native_surface; - ReleaseCapture (); new_window = NULL; @@ -2718,16 +2727,19 @@ gdk_event_translate (MSG *msg, } synthesize_crossing_events (display, - native_surface, new_window, + implicit_grab_surface, new_window, GDK_CROSSING_UNGRAB, &msg->pt, 0, /* TODO: Set right mask */ msg->time, FALSE); + g_set_object (&implicit_grab_surface, NULL); g_set_object (&mouse_window, new_window); mouse_window_ignored_leave = NULL; } } + else + g_set_object (&implicit_grab_surface, NULL); generate_button_event (GDK_BUTTON_RELEASE, button, window, msg); @@ -3071,23 +3083,33 @@ gdk_event_translate (MSG *msg, if (grab_window == NULL && LOWORD (msg->lParam) != HTCLIENT) break; - if (grab_window != NULL) + return_val = FALSE; + + if (grab_window != NULL && + !GDK_SURFACE_DESTROYED (grab_window)) { win32_display = GDK_WIN32_DISPLAY (gdk_surface_get_display (grab_window)); if (win32_display->grab_cursor != NULL) - cursor = win32_display->grab_cursor; + { + GDK_NOTE (EVENTS, g_print (" (grab SetCursor(%p)", gdk_win32_hcursor_get_handle (win32_display->grab_cursor))); + SetCursor (gdk_win32_hcursor_get_handle (win32_display->grab_cursor)); + return_val = TRUE; + *ret_valp = TRUE; + } } - else - cursor = NULL; - if (cursor != NULL) + if (!return_val && + !GDK_SURFACE_DESTROYED (window) && + GDK_SURFACE_IMPL_WIN32 (window->impl)->cursor != NULL) { - GDK_NOTE (EVENTS, g_print (" (SetCursor(%p)", cursor)); - SetCursor (g_hash_table_lookup (win32_display->cursors, cursor)); + win32_display = GDK_WIN32_DISPLAY (gdk_surface_get_display (window)); + GDK_NOTE (EVENTS, g_print (" (window SetCursor(%p)", gdk_win32_hcursor_get_handle (GDK_SURFACE_IMPL_WIN32 (window->impl)->cursor))); + SetCursor (gdk_win32_hcursor_get_handle (GDK_SURFACE_IMPL_WIN32 (window->impl)->cursor)); return_val = TRUE; *ret_valp = TRUE; } + break; case WM_SYSMENU: diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h index 7cea29150f..28d410320d 100644 --- a/gdk/win32/gdkprivate-win32.h +++ b/gdk/win32/gdkprivate-win32.h @@ -130,13 +130,6 @@ GdkWin32Screen *GDK_SURFACE_SCREEN(GObject *win); typedef struct _GdkWin32SingleFont GdkWin32SingleFont; -struct _GdkWin32Cursor -{ - GdkCursor cursor; - - HCURSOR hcursor; -}; - struct _GdkWin32SingleFont { HFONT hfont; @@ -351,6 +344,7 @@ typedef enum GdkWin32CursorLoadType { GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_NULL = 1, GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_THIS = 2, GDK_WIN32_CURSOR_CREATE = 3, + GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_GTK = 4, } GdkWin32CursorLoadType; typedef struct _Win32Cursor Win32Cursor; diff --git a/gdk/win32/gdksurface-win32.c b/gdk/win32/gdksurface-win32.c index faee484110..9fc030395e 100644 --- a/gdk/win32/gdksurface-win32.c +++ b/gdk/win32/gdksurface-win32.c @@ -38,6 +38,7 @@ #include "gdkdisplayprivate.h" #include "gdkmonitorprivate.h" #include "gdkwin32surface.h" +#include "gdkwin32cursor.h" #include "gdkglcontext-win32.h" #include "gdkdisplay-win32.h" @@ -201,6 +202,22 @@ gdk_surface_impl_win32_init (GdkSurfaceImplWin32 *impl) impl->surface_scale = 1; } + +static void +gdk_surface_impl_win32_dispose (GObject *object) +{ + GdkSurfaceImplWin32 *surface_impl; + + g_return_if_fail (GDK_IS_SURFACE_IMPL_WIN32 (object)); + + surface_impl = GDK_SURFACE_IMPL_WIN32 (object); + + g_clear_object (&surface_impl->cursor); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + + static void gdk_surface_impl_win32_finalize (GObject *object) { @@ -5813,6 +5830,7 @@ gdk_surface_impl_win32_class_init (GdkSurfaceImplWin32Class *klass) parent_class = g_type_class_peek_parent (klass); + object_class->dispose = gdk_surface_impl_win32_dispose; object_class->finalize = gdk_surface_impl_win32_finalize; impl_class->ref_cairo_surface = gdk_win32_ref_cairo_surface; diff --git a/gdk/win32/gdksurface-win32.h b/gdk/win32/gdksurface-win32.h index c451a310df..a4a88f6948 100644 --- a/gdk/win32/gdksurface-win32.h +++ b/gdk/win32/gdksurface-win32.h @@ -26,6 +26,7 @@ #define __GDK_SURFACE_WIN32_H__ #include "gdk/win32/gdkprivate-win32.h" +#include "gdk/win32/gdkwin32cursor.h" #include "gdk/gdksurfaceimpl.h" #include "gdk/gdkcursor.h" @@ -225,6 +226,9 @@ struct _GdkSurfaceImplWin32 HICON hicon_big; HICON hicon_small; + /* The cursor that GDK set for this window via GdkDevice */ + GdkWin32HCursor *cursor; + /* When VK_PACKET sends us a leading surrogate, it's stashed here. * Later, when another VK_PACKET sends a tailing surrogate, we make up * a full unicode character from them, or discard the leading surrogate, diff --git a/gdk/win32/gdkwin32cursor.h b/gdk/win32/gdkwin32cursor.h index 19033d84ee..60d8a6ad5d 100644 --- a/gdk/win32/gdkwin32cursor.h +++ b/gdk/win32/gdkwin32cursor.h @@ -29,26 +29,57 @@ #error "Only <gdk/gdkwin32.h> can be included directly." #endif +#include <Windows.h> #include <gdk/gdk.h> +#include <gdk/win32/gdkwin32display.h> G_BEGIN_DECLS -#define GDK_TYPE_WIN32_CURSOR (gdk_win32_cursor_get_type ()) -#define GDK_WIN32_CURSOR(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WIN32_CURSOR, GdkWin32Cursor)) -#define GDK_WIN32_CURSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WIN32_CURSOR, GdkWin32CursorClass)) -#define GDK_IS_WIN32_CURSOR(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WIN32_CURSOR)) -#define GDK_IS_WIN32_CURSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WIN32_CURSOR)) -#define GDK_WIN32_CURSOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WIN32_CURSOR, GdkWin32CursorClass)) +typedef struct _GdkWin32HCursor GdkWin32HCursor; +typedef struct _GdkWin32HCursorClass GdkWin32HCursorClass; -#ifdef GDK_COMPILATION -typedef struct _GdkWin32Cursor GdkWin32Cursor; +#define GDK_TYPE_WIN32_HCURSOR (gdk_win32_hcursor_get_type()) +#define GDK_WIN32_HCURSOR(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WIN32_HCURSOR, GdkWin32HCursor)) +#define GDK_WIN32_HCURSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WIN32_HCURSOR, GdkWin32HCursorClass)) +#define GDK_IS_WIN32_HCURSOR(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WIN32_HCURSOR)) +#define GDK_IS_WIN32_HCURSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WIN32_HCURSOR)) +#define GDK_WIN32_HCURSOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WIN32_HCURSOR, GdkWin32HCursorClass)) + +GDK_AVAILABLE_IN_ALL +GType gdk_win32_hcursor_get_type (void); + +struct _GdkWin32HCursorFake +{ + GObject parent_instance; + HCURSOR readonly_handle; +}; + +#define gdk_win32_hcursor_get_handle_fast(x) (((struct _GdkWin32HCursorFake *) x)->readonly_handle) + +#if defined (GDK_COMPILATION) +#define gdk_win32_hcursor_get_handle gdk_win32_hcursor_get_handle_fast #else -typedef GdkCursor GdkWin32Cursor; +GDK_AVAILABLE_IN_ALL +HCURSOR gdk_win32_hcursor_get_handle (GdkWin32HCursor *cursor); #endif -typedef struct _GdkWin32CursorClass GdkWin32CursorClass; GDK_AVAILABLE_IN_ALL -GType gdk_win32_cursor_get_type (void); +GdkWin32HCursor *gdk_win32_hcursor_new (GdkWin32Display *display, + HCURSOR handle, + gboolean destroyable); + +GDK_AVAILABLE_IN_ALL +GdkWin32HCursor *gdk_win32_display_get_win32hcursor (GdkWin32Display *display, + GdkCursor *cursor); + +GDK_AVAILABLE_IN_ALL +void _gdk_win32_display_hcursor_ref (GdkWin32Display *display, + HCURSOR handle, + gboolean destroyable); + +GDK_AVAILABLE_IN_ALL +void _gdk_win32_display_hcursor_unref (GdkWin32Display *display, + HCURSOR handle); G_END_DECLS diff --git a/gdk/win32/gdkwin32display.h b/gdk/win32/gdkwin32display.h index fb1aecb6ef..75acc4f86b 100644 --- a/gdk/win32/gdkwin32display.h +++ b/gdk/win32/gdkwin32display.h @@ -57,10 +57,6 @@ void gdk_win32_display_set_cursor_theme (GdkDisplay *display, const gchar *name, gint size); -GDK_AVAILABLE_IN_ALL -HCURSOR gdk_win32_display_get_hcursor (GdkDisplay *display, - GdkCursor *cursor); - /** * GdkWin32MessageFilterReturn: * @GDK_WIN32_MESSAGE_FILTER_CONTINUE: event not handled, continue processing. |