mirror of
https://github.com/brl/mutter.git
synced 2024-11-26 18:11:05 -05:00
Compress events as part of the frame cycle
Instead of trying to guess about which motion events are extraneous, queue up all events until we process a frame. This allows us to look ahead and reliably compress consecutive sequence of motion events. clutter-main.c: Feed received events to the stage for queueing. Remove old compression code. Remove clutter_get_motion_events_frequency() clutter_set_motion_events_frequency() clutter-stage.c: Keep a queue of pending events. clutter-master-clock.c: Add processng of queued events to the clock source dispatch function. http://bugzilla.openedhand.com/show_bug.cgi?id=1637 Signed-off-by: Emmanuele Bassi <ebassi@linux.intel.com>
This commit is contained in:
parent
89a8fd7755
commit
6e69692e22
@ -2004,6 +2004,29 @@ generate_enter_leave_events (ClutterEvent *event)
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
clutter_do_event (ClutterEvent *event)
|
clutter_do_event (ClutterEvent *event)
|
||||||
|
{
|
||||||
|
if (!event->any.stage)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Instead of processing events when received, we queue them up to
|
||||||
|
* handle per-frame before animations, layout, and drawing.
|
||||||
|
*
|
||||||
|
* This gives us the chance to reliably compress motion events
|
||||||
|
* because we've "looked ahead" and know all motion events that
|
||||||
|
* will occur before drawing the frame.
|
||||||
|
*/
|
||||||
|
_clutter_stage_queue_event (event->any.stage, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _clutter_process_event
|
||||||
|
* @event: a #ClutterEvent.
|
||||||
|
*
|
||||||
|
* Does the actual work of processing an event that was queued earlier
|
||||||
|
* out of clutter_do_event().
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_clutter_process_event (ClutterEvent *event)
|
||||||
{
|
{
|
||||||
/* FIXME: This should probably be clutter_cook_event() - it would
|
/* FIXME: This should probably be clutter_cook_event() - it would
|
||||||
* take a raw event from the backend and 'cook' it so its more tasty.
|
* take a raw event from the backend and 'cook' it so its more tasty.
|
||||||
@ -2013,8 +2036,6 @@ clutter_do_event (ClutterEvent *event)
|
|||||||
ClutterBackend *backend;
|
ClutterBackend *backend;
|
||||||
ClutterActor *stage;
|
ClutterActor *stage;
|
||||||
ClutterInputDevice *device = NULL;
|
ClutterInputDevice *device = NULL;
|
||||||
static gint32 motion_last_time = 0L;
|
|
||||||
gint32 local_motion_time;
|
|
||||||
|
|
||||||
context = clutter_context_get_default ();
|
context = clutter_context_get_default ();
|
||||||
backend = context->backend;
|
backend = context->backend;
|
||||||
@ -2091,52 +2112,6 @@ clutter_do_event (ClutterEvent *event)
|
|||||||
case CLUTTER_MOTION:
|
case CLUTTER_MOTION:
|
||||||
device = event->motion.device;
|
device = event->motion.device;
|
||||||
|
|
||||||
if (device)
|
|
||||||
local_motion_time = device->motion_last_time;
|
|
||||||
else
|
|
||||||
local_motion_time = motion_last_time;
|
|
||||||
|
|
||||||
/* avoid rate throttling for synthetic motion events or if
|
|
||||||
* the per-actor events are disabled
|
|
||||||
*/
|
|
||||||
if (!(event->any.flags & CLUTTER_EVENT_FLAG_SYNTHETIC) ||
|
|
||||||
!context->motion_events_per_actor)
|
|
||||||
{
|
|
||||||
gint32 frame_rate, delta;
|
|
||||||
|
|
||||||
/* avoid issuing too many motion events, which leads to many
|
|
||||||
* redraws in pick mode (performance penalty)
|
|
||||||
*/
|
|
||||||
frame_rate = clutter_get_motion_events_frequency ();
|
|
||||||
delta = 1000 / frame_rate;
|
|
||||||
|
|
||||||
CLUTTER_NOTE (EVENT,
|
|
||||||
"skip motion event: %s (last:%d, delta:%d, time:%d)",
|
|
||||||
(event->any.time < (local_motion_time + delta) ? "yes" : "no"),
|
|
||||||
local_motion_time,
|
|
||||||
delta,
|
|
||||||
event->any.time);
|
|
||||||
|
|
||||||
/* we need to guard against roll-overs and the
|
|
||||||
* case where the time is rolled backwards and
|
|
||||||
* the backend is not ensuring a monotonic clock
|
|
||||||
* for the events.
|
|
||||||
*
|
|
||||||
* see:
|
|
||||||
* http://bugzilla.openedhand.com/show_bug.cgi?id=1130
|
|
||||||
*/
|
|
||||||
if (event->any.time >= local_motion_time &&
|
|
||||||
event->any.time < (local_motion_time + delta))
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
local_motion_time = event->any.time;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device)
|
|
||||||
device->motion_last_time = local_motion_time;
|
|
||||||
else
|
|
||||||
motion_last_time = local_motion_time;
|
|
||||||
|
|
||||||
/* Only stage gets motion events if clutter_set_motion_events is TRUE,
|
/* Only stage gets motion events if clutter_set_motion_events is TRUE,
|
||||||
* and the event is not a synthetic event with source set.
|
* and the event is not a synthetic event with source set.
|
||||||
*/
|
*/
|
||||||
@ -2321,9 +2296,6 @@ clutter_base_init (void)
|
|||||||
* Retrieves the default frame rate used when creating #ClutterTimeline<!--
|
* Retrieves the default frame rate used when creating #ClutterTimeline<!--
|
||||||
* -->s.
|
* -->s.
|
||||||
*
|
*
|
||||||
* This value is also used to compute the default frequency of motion
|
|
||||||
* events.
|
|
||||||
*
|
|
||||||
* Return value: the default frame rate
|
* Return value: the default frame rate
|
||||||
*
|
*
|
||||||
* Since: 0.6
|
* Since: 0.6
|
||||||
@ -2621,61 +2593,6 @@ clutter_get_keyboard_grab (void)
|
|||||||
return context->keyboard_grab_actor;
|
return context->keyboard_grab_actor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* clutter_get_motion_events_frequency:
|
|
||||||
*
|
|
||||||
* Retrieves the number of motion events per second that are delivered
|
|
||||||
* to the stage.
|
|
||||||
*
|
|
||||||
* See clutter_set_motion_events_frequency().
|
|
||||||
*
|
|
||||||
* Return value: the number of motion events per second
|
|
||||||
*
|
|
||||||
* Since: 0.6
|
|
||||||
*/
|
|
||||||
guint
|
|
||||||
clutter_get_motion_events_frequency (void)
|
|
||||||
{
|
|
||||||
ClutterMainContext *context = clutter_context_get_default ();
|
|
||||||
|
|
||||||
if (G_LIKELY (context->motion_frequency == 0))
|
|
||||||
{
|
|
||||||
guint frequency;
|
|
||||||
|
|
||||||
frequency = clutter_default_fps / 4;
|
|
||||||
frequency = CLAMP (frequency, 20, 45);
|
|
||||||
|
|
||||||
return frequency;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return context->motion_frequency;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* clutter_set_motion_events_frequency:
|
|
||||||
* @frequency: the number of motion events per second, or 0 for the
|
|
||||||
* default value
|
|
||||||
*
|
|
||||||
* Sets the motion events frequency. Setting this to a non-zero value
|
|
||||||
* will override the default setting, so it should be rarely used.
|
|
||||||
*
|
|
||||||
* Motion events are delivered from the default backend to the stage
|
|
||||||
* and are used to generate the enter/leave events pair. This might lead
|
|
||||||
* to a performance penalty due to the way the actors are identified.
|
|
||||||
* Using this function is possible to reduce the frequency of the motion
|
|
||||||
* events delivery to the stage.
|
|
||||||
*
|
|
||||||
* Since: 0.6
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
clutter_set_motion_events_frequency (guint frequency)
|
|
||||||
{
|
|
||||||
ClutterMainContext *context = clutter_context_get_default ();
|
|
||||||
|
|
||||||
/* never allow the motion events to exceed the default frame rate */
|
|
||||||
context->motion_frequency = CLAMP (frequency, 1, clutter_default_fps);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* clutter_clear_glyph_cache:
|
* clutter_clear_glyph_cache:
|
||||||
*
|
*
|
||||||
|
@ -144,8 +144,6 @@ void clutter_threads_remove_repaint_func (guint handle_id
|
|||||||
|
|
||||||
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);
|
||||||
void clutter_set_motion_events_frequency (guint frequency);
|
|
||||||
guint clutter_get_motion_events_frequency (void);
|
|
||||||
|
|
||||||
void clutter_set_default_frame_rate (guint frames_per_sec);
|
void clutter_set_default_frame_rate (guint frames_per_sec);
|
||||||
guint clutter_get_default_frame_rate (void);
|
guint clutter_get_default_frame_rate (void);
|
||||||
|
@ -115,7 +115,8 @@ master_clock_is_running (ClutterMasterClock *master_clock)
|
|||||||
|
|
||||||
stages = clutter_stage_manager_peek_stages (stage_manager);
|
stages = clutter_stage_manager_peek_stages (stage_manager);
|
||||||
for (l = stages; l; l = l->next)
|
for (l = stages; l; l = l->next)
|
||||||
if (_clutter_stage_needs_update (l->data))
|
if (_clutter_stage_has_queued_events (l->data) ||
|
||||||
|
_clutter_stage_needs_update (l->data))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -179,11 +180,20 @@ clutter_clock_dispatch (GSource *source,
|
|||||||
ClutterClockSource *clock_source = (ClutterClockSource *) source;
|
ClutterClockSource *clock_source = (ClutterClockSource *) source;
|
||||||
ClutterMasterClock *master_clock = clock_source->master_clock;
|
ClutterMasterClock *master_clock = clock_source->master_clock;
|
||||||
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
||||||
const GSList *stages, *l;
|
GSList *stages, *l;
|
||||||
|
|
||||||
CLUTTER_NOTE (SCHEDULER, "Master clock [tick]");
|
CLUTTER_NOTE (SCHEDULER, "Master clock [tick]");
|
||||||
|
|
||||||
stages = clutter_stage_manager_peek_stages (stage_manager);
|
/* We need to protect ourselves against stages being destroyed during
|
||||||
|
* event handling
|
||||||
|
*/
|
||||||
|
stages = clutter_stage_manager_list_stages (stage_manager);
|
||||||
|
g_slist_foreach (stages, (GFunc)g_object_ref, NULL);
|
||||||
|
|
||||||
|
/* Process queued events
|
||||||
|
*/
|
||||||
|
for (l = stages; l != NULL; l = l->next)
|
||||||
|
_clutter_stage_process_queued_events (l->data);
|
||||||
|
|
||||||
_clutter_master_clock_advance (master_clock);
|
_clutter_master_clock_advance (master_clock);
|
||||||
|
|
||||||
@ -193,6 +203,9 @@ clutter_clock_dispatch (GSource *source,
|
|||||||
for (l = stages; l != NULL; l = l->next)
|
for (l = stages; l != NULL; l = l->next)
|
||||||
_clutter_stage_do_update (l->data);
|
_clutter_stage_do_update (l->data);
|
||||||
|
|
||||||
|
g_slist_foreach (stages, (GFunc)g_object_unref, NULL);
|
||||||
|
g_slist_free (stages);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,6 @@ typedef enum {
|
|||||||
struct _ClutterInputDevice
|
struct _ClutterInputDevice
|
||||||
{
|
{
|
||||||
gint id;
|
gint id;
|
||||||
gint32 motion_last_time;
|
|
||||||
ClutterActor *pointer_grab_actor;
|
ClutterActor *pointer_grab_actor;
|
||||||
ClutterActor *motion_last_actor;
|
ClutterActor *motion_last_actor;
|
||||||
|
|
||||||
@ -99,7 +98,6 @@ struct _ClutterMainContext
|
|||||||
|
|
||||||
ClutterPickMode pick_mode; /* Indicates pick render mode */
|
ClutterPickMode pick_mode; /* Indicates pick render mode */
|
||||||
|
|
||||||
guint motion_frequency; /* Motion events per second */
|
|
||||||
gint num_reactives; /* Num of reactive actors */
|
gint num_reactives; /* Num of reactive actors */
|
||||||
|
|
||||||
ClutterIDPool *id_pool; /* mapping between reused integer ids
|
ClutterIDPool *id_pool; /* mapping between reused integer ids
|
||||||
@ -178,6 +176,11 @@ void _clutter_stage_maybe_relayout (ClutterActor *sta
|
|||||||
gboolean _clutter_stage_needs_update (ClutterStage *stage);
|
gboolean _clutter_stage_needs_update (ClutterStage *stage);
|
||||||
void _clutter_stage_do_update (ClutterStage *stage);
|
void _clutter_stage_do_update (ClutterStage *stage);
|
||||||
|
|
||||||
|
void _clutter_stage_queue_event (ClutterStage *stage,
|
||||||
|
ClutterEvent *event);
|
||||||
|
gboolean _clutter_stage_has_queued_events (ClutterStage *stage);
|
||||||
|
void _clutter_stage_process_queued_events (ClutterStage *stage);
|
||||||
|
|
||||||
/* vfuncs implemented by backend */
|
/* vfuncs implemented by backend */
|
||||||
GType _clutter_backend_impl_get_type (void);
|
GType _clutter_backend_impl_get_type (void);
|
||||||
|
|
||||||
@ -207,6 +210,9 @@ gfloat _clutter_backend_get_units_per_em (ClutterBackend *backend
|
|||||||
|
|
||||||
void _clutter_feature_init (void);
|
void _clutter_feature_init (void);
|
||||||
|
|
||||||
|
/* Reinjecting queued events for processing */
|
||||||
|
void _clutter_process_event (ClutterEvent *event);
|
||||||
|
|
||||||
/* Picking code */
|
/* Picking code */
|
||||||
ClutterActor *_clutter_do_pick (ClutterStage *stage,
|
ClutterActor *_clutter_do_pick (ClutterStage *stage,
|
||||||
gint x,
|
gint x,
|
||||||
|
@ -87,6 +87,8 @@ struct _ClutterStagePrivate
|
|||||||
gchar *title;
|
gchar *title;
|
||||||
ClutterActor *key_focused_actor;
|
ClutterActor *key_focused_actor;
|
||||||
|
|
||||||
|
GQueue *event_queue;
|
||||||
|
|
||||||
guint redraw_pending : 1;
|
guint redraw_pending : 1;
|
||||||
guint is_fullscreen : 1;
|
guint is_fullscreen : 1;
|
||||||
guint is_offscreen : 1;
|
guint is_offscreen : 1;
|
||||||
@ -389,6 +391,86 @@ clutter_stage_real_fullscreen (ClutterStage *stage)
|
|||||||
CLUTTER_ALLOCATION_NONE);
|
CLUTTER_ALLOCATION_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_clutter_stage_queue_event (ClutterStage *stage,
|
||||||
|
ClutterEvent *event)
|
||||||
|
{
|
||||||
|
ClutterStagePrivate *priv;
|
||||||
|
|
||||||
|
g_return_if_fail (CLUTTER_IS_STAGE (stage));
|
||||||
|
|
||||||
|
priv = stage->priv;
|
||||||
|
|
||||||
|
g_queue_push_tail (priv->event_queue,
|
||||||
|
clutter_event_copy (event));
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
_clutter_stage_has_queued_events (ClutterStage *stage)
|
||||||
|
{
|
||||||
|
ClutterStagePrivate *priv;
|
||||||
|
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
|
||||||
|
|
||||||
|
priv = stage->priv;
|
||||||
|
|
||||||
|
return priv->event_queue->length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_clutter_stage_process_queued_events (ClutterStage *stage)
|
||||||
|
{
|
||||||
|
ClutterStagePrivate *priv;
|
||||||
|
GList *events, *l;;
|
||||||
|
|
||||||
|
g_return_if_fail (CLUTTER_IS_STAGE (stage));
|
||||||
|
|
||||||
|
priv = stage->priv;
|
||||||
|
|
||||||
|
if (priv->event_queue->length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* In case the stage gets destroyed during event processing */
|
||||||
|
g_object_ref (stage);
|
||||||
|
|
||||||
|
/* Steal events before starting processing to avoid reentrancy
|
||||||
|
* issues */
|
||||||
|
events = priv->event_queue->head;
|
||||||
|
priv->event_queue->head = NULL;
|
||||||
|
priv->event_queue->tail = NULL;
|
||||||
|
priv->event_queue->length = 0;
|
||||||
|
|
||||||
|
for (l = events; l; l = l->next)
|
||||||
|
{
|
||||||
|
ClutterEvent *event;
|
||||||
|
ClutterEvent *next_event;
|
||||||
|
|
||||||
|
event = l->data;
|
||||||
|
next_event = l->next ? l->next->data : NULL;
|
||||||
|
|
||||||
|
/* Skip consecutive motion events */
|
||||||
|
if (next_event &&
|
||||||
|
event->type == CLUTTER_MOTION &&
|
||||||
|
(next_event->type == CLUTTER_MOTION ||
|
||||||
|
next_event->type == CLUTTER_LEAVE))
|
||||||
|
{
|
||||||
|
CLUTTER_NOTE (EVENT,
|
||||||
|
"Omitting motion event at %.2f, %.2f",
|
||||||
|
event->motion.x, event->motion.y);
|
||||||
|
goto next_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
_clutter_process_event (event);
|
||||||
|
|
||||||
|
next_event:
|
||||||
|
clutter_event_free (event);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_list_free (events);
|
||||||
|
|
||||||
|
g_object_unref (stage);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* _clutter_stage_needs_update:
|
* _clutter_stage_needs_update:
|
||||||
* @stage: A #ClutterStage
|
* @stage: A #ClutterStage
|
||||||
@ -635,6 +717,10 @@ static void
|
|||||||
clutter_stage_finalize (GObject *object)
|
clutter_stage_finalize (GObject *object)
|
||||||
{
|
{
|
||||||
ClutterStage *stage = CLUTTER_STAGE (object);
|
ClutterStage *stage = CLUTTER_STAGE (object);
|
||||||
|
ClutterStagePrivate *priv = stage->priv;
|
||||||
|
|
||||||
|
g_queue_foreach (priv->event_queue, (GFunc)clutter_event_free, NULL);
|
||||||
|
g_queue_free (priv->event_queue);
|
||||||
|
|
||||||
g_free (stage->priv->title);
|
g_free (stage->priv->title);
|
||||||
|
|
||||||
@ -908,6 +994,8 @@ clutter_stage_init (ClutterStage *self)
|
|||||||
/* make sure that the implementation is considered a top level */
|
/* make sure that the implementation is considered a top level */
|
||||||
CLUTTER_SET_PRIVATE_FLAGS (priv->impl, CLUTTER_ACTOR_IS_TOPLEVEL);
|
CLUTTER_SET_PRIVATE_FLAGS (priv->impl, CLUTTER_ACTOR_IS_TOPLEVEL);
|
||||||
|
|
||||||
|
priv->event_queue = g_queue_new ();
|
||||||
|
|
||||||
priv->is_offscreen = FALSE;
|
priv->is_offscreen = FALSE;
|
||||||
priv->is_fullscreen = FALSE;
|
priv->is_fullscreen = FALSE;
|
||||||
priv->is_user_resizable = FALSE;
|
priv->is_user_resizable = FALSE;
|
||||||
|
Loading…
Reference in New Issue
Block a user