summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorges Basile Stavracas Neto <georges.stavracas@gmail.com>2020-10-16 19:13:12 -0300
committerGeorges Basile Stavracas Neto <georges.stavracas@gmail.com>2020-10-26 09:56:42 -0300
commitf411834d42a11172ac8fd090051ed174f437e09f (patch)
tree96f32fc48330dc468d7c7fc3737eace1d67794df
parent0d79a0faf89644088bc3a50bbb9d23dc50a45791 (diff)
downloadmutter-f411834d42a11172ac8fd090051ed174f437e09f.tar.gz
Introduce ClutterPickStack
ClutterPickStack is a new boxed type that stores the vertices and clip rectangles. It is meant to be a byproduct of picking, and takes over most of what ClutterStage currently does. It introduces a 'seal' system, inspired by MetaKmsUpdate. After the pick operation is done, and the rectangles are collected, the pick stack is sealed, and is not allowed to be externally modified anymore. Internally, it still can invalidate pick records when an actor is destroyed. For now, it handles both the clip rectangles, and the matrix stack, separatedly. Future commits will rearrange this. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1509
-rw-r--r--clutter/clutter/clutter-actor.c30
-rw-r--r--clutter/clutter/clutter-pick-context-private.h4
-rw-r--r--clutter/clutter/clutter-pick-context.c101
-rw-r--r--clutter/clutter/clutter-pick-context.h23
-rw-r--r--clutter/clutter/clutter-pick-stack-private.h65
-rw-r--r--clutter/clutter/clutter-pick-stack.c386
-rw-r--r--clutter/clutter/clutter-stage-private.h9
-rw-r--r--clutter/clutter/clutter-stage.c285
-rw-r--r--clutter/clutter/meson.build1
9 files changed, 595 insertions, 309 deletions
diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c
index 4a92d6ffd..867b54c47 100644
--- a/clutter/clutter/clutter-actor.c
+++ b/clutter/clutter/clutter-actor.c
@@ -1252,8 +1252,6 @@ _clutter_actor_transform_local_box_to_stage (ClutterActor *self,
{
ClutterActor *stage_actor = CLUTTER_ACTOR (stage);
ClutterActorPrivate *stage_priv = stage_actor->priv;
- CoglFramebuffer *fb =
- clutter_pick_context_get_framebuffer (pick_context);
graphene_matrix_t modelview, transform_to_stage;
int v;
@@ -1261,7 +1259,7 @@ _clutter_actor_transform_local_box_to_stage (ClutterActor *self,
if (!stage_priv->has_inverse_transform)
return FALSE;
- cogl_framebuffer_get_modelview_matrix (fb, &modelview);
+ clutter_pick_context_get_transform (pick_context, &modelview);
graphene_matrix_multiply (&modelview,
&stage_priv->inverse_transform,
&transform_to_stage);
@@ -1326,7 +1324,7 @@ clutter_actor_pick_box (ClutterActor *self,
if (_clutter_actor_transform_local_box_to_stage (self, stage, pick_context,
box, vertices))
- clutter_stage_log_pick (stage, vertices, self);
+ clutter_pick_context_log_pick (pick_context, vertices, self);
}
static gboolean
@@ -1343,20 +1341,11 @@ _clutter_actor_push_pick_clip (ClutterActor *self,
clip, vertices))
return FALSE;
- clutter_stage_push_pick_clip (stage, vertices);
+ clutter_pick_context_push_clip (pick_context, vertices);
return TRUE;
}
static void
-_clutter_actor_pop_pick_clip (ClutterActor *self)
-{
- ClutterActor *stage;
-
- stage = _clutter_actor_get_stage_internal (self);
- clutter_stage_pop_pick_clip (CLUTTER_STAGE (stage));
-}
-
-static void
clutter_actor_set_mapped (ClutterActor *self,
gboolean mapped)
{
@@ -4021,7 +4010,6 @@ clutter_actor_pick (ClutterActor *actor,
ClutterPickContext *pick_context)
{
ClutterActorPrivate *priv;
- CoglFramebuffer *framebuffer;
ClutterActorBox clip;
gboolean clip_set = FALSE;
@@ -4039,16 +4027,13 @@ clutter_actor_pick (ClutterActor *actor,
/* mark that we are in the paint process */
CLUTTER_SET_PRIVATE_FLAGS (actor, CLUTTER_IN_PICK);
- framebuffer = clutter_pick_context_get_framebuffer (pick_context);
- cogl_framebuffer_push_matrix (framebuffer);
-
if (priv->enable_model_view_transform)
{
graphene_matrix_t matrix;
- cogl_framebuffer_get_modelview_matrix (framebuffer, &matrix);
+ graphene_matrix_init_identity (&matrix);
_clutter_actor_apply_modelview_transform (actor, &matrix);
- cogl_framebuffer_set_modelview_matrix (framebuffer, &matrix);
+ clutter_pick_context_push_transform (pick_context, &matrix);
}
if (priv->has_clip)
@@ -4081,9 +4066,10 @@ clutter_actor_pick (ClutterActor *actor,
clutter_actor_continue_pick (actor, pick_context);
if (clip_set)
- _clutter_actor_pop_pick_clip (actor);
+ clutter_pick_context_pop_clip (pick_context);
- cogl_framebuffer_pop_matrix (framebuffer);
+ if (priv->enable_model_view_transform)
+ clutter_pick_context_pop_transform (pick_context);
/* paint sequence complete */
CLUTTER_UNSET_PRIVATE_FLAGS (actor, CLUTTER_IN_PICK);
diff --git a/clutter/clutter/clutter-pick-context-private.h b/clutter/clutter/clutter-pick-context-private.h
index 7e4422edd..cfbfce6eb 100644
--- a/clutter/clutter/clutter-pick-context-private.h
+++ b/clutter/clutter/clutter-pick-context-private.h
@@ -19,8 +19,12 @@
#define CLUTTER_PICK_CONTEXT_PRIVATE_H
#include "clutter-pick-context.h"
+#include "clutter-pick-stack-private.h"
ClutterPickContext * clutter_pick_context_new_for_view (ClutterStageView *view,
ClutterPickMode mode);
+ClutterPickStack *
+clutter_pick_context_steal_stack (ClutterPickContext *pick_context);
+
#endif /* CLUTTER_PICK_CONTEXT_PRIVATE_H */
diff --git a/clutter/clutter/clutter-pick-context.c b/clutter/clutter/clutter-pick-context.c
index 6209e58ce..29027284a 100644
--- a/clutter/clutter/clutter-pick-context.c
+++ b/clutter/clutter/clutter-pick-context.c
@@ -25,6 +25,7 @@ struct _ClutterPickContext
ClutterPickMode mode;
CoglFramebuffer *framebuffer;
+ ClutterPickStack *pick_stack;
};
G_DEFINE_BOXED_TYPE (ClutterPickContext, clutter_pick_context,
@@ -36,6 +37,7 @@ clutter_pick_context_new_for_view (ClutterStageView *view,
ClutterPickMode mode)
{
ClutterPickContext *pick_context;
+ CoglContext *context;
pick_context = g_new0 (ClutterPickContext, 1);
g_ref_count_init (&pick_context->ref_count);
@@ -43,6 +45,9 @@ clutter_pick_context_new_for_view (ClutterStageView *view,
pick_context->framebuffer =
g_object_ref (clutter_stage_view_get_framebuffer (view));
+ context = cogl_framebuffer_get_context (pick_context->framebuffer);
+ pick_context->pick_stack = clutter_pick_stack_new (context);
+
return pick_context;
}
@@ -56,6 +61,7 @@ clutter_pick_context_ref (ClutterPickContext *pick_context)
static void
clutter_pick_context_dispose (ClutterPickContext *pick_context)
{
+ g_clear_pointer (&pick_context->pick_stack, clutter_pick_stack_unref);
g_clear_object (&pick_context->framebuffer);
}
@@ -93,3 +99,98 @@ clutter_pick_context_get_mode (ClutterPickContext *pick_context)
{
return pick_context->mode;
}
+
+ClutterPickStack *
+clutter_pick_context_steal_stack (ClutterPickContext *pick_context)
+{
+ clutter_pick_stack_seal (pick_context->pick_stack);
+ return g_steal_pointer (&pick_context->pick_stack);
+}
+
+/**
+ * clutter_pick_context_log_pick:
+ * @pick_context: a #ClutterPickContext
+ * @vertices: (array fixed-size=4): array of #graphene_point_t
+ * @actor: a #ClutterActor
+ *
+ * Logs a pick rectangle into the pick stack.
+ */
+void
+clutter_pick_context_log_pick (ClutterPickContext *pick_context,
+ const graphene_point_t vertices[4],
+ ClutterActor *actor)
+{
+ clutter_pick_stack_log_pick (pick_context->pick_stack, vertices, actor);
+}
+
+/**
+ * clutter_pick_context_push_clip:
+ * @pick_context: a #ClutterPickContext
+ * @vertices: (array fixed-size=4): array of #graphene_point_t
+ *
+ * Pushes a clip rectangle defined by @vertices into the pick stack.
+ * Pop with clutter_pick_context_pop_clip() when done.
+ */
+void
+clutter_pick_context_push_clip (ClutterPickContext *pick_context,
+ const graphene_point_t vertices[4])
+{
+ clutter_pick_stack_push_clip (pick_context->pick_stack, vertices);
+}
+
+/**
+ * clutter_pick_context_pop_clip:
+ * @pick_context: a #ClutterPickContext
+ *
+ * Pops the current clip rectangle from the clip stack. It is a programming
+ * error to call this without a corresponding clutter_pick_context_push_clip()
+ * call first.
+ */
+void
+clutter_pick_context_pop_clip (ClutterPickContext *pick_context)
+{
+ clutter_pick_stack_pop_clip (pick_context->pick_stack);
+}
+
+/**
+ * clutter_pick_context_push_transform:
+ * @pick_context: a #ClutterPickContext
+ * @transform: a #graphene_matrix_t
+ *
+ * Pushes @transform into the pick stack. Pop with
+ * clutter_pick_context_pop_transform() when done.
+ */
+void
+clutter_pick_context_push_transform (ClutterPickContext *pick_context,
+ const graphene_matrix_t *transform)
+{
+ clutter_pick_stack_push_transform (pick_context->pick_stack, transform);
+}
+
+/**
+ * clutter_pick_context_get_transform:
+ * @pick_context: a #ClutterPickContext
+ * @out_matrix: (out): a #graphene_matrix_t
+ *
+ * Retrieves the current transform of the pick stack.
+ */
+void
+clutter_pick_context_get_transform (ClutterPickContext *pick_context,
+ graphene_matrix_t *out_transform)
+{
+ clutter_pick_stack_get_transform (pick_context->pick_stack, out_transform);
+}
+
+/**
+ * clutter_pick_context_pop_transform:
+ * @pick_context: a #ClutterPickContext
+ *
+ * Pops the current transform from the clip stack. It is a programming error
+ * to call this without a corresponding clutter_pick_context_push_transform()
+ * call first.
+ */
+void
+clutter_pick_context_pop_transform (ClutterPickContext *pick_context)
+{
+ clutter_pick_stack_pop_transform (pick_context->pick_stack);
+}
diff --git a/clutter/clutter/clutter-pick-context.h b/clutter/clutter/clutter-pick-context.h
index d420d0a57..4afa25af7 100644
--- a/clutter/clutter/clutter-pick-context.h
+++ b/clutter/clutter/clutter-pick-context.h
@@ -49,4 +49,27 @@ CoglFramebuffer * clutter_pick_context_get_framebuffer (ClutterPickContext *pick
CLUTTER_EXPORT
ClutterPickMode clutter_pick_context_get_mode (ClutterPickContext *pick_context);
+CLUTTER_EXPORT
+void clutter_pick_context_log_pick (ClutterPickContext *pick_context,
+ const graphene_point_t vertices[4],
+ ClutterActor *actor);
+
+CLUTTER_EXPORT
+void clutter_pick_context_push_clip (ClutterPickContext *pick_context,
+ const graphene_point_t vertices[4]);
+
+CLUTTER_EXPORT
+void clutter_pick_context_pop_clip (ClutterPickContext *pick_context);
+
+CLUTTER_EXPORT
+void clutter_pick_context_push_transform (ClutterPickContext *pick_context,
+ const graphene_matrix_t *transform);
+
+CLUTTER_EXPORT
+void clutter_pick_context_get_transform (ClutterPickContext *pick_context,
+ graphene_matrix_t *out_matrix);
+
+CLUTTER_EXPORT
+void clutter_pick_context_pop_transform (ClutterPickContext *pick_context);
+
#endif /* CLUTTER_PICK_CONTEXT_H */
diff --git a/clutter/clutter/clutter-pick-stack-private.h b/clutter/clutter/clutter-pick-stack-private.h
new file mode 100644
index 000000000..1be943aae
--- /dev/null
+++ b/clutter/clutter/clutter-pick-stack-private.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 Endless OS Foundation, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CLUTTER_PICK_STACK_PRIVATE_H
+#define CLUTTER_PICK_STACK_PRIVATE_H
+
+#include <glib-object.h>
+
+#include "clutter-macros.h"
+#include "clutter-stage-view.h"
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_PICK_STACK (clutter_pick_stack_get_type ())
+
+typedef struct _ClutterPickStack ClutterPickStack;
+
+GType clutter_pick_stack_get_type (void) G_GNUC_CONST;
+
+ClutterPickStack * clutter_pick_stack_new (CoglContext *context);
+
+ClutterPickStack * clutter_pick_stack_ref (ClutterPickStack *pick_stack);
+
+void clutter_pick_stack_unref (ClutterPickStack *pick_stack);
+
+void clutter_pick_stack_seal (ClutterPickStack *pick_stack);
+
+void clutter_pick_stack_log_pick (ClutterPickStack *pick_stack,
+ const graphene_point_t vertices[4],
+ ClutterActor *actor);
+
+void clutter_pick_stack_push_clip (ClutterPickStack *pick_stack,
+ const graphene_point_t vertices[4]);
+
+void clutter_pick_stack_pop_clip (ClutterPickStack *pick_stack);
+
+void clutter_pick_stack_push_transform (ClutterPickStack *pick_stack,
+ const graphene_matrix_t *transform);
+
+void clutter_pick_stack_get_transform (ClutterPickStack *pick_stack,
+ graphene_matrix_t *out_transform);
+
+void clutter_pick_stack_pop_transform (ClutterPickStack *pick_stack);
+
+ClutterActor * clutter_pick_stack_find_actor_at (ClutterPickStack *pick_stack,
+ float x,
+ float y);
+
+G_END_DECLS
+
+#endif /* CLUTTER_PICK_STACK_PRIVATE_H */
diff --git a/clutter/clutter/clutter-pick-stack.c b/clutter/clutter/clutter-pick-stack.c
new file mode 100644
index 000000000..1f0c47a21
--- /dev/null
+++ b/clutter/clutter/clutter-pick-stack.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2020 Endless OS Foundation, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "clutter-pick-stack-private.h"
+#include "clutter-private.h"
+
+typedef struct
+{
+ graphene_point_t vertices[4];
+ ClutterActor *actor;
+ int clip_index;
+} PickRecord;
+
+typedef struct
+{
+ int prev;
+ graphene_point_t vertices[4];
+} PickClipRecord;
+
+struct _ClutterPickStack
+{
+ grefcount ref_count;
+
+ CoglMatrixStack *matrix_stack;
+ GArray *vertices_stack;
+ GArray *clip_stack;
+ int current_clip_stack_top;
+
+ gboolean sealed : 1;
+};
+
+G_DEFINE_BOXED_TYPE (ClutterPickStack, clutter_pick_stack,
+ clutter_pick_stack_ref, clutter_pick_stack_unref)
+
+static gboolean
+is_quadrilateral_axis_aligned_rectangle (const graphene_point_t vertices[4])
+{
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (!G_APPROX_VALUE (vertices[i].x,
+ vertices[(i + 1) % 4].x,
+ FLT_EPSILON) &&
+ !G_APPROX_VALUE (vertices[i].y,
+ vertices[(i + 1) % 4].y,
+ FLT_EPSILON))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+is_inside_axis_aligned_rectangle (const graphene_point_t *point,
+ const graphene_point_t vertices[4])
+{
+ float min_x = FLT_MAX;
+ float max_x = -FLT_MAX;
+ float min_y = FLT_MAX;
+ float max_y = -FLT_MAX;
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ min_x = MIN (min_x, vertices[i].x);
+ min_y = MIN (min_y, vertices[i].y);
+ max_x = MAX (max_x, vertices[i].x);
+ max_y = MAX (max_y, vertices[i].y);
+ }
+
+ return (point->x >= min_x &&
+ point->y >= min_y &&
+ point->x < max_x &&
+ point->y < max_y);
+}
+
+static int
+clutter_point_compare_line (const graphene_point_t *p,
+ const graphene_point_t *a,
+ const graphene_point_t *b)
+{
+ graphene_vec3_t vec_pa;
+ graphene_vec3_t vec_pb;
+ graphene_vec3_t cross;
+ float cross_z;
+
+ graphene_vec3_init (&vec_pa, p->x - a->x, p->y - a->y, 0.f);
+ graphene_vec3_init (&vec_pb, p->x - b->x, p->y - b->y, 0.f);
+ graphene_vec3_cross (&vec_pa, &vec_pb, &cross);
+ cross_z = graphene_vec3_get_z (&cross);
+
+ if (cross_z > 0.f)
+ return 1;
+ else if (cross_z < 0.f)
+ return -1;
+ else
+ return 0;
+}
+
+static gboolean
+is_inside_unaligned_rectangle (const graphene_point_t *point,
+ const graphene_point_t vertices[4])
+{
+ unsigned int i;
+ int first_side;
+
+ first_side = 0;
+
+ for (i = 0; i < 4; i++)
+ {
+ int side;
+
+ side = clutter_point_compare_line (point,
+ &vertices[i],
+ &vertices[(i + 1) % 4]);
+
+ if (side)
+ {
+ if (first_side == 0)
+ first_side = side;
+ else if (side != first_side)
+ return FALSE;
+ }
+ }
+
+ if (first_side == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+is_inside_input_region (const graphene_point_t *point,
+ const graphene_point_t vertices[4])
+{
+
+ if (is_quadrilateral_axis_aligned_rectangle (vertices))
+ return is_inside_axis_aligned_rectangle (point, vertices);
+ else
+ return is_inside_unaligned_rectangle (point, vertices);
+}
+
+static gboolean
+pick_record_contains_point (ClutterPickStack *pick_stack,
+ const PickRecord *rec,
+ float x,
+ float y)
+{
+ const graphene_point_t point = GRAPHENE_POINT_INIT (x, y);
+ int clip_index;
+
+ if (!is_inside_input_region (&point, rec->vertices))
+ return FALSE;
+
+ clip_index = rec->clip_index;
+ while (clip_index >= 0)
+ {
+ const PickClipRecord *clip =
+ &g_array_index (pick_stack->clip_stack, PickClipRecord, clip_index);
+
+ if (!is_inside_input_region (&point, clip->vertices))
+ return FALSE;
+
+ clip_index = clip->prev;
+ }
+
+ return TRUE;
+}
+
+static void
+add_pick_stack_weak_refs (ClutterPickStack *pick_stack)
+{
+ int i;
+
+ g_assert (!pick_stack->sealed);
+
+ for (i = 0; i < pick_stack->vertices_stack->len; i++)
+ {
+ PickRecord *rec =
+ &g_array_index (pick_stack->vertices_stack, PickRecord, i);
+
+ if (rec->actor)
+ g_object_add_weak_pointer (G_OBJECT (rec->actor),
+ (gpointer) &rec->actor);
+ }
+}
+
+static void
+remove_pick_stack_weak_refs (ClutterPickStack *pick_stack)
+{
+ int i;
+
+ for (i = 0; i < pick_stack->vertices_stack->len; i++)
+ {
+ PickRecord *rec =
+ &g_array_index (pick_stack->vertices_stack, PickRecord, i);
+
+ if (rec->actor)
+ g_object_remove_weak_pointer (G_OBJECT (rec->actor),
+ (gpointer) &rec->actor);
+ }
+}
+
+static void
+clutter_pick_stack_dispose (ClutterPickStack *pick_stack)
+{
+ remove_pick_stack_weak_refs (pick_stack);
+ g_clear_pointer (&pick_stack->matrix_stack, cogl_object_unref);
+ g_clear_pointer (&pick_stack->vertices_stack, g_array_unref);
+ g_clear_pointer (&pick_stack->clip_stack, g_array_unref);
+}
+
+/**
+ * clutter_pick_stack_new:
+ * @context: a #CoglContext
+ *
+ * Creates a new #ClutterPickStack.
+ *
+ * Returns: (transfer full): A newly created #ClutterPickStack
+ */
+ClutterPickStack *
+clutter_pick_stack_new (CoglContext *context)
+{
+ ClutterPickStack *pick_stack;
+
+ pick_stack = g_new0 (ClutterPickStack, 1);
+ g_ref_count_init (&pick_stack->ref_count);
+ pick_stack->matrix_stack = cogl_matrix_stack_new (context);
+ pick_stack->vertices_stack = g_array_new (FALSE, FALSE, sizeof (PickRecord));
+ pick_stack->clip_stack = g_array_new (FALSE, FALSE, sizeof (PickClipRecord));
+ pick_stack->current_clip_stack_top = -1;
+
+ return pick_stack;
+}
+
+/**
+ * clutter_pick_stack_ref:
+ * @pick_stack: A #ClutterPickStack
+ *
+ * Increments the reference count of @pick_stack by one.
+ *
+ * Returns: (transfer full): @pick_stack
+ */
+ClutterPickStack *
+clutter_pick_stack_ref (ClutterPickStack *pick_stack)
+{
+ g_ref_count_inc (&pick_stack->ref_count);
+ return pick_stack;
+}
+
+/**
+ * clutter_pick_stack_unref:
+ * @pick_stack: A #ClutterPickStack
+ *
+ * Decrements the reference count of @pick_stack by one, freeing the structure
+ * when the reference count reaches zero.
+ */
+void
+clutter_pick_stack_unref (ClutterPickStack *pick_stack)
+{
+ if (g_ref_count_dec (&pick_stack->ref_count))
+ {
+ clutter_pick_stack_dispose (pick_stack);
+ g_free (pick_stack);
+ }
+}
+
+void
+clutter_pick_stack_seal (ClutterPickStack *pick_stack)
+{
+ g_assert (!pick_stack->sealed);
+ add_pick_stack_weak_refs (pick_stack);
+ pick_stack->sealed = TRUE;
+}
+
+void
+clutter_pick_stack_log_pick (ClutterPickStack *pick_stack,
+ const graphene_point_t vertices[4],
+ ClutterActor *actor)
+{
+ PickRecord rec;
+
+ g_return_if_fail (actor != NULL);
+
+ g_assert (!pick_stack->sealed);
+
+ memcpy (rec.vertices, vertices, 4 * sizeof (graphene_point_t));
+ rec.actor = actor;
+ rec.clip_index = pick_stack->current_clip_stack_top;
+
+ g_array_append_val (pick_stack->vertices_stack, rec);
+}
+
+void
+clutter_pick_stack_push_clip (ClutterPickStack *pick_stack,
+ const graphene_point_t vertices[4])
+{
+ PickClipRecord clip;
+
+ g_assert (!pick_stack->sealed);
+
+ clip.prev = pick_stack->current_clip_stack_top;
+ memcpy (clip.vertices, vertices, 4 * sizeof (graphene_point_t));
+
+ g_array_append_val (pick_stack->clip_stack, clip);
+ pick_stack->current_clip_stack_top = pick_stack->clip_stack->len - 1;
+}
+
+void
+clutter_pick_stack_pop_clip (ClutterPickStack *pick_stack)
+{
+ const PickClipRecord *top;
+
+ g_assert (!pick_stack->sealed);
+ g_assert (pick_stack->current_clip_stack_top >= 0);
+
+ /* Individual elements of clip_stack are not freed. This is so they can
+ * be shared as part of a tree of different stacks used by different
+ * actors in the pick_stack. The whole clip_stack does however get
+ * freed later in clutter_pick_stack_dispose.
+ */
+
+ top = &g_array_index (pick_stack->clip_stack,
+ PickClipRecord,
+ pick_stack->current_clip_stack_top);
+
+ pick_stack->current_clip_stack_top = top->prev;
+}
+
+void
+clutter_pick_stack_push_transform (ClutterPickStack *pick_stack,
+ const graphene_matrix_t *transform)
+{
+ cogl_matrix_stack_push (pick_stack->matrix_stack);
+ cogl_matrix_stack_multiply (pick_stack->matrix_stack, transform);
+}
+
+void
+clutter_pick_stack_get_transform (ClutterPickStack *pick_stack,
+ graphene_matrix_t *out_transform)
+{
+ cogl_matrix_stack_get (pick_stack->matrix_stack, out_transform);
+}
+
+void
+clutter_pick_stack_pop_transform (ClutterPickStack *pick_stack)
+{
+ cogl_matrix_stack_pop (pick_stack->matrix_stack);
+}
+
+ClutterActor *
+clutter_pick_stack_find_actor_at (ClutterPickStack *pick_stack,
+ float x,
+ float y)
+{
+ int i;
+
+ /* Search all "painted" pickable actors from front to back. A linear search
+ * is required, and also performs fine since there is typically only
+ * on the order of dozens of actors in the list (on screen) at a time.
+ */
+ for (i = pick_stack->vertices_stack->len - 1; i >= 0; i--)
+ {
+ const PickRecord *rec =
+ &g_array_index (pick_stack->vertices_stack, PickRecord, i);
+
+ if (rec->actor && pick_record_contains_point (pick_stack, rec, x, y))
+ return rec->actor;
+ }
+
+ return NULL;
+}
diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h
index a80539666..85f54e497 100644
--- a/clutter/clutter/clutter-stage-private.h
+++ b/clutter/clutter/clutter-stage-private.h
@@ -81,15 +81,6 @@ void _clutter_stage_process_queued_events (ClutterStage *stage);
void _clutter_stage_update_input_devices (ClutterStage *stage);
gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage);
-void clutter_stage_log_pick (ClutterStage *stage,
- const graphene_point_t *vertices,
- ClutterActor *actor);
-
-void clutter_stage_push_pick_clip (ClutterStage *stage,
- const graphene_point_t *vertices);
-
-void clutter_stage_pop_pick_clip (ClutterStage *stage);
-
ClutterActor *_clutter_stage_do_pick (ClutterStage *stage,
float x,
float y,
diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
index 53a8ec9b8..350b1b4a3 100644
--- a/clutter/clutter/clutter-stage.c
+++ b/clutter/clutter/clutter-stage.c
@@ -125,10 +125,7 @@ struct _ClutterStagePrivate
GTimer *fps_timer;
gint32 timer_n_frames;
- GArray *pick_stack;
- GArray *pick_clip_stack;
- int pick_clip_stack_top;
- gboolean pick_stack_frozen;
+ ClutterPickStack *pick_stack;
ClutterPickMode cached_pick_mode;
#ifdef CLUTTER_ENABLE_DEBUG
@@ -238,265 +235,14 @@ clutter_stage_get_preferred_height (ClutterActor *self,
}
static void
-add_pick_stack_weak_refs (ClutterStage *stage)
-{
- ClutterStagePrivate *priv = stage->priv;
- int i;
-
- if (priv->pick_stack_frozen)
- return;
-
- for (i = 0; i < priv->pick_stack->len; i++)
- {
- PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i);
-
- if (rec->actor)
- g_object_add_weak_pointer (G_OBJECT (rec->actor),
- (gpointer) &rec->actor);
- }
-
- priv->pick_stack_frozen = TRUE;
-}
-
-static void
-remove_pick_stack_weak_refs (ClutterStage *stage)
-{
- ClutterStagePrivate *priv = stage->priv;
- int i;
-
- if (!priv->pick_stack_frozen)
- return;
-
- for (i = 0; i < priv->pick_stack->len; i++)
- {
- PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i);
-
- if (rec->actor)
- g_object_remove_weak_pointer (G_OBJECT (rec->actor),
- (gpointer) &rec->actor);
- }
-
- priv->pick_stack_frozen = FALSE;
-}
-
-static void
_clutter_stage_clear_pick_stack (ClutterStage *stage)
{
ClutterStagePrivate *priv = stage->priv;
- remove_pick_stack_weak_refs (stage);
- g_array_set_size (priv->pick_stack, 0);
- g_array_set_size (priv->pick_clip_stack, 0);
- priv->pick_clip_stack_top = -1;
+ g_clear_pointer (&priv->pick_stack, clutter_pick_stack_unref);
priv->cached_pick_mode = CLUTTER_PICK_NONE;
}
-void
-clutter_stage_log_pick (ClutterStage *stage,
- const graphene_point_t *vertices,
- ClutterActor *actor)
-{
- ClutterStagePrivate *priv;
- PickRecord rec;
-
- g_return_if_fail (CLUTTER_IS_STAGE (stage));
- g_return_if_fail (actor != NULL);
-
- priv = stage->priv;
-
- g_assert (!priv->pick_stack_frozen);
-
- memcpy (rec.vertex, vertices, 4 * sizeof (graphene_point_t));
- rec.actor = actor;
- rec.clip_stack_top = priv->pick_clip_stack_top;
-
- g_array_append_val (priv->pick_stack, rec);
-}
-
-void
-clutter_stage_push_pick_clip (ClutterStage *stage,
- const graphene_point_t *vertices)
-{
- ClutterStagePrivate *priv;
- PickClipRecord clip;
-
- g_return_if_fail (CLUTTER_IS_STAGE (stage));
-
- priv = stage->priv;
-
- g_assert (!priv->pick_stack_frozen);
-
- clip.prev = priv->pick_clip_stack_top;
- memcpy (clip.vertex, vertices, 4 * sizeof (graphene_point_t));
-
- g_array_append_val (priv->pick_clip_stack, clip);
- priv->pick_clip_stack_top = priv->pick_clip_stack->len - 1;
-}
-
-void
-clutter_stage_pop_pick_clip (ClutterStage *stage)
-{
- ClutterStagePrivate *priv;
- const PickClipRecord *top;
-
- g_return_if_fail (CLUTTER_IS_STAGE (stage));
-
- priv = stage->priv;
-
- g_assert (!priv->pick_stack_frozen);
- g_assert (priv->pick_clip_stack_top >= 0);
-
- /* Individual elements of pick_clip_stack are not freed. This is so they
- * can be shared as part of a tree of different stacks used by different
- * actors in the pick_stack. The whole pick_clip_stack does however get
- * freed later in _clutter_stage_clear_pick_stack.
- */
-
- top = &g_array_index (priv->pick_clip_stack,
- PickClipRecord,
- priv->pick_clip_stack_top);
-
- priv->pick_clip_stack_top = top->prev;
-}
-
-static gboolean
-is_quadrilateral_axis_aligned_rectangle (const graphene_point_t *vertices)
-{
- int i;
-
- for (i = 0; i < 4; i++)
- {
- if (!G_APPROX_VALUE (vertices[i].x,
- vertices[(i + 1) % 4].x,
- FLT_EPSILON) &&
- !G_APPROX_VALUE (vertices[i].y,
- vertices[(i + 1) % 4].y,
- FLT_EPSILON))
- return FALSE;
- }
- return TRUE;
-}
-
-static gboolean
-is_inside_axis_aligned_rectangle (const graphene_point_t *point,
- const graphene_point_t *vertices)
-{
- float min_x = FLT_MAX;
- float max_x = -FLT_MAX;
- float min_y = FLT_MAX;
- float max_y = -FLT_MAX;
- int i;
-
- for (i = 0; i < 3; i++)
- {
- min_x = MIN (min_x, vertices[i].x);
- min_y = MIN (min_y, vertices[i].y);
- max_x = MAX (max_x, vertices[i].x);
- max_y = MAX (max_y, vertices[i].y);
- }
-
- return (point->x >= min_x &&
- point->y >= min_y &&
- point->x < max_x &&
- point->y < max_y);
-}
-
-static int
-clutter_point_compare_line (const graphene_point_t *p,
- const graphene_point_t *a,
- const graphene_point_t *b)
-{
- graphene_vec3_t vec_pa;
- graphene_vec3_t vec_pb;
- graphene_vec3_t cross;
- float cross_z;
-
- graphene_vec3_init (&vec_pa, p->x - a->x, p->y - a->y, 0.f);
- graphene_vec3_init (&vec_pb, p->x - b->x, p->y - b->y, 0.f);
- graphene_vec3_cross (&vec_pa, &vec_pb, &cross);
- cross_z = graphene_vec3_get_z (&cross);
-
- if (cross_z > 0.f)
- return 1;
- else if (cross_z < 0.f)
- return -1;
- else
- return 0;
-}
-
-static gboolean
-is_inside_unaligned_rectangle (const graphene_point_t *point,
- const graphene_point_t *vertices)
-{
- unsigned int i;
- int first_side;
-
- first_side = 0;
-
- for (i = 0; i < 4; i++)
- {
- int side;
-
- side = clutter_point_compare_line (point,
- &vertices[i],
- &vertices[(i + 1) % 4]);
-
- if (side)
- {
- if (first_side == 0)
- first_side = side;
- else if (side != first_side)
- return FALSE;
- }
- }
-
- if (first_side == 0)
- return FALSE;
-
- return TRUE;
-}
-
-static gboolean
-is_inside_input_region (const graphene_point_t *point,
- const graphene_point_t *vertices)
-{
-
- if (is_quadrilateral_axis_aligned_rectangle (vertices))
- return is_inside_axis_aligned_rectangle (point, vertices);
- else
- return is_inside_unaligned_rectangle (point, vertices);
-}
-
-static gboolean
-pick_record_contains_point (ClutterStage *stage,
- const PickRecord *rec,
- float x,
- float y)
-{
- const graphene_point_t point = GRAPHENE_POINT_INIT (x, y);
- ClutterStagePrivate *priv;
- int clip_index;
-
- if (!is_inside_input_region (&point, rec->vertex))
- return FALSE;
-
- priv = stage->priv;
- clip_index = rec->clip_stack_top;
- while (clip_index >= 0)
- {
- const PickClipRecord *clip = &g_array_index (priv->pick_clip_stack,
- PickClipRecord,
- clip_index);
-
- if (!is_inside_input_region (&point, clip->vertex))
- return FALSE;
-
- clip_index = clip->prev;
- }
-
- return TRUE;
-}
-
static void
clutter_stage_add_redraw_clip (ClutterStage *stage,
cairo_rectangle_int_t *clip)
@@ -1392,11 +1138,11 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage,
ClutterStageView *view)
{
ClutterStagePrivate *priv = stage->priv;
- int i;
+ ClutterActor *actor;
COGL_TRACE_BEGIN_SCOPED (ClutterStagePickView, "Pick (view)");
- if (mode != priv->cached_pick_mode)
+ if (!priv->pick_stack || mode != priv->cached_pick_mode)
{
ClutterPickContext *pick_context;
@@ -1405,26 +1151,14 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage,
pick_context = clutter_pick_context_new_for_view (view, mode);
clutter_actor_pick (CLUTTER_ACTOR (stage), pick_context);
+ priv->pick_stack = clutter_pick_context_steal_stack (pick_context);
priv->cached_pick_mode = mode;
clutter_pick_context_destroy (pick_context);
-
- add_pick_stack_weak_refs (stage);
- }
-
- /* Search all "painted" pickable actors from front to back. A linear search
- * is required, and also performs fine since there is typically only
- * on the order of dozens of actors in the list (on screen) at a time.
- */
- for (i = priv->pick_stack->len - 1; i >= 0; i--)
- {
- const PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i);
-
- if (rec->actor && pick_record_contains_point (stage, rec, x, y))
- return rec->actor;
}
- return CLUTTER_ACTOR (stage);
+ actor = clutter_pick_stack_find_actor_at (priv->pick_stack, x, y);
+ return actor ? actor : CLUTTER_ACTOR (stage);
}
/**
@@ -1636,8 +1370,6 @@ clutter_stage_finalize (GObject *object)
g_array_free (priv->paint_volume_stack, TRUE);
_clutter_stage_clear_pick_stack (stage);
- g_array_free (priv->pick_clip_stack, TRUE);
- g_array_free (priv->pick_stack, TRUE);
if (priv->fps_timer != NULL)
g_timer_destroy (priv->fps_timer);
@@ -1953,9 +1685,6 @@ clutter_stage_init (ClutterStage *self)
priv->paint_volume_stack =
g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume));
- priv->pick_stack = g_array_new (FALSE, FALSE, sizeof (PickRecord));
- priv->pick_clip_stack = g_array_new (FALSE, FALSE, sizeof (PickClipRecord));
- priv->pick_clip_stack_top = -1;
priv->cached_pick_mode = CLUTTER_PICK_NONE;
}
diff --git a/clutter/clutter/meson.build b/clutter/clutter/meson.build
index 18994df8d..f914370ec 100644
--- a/clutter/clutter/meson.build
+++ b/clutter/clutter/meson.build
@@ -150,6 +150,7 @@ clutter_sources = [
'clutter-path-constraint.c',
'clutter-path.c',
'clutter-pick-context.c',
+ 'clutter-pick-stack.c',
'clutter-property-transition.c',
'clutter-rotate-action.c',
'clutter-script.c',