Commit ef01e6ee authored by LRN's avatar LRN
Browse files

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
parent 38b4c8d1
......@@ -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);
}
......
......@@ -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__ */
......@@ -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 */
......
......@@ -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);
......
......@@ -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__ */
......@@ -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;
}
......@@ -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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment