[mutter] core: Add support for custom window placement rules
- From: Jonas Ådahl <jadahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter] core: Add support for custom window placement rules
- Date: Thu, 25 Aug 2016 04:45:15 +0000 (UTC)
commit 8833991201ce689de894720f1651dfeae516866d
Author: Jonas Ådahl <jadahl gmail com>
Date: Mon Feb 1 18:46:11 2016 +0800
core: Add support for custom window placement rules
Add support for assigning a window a custom window placement rule used
for calculating the initial window position as well as defining how a
window is constrained.
The custom rule is a declarative rule which defines a set of parameters
which the placing algorithm and constrain algorithm uses for
calculating the position of a window. It is meant to be used to
implement positioning of menus and other popup windows created via
Wayland.
A custom placement rule replaces any other placement or constraint
rule.
https://bugzilla.gnome.org/show_bug.cgi?id=769936
src/core/constraints.c | 237 ++++++++++++++++++++++++++++++++++++-
src/core/place.c | 65 ++++++++++
src/core/place.h | 5 +
src/core/window-private.h | 47 ++++++++
src/core/window.c | 11 ++-
src/wayland/meta-window-wayland.c | 13 ++
src/wayland/meta-window-wayland.h | 4 +
7 files changed, 374 insertions(+), 8 deletions(-)
---
diff --git a/src/core/constraints.c b/src/core/constraints.c
index 7b23797..7c4d61a 100644
--- a/src/core/constraints.c
+++ b/src/core/constraints.c
@@ -102,6 +102,7 @@ typedef enum
PRIORITY_SIZE_HINTS_LIMITS = 3,
PRIORITY_TITLEBAR_VISIBLE = 4,
PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA = 4,
+ PRIORITY_CUSTOM_RULE = 4,
PRIORITY_MAXIMUM = 4 /* Dummy value used for loop end = max(all priorities) */
} ConstraintPriority;
@@ -144,6 +145,10 @@ static gboolean do_screen_and_monitor_relative_constraints (MetaWindow *wind
GList *region_spanning_rectangles,
ConstraintInfo *info,
gboolean check_only);
+static gboolean constrain_custom_rule (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
static gboolean constrain_modal_dialog (MetaWindow *window,
ConstraintInfo *info,
ConstraintPriority priority,
@@ -211,6 +216,7 @@ typedef struct {
} Constraint;
static const Constraint all_constraints[] = {
+ {constrain_custom_rule, "constrain_custom_rule"},
{constrain_modal_dialog, "constrain_modal_dialog"},
{constrain_maximization, "constrain_maximization"},
{constrain_tiling, "constrain_tiling"},
@@ -641,6 +647,222 @@ get_size_limits (MetaWindow *window,
meta_window_client_rect_to_frame_rect (window, max_size, max_size);
}
+static void
+placement_rule_flip_horizontally (MetaPlacementRule *placement_rule)
+{
+ if (placement_rule->anchor & META_PLACEMENT_ANCHOR_LEFT)
+ {
+ placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_LEFT;
+ placement_rule->anchor |= META_PLACEMENT_ANCHOR_RIGHT;
+ }
+ else if (placement_rule->anchor & META_PLACEMENT_ANCHOR_RIGHT)
+ {
+ placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_RIGHT;
+ placement_rule->anchor |= META_PLACEMENT_ANCHOR_LEFT;
+ }
+
+ if (placement_rule->gravity & META_PLACEMENT_GRAVITY_LEFT)
+ {
+ placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_LEFT;
+ placement_rule->gravity |= META_PLACEMENT_GRAVITY_RIGHT;
+ }
+ else if (placement_rule->gravity & META_PLACEMENT_GRAVITY_RIGHT)
+ {
+ placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_RIGHT;
+ placement_rule->gravity |= META_PLACEMENT_GRAVITY_LEFT;
+ }
+}
+
+static void
+placement_rule_flip_vertically (MetaPlacementRule *placement_rule)
+{
+ if (placement_rule->anchor & META_PLACEMENT_ANCHOR_TOP)
+ {
+ placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_TOP;
+ placement_rule->anchor |= META_PLACEMENT_ANCHOR_BOTTOM;
+ }
+ else if (placement_rule->anchor & META_PLACEMENT_ANCHOR_BOTTOM)
+ {
+ placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_BOTTOM;
+ placement_rule->anchor |= META_PLACEMENT_ANCHOR_TOP;
+ }
+
+ if (placement_rule->gravity & META_PLACEMENT_GRAVITY_TOP)
+ {
+ placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_TOP;
+ placement_rule->gravity |= META_PLACEMENT_GRAVITY_BOTTOM;
+ }
+ else if (placement_rule->gravity & META_PLACEMENT_GRAVITY_BOTTOM)
+ {
+ placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_BOTTOM;
+ placement_rule->gravity |= META_PLACEMENT_GRAVITY_TOP;
+ }
+}
+
+static void
+try_flip_window_position (MetaWindow *window,
+ ConstraintInfo *info,
+ MetaPlacementRule *placement_rule,
+ MetaPlacementConstraintAdjustment constraint_adjustment,
+ MetaRectangle *rect,
+ MetaRectangle *intersection)
+{
+ MetaPlacementRule flipped_rule = *placement_rule;;
+ MetaRectangle flipped_rect;
+ MetaRectangle flipped_intersection;
+
+ switch (constraint_adjustment)
+ {
+ case META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X:
+ placement_rule_flip_horizontally (&flipped_rule);
+ break;
+ case META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y:
+ placement_rule_flip_vertically (&flipped_rule);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ flipped_rect = info->current;
+ meta_window_process_placement (window, &flipped_rule,
+ &flipped_rect.x, &flipped_rect.y);
+ meta_rectangle_intersect (&flipped_rect, &info->work_area_monitor,
+ &flipped_intersection);
+
+ if ((constraint_adjustment == META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X &&
+ flipped_intersection.width == flipped_rect.width) ||
+ (constraint_adjustment == META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y &&
+ flipped_intersection.height == flipped_rect.height))
+ {
+ *placement_rule = flipped_rule;
+ *rect = flipped_rect;
+ *intersection = flipped_intersection;
+ }
+}
+
+static gboolean
+is_custom_rule_satisfied (ConstraintInfo *info,
+ MetaPlacementRule *placement_rule,
+ MetaRectangle *intersection)
+{
+ uint32_t x_constrain_actions, y_constrain_actions;
+
+ x_constrain_actions = (META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_X |
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X);
+ y_constrain_actions = (META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_Y |
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y);
+ if ((placement_rule->constraint_adjustment & x_constrain_actions &&
+ info->current.width != intersection->width) ||
+ (placement_rule->constraint_adjustment & y_constrain_actions &&
+ info->current.height != intersection->height))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static gboolean
+constrain_custom_rule (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only)
+{
+ MetaPlacementRule *placement_rule;
+ MetaRectangle intersection;
+ gboolean constraint_satisfied;
+ MetaPlacementRule current_rule;
+
+ if (priority > PRIORITY_CUSTOM_RULE)
+ return TRUE;
+
+ placement_rule = meta_window_get_placement_rule (window);
+ if (!placement_rule)
+ return TRUE;
+
+ if (!meta_rectangle_could_fit_rect (&info->work_area_monitor,
+ &info->current))
+ return TRUE;
+
+ meta_rectangle_intersect (&info->current, &info->work_area_monitor,
+ &intersection);
+
+ constraint_satisfied = is_custom_rule_satisfied (info,
+ placement_rule,
+ &intersection);
+
+ if (constraint_satisfied || check_only)
+ return constraint_satisfied;
+
+ 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,
+ &info->current, &intersection);
+ }
+ if (info->current.height != intersection.height &&
+ (current_rule.constraint_adjustment &
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y))
+ {
+ try_flip_window_position (window, info, ¤t_rule,
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y,
+ &info->current, &intersection);
+ }
+
+ meta_rectangle_intersect (&info->current, &info->work_area_monitor,
+ &intersection);
+ constraint_satisfied = is_custom_rule_satisfied (info,
+ placement_rule,
+ &intersection);
+
+ if (constraint_satisfied)
+ return TRUE;
+
+ if (current_rule.constraint_adjustment &
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_X)
+ {
+ if (info->current.x != intersection.x)
+ info->current.x = intersection.x;
+ else if (info->current.width != intersection.width)
+ info->current.x -= info->current.width - intersection.width;
+ }
+ if (current_rule.constraint_adjustment &
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_Y)
+ {
+ if (info->current.y != intersection.y)
+ info->current.y = intersection.y;
+ else if (info->current.height != intersection.height)
+ info->current.y -= info->current.height - intersection.height;
+ }
+
+ meta_rectangle_intersect (&info->current, &info->work_area_monitor,
+ &intersection);
+ constraint_satisfied = is_custom_rule_satisfied (info,
+ placement_rule,
+ &intersection);
+
+ if (constraint_satisfied)
+ return TRUE;
+
+ if (current_rule.constraint_adjustment &
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_X)
+ {
+ info->current.x = intersection.x;
+ info->current.width = intersection.width;
+ }
+ if (current_rule.constraint_adjustment &
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_Y)
+ {
+ info->current.y = intersection.y;
+ info->current.height = intersection.height;
+ }
+
+ return TRUE;
+}
+
static gboolean
constrain_modal_dialog (MetaWindow *window,
ConstraintInfo *info,
@@ -652,7 +874,8 @@ constrain_modal_dialog (MetaWindow *window,
MetaRectangle child_rect, parent_rect;
gboolean constraint_already_satisfied;
- if (!meta_window_is_attached_dialog (window))
+ if (!meta_window_is_attached_dialog (window) ||
+ meta_window_get_placement_rule (window))
return TRUE;
/* We want to center the dialog on the parent, including the decorations
@@ -1230,7 +1453,8 @@ constrain_to_single_monitor (MetaWindow *window,
window->screen->n_monitor_infos == 1 ||
!window->require_on_single_monitor ||
!window->frame ||
- info->is_user_action)
+ info->is_user_action ||
+ meta_window_get_placement_rule (window))
return TRUE;
/* Have a helper function handle the constraint for us */
@@ -1257,7 +1481,8 @@ constrain_fully_onscreen (MetaWindow *window,
window->type == META_WINDOW_DOCK ||
window->fullscreen ||
!window->require_fully_onscreen ||
- info->is_user_action)
+ info->is_user_action ||
+ meta_window_get_placement_rule (window))
return TRUE;
/* Have a helper function handle the constraint for us */
@@ -1296,7 +1521,8 @@ constrain_titlebar_visible (MetaWindow *window,
window->type == META_WINDOW_DOCK ||
window->fullscreen ||
!window->require_titlebar_visible ||
- unconstrained_user_action)
+ unconstrained_user_action ||
+ meta_window_get_placement_rule (window))
return TRUE;
/* Determine how much offscreen things are allowed. We first need to
@@ -1373,7 +1599,8 @@ constrain_partially_onscreen (MetaWindow *window,
* "onscreen" by their own strut).
*/
if (window->type == META_WINDOW_DESKTOP ||
- window->type == META_WINDOW_DOCK)
+ window->type == META_WINDOW_DOCK ||
+ meta_window_get_placement_rule (window))
return TRUE;
/* Determine how much offscreen things are allowed. We first need to
diff --git a/src/core/place.c b/src/core/place.c
index fb2631e..db71b83 100644
--- a/src/core/place.c
+++ b/src/core/place.c
@@ -599,6 +599,61 @@ find_first_fit (MetaWindow *window,
}
void
+meta_window_process_placement (MetaWindow *window,
+ MetaPlacementRule *placement_rule,
+ int *x,
+ int *y)
+{
+ MetaWindow *parent = meta_window_get_transient_for (window);
+ MetaRectangle parent_rect;
+ MetaRectangle anchor_rect;
+ int window_width, window_height;
+
+ window_width = placement_rule->width;
+ window_height = placement_rule->height;
+ meta_window_get_frame_rect (parent, &parent_rect);
+
+ anchor_rect = (MetaRectangle) {
+ .x = parent_rect.x + placement_rule->anchor_rect.x,
+ .y = parent_rect.y + placement_rule->anchor_rect.y,
+ .width = placement_rule->anchor_rect.width,
+ .height = placement_rule->anchor_rect.height,
+ };
+
+ /* Place at anchor point. */
+ if (placement_rule->anchor & META_PLACEMENT_ANCHOR_LEFT)
+ *x = anchor_rect.x;
+ else if (placement_rule->anchor & META_PLACEMENT_ANCHOR_RIGHT)
+ *x = anchor_rect.x + anchor_rect.width;
+ else
+ *x = anchor_rect.x + (anchor_rect.width / 2);
+ if (placement_rule->anchor & META_PLACEMENT_ANCHOR_TOP)
+ *y = anchor_rect.y;
+ else if (placement_rule->anchor & META_PLACEMENT_ANCHOR_BOTTOM)
+ *y = anchor_rect.y + anchor_rect.height;
+ else
+ *y = anchor_rect.y + (anchor_rect.height / 2);
+
+ /* Shift according to gravity. */
+ if (placement_rule->gravity & META_PLACEMENT_GRAVITY_LEFT)
+ *x -= window_width;
+ else if (placement_rule->gravity & META_PLACEMENT_GRAVITY_RIGHT)
+ *x = *x;
+ else
+ *x -= window_width / 2;
+ if (placement_rule->gravity & META_PLACEMENT_GRAVITY_TOP)
+ *y -= window_height;
+ else if (placement_rule->gravity & META_PLACEMENT_GRAVITY_BOTTOM)
+ *y = *y;
+ else
+ *y -= window_height / 2;
+
+ /* Offset according to offset. */
+ *x += placement_rule->offset_x;
+ *y += placement_rule->offset_y;
+}
+
+void
meta_window_place (MetaWindow *window,
int x,
int y,
@@ -610,6 +665,16 @@ meta_window_place (MetaWindow *window,
meta_topic (META_DEBUG_PLACEMENT, "Placing window %s\n", window->desc);
+ /* If the window has a custom placement rule, always run only that. */
+ if (window->placement_rule)
+ {
+ meta_window_process_placement (window,
+ window->placement_rule,
+ &x, &y);
+
+ goto done;
+ }
+
switch (window->type)
{
/* Run placement algorithm on these. */
diff --git a/src/core/place.h b/src/core/place.h
index 5f60225..ea0a330 100644
--- a/src/core/place.h
+++ b/src/core/place.h
@@ -25,6 +25,11 @@
#include "window-private.h"
#include "frame.h"
+void meta_window_process_placement (MetaWindow *window,
+ MetaPlacementRule *placement_rule,
+ int *x,
+ int *y);
+
void meta_window_place (MetaWindow *window,
int x,
int y,
diff --git a/src/core/window-private.h b/src/core/window-private.h
index f99e104..7d30652 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -88,6 +88,47 @@ typedef enum
META_MOVE_RESIZE_RESULT_FRAME_SHAPE_CHANGED = 1 << 2,
} MetaMoveResizeResultFlags;
+typedef enum
+{
+ META_PLACEMENT_GRAVITY_NONE = 0,
+ META_PLACEMENT_GRAVITY_TOP = 1 << 0,
+ META_PLACEMENT_GRAVITY_BOTTOM = 1 << 1,
+ META_PLACEMENT_GRAVITY_LEFT = 1 << 2,
+ META_PLACEMENT_GRAVITY_RIGHT = 1 << 3,
+} MetaPlacementGravity;
+
+typedef enum
+{
+ META_PLACEMENT_ANCHOR_NONE = 0,
+ META_PLACEMENT_ANCHOR_TOP = 1 << 0,
+ META_PLACEMENT_ANCHOR_BOTTOM = 1 << 1,
+ META_PLACEMENT_ANCHOR_LEFT = 1 << 2,
+ META_PLACEMENT_ANCHOR_RIGHT = 1 << 3,
+} MetaPlacementAnchor;
+
+typedef enum
+{
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_NONE = 0,
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1 << 0,
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 1 << 1,
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X = 1 << 2,
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y = 1 << 3,
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_X = 1 << 4,
+ META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 1 << 5,
+} MetaPlacementConstraintAdjustment;
+
+typedef struct _MetaPlacementRule
+{
+ MetaRectangle anchor_rect;
+ MetaPlacementGravity gravity;
+ MetaPlacementAnchor anchor;
+ MetaPlacementConstraintAdjustment constraint_adjustment;
+ int offset_x;
+ int offset_y;
+ int width;
+ int height;
+} MetaPlacementRule;
+
struct _MetaWindow
{
GObject parent_instance;
@@ -448,6 +489,8 @@ struct _MetaWindow
/* Bypass compositor hints */
guint bypass_compositor;
+
+ MetaPlacementRule *placement_rule;
};
struct _MetaWindowClass
@@ -698,4 +741,8 @@ gboolean meta_window_has_pointer (MetaWindow *window);
void meta_window_emit_size_changed (MetaWindow *window);
+MetaPlacementRule *meta_window_get_placement_rule (MetaWindow *window);
+
+void meta_window_force_placement (MetaWindow *window);
+
#endif
diff --git a/src/core/window.c b/src/core/window.c
index 1d5882a..9cfb095 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -84,8 +84,6 @@ static void set_net_wm_state (MetaWindow *window);
static void meta_window_set_above (MetaWindow *window,
gboolean new_value);
-static void meta_window_force_placement (MetaWindow *window);
-
static void meta_window_show (MetaWindow *window);
static void meta_window_hide (MetaWindow *window);
@@ -296,6 +294,7 @@ meta_window_finalize (GObject *object)
g_free (window->gtk_window_object_path);
g_free (window->gtk_app_menu_object_path);
g_free (window->gtk_menubar_object_path);
+ g_free (window->placement_rule);
G_OBJECT_CLASS (meta_window_parent_class)->finalize (object);
}
@@ -2168,7 +2167,7 @@ window_would_be_covered (const MetaWindow *newbie)
return FALSE; /* none found */
}
-static void
+void
meta_window_force_placement (MetaWindow *window)
{
if (window->placed)
@@ -7883,3 +7882,9 @@ meta_window_emit_size_changed (MetaWindow *window)
{
g_signal_emit (window, window_signals[SIZE_CHANGED], 0);
}
+
+MetaPlacementRule *
+meta_window_get_placement_rule (MetaWindow *window)
+{
+ return window->placement_rule;
+}
diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c
index 9b5ec5f..43ffe69 100644
--- a/src/wayland/meta-window-wayland.c
+++ b/src/wayland/meta-window-wayland.c
@@ -640,3 +640,16 @@ meta_window_wayland_place_relative_to (MetaWindow *window,
other->buffer_rect.y + (y * monitor_scale));
window->placed = TRUE;
}
+
+void
+meta_window_place_with_placement_rule (MetaWindow *window,
+ MetaPlacementRule *placement_rule)
+{
+ g_clear_pointer (&window->placement_rule, g_free);
+ window->placement_rule = g_new0 (MetaPlacementRule, 1);
+ *window->placement_rule = *placement_rule;
+
+ window->unconstrained_rect.width = placement_rule->width;
+ window->unconstrained_rect.height = placement_rule->height;
+ meta_window_force_placement (window);
+}
diff --git a/src/wayland/meta-window-wayland.h b/src/wayland/meta-window-wayland.h
index 9287db8..1847f70 100644
--- a/src/wayland/meta-window-wayland.h
+++ b/src/wayland/meta-window-wayland.h
@@ -25,6 +25,7 @@
#ifndef META_WINDOW_WAYLAND_H
#define META_WINDOW_WAYLAND_H
+#include "core/window-private.h"
#include <meta/window.h>
#include "wayland/meta-wayland-types.h"
@@ -57,4 +58,7 @@ void meta_window_wayland_place_relative_to (MetaWindow *window,
int x,
int y);
+void meta_window_place_with_placement_rule (MetaWindow *window,
+ MetaPlacementRule *placement_rule);
+
#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]