Clean up grab implementation

The grab API is a relic of Clutter 0.6, and hasn't been through proper
vetting in a *long* time — mostly due to the fact that we don't really
like grabs, and point to the ::captured-event as a way to implement
"soft grabs" in toolkits and applications.

The implementation of full and device grabs uses weak references on
actors instead of using the ::destroy signal, which is meant exactly for
the case of releasing pointers to actors when they are disposed.

The API naming scheme is also fairly broken, especially for
device-related grabs.

Finally, keyboard device grabs are just not implemented.

We can, in one go, clean up this mess and deprecate a bunch of badly
named API by introducing generic device grab/ungrab methods on
ClutterInputDevice, and re-implement the current API on top of them.
This commit is contained in:
Emmanuele Bassi 2011-10-10 16:45:20 +01:00
parent 33846dcf4d
commit b6dd306998
4 changed files with 216 additions and 70 deletions

View File

@ -75,6 +75,7 @@ struct _ClutterInputDevice
/* the actor that has a grab in place for the device */ /* the actor that has a grab in place for the device */
ClutterActor *pointer_grab_actor; ClutterActor *pointer_grab_actor;
ClutterActor *keyboard_grab_actor;
/* the current click count */ /* the current click count */
gint click_count; gint click_count;

View File

@ -167,6 +167,11 @@ void clutter_input_device_update_from_event (ClutterInputDev
ClutterEvent *event, ClutterEvent *event,
gboolean update_stage); gboolean update_stage);
void clutter_input_device_grab (ClutterInputDevice *device,
ClutterActor *actor);
void clutter_input_device_ungrab (ClutterInputDevice *device);
ClutterActor * clutter_input_device_get_grabbed_actor (ClutterInputDevice *device);
G_END_DECLS G_END_DECLS
#endif /* __CLUTTER_INPUT_DEVICE_H__ */ #endif /* __CLUTTER_INPUT_DEVICE_H__ */

View File

@ -2007,14 +2007,27 @@ emit_pointer_event (ClutterEvent *event,
} }
static inline void static inline void
emit_keyboard_event (ClutterEvent *event) emit_keyboard_event (ClutterEvent *event,
ClutterInputDevice *device)
{ {
ClutterMainContext *context = _clutter_context_get_default (); ClutterMainContext *context = _clutter_context_get_default ();
if (context->keyboard_grab_actor == NULL) if (context->keyboard_grab_actor == NULL &&
emit_event (event, TRUE); (device == NULL || device->keyboard_grab_actor == NULL))
{
emit_event (event, FALSE);
}
else else
{
if (context->keyboard_grab_actor != NULL)
{
clutter_actor_event (context->keyboard_grab_actor, event, FALSE); clutter_actor_event (context->keyboard_grab_actor, event, FALSE);
}
else if (device != NULL && device->keyboard_grab_actor != NULL)
{
clutter_actor_event (context->keyboard_grab_actor, event, FALSE);
}
}
} }
static gboolean static gboolean
@ -2102,7 +2115,7 @@ _clutter_process_event_details (ClutterActor *stage,
} }
} }
emit_keyboard_event (event); emit_keyboard_event (event, device);
} }
break; break;
@ -2411,23 +2424,34 @@ clutter_set_default_frame_rate (guint frames_per_sec)
static void static void
on_pointer_grab_weak_notify (gpointer data, on_grab_actor_destroy (ClutterActor *actor,
GObject *where_the_object_was) ClutterInputDevice *device)
{ {
ClutterInputDevice *dev = (ClutterInputDevice *)data; if (device == NULL)
ClutterMainContext *context;
context = _clutter_context_get_default ();
if (dev)
{ {
dev->pointer_grab_actor = NULL; ClutterMainContext *context = _clutter_context_get_default ();
clutter_ungrab_pointer_for_device (dev->id);
} if (context->pointer_grab_actor == actor)
else
{
context->pointer_grab_actor = NULL;
clutter_ungrab_pointer (); clutter_ungrab_pointer ();
if (context->keyboard_grab_actor == actor)
clutter_ungrab_keyboard ();
return;
}
switch (device->device_type)
{
case CLUTTER_POINTER_DEVICE:
device->pointer_grab_actor = NULL;
break;
case CLUTTER_KEYBOARD_DEVICE:
device->keyboard_grab_actor = NULL;
break;
default:
g_assert_not_reached ();
} }
} }
@ -2446,8 +2470,10 @@ on_pointer_grab_weak_notify (gpointer data,
* using the #ClutterActor::captured-event signal should always be the * using the #ClutterActor::captured-event signal should always be the
* preferred way to intercept event delivery to reactive actors.</para></note> * preferred way to intercept event delivery to reactive actors.</para></note>
* *
* If you wish to grab all the pointer events for a specific input device, * This function should rarely be used.
* you should use clutter_grab_pointer_for_device(). *
* If a grab is required, you are strongly encouraged to use a specific
* input device by calling clutter_input_device_grab().
* *
* Since: 0.6 * Since: 0.6
*/ */
@ -2463,24 +2489,152 @@ clutter_grab_pointer (ClutterActor *actor)
if (context->pointer_grab_actor == actor) if (context->pointer_grab_actor == actor)
return; return;
if (context->pointer_grab_actor) if (context->pointer_grab_actor != NULL)
{ {
g_object_weak_unref (G_OBJECT (context->pointer_grab_actor), g_signal_handlers_disconnect_by_func (context->pointer_grab_actor,
on_pointer_grab_weak_notify, G_CALLBACK (on_grab_actor_destroy),
NULL); NULL);
context->pointer_grab_actor = NULL; context->pointer_grab_actor = NULL;
} }
if (actor) if (actor != NULL)
{ {
context->pointer_grab_actor = actor; context->pointer_grab_actor = actor;
g_object_weak_ref (G_OBJECT (actor), g_signal_connect (context->pointer_grab_actor, "destroy",
on_pointer_grab_weak_notify, G_CALLBACK (on_grab_actor_destroy),
NULL); NULL);
} }
} }
/**
* clutter_input_device_grab:
* @device: a #ClutterInputDevice
* @actor: a #ClutterActor
*
* Acquires a grab on @actor for the given @device.
*
* Any event coming from @device will be delivered to @actor, bypassing
* the usual event delivery mechanism, until the grab is released by
* calling clutter_input_device_ungrab().
*
* The grab is client-side: even if the windowing system used by the Clutter
* backend has the concept of "device grabs", Clutter will not use them.
*
* Only #ClutterInputDevice of types %CLUTTER_POINTER_DEVICE and
* %CLUTTER_KEYBOARD_DEVICE can hold a grab.
*
* Since: 1.10
*/
void
clutter_input_device_grab (ClutterInputDevice *device,
ClutterActor *actor)
{
ClutterActor **grab_actor;
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
switch (device->device_type)
{
case CLUTTER_POINTER_DEVICE:
grab_actor = &(device->pointer_grab_actor);
break;
case CLUTTER_KEYBOARD_DEVICE:
grab_actor = &(device->keyboard_grab_actor);
break;
default:
g_critical ("Only pointer and keyboard devices can grab an actor");
return;
}
if (*grab_actor != NULL)
{
g_signal_handlers_disconnect_by_func (*grab_actor,
G_CALLBACK (on_grab_actor_destroy),
device);
}
*grab_actor = actor;
g_signal_connect (*grab_actor,
"destroy",
G_CALLBACK (on_grab_actor_destroy),
device);
}
/**
* clutter_input_device_ungrab:
* @device: a #ClutterInputDevice
*
* Releases the grab on the @device, if one is in place.
*
* Since: 1.10
*/
void
clutter_input_device_ungrab (ClutterInputDevice *device)
{
ClutterActor **grab_actor;
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
switch (device->device_type)
{
case CLUTTER_POINTER_DEVICE:
grab_actor = &(device->pointer_grab_actor);
break;
case CLUTTER_KEYBOARD_DEVICE:
grab_actor = &(device->keyboard_grab_actor);
break;
default:
return;
}
if (*grab_actor == NULL)
return;
g_signal_handlers_disconnect_by_func (*grab_actor,
G_CALLBACK (on_grab_actor_destroy),
device);
*grab_actor = NULL;
}
/**
* clutter_input_device_get_grabbed_actor:
* @device: a #ClutterInputDevice
*
* Retrieves a pointer to the #ClutterActor currently grabbing all
* the events coming from @device.
*
* Return value: (transfer none): a #ClutterActor, or %NULL
*
* Since: 1.10
*/
ClutterActor *
clutter_input_device_get_grabbed_actor (ClutterInputDevice *device)
{
g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL);
switch (device->device_type)
{
case CLUTTER_POINTER_DEVICE:
return device->pointer_grab_actor;
case CLUTTER_KEYBOARD_DEVICE:
return device->keyboard_grab_actor;
default:
g_critical ("Only pointer and keyboard devices can grab an actor");
}
return NULL;
}
/** /**
* clutter_grab_pointer_for_device: * clutter_grab_pointer_for_device:
* @actor: a #ClutterActor * @actor: a #ClutterActor
@ -2491,6 +2645,8 @@ clutter_grab_pointer (ClutterActor *actor)
* If @id is -1 then this function is equivalent to clutter_grab_pointer(). * If @id is -1 then this function is equivalent to clutter_grab_pointer().
* *
* Since: 0.8 * Since: 0.8
*
* Deprecated: 1.10: Use clutter_input_device_grab() instead.
*/ */
void void
clutter_grab_pointer_for_device (ClutterActor *actor, clutter_grab_pointer_for_device (ClutterActor *actor,
@ -2503,7 +2659,11 @@ clutter_grab_pointer_for_device (ClutterActor *actor,
/* essentially a global grab */ /* essentially a global grab */
if (id_ == -1) if (id_ == -1)
{ {
if (actor == NULL)
clutter_ungrab_pointer ();
else
clutter_grab_pointer (actor); clutter_grab_pointer (actor);
return; return;
} }
@ -2511,25 +2671,13 @@ clutter_grab_pointer_for_device (ClutterActor *actor,
if (dev == NULL) if (dev == NULL)
return; return;
if (dev->pointer_grab_actor == actor) if (dev->device_type != CLUTTER_POINTER_DEVICE)
return; return;
if (dev->pointer_grab_actor) if (actor == NULL)
{ clutter_input_device_ungrab (dev);
g_object_weak_unref (G_OBJECT (dev->pointer_grab_actor), else
on_pointer_grab_weak_notify, clutter_input_device_grab (dev, actor);
dev);
dev->pointer_grab_actor = NULL;
}
if (actor)
{
dev->pointer_grab_actor = actor;
g_object_weak_ref (G_OBJECT (actor),
on_pointer_grab_weak_notify,
dev);
}
} }
@ -2553,6 +2701,8 @@ clutter_ungrab_pointer (void)
* Removes an existing grab of the pointer events for device @id_. * Removes an existing grab of the pointer events for device @id_.
* *
* Since: 0.8 * Since: 0.8
*
* Deprecated: 1.10: Use clutter_input_device_ungrab() instead.
*/ */
void void
clutter_ungrab_pointer_for_device (gint id_) clutter_ungrab_pointer_for_device (gint id_)
@ -2580,18 +2730,6 @@ clutter_get_pointer_grab (void)
} }
static void
on_keyboard_grab_weak_notify (gpointer data,
GObject *where_the_object_was)
{
ClutterMainContext *context;
context = _clutter_context_get_default ();
context->keyboard_grab_actor = NULL;
clutter_ungrab_keyboard ();
}
/** /**
* clutter_grab_keyboard: * clutter_grab_keyboard:
* @actor: a #ClutterActor * @actor: a #ClutterActor
@ -2622,20 +2760,20 @@ clutter_grab_keyboard (ClutterActor *actor)
if (context->keyboard_grab_actor == actor) if (context->keyboard_grab_actor == actor)
return; return;
if (context->keyboard_grab_actor) if (context->keyboard_grab_actor != NULL)
{ {
g_object_weak_unref (G_OBJECT (context->keyboard_grab_actor), g_signal_handlers_disconnect_by_func (context->keyboard_grab_actor,
on_keyboard_grab_weak_notify, G_CALLBACK (on_grab_actor_destroy),
NULL); NULL);
context->keyboard_grab_actor = NULL; context->keyboard_grab_actor = NULL;
} }
if (actor) if (actor != NULL)
{ {
context->keyboard_grab_actor = actor; context->keyboard_grab_actor = actor;
g_object_weak_ref (G_OBJECT (actor), g_signal_connect (context->keyboard_grab_actor, "destroy",
on_keyboard_grab_weak_notify, G_CALLBACK (on_grab_actor_destroy),
NULL); NULL);
} }
} }

View File

@ -96,7 +96,7 @@ void clutter_main (void);
void clutter_main_quit (void); void clutter_main_quit (void);
gint clutter_main_level (void); gint clutter_main_level (void);
#ifndef CLUTTER_DISABLE_DEPRECATED #if !defined(CLUTTER_DISABLE_DEPRECATED) || defined(CLUTTER_COMPILATION)
void clutter_redraw (ClutterStage *stage) void clutter_redraw (ClutterStage *stage)
G_GNUC_DEPRECATED_FOR (clutter_stage_ensure_redraw); G_GNUC_DEPRECATED_FOR (clutter_stage_ensure_redraw);
#endif #endif
@ -110,7 +110,7 @@ gulong clutter_get_timestamp (void);
gboolean clutter_get_accessibility_enabled (void); gboolean clutter_get_accessibility_enabled (void);
/* Threading functions */ /* Threading functions */
#ifndef CLUTTER_DISABLE_DEPRECATED #if !defined(CLUTTER_DISABLE_DEPRECATED) || defined(CLUTTER_COMPILATION)
void clutter_threads_init (void); void clutter_threads_init (void);
#endif #endif
@ -133,7 +133,7 @@ guint clutter_threads_add_timeout_full (gint priority,
gpointer data, gpointer data,
GDestroyNotify notify); GDestroyNotify notify);
#ifndef CLUTTER_DISABLE_DEPRECATED #if !defined(CLUTTER_DISABLE_DEPRECATED) || defined(CLUTTER_COMPILATION)
guint clutter_threads_add_frame_source (guint fps, guint clutter_threads_add_frame_source (guint fps,
GSourceFunc func, GSourceFunc func,
gpointer data); gpointer data);
@ -149,7 +149,7 @@ guint clutter_threads_add_repaint_func (GSourceFunc func,
GDestroyNotify notify); GDestroyNotify notify);
void clutter_threads_remove_repaint_func (guint handle_id); void clutter_threads_remove_repaint_func (guint handle_id);
#ifndef CLUTTER_DISABLE_DEPRECATED #if !defined(CLUTTER_DISABLE_DEPRECATED) || defined(CLUTTER_COMPILATION)
void clutter_set_motion_events_enabled (gboolean enable); void clutter_set_motion_events_enabled (gboolean enable);
gboolean clutter_get_motion_events_enabled (void); gboolean clutter_get_motion_events_enabled (void);
#endif /* CLUTTER_DISABLE_DEPRECATED */ #endif /* CLUTTER_DISABLE_DEPRECATED */
@ -171,9 +171,11 @@ ClutterFontFlags clutter_get_font_flags (void);
ClutterInputDevice *clutter_get_input_device_for_id (gint id_); ClutterInputDevice *clutter_get_input_device_for_id (gint id_);
#if !defined(CLUTTER_DISABLE_DEPRECATED) || defined(CLUTTER_COMPILATION)
void clutter_grab_pointer_for_device (ClutterActor *actor, void clutter_grab_pointer_for_device (ClutterActor *actor,
gint id_); gint id_);
void clutter_ungrab_pointer_for_device (gint id_); void clutter_ungrab_pointer_for_device (gint id_);
#endif
PangoFontMap * clutter_get_font_map (void); PangoFontMap * clutter_get_font_map (void);