diff --git a/src/core/delete.c b/src/core/delete.c index bd4e96e96..90a35f33b 100644 --- a/src/core/delete.c +++ b/src/core/delete.c @@ -43,14 +43,11 @@ static void meta_window_present_delete_dialog (MetaWindow *window, guint32 timestamp); static void -delete_ping_reply_func (MetaDisplay *display, - Window xwindow, +delete_ping_reply_func (MetaWindow *window, guint32 timestamp, void *user_data) { - meta_topic (META_DEBUG_PING, - "Got reply to delete ping for %s\n", - ((MetaWindow*)user_data)->desc); + meta_topic (META_DEBUG_PING, "Got reply to delete ping for %s\n", window->desc); /* we do nothing */ } @@ -68,12 +65,10 @@ dialog_exited (GPid pid, int status, gpointer user_data) } static void -delete_ping_timeout_func (MetaDisplay *display, - Window xwindow, +delete_ping_timeout_func (MetaWindow *window, guint32 timestamp, void *user_data) { - MetaWindow *window = user_data; char *window_title; gchar *window_content, *tmp; GPid dialog_pid; @@ -137,12 +132,11 @@ void meta_window_check_alive (MetaWindow *window, guint32 timestamp) { - meta_display_ping_window (window->display, - window, - timestamp, - delete_ping_reply_func, - delete_ping_timeout_func, - window); + meta_window_ping (window, + timestamp, + delete_ping_reply_func, + delete_ping_timeout_func, + NULL); } void diff --git a/src/core/display-private.h b/src/core/display-private.h index 79d281b22..169682ee8 100644 --- a/src/core/display-private.h +++ b/src/core/display-private.h @@ -57,11 +57,6 @@ typedef struct _MetaWindowPropHooks MetaWindowPropHooks; typedef struct MetaEdgeResistanceData MetaEdgeResistanceData; -typedef void (* MetaWindowPingFunc) (MetaDisplay *display, - Window xwindow, - guint32 timestamp, - gpointer user_data); - typedef enum { META_LIST_DEFAULT = 0, /* normal windows */ META_LIST_INCLUDE_OVERRIDE_REDIRECT = 1 << 0, /* normal and O-R */ @@ -182,7 +177,7 @@ struct _MetaDisplay guint32 window_sequence_counter; /* Pings which we're waiting for a reply from */ - GSList *pending_pings; + GHashTable *pending_pings; /* Pending focus change */ guint focus_timeout_id; @@ -448,15 +443,6 @@ void meta_display_retheme_all (void); void meta_display_set_cursor_theme (const char *theme, int size); -void meta_display_ping_window (MetaDisplay *display, - MetaWindow *window, - guint32 timestamp, - MetaWindowPingFunc ping_reply_func, - MetaWindowPingFunc ping_timeout_func, - void *user_data); -gboolean meta_display_window_has_pending_pings (MetaDisplay *display, - MetaWindow *window); - int meta_resize_gravity_from_grab_op (MetaGrabOp op); gboolean meta_grab_op_is_moving (MetaGrabOp op); diff --git a/src/core/display.c b/src/core/display.c index 6d1b24533..2989648be 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -83,41 +83,6 @@ g == META_GRAB_OP_KEYBOARD_ESCAPING_DOCK || \ g == META_GRAB_OP_KEYBOARD_ESCAPING_GROUP) -/* - * SECTION:pings - * - * Sometimes we want to see whether a window is responding, - * so we send it a "ping" message and see whether it sends us back a "pong" - * message within a reasonable time. Here we have a system which lets us - * nominate one function to be called if we get the pong in time and another - * function if we don't. The system is rather more complicated than it needs - * to be, since we only ever use it to destroy windows which are asked to - * close themselves and don't do so within a reasonable amount of time, and - * therefore we always use the same callbacks. It's possible that we might - * use it for other things in future, or on the other hand we might decide - * that we're never going to do so and simplify it a bit. - */ - -/** - * MetaPingData: - * - * Describes a ping on a window. When we send a ping to a window, we build - * one of these structs, and it eventually gets passed to the timeout function - * or to the function which handles the response from the window. If the window - * does or doesn't respond to the ping, we use this information to deal with - * these facts; we have a handler function for each. - */ -typedef struct -{ - MetaDisplay *display; - Window xwindow; - guint32 timestamp; - MetaWindowPingFunc ping_reply_func; - MetaWindowPingFunc ping_timeout_func; - void *user_data; - guint ping_timeout_id; -} MetaPingData; - G_DEFINE_TYPE(MetaDisplay, meta_display, G_TYPE_OBJECT); /* Signals */ @@ -172,8 +137,6 @@ static guint32 event_get_time (MetaDisplay *display, XEvent *event); static void process_request_frame_extents (MetaDisplay *display, XEvent *event); -static void process_pong_message (MetaDisplay *display, - XEvent *event); static void process_selection_request (MetaDisplay *display, XEvent *event); static void process_selection_clear (MetaDisplay *display, @@ -321,62 +284,6 @@ meta_display_class_init (MetaDisplayClass *klass) G_PARAM_READABLE)); } - -/** - * ping_data_free: - * - * Destructor for #MetaPingData structs. Will destroy the - * event source for the struct as well. - */ -static void -ping_data_free (MetaPingData *ping_data) -{ - /* Remove the timeout */ - if (ping_data->ping_timeout_id != 0) - g_source_remove (ping_data->ping_timeout_id); - - g_free (ping_data); -} - -/** - * remove_pending_pings_for_window: - * @display: The display the window appears on - * @xwindow: The X ID of the window whose pings we should remove - * - * Frees every pending ping structure for the given X window on the - * given display. This means that we also destroy the timeouts. - */ -static void -remove_pending_pings_for_window (MetaDisplay *display, Window xwindow) -{ - GSList *tmp; - GSList *dead; - - /* could obviously be more efficient, don't care */ - - /* build list to be removed */ - dead = NULL; - for (tmp = display->pending_pings; tmp; tmp = tmp->next) - { - MetaPingData *ping_data = tmp->data; - - if (ping_data->xwindow == xwindow) - dead = g_slist_prepend (dead, ping_data); - } - - /* remove what we found */ - for (tmp = dead; tmp; tmp = tmp->next) - { - MetaPingData *ping_data = tmp->data; - - display->pending_pings = g_slist_remove (display->pending_pings, ping_data); - ping_data_free (ping_data); - } - - g_slist_free (dead); -} - - #ifdef HAVE_STARTUP_NOTIFICATION static void sn_error_trap_push (SnDisplay *sn_display, @@ -528,7 +435,6 @@ meta_display_open (void) the_display->server_grab_count = 0; the_display->display_opening = TRUE; - the_display->pending_pings = NULL; the_display->autoraise_timeout_id = 0; the_display->autoraise_window = NULL; the_display->focus_window = NULL; @@ -600,6 +506,8 @@ meta_display_open (void) the_display->xids = g_hash_table_new (meta_unsigned_long_hash, meta_unsigned_long_equal); + the_display->pending_pings = g_hash_table_new (meta_unsigned_long_hash, + meta_unsigned_long_equal); the_display->wayland_windows = g_hash_table_new (NULL, NULL); i = 0; @@ -2887,7 +2795,13 @@ handle_other_xevent (MetaDisplay *display, if ((Atom)event->xclient.data.l[0] == display->atom__NET_WM_PING) { - process_pong_message (display, event); + guint32 timestamp = event->xclient.data.l[1]; + window = g_hash_table_lookup (display->pending_pings, ×tamp); + + if (window) + meta_window_pong (window, timestamp); + else + meta_verbose ("Received invalid _NET_WM_PING for unknown timestamp %d\n", timestamp); /* We don't want ping reply events going into * the GTK+ event loop because gtk+ will treat @@ -3794,9 +3708,6 @@ meta_display_unregister_x_window (MetaDisplay *display, g_return_if_fail (g_hash_table_lookup (display->xids, &xwindow) != NULL); g_hash_table_remove (display->xids, &xwindow); - - /* Remove any pending pings */ - remove_pending_pings_for_window (display, xwindow); } void @@ -4644,120 +4555,6 @@ meta_set_syncing (gboolean setting) } } -/* - * How long, in milliseconds, we should wait after pinging a window - * before deciding it's not going to get back to us. - */ -#define PING_TIMEOUT_DELAY 5000 - -/** - * meta_display_ping_timeout: - * @data: All the information about this ping. It is a #MetaPingData - * cast to a #gpointer in order to be passable to a timeout function. - * This function will also free this parameter. - * - * Does whatever it is we decided to do when a window didn't respond - * to a ping. We also remove the ping from the display's list of - * pending pings. This function is called by the event loop when the timeout - * times out which we created at the start of the ping. - * - * Returns: Always returns %FALSE, because this function is called as a - * timeout and we don't want to run the timer again. - */ -static gboolean -meta_display_ping_timeout (gpointer data) -{ - MetaPingData *ping_data; - - ping_data = data; - - ping_data->ping_timeout_id = 0; - - meta_topic (META_DEBUG_PING, - "Ping %u on window %lx timed out\n", - ping_data->timestamp, ping_data->xwindow); - - (* ping_data->ping_timeout_func) (ping_data->display, ping_data->xwindow, - ping_data->timestamp, ping_data->user_data); - - ping_data->display->pending_pings = - g_slist_remove (ping_data->display->pending_pings, - ping_data); - ping_data_free (ping_data); - - return FALSE; -} - -/** - * meta_display_ping_window: - * @display: The #MetaDisplay that the window is on - * @window: The #MetaWindow to send the ping to - * @timestamp: The timestamp of the ping. Used for uniqueness. - * Cannot be CurrentTime; use a real timestamp! - * @ping_reply_func: The callback to call if we get a response. - * @ping_timeout_func: The callback to call if we don't get a response. - * @user_data: Arbitrary data that will be passed to the callback - * function. (In practice it's often a pointer to - * the window.) - * - * Sends a ping request to a window. The window must respond to - * the request within a certain amount of time. If it does, we - * will call one callback; if the time passes and we haven't had - * a response, we call a different callback. The window must have - * the hint showing that it can respond to a ping; if it doesn't, - * we call the "got a response" callback immediately and return. - * This function returns straight away after setting things up; - * the callbacks will be called from the event loop. - * - * FIXME: This should probably be a method on windows, rather than displays - * for one of their windows. - * - */ -void -meta_display_ping_window (MetaDisplay *display, - MetaWindow *window, - guint32 timestamp, - MetaWindowPingFunc ping_reply_func, - MetaWindowPingFunc ping_timeout_func, - gpointer user_data) -{ - MetaPingData *ping_data; - - if (timestamp == CurrentTime) - { - meta_warning ("Tried to ping a window with CurrentTime! Not allowed.\n"); - return; - } - - if (!window->net_wm_ping) - { - if (ping_reply_func) - (* ping_reply_func) (display, window->xwindow, timestamp, user_data); - - return; - } - - ping_data = g_new (MetaPingData, 1); - ping_data->display = display; - ping_data->xwindow = window->xwindow; - ping_data->timestamp = timestamp; - ping_data->ping_reply_func = ping_reply_func; - ping_data->ping_timeout_func = ping_timeout_func; - ping_data->user_data = user_data; - ping_data->ping_timeout_id = g_timeout_add (PING_TIMEOUT_DELAY, - meta_display_ping_timeout, - ping_data); - - display->pending_pings = g_slist_prepend (display->pending_pings, ping_data); - - meta_topic (META_DEBUG_PING, - "Sending ping with timestamp %u to window %s\n", - timestamp, window->desc); - meta_window_send_icccm_message (window, - display->atom__NET_WM_PING, - timestamp); -} - static void process_request_frame_extents (MetaDisplay *display, XEvent *event) @@ -4818,91 +4615,6 @@ process_request_frame_extents (MetaDisplay *display, meta_XFree (hints); } -/** - * process_pong_message: - * @display: the display we got the pong from - * @event: the #XEvent which is a pong; we can tell which - * ping it corresponds to because it bears the - * same timestamp. - * - * Process the pong (the response message) from the ping we sent - * to the window. This involves removing the timeout, calling the - * reply handler function, and freeing memory. - */ -static void -process_pong_message (MetaDisplay *display, - XEvent *event) -{ - GSList *tmp; - guint32 timestamp = event->xclient.data.l[1]; - - meta_topic (META_DEBUG_PING, "Received a pong with timestamp %u\n", - timestamp); - - for (tmp = display->pending_pings; tmp; tmp = tmp->next) - { - MetaPingData *ping_data = tmp->data; - - if (timestamp == ping_data->timestamp) - { - meta_topic (META_DEBUG_PING, - "Matching ping found for pong %u\n", - ping_data->timestamp); - - /* Remove the ping data from the list */ - display->pending_pings = g_slist_remove (display->pending_pings, - ping_data); - - /* Remove the timeout */ - if (ping_data->ping_timeout_id != 0) - { - g_source_remove (ping_data->ping_timeout_id); - ping_data->ping_timeout_id = 0; - } - - /* Call callback */ - (* ping_data->ping_reply_func) (display, - ping_data->xwindow, - ping_data->timestamp, - ping_data->user_data); - - ping_data_free (ping_data); - - break; - } - } -} - -/** - * meta_display_window_has_pending_pings: - * @display: The #MetaDisplay of the window. - * @window: The #MetaWindow whose pings we want to know about. - * - * Finds whether a window has any pings waiting on it. - * - * FIXME: This should probably be a method on windows, rather than displays - * for one of their windows. - * - * Returns: %TRUE if there is at least one ping which has been sent - * to the window without getting a response; %FALSE otherwise. - */ -gboolean -meta_display_window_has_pending_pings (MetaDisplay *display, - MetaWindow *window) -{ - GSList *tmp; - - for (tmp = display->pending_pings; tmp; tmp = tmp->next) - { - MetaPingData *ping_data = tmp->data; - - if (ping_data->xwindow == window->xwindow) - return TRUE; - } - - return FALSE; -} - MetaGroup* get_focussed_group (MetaDisplay *display) { diff --git a/src/core/window-private.h b/src/core/window-private.h index 49dbfc7c3..d67af673b 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -466,6 +466,8 @@ struct _MetaWindow /* Bypass compositor hints */ guint bypass_compositor; + + GSList *pending_pings; }; struct _MetaWindowClass @@ -747,4 +749,16 @@ void meta_window_handle_enter (MetaWindow *window, void meta_window_set_surface_mapped (MetaWindow *window, gboolean surface_mapped); +typedef void (* MetaWindowPingFunc) (MetaWindow *window, + guint32 timestamp, + gpointer user_data); + +void meta_window_ping (MetaWindow *window, + guint32 timestamp, + MetaWindowPingFunc ping_reply_func, + MetaWindowPingFunc ping_timeout_func, + void *user_data); +void meta_window_pong (MetaWindow *window, + guint32 timestamp); + #endif diff --git a/src/core/window.c b/src/core/window.c index f8823b68b..532ae0c06 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -73,6 +73,26 @@ static int destroying_windows_disallowed = 0; +/** + * MetaPingData: + * + * Describes a ping on a window. When we send a ping to a window, we build + * one of these structs, and it eventually gets passed to the timeout function + * or to the function which handles the response from the window. If the window + * does or doesn't respond to the ping, we use this information to deal with + * these facts; we have a handler function for each. + */ +typedef struct +{ + MetaWindow *window; + guint32 timestamp; + MetaWindowPingFunc ping_reply_func; + MetaWindowPingFunc ping_timeout_func; + void *user_data; + guint ping_timeout_id; +} MetaPingData; + +static void ping_data_free (MetaPingData *ping_data); static void update_sm_hints (MetaWindow *window); static void update_net_frame_extents (MetaWindow *window); @@ -2051,6 +2071,8 @@ meta_window_unmanage (MetaWindow *window, if (window->surface) meta_wayland_surface_free (window->surface); + g_slist_free_full (window->pending_pings, (GDestroyNotify) ping_data_free); + meta_prefs_remove_listener (prefs_changed_callback, window); meta_screen_queue_check_fullscreen (window->screen); @@ -11960,3 +11982,194 @@ meta_window_set_surface_mapped (MetaWindow *window, window->surface_mapped = surface_mapped; meta_window_queue (window, META_QUEUE_CALC_SHOWING); } + +/* + * SECTION:pings + * + * Sometimes we want to see whether a window is responding, + * so we send it a "ping" message and see whether it sends us back a "pong" + * message within a reasonable time. Here we have a system which lets us + * nominate one function to be called if we get the pong in time and another + * function if we don't. The system is rather more complicated than it needs + * to be, since we only ever use it to destroy windows which are asked to + * close themselves and don't do so within a reasonable amount of time, and + * therefore we always use the same callbacks. It's possible that we might + * use it for other things in future, or on the other hand we might decide + * that we're never going to do so and simplify it a bit. + */ + +/** + * ping_data_free: + * + * Destructor for #MetaPingData structs. Will destroy the + * event source for the struct as well. + */ +static void +ping_data_free (MetaPingData *ping_data) +{ + MetaWindow *window = ping_data->window; + MetaDisplay *display = window->display; + + /* Remove the timeout */ + if (ping_data->ping_timeout_id != 0) + g_source_remove (ping_data->ping_timeout_id); + + g_hash_table_remove (display->pending_pings, &ping_data->timestamp); + + g_free (ping_data); +} + +/** + * meta_window_pong: + * @window: the window we got the pong on + * @timestamp: the timestamp that the client sent back + * + * Process the pong (the response message) from the ping we sent + * to the window. This involves removing the timeout, calling the + * reply handler function, and freeing memory. + */ +void +meta_window_pong (MetaWindow *window, + guint32 timestamp) +{ + GSList *tmp; + + meta_topic (META_DEBUG_PING, "Received a pong with timestamp %u\n", + timestamp); + + for (tmp = window->pending_pings; tmp; tmp = tmp->next) + { + MetaPingData *ping_data = tmp->data; + + if (timestamp == ping_data->timestamp) + { + meta_topic (META_DEBUG_PING, + "Matching ping found for pong %u\n", + ping_data->timestamp); + + /* Remove the ping data from the list */ + window->pending_pings = g_slist_remove (window->pending_pings, ping_data); + + /* Remove the timeout */ + if (ping_data->ping_timeout_id != 0) + { + g_source_remove (ping_data->ping_timeout_id); + ping_data->ping_timeout_id = 0; + } + + /* Call callback */ + (* ping_data->ping_reply_func) (window, + ping_data->timestamp, + ping_data->user_data); + + /* Remove the ping data from the list */ + window->pending_pings = g_slist_remove (window->pending_pings, ping_data); + + ping_data_free (ping_data); + + break; + } + } +} + +/* + * How long, in milliseconds, we should wait after pinging a window + * before deciding it's not going to get back to us. + */ +#define PING_TIMEOUT_DELAY 5000 + +/** + * ping_timeout: + * @data: All the information about this ping. It is a #MetaPingData + * cast to a #gpointer in order to be passable to a timeout function. + * This function will also free this parameter. + * + * Does whatever it is we decided to do when a window didn't respond + * to a ping. We also remove the ping from the display's list of + * pending pings. This function is called by the event loop when the timeout + * times out which we created at the start of the ping. + * + * Returns: Always returns %FALSE, because this function is called as a + * timeout and we don't want to run the timer again. + */ +static gboolean +ping_timeout (gpointer data) +{ + MetaPingData *ping_data; + + ping_data = data; + + ping_data->ping_timeout_id = 0; + + meta_topic (META_DEBUG_PING, + "Ping %u on window %s timed out\n", + ping_data->timestamp, ping_data->window->desc); + + (* ping_data->ping_timeout_func) (ping_data->window, ping_data->timestamp, ping_data->user_data); + + ping_data_free (ping_data); + + return FALSE; +} + +/** + * meta_window_ping: + * @window: The #MetaWindow to send the ping to + * @timestamp: The timestamp of the ping. Used for uniqueness. + * Cannot be CurrentTime; use a real timestamp! + * @ping_reply_func: The callback to call if we get a response. + * @ping_timeout_func: The callback to call if we don't get a response. + * @user_data: Arbitrary data that will be passed to the callback + * function. (In practice it's often a pointer to + * the window.) + * + * Sends a ping request to a window. The window must respond to + * the request within a certain amount of time. If it does, we + * will call one callback; if the time passes and we haven't had + * a response, we call a different callback. The window must have + * the hint showing that it can respond to a ping; if it doesn't, + * we call the "got a response" callback immediately and return. + * This function returns straight away after setting things up; + * the callbacks will be called from the event loop. + */ +void +meta_window_ping (MetaWindow *window, + guint32 timestamp, + MetaWindowPingFunc ping_reply_func, + MetaWindowPingFunc ping_timeout_func, + gpointer user_data) +{ + MetaPingData *ping_data; + MetaDisplay *display = window->display; + + if (timestamp == CurrentTime) + { + meta_warning ("Tried to ping a window with CurrentTime! Not allowed.\n"); + return; + } + + if (!window->net_wm_ping) + { + if (ping_reply_func) + (* ping_reply_func) (window, timestamp, user_data); + + return; + } + + ping_data = g_new (MetaPingData, 1); + ping_data->window = window; + ping_data->timestamp = timestamp; + ping_data->ping_reply_func = ping_reply_func; + ping_data->ping_timeout_func = ping_timeout_func; + ping_data->user_data = user_data; + ping_data->ping_timeout_id = g_timeout_add (PING_TIMEOUT_DELAY, ping_timeout, ping_data); + + g_hash_table_insert (display->pending_pings, &ping_data->timestamp, window); + + meta_topic (META_DEBUG_PING, + "Sending ping with timestamp %u to window %s\n", + timestamp, window->desc); + meta_window_send_icccm_message (window, + display->atom__NET_WM_PING, + timestamp); +}