2007-10-03 Matthew Allum <mallum@openedhand.com>

* clutter/clutter-actor.c:
        * clutter/clutter-event.c:
        * clutter/clutter-event.h:
        * clutter/clutter-main.c:
        * clutter/clutter-private.h:
        * clutter/eglnative/clutter-event-egl.c:
        * clutter/eglx/clutter-event-egl.c:
        * clutter/glx/clutter-event-glx.c:
        * clutter/sdl/clutter-event-sdl.c:
        Remove seperate double/triple click events and replace with
        a click_count member.
        Move calculating click counts from backend to do_event()
        Initial implementation of ENTER/LEAVE events.

        * tests/test-events.c:
        Add code to test above.

        * tests/test-behave.c:
        Sync with newer API.

        * clutter/clutter-score.c:
        Silence a warning.
This commit is contained in:
Matthew Allum 2007-10-03 09:28:16 +00:00
parent 4189c66a21
commit bc13e907cc
13 changed files with 328 additions and 270 deletions

View File

@ -1,3 +1,28 @@
2007-10-03 Matthew Allum <mallum@openedhand.com>
* clutter/clutter-actor.c:
* clutter/clutter-event.c:
* clutter/clutter-event.h:
* clutter/clutter-main.c:
* clutter/clutter-private.h:
* clutter/eglnative/clutter-event-egl.c:
* clutter/eglx/clutter-event-egl.c:
* clutter/glx/clutter-event-glx.c:
* clutter/sdl/clutter-event-sdl.c:
Remove seperate double/triple click events and replace with
a click_count member.
Move calculating click counts from backend to do_event()
Initial implementation of ENTER/LEAVE events.
* tests/test-events.c:
Add code to test above.
* tests/test-behave.c:
Sync with newer API.
* clutter/clutter-score.c:
Silence a warning.
2007-10-01 Emmanuele Bassi <ebassi@openedhand.com>
* clutter/clutter-fixed.h: Add CLUTTER_FIXED_TO_INT() and

View File

@ -2942,8 +2942,6 @@ clutter_actor_event (ClutterActor *actor,
case CLUTTER_NOTHING:
break;
case CLUTTER_BUTTON_PRESS:
case CLUTTER_2BUTTON_PRESS:
case CLUTTER_3BUTTON_PRESS:
signal_num = BUTTON_PRESS_EVENT;
break;
case CLUTTER_BUTTON_RELEASE:

View File

@ -39,12 +39,6 @@
* Windowing events handled by Clutter.
*/
/* multiple button click detection */
static guint32 button_click_time[2] = { 0, 0 };
static guint32 button_number[2] = { -1, -1 };
static gint button_x[2] = { 0, 0 };
static gint button_y[2] = { 0, 0 };
/**
* clutter_event_type:
* @event: a #ClutterEvent
@ -76,24 +70,7 @@ clutter_event_get_time (ClutterEvent *event)
{
g_return_val_if_fail (event != NULL, CLUTTER_CURRENT_TIME);
switch (event->type)
{
case CLUTTER_KEY_PRESS:
case CLUTTER_KEY_RELEASE:
return event->key.time;
case CLUTTER_BUTTON_PRESS:
case CLUTTER_2BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE:
return event->button.time;
case CLUTTER_MOTION:
return event->motion.time;
case CLUTTER_SCROLL:
return event->scroll.time;
default:
break;
}
return CLUTTER_CURRENT_TIME;
return event->any.time;
}
/**
@ -117,8 +94,6 @@ clutter_event_get_state (ClutterEvent *event)
case CLUTTER_KEY_RELEASE:
return event->key.modifier_state;
case CLUTTER_BUTTON_PRESS:
case CLUTTER_2BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE:
return event->button.modifier_state;
case CLUTTER_MOTION:
return event->motion.modifier_state;
@ -161,10 +136,10 @@ clutter_event_get_coords (ClutterEvent *event,
case CLUTTER_DESTROY_NOTIFY:
case CLUTTER_CLIENT_MESSAGE:
case CLUTTER_DELETE:
case CLUTTER_ENTER:
case CLUTTER_LEAVE:
break;
case CLUTTER_BUTTON_PRESS:
case CLUTTER_2BUTTON_PRESS:
case CLUTTER_3BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE:
event_x = event->button.x;
event_y = event->button.y;
@ -212,7 +187,6 @@ clutter_event_get_source (ClutterEvent *event)
res = event->key.source;
break;
case CLUTTER_BUTTON_PRESS:
case CLUTTER_2BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE:
res = event->button.source;
break;
@ -504,71 +478,3 @@ clutter_events_pending (void)
return g_queue_is_empty (context->events_queue) == FALSE;
}
/* Backend helpers (private) */
static void
synthesize_click (ClutterBackend *backend,
ClutterEvent *event,
gint n_clicks)
{
ClutterEvent temp_event;
temp_event = *event;
temp_event.type = (n_clicks == 2) ? CLUTTER_2BUTTON_PRESS
: CLUTTER_3BUTTON_PRESS;
clutter_event_put (&temp_event);
}
/* post process a button to synthesize double clicks etc */
void
_clutter_event_button_generate (ClutterBackend *backend,
ClutterEvent *event)
{
guint double_click_time, double_click_distance;
double_click_distance = clutter_backend_get_double_click_distance (backend);
double_click_time = clutter_backend_get_double_click_time (backend);
if ((event->button.time < (button_click_time[1] + 2 * double_click_time))
&& (event->button.button == button_number[1])
&& (ABS (event->button.x - button_x[1]) <= double_click_distance)
&& (ABS (event->button.y - button_y[1]) <= double_click_distance))
{
synthesize_click (backend, event, 3);
button_click_time[1] = 0;
button_click_time[0] = 0;
button_number[1] = -1;
button_number[0] = -1;
button_x[0] = button_x[1] = 0;
button_y[0] = button_y[1] = 0;
}
else if ((event->button.time < (button_click_time[0] + double_click_time)) &&
(event->button.button == button_number[0]) &&
(ABS (event->button.x - button_x[0]) <= double_click_distance) &&
(ABS (event->button.y - button_y[0]) <= double_click_distance))
{
synthesize_click (backend, event, 2);
button_click_time[1] = button_click_time[0];
button_click_time[0] = event->button.time;
button_number[1] = button_number[0];
button_number[0] = event->button.button;
button_x[1] = button_x[0];
button_x[0] = event->button.x;
button_y[1] = button_y[0];
button_y[0] = event->button.y;
}
else
{
button_click_time[1] = 0;
button_click_time[0] = event->button.time;
button_number[1] = -1;
button_number[0] = event->button.button;
button_x[1] = 0;
button_x[0] = event->button.x;
button_y[1] = 0;
button_y[0] = event->button.y;
}
}

View File

@ -58,13 +58,12 @@ typedef enum {
typedef enum
{
CLUTTER_NOTHING = 0,
CLUTTER_KEY_PRESS,
CLUTTER_KEY_RELEASE,
CLUTTER_MOTION,
CLUTTER_ENTER,
CLUTTER_LEAVE,
CLUTTER_BUTTON_PRESS,
CLUTTER_2BUTTON_PRESS, /* Double click */
CLUTTER_3BUTTON_PRESS, /* Triple click */
CLUTTER_BUTTON_RELEASE,
CLUTTER_SCROLL,
CLUTTER_STAGE_STATE,
@ -96,6 +95,7 @@ typedef struct _ClutterKeyEvent ClutterKeyEvent;
typedef struct _ClutterMotionEvent ClutterMotionEvent;
typedef struct _ClutterScrollEvent ClutterScrollEvent;
typedef struct _ClutterStageStateEvent ClutterStageStateEvent;
typedef struct _ClutterCrossingEvent ClutterCrossingEvent;
typedef struct _ClutterInputDevice ClutterInputDevice;
@ -132,6 +132,18 @@ struct _ClutterButtonEvent
ClutterActor *source;
};
struct _ClutterCrossingEvent
{
ClutterEventType type;
guint32 time;
ClutterEventFlags flags;
gint x;
gint y;
ClutterActor *source;
ClutterActor *related;
};
struct _ClutterMotionEvent
{
ClutterEventType type;
@ -178,6 +190,7 @@ union _ClutterEvent
ClutterMotionEvent motion;
ClutterScrollEvent scroll;
ClutterStageStateEvent stage_state;
ClutterCrossingEvent crossing;
};
GType clutter_event_get_type (void) G_GNUC_CONST;

View File

@ -188,153 +188,6 @@ clutter_get_motion_events_enabled (void)
return context->motion_events_per_actor;
}
/**
* clutter_do_event
* @event: a #ClutterEvent.
*
* Processes an event. This function should never be called by applications.
*
* Since: 0.4
*/
void
clutter_do_event (ClutterEvent *event)
{
/* FIXME: This should probably be clutter_cook_event() - it would
* take a raw event from the backend and 'cook' it so its more tasty.
*
*/
ClutterMainContext *context;
ClutterBackend *backend;
ClutterActor *stage;
static ClutterActor *motion_last_actor = NULL;
context = clutter_context_get_default ();
backend = context->backend;
stage = _clutter_backend_get_stage (backend);
if (!stage)
return;
CLUTTER_TIMESTAMP (EVENT, "Event received");
switch (event->type)
{
case CLUTTER_NOTHING:
break;
case CLUTTER_DESTROY_NOTIFY:
case CLUTTER_DELETE:
if (clutter_stage_event (CLUTTER_STAGE (stage), event))
clutter_main_quit ();
break;
case CLUTTER_KEY_PRESS:
case CLUTTER_KEY_RELEASE:
{
ClutterActor *actor = NULL;
actor = clutter_stage_get_key_focus (CLUTTER_STAGE(stage));
g_return_if_fail (actor != NULL);
event->key.source = g_object_ref (actor);
/* bubble up */
do
{
if (clutter_actor_event (actor, event))
break;
actor = clutter_actor_get_parent (actor);
}
while (actor != NULL);
}
break;
case CLUTTER_MOTION:
if (context->motion_events_per_actor == FALSE)
{
/* Only stage gets motion events */
event->motion.source = g_object_ref (stage);
clutter_actor_event (stage, event);
break;
}
case CLUTTER_BUTTON_PRESS:
case CLUTTER_2BUTTON_PRESS:
case CLUTTER_3BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE:
case CLUTTER_SCROLL:
{
ClutterActor *actor;
gint x,y;
clutter_event_get_coords (event, &x, &y);
/* Safety on - probably a release off stage ?
* FIXME: should likely deliver the release somehow - grabs ?
*/
if (x > CLUTTER_STAGE_WIDTH () ||
y > CLUTTER_STAGE_HEIGHT() ||
x < 0 || y < 0)
break;
/* Map the event to a reactive actor */
actor = _clutter_do_pick (CLUTTER_STAGE (stage),
x, y,
CLUTTER_PICK_REACTIVE);
CLUTTER_NOTE (EVENT, "Reactive event received at %i, %i - actor: %p",
x, y, actor);
if (event->type == CLUTTER_SCROLL)
event->scroll.source = g_object_ref (actor);
else
event->button.source = g_object_ref (actor);
/* Motion enter leave events */
if (event->type == CLUTTER_MOTION)
{
if (motion_last_actor != actor)
{
if (motion_last_actor)
; /* FIXME: leave_notify to motion_last_actor */
if (actor)
; /* FIXME: Enter notify to actor */
}
motion_last_actor = actor;
}
/* Send the event to the actor and all parents always the
* stage.
*
* FIXME: for an optimisation should check if there are
* actually any reactive actors and avoid the pick all togeather
* (signalling just the stage). Should be big help for gles.
*
* FIXME: Actors be able to stop emission.
*/
while (actor)
{
if (clutter_actor_is_reactive (actor) ||
clutter_actor_get_parent (actor) == NULL /* STAGE */ )
{
CLUTTER_NOTE (EVENT, "forwarding event to reactive actor");
if (clutter_actor_event (actor, event))
break;
}
actor = clutter_actor_get_parent (actor);
}
}
break;
case CLUTTER_STAGE_STATE:
/* fullscreen / focus - forward to stage */
clutter_stage_event (CLUTTER_STAGE (stage), event);
break;
case CLUTTER_CLIENT_MESSAGE:
break;
}
}
ClutterActor *
_clutter_do_pick (ClutterStage *stage,
gint x,
@ -1213,6 +1066,257 @@ _clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint,
return continue_emission;
}
static void
event_click_count_generate (ClutterEvent *event)
{
/* multiple button click detection */
static guint32 button_click_time[2] = { 0, 0 };
static guint32 button_number[2] = { -1, -1 };
static gint button_x[2] = { 0, 0 };
static gint button_y[2] = { 0, 0 };
guint double_click_time, double_click_distance;
ClutterBackend *backend;
ClutterMainContext *context;
context = clutter_context_get_default ();
backend = context->backend;
double_click_distance = clutter_backend_get_double_click_distance (backend);
double_click_time = clutter_backend_get_double_click_time (backend);
/* FIXME: below could be reduced in lines and handle >3 clicks */
if ((event->button.time < (button_click_time[1] + 2 * double_click_time))
&& (event->button.button == button_number[1])
&& (ABS (event->button.x - button_x[1]) <= double_click_distance)
&& (ABS (event->button.y - button_y[1]) <= double_click_distance))
{
event->button.click_count = 2;
button_click_time[1] = 0;
button_click_time[0] = 0;
button_number[1] = -1;
button_number[0] = -1;
button_x[0] = button_x[1] = 0;
button_y[0] = button_y[1] = 0;
}
else if ((event->button.time < (button_click_time[0] + double_click_time)) &&
(event->button.button == button_number[0]) &&
(ABS (event->button.x - button_x[0]) <= double_click_distance) &&
(ABS (event->button.y - button_y[0]) <= double_click_distance))
{
event->button.click_count = 3;
button_click_time[1] = button_click_time[0];
button_click_time[0] = event->button.time;
button_number[1] = button_number[0];
button_number[0] = event->button.button;
button_x[1] = button_x[0];
button_x[0] = event->button.x;
button_y[1] = button_y[0];
button_y[0] = event->button.y;
}
else
{
event->button.click_count = 1;
button_click_time[1] = 0;
button_click_time[0] = event->button.time;
button_number[1] = -1;
button_number[0] = event->button.button;
button_x[1] = 0;
button_x[0] = event->button.x;
button_y[1] = 0;
button_y[0] = event->button.y;
}
}
/**
* clutter_do_event
* @event: a #ClutterEvent.
*
* Processes an event. This function should never be called by applications.
*
* Since: 0.4
*/
void
clutter_do_event (ClutterEvent *event)
{
/* FIXME: This should probably be clutter_cook_event() - it would
* take a raw event from the backend and 'cook' it so its more tasty.
*
*/
ClutterMainContext *context;
ClutterBackend *backend;
ClutterActor *stage;
static ClutterActor *motion_last_actor = NULL;
context = clutter_context_get_default ();
backend = context->backend;
stage = _clutter_backend_get_stage (backend);
if (!stage)
return;
CLUTTER_TIMESTAMP (EVENT, "Event received");
switch (event->type)
{
case CLUTTER_NOTHING:
break;
case CLUTTER_ENTER:
case CLUTTER_LEAVE:
{
ClutterActor *actor = NULL;
actor = event->crossing.source;
while (actor)
{
if (clutter_actor_is_reactive (actor) ||
clutter_actor_get_parent (actor) == NULL /* STAGE */ )
{
CLUTTER_NOTE (EVENT, "forwarding event to reactive actor");
if (clutter_actor_event (actor, event))
break;
}
actor = clutter_actor_get_parent (actor);
}
}
break;
case CLUTTER_DESTROY_NOTIFY:
case CLUTTER_DELETE:
if (clutter_stage_event (CLUTTER_STAGE (stage), event))
clutter_main_quit ();
break;
case CLUTTER_KEY_PRESS:
case CLUTTER_KEY_RELEASE:
{
ClutterActor *actor = NULL;
actor = clutter_stage_get_key_focus (CLUTTER_STAGE(stage));
g_return_if_fail (actor != NULL);
event->key.source = g_object_ref (actor);
/* bubble up */
do
{
if (clutter_actor_event (actor, event))
break;
actor = clutter_actor_get_parent (actor);
}
while (actor != NULL);
}
break;
case CLUTTER_MOTION:
if (context->motion_events_per_actor == FALSE)
{
/* Only stage gets motion events */
event->motion.source = g_object_ref (stage);
clutter_actor_event (stage, event);
break;
}
case CLUTTER_BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE:
case CLUTTER_SCROLL:
{
ClutterActor *actor;
gint x,y;
clutter_event_get_coords (event, &x, &y);
/* Safety on - probably a release off stage ?
* FIXME: should likely deliver the release somehow - grabs ?
*/
if (x > CLUTTER_STAGE_WIDTH () ||
y > CLUTTER_STAGE_HEIGHT() ||
x < 0 || y < 0)
break;
/* Map the event to a reactive actor */
actor = _clutter_do_pick (CLUTTER_STAGE (stage),
x, y,
CLUTTER_PICK_REACTIVE);
CLUTTER_NOTE (EVENT, "Reactive event received at %i, %i - actor: %p",
x, y, actor);
if (event->type == CLUTTER_SCROLL)
event->scroll.source = g_object_ref (actor);
else
event->button.source = g_object_ref (actor);
/* Motion enter leave events */
if (event->type == CLUTTER_MOTION)
{
if (motion_last_actor != actor)
{
if (motion_last_actor && actor)
{
ClutterEvent cev;
cev.crossing.type = CLUTTER_LEAVE;
cev.crossing.time = 0; /* FIXME */
cev.crossing.flags = 0;
cev.crossing.x = x;
cev.crossing.y = y;
cev.crossing.source = g_object_ref (motion_last_actor);
cev.crossing.related = g_object_ref (actor);
clutter_event_put (&cev); /* copys */
cev.crossing.type = CLUTTER_ENTER;
cev.crossing.time = 0; /* FIXME */
cev.crossing.flags = 0;
cev.crossing.x = x;
cev.crossing.y = y;
cev.crossing.source = g_object_ref (actor);
cev.crossing.related = g_object_ref (motion_last_actor);
clutter_event_put (&cev);
}
}
motion_last_actor = actor;
}
else
event_click_count_generate (event);
/* Send the event to the actor and all parents always the
* stage.
*
* FIXME: for an optimisation should check if there are
* actually any reactive actors and avoid the pick all togeather
* (signalling just the stage). Should be big help for gles.
*
* FIXME: Actors be able to stop emission.
*/
while (actor)
{
if (clutter_actor_is_reactive (actor) ||
clutter_actor_get_parent (actor) == NULL /* STAGE */ )
{
CLUTTER_NOTE (EVENT, "forwarding event to reactive actor");
if (clutter_actor_event (actor, event))
break;
}
actor = clutter_actor_get_parent (actor);
}
}
break;
case CLUTTER_STAGE_STATE:
/* fullscreen / focus - forward to stage */
clutter_stage_event (CLUTTER_STAGE (stage), event);
break;
case CLUTTER_CLIENT_MESSAGE:
break;
}
}
void
clutter_base_init (void)
{

View File

@ -117,10 +117,6 @@ void _clutter_backend_init_events (ClutterBackend *backend);
ClutterFeatureFlags _clutter_backend_get_features (ClutterBackend *backend);
/* backend helpers */
void _clutter_event_button_generate (ClutterBackend *backend,
ClutterEvent *event);
void _clutter_feature_init (void);
ClutterActor *_clutter_do_pick (ClutterStage *stage,

View File

@ -94,7 +94,7 @@ enum
LAST_SIGNAL
};
static int score_signals[LAST_SIGNAL] = { 0 };
/* static int score_signals[LAST_SIGNAL] = { 0 }; */
static void start_entry (ClutterScoreEntry *entry);

View File

@ -235,8 +235,6 @@ clutter_event_dispatch (GSource *source,
clicked = FALSE;
}
_clutter_event_button_generate (backend, event);
g_queue_push_head (clutter_context->events_queue, event);
}

View File

@ -246,7 +246,6 @@ clutter_event_translate (ClutterBackend *backend,
event->button.modifier_state = xevent->xbutton.state;
event->button.button = xevent->xbutton.button;
_clutter_event_button_generate (backend, event);
break;
}

View File

@ -550,7 +550,6 @@ event_translate (ClutterBackend *backend,
event->button.modifier_state = xevent->xbutton.state;
event->button.button = xevent->xbutton.button;
_clutter_event_button_generate (backend, event);
break;
}

View File

@ -257,7 +257,6 @@ event_translate (ClutterBackend *backend,
event->button.y = sdl_event->button.y;
event->button.modifier_state = sdl_event->button.state;
event->button.button = sdl_event->button.button;
_clutter_event_button_generate (backend, event);
break;
}
break;

View File

@ -13,12 +13,12 @@ button_press_cb (ClutterStage *stage,
{
const gchar *click_type;
switch (event->type)
switch (event->click_count)
{
case CLUTTER_2BUTTON_PRESS:
case 2:
click_type = "double";
break;
case CLUTTER_3BUTTON_PRESS:
case 3:
click_type = "triple";
break;
default:

View File

@ -1,6 +1,6 @@
#include <clutter/clutter.h>
gboolean IsFullScreen = FALSE;
gboolean IsFullScreen = FALSE, IsMotion = FALSE;
static void
stage_state_cb (ClutterStage *stage,
@ -30,6 +30,20 @@ blue_button_cb (ClutterActor *actor,
return FALSE;
}
static gboolean
red_button_cb (ClutterActor *actor,
ClutterEvent *event,
gpointer data)
{
if (IsMotion)
IsMotion = FALSE;
else
IsMotion = TRUE;
clutter_enable_motion_events (IsMotion);
}
static void
key_focus_in_cb (ClutterActor *actor,
gpointer data)
@ -79,14 +93,15 @@ input_cb (ClutterActor *actor,
case CLUTTER_MOTION:
printf("[%s] MOTION", source);
break;
case CLUTTER_ENTER:
printf("[%s] ENTER", source);
break;
case CLUTTER_LEAVE:
printf("[%s] LEAVE", source);
break;
case CLUTTER_BUTTON_PRESS:
printf("[%s] BUTTON PRESS", source);
break;
case CLUTTER_2BUTTON_PRESS:
printf("[%s] BUTTON 2 PRESS", source);
break;
case CLUTTER_3BUTTON_PRESS:
printf("[%s] BUTTON 3 PRESS", source);
printf("[%s] BUTTON PRESS (click count:%i)",
source, event->button.click_count);
break;
case CLUTTER_BUTTON_RELEASE:
printf("[%s] BUTTON RELEASE", source);
@ -133,6 +148,8 @@ main (int argc, char *argv[])
clutter_init (&argc, &argv);
stage = clutter_stage_get_default ();
g_signal_connect (stage, "event", G_CALLBACK (input_cb), "stage");
g_signal_connect (stage, "fullscreen",
@ -159,6 +176,9 @@ main (int argc, char *argv[])
g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb),
focus_box);
/* Toggle motion - enter/leave capture */
g_signal_connect (actor, "button-press-event", G_CALLBACK (red_button_cb), NULL);
clutter_stage_set_key_focus (CLUTTER_STAGE (stage), actor);
actor = clutter_rectangle_new_with_color (&gcol);
@ -184,6 +204,7 @@ main (int argc, char *argv[])
g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box");
g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb),
focus_box);
/* Fullscreen */
g_signal_connect (actor, "button-press-event", G_CALLBACK (blue_button_cb), NULL);
actor = clutter_rectangle_new_with_color (&ncol);