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 */
ClutterActor *pointer_grab_actor;
ClutterActor *keyboard_grab_actor;
/* the current click count */
gint click_count;

View File

@ -167,6 +167,11 @@ void clutter_input_device_update_from_event (ClutterInputDev
ClutterEvent *event,
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
#endif /* __CLUTTER_INPUT_DEVICE_H__ */

View File

@ -2007,15 +2007,28 @@ emit_pointer_event (ClutterEvent *event,
}
static inline void
emit_keyboard_event (ClutterEvent *event)
emit_keyboard_event (ClutterEvent *event,
ClutterInputDevice *device)
{
ClutterMainContext *context = _clutter_context_get_default ();
if (context->keyboard_grab_actor == NULL)
emit_event (event, TRUE);
if (context->keyboard_grab_actor == NULL &&
(device == NULL || device->keyboard_grab_actor == NULL))
{
emit_event (event, FALSE);
}
else
{
if (context->keyboard_grab_actor != NULL)
{
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
is_off_stage (ClutterActor *stage,
@ -2102,7 +2115,7 @@ _clutter_process_event_details (ClutterActor *stage,
}
}
emit_keyboard_event (event);
emit_keyboard_event (event, device);
}
break;
@ -2411,23 +2424,34 @@ clutter_set_default_frame_rate (guint frames_per_sec)
static void
on_pointer_grab_weak_notify (gpointer data,
GObject *where_the_object_was)
on_grab_actor_destroy (ClutterActor *actor,
ClutterInputDevice *device)
{
ClutterInputDevice *dev = (ClutterInputDevice *)data;
ClutterMainContext *context;
if (device == NULL)
{
ClutterMainContext *context = _clutter_context_get_default ();
context = _clutter_context_get_default ();
if (dev)
{
dev->pointer_grab_actor = NULL;
clutter_ungrab_pointer_for_device (dev->id);
}
else
{
context->pointer_grab_actor = NULL;
if (context->pointer_grab_actor == actor)
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
* 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,
* you should use clutter_grab_pointer_for_device().
* This function should rarely be used.
*
* If a grab is required, you are strongly encouraged to use a specific
* input device by calling clutter_input_device_grab().
*
* Since: 0.6
*/
@ -2463,24 +2489,152 @@ clutter_grab_pointer (ClutterActor *actor)
if (context->pointer_grab_actor == actor)
return;
if (context->pointer_grab_actor)
if (context->pointer_grab_actor != NULL)
{
g_object_weak_unref (G_OBJECT (context->pointer_grab_actor),
on_pointer_grab_weak_notify,
g_signal_handlers_disconnect_by_func (context->pointer_grab_actor,
G_CALLBACK (on_grab_actor_destroy),
NULL);
context->pointer_grab_actor = NULL;
}
if (actor)
if (actor != NULL)
{
context->pointer_grab_actor = actor;
g_object_weak_ref (G_OBJECT (actor),
on_pointer_grab_weak_notify,
g_signal_connect (context->pointer_grab_actor, "destroy",
G_CALLBACK (on_grab_actor_destroy),
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:
* @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().
*
* Since: 0.8
*
* Deprecated: 1.10: Use clutter_input_device_grab() instead.
*/
void
clutter_grab_pointer_for_device (ClutterActor *actor,
@ -2503,7 +2659,11 @@ clutter_grab_pointer_for_device (ClutterActor *actor,
/* essentially a global grab */
if (id_ == -1)
{
if (actor == NULL)
clutter_ungrab_pointer ();
else
clutter_grab_pointer (actor);
return;
}
@ -2511,25 +2671,13 @@ clutter_grab_pointer_for_device (ClutterActor *actor,
if (dev == NULL)
return;
if (dev->pointer_grab_actor == actor)
if (dev->device_type != CLUTTER_POINTER_DEVICE)
return;
if (dev->pointer_grab_actor)
{
g_object_weak_unref (G_OBJECT (dev->pointer_grab_actor),
on_pointer_grab_weak_notify,
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);
}
if (actor == NULL)
clutter_input_device_ungrab (dev);
else
clutter_input_device_grab (dev, actor);
}
@ -2553,6 +2701,8 @@ clutter_ungrab_pointer (void)
* Removes an existing grab of the pointer events for device @id_.
*
* Since: 0.8
*
* Deprecated: 1.10: Use clutter_input_device_ungrab() instead.
*/
void
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:
* @actor: a #ClutterActor
@ -2622,20 +2760,20 @@ clutter_grab_keyboard (ClutterActor *actor)
if (context->keyboard_grab_actor == actor)
return;
if (context->keyboard_grab_actor)
if (context->keyboard_grab_actor != NULL)
{
g_object_weak_unref (G_OBJECT (context->keyboard_grab_actor),
on_keyboard_grab_weak_notify,
g_signal_handlers_disconnect_by_func (context->keyboard_grab_actor,
G_CALLBACK (on_grab_actor_destroy),
NULL);
context->keyboard_grab_actor = NULL;
}
if (actor)
if (actor != NULL)
{
context->keyboard_grab_actor = actor;
g_object_weak_ref (G_OBJECT (actor),
on_keyboard_grab_weak_notify,
g_signal_connect (context->keyboard_grab_actor, "destroy",
G_CALLBACK (on_grab_actor_destroy),
NULL);
}
}

View File

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