diff --git a/src/core/delete.c b/src/core/delete.c index 058764b08..81537dfe5 100644 --- a/src/core/delete.c +++ b/src/core/delete.c @@ -22,6 +22,8 @@ #define _XOPEN_SOURCE /* for kill() */ +#define MAX_QUEUED_EVENTS 400 + #include "config.h" #include @@ -39,6 +41,8 @@ close_dialog_response_cb (MetaCloseDialog *dialog, { if (response == META_CLOSE_DIALOG_RESPONSE_FORCE_CLOSE) meta_window_kill (window); + else + meta_window_ensure_close_dialog_timeout (window); } static void @@ -57,23 +61,22 @@ meta_window_ensure_close_dialog (MetaWindow *window) } void -meta_window_set_alive (MetaWindow *window, - gboolean is_alive) +meta_window_show_close_dialog (MetaWindow *window) { - if (is_alive && window->close_dialog) - { - meta_close_dialog_hide (window->close_dialog); - } - else if (!is_alive) - { - meta_window_ensure_close_dialog (window); - meta_close_dialog_show (window->close_dialog); + meta_window_ensure_close_dialog (window); + meta_close_dialog_show (window->close_dialog); - if (window->display && - window->display->event_route == META_EVENT_ROUTE_NORMAL && - window == window->display->focus_window) - meta_close_dialog_focus (window->close_dialog); - } + if (window->display && + window->display->event_route == META_EVENT_ROUTE_NORMAL && + window == window->display->focus_window) + meta_close_dialog_focus (window->close_dialog); +} + +void +meta_window_hide_close_dialog (MetaWindow *window) +{ + if (window->close_dialog) + meta_close_dialog_hide (window->close_dialog); } void @@ -83,6 +86,21 @@ meta_window_check_alive (MetaWindow *window, meta_display_ping_window (window, timestamp); } +void +meta_window_check_alive_on_event (MetaWindow *window, + uint32_t timestamp) +{ + if (!meta_window_can_ping (window)) + return; + + meta_display_ping_window (window, timestamp); + + window->events_during_ping++; + + if (window->events_during_ping > MAX_QUEUED_EVENTS) + meta_window_set_alive (window, FALSE); +} + void meta_window_delete (MetaWindow *window, guint32 timestamp) diff --git a/src/core/display.c b/src/core/display.c index 532d024f8..83c255bf6 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -2146,6 +2146,7 @@ meta_display_ping_timeout (gpointer data) MetaDisplay *display = window->display; meta_window_set_alive (window, FALSE); + meta_window_show_close_dialog (window); ping_data->ping_timeout_id = 0; @@ -2235,6 +2236,8 @@ meta_display_ping_window (MetaWindow *window, serial, window->desc); META_WINDOW_GET_CLASS (window)->ping (window, serial); + + window->events_during_ping = 0; } /** diff --git a/src/core/events.c b/src/core/events.c index 9ecf085fd..60fb91233 100644 --- a/src/core/events.c +++ b/src/core/events.c @@ -202,7 +202,7 @@ meta_display_handle_event (MetaDisplay *display, const ClutterEvent *event) { MetaBackend *backend = meta_get_backend (); - MetaWindow *window; + MetaWindow *window = NULL; gboolean bypass_clutter = FALSE; G_GNUC_UNUSED gboolean bypass_wayland = FALSE; MetaGestureTracker *gesture_tracker; @@ -475,6 +475,10 @@ meta_display_handle_event (MetaDisplay *display, #ifdef HAVE_WAYLAND if (compositor && !bypass_wayland) { + if (window && event->type == CLUTTER_MOTION && + event->any.time != CLUTTER_CURRENT_TIME) + meta_window_check_alive_on_event (window, event->any.time); + if (meta_wayland_compositor_handle_event (compositor, event)) bypass_clutter = TRUE; } diff --git a/src/core/window-private.h b/src/core/window-private.h index 1b4323986..b4de8a60a 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -437,6 +437,9 @@ struct _MetaWindow /* whether focus should be restored on map */ guint restore_focus_on_map : 1; + /* Whether the window is alive */ + guint is_alive : 1; + /* if non-NULL, the bounds of the window frame */ cairo_region_t *frame_bounds; @@ -564,11 +567,14 @@ struct _MetaWindow } placement; guint unmanage_idle_id; + guint close_dialog_timeout_id; pid_t client_pid; gboolean has_valid_cgroup; GFile *cgroup_path; + + unsigned int events_during_ping; }; struct _MetaWindowClass @@ -877,6 +883,11 @@ void meta_window_grab_op_began (MetaWindow *window, MetaGrabOp op); void meta_window_grab_op_ended (MetaWindow *window, MetaGrabOp op); void meta_window_set_alive (MetaWindow *window, gboolean is_alive); +gboolean meta_window_get_alive (MetaWindow *window); + +void meta_window_show_close_dialog (MetaWindow *window); +void meta_window_hide_close_dialog (MetaWindow *window); +void meta_window_ensure_close_dialog_timeout (MetaWindow *window); gboolean meta_window_has_pointer (MetaWindow *window); @@ -898,4 +909,8 @@ gboolean meta_window_is_focus_async (MetaWindow *window); GFile *meta_window_get_unit_cgroup (MetaWindow *window); gboolean meta_window_unit_cgroup_equal (MetaWindow *window1, MetaWindow *window2); + +void meta_window_check_alive_on_event (MetaWindow *window, + uint32_t timestamp); + #endif diff --git a/src/core/window.c b/src/core/window.c index a9ed975d2..adb9315b3 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -214,6 +214,7 @@ enum PROP_GTK_APP_MENU_OBJECT_PATH, PROP_GTK_MENUBAR_OBJECT_PATH, PROP_ON_ALL_WORKSPACES, + PROP_IS_ALIVE, PROP_LAST, }; @@ -633,6 +634,13 @@ meta_window_class_init (MetaWindowClass *klass) FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_props[PROP_IS_ALIVE] = + g_param_spec_boolean ("is-alive", + "Is alive", + "Whether the window responds to pings", + TRUE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, PROP_LAST, obj_props); window_signals[WORKSPACE_CHANGED] = @@ -729,6 +737,7 @@ meta_window_init (MetaWindow *self) { self->stamp = next_window_stamp++; meta_prefs_add_listener (prefs_changed_callback, self); + self->is_alive = TRUE; } static gboolean @@ -1457,6 +1466,7 @@ meta_window_unmanage (MetaWindow *window, window->unmanaging = TRUE; g_clear_handle_id (&window->unmanage_idle_id, g_source_remove); + g_clear_handle_id (&window->close_dialog_timeout_id, g_source_remove); g_signal_emit (window, window_signals[UNMANAGING], 0); @@ -8816,3 +8826,55 @@ meta_window_get_client_type (MetaWindow *window) { return window->client_type; } + +static gboolean +meta_window_close_dialog_timeout (MetaWindow *window) +{ + meta_window_show_close_dialog (window); + window->close_dialog_timeout_id = 0; + + return G_SOURCE_REMOVE; +} + +void +meta_window_ensure_close_dialog_timeout (MetaWindow *window) +{ + guint check_alive_timeout = meta_prefs_get_check_alive_timeout (); + + if (window->is_alive) + return; + if (window->close_dialog_timeout_id != 0) + return; + if (check_alive_timeout == 0) + return; + + window->close_dialog_timeout_id = + g_timeout_add (check_alive_timeout, + (GSourceFunc) meta_window_close_dialog_timeout, + window); + g_source_set_name_by_id (window->close_dialog_timeout_id, + "[mutter] meta_window_close_dialog_timeout"); +} + +void +meta_window_set_alive (MetaWindow *window, + gboolean is_alive) +{ + if (window->is_alive == is_alive) + return; + + window->is_alive = is_alive; + g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_IS_ALIVE]); + + if (is_alive) + { + g_clear_handle_id (&window->close_dialog_timeout_id, g_source_remove); + meta_window_hide_close_dialog (window); + } +} + +gboolean +meta_window_get_alive (MetaWindow *window) +{ + return window->is_alive; +}