* clutter/clutter-main.[ch]: added clutter_grab_pointer,

clutter_ungrab_pointer and clutter_get_pointer_grab, in
clutter_do_event deliver pointer related events only to the
actor with the pointer grab if a grab exists.
* clutter/clutter-private.h: added pointer_grab_actor to context.
* tests/Makefile.am:
* tests/test-grab.c: added test for testing the pointer grab.
This commit is contained in:
Øyvind Kolås 2007-11-15 17:08:48 +00:00
parent 0d082df3a5
commit 53ec33f34c
6 changed files with 360 additions and 13 deletions

View File

@ -1,3 +1,13 @@
2007-11-15 Øyvind Kolås <pippin@o-hand.com>
* clutter/clutter-main.[ch]: added clutter_grab_pointer,
clutter_ungrab_pointer and clutter_get_pointer_grab, in
clutter_do_event deliver pointer related events only to the
actor with the pointer grab if a grab exists.
* clutter/clutter-private.h: added pointer_grab_actor to context.
* tests/Makefile.am:
* tests/test-grab.c: added test for testing the pointer grab.
2007-11-15 Emmanuele Bassi <ebassi@openedhand.com> 2007-11-15 Emmanuele Bassi <ebassi@openedhand.com>
* clutter/clutter-timeline.[ch]: Add ClutterTimeline:direction * clutter/clutter-timeline.[ch]: Add ClutterTimeline:direction

View File

@ -1240,9 +1240,14 @@ clutter_do_event (ClutterEvent *event)
break; break;
case CLUTTER_ENTER: case CLUTTER_ENTER:
case CLUTTER_LEAVE: case CLUTTER_LEAVE:
{
deliver_event (event); if (context->pointer_grab_actor != NULL)
} {
clutter_actor_event (context->pointer_grab_actor, event, FALSE);
return;
}
deliver_event (event);
break; break;
case CLUTTER_DESTROY_NOTIFY: case CLUTTER_DESTROY_NOTIFY:
case CLUTTER_DELETE: case CLUTTER_DELETE:
@ -1269,6 +1274,12 @@ clutter_do_event (ClutterEvent *event)
/* Only stage gets motion events */ /* Only stage gets motion events */
event->any.source = stage; event->any.source = stage;
if (context->pointer_grab_actor != NULL)
{
clutter_actor_event (context->pointer_grab_actor, event, FALSE);
return;
}
/* Trigger handlers on stage in both capture .. */ /* Trigger handlers on stage in both capture .. */
if (!clutter_actor_event (stage, event, TRUE)) if (!clutter_actor_event (stage, event, TRUE))
{ {
@ -1288,14 +1299,24 @@ clutter_do_event (ClutterEvent *event)
clutter_event_get_coords (event, &x, &y); clutter_event_get_coords (event, &x, &y);
/* Handle release off stage */ /* Handle release off stage */
if (x >= CLUTTER_STAGE_WIDTH () || if ((x >= CLUTTER_STAGE_WIDTH () ||
y >= CLUTTER_STAGE_HEIGHT() || y >= CLUTTER_STAGE_HEIGHT() ||
x < 0 || y < 0) x < 0 || y < 0))
{ {
if (event->type == CLUTTER_BUTTON_RELEASE) if (event->type == CLUTTER_BUTTON_RELEASE)
{ {
CLUTTER_NOTE (EVENT, "Release off stage received at %i, %i", x, y); CLUTTER_NOTE (EVENT, "Release off stage received at %i, %i", x, y);
event->button.source = stage; event->button.source = stage;
if (context->pointer_grab_actor != NULL)
{
clutter_actor_event (context->pointer_grab_actor,
event, FALSE);
return;
}
deliver_event (event); deliver_event (event);
} }
break; break;
@ -1305,6 +1326,9 @@ clutter_do_event (ClutterEvent *event)
actor = _clutter_do_pick (CLUTTER_STAGE (stage), actor = _clutter_do_pick (CLUTTER_STAGE (stage),
x, y, x, y,
CLUTTER_PICK_REACTIVE); CLUTTER_PICK_REACTIVE);
event->any.source = actor;
if (!actor) if (!actor)
break; break;
@ -1316,7 +1340,6 @@ clutter_do_event (ClutterEvent *event)
CLUTTER_NOTE (EVENT, "Reactive event received at %i, %i - actor: %p", CLUTTER_NOTE (EVENT, "Reactive event received at %i, %i - actor: %p",
x, y, actor); x, y, actor);
event->any.source = actor;
/* Motion enter leave events */ /* Motion enter leave events */
if (event->type == CLUTTER_MOTION) if (event->type == CLUTTER_MOTION)
@ -1355,7 +1378,13 @@ clutter_do_event (ClutterEvent *event)
} }
else else
event_click_count_generate (event); event_click_count_generate (event);
if (context->pointer_grab_actor != NULL)
{
clutter_actor_event (context->pointer_grab_actor, event, FALSE);
return;
}
deliver_event (event); deliver_event (event);
} }
break; break;
@ -1429,3 +1458,88 @@ clutter_set_default_frame_rate (guint frames_per_sec)
if (context->frame_rate != frames_per_sec) if (context->frame_rate != frames_per_sec)
context->frame_rate = frames_per_sec; context->frame_rate = frames_per_sec;
} }
static void
on_pointer_grab_weak_notify (gpointer data,
GObject *where_the_object_was)
{
ClutterMainContext *context;
context = clutter_context_get_default ();
context->pointer_grab_actor = NULL;
clutter_ungrab_pointer ();
}
/**
* clutter_grab_pointer:
* @actor: a #ClutterActor
*
* Grabs pointer events, after the grab is done all pointer related events
* (press, motion, release, enter, leave and scroll) are delivered to this
* actor directly. The source set in the event will be the actor that would
* have received the event if the pointer grab was not in effect.
*
* Since: 0.6
*/
void
clutter_grab_pointer (ClutterActor *actor)
{
ClutterMainContext *context;
g_return_if_fail (actor == NULL || CLUTTER_IS_ACTOR (actor));
context = clutter_context_get_default ();
if (context->pointer_grab_actor == actor)
return;
if (context->pointer_grab_actor)
{
g_object_weak_unref (G_OBJECT (context->pointer_grab_actor),
on_pointer_grab_weak_notify,
NULL);
context->pointer_grab_actor = NULL;
}
if (actor)
{
context->pointer_grab_actor = actor;
g_object_weak_ref (G_OBJECT (actor),
on_pointer_grab_weak_notify,
NULL);
}
}
/**
* clutter_ungrab_pointer:
*
* Removes an existing grab of the pointer.
*
* Since: 0.6
*/
void
clutter_ungrab_pointer (void)
{
clutter_grab_pointer (NULL);
}
/**
* clutter_get_pointer_grab:
*
* Queries the current pointer grab of clutter.
*
* Return value: the actor currently holding the pointer grab, or NULL if there is no grab.
*
* Since: 0.6
*/
ClutterActor *
clutter_get_pointer_grab (void)
{
ClutterMainContext *context;
context = clutter_context_get_default ();
return context->pointer_grab_actor;
}

View File

@ -106,6 +106,11 @@ gboolean clutter_get_motion_events_enabled (void);
guint clutter_get_default_frame_rate (void); guint clutter_get_default_frame_rate (void);
void clutter_set_default_frame_rate (guint frames_per_sec); void clutter_set_default_frame_rate (guint frames_per_sec);
void clutter_grab_pointer (ClutterActor *actor);
void clutter_ungrab_pointer (void);
ClutterActor * clutter_get_pointer_grab (void);
G_END_DECLS G_END_DECLS
#endif /* _HAVE_CLUTTER_MAIN_H */ #endif /* _HAVE_CLUTTER_MAIN_H */

View File

@ -69,11 +69,11 @@ typedef struct _ClutterMainContext ClutterMainContext;
struct _ClutterMainContext struct _ClutterMainContext
{ {
ClutterBackend *backend; /* holds a pointer to the windowing ClutterBackend *backend; /* holds a pointer to the windowing
system backend */ system backend */
GQueue *events_queue; /* the main event queue */ GQueue *events_queue; /* the main event queue */
PangoFT2FontMap *font_map; PangoFT2FontMap *font_map;
guint update_idle; /* repaint idler id */ guint update_idle; /* repaint idler id */
guint is_initialized : 1; guint is_initialized : 1;
GTimer *timer; /* Used for debugging scheduler */ GTimer *timer; /* Used for debugging scheduler */
@ -85,6 +85,10 @@ struct _ClutterMainContext
GHashTable *actor_hash; /* Hash of all actors mapped to id */ GHashTable *actor_hash; /* Hash of all actors mapped to id */
guint frame_rate; /* Default FPS */ guint frame_rate; /* Default FPS */
ClutterActor *pointer_grab_actor; /* The actor having the pointer grab
(or NULL if there is no pointer grab)
*/
}; };
#define CLUTTER_CONTEXT() (clutter_context_get_default ()) #define CLUTTER_CONTEXT() (clutter_context_get_default ())

View File

@ -1,7 +1,7 @@
noinst_PROGRAMS = test-textures test-events test-offscreen test-scale \ noinst_PROGRAMS = test-textures test-events test-offscreen test-scale \
test-actors test-behave test-text test-entry test-project \ test-actors test-behave test-text test-entry test-project \
test-boxes test-perspective test-rotate test-depth \ test-boxes test-perspective test-rotate test-depth \
test-threads test-timeline test-score test-script test-model test-threads test-timeline test-score test-script test-model test-grab
INCLUDES = -I$(top_srcdir)/ INCLUDES = -I$(top_srcdir)/
LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la
@ -13,6 +13,7 @@ test_events_SOURCES = test-events.c
test_offscreen_SOURCES = test-offscreen.c test_offscreen_SOURCES = test-offscreen.c
test_scale_SOURCES = test-scale.c test_scale_SOURCES = test-scale.c
test_actors_SOURCES = test-actors.c test_actors_SOURCES = test-actors.c
test_grab_SOURCES = test-grab.c
test_behave_SOURCES = test-behave.c test_behave_SOURCES = test-behave.c
test_text_SOURCES = test-text.c test_text_SOURCES = test-text.c
test_entry_SOURCES = test-entry.c test_entry_SOURCES = test-entry.c

213
tests/test-grab.c Normal file
View File

@ -0,0 +1,213 @@
#include <clutter/clutter.h>
static void
stage_state_cb (ClutterStage *stage,
gpointer data)
{
gchar *detail = (gchar*)data;
printf("[stage signal] %s\n", detail);
}
static gboolean
debug_event_cb (ClutterActor *actor,
ClutterEvent *event,
gpointer data)
{
ClutterStage *stage = CLUTTER_STAGE (clutter_stage_get_default ());
gchar keybuf[9], *source = (gchar*)data;
int len = 0;
switch (event->type)
{
case CLUTTER_KEY_PRESS:
len = g_unichar_to_utf8 (clutter_keysym_to_unicode (event->key.keyval),
keybuf);
keybuf[len] = '\0';
printf ("[%s] KEY PRESS '%s'", source, keybuf);
break;
case CLUTTER_KEY_RELEASE:
len = g_unichar_to_utf8 (clutter_keysym_to_unicode (event->key.keyval),
keybuf);
keybuf[len] = '\0';
printf ("[%s] KEY RELEASE '%s'", source, keybuf);
break;
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 (click count:%i)",
source, event->button.click_count);
break;
case CLUTTER_BUTTON_RELEASE:
printf("[%s] BUTTON RELEASE", source);
if (clutter_event_get_source (event) == CLUTTER_ACTOR (stage))
clutter_stage_set_key_focus (stage, NULL);
else if (clutter_event_get_source (event) == actor)
clutter_stage_set_key_focus (stage, actor);
break;
case CLUTTER_SCROLL:
printf("[%s] BUTTON SCROLL", source);
break;
case CLUTTER_STAGE_STATE:
printf("[%s] STAGE STATE", source);
break;
case CLUTTER_DESTROY_NOTIFY:
printf("[%s] DESTROY NOTIFY", source);
break;
case CLUTTER_CLIENT_MESSAGE:
printf("[%s] CLIENT MESSAGE\n", source);
break;
case CLUTTER_DELETE:
printf("[%s] DELETE", source);
break;
case CLUTTER_NOTHING:
return;
}
if (clutter_event_get_source (event) == actor)
printf(" *source*");
printf("\n");
return FALSE;
}
static gboolean
grab_pointer_cb (ClutterActor *actor,
ClutterEvent *event,
gpointer data)
{
clutter_grab_pointer (actor);
return FALSE;
}
static gboolean
red_release_cb (ClutterActor *actor,
ClutterEvent *event,
gpointer data)
{
clutter_ungrab_pointer ();
return FALSE;
}
static gboolean
blue_release_cb (ClutterActor *actor,
ClutterEvent *event,
gpointer data)
{
clutter_actor_destroy (actor);
return FALSE;
}
static gboolean
green_press_cb (ClutterActor *actor,
ClutterEvent *event,
gpointer data)
{
clutter_enable_motion_events (!clutter_get_motion_events_enabled ());
g_print ("per actor motion events are now %s\n",
clutter_get_motion_events_enabled ()?"enabled":"disabled");
return FALSE;
}
static gboolean
toggle_grab_pointer_cb (ClutterActor *actor,
ClutterEvent *event,
gpointer data)
{
/* we only deal with the event if the source is ourself */
if (event->button.source == actor)
{
if (clutter_get_pointer_grab () != NULL)
clutter_ungrab_pointer ();
else
clutter_grab_pointer (actor);
}
return FALSE;
}
int
main (int argc, char *argv[])
{
ClutterActor *stage, *actor;
ClutterColor rcol = { 0xff, 0, 0, 0xff},
bcol = { 0, 0, 0xff, 0xff },
gcol = { 0, 0xff, 0, 0xff },
ycol = { 0xff, 0xff, 0, 0xff },
ncol = { 0, 0, 0, 0xff };
clutter_init (&argc, &argv);
g_print ("Red box: aquire grab on press, releases it on next button release\n");
g_print ("Blue box: aquire grab on press, destroys the blue box actor on release\n");
g_print ("Yellow box: aquire grab on press, releases grab on next press on yellow box\n");
g_print ("Green box: toggle per actor motion events.\n\n");
stage = clutter_stage_get_default ();
g_signal_connect (stage, "event", G_CALLBACK (debug_event_cb), "stage");
g_signal_connect (stage, "fullscreen",
G_CALLBACK (stage_state_cb), "fullscreen");
g_signal_connect (stage, "unfullscreen",
G_CALLBACK (stage_state_cb), "unfullscreen");
g_signal_connect (stage, "activate",
G_CALLBACK (stage_state_cb), "activate");
g_signal_connect (stage, "deactivate",
G_CALLBACK (stage_state_cb), "deactivate");
actor = clutter_rectangle_new_with_color (&rcol);
clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_position (actor, 100, 100);
clutter_actor_set_reactive (actor);
clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL);
g_signal_connect (actor, "event", G_CALLBACK (debug_event_cb), "red box");
g_signal_connect (actor, "button-press-event",
G_CALLBACK (grab_pointer_cb), NULL);
g_signal_connect (actor, "button-release-event",
G_CALLBACK (red_release_cb), NULL);
actor = clutter_rectangle_new_with_color (&ycol);
clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_position (actor, 100, 300);
clutter_actor_set_reactive (actor);
clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL);
g_signal_connect (actor, "event", G_CALLBACK (debug_event_cb), "yellow box");
g_signal_connect (actor, "button-press-event",
G_CALLBACK (toggle_grab_pointer_cb), NULL);
actor = clutter_rectangle_new_with_color (&bcol);
clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_position (actor, 300, 100);
clutter_actor_set_reactive (actor);
clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL);
g_signal_connect (actor, "event",
G_CALLBACK (debug_event_cb), "blue box");
g_signal_connect (actor, "button-press-event",
G_CALLBACK (grab_pointer_cb), NULL);
g_signal_connect (actor, "button-release-event",
G_CALLBACK (blue_release_cb), NULL);
actor = clutter_rectangle_new_with_color (&gcol);
clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_position (actor, 300, 300);
clutter_actor_set_reactive (actor);
clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL);
g_signal_connect (actor, "event",
G_CALLBACK (debug_event_cb), "green box");
g_signal_connect (actor, "button-press-event",
G_CALLBACK (green_press_cb), NULL);
clutter_actor_show_all (CLUTTER_ACTOR (stage));
clutter_main();
return 0;
}