#define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include #define STAGE_WIDTH 640 #define STAGE_HEIGHT 480 #define ACTORS_X 12 #define ACTORS_Y 16 #define SHIFT_STEP STAGE_WIDTH / ACTORS_X typedef struct _State State; struct _State { ClutterActor *stage; int y, x; ClutterActor *actors[ACTORS_X * ACTORS_Y]; guint actor_width, actor_height; guint failed_pass; guint failed_idx; gboolean pass; }; struct _ShiftEffect { ClutterShaderEffect parent_instance; }; struct _ShiftEffectClass { ClutterShaderEffectClass parent_class; }; typedef struct _ShiftEffect ShiftEffect; typedef struct _ShiftEffectClass ShiftEffectClass; #define TYPE_SHIFT_EFFECT (shift_effect_get_type ()) GType shift_effect_get_type (void); G_DEFINE_TYPE (ShiftEffect, shift_effect, CLUTTER_TYPE_SHADER_EFFECT); static void shader_paint (ClutterEffect *effect, ClutterEffectPaintFlags flags) { ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect); float tex_width; ClutterActor *actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); if (g_test_verbose ()) g_debug ("shader_paint"); clutter_shader_effect_set_shader_source (shader, "uniform sampler2D tex;\n" "uniform float step;\n" "void main (void)\n" "{\n" " cogl_color_out = texture2D(tex, vec2 (cogl_tex_coord_in[0].s + step,\n" " cogl_tex_coord_in[0].t));\n" "}\n"); tex_width = clutter_actor_get_width (actor); clutter_shader_effect_set_uniform (shader, "tex", G_TYPE_INT, 1, 0); clutter_shader_effect_set_uniform (shader, "step", G_TYPE_FLOAT, 1, SHIFT_STEP / tex_width); CLUTTER_EFFECT_CLASS (shift_effect_parent_class)->paint (effect, flags); } static void shader_pick (ClutterEffect *effect, ClutterEffectPaintFlags flags) { shader_paint (effect, flags); } static void shift_effect_class_init (ShiftEffectClass *klass) { ClutterEffectClass *shader_class = CLUTTER_EFFECT_CLASS (klass); shader_class->paint = shader_paint; shader_class->pick = shader_pick; } static void shift_effect_init (ShiftEffect *self) { } static const char *test_passes[] = { "No covering actor", "Invisible covering actor", "Clipped covering actor", "Blur effect", "Shift effect", }; static gboolean on_timeout (gpointer data) { State *state = data; int test_num = 0; int y, x; ClutterActor *over_actor = NULL; /* This will cause an unclipped pick redraw that will get buffered. We'll check below that this buffer is discarded because we also need to pick non-reactive actors */ clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage), CLUTTER_PICK_REACTIVE, 10, 10); clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage), CLUTTER_PICK_REACTIVE, 10, 10); for (test_num = 0; test_num < G_N_ELEMENTS (test_passes); test_num++) { if (test_num == 0) { if (g_test_verbose ()) g_print ("No covering actor:\n"); } if (test_num == 1) { static const ClutterColor red = { 0xff, 0x00, 0x00, 0xff }; /* Create an actor that covers the whole stage but that isn't visible so it shouldn't affect the picking */ over_actor = clutter_rectangle_new_with_color (&red); clutter_actor_set_size (over_actor, STAGE_WIDTH, STAGE_HEIGHT); clutter_actor_add_child (state->stage, over_actor); clutter_actor_hide (over_actor); if (g_test_verbose ()) g_print ("Invisible covering actor:\n"); } else if (test_num == 2) { /* Make the actor visible but set a clip so that only some of the actors are accessible */ clutter_actor_show (over_actor); clutter_actor_set_clip (over_actor, state->actor_width * 2, state->actor_height * 2, state->actor_width * (ACTORS_X - 4), state->actor_height * (ACTORS_Y - 4)); if (g_test_verbose ()) g_print ("Clipped covering actor:\n"); } else if (test_num == 3) { if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) continue; clutter_actor_hide (over_actor); clutter_actor_add_effect_with_name (CLUTTER_ACTOR (state->stage), "blur", clutter_blur_effect_new ()); if (g_test_verbose ()) g_print ("With blur effect:\n"); } else if (test_num == 4) { if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) continue; clutter_actor_hide (over_actor); clutter_actor_remove_effect_by_name (CLUTTER_ACTOR (state->stage), "blur"); clutter_actor_add_effect_with_name (CLUTTER_ACTOR (state->stage), "shift", g_object_new (TYPE_SHIFT_EFFECT, NULL)); if (g_test_verbose ()) g_print ("With shift effect:\n"); } for (y = 0; y < ACTORS_Y; y++) { if (test_num == 4) x = 1; else x = 0; for (; x < ACTORS_X; x++) { gboolean pass = FALSE; gfloat pick_x; ClutterActor *actor; pick_x = x * state->actor_width + state->actor_width / 2; if (test_num == 4) pick_x -= SHIFT_STEP; actor = clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage), CLUTTER_PICK_ALL, pick_x, y * state->actor_height + state->actor_height / 2); if (g_test_verbose ()) g_print ("% 3i,% 3i / %p -> ", x, y, state->actors[y * ACTORS_X + x]); if (actor == NULL) { if (g_test_verbose ()) g_print ("NULL: FAIL\n"); } else if (actor == over_actor) { if (test_num == 2 && x >= 2 && x < ACTORS_X - 2 && y >= 2 && y < ACTORS_Y - 2) pass = TRUE; if (g_test_verbose ()) g_print ("over_actor: %s\n", pass ? "pass" : "FAIL"); } else { if (actor == state->actors[y * ACTORS_X + x] && (test_num != 2 || x < 2 || x >= ACTORS_X - 2 || y < 2 || y >= ACTORS_Y - 2)) pass = TRUE; if (g_test_verbose ()) g_print ("%p: %s\n", actor, pass ? "pass" : "FAIL"); } if (!pass) { state->failed_pass = test_num; state->failed_idx = y * ACTORS_X + x; state->pass = FALSE; } } } } clutter_main_quit (); return G_SOURCE_REMOVE; } static void actor_pick (void) { int y, x; State state; state.pass = TRUE; state.stage = clutter_test_get_stage (); state.actor_width = STAGE_WIDTH / ACTORS_X; state.actor_height = STAGE_HEIGHT / ACTORS_Y; for (y = 0; y < ACTORS_Y; y++) for (x = 0; x < ACTORS_X; x++) { ClutterColor color = { x * 255 / (ACTORS_X - 1), y * 255 / (ACTORS_Y - 1), 128, 255 }; ClutterActor *rect = clutter_rectangle_new_with_color (&color); clutter_actor_set_position (rect, x * state.actor_width, y * state.actor_height); clutter_actor_set_size (rect, state.actor_width, state.actor_height); clutter_actor_add_child (state.stage, rect); state.actors[y * ACTORS_X + x] = rect; } clutter_actor_show (state.stage); clutter_threads_add_idle (on_timeout, &state); clutter_main (); if (g_test_verbose ()) { if (!state.pass) g_test_message ("Failed pass: %s[%d], actor index: %d [%p]\n", test_passes[state.failed_pass], state.failed_pass, state.failed_idx, state.actors[state.failed_idx]); } g_assert (state.pass); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/actor/pick", actor_pick) )