GDK W32: Adapt to event filter removal
authorРуслан Ижбулатов <lrn1986@gmail.com>
Sat, 24 Mar 2018 16:39:13 +0000 (16:39 +0000)
committerРуслан Ижбулатов <lrn1986@gmail.com>
Thu, 29 Mar 2018 17:43:55 +0000 (17:43 +0000)
Add a new W32 backend-specific message filtering mechanism.
Works roughly the same way old event filtering did, but without
events (events are GDK/X11 concept that never really made sense
on W32), so there's no functionality for 'altering' events being
emitted. If an event needs to be emitted in response to a message
do it yourself.

Implemented like this, it should give better performance than
if we were to use GLib signals for this, since W32 sends a LOT
of messages (unlike X11, which doesn't send events as often)
all the time, and invoking the signal machinery on *each* message
would probably be bad.

https://bugzilla.gnome.org/show_bug.cgi?id=773299

gdk/win32/gdkdisplay-win32.c
gdk/win32/gdkdisplay-win32.h
gdk/win32/gdkdrop-win32.c
gdk/win32/gdkevents-win32.c
gdk/win32/gdkwin32display.h
gtk/gtkimcontextime.c
gtk/gtkwin32theme.c

index b8d3bff7e8a2dc079c7a6bfd44cc9b1332f9e65c..50d6fad09813fbc593a0a738a4c57d7b54c0f5a4 100644 (file)
 
 static int debug_indent = 0;
 
+/**
+ * gdk_win32_display_add_filter:
+ * @display: a #GdkWin32Display
+ * @function: filter callback
+ * @data: data to pass to filter callback
+ *
+ * Adds an event filter to @window, allowing you to intercept messages
+ * before they reach GDK. This is a low-level operation and makes it
+ * easy to break GDK and/or GTK+, so you have to know what you're
+ * doing.
+ **/
+void
+gdk_win32_display_add_filter (GdkWin32Display           *display,
+                              GdkWin32MessageFilterFunc  function,
+                              gpointer                   data)
+{
+  GList *tmp_list;
+  GdkWin32MessageFilter *filter;
+
+  g_return_if_fail (GDK_IS_WIN32_DISPLAY (display));
+
+  tmp_list = display->filters;
+
+  for (tmp_list = display->filters; tmp_list; tmp_list = tmp_list->next)
+    {
+      filter = (GdkWin32MessageFilter *) tmp_list->data;
+
+      if ((filter->function == function) && (filter->data == data))
+        {
+          filter->ref_count++;
+          return;
+        }
+    }
+
+  filter = g_new (GdkWin32MessageFilter, 1);
+  filter->function = function;
+  filter->data = data;
+  filter->ref_count = 1;
+  filter->removed = FALSE;
+
+  display->filters = g_list_append (display->filters, filter);
+}
+
+/**
+ * _gdk_win32_message_filter_unref:
+ * @display: A #GdkWin32Display
+ * @filter: A message filter
+ *
+ * Release a reference to @filter.  Note this function may
+ * mutate the list storage, so you need to handle this
+ * if iterating over a list of filters.
+ */
+void
+_gdk_win32_message_filter_unref (GdkWin32Display       *display,
+                                GdkWin32MessageFilter *filter)
+{
+  GList **filters;
+  GList *tmp_list;
+
+  filters = &display->filters;
+
+  tmp_list = *filters;
+  while (tmp_list)
+    {
+      GdkWin32MessageFilter *iter_filter = tmp_list->data;
+      GList *node;
+
+      node = tmp_list;
+      tmp_list = tmp_list->next;
+
+      if (iter_filter != filter)
+       continue;
+
+      g_assert (iter_filter->ref_count > 0);
+
+      filter->ref_count--;
+      if (filter->ref_count != 0)
+       continue;
+
+      *filters = g_list_remove_link (*filters, node);
+      g_free (filter);
+      g_list_free_1 (node);
+    }
+}
+
+/**
+ * gdk_win32_display_remove_filter:
+ * @display: A #GdkWin32Display
+ * @function: previously-added filter function
+ * @data: user data for previously-added filter function
+ *
+ * Remove a filter previously added with gdk_win32_display_add_filter().
+ */
+void
+gdk_win32_display_remove_filter (GdkWin32Display           *display,
+                                 GdkWin32MessageFilterFunc  function,
+                                 gpointer                   data)
+{
+  GList *tmp_list;
+  GdkWin32MessageFilter *filter;
+
+  g_return_if_fail (GDK_IS_WIN32_DISPLAY (display));
+
+  tmp_list = display->filters;
+
+  while (tmp_list)
+    {
+      filter = (GdkWin32MessageFilter *) tmp_list->data;
+      tmp_list = tmp_list->next;
+
+      if ((filter->function == function) && (filter->data == data))
+        {
+          filter->removed = TRUE;
+
+          _gdk_win32_message_filter_unref (display, filter);
+
+          return;
+        }
+    }
+}
+
 static GdkMonitor *
 _gdk_win32_display_find_matching_monitor (GdkWin32Display *win32_display,
                                           GdkMonitor      *needle)
@@ -582,6 +703,9 @@ gdk_win32_display_finalize (GObject *object)
 
   g_ptr_array_free (display_win32->monitors, TRUE);
 
+  while (display_win32->filters)
+    _gdk_win32_message_filter_unref (display_win32, display_win32->filters->data);
+
   G_OBJECT_CLASS (gdk_win32_display_parent_class)->finalize (object);
 }
 
index 3b7376003656dffec4a8e9ac6355fdf5800ead28..76e45449815bd7819d89f487a221665c21ae912e 100644 (file)
@@ -101,6 +101,9 @@ struct _GdkWin32Display
   /* Cursor Items (GdkCursor->HCURSOR) */
   GHashTable *cursors;
   GdkCursor *grab_cursor;
+
+  /* Message filters */
+  GList *filters;
 };
 
 struct _GdkWin32DisplayClass
@@ -119,4 +122,14 @@ guint      _gdk_win32_display_get_monitor_scale_factor (GdkWin32Display *win32_d
                                                         HWND             hwnd,
                                                         gint             *dpi);
 
+typedef struct _GdkWin32MessageFilter GdkWin32MessageFilter;
+
+struct _GdkWin32MessageFilter
+{
+  GdkWin32MessageFilterFunc function;
+  gpointer data;
+  gboolean removed;
+  guint ref_count;
+};
+
 #endif /* __GDK_DISPLAY__WIN32_H__ */
index 4fd1f7896cf4f92f3bb97b4211e0860835ab563b..c88fdd4a39441d23397c5e10bfe6fc7cdca32ed7 100644 (file)
@@ -697,27 +697,32 @@ close_it (gpointer data)
 
 #endif
 
-static GdkFilterReturn
-gdk_dropfiles_filter (GdkXEvent *xev,
-                      GdkEvent  *event,
-                      gpointer   data)
+static GdkWin32MessageFilterReturn
+gdk_dropfiles_filter (GdkWin32Display *display,
+                      MSG             *msg,
+                      gint            *ret_valp,
+                      gpointer         data)
 {
+  GdkSurface *window;
   GdkDragContext *context;
   GdkWin32DropContext *context_win32;
+  GdkEvent *event;
   GString *result;
-  MSG *msg = (MSG *) xev;
   HANDLE hdrop;
   POINT pt;
   gint nfiles, i;
   gchar *fileName, *linkedFile;
 
-  if (msg->message == WM_DROPFILES)
-    {
+  if (msg->message != WM_DROPFILES)
+    return GDK_WIN32_MESSAGE_FILTER_CONTINUE;
+
       GDK_NOTE (DND, g_print ("WM_DROPFILES: %p\n", msg->hwnd));
 
-      context = gdk_drop_context_new (gdk_surface_get_display (event->any.surface),
+      window = gdk_win32_handle_table_lookup (msg->hwnd);
+
+      context = gdk_drop_context_new (display,
                                       NULL,
-                                      event->any.surface,
+                                      window,
                                       GDK_ACTION_COPY,
                                       GDK_DRAG_PROTO_WIN32_DROPFILES);
       context_win32 = GDK_WIN32_DROP_CONTEXT (context);
@@ -730,14 +735,18 @@ gdk_dropfiles_filter (GdkXEvent *xev,
 
       context->suggested_action = GDK_ACTION_COPY;
       current_dest_drag = context;
-      event->any.type = GDK_DROP_START;
-      event->dnd.context = current_dest_drag;
-      gdk_event_set_device (event, gdk_drag_context_get_device (current_dest_drag));
 
       hdrop = (HANDLE) msg->wParam;
       DragQueryPoint (hdrop, &pt);
       ClientToScreen (msg->hwnd, &pt);
 
+      event = gdk_event_new (GDK_DROP_START);
+
+      event->any.send_event = FALSE;
+      g_set_object (&event->dnd.context, context);
+      g_set_object (&event->any.surface, window);
+      gdk_event_set_display (event, display);
+      gdk_event_set_device (event, gdk_drag_context_get_device (context));
       event->dnd.x_root = pt.x / context_win32->scale + _gdk_offset_x;
       event->dnd.y_root = pt.y / context_win32->scale + _gdk_offset_y;
       event->dnd.time = _gdk_win32_get_next_tick (msg->time);
@@ -813,14 +822,25 @@ gdk_dropfiles_filter (GdkXEvent *xev,
           g_string_append (result, "\015\012");
         }
 
+      /* FIXME: this call is currently a no-op, but it should
+       * stash the string somewhere, and later produce it,
+       * maybe in response to gdk_win32_drop_context_read_async()?
+       */
       _gdk_dropfiles_store (result->str);
       g_string_free (result, FALSE);
 
+      GDK_NOTE (EVENTS, _gdk_win32_print_event (event));
+      _gdk_event_emit (event);
+      gdk_event_free (event);
+
       DragFinish (hdrop);
-      return GDK_FILTER_TRANSLATE;
-    }
-  else
-    return GDK_FILTER_CONTINUE;
+
+  gdk_display_put_event (display, event);
+  gdk_event_free (event);
+
+  *ret_valp = 0;
+
+  return GDK_WIN32_MESSAGE_FILTER_REMOVE;
 }
 
 /* Destination side */
index dab4d7542fb5111be87763c36cc3364a74597610..bb422d91a4f95fe8dbbb16747f27373ce265bdb4 100644 (file)
@@ -1016,43 +1016,30 @@ fill_key_event_string (GdkEvent *event)
     }
 }
 
-static GdkFilterReturn
-apply_event_filters (GdkSurface  *window,
-                    MSG        *msg,
-                    GList     **filters)
+static GdkWin32MessageFilterReturn
+apply_message_filters (GdkDisplay *display,
+                       MSG        *msg,
+                       gint       *ret_valp,
+                       GList     **filters)
 {
-  GdkFilterReturn result = GDK_FILTER_CONTINUE;
-  GdkEvent *event;
-  GdkDisplay *display;
+  GdkWin32MessageFilterReturn result = GDK_WIN32_MESSAGE_FILTER_CONTINUE;
   GList *node;
   GList *tmp_list;
 
-  event = gdk_event_new (GDK_NOTHING);
-  event->any.surface = g_object_ref (window);
-  event->any.flags |= GDK_EVENT_PENDING;
-
-  display = gdk_display_get_default ();
-
-  /* I think GdkFilterFunc semantics require the passed-in event
-   * to already be in the queue. The filter func can generate
-   * more events and append them after it if it likes.
-   */
-  node = _gdk_event_queue_append (display, event);
-
   tmp_list = *filters;
   while (tmp_list)
     {
-      GdkEventFilter *filter = (GdkEventFilter *) tmp_list->data;
+      GdkWin32MessageFilter *filter = (GdkWin32MessageFilter *) tmp_list->data;
       GList *node;
 
-      if ((filter->flags & GDK_EVENT_FILTER_REMOVED) != 0)
+      if (filter->removed)
         {
           tmp_list = tmp_list->next;
           continue;
         }
 
       filter->ref_count++;
-      result = filter->function (msg, event, filter->data);
+      result = filter->function (display, msg, ret_valp, filter->data);
 
       /* get the next node after running the function since the
          function may add or remove a next node */
@@ -1067,23 +1054,10 @@ apply_event_filters (GdkSurface  *window,
           g_free (filter);
         }
 
-      if (result !=  GDK_FILTER_CONTINUE)
+      if (result != GDK_WIN32_MESSAGE_FILTER_CONTINUE)
        break;
     }
 
-  if (result == GDK_FILTER_CONTINUE || result == GDK_FILTER_REMOVE)
-    {
-      _gdk_event_queue_remove_link (display, node);
-      g_list_free_1 (node);
-      gdk_event_free (event);
-    }
-  else /* GDK_FILTER_TRANSLATE */
-    {
-      event->any.flags &= ~GDK_EVENT_PENDING;
-      fixup_event (event);
-      GDK_NOTE (EVENTS, _gdk_win32_print_event (event));
-    }
-
   return result;
 }
 
@@ -2290,22 +2264,19 @@ gdk_event_translate (MSG  *msg,
   STGMEDIUM *property_change_data;
 
   display = gdk_display_get_default ();
-  window = gdk_win32_handle_table_lookup (msg->hwnd);
+  win32_display = GDK_WIN32_DISPLAY (display);
 
-  if (_gdk_default_filters)
+  if (win32_display->filters)
     {
-      /* Apply global filters */
+      /* Apply display filters */
+      GdkWin32MessageFilterReturn result = apply_message_filters (win32_display, msg, ret_valp, &win32_display->filters);
 
-      GdkFilterReturn result = apply_event_filters (window, msg, &_gdk_default_filters);
-
-      /* If result is GDK_FILTER_CONTINUE, we continue as if nothing
-       * happened. If it is GDK_FILTER_REMOVE or GDK_FILTER_TRANSLATE,
-       * we return TRUE, and DefWindowProcW() will not be called.
-       */
-      if (result == GDK_FILTER_REMOVE || result == GDK_FILTER_TRANSLATE)
+      if (result == GDK_WIN32_MESSAGE_FILTER_REMOVE)
        return TRUE;
     }
 
+  window = gdk_win32_handle_table_lookup (msg->hwnd);
+
   if (window == NULL)
     {
       /* XXX Handle WM_QUIT here ? */
@@ -2342,19 +2313,6 @@ gdk_event_translate (MSG  *msg,
    */
 #define return GOTO_DONE_INSTEAD
 
-  if (!GDK_SURFACE_DESTROYED (window) && window->filters)
-    {
-      /* Apply per-window filters */
-
-      GdkFilterReturn result = apply_event_filters (window, msg, &window->filters);
-
-      if (result == GDK_FILTER_REMOVE || result == GDK_FILTER_TRANSLATE)
-       {
-         return_val = TRUE;
-         goto done;
-       }
-    }
-
   if (msg->message == aerosnap_message)
     _gdk_win32_surface_handle_aerosnap (gdk_surface_get_toplevel (window),
                                        (GdkWin32AeroSnapCombo) msg->wParam);
index 7e0bf6d31e2897de1f2e33c2db298583519d6782..fb1aecb6ef15f06d4c0b95b6c5485524b28d2cdf 100644 (file)
@@ -61,6 +61,50 @@ 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.
+ * @GDK_WIN32_MESSAGE_FILTER_REMOVE: event handled, terminate processing.
+ *
+ * Specifies the result of applying a #GdkWin32MessageFilterFunc to a Windows message.
+ */
+typedef enum {
+  GDK_WIN32_MESSAGE_FILTER_CONTINUE,     /* Message not handled, continue processing */
+  GDK_WIN32_MESSAGE_FILTER_REMOVE        /* Terminate processing, removing message */
+} GdkWin32MessageFilterReturn;
+
+/**
+ * GdkWin32MessageFilterFunc:
+ * @msg: the Windows message to filter.
+ * @return_value: a location to store the return value for the message
+ * @data: (closure): user data set when the filter was installed.
+ *
+ * Specifies the type of function used to filter Windows messages before they are
+ * processed by GDK Win32 backend.
+ *
+ * The @return_value must be set, if this function returns
+ * #GDK_WIN32_MESSAGE_FILTER_REMOVE, otherwise it is ignored.
+ *
+ * The event translation and message filtering are orthogonal - 
+ * if a filter wants a GDK event queued, it should do that itself.
+ *
+ * Returns: a #GdkWin32MessageFilterReturn value.
+ */
+typedef GdkWin32MessageFilterReturn (*GdkWin32MessageFilterFunc) (GdkWin32Display *display,
+                                                                  MSG             *message,
+                                                                  gint            *return_value,
+                                                                  gpointer         data);
+
+GDK_AVAILABLE_IN_ALL
+void       gdk_win32_display_add_filter          (GdkWin32Display                 *display,
+                                                  GdkWin32MessageFilterFunc        function,
+                                                  gpointer                         data);
+
+GDK_AVAILABLE_IN_ALL
+void       gdk_win32_display_remove_filter       (GdkWin32Display                 *display,
+                                                  GdkWin32MessageFilterFunc        function,
+                                                  gpointer                         data);
+
 G_END_DECLS
 
 #endif /* __GDK_WIN32_DISPLAY_H__ */
index de202faa46f3c4f4483cb469c245a8765d3ea0ac..77f3b40ed4556aa3195ebf147d2057244ad50610 100644 (file)
@@ -113,9 +113,10 @@ static void gtk_im_context_ime_set_use_preedit     (GtkIMContext   *context,
 /* GtkIMContextIME's private functions */
 static void gtk_im_context_ime_set_preedit_font (GtkIMContext    *context);
 
-static GdkFilterReturn
-gtk_im_context_ime_message_filter               (GdkXEvent       *xevent,
-                                                 GdkEvent        *event,
+static GdkWin32MessageFilterReturn
+gtk_im_context_ime_message_filter               (GdkWin32Display *display,
+                                                 MSG             *msg,
+                                                 gint            *ret_valp,
                                                  gpointer         data);
 static void get_window_position                 (GdkSurface       *win,
                                                  gint            *x,
@@ -661,8 +662,8 @@ gtk_im_context_ime_focus_in (GtkIMContext *context)
   toplevel = gdk_surface_get_toplevel (context_ime->client_surface);
   if (GDK_IS_SURFACE (toplevel))
     {
-      gdk_surface_add_filter (toplevel,
-                             gtk_im_context_ime_message_filter, context_ime);
+      gdk_win32_display_add_filter (gdk_surface_get_display (toplevel),
+                                    gtk_im_context_ime_message_filter, context_ime);
       context_ime->toplevel = toplevel;
     }
   else
@@ -783,9 +784,9 @@ gtk_im_context_ime_focus_out (GtkIMContext *context)
   toplevel = gdk_surface_get_toplevel (context_ime->client_surface);
   if (GDK_IS_SURFACE (toplevel))
     {
-      gdk_surface_remove_filter (toplevel,
-                                gtk_im_context_ime_message_filter,
-                                context_ime);
+      gdk_win32_display_remove_filter (gdk_surface_get_display (toplevel),
+                                       gtk_im_context_ime_message_filter,
+                                       context_ime);
       context_ime->toplevel = NULL;
     }
   else
@@ -983,17 +984,17 @@ ERROR_OUT:
 }
 
 
-static GdkFilterReturn
-gtk_im_context_ime_message_filter (GdkXEvent *xevent,
-                                   GdkEvent  *event,
-                                   gpointer   data)
+static GdkWin32MessageFilterReturn
+gtk_im_context_ime_message_filter (GdkWin32Display *display,
+                                   MSG             *msg,
+                                   gint            *ret_valp,
+                                   gpointer         data)
 {
   GtkIMContext *context;
   GtkIMContextIME *context_ime;
   HWND hwnd;
   HIMC himc;
-  MSG *msg = (MSG *) xevent;
-  GdkFilterReturn retval = GDK_FILTER_CONTINUE;
+  GdkWin32MessageFilterReturn retval = GDK_WIN32_MESSAGE_FILTER_CONTINUE;
 
   g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (data), retval);
 
@@ -1002,11 +1003,16 @@ gtk_im_context_ime_message_filter (GdkXEvent *xevent,
   if (!context_ime->focus)
     return retval;
 
+  if (gdk_win32_surface_get_impl_hwnd (context_ime->toplevel) != msg->hwnd)
+    return retval;
+
   hwnd = gdk_win32_surface_get_impl_hwnd (context_ime->client_surface);
   himc = ImmGetContext (hwnd);
   if (!himc)
     return retval;
 
+  *ret_valp = 0;
+
   switch (msg->message)
     {
     case WM_IME_COMPOSITION:
@@ -1063,11 +1069,11 @@ gtk_im_context_ime_message_filter (GdkXEvent *xevent,
               }
 
             if (context_ime->commit_string)
-              retval = TRUE;
+              retval = GDK_WIN32_MESSAGE_FILTER_REMOVE;
           }
 
         if (context_ime->use_preedit)
-          retval = TRUE;
+          retval = GDK_WIN32_MESSAGE_FILTER_REMOVE;
         break;
       }
 
@@ -1076,7 +1082,7 @@ gtk_im_context_ime_message_filter (GdkXEvent *xevent,
       gtk_im_context_ime_set_cursor_location (context, NULL);
       g_signal_emit_by_name (context, "preedit-start");
       if (context_ime->use_preedit)
-        retval = TRUE;
+        retval = GDK_WIN32_MESSAGE_FILTER_REMOVE;
       break;
 
     case WM_IME_ENDCOMPOSITION:
@@ -1092,7 +1098,7 @@ gtk_im_context_ime_message_filter (GdkXEvent *xevent,
         }
 
       if (context_ime->use_preedit)
-        retval = TRUE;
+        retval = GDK_WIN32_MESSAGE_FILTER_REMOVE;
       break;
 
     case WM_IME_NOTIFY:
@@ -1161,26 +1167,5 @@ cb_client_widget_hierarchy_changed (GtkWidget       *widget,
   if (context_ime->toplevel == new_toplevel)
     return;
 
-  /* remove filter from old toplevel */
-  if (GDK_IS_SURFACE (context_ime->toplevel))
-    {
-      gdk_surface_remove_filter (context_ime->toplevel,
-                                gtk_im_context_ime_message_filter,
-                                context_ime);
-    }
-  else
-    {
-    }
-
-  /* add filter to new toplevel */
-  if (GDK_IS_SURFACE (new_toplevel))
-    {
-      gdk_surface_add_filter (new_toplevel,
-                             gtk_im_context_ime_message_filter, context_ime);
-    }
-  else
-    {
-    }
-
   context_ime->toplevel = new_toplevel;
 }
index 085389b690b4ca83c3e4c4465d7011ee3709c16e..06f14496ddf2a1be6d1ae683b354a3c912ebd8f6 100644 (file)
@@ -147,22 +147,18 @@ gtk_win32_theme_equal (GtkWin32Theme *theme1,
 
 #ifdef G_OS_WIN32
 
-static GdkFilterReturn
-invalidate_win32_themes (GdkXEvent *xevent,
-                         GdkEvent  *event,
-                         gpointer   unused)
+static GdkWin32MessageFilterReturn
+invalidate_win32_themes (GdkWin32Display *display,
+                         MSG             *msg,
+                         gint            *ret_valp,
+                         gpointer         data)
 {
   GHashTableIter iter;
   gboolean theme_was_open = FALSE;
   gpointer theme;
-  MSG *msg;
 
-  if (!GDK_IS_WIN32_SURFACE (event->any.surface))
-    return GDK_FILTER_CONTINUE;
-
-  msg = (MSG *) xevent;
   if (msg->message != WM_THEMECHANGED)
-    return GDK_FILTER_CONTINUE;
+    return GDK_WIN32_MESSAGE_FILTER_CONTINUE;
 
   g_hash_table_iter_init (&iter, themes_by_class);
   while (g_hash_table_iter_next (&iter, NULL, &theme))
@@ -170,9 +166,9 @@ invalidate_win32_themes (GdkXEvent *xevent,
       theme_was_open |= gtk_win32_theme_close (theme);
     }
   if (theme_was_open)
-    gtk_style_context_reset_widgets (gdk_surface_get_display (event->any.surface));
+    gtk_style_context_reset_widgets (display);
 
-  return GDK_FILTER_CONTINUE;
+  return GDK_WIN32_MESSAGE_FILTER_CONTINUE;
 }
 
 static void
@@ -233,7 +229,7 @@ gtk_win32_theme_init (void)
       use_xp_theme = FALSE;
     }
 
-  gdk_surface_add_filter (NULL, invalidate_win32_themes, NULL);
+  gdk_win32_display_add_filter (gdk_display_get_default (), invalidate_win32_themes, NULL);
 }
 
 static HTHEME