From ef01e6ee52aa3b0f4776bdeb524d0967a0f3dd0f Mon Sep 17 00:00:00 2001 From: =?utf8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1?= =?utf8?q?=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= Date: Sat, 24 Mar 2018 16:39:13 +0000 Subject: [PATCH] GDK W32: Adapt to event filter removal 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 | 124 +++++++++++++++++++++++++++++++++++ gdk/win32/gdkdisplay-win32.h | 13 ++++ gdk/win32/gdkdrop-win32.c | 52 ++++++++++----- gdk/win32/gdkevents-win32.c | 76 +++++---------------- gdk/win32/gdkwin32display.h | 44 +++++++++++++ gtk/gtkimcontextime.c | 63 +++++++----------- gtk/gtkwin32theme.c | 22 +++---- 7 files changed, 267 insertions(+), 127 deletions(-) diff --git a/gdk/win32/gdkdisplay-win32.c b/gdk/win32/gdkdisplay-win32.c index b8d3bff7e8..50d6fad098 100644 --- a/gdk/win32/gdkdisplay-win32.c +++ b/gdk/win32/gdkdisplay-win32.c @@ -39,6 +39,127 @@ 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); } diff --git a/gdk/win32/gdkdisplay-win32.h b/gdk/win32/gdkdisplay-win32.h index 3b73760036..76e4544981 100644 --- a/gdk/win32/gdkdisplay-win32.h +++ b/gdk/win32/gdkdisplay-win32.h @@ -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__ */ diff --git a/gdk/win32/gdkdrop-win32.c b/gdk/win32/gdkdrop-win32.c index 4fd1f7896c..c88fdd4a39 100644 --- a/gdk/win32/gdkdrop-win32.c +++ b/gdk/win32/gdkdrop-win32.c @@ -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 */ diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index dab4d7542f..bb422d91a4 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -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); diff --git a/gdk/win32/gdkwin32display.h b/gdk/win32/gdkwin32display.h index 7e0bf6d31e..fb1aecb6ef 100644 --- a/gdk/win32/gdkwin32display.h +++ b/gdk/win32/gdkwin32display.h @@ -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__ */ diff --git a/gtk/gtkimcontextime.c b/gtk/gtkimcontextime.c index de202faa46..77f3b40ed4 100644 --- a/gtk/gtkimcontextime.c +++ b/gtk/gtkimcontextime.c @@ -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; } diff --git a/gtk/gtkwin32theme.c b/gtk/gtkwin32theme.c index 085389b690..06f14496dd 100644 --- a/gtk/gtkwin32theme.c +++ b/gtk/gtkwin32theme.c @@ -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 -- 2.30.2