diff --git a/src/core/constraints.c b/src/core/constraints.c index f743bae5b..67fb92215 100644 --- a/src/core/constraints.c +++ b/src/core/constraints.c @@ -145,6 +145,8 @@ typedef struct */ GList *usable_screen_region; GList *usable_monitor_region; + + gboolean should_unmanage; } ConstraintInfo; static gboolean do_screen_and_monitor_relative_constraints (MetaWindow *window, @@ -253,6 +255,14 @@ do_all_constraints (MetaWindow *window, satisfied = satisfied && (*constraint->func) (window, info, priority, check_only); + if (info->should_unmanage) + { + meta_topic (META_DEBUG_GEOMETRY, + "constraint %s wants to unmanage window.\n", + constraint->name); + return TRUE; + } + if (!check_only) { /* Log how the constraint modified the position */ @@ -312,6 +322,12 @@ meta_window_constrain (MetaWindow *window, */ satisfied = do_all_constraints (window, &info, priority, check_only); + if (info.should_unmanage) + { + meta_window_unmanage_on_idle (window); + return; + } + /* Drop the least important constraints if we can't satisfy them all */ priority++; } @@ -421,6 +437,8 @@ setup_constraint_info (ConstraintInfo *info, info->usable_monitor_region = meta_workspace_get_onmonitor_region (cur_workspace, logical_monitor); + info->should_unmanage = FALSE; + /* Log all this information for debugging */ meta_topic (META_DEBUG_GEOMETRY, "Setting up constraint info:\n" @@ -802,6 +820,8 @@ constrain_custom_rule (MetaWindow *window, if (window->placement_rule_constrained) { + MetaRectangle parent_buffer_rect; + parent = meta_window_get_transient_for (window); meta_window_get_frame_rect (parent, &parent_rect); info->current.x = @@ -809,6 +829,16 @@ constrain_custom_rule (MetaWindow *window, info->current.y = parent_rect.y + window->constrained_placement_rule_offset_y; + meta_window_get_buffer_rect (parent, &parent_buffer_rect); + if (!meta_rectangle_overlap (&info->current, &parent_buffer_rect) && + !meta_rectangle_is_adjacent_to (&info->current, &parent_buffer_rect)) + { + g_warning ("Buggy client caused popup to be placed outside of parent " + "window"); + info->should_unmanage = TRUE; + return TRUE; + } + return TRUE; } diff --git a/src/core/window-private.h b/src/core/window-private.h index 9fe5bc526..a6cebb4fb 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -527,6 +527,8 @@ struct _MetaWindow gboolean placement_rule_constrained; int constrained_placement_rule_offset_x; int constrained_placement_rule_offset_y; + + guint unmanage_idle_id; }; struct _MetaWindowClass @@ -610,6 +612,7 @@ MetaWindow * _meta_window_shared_new (MetaDisplay *display, void meta_window_unmanage (MetaWindow *window, guint32 timestamp); +void meta_window_unmanage_on_idle (MetaWindow *window); void meta_window_queue (MetaWindow *window, guint queuebits); void meta_window_tile (MetaWindow *window, diff --git a/src/core/window.c b/src/core/window.c index b85db6ee3..d2c24506b 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -1424,6 +1424,9 @@ meta_window_unmanage (MetaWindow *window, meta_verbose ("Unmanaging %s\n", window->desc); window->unmanaging = TRUE; + if (window->unmanage_idle_id) + g_source_remove (window->unmanage_idle_id); + #ifdef HAVE_WAYLAND /* This needs to happen for both Wayland and XWayland clients, * so it can't be in MetaWindowWayland. */ @@ -1595,6 +1598,32 @@ meta_window_unmanage (MetaWindow *window, g_object_unref (window); } +static gboolean +unmanage_window_idle_callback (gpointer user_data) +{ + MetaWindow *window = META_WINDOW (user_data); + uint32_t timestamp; + + window->unmanage_idle_id = 0; + + timestamp = meta_display_get_current_time_roundtrip (window->display); + meta_window_unmanage (window, timestamp); + + return G_SOURCE_REMOVE; +} + +void +meta_window_unmanage_on_idle (MetaWindow *window) +{ + if (window->unmanage_idle_id) + return; + + window->unmanage_idle_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, + unmanage_window_idle_callback, + window, + NULL); +} + static void set_wm_state (MetaWindow *window) {