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,19 +1341,10 @@ _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 .
+ */
+
+#ifndef CLUTTER_PICK_STACK_PRIVATE_H
+#define CLUTTER_PICK_STACK_PRIVATE_H
+
+#include
+
+#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 .
+ */
+
+#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
@@ -237,266 +234,15 @@ clutter_stage_get_preferred_height (ClutterActor *self,
*natural_height_p = geom.height;
}
-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',