From d8da6d38db4732201bd961c8cc120197fbf1b1bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1?= =?UTF-8?q?=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= Date: Thu, 29 Mar 2018 23:38:05 +0000 Subject: [PATCH 1/3] GDK W32: New cursor class Instead of now-unused GdkWin32Cursor class (a subclass of GdkCursor), add a stand-alone GdkWin32HCursor class that is a wrapper around HCURSOR handle. On creation it's given a display instance, a HCURSOR handle and a boolean that indicates whether the HCURSOR handle can or cannot be destroyed (this depends on how the handle was obtained). That information is stored in a hash table inside the GdkWin32Display singleton, each entry of that table has reference count. When the GdkWin32HCursor object is finalized, it reduces the reference count on the table entry in the GdkWin32Display. When it's created, it either adds such an entry or refs an existing one. This way two pieces of code (or the same piece of code called multiple times) that independently obtain the same HCURSOR from the OS will get to different GdkWin32HCursor instances, but GdkWin32Display will know that both use the same handle. Once the reference count reaches 0 on the table entry, it is freed and the handle (if destroyable) is put on the destruction list, and an idle destruction function is queued. If the same handle is once again registered for use before the idle destructior is invoked (this happens, for example, when an old cursor is destroyed and then replaced with a new one), the handle gets removed from the destruction list. The destructor just calls DestroyCursor() on each handle, calling SetCursor(NULL) before doing that when the handle is in use. This ensures that SetCursor(NULL) (which will cause cursor to disappear, which is bad by itself, and which will also cause flickering if the cursor is set to a non-NULL again shortly afterward) is almost never called, unless GTK messes up and keeps using a cursor beyond its lifetime. This scheme also ensures that non-destructable cursors are not destroyed. It's also possible to call _gdk_win32_display_hcursor_ref() and _gdk_win32_display_hcursor_unref() manually instead of creating GdkWin32HCursor objects, but that is not recommended. --- gdk/win32/gdkcursor-win32.c | 304 +++++++++++++++++++++++++++++++++++ gdk/win32/gdkdisplay-win32.h | 8 + gdk/win32/gdkprivate-win32.h | 7 - gdk/win32/gdkwin32cursor.h | 57 +++++-- 4 files changed, 356 insertions(+), 20 deletions(-) diff --git a/gdk/win32/gdkcursor-win32.c b/gdk/win32/gdkcursor-win32.c index 791ec00e9b..5490322e42 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) diff --git a/gdk/win32/gdkdisplay-win32.h b/gdk/win32/gdkdisplay-win32.h index 76e4544981..9190193c6b 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 { @@ -101,6 +102,13 @@ struct _GdkWin32Display /* Cursor Items (GdkCursor->HCURSOR) */ GHashTable *cursors; GdkCursor *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/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h index 7cea29150f..b1ab8c9b3e 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; 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 can be included directly." #endif +#include #include +#include 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; -#else -typedef GdkCursor GdkWin32Cursor; -#endif -typedef struct _GdkWin32CursorClass GdkWin32CursorClass; +#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_cursor_get_type (void); +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 +GDK_AVAILABLE_IN_ALL +HCURSOR gdk_win32_hcursor_get_handle (GdkWin32HCursor *cursor); +#endif + +GDK_AVAILABLE_IN_ALL +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 From a82d67bb7d9290693e8d8c503e65694bb62b44ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1?= =?UTF-8?q?=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= Date: Thu, 29 Mar 2018 23:40:32 +0000 Subject: [PATCH 2/3] GDK W32: Use the new cursor class This makes all the code use the new cursor class instead of raw HCURSOR handles. --- gdk/win32/gdkcursor-win32.c | 387 +++++++++++++++++----------------- gdk/win32/gdkdevice-virtual.c | 49 ++--- gdk/win32/gdkdisplay-win32.h | 5 +- gdk/win32/gdkprivate-win32.h | 1 + gdk/win32/gdksurface-win32.c | 18 ++ gdk/win32/gdksurface-win32.h | 4 + gdk/win32/gdkwin32display.h | 4 - 7 files changed, 234 insertions(+), 234 deletions(-) diff --git a/gdk/win32/gdkcursor-win32.c b/gdk/win32/gdkcursor-win32.c index 5490322e42..95bcb334a8 100644 --- a/gdk/win32/gdkcursor-win32.c +++ b/gdk/win32/gdkcursor-win32.c @@ -445,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; @@ -607,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); @@ -649,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, @@ -714,15 +734,10 @@ static void gdk_win32_cursor_remove_from_cache (gpointer data, GObject *cursor) { GdkDisplay *display = data; - HCURSOR hcursor; + GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display); - hcursor = g_hash_table_lookup (GDK_WIN32_DISPLAY (display)->cursors, cursor); - - if (GetCursor () == hcursor) - SetCursor (NULL); - - 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 @@ -743,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); } @@ -750,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; @@ -766,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; @@ -802,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; @@ -849,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 (); + return create_blank_win32hcursor (display); - hcursor = hcursor_from_name (display, name); + win32hcursor = win32hcursor_from_name (display, name); - /* allow to load named cursor resources linked into the executable */ - if (!hcursor) - hcursor = LoadCursor (_gdk_app_hmodule, name); + if (win32hcursor) + return win32hcursor; - if (hcursor == NULL) - return NULL; - - 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 @@ -882,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; @@ -903,72 +925,20 @@ gdk_win32_cursor_create_for_texture (GdkDisplay *display, g_object_unref (pixbuf); - return (HCURSOR)icon; + return gdk_win32_hcursor_new (display, (HCURSOR) icon, TRUE); } -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; -} - -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); @@ -980,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 (GetCursor () == win32_hcursor->readonly_handle) + SetCursor (win32hcursor_new->readonly_handle); - if (!DestroyCursor (hcursor)) - g_warning (G_STRLOC ": DestroyCursor (%p) failed: %lu", hcursor, GetLastError ()); - - 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; } @@ -1011,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 * @@ -1509,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..30fc95c884 100644 --- a/gdk/win32/gdkdevice-virtual.c +++ b/gdk/win32/gdkdevice-virtual.c @@ -89,21 +89,25 @@ 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)); } static void @@ -142,21 +146,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 +181,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 9190193c6b..a4d9cc7378 100644 --- a/gdk/win32/gdkdisplay-win32.h +++ b/gdk/win32/gdkdisplay-win32.h @@ -99,9 +99,10 @@ 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 */ diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h index b1ab8c9b3e..28d410320d 100644 --- a/gdk/win32/gdkprivate-win32.h +++ b/gdk/win32/gdkprivate-win32.h @@ -344,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/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. From 795572710c99d7612bd7d71d538e8dab60d84309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1?= =?UTF-8?q?=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= Date: Thu, 29 Mar 2018 23:42:49 +0000 Subject: [PATCH 3/3] GDK W32: Remember surface cursor, implicit surface grab This commit ensures that each GdkSurface impl remembers the cursor that GDK sets for it, and that this cursor is set each time WM_SETCURSOR is called for that sufrace's HWND. This is needed because W32, unlike X, has no per-window cursors - the cursor on W32 is a global resource, and we need to keep track of which cursor should be set when pointer is over which surface ourselves (WM_SETCURSOR exists exactly for this reason). This commit also makes GDK remember the surface that has an implicit grab (since implicit grabs are gone from the upper levels of the toolkit), and ensures that crossing events are correctly synthesized and the grab is broken when surface focus changes. This fixes a bug where opening a new window (by clicking something in some other, pre-existing window) will make that new window not get any mouse input due to the fact that the mouse-button-down event from that click caused an implicit grab on the pre-existing window, and that grab was not released afterward. --- gdk/win32/gdkdevice-virtual.c | 2 ++ gdk/win32/gdkevents-win32.c | 46 ++++++++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/gdk/win32/gdkdevice-virtual.c b/gdk/win32/gdkdevice-virtual.c index 30fc95c884..0826ee3013 100644 --- a/gdk/win32/gdkdevice-virtual.c +++ b/gdk/win32/gdkdevice-virtual.c @@ -108,6 +108,8 @@ gdk_device_virtual_set_surface_cursor (GdkDevice *device, */ 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 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: