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
This commit is contained in:
parent
0d79a0faf8
commit
f411834d42
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
65
clutter/clutter/clutter-pick-stack-private.h
Normal file
65
clutter/clutter/clutter-pick-stack-private.h
Normal file
@ -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 */
|
386
clutter/clutter/clutter-pick-stack.c
Normal file
386
clutter/clutter/clutter-pick-stack.c
Normal file
@ -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;
|
||||
}
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user