diff --git a/src/core/constraints.c b/src/core/constraints.c index 79019b902..51006614f 100644 --- a/src/core/constraints.c +++ b/src/core/constraints.c @@ -123,6 +123,7 @@ typedef struct { MetaRectangle orig; MetaRectangle current; + MetaRectangle temporary; int rel_x; int rel_y; ActionType action_type; @@ -147,6 +148,8 @@ typedef struct */ GList *usable_screen_region; GList *usable_monitor_region; + + MetaMoveResizeFlags flags; } ConstraintInfo; static gboolean do_screen_and_monitor_relative_constraints (MetaWindow *window, @@ -284,6 +287,7 @@ meta_window_constrain (MetaWindow *window, MetaGravity resize_gravity, const MetaRectangle *orig, MetaRectangle *new, + MetaRectangle *temporary, int *rel_x, int *rel_y) { @@ -322,6 +326,7 @@ meta_window_constrain (MetaWindow *window, /* Make sure we use the constrained position */ *new = info.current; + *temporary = info.temporary; *rel_x = info.rel_x; *rel_y = info.rel_y; @@ -348,8 +353,10 @@ setup_constraint_info (ConstraintInfo *info, info->orig = *orig; info->current = *new; + info->temporary = *orig; info->rel_x = 0; info->rel_y = 0; + info->flags = flags; if (info->current.width < 1) info->current.width = 1; @@ -500,16 +507,11 @@ place_window_if_needed(MetaWindow *window, if (window->placement.rule) { - MetaWindow *parent = meta_window_get_transient_for (window); - MetaRectangle parent_rect; - meta_window_process_placement (window, window->placement.rule, &info->rel_x, &info->rel_y); - meta_window_get_frame_rect (parent, &parent_rect); - - placed_rect.x = parent_rect.x + info->rel_x; - placed_rect.y = parent_rect.y + info->rel_y; + placed_rect.x = window->placement.rule->parent_rect.x + info->rel_x; + placed_rect.y = window->placement.rule->parent_rect.y + info->rel_y; } else { @@ -825,12 +827,13 @@ constrain_custom_rule (MetaWindow *window, MetaPlacementRule *placement_rule; MetaRectangle intersection; gboolean constraint_satisfied; + MetaRectangle temporary_rect; MetaRectangle adjusted_unconstrained; int adjusted_rel_x; int adjusted_rel_y; MetaPlacementRule current_rule; MetaWindow *parent; - MetaRectangle parent_rect; + int parent_x, parent_y; if (priority > PRIORITY_CUSTOM_RULE) return TRUE; @@ -839,25 +842,72 @@ constrain_custom_rule (MetaWindow *window, if (!placement_rule) return TRUE; - adjusted_unconstrained = info->current; - parent = meta_window_get_transient_for (window); - meta_window_get_frame_rect (parent, &parent_rect); + if (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_FINISHED) + { + placement_rule->parent_rect.x = parent->rect.x; + placement_rule->parent_rect.y = parent->rect.y; + } + parent_x = placement_rule->parent_rect.x; + parent_y = placement_rule->parent_rect.y; + + /* + * Calculate the temporary position, meaning a position that will be + * applied if the new constrained position requires asynchronous + * configuration of the window. This happens for example when the parent + * moves, causing this window to change relative position, meaning it can + * only have its newly constrained position applied when the configuration is + * acknowledged. + */ switch (window->placement.state) { case META_PLACEMENT_STATE_UNCONSTRAINED: - adjusted_rel_x = window->rect.x - parent->rect.x; - adjusted_rel_y = window->rect.y - parent->rect.y; + temporary_rect = info->current; break; - case META_PLACEMENT_STATE_CONSTRAINED: - adjusted_unconstrained.x = - parent->rect.x + window->placement.current.rel_x; - adjusted_unconstrained.y = - parent->rect.y + window->placement.current.rel_y; + case META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED: + case META_PLACEMENT_STATE_CONSTRAINED_PENDING: + case META_PLACEMENT_STATE_CONSTRAINED_FINISHED: + case META_PLACEMENT_STATE_INVALIDATED: + temporary_rect = (MetaRectangle) { + .x = parent->rect.x + window->placement.current.rel_x, + .y = parent->rect.y + window->placement.current.rel_y, + .width = info->current.width, + .height = info->current.height, + }; + break; + } + + /* + * Calculate an adjusted current position. Depending on the rule + * configuration and placement state, this may result in window being + * reconstrained. + */ + + adjusted_unconstrained = temporary_rect; + + if (window->placement.state == META_PLACEMENT_STATE_INVALIDATED || + window->placement.state == META_PLACEMENT_STATE_UNCONSTRAINED || + (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_FINISHED && + placement_rule->is_reactive)) + { + meta_window_process_placement (window, placement_rule, + &adjusted_rel_x, + &adjusted_rel_y); + adjusted_unconstrained.x = parent_x + adjusted_rel_x; + adjusted_unconstrained.y = parent_y + adjusted_rel_y; + } + else if (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_PENDING) + { + adjusted_rel_x = window->placement.pending.rel_x; + adjusted_rel_y = window->placement.pending.rel_y; + adjusted_unconstrained.x = window->placement.pending.x; + adjusted_unconstrained.y = window->placement.pending.y; + } + else + { adjusted_rel_x = window->placement.current.rel_x; adjusted_rel_y = window->placement.current.rel_y; - break; } meta_rectangle_intersect (&adjusted_unconstrained, &info->work_area_monitor, @@ -872,30 +922,43 @@ constrain_custom_rule (MetaWindow *window, if (check_only) return constraint_satisfied; - current_rule = *placement_rule; + info->current = adjusted_unconstrained; + info->rel_x = adjusted_rel_x; + info->rel_y = adjusted_rel_y; + info->temporary = temporary_rect; switch (window->placement.state) { - case META_PLACEMENT_STATE_CONSTRAINED: - info->current = adjusted_unconstrained; - info->rel_x = adjusted_rel_x; - info->rel_y = adjusted_rel_y; - goto done; + case META_PLACEMENT_STATE_CONSTRAINED_FINISHED: + if (!placement_rule->is_reactive) + return TRUE; + break; + case META_PLACEMENT_STATE_CONSTRAINED_PENDING: + case META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED: + return TRUE; case META_PLACEMENT_STATE_UNCONSTRAINED: + case META_PLACEMENT_STATE_INVALIDATED: break; } if (constraint_satisfied) goto done; + /* + * Process the placement rule in order either until constraints are + * satisfied, or there are no more rules to process. + */ + + current_rule = *placement_rule; + if (info->current.width != intersection.width && (current_rule.constraint_adjustment & META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X)) { try_flip_window_position (window, info, ¤t_rule, META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X, - parent_rect.x, - parent_rect.y, + parent_x, + parent_y, &info->current, &info->rel_x, &info->rel_y, @@ -907,8 +970,8 @@ constrain_custom_rule (MetaWindow *window, { try_flip_window_position (window, info, ¤t_rule, META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y, - parent_rect.x, - parent_rect.y, + parent_x, + parent_y, &info->current, &info->rel_x, &info->rel_y, @@ -1010,10 +1073,12 @@ constrain_custom_rule (MetaWindow *window, } done: - window->placement.state = META_PLACEMENT_STATE_CONSTRAINED; + window->placement.state = META_PLACEMENT_STATE_CONSTRAINED_PENDING; - window->placement.current.rel_x = info->rel_x; - window->placement.current.rel_y = info->rel_y; + window->placement.pending.rel_x = info->rel_x; + window->placement.pending.rel_y = info->rel_y; + window->placement.pending.x = info->current.x; + window->placement.pending.y = info->current.y; return TRUE; } diff --git a/src/core/constraints.h b/src/core/constraints.h index 1517aa060..eaa4e4594 100644 --- a/src/core/constraints.h +++ b/src/core/constraints.h @@ -32,6 +32,7 @@ void meta_window_constrain (MetaWindow *window, MetaGravity resize_gravity, const MetaRectangle *orig, MetaRectangle *new, + MetaRectangle *intermediate, int *rel_x, int *rel_y); diff --git a/src/core/window-private.h b/src/core/window-private.h index 04216113e..07f316e90 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -85,6 +85,7 @@ typedef enum META_MOVE_RESIZE_FORCE_MOVE = 1 << 8, META_MOVE_RESIZE_WAYLAND_STATE_CHANGED = 1 << 9, META_MOVE_RESIZE_FORCE_UPDATE_MONITOR = 1 << 10, + META_MOVE_RESIZE_PLACEMENT_CHANGED = 1 << 11, } MetaMoveResizeFlags; typedef enum @@ -141,12 +142,19 @@ typedef struct _MetaPlacementRule int offset_y; int width; int height; + + gboolean is_reactive; + + MetaRectangle parent_rect; } MetaPlacementRule; typedef enum _MetaPlacementState { META_PLACEMENT_STATE_UNCONSTRAINED, - META_PLACEMENT_STATE_CONSTRAINED, + META_PLACEMENT_STATE_CONSTRAINED_PENDING, + META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED, + META_PLACEMENT_STATE_CONSTRAINED_FINISHED, + META_PLACEMENT_STATE_INVALIDATED, } MetaPlacementState; typedef enum @@ -538,6 +546,13 @@ struct _MetaWindow MetaPlacementRule *rule; MetaPlacementState state; + struct { + int x; + int y; + int rel_x; + int rel_y; + } pending; + struct { int rel_x; int rel_y; @@ -569,6 +584,7 @@ struct _MetaWindowClass MetaGravity gravity, MetaRectangle unconstrained_rect, MetaRectangle constrained_rect, + MetaRectangle temporary_rect, int rel_x, int rel_y, MetaMoveResizeFlags flags, diff --git a/src/core/window.c b/src/core/window.c index 6573c217e..5a9341578 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -4001,6 +4001,7 @@ meta_window_move_resize_internal (MetaWindow *window, gboolean did_placement; MetaRectangle unconstrained_rect; MetaRectangle constrained_rect; + MetaRectangle temporary_rect; int rel_x = 0; int rel_y = 0; MetaMoveResizeResultFlags result = 0; @@ -4057,6 +4058,7 @@ meta_window_move_resize_internal (MetaWindow *window, g_assert_not_reached (); constrained_rect = unconstrained_rect; + temporary_rect = window->rect; if (flags & (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION) && !(flags & META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE) && window->monitor) @@ -4069,13 +4071,14 @@ meta_window_move_resize_internal (MetaWindow *window, gravity, &old_rect, &constrained_rect, + &temporary_rect, &rel_x, &rel_y); } else if (window->placement.rule) { - rel_x = window->placement.current.rel_x; - rel_y = window->placement.current.rel_y; + rel_x = window->placement.pending.rel_x; + rel_y = window->placement.pending.rel_y; } /* If we did placement, then we need to save the position that the window @@ -4093,6 +4096,7 @@ meta_window_move_resize_internal (MetaWindow *window, gravity, unconstrained_rect, constrained_rect, + temporary_rect, rel_x, rel_y, flags, &result); diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c index 89e33d5d3..8f4b7a0e7 100644 --- a/src/wayland/meta-wayland-xdg-shell.c +++ b/src/wayland/meta-wayland-xdg-shell.c @@ -138,7 +138,8 @@ G_DEFINE_TYPE_WITH_CODE (MetaWaylandXdgPopup, popup_surface_iface_init)); static MetaPlacementRule -meta_wayland_xdg_positioner_to_placement (MetaWaylandXdgPositioner *xdg_positioner); +meta_wayland_xdg_positioner_to_placement (MetaWaylandXdgPositioner *xdg_positioner, + MetaWindow *parent_window); static struct wl_resource * meta_wayland_xdg_surface_get_wm_base_resource (MetaWaylandXdgSurface *xdg_surface); @@ -1745,6 +1746,7 @@ xdg_surface_constructor_get_popup (struct wl_client *client, struct wl_resource *xdg_surface_resource = constructor->resource; MetaWaylandSurface *parent_surface = surface_from_xdg_surface_resource (parent_resource); + MetaWindow *parent_window; MetaWaylandXdgPositioner *xdg_positioner; MetaWaylandXdgPopup *xdg_popup; MetaWaylandXdgSurface *xdg_surface; @@ -1783,9 +1785,11 @@ xdg_surface_constructor_get_popup (struct wl_client *client, xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup); meta_wayland_xdg_surface_constructor_finalize (constructor, xdg_surface); + parent_window = meta_wayland_surface_get_window (parent_surface); + xdg_positioner = wl_resource_get_user_data (positioner_resource); xdg_popup->setup.placement_rule = - meta_wayland_xdg_positioner_to_placement (xdg_positioner); + meta_wayland_xdg_positioner_to_placement (xdg_positioner, parent_window); xdg_popup->setup.parent_surface = parent_surface; } @@ -1892,8 +1896,13 @@ positioner_gravity_to_placement_gravity (uint32_t gravity) } static MetaPlacementRule -meta_wayland_xdg_positioner_to_placement (MetaWaylandXdgPositioner *xdg_positioner) +meta_wayland_xdg_positioner_to_placement (MetaWaylandXdgPositioner *xdg_positioner, + MetaWindow *parent_window) { + MetaRectangle parent_rect; + + meta_window_get_frame_rect (parent_window, &parent_rect); + return (MetaPlacementRule) { .anchor_rect = xdg_positioner->anchor_rect, .gravity = positioner_gravity_to_placement_gravity (xdg_positioner->gravity), @@ -1903,6 +1912,8 @@ meta_wayland_xdg_positioner_to_placement (MetaWaylandXdgPositioner *xdg_position .offset_y = xdg_positioner->offset_y, .width = xdg_positioner->width, .height = xdg_positioner->height, + + .parent_rect = parent_rect, }; } diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c index e86378995..80be4b7a3 100644 --- a/src/wayland/meta-window-wayland.c +++ b/src/wayland/meta-window-wayland.c @@ -222,18 +222,23 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, MetaGravity gravity, MetaRectangle unconstrained_rect, MetaRectangle constrained_rect, + MetaRectangle temporary_rect, int rel_x, int rel_y, MetaMoveResizeFlags flags, MetaMoveResizeResultFlags *result) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); - gboolean can_move_now; + gboolean can_move_now = FALSE; int configured_x; int configured_y; int configured_width; int configured_height; int geometry_scale; + int new_x; + int new_y; + int new_buffer_x; + int new_buffer_y; g_assert (window->frame == NULL); @@ -300,14 +305,61 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, /* This is a commit of an attach. We should move the window to match the * new position the client wants. */ can_move_now = TRUE; + if (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED) + window->placement.state = META_PLACEMENT_STATE_CONSTRAINED_FINISHED; } else { - /* If the size changed, or the state changed, then we have to wait until - * the client acks our configure before moving the window. */ - if (constrained_rect.width != window->rect.width || - constrained_rect.height != window->rect.height || - (flags & META_MOVE_RESIZE_STATE_CHANGED)) + if (window->placement.rule) + { + switch (window->placement.state) + { + case META_PLACEMENT_STATE_UNCONSTRAINED: + case META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED: + case META_PLACEMENT_STATE_INVALIDATED: + can_move_now = FALSE; + break; + case META_PLACEMENT_STATE_CONSTRAINED_PENDING: + { + if (flags & META_MOVE_RESIZE_PLACEMENT_CHANGED || + rel_x != wl_window->last_sent_rel_x || + rel_y != wl_window->last_sent_rel_y || + constrained_rect.width != window->rect.width || + constrained_rect.height != window->rect.height) + { + MetaWaylandWindowConfiguration *configuration; + + configuration = + meta_wayland_window_configuration_new_relative (rel_x, + rel_y, + configured_width, + configured_height); + meta_window_wayland_configure (wl_window, configuration); + + wl_window->last_sent_rel_x = rel_x; + wl_window->last_sent_rel_y = rel_y; + + window->placement.state = META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED; + + can_move_now = FALSE; + } + else + { + window->placement.state = + META_PLACEMENT_STATE_CONSTRAINED_FINISHED; + + can_move_now = TRUE; + } + break; + } + case META_PLACEMENT_STATE_CONSTRAINED_FINISHED: + can_move_now = TRUE; + break; + } + } + else if (constrained_rect.width != window->rect.width || + constrained_rect.height != window->rect.height || + flags & META_MOVE_RESIZE_STATE_CHANGED) { MetaWaylandWindowConfiguration *configuration; @@ -328,32 +380,18 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, constrained_rect.height == 1) return; - if (window->placement.rule) - { - configuration = - meta_wayland_window_configuration_new_relative (rel_x, - rel_y, - configured_width, - configured_height); - } - else - { - configuration = - meta_wayland_window_configuration_new (configured_x, - configured_y, - configured_width, - configured_height, - flags, - gravity); - } - + configuration = + meta_wayland_window_configuration_new (configured_x, + configured_y, + configured_width, + configured_height, + flags, + gravity); meta_window_wayland_configure (wl_window, configuration); can_move_now = FALSE; } else { - /* We're just moving the window, so we don't need to wait for a configure - * and then ack to simply move the window. */ can_move_now = TRUE; } } @@ -365,33 +403,46 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, if (can_move_now) { - int new_x = constrained_rect.x; - int new_y = constrained_rect.y; - - if (new_x != window->rect.x || new_y != window->rect.y) - { - *result |= META_MOVE_RESIZE_RESULT_MOVED; - window->rect.x = new_x; - window->rect.y = new_y; - } - - int new_buffer_x = new_x - window->custom_frame_extents.left; - int new_buffer_y = new_y - window->custom_frame_extents.top; - - if (new_buffer_x != window->buffer_rect.x || new_buffer_y != window->buffer_rect.y) - { - *result |= META_MOVE_RESIZE_RESULT_MOVED; - window->buffer_rect.x = new_buffer_x; - window->buffer_rect.y = new_buffer_y; - } - - if (flags & META_MOVE_RESIZE_WAYLAND_STATE_CHANGED) - *result |= META_MOVE_RESIZE_RESULT_STATE_CHANGED; + new_x = constrained_rect.x; + new_y = constrained_rect.y; } else { - wl_window->has_pending_state_change = (flags & META_MOVE_RESIZE_STATE_CHANGED) != 0; + new_x = temporary_rect.x; + new_y = temporary_rect.y; + + wl_window->has_pending_state_change |= + !!(flags & META_MOVE_RESIZE_STATE_CHANGED); } + + if (new_x != window->rect.x || new_y != window->rect.y) + { + *result |= META_MOVE_RESIZE_RESULT_MOVED; + window->rect.x = new_x; + window->rect.y = new_y; + } + + if (window->placement.rule && + window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_FINISHED) + { + window->placement.current.rel_x = rel_x; + window->placement.current.rel_y = rel_y; + } + + new_buffer_x = new_x - window->custom_frame_extents.left; + new_buffer_y = new_y - window->custom_frame_extents.top; + + if (new_buffer_x != window->buffer_rect.x || + new_buffer_y != window->buffer_rect.y) + { + *result |= META_MOVE_RESIZE_RESULT_MOVED; + window->buffer_rect.x = new_buffer_x; + window->buffer_rect.y = new_buffer_y; + } + + if (can_move_now && + flags & META_MOVE_RESIZE_WAYLAND_STATE_CHANGED) + *result |= META_MOVE_RESIZE_RESULT_STATE_CHANGED; } static void @@ -857,8 +908,19 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, { if (acked_configuration) { - rect.x = acked_configuration->x; - rect.y = acked_configuration->y; + if (window->placement.rule) + { + MetaWindow *parent; + + parent = meta_window_get_transient_for (window); + rect.x = parent->rect.x + acked_configuration->rel_x; + rect.y = parent->rect.y + acked_configuration->rel_y; + } + else + { + rect.x = acked_configuration->x; + rect.y = acked_configuration->y; + } } else { @@ -872,6 +934,14 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, if (rect.x != window->rect.x || rect.y != window->rect.y) flags |= META_MOVE_RESIZE_MOVE_ACTION; } + else + { + if (acked_configuration) + { + rect.x = acked_configuration->x; + rect.y = acked_configuration->y; + } + } if (wl_window->has_pending_state_change && acked_configuration) { @@ -918,9 +988,24 @@ meta_window_place_with_placement_rule (MetaWindow *window, window->placement.rule = g_new0 (MetaPlacementRule, 1); *window->placement.rule = *placement_rule; + window->unconstrained_rect.x = window->rect.x; + window->unconstrained_rect.y = window->rect.y; window->unconstrained_rect.width = placement_rule->width; window->unconstrained_rect.height = placement_rule->height; - meta_window_force_placement (window, FALSE); + meta_window_move_resize_internal (window, + (META_MOVE_RESIZE_MOVE_ACTION | + META_MOVE_RESIZE_RESIZE_ACTION | + META_MOVE_RESIZE_PLACEMENT_CHANGED), + META_GRAVITY_NORTH_WEST, + window->unconstrained_rect); +} + +void +meta_window_update_placement_rule (MetaWindow *window, + MetaPlacementRule *placement_rule) +{ + window->placement.state = META_PLACEMENT_STATE_INVALIDATED; + meta_window_place_with_placement_rule (window, placement_rule); } void diff --git a/src/wayland/meta-window-wayland.h b/src/wayland/meta-window-wayland.h index 0e18d4fe1..22824a6ae 100644 --- a/src/wayland/meta-window-wayland.h +++ b/src/wayland/meta-window-wayland.h @@ -53,6 +53,9 @@ void meta_window_wayland_place_relative_to (MetaWindow *window, void meta_window_place_with_placement_rule (MetaWindow *window, MetaPlacementRule *placement_rule); +void meta_window_update_placement_rule (MetaWindow *window, + MetaPlacementRule *placement_rule); + void meta_window_wayland_set_min_size (MetaWindow *window, int width, int height); diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c index fcd06f632..2c745f4aa 100644 --- a/src/x11/window-x11.c +++ b/src/x11/window-x11.c @@ -1312,6 +1312,7 @@ meta_window_x11_move_resize_internal (MetaWindow *window, MetaGravity gravity, MetaRectangle unconstrained_rect, MetaRectangle constrained_rect, + MetaRectangle intermediate_rect, int rel_x, int rel_y, MetaMoveResizeFlags flags,