Move all picking-related operations inside InputDevice

The InputDevice objects stores pointer coordinates, state, stage and
the actor under the cursor, so if the current backend provides us with
one attached to the Event structure then we want the InputDevice itself
to update its state and give us the ClutterActor underneath the
pointer's cursor.
This commit is contained in:
Emmanuele Bassi 2010-01-08 17:51:00 +00:00
parent 1f87cac069
commit 9506510d1c
9 changed files with 225 additions and 100 deletions

View File

@ -144,3 +144,27 @@ _clutter_device_manager_remove_device (ClutterDeviceManager *device_manager,
device_manager->devices = g_slist_remove (device_manager->devices, device); device_manager->devices = g_slist_remove (device_manager->devices, device);
} }
void
_clutter_device_manager_update_devices (ClutterDeviceManager *device_manager)
{
GSList *d;
/* perform a pick() on the stage at the coordinates of every
* input device, and store the actor currently under the pointer
*/
for (d = device_manager->devices; d != NULL; d = d->next)
{
ClutterInputDevice *device = d->data;
ClutterInputDeviceType device_type;
device_type = clutter_input_device_get_device_type (device);
if (device_type != CLUTTER_POINTER_DEVICE)
continue;
if (device->stage == NULL)
continue;
_clutter_input_device_update (device);
}
}

View File

@ -787,39 +787,3 @@ clutter_get_current_event (void)
return context->current_event; return context->current_event;
} }
/**
* clutter_input_device_get_device_type:
* @device: a #ClutterInputDevice
*
* Retrieves the type of @device
*
* Return value: the type of the device
*
* Since: 1.0
*/
ClutterInputDeviceType
clutter_input_device_get_device_type (ClutterInputDevice *device)
{
g_return_val_if_fail (device != NULL, CLUTTER_POINTER_DEVICE);
return device->device_type;
}
/**
* clutter_input_device_get_device_id:
* @device: a #ClutterInputDevice
*
* Retrieves the unique identifier of @device
*
* Return value: the identifier of the device
*
* Since: 1.0
*/
gint
clutter_input_device_get_device_id (ClutterInputDevice *device)
{
g_return_val_if_fail (device != NULL, -1);
return device->id;
}

View File

@ -110,13 +110,13 @@ clutter_input_device_init (ClutterInputDevice *self)
void void
_clutter_input_device_set_coords (ClutterInputDevice *device, _clutter_input_device_set_coords (ClutterInputDevice *device,
gfloat x, gint x,
gfloat y) gint y)
{ {
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
device->previous_x = floorf (x) + 0.5; device->previous_x = x;
device->previous_y = floorf (y) + 0.5; device->previous_y = y;
} }
void void
@ -137,6 +137,45 @@ _clutter_input_device_set_time (ClutterInputDevice *device,
device->previous_time = time_; device->previous_time = time_;
} }
void
_clutter_input_device_set_stage (ClutterInputDevice *device,
ClutterStage *stage)
{
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
device->stage = stage;
}
static void
cursor_weak_unref (gpointer user_data,
GObject *object_pointer)
{
ClutterInputDevice *device = user_data;
device->cursor_actor = NULL;
}
void
_clutter_input_device_set_actor (ClutterInputDevice *device,
ClutterActor *actor)
{
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
if (device->cursor_actor != NULL)
{
_clutter_actor_set_has_pointer (device->cursor_actor, FALSE);
g_object_weak_unref (G_OBJECT (device->cursor_actor),
cursor_weak_unref,
device);
}
device->cursor_actor = actor;
g_object_weak_ref (G_OBJECT (device->cursor_actor),
cursor_weak_unref,
device);
_clutter_actor_set_has_pointer (device->cursor_actor, TRUE);
}
/** /**
* clutter_input_device_get_device_type: * clutter_input_device_get_device_type:
* @device: a #ClutterInputDevice * @device: a #ClutterInputDevice
@ -173,3 +212,47 @@ clutter_input_device_get_device_id (ClutterInputDevice *device)
return device->id; return device->id;
} }
void
clutter_input_device_get_device_coords (ClutterInputDevice *device,
gint *x,
gint *y)
{
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
if (x)
*x = device->previous_x;
if (y)
*y = device->previous_y;
}
ClutterActor *
_clutter_input_device_update (ClutterInputDevice *device)
{
ClutterStage *stage;
ClutterActor *new_cursor_actor;
ClutterActor *old_cursor_actor;
gint x, y;
clutter_input_device_get_device_coords (device, &x, &y);
stage = device->stage;
old_cursor_actor = device->cursor_actor;
new_cursor_actor = _clutter_do_pick (stage, x, y, CLUTTER_PICK_REACTIVE);
if (new_cursor_actor == NULL)
new_cursor_actor = CLUTTER_ACTOR (stage);
CLUTTER_NOTE (EVENT,
"Actor under cursor (device %d, at %d, %d): %s",
clutter_input_device_get_device_id (device),
x, y,
clutter_actor_get_name (new_cursor_actor) != NULL
? clutter_actor_get_name (new_cursor_actor)
: G_OBJECT_TYPE_NAME (new_cursor_actor));
_clutter_input_device_set_actor (device, new_cursor_actor);
return device->cursor_actor;
}

View File

@ -56,8 +56,11 @@ struct _ClutterInputDeviceClass
GType clutter_input_device_get_type (void) G_GNUC_CONST; GType clutter_input_device_get_type (void) G_GNUC_CONST;
ClutterInputDeviceType clutter_input_device_get_device_type (ClutterInputDevice *device); ClutterInputDeviceType clutter_input_device_get_device_type (ClutterInputDevice *device);
gint clutter_input_device_get_device_id (ClutterInputDevice *device); gint clutter_input_device_get_device_id (ClutterInputDevice *device);
void clutter_input_device_get_device_coords (ClutterInputDevice *device,
gint *x,
gint *y);
G_END_DECLS G_END_DECLS

View File

@ -2118,7 +2118,7 @@ emit_event (ClutterEvent *event,
ClutterActor *actor; ClutterActor *actor;
gint i = 0; gint i = 0;
if (!event->any.source) if (event->any.source == NULL)
{ {
CLUTTER_NOTE (EVENT, "No source set, discarding event"); CLUTTER_NOTE (EVENT, "No source set, discarding event");
return; return;
@ -2445,6 +2445,24 @@ _clutter_process_event_details (ClutterActor *stage,
clutter_event_get_coords (event, &x, &y); clutter_event_get_coords (event, &x, &y);
if (device == NULL)
{
switch (event->type)
{
case CLUTTER_BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE:
device = event->button.device;
break;
case CLUTTER_SCROLL:
device = event->scroll.device;
break;
case CLUTTER_MOTION:
/* already handled in the MOTION case of the switch */
default:
break;
}
}
/* Only do a pick to find the source if source is not already set /* Only do a pick to find the source if source is not already set
* (as it could be in a synthetic event) * (as it could be in a synthetic event)
*/ */
@ -2467,13 +2485,24 @@ _clutter_process_event_details (ClutterActor *stage,
break; break;
} }
/* Map the event to a reactive actor */ /* if the backend provides a device then we should
actor = _clutter_do_pick (CLUTTER_STAGE (stage), * already have everything we need to update it and
x, y, * get the actor underneath
CLUTTER_PICK_REACTIVE); */
if (device == NULL)
{
CLUTTER_NOTE (EVENT, "No device found: picking");
/* Map the event to a reactive actor */
actor = _clutter_do_pick (CLUTTER_STAGE (stage),
x, y,
CLUTTER_PICK_REACTIVE);
}
else
actor = _clutter_input_device_update (device);
event->any.source = actor; event->any.source = actor;
if (!actor) if (actor == NULL)
break; break;
} }
else else
@ -2482,7 +2511,6 @@ _clutter_process_event_details (ClutterActor *stage,
actor = event->any.source; actor = event->any.source;
} }
/* FIXME: for an optimisation should check if there are /* FIXME: for an optimisation should check if there are
* actually any reactive actors and avoid the pick all together * actually any reactive actors and avoid the pick all together
* (signalling just the stage). Should be big help for gles. * (signalling just the stage). Should be big help for gles.
@ -2502,24 +2530,6 @@ _clutter_process_event_details (ClutterActor *stage,
event_click_count_generate (event); event_click_count_generate (event);
} }
if (device == NULL)
{
switch (event->type)
{
case CLUTTER_BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE:
device = event->button.device;
break;
case CLUTTER_SCROLL:
device = event->scroll.device;
break;
case CLUTTER_MOTION:
/* already handled in the MOTION case of the switch */
default:
break;
}
}
emit_pointer_event (event, device); emit_pointer_event (event, device);
break; break;
} }

View File

@ -288,14 +288,13 @@ clutter_clock_dispatch (GSource *source,
* event handling * event handling
*/ */
stages = clutter_stage_manager_list_stages (stage_manager); stages = clutter_stage_manager_list_stages (stage_manager);
g_slist_foreach (stages, (GFunc)g_object_ref, NULL); g_slist_foreach (stages, (GFunc) g_object_ref, NULL);
CLUTTER_TIMER_START (_clutter_uprof_context, master_event_process); CLUTTER_TIMER_START (_clutter_uprof_context, master_event_process);
master_clock->updated_stages = FALSE; master_clock->updated_stages = FALSE;
/* Process queued events /* Process queued events */
*/
for (l = stages; l != NULL; l = l->next) for (l = stages; l != NULL; l = l->next)
_clutter_stage_process_queued_events (l->data); _clutter_stage_process_queued_events (l->data);
@ -311,7 +310,7 @@ clutter_clock_dispatch (GSource *source,
for (l = stages; l != NULL; l = l->next) for (l = stages; l != NULL; l = l->next)
master_clock->updated_stages |= _clutter_stage_do_update (l->data); master_clock->updated_stages |= _clutter_stage_do_update (l->data);
g_slist_foreach (stages, (GFunc)g_object_unref, NULL); g_slist_foreach (stages, (GFunc) g_object_unref, NULL);
g_slist_free (stages); g_slist_free (stages);
master_clock->prev_tick = master_clock->cur_tick; master_clock->prev_tick = master_clock->cur_tick;

View File

@ -91,13 +91,16 @@ struct _ClutterInputDevice
ClutterInputDeviceType device_type; ClutterInputDeviceType device_type;
ClutterActor *cursor_actor;
ClutterActor *pointer_grab_actor; ClutterActor *pointer_grab_actor;
ClutterActor *motion_last_actor; ClutterActor *motion_last_actor;
gint click_count; gint click_count;
gfloat previous_x; ClutterStage *stage;
gfloat previous_y; gint previous_x;
gint previous_y;
guint32 previous_time; guint32 previous_time;
gint previous_button_number; gint previous_button_number;
ClutterModifierType previous_state; ClutterModifierType previous_state;
@ -189,19 +192,25 @@ PangoContext *_clutter_context_get_pango_context (ClutterMainContext *self);
#define I_(str) (g_intern_static_string ((str))) #define I_(str) (g_intern_static_string ((str)))
/* device manager */ /* device manager */
void _clutter_device_manager_add_device (ClutterDeviceManager *device_manager, void _clutter_device_manager_add_device (ClutterDeviceManager *device_manager,
ClutterInputDevice *device); ClutterInputDevice *device);
void _clutter_device_manager_remove_device (ClutterDeviceManager *device_manager, void _clutter_device_manager_remove_device (ClutterDeviceManager *device_manager,
ClutterInputDevice *device); ClutterInputDevice *device);
void _clutter_device_manager_update_devices (ClutterDeviceManager *device_manager);
/* input device */ /* input device */
void _clutter_input_device_set_coords (ClutterInputDevice *device, void _clutter_input_device_set_coords (ClutterInputDevice *device,
gfloat x, gint x,
gfloat y); gint y);
void _clutter_input_device_set_state (ClutterInputDevice *device, void _clutter_input_device_set_state (ClutterInputDevice *device,
ClutterModifierType state); ClutterModifierType state);
void _clutter_input_device_set_time (ClutterInputDevice *device, void _clutter_input_device_set_time (ClutterInputDevice *device,
guint32 time_); guint32 time_);
void _clutter_input_device_set_stage (ClutterInputDevice *device,
ClutterStage *stage);
void _clutter_input_device_set_actor (ClutterInputDevice *device,
ClutterActor *actor);
ClutterActor *_clutter_input_device_update (ClutterInputDevice *device);
/* stage manager */ /* stage manager */
void _clutter_stage_manager_add_stage (ClutterStageManager *stage_manager, void _clutter_stage_manager_add_stage (ClutterStageManager *stage_manager,
@ -223,6 +232,7 @@ void _clutter_stage_queue_event (ClutterStage *stage,
ClutterEvent *event); ClutterEvent *event);
gboolean _clutter_stage_has_queued_events (ClutterStage *stage); gboolean _clutter_stage_has_queued_events (ClutterStage *stage);
void _clutter_stage_process_queued_events (ClutterStage *stage); void _clutter_stage_process_queued_events (ClutterStage *stage);
void _clutter_stage_update_input_devices (ClutterStage *stage);
/* vfuncs implemented by backend */ /* vfuncs implemented by backend */
GType _clutter_backend_impl_get_type (void); GType _clutter_backend_impl_get_type (void);

View File

@ -69,6 +69,7 @@
#include "clutter-id-pool.h" #include "clutter-id-pool.h"
#include "clutter-container.h" #include "clutter-container.h"
#include "clutter-profile.h" #include "clutter-profile.h"
#include "clutter-input-device.h"
#include "cogl/cogl.h" #include "cogl/cogl.h"
@ -454,6 +455,7 @@ _clutter_stage_queue_event (ClutterStage *stage,
{ {
ClutterStagePrivate *priv; ClutterStagePrivate *priv;
gboolean first_event; gboolean first_event;
ClutterInputDevice *device;
g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (CLUTTER_IS_STAGE (stage));
@ -461,14 +463,32 @@ _clutter_stage_queue_event (ClutterStage *stage,
first_event = priv->event_queue->length == 0; first_event = priv->event_queue->length == 0;
g_queue_push_tail (priv->event_queue, g_queue_push_tail (priv->event_queue, clutter_event_copy (event));
clutter_event_copy (event));
if (first_event) if (first_event)
{ {
ClutterMasterClock *master_clock = _clutter_master_clock_get_default (); ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
_clutter_master_clock_start_running (master_clock); _clutter_master_clock_start_running (master_clock);
} }
/* if needed, update the state of the input device of the event.
* we do it here to avoid calling the same code from every backend
* event processing function
*/
device = clutter_event_get_device (event);
if (device != NULL)
{
ClutterModifierType event_state = clutter_event_get_state (event);
guint32 event_time = clutter_event_get_time (event);
gfloat event_x, event_y;
clutter_event_get_coords (event, &event_x, &event_y);
_clutter_input_device_set_coords (device, event_x, event_y);
_clutter_input_device_set_state (device, event_state);
_clutter_input_device_set_time (device, event_time);
_clutter_input_device_set_stage (device, stage);
}
} }
gboolean gboolean
@ -487,7 +507,7 @@ void
_clutter_stage_process_queued_events (ClutterStage *stage) _clutter_stage_process_queued_events (ClutterStage *stage)
{ {
ClutterStagePrivate *priv; ClutterStagePrivate *priv;
GList *events, *l;; GList *events, *l;
g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (CLUTTER_IS_STAGE (stage));
@ -506,7 +526,7 @@ _clutter_stage_process_queued_events (ClutterStage *stage)
priv->event_queue->tail = NULL; priv->event_queue->tail = NULL;
priv->event_queue->length = 0; priv->event_queue->length = 0;
for (l = events; l; l = l->next) for (l = events; l != NULL; l = l->next)
{ {
ClutterEvent *event; ClutterEvent *event;
ClutterEvent *next_event; ClutterEvent *next_event;

View File

@ -624,9 +624,10 @@ event_translate (ClutterBackend *backend,
case KeyPress: case KeyPress:
event->key.type = event->type = CLUTTER_KEY_PRESS; event->key.type = event->type = CLUTTER_KEY_PRESS;
translate_key_event (backend, event, xevent);
/* default key device if no XInput support is defined */ /* default key device if no XInput support is defined */
event->key.device = clutter_device_manager_get_device (manager, 1); event->key.device = clutter_device_manager_get_device (manager, 1);
translate_key_event (backend, event, xevent);
set_user_time (backend_x11, &xwindow, xevent->xkey.time); set_user_time (backend_x11, &xwindow, xevent->xkey.time);
break; break;
@ -659,9 +660,10 @@ event_translate (ClutterBackend *backend,
} }
event->key.type = event->type = CLUTTER_KEY_RELEASE; event->key.type = event->type = CLUTTER_KEY_RELEASE;
translate_key_event (backend, event, xevent);
/* default key device if no XInput support is defined */ /* default key device if no XInput support is defined */
event->key.device = clutter_device_manager_get_device (manager, 1); event->key.device = clutter_device_manager_get_device (manager, 1);
translate_key_event (backend, event, xevent);
break; break;
default: default:
@ -763,6 +765,10 @@ event_translate (ClutterBackend *backend,
event->motion.modifier_state = xevent->xcrossing.state; event->motion.modifier_state = xevent->xcrossing.state;
event->motion.device = event->motion.device =
clutter_device_manager_get_device (manager, 0); clutter_device_manager_get_device (manager, 0);
/* we know that we are entering the stage here */
_clutter_input_device_set_stage (event->motion.device, stage);
CLUTTER_NOTE (EVENT, "Entering the stage");
break; break;
case LeaveNotify: case LeaveNotify:
@ -770,8 +776,12 @@ event_translate (ClutterBackend *backend,
event->crossing.time = xevent->xcrossing.time; event->crossing.time = xevent->xcrossing.time;
event->crossing.x = xevent->xcrossing.x; event->crossing.x = xevent->xcrossing.x;
event->crossing.y = xevent->xcrossing.y; event->crossing.y = xevent->xcrossing.y;
event->motion.device = event->crossing.device =
clutter_device_manager_get_device (manager, 0); clutter_device_manager_get_device (manager, 0);
/* we know that we are leaving the stage here */
_clutter_input_device_set_stage (event->crossing.device, NULL);
CLUTTER_NOTE (EVENT, "Leaving the stage");
break; break;
default: default:
@ -784,10 +794,16 @@ event_translate (ClutterBackend *backend,
{ /* XInput fun.. Needs clean up. */ { /* XInput fun.. Needs clean up. */
#ifdef HAVE_XINPUT #ifdef HAVE_XINPUT
int *ev_types = backend_x11->event_types; int *ev_types = backend_x11->event_types;
int button_press, button_release;
int motion_notify;
button_press = ev_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT];
button_release = ev_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT];
motion_notify = ev_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT];
CLUTTER_NOTE (EVENT, "XInput event type: %d", xevent->type); CLUTTER_NOTE (EVENT, "XInput event type: %d", xevent->type);
if (xevent->type == ev_types [CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT]) if (xevent->type == button_press)
{ {
XDeviceButtonEvent *xbev = (XDeviceButtonEvent *) xevent; XDeviceButtonEvent *xbev = (XDeviceButtonEvent *) xevent;
@ -833,8 +849,7 @@ event_translate (ClutterBackend *backend,
set_user_time (backend_x11, &xwindow, xbev->time); set_user_time (backend_x11, &xwindow, xbev->time);
} }
else if (xevent->type else if (xevent->type == button_release)
== ev_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT])
{ {
XDeviceButtonEvent *xbev = (XDeviceButtonEvent *)xevent; XDeviceButtonEvent *xbev = (XDeviceButtonEvent *)xevent;
@ -860,8 +875,7 @@ event_translate (ClutterBackend *backend,
event->button.button = xbev->button; event->button.button = xbev->button;
event->button.device = _clutter_x11_get_device_for_xid (xbev->deviceid); event->button.device = _clutter_x11_get_device_for_xid (xbev->deviceid);
} }
else if (xevent->type else if (xevent->type == motion_notify)
== ev_types [CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT])
{ {
XDeviceMotionEvent *xmev = (XDeviceMotionEvent *)xevent; XDeviceMotionEvent *xmev = (XDeviceMotionEvent *)xevent;
@ -883,8 +897,7 @@ event_translate (ClutterBackend *backend,
* not generate events even when the window has focus * not generate events even when the window has focus
*/ */
else if (xevent->type else if (xevent->type == ev_types[CLUTTER_X11_XINPUT_KEY_PRESS_EVENT])
== ev_types [CLUTTER_X11_XINPUT_KEY_PRESS_EVENT])
{ {
XEvent xevent_converted; XEvent xevent_converted;
XDeviceKeyEvent *xkev = (XDeviceKeyEvent *)xevent; XDeviceKeyEvent *xkev = (XDeviceKeyEvent *)xevent;
@ -896,8 +909,7 @@ event_translate (ClutterBackend *backend,
set_user_time (backend_x11, &xwindow, xkev->time); set_user_time (backend_x11, &xwindow, xkev->time);
} }
else if (xevent->type else if (xevent->type == ev_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT])
== ev_types [CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT])
{ {
XEvent xevent_converted; XEvent xevent_converted;
XDeviceKeyEvent *xkev = (XDeviceKeyEvent *)xevent; XDeviceKeyEvent *xkev = (XDeviceKeyEvent *)xevent;