mirror of
https://github.com/brl/mutter.git
synced 2024-12-23 19:42:05 +00:00
Bug 1637 - Master clock improvements
Merge branch 'master-clock-updates' * master-clock-updates: (22 commits) Change the paint forcing on the Text cache text [timelines] Improve marker hit check and don't fudge the delta Revert "[timeline] Don't clamp the elapsed time when a looping tl reaches the end" [tests] Don't add a newline to the end of g_test_message calls [test-timeline] Add a marker at the beginning of the timeline [timeline] Don't clamp the elapsed time when a looping tl reaches the end [master-clock] Throttle if no redraw was performed [docs] Update Clutter's API reference Force a paint instead of calling clutter_redraw() Fix clutter_redraw() to match the redraw cycle Run the repaint functions inside the redraw cycle Remove useless manual timeline ticking Move elapsed-time calculations into ClutterTimeline Limit the frame rate when not syncing to VBLANK Decrease the main-loop priority of the frame cycle Avoid motion-compression in test-picking test Compress events as part of the frame cycle Remove stage update idle and do updates from the master clock Call g_main_context_wakeup() when we start running timelines Remove unused msecs_delta member ...
This commit is contained in:
commit
61c45da90a
@ -208,11 +208,6 @@ _clutter_do_redraw (ClutterStage *stage)
|
||||
*/
|
||||
_clutter_backend_redraw (ctx->backend, stage);
|
||||
|
||||
/* prepare for the next frame; if anything queues a redraw as the
|
||||
* result of a timeline, this will end up redrawing the scene
|
||||
*/
|
||||
_clutter_master_clock_advance (master_clock);
|
||||
|
||||
/* Complete FPS info */
|
||||
if (G_UNLIKELY (clutter_get_show_fps ()))
|
||||
{
|
||||
@ -241,18 +236,9 @@ _clutter_do_redraw (ClutterStage *stage)
|
||||
void
|
||||
clutter_redraw (ClutterStage *stage)
|
||||
{
|
||||
ClutterMasterClock *master_clock;
|
||||
g_return_if_fail (CLUTTER_IS_STAGE (stage));
|
||||
|
||||
/* we need to do this because the clutter_redraw() might be called by
|
||||
* clutter-gtk, and this will not cause the master clock to advance nor
|
||||
* the repaint functions to be run
|
||||
*/
|
||||
master_clock = _clutter_master_clock_get_default ();
|
||||
_clutter_master_clock_advance (master_clock);
|
||||
|
||||
_clutter_run_repaint_functions ();
|
||||
|
||||
_clutter_do_redraw (stage);
|
||||
clutter_stage_ensure_redraw (stage);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2009,6 +1995,29 @@ generate_enter_leave_events (ClutterEvent *event)
|
||||
*/
|
||||
void
|
||||
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
|
||||
* take a raw event from the backend and 'cook' it so its more tasty.
|
||||
@ -2018,8 +2027,6 @@ clutter_do_event (ClutterEvent *event)
|
||||
ClutterBackend *backend;
|
||||
ClutterActor *stage;
|
||||
ClutterInputDevice *device = NULL;
|
||||
static gint32 motion_last_time = 0L;
|
||||
gint32 local_motion_time;
|
||||
|
||||
context = clutter_context_get_default ();
|
||||
backend = context->backend;
|
||||
@ -2096,52 +2103,6 @@ clutter_do_event (ClutterEvent *event)
|
||||
case CLUTTER_MOTION:
|
||||
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,
|
||||
* and the event is not a synthetic event with source set.
|
||||
*/
|
||||
@ -2323,11 +2284,7 @@ clutter_base_init (void)
|
||||
/**
|
||||
* clutter_get_default_frame_rate:
|
||||
*
|
||||
* Retrieves the default frame rate used when creating #ClutterTimeline<!--
|
||||
* -->s.
|
||||
*
|
||||
* This value is also used to compute the default frequency of motion
|
||||
* events.
|
||||
* Retrieves the default frame rate. See clutter_set_default_frame_rate().
|
||||
*
|
||||
* Return value: the default frame rate
|
||||
*
|
||||
@ -2347,8 +2304,10 @@ clutter_get_default_frame_rate (void)
|
||||
* clutter_set_default_frame_rate:
|
||||
* @frames_per_sec: the new default frame rate
|
||||
*
|
||||
* Sets the default frame rate to be used when creating #ClutterTimeline<!--
|
||||
* -->s
|
||||
* Sets the default frame rate. This frame rate will be used to limit
|
||||
* the number of frames drawn if Clutter is not able to synchronize
|
||||
* with the vertical refresh rate of the display. When synchronization
|
||||
* is possible, this value is ignored.
|
||||
*
|
||||
* Since: 0.6
|
||||
*/
|
||||
@ -2626,61 +2585,6 @@ clutter_get_keyboard_grab (void)
|
||||
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:
|
||||
*
|
||||
|
@ -66,20 +66,15 @@ GQuark clutter_init_error_quark (void);
|
||||
/**
|
||||
* CLUTTER_PRIORITY_REDRAW:
|
||||
*
|
||||
* Priority of the redraws.
|
||||
* Priority of the redraws. This is chosen to be lower than the GTK+
|
||||
* redraw and resize priorities, because in application with both
|
||||
* GTK+ and Clutter it's more likely that the Clutter part will be
|
||||
* continually animating (and thus able to starve GTK+) than
|
||||
* vice-versa.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
#define CLUTTER_PRIORITY_REDRAW (G_PRIORITY_HIGH_IDLE + 20)
|
||||
|
||||
/**
|
||||
* CLUTTER_PRIORITY_TIMELINE:
|
||||
*
|
||||
* Priority of the timelines.
|
||||
*
|
||||
* Since: 0.8
|
||||
*/
|
||||
#define CLUTTER_PRIORITY_TIMELINE (G_PRIORITY_DEFAULT + 30)
|
||||
#define CLUTTER_PRIORITY_REDRAW (G_PRIORITY_HIGH_IDLE + 50)
|
||||
|
||||
/* Initialisation */
|
||||
void clutter_base_init (void);
|
||||
@ -144,8 +139,6 @@ void clutter_threads_remove_repaint_func (guint handle_id
|
||||
|
||||
void clutter_set_motion_events_enabled (gboolean enable);
|
||||
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);
|
||||
guint clutter_get_default_frame_rate (void);
|
||||
|
@ -53,21 +53,21 @@ struct _ClutterMasterClock
|
||||
/* the list of timelines handled by the clock */
|
||||
GSList *timelines;
|
||||
|
||||
/* the current state of the clock
|
||||
*/
|
||||
GTimeVal cur_tick;
|
||||
|
||||
/* the previous state of the clock, used to compute
|
||||
* the delta
|
||||
*/
|
||||
GTimeVal prev_tick;
|
||||
gulong msecs_delta;
|
||||
|
||||
/* an idle source, used by the Master Clock to queue
|
||||
* a redraw on the stage and drive the animations
|
||||
*/
|
||||
GSource *source;
|
||||
|
||||
/* a guard, so that we dispatch the last redraw even
|
||||
* after the last timeline has been completed
|
||||
*/
|
||||
guint last_advance : 1;
|
||||
guint updated_stages : 1;
|
||||
};
|
||||
|
||||
struct _ClutterMasterClockClass
|
||||
@ -82,13 +82,6 @@ struct _ClutterClockSource
|
||||
ClutterMasterClock *master_clock;
|
||||
};
|
||||
|
||||
static void on_timeline_started (ClutterTimeline *timeline,
|
||||
ClutterMasterClock *master_clock);
|
||||
static void on_timeline_completed (ClutterTimeline *timeline,
|
||||
ClutterMasterClock *master_clock);
|
||||
static void on_timeline_paused (ClutterTimeline *timeline,
|
||||
ClutterMasterClock *master_clock);
|
||||
|
||||
static gboolean clutter_clock_prepare (GSource *source,
|
||||
gint *timeout);
|
||||
static gboolean clutter_clock_check (GSource *source);
|
||||
@ -108,44 +101,87 @@ static GSourceFuncs clock_funcs = {
|
||||
G_DEFINE_TYPE (ClutterMasterClock, clutter_master_clock, G_TYPE_OBJECT);
|
||||
|
||||
/*
|
||||
* has_running_timeline:
|
||||
* master_clock_is_running:
|
||||
* @master_clock: a #ClutterMasterClock
|
||||
* @filter: a #ClutterTimeline or %NULL
|
||||
*
|
||||
* Checks if @master_clock has any running timeline; if @filter
|
||||
* is not %NULL then the timeline will be filtered from the
|
||||
* list of timelines held by @master_clock
|
||||
* Checks if we should currently be advancing timelines or redrawing
|
||||
* stages.
|
||||
*
|
||||
* Return value: %TRUE if the #ClutterMasterClock has at least
|
||||
* one running timeline
|
||||
*/
|
||||
static gboolean
|
||||
has_running_timeline (ClutterMasterClock *master_clock,
|
||||
ClutterTimeline *filter)
|
||||
master_clock_is_running (ClutterMasterClock *master_clock)
|
||||
{
|
||||
GSList *l;
|
||||
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
||||
const GSList *stages, *l;
|
||||
|
||||
if (master_clock->last_advance)
|
||||
if (master_clock->timelines)
|
||||
return TRUE;
|
||||
|
||||
if (master_clock->timelines == NULL)
|
||||
return FALSE;
|
||||
|
||||
for (l = master_clock->timelines; l != NULL; l = l->next)
|
||||
{
|
||||
/* if we get a timeline then we should filter it
|
||||
* from the list of timelines we want to check
|
||||
*/
|
||||
if (filter != NULL && filter == l->data)
|
||||
continue;
|
||||
|
||||
if (clutter_timeline_is_playing (l->data))
|
||||
return TRUE;
|
||||
}
|
||||
stages = clutter_stage_manager_peek_stages (stage_manager);
|
||||
for (l = stages; l; l = l->next)
|
||||
if (_clutter_stage_has_queued_events (l->data) ||
|
||||
_clutter_stage_needs_update (l->data))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* master_clock_next_frame_delay:
|
||||
* @master_clock: a #ClutterMasterClock
|
||||
*
|
||||
* Computes the number of delay before we need to draw the next frame.
|
||||
*
|
||||
* Return value: -1 if there is no next frame pending, otherwise the
|
||||
* number of millseconds before the we need to draw the next frame
|
||||
*/
|
||||
static gint
|
||||
master_clock_next_frame_delay (ClutterMasterClock *master_clock)
|
||||
{
|
||||
GTimeVal now;
|
||||
GTimeVal next;
|
||||
|
||||
if (!master_clock_is_running (master_clock))
|
||||
return -1;
|
||||
|
||||
if (clutter_feature_available (CLUTTER_FEATURE_SYNC_TO_VBLANK) &&
|
||||
master_clock->updated_stages)
|
||||
{
|
||||
/* When we have sync-to-vblank, we count on that to throttle
|
||||
* our frame rate, and otherwise draw frames as fast as possible.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (master_clock->prev_tick.tv_sec == 0)
|
||||
{
|
||||
/* If we weren't previously running, then draw the next frame
|
||||
* immediately
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Otherwise, wait at least 1/frame_rate seconds since we last started a frame */
|
||||
|
||||
g_source_get_current_time (master_clock->source, &now);
|
||||
|
||||
next = master_clock->prev_tick;
|
||||
g_time_val_add (&next, 1000000 / clutter_get_default_frame_rate ());
|
||||
|
||||
if (next.tv_sec < now.tv_sec ||
|
||||
(next.tv_sec == now.tv_sec && next.tv_usec < now.tv_usec))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((next.tv_sec - now.tv_sec) * 1000 +
|
||||
(next.tv_usec - now.tv_usec) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* clutter_clock_source_new:
|
||||
* @master_clock: a #ClutterMasterClock for the source
|
||||
@ -174,14 +210,13 @@ clutter_clock_prepare (GSource *source,
|
||||
{
|
||||
ClutterClockSource *clock_source = (ClutterClockSource *) source;
|
||||
ClutterMasterClock *master_clock = clock_source->master_clock;
|
||||
gboolean retval;
|
||||
int delay;
|
||||
|
||||
/* just like an idle source, we are ready if nothing else is */
|
||||
*timeout = -1;
|
||||
delay = master_clock_next_frame_delay (master_clock);
|
||||
|
||||
retval = has_running_timeline (master_clock, NULL);
|
||||
*timeout = delay;
|
||||
|
||||
return retval;
|
||||
return delay == 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -189,11 +224,11 @@ clutter_clock_check (GSource *source)
|
||||
{
|
||||
ClutterClockSource *clock_source = (ClutterClockSource *) source;
|
||||
ClutterMasterClock *master_clock = clock_source->master_clock;
|
||||
gboolean retval;
|
||||
int delay;
|
||||
|
||||
retval = has_running_timeline (master_clock, NULL);
|
||||
delay = master_clock_next_frame_delay (master_clock);
|
||||
|
||||
return retval;
|
||||
return delay == 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -204,68 +239,48 @@ clutter_clock_dispatch (GSource *source,
|
||||
ClutterClockSource *clock_source = (ClutterClockSource *) source;
|
||||
ClutterMasterClock *master_clock = clock_source->master_clock;
|
||||
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
||||
const GSList *stages, *l;
|
||||
GSList *stages, *l;
|
||||
|
||||
CLUTTER_NOTE (SCHEDULER, "Master clock [tick]");
|
||||
|
||||
stages = clutter_stage_manager_peek_stages (stage_manager);
|
||||
/* Get the time to use for this frame.
|
||||
*/
|
||||
g_source_get_current_time (source, &master_clock->cur_tick);
|
||||
|
||||
/* queue a redraw for each stage; this will advance each timeline
|
||||
* held by the master clock of the amount of milliseconds elapsed
|
||||
* since the last redraw
|
||||
/* 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);
|
||||
|
||||
master_clock->updated_stages = FALSE;
|
||||
|
||||
/* Process queued events
|
||||
*/
|
||||
for (l = stages; l != NULL; l = l->next)
|
||||
clutter_actor_queue_redraw (l->data);
|
||||
_clutter_stage_process_queued_events (l->data);
|
||||
|
||||
/* if this is the remainder of an advancement, needed for the last
|
||||
* timeline to finish its run, then we need to reset the prev_tick
|
||||
_clutter_master_clock_advance (master_clock);
|
||||
_clutter_run_repaint_functions ();
|
||||
|
||||
/* Update any stage that needs redraw/relayout after the clock
|
||||
* is advanced.
|
||||
*/
|
||||
if (master_clock->last_advance)
|
||||
{
|
||||
master_clock->prev_tick.tv_sec = 0;
|
||||
master_clock->last_advance = FALSE;
|
||||
}
|
||||
for (l = stages; l != NULL; l = l->next)
|
||||
master_clock->updated_stages |= _clutter_stage_do_update (l->data);
|
||||
|
||||
g_slist_foreach (stages, (GFunc)g_object_unref, NULL);
|
||||
g_slist_free (stages);
|
||||
|
||||
master_clock->prev_tick = master_clock->cur_tick;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
timeline_weak_ref (gpointer data,
|
||||
GObject *object_pointer)
|
||||
{
|
||||
ClutterMasterClock *master_clock = data;
|
||||
|
||||
master_clock->timelines =
|
||||
g_slist_remove (master_clock->timelines, object_pointer);
|
||||
|
||||
if (master_clock->timelines == NULL)
|
||||
master_clock->prev_tick.tv_sec = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_master_clock_finalize (GObject *gobject)
|
||||
{
|
||||
ClutterMasterClock *master_clock = CLUTTER_MASTER_CLOCK (gobject);
|
||||
GSList *l;
|
||||
|
||||
for (l = master_clock->timelines; l != NULL; l = l->next)
|
||||
{
|
||||
ClutterTimeline *timeline = l->data;
|
||||
|
||||
g_object_weak_unref (G_OBJECT (timeline),
|
||||
timeline_weak_ref,
|
||||
master_clock);
|
||||
|
||||
g_signal_handlers_disconnect_by_func (timeline,
|
||||
G_CALLBACK (on_timeline_started),
|
||||
master_clock);
|
||||
g_signal_handlers_disconnect_by_func (timeline,
|
||||
G_CALLBACK (on_timeline_completed),
|
||||
master_clock);
|
||||
g_signal_handlers_disconnect_by_func (timeline,
|
||||
G_CALLBACK (on_timeline_paused),
|
||||
master_clock);
|
||||
}
|
||||
|
||||
g_slist_free (master_clock->timelines);
|
||||
|
||||
@ -288,6 +303,8 @@ clutter_master_clock_init (ClutterMasterClock *self)
|
||||
source = clutter_clock_source_new (self);
|
||||
self->source = source;
|
||||
|
||||
self->updated_stages = TRUE;
|
||||
|
||||
g_source_set_priority (source, CLUTTER_PRIORITY_REDRAW);
|
||||
g_source_set_can_recurse (source, FALSE);
|
||||
g_source_attach (source, NULL);
|
||||
@ -313,77 +330,30 @@ _clutter_master_clock_get_default (void)
|
||||
return default_clock;
|
||||
}
|
||||
|
||||
static void
|
||||
on_timeline_started (ClutterTimeline *timeline,
|
||||
ClutterMasterClock *master_clock)
|
||||
{
|
||||
/* we want to reset the prev_tick if this is the first
|
||||
* timeline; since timeline is playing we need to filter
|
||||
* it out, otherwise has_running_timeline() will return
|
||||
* TRUE and prev_tick will not be unset
|
||||
*/
|
||||
if (!has_running_timeline (master_clock, timeline))
|
||||
master_clock->prev_tick.tv_sec = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
on_timeline_completed (ClutterTimeline *timeline,
|
||||
ClutterMasterClock *master_clock)
|
||||
{
|
||||
/* if this is the last timeline we need to turn :last-advance
|
||||
* on in order to queue the redraw of the scene for the last
|
||||
* frame; otherwise the ClockSource will fail the prepare and
|
||||
* check phases and the last frame will not be painted
|
||||
*/
|
||||
if (!has_running_timeline (master_clock, NULL))
|
||||
master_clock->last_advance = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_timeline_paused (ClutterTimeline *timeline,
|
||||
ClutterMasterClock *master_clock)
|
||||
{
|
||||
/* see the comment in on_timeline_completed */
|
||||
if (!has_running_timeline (master_clock, NULL))
|
||||
master_clock->last_advance = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* _clutter_master_clock_add_timeline:
|
||||
* @master_clock: a #ClutterMasterClock
|
||||
* @timeline: a #ClutterTimeline
|
||||
*
|
||||
* Adds @timeline to the list of timelines held by the master
|
||||
* clock. This function should be called during the instance
|
||||
* creation phase of the timeline.
|
||||
* Adds @timeline to the list of playing timelines held by the master
|
||||
* clock.
|
||||
*/
|
||||
void
|
||||
_clutter_master_clock_add_timeline (ClutterMasterClock *master_clock,
|
||||
ClutterTimeline *timeline)
|
||||
{
|
||||
gboolean is_first = FALSE;
|
||||
gboolean is_first;
|
||||
|
||||
if (g_slist_find (master_clock->timelines, timeline))
|
||||
return;
|
||||
|
||||
is_first = (master_clock->timelines == NULL) ? TRUE : FALSE;
|
||||
is_first = master_clock->timelines == NULL;
|
||||
|
||||
master_clock->timelines = g_slist_prepend (master_clock->timelines,
|
||||
timeline);
|
||||
|
||||
g_object_weak_ref (G_OBJECT (timeline),
|
||||
timeline_weak_ref,
|
||||
master_clock);
|
||||
|
||||
g_signal_connect (timeline, "started",
|
||||
G_CALLBACK (on_timeline_started),
|
||||
master_clock);
|
||||
g_signal_connect (timeline, "completed",
|
||||
G_CALLBACK (on_timeline_completed),
|
||||
master_clock);
|
||||
g_signal_connect (timeline, "paused",
|
||||
G_CALLBACK (on_timeline_paused),
|
||||
master_clock);
|
||||
if (is_first)
|
||||
_clutter_master_clock_start_running (master_clock);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -391,39 +361,31 @@ _clutter_master_clock_add_timeline (ClutterMasterClock *master_clock,
|
||||
* @master_clock: a #ClutterMasterClock
|
||||
* @timeline: a #ClutterTimeline
|
||||
*
|
||||
* Removes @timeline from the list of timelines held by the
|
||||
* master clock. This function should be called during the
|
||||
* #ClutterTimeline finalization.
|
||||
* Removes @timeline from the list of playing timelines held by the
|
||||
* master clock.
|
||||
*/
|
||||
void
|
||||
_clutter_master_clock_remove_timeline (ClutterMasterClock *master_clock,
|
||||
ClutterTimeline *timeline)
|
||||
{
|
||||
if (!g_slist_find (master_clock->timelines, timeline))
|
||||
return;
|
||||
|
||||
master_clock->timelines = g_slist_remove (master_clock->timelines,
|
||||
timeline);
|
||||
}
|
||||
|
||||
g_object_weak_unref (G_OBJECT (timeline),
|
||||
timeline_weak_ref,
|
||||
master_clock);
|
||||
|
||||
g_signal_handlers_disconnect_by_func (timeline,
|
||||
G_CALLBACK (on_timeline_started),
|
||||
master_clock);
|
||||
g_signal_handlers_disconnect_by_func (timeline,
|
||||
G_CALLBACK (on_timeline_completed),
|
||||
master_clock);
|
||||
g_signal_handlers_disconnect_by_func (timeline,
|
||||
G_CALLBACK (on_timeline_paused),
|
||||
master_clock);
|
||||
|
||||
/* last timeline: unset the prev_tick so that we can start
|
||||
* from scratch when we add a new timeline
|
||||
/*
|
||||
* _clutter_master_clock_start_running:
|
||||
* @master_clock: a #ClutterMasterClock
|
||||
*
|
||||
* Called when we have events or redraws to process; if the clock
|
||||
* is stopped, does the processing necessary to wake it up again.
|
||||
*/
|
||||
void
|
||||
_clutter_master_clock_start_running (ClutterMasterClock *master_clock)
|
||||
{
|
||||
/* If called from a different thread, we need to wake up the
|
||||
* main loop to start running the timelines
|
||||
*/
|
||||
if (master_clock->timelines == NULL)
|
||||
master_clock->prev_tick.tv_sec = 0;
|
||||
g_main_context_wakeup (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -437,41 +399,14 @@ _clutter_master_clock_remove_timeline (ClutterMasterClock *master_clock,
|
||||
void
|
||||
_clutter_master_clock_advance (ClutterMasterClock *master_clock)
|
||||
{
|
||||
GTimeVal cur_tick = { 0, };
|
||||
gulong msecs;
|
||||
GSList *l;
|
||||
GSList *l, *next;
|
||||
|
||||
g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock));
|
||||
|
||||
if (master_clock->timelines == NULL)
|
||||
return;
|
||||
|
||||
g_get_current_time (&cur_tick);
|
||||
|
||||
if (master_clock->prev_tick.tv_sec == 0)
|
||||
master_clock->prev_tick = cur_tick;
|
||||
|
||||
msecs = (cur_tick.tv_sec - master_clock->prev_tick.tv_sec) * 1000
|
||||
+ (cur_tick.tv_usec - master_clock->prev_tick.tv_usec) / 1000;
|
||||
|
||||
if (msecs == 0)
|
||||
return;
|
||||
|
||||
CLUTTER_NOTE (SCHEDULER, "Advancing %d timelines by %lu milliseconds",
|
||||
g_slist_length (master_clock->timelines),
|
||||
msecs);
|
||||
|
||||
for (l = master_clock->timelines; l != NULL; l = l->next)
|
||||
for (l = master_clock->timelines; l != NULL; l = next)
|
||||
{
|
||||
ClutterTimeline *timeline = l->data;
|
||||
next = l->next;
|
||||
|
||||
if (clutter_timeline_is_playing (timeline))
|
||||
clutter_timeline_advance_delta (timeline, msecs);
|
||||
clutter_timeline_do_tick (l->data, &master_clock->cur_tick);
|
||||
}
|
||||
|
||||
/* store the previous state so that we can use
|
||||
* it for the next advancement
|
||||
*/
|
||||
master_clock->msecs_delta = msecs;
|
||||
master_clock->prev_tick = cur_tick;
|
||||
}
|
||||
|
@ -42,6 +42,8 @@ void _clutter_master_clock_add_timeline (ClutterMasterClock *m
|
||||
void _clutter_master_clock_remove_timeline (ClutterMasterClock *master_clock,
|
||||
ClutterTimeline *timeline);
|
||||
void _clutter_master_clock_advance (ClutterMasterClock *master_clock);
|
||||
void _clutter_master_clock_start_running (ClutterMasterClock *master_clock);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -70,7 +70,6 @@ typedef enum {
|
||||
struct _ClutterInputDevice
|
||||
{
|
||||
gint id;
|
||||
gint32 motion_last_time;
|
||||
ClutterActor *pointer_grab_actor;
|
||||
ClutterActor *motion_last_actor;
|
||||
|
||||
@ -99,7 +98,6 @@ struct _ClutterMainContext
|
||||
|
||||
ClutterPickMode pick_mode; /* Indicates pick render mode */
|
||||
|
||||
guint motion_frequency; /* Motion events per second */
|
||||
gint num_reactives; /* Num of reactive actors */
|
||||
|
||||
ClutterIDPool *id_pool; /* mapping between reused integer ids
|
||||
@ -175,6 +173,13 @@ ClutterStageWindow *_clutter_stage_get_window (ClutterStage *sta
|
||||
ClutterStageWindow *_clutter_stage_get_default_window (void);
|
||||
void _clutter_stage_maybe_setup_viewport (ClutterStage *stage);
|
||||
void _clutter_stage_maybe_relayout (ClutterActor *stage);
|
||||
gboolean _clutter_stage_needs_update (ClutterStage *stage);
|
||||
gboolean _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 */
|
||||
GType _clutter_backend_impl_get_type (void);
|
||||
@ -205,6 +210,9 @@ gfloat _clutter_backend_get_units_per_em (ClutterBackend *backend
|
||||
|
||||
void _clutter_feature_init (void);
|
||||
|
||||
/* Reinjecting queued events for processing */
|
||||
void _clutter_process_event (ClutterEvent *event);
|
||||
|
||||
/* Picking code */
|
||||
ClutterActor *_clutter_do_pick (ClutterStage *stage,
|
||||
gint x,
|
||||
|
@ -87,8 +87,9 @@ struct _ClutterStagePrivate
|
||||
gchar *title;
|
||||
ClutterActor *key_focused_actor;
|
||||
|
||||
guint update_idle; /* repaint idler id */
|
||||
GQueue *event_queue;
|
||||
|
||||
guint redraw_pending : 1;
|
||||
guint is_fullscreen : 1;
|
||||
guint is_offscreen : 1;
|
||||
guint is_cursor_visible : 1;
|
||||
@ -390,29 +391,134 @@ clutter_stage_real_fullscreen (ClutterStage *stage)
|
||||
CLUTTER_ALLOCATION_NONE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
redraw_update_idle (gpointer user_data)
|
||||
void
|
||||
_clutter_stage_queue_event (ClutterStage *stage,
|
||||
ClutterEvent *event)
|
||||
{
|
||||
ClutterStage *stage = user_data;
|
||||
ClutterStagePrivate *priv = stage->priv;
|
||||
ClutterMasterClock *master_clock;
|
||||
ClutterStagePrivate *priv;
|
||||
gboolean first_event;
|
||||
|
||||
/* before we redraw we advance the master clock of one tick; this means
|
||||
* that all the timelines that need advancing will be advanced by one
|
||||
* frame. this will cause multiple redraw requests, so we do this before
|
||||
* we ask for a relayout and before we do the actual redraw. this ensures
|
||||
* that we paint the most updated scenegraph state and that all animations
|
||||
* are in sync with the paint process.
|
||||
*/
|
||||
CLUTTER_NOTE (PAINT, "Avdancing master clock");
|
||||
master_clock = _clutter_master_clock_get_default ();
|
||||
_clutter_master_clock_advance (master_clock);
|
||||
g_return_if_fail (CLUTTER_IS_STAGE (stage));
|
||||
|
||||
/* run the (eventual) repaint functions; since those might end up queuing
|
||||
* a relayout or a redraw we need to execute them before maybe_relayout()
|
||||
*/
|
||||
CLUTTER_NOTE (PAINT, "Repaint functions");
|
||||
_clutter_run_repaint_functions ();
|
||||
priv = stage->priv;
|
||||
|
||||
first_event = priv->event_queue->length == 0;
|
||||
|
||||
g_queue_push_tail (priv->event_queue,
|
||||
clutter_event_copy (event));
|
||||
|
||||
if (first_event)
|
||||
{
|
||||
ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
|
||||
_clutter_master_clock_start_running (master_clock);
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
* @stage: A #ClutterStage
|
||||
*
|
||||
* Determines if _clutter_stage_do_update() needs to be called.
|
||||
*
|
||||
* Return value: %TRUE if the stage need layout or painting
|
||||
*/
|
||||
gboolean
|
||||
_clutter_stage_needs_update (ClutterStage *stage)
|
||||
{
|
||||
ClutterStagePrivate *priv;
|
||||
|
||||
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
|
||||
|
||||
priv = stage->priv;
|
||||
|
||||
return priv->redraw_pending;
|
||||
}
|
||||
|
||||
/**
|
||||
* _clutter_stage_do_update:
|
||||
* @stage: A #ClutterStage
|
||||
*
|
||||
* Handles per-frame layout and repaint for the stage.
|
||||
*
|
||||
* Return value: %TRUE if the stage was updated
|
||||
*/
|
||||
gboolean
|
||||
_clutter_stage_do_update (ClutterStage *stage)
|
||||
{
|
||||
ClutterStagePrivate *priv;
|
||||
|
||||
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
|
||||
|
||||
priv = stage->priv;
|
||||
|
||||
if (!priv->redraw_pending)
|
||||
return FALSE;
|
||||
|
||||
/* clutter_do_redraw() will also call maybe_relayout(), but since a relayout
|
||||
* can queue a redraw, we want to do the relayout before we clear the
|
||||
@ -422,12 +528,11 @@ redraw_update_idle (gpointer user_data)
|
||||
*/
|
||||
_clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage));
|
||||
|
||||
/* redrawing will advance the master clock */
|
||||
CLUTTER_NOTE (PAINT, "redrawing via idle for stage[%p]", stage);
|
||||
_clutter_do_redraw (stage);
|
||||
|
||||
/* reset the guard, so that new redraws are possible */
|
||||
priv->update_idle = 0;
|
||||
priv->redraw_pending = FALSE;
|
||||
|
||||
if (CLUTTER_CONTEXT ()->redraw_count > 0)
|
||||
{
|
||||
@ -437,7 +542,7 @@ redraw_update_idle (gpointer user_data)
|
||||
CLUTTER_CONTEXT ()->redraw_count = 0;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -450,15 +555,14 @@ clutter_stage_real_queue_redraw (ClutterActor *actor,
|
||||
CLUTTER_NOTE (PAINT, "Redraw request number %lu",
|
||||
CLUTTER_CONTEXT ()->redraw_count + 1);
|
||||
|
||||
if (priv->update_idle == 0)
|
||||
if (!priv->redraw_pending)
|
||||
{
|
||||
CLUTTER_NOTE (PAINT, "Adding idle source for stage: %p", stage);
|
||||
ClutterMasterClock *master_clock;
|
||||
|
||||
priv->update_idle =
|
||||
clutter_threads_add_idle_full (CLUTTER_PRIORITY_REDRAW,
|
||||
redraw_update_idle,
|
||||
stage,
|
||||
NULL);
|
||||
priv->redraw_pending = TRUE;
|
||||
|
||||
master_clock = _clutter_master_clock_get_default ();
|
||||
_clutter_master_clock_start_running (master_clock);
|
||||
}
|
||||
else
|
||||
CLUTTER_CONTEXT ()->redraw_count += 1;
|
||||
@ -608,12 +712,6 @@ clutter_stage_dispose (GObject *object)
|
||||
|
||||
clutter_actor_hide (CLUTTER_ACTOR (object));
|
||||
|
||||
if (priv->update_idle)
|
||||
{
|
||||
g_source_remove (priv->update_idle);
|
||||
priv->update_idle = 0;
|
||||
}
|
||||
|
||||
_clutter_stage_manager_remove_stage (stage_manager, stage);
|
||||
|
||||
if (priv->impl)
|
||||
@ -631,9 +729,13 @@ static void
|
||||
clutter_stage_finalize (GObject *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_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
@ -904,6 +1006,8 @@ clutter_stage_init (ClutterStage *self)
|
||||
/* make sure that the implementation is considered a top level */
|
||||
CLUTTER_SET_PRIVATE_FLAGS (priv->impl, CLUTTER_ACTOR_IS_TOPLEVEL);
|
||||
|
||||
priv->event_queue = g_queue_new ();
|
||||
|
||||
priv->is_offscreen = FALSE;
|
||||
priv->is_fullscreen = FALSE;
|
||||
priv->is_user_resizable = FALSE;
|
||||
@ -1272,8 +1376,8 @@ clutter_stage_read_pixels (ClutterStage *stage,
|
||||
g_return_val_if_fail (x >= 0 && y >= 0, NULL);
|
||||
|
||||
/* Force a redraw of the stage before reading back pixels */
|
||||
clutter_redraw (stage);
|
||||
clutter_stage_ensure_current (stage);
|
||||
clutter_actor_paint (CLUTTER_ACTOR (stage));
|
||||
|
||||
glGetIntegerv (GL_VIEWPORT, viewport);
|
||||
stage_x = viewport[0];
|
||||
@ -1862,6 +1966,33 @@ clutter_stage_ensure_viewport (ClutterStage *stage)
|
||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_stage_ensure_redraw:
|
||||
* @stage: a #ClutterStage
|
||||
*
|
||||
* Ensures that @stage is redrawn
|
||||
*
|
||||
* This function should not be called by applications: it is
|
||||
* used when embedding a #ClutterStage into a toolkit with
|
||||
* another windowing system, like GTK+.
|
||||
*
|
||||
* Since: 1.0
|
||||
*/
|
||||
void
|
||||
clutter_stage_ensure_redraw (ClutterStage *stage)
|
||||
{
|
||||
ClutterMasterClock *master_clock;
|
||||
ClutterStagePrivate *priv;
|
||||
|
||||
g_return_if_fail (CLUTTER_IS_STAGE (stage));
|
||||
|
||||
priv = stage->priv;
|
||||
priv->redraw_pending = TRUE;
|
||||
|
||||
master_clock = _clutter_master_clock_get_default ();
|
||||
_clutter_master_clock_start_running (master_clock);
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_stage_queue_redraw:
|
||||
* @stage: the #ClutterStage
|
||||
|
@ -243,6 +243,7 @@ void clutter_stage_ensure_current (ClutterStage *stage);
|
||||
void clutter_stage_queue_redraw (ClutterStage *stage);
|
||||
gboolean clutter_stage_is_default (ClutterStage *stage);
|
||||
void clutter_stage_ensure_viewport (ClutterStage *stage);
|
||||
void clutter_stage_ensure_redraw (ClutterStage *stage);
|
||||
|
||||
/* Commodity macro */
|
||||
#define clutter_stage_add(stage,actor) G_STMT_START { \
|
||||
|
@ -62,8 +62,13 @@ struct _ClutterTimelinePrivate
|
||||
|
||||
GHashTable *markers_by_name;
|
||||
|
||||
/* Time we last advanced the elapsed time and showed a frame */
|
||||
GTimeVal last_frame_time;
|
||||
|
||||
guint loop : 1;
|
||||
guint is_playing : 1;
|
||||
/* If we've just started playing and haven't yet gotten a tick from the master clock */
|
||||
guint waiting_first_tick : 1;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@ -204,8 +209,11 @@ clutter_timeline_finalize (GObject *object)
|
||||
if (priv->markers_by_name)
|
||||
g_hash_table_destroy (priv->markers_by_name);
|
||||
|
||||
master_clock = _clutter_master_clock_get_default ();
|
||||
_clutter_master_clock_remove_timeline (master_clock, self);
|
||||
if (priv->is_playing)
|
||||
{
|
||||
master_clock = _clutter_master_clock_get_default ();
|
||||
_clutter_master_clock_remove_timeline (master_clock, self);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (clutter_timeline_parent_class)->finalize (object);
|
||||
}
|
||||
@ -412,7 +420,6 @@ static void
|
||||
clutter_timeline_init (ClutterTimeline *self)
|
||||
{
|
||||
ClutterTimelinePrivate *priv;
|
||||
ClutterMasterClock *master_clock;
|
||||
|
||||
self->priv = priv =
|
||||
G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_TIMELINE,
|
||||
@ -421,33 +428,95 @@ clutter_timeline_init (ClutterTimeline *self)
|
||||
priv->duration = 0;
|
||||
priv->delay = 0;
|
||||
priv->elapsed_time = 0;
|
||||
}
|
||||
|
||||
master_clock = _clutter_master_clock_get_default ();
|
||||
_clutter_master_clock_add_timeline (master_clock, self);
|
||||
struct CheckIfMarkerHitClosure
|
||||
{
|
||||
ClutterTimeline *timeline;
|
||||
ClutterTimelineDirection direction;
|
||||
gint new_time;
|
||||
gint duration;
|
||||
gint delta;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
have_passed_time (const struct CheckIfMarkerHitClosure *data,
|
||||
gint msecs)
|
||||
{
|
||||
/* Ignore markers that are outside the duration of the timeline */
|
||||
if (msecs < 0 || msecs > data->duration)
|
||||
return FALSE;
|
||||
|
||||
if (data->direction == CLUTTER_TIMELINE_FORWARD)
|
||||
{
|
||||
/* We need to special case when a marker is added at the
|
||||
beginning of the timeline */
|
||||
if (msecs == 0 &&
|
||||
data->delta > 0 &&
|
||||
data->new_time - data->delta <= 0)
|
||||
return TRUE;
|
||||
|
||||
/* Otherwise it's just a simple test if the time is in range of
|
||||
the previous time and the new time */
|
||||
return (msecs > data->new_time - data->delta
|
||||
&& msecs <= data->new_time);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We need to special case when a marker is added at the
|
||||
end of the timeline */
|
||||
if (msecs == data->duration &&
|
||||
data->delta > 0 &&
|
||||
data->new_time + data->delta >= data->duration)
|
||||
return TRUE;
|
||||
|
||||
/* Otherwise it's just a simple test if the time is in range of
|
||||
the previous time and the new time */
|
||||
return (msecs >= data->new_time
|
||||
&& msecs < data->new_time + data->delta);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_if_marker_hit (const gchar *name,
|
||||
TimelineMarker *marker,
|
||||
ClutterTimeline *timeline)
|
||||
struct CheckIfMarkerHitClosure *data)
|
||||
{
|
||||
ClutterTimelinePrivate *priv = timeline->priv;
|
||||
|
||||
if (priv->direction == CLUTTER_TIMELINE_FORWARD
|
||||
? (marker->msecs > priv->elapsed_time - priv->msecs_delta
|
||||
&& marker->msecs <= priv->elapsed_time)
|
||||
: (marker->msecs >= priv->elapsed_time
|
||||
&& marker->msecs < priv->elapsed_time + priv->msecs_delta))
|
||||
if (have_passed_time (data, marker->msecs))
|
||||
{
|
||||
CLUTTER_NOTE (SCHEDULER, "Marker '%s' reached", name);
|
||||
|
||||
g_signal_emit (timeline, timeline_signals[MARKER_REACHED],
|
||||
g_signal_emit (data->timeline, timeline_signals[MARKER_REACHED],
|
||||
marker->quark,
|
||||
name,
|
||||
marker->msecs);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_markers (ClutterTimeline *timeline,
|
||||
gint delta)
|
||||
{
|
||||
ClutterTimelinePrivate *priv = timeline->priv;
|
||||
struct CheckIfMarkerHitClosure data;
|
||||
|
||||
/* shortcircuit here if we don't have any marker installed */
|
||||
if (priv->markers_by_name == NULL)
|
||||
return;
|
||||
|
||||
/* store the details of the timeline so that changing them in a
|
||||
marker signal handler won't affect which markers are hit */
|
||||
data.timeline = timeline;
|
||||
data.direction = priv->direction;
|
||||
data.new_time = priv->elapsed_time;
|
||||
data.duration = priv->duration;
|
||||
data.delta = delta;
|
||||
|
||||
g_hash_table_foreach (priv->markers_by_name,
|
||||
(GHFunc) check_if_marker_hit,
|
||||
&data);
|
||||
}
|
||||
|
||||
static void
|
||||
emit_frame_signal (ClutterTimeline *timeline)
|
||||
{
|
||||
@ -455,14 +524,6 @@ emit_frame_signal (ClutterTimeline *timeline)
|
||||
|
||||
g_signal_emit (timeline, timeline_signals[NEW_FRAME], 0,
|
||||
priv->elapsed_time);
|
||||
|
||||
/* shortcircuit here if we don't have any marker installed */
|
||||
if (priv->markers_by_name == NULL)
|
||||
return;
|
||||
|
||||
g_hash_table_foreach (priv->markers_by_name,
|
||||
(GHFunc) check_if_marker_hit,
|
||||
timeline);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -475,8 +536,33 @@ is_complete (ClutterTimeline *timeline)
|
||||
: priv->elapsed_time <= 0);
|
||||
}
|
||||
|
||||
static void
|
||||
set_is_playing (ClutterTimeline *timeline,
|
||||
gboolean is_playing)
|
||||
{
|
||||
ClutterTimelinePrivate *priv = timeline->priv;
|
||||
ClutterMasterClock *master_clock;
|
||||
|
||||
is_playing = is_playing != FALSE;
|
||||
|
||||
if (is_playing == priv->is_playing)
|
||||
return;
|
||||
|
||||
priv->is_playing = is_playing;
|
||||
master_clock = _clutter_master_clock_get_default ();
|
||||
if (priv->is_playing)
|
||||
{
|
||||
_clutter_master_clock_add_timeline (master_clock, timeline);
|
||||
priv->waiting_first_tick = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
_clutter_master_clock_remove_timeline (master_clock, timeline);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clutter_timeline_advance_internal (ClutterTimeline *timeline)
|
||||
clutter_timeline_do_frame (ClutterTimeline *timeline)
|
||||
{
|
||||
ClutterTimelinePrivate *priv;
|
||||
|
||||
@ -499,6 +585,7 @@ clutter_timeline_advance_internal (ClutterTimeline *timeline)
|
||||
{
|
||||
/* Emit the signal */
|
||||
emit_frame_signal (timeline);
|
||||
check_markers (timeline, priv->msecs_delta);
|
||||
|
||||
/* Signal pauses timeline ? */
|
||||
if (!priv->is_playing)
|
||||
@ -514,21 +601,22 @@ clutter_timeline_advance_internal (ClutterTimeline *timeline)
|
||||
{
|
||||
/* Handle loop or stop */
|
||||
ClutterTimelineDirection saved_direction = priv->direction;
|
||||
gint elapsed_time_delta = priv->msecs_delta;
|
||||
guint overflow_msecs = priv->elapsed_time;
|
||||
gint end_msecs;
|
||||
|
||||
/* Update the current elapsed time in case the signal handlers
|
||||
* want to take a peek. If we clamp elapsed time, then we need
|
||||
* to correpondingly reduce msecs_delta to reflect the correct
|
||||
* to correpondingly reduce elapsed_time_delta to reflect the correct
|
||||
* range of times */
|
||||
if (priv->direction == CLUTTER_TIMELINE_FORWARD)
|
||||
{
|
||||
priv->msecs_delta -= (priv->elapsed_time - priv->duration);
|
||||
elapsed_time_delta -= (priv->elapsed_time - priv->duration);
|
||||
priv->elapsed_time = priv->duration;
|
||||
}
|
||||
else if (priv->direction == CLUTTER_TIMELINE_BACKWARD)
|
||||
{
|
||||
priv->msecs_delta -= - priv->elapsed_time;
|
||||
elapsed_time_delta -= - priv->elapsed_time;
|
||||
priv->elapsed_time = 0;
|
||||
}
|
||||
|
||||
@ -536,6 +624,7 @@ clutter_timeline_advance_internal (ClutterTimeline *timeline)
|
||||
|
||||
/* Emit the signal */
|
||||
emit_frame_signal (timeline);
|
||||
check_markers (timeline, elapsed_time_delta);
|
||||
|
||||
/* Did the signal handler modify the elapsed time? */
|
||||
if (priv->elapsed_time != end_msecs)
|
||||
@ -561,7 +650,7 @@ clutter_timeline_advance_internal (ClutterTimeline *timeline)
|
||||
* XXX Perhaps we should remove this earlier, and regardless
|
||||
* of priv->loop. Are we limiting the things that could be done in
|
||||
* the above new-frame signal handler */
|
||||
priv->is_playing = FALSE;
|
||||
set_is_playing (timeline, FALSE);
|
||||
}
|
||||
|
||||
g_signal_emit (timeline, timeline_signals[COMPLETED], 0);
|
||||
@ -592,6 +681,14 @@ clutter_timeline_advance_internal (ClutterTimeline *timeline)
|
||||
if (priv->direction != saved_direction)
|
||||
priv->elapsed_time = priv->duration - priv->elapsed_time;
|
||||
|
||||
/* If we have overflowed then we are changing the elapsed
|
||||
time without emitting the new frame signal so we need to
|
||||
check for markers again */
|
||||
check_markers (timeline,
|
||||
priv->direction == CLUTTER_TIMELINE_FORWARD ?
|
||||
priv->elapsed_time :
|
||||
priv->duration - priv->elapsed_time);
|
||||
|
||||
g_object_unref (timeline);
|
||||
return TRUE;
|
||||
}
|
||||
@ -613,7 +710,7 @@ delay_timeout_func (gpointer data)
|
||||
|
||||
priv->delay_id = 0;
|
||||
priv->msecs_delta = 0;
|
||||
priv->is_playing = TRUE;
|
||||
set_is_playing (timeline, TRUE);
|
||||
|
||||
g_signal_emit (timeline, timeline_signals[STARTED], 0);
|
||||
|
||||
@ -648,7 +745,7 @@ clutter_timeline_start (ClutterTimeline *timeline)
|
||||
else
|
||||
{
|
||||
priv->msecs_delta = 0;
|
||||
priv->is_playing = TRUE;
|
||||
set_is_playing (timeline, TRUE);
|
||||
|
||||
g_signal_emit (timeline, timeline_signals[STARTED], 0);
|
||||
}
|
||||
@ -679,7 +776,7 @@ clutter_timeline_pause (ClutterTimeline *timeline)
|
||||
}
|
||||
|
||||
priv->msecs_delta = 0;
|
||||
priv->is_playing = FALSE;
|
||||
set_is_playing (timeline, FALSE);
|
||||
|
||||
g_signal_emit (timeline, timeline_signals[PAUSED], 0);
|
||||
}
|
||||
@ -1116,19 +1213,18 @@ clutter_timeline_get_delta (ClutterTimeline *timeline)
|
||||
}
|
||||
|
||||
/*
|
||||
* clutter_timeline_advance_delta:
|
||||
* clutter_timeline_do_tick
|
||||
* @timeline: a #ClutterTimeline
|
||||
* @msecs: advance in milliseconds
|
||||
* @tick_time: time of advance
|
||||
*
|
||||
* Advances @timeline by @msecs. This function is called by the master
|
||||
* clock and it is used to advance a timeline by the amount of milliseconds
|
||||
* elapsed since the last redraw operation. The @timeline will use this
|
||||
* interval to emit the #ClutterTimeline::new-frame signal and eventually
|
||||
* skip frames.
|
||||
* Advances @timeline based on the time passed in @msecs. This
|
||||
* function is called by the master clock. The @timeline will use this
|
||||
* interval to emit the #ClutterTimeline::new-frame signal and
|
||||
* eventually skip frames.
|
||||
*/
|
||||
void
|
||||
clutter_timeline_advance_delta (ClutterTimeline *timeline,
|
||||
guint msecs)
|
||||
clutter_timeline_do_tick (ClutterTimeline *timeline,
|
||||
GTimeVal *tick_time)
|
||||
{
|
||||
ClutterTimelinePrivate *priv;
|
||||
|
||||
@ -1136,9 +1232,25 @@ clutter_timeline_advance_delta (ClutterTimeline *timeline,
|
||||
|
||||
priv = timeline->priv;
|
||||
|
||||
priv->msecs_delta = msecs;
|
||||
if (priv->waiting_first_tick)
|
||||
{
|
||||
priv->last_frame_time = *tick_time;
|
||||
priv->waiting_first_tick = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gint msecs =
|
||||
(tick_time->tv_sec - priv->last_frame_time.tv_sec) * 1000
|
||||
+ (tick_time->tv_usec - priv->last_frame_time.tv_usec) / 1000;
|
||||
|
||||
clutter_timeline_advance_internal (timeline);
|
||||
if (msecs != 0)
|
||||
{
|
||||
/* Avoid accumulating error */
|
||||
g_time_val_add (&priv->last_frame_time, msecs * 1000L);
|
||||
priv->msecs_delta = msecs;
|
||||
clutter_timeline_do_frame (timeline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -155,8 +155,8 @@ void clutter_timeline_advance_to_marker (ClutterTimeline *timeli
|
||||
const gchar *marker_name);
|
||||
|
||||
/*< private >*/
|
||||
void clutter_timeline_advance_delta (ClutterTimeline *timeline,
|
||||
guint msecs);
|
||||
void clutter_timeline_do_tick (ClutterTimeline *timeline,
|
||||
GTimeVal *tick_time);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -485,6 +485,7 @@ ClutterPickMode
|
||||
clutter_stage_get_actor_at_pos
|
||||
clutter_stage_ensure_current
|
||||
clutter_stage_ensure_viewport
|
||||
clutter_stage_ensure_redraw
|
||||
clutter_stage_queue_redraw
|
||||
clutter_stage_event
|
||||
clutter_stage_set_key_focus
|
||||
@ -576,7 +577,7 @@ CLUTTER_TIMELINE_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
ClutterTimelinePrivate
|
||||
clutter_timeline_get_type
|
||||
clutter_timeline_advance_delta
|
||||
clutter_timeline_do_tick
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
@ -968,7 +969,6 @@ clutter_event_get_type
|
||||
<FILE>clutter-main</FILE>
|
||||
<TITLE>General</TITLE>
|
||||
CLUTTER_PRIORITY_REDRAW
|
||||
CLUTTER_PRIORITY_TIMELINE
|
||||
|
||||
<SUBSECTION>
|
||||
ClutterInitError
|
||||
@ -991,8 +991,6 @@ clutter_set_default_frame_rate
|
||||
clutter_get_default_frame_rate
|
||||
clutter_set_motion_events_enabled
|
||||
clutter_get_motion_events_enabled
|
||||
clutter_set_motion_events_frequency
|
||||
clutter_get_motion_events_frequency
|
||||
clutter_clear_glyph_cache
|
||||
ClutterFontFlags
|
||||
clutter_set_font_flags
|
||||
|
@ -72,6 +72,13 @@ main (int argc, char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Turning of sync-to-vblank removes a dependency on the specifics of the
|
||||
* test environment. It also means that the timeline-only tests are
|
||||
* throttled to a reasonable frame rate rather than running in tight
|
||||
* infinite loop.
|
||||
*/
|
||||
g_setenv ("CLUTTER_VBLANK", "none", FALSE);
|
||||
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_bug_base ("http://bugzilla.openedhand.com/show_bug.cgi?id=%s");
|
||||
|
@ -49,7 +49,14 @@ on_paint (ClutterActor *label, CallbackData *data)
|
||||
static void
|
||||
force_redraw (CallbackData *data)
|
||||
{
|
||||
clutter_redraw (CLUTTER_STAGE (clutter_actor_get_stage (data->label)));
|
||||
/* XXX - this is fugly; we force a paint on the stage, which
|
||||
* will then paint the Text actor. inside the Text actor we
|
||||
* check for a Layout with the allocation size. if the allocation
|
||||
* has changed it will cause a relayout in the middle of the
|
||||
* paint, which is expensive and broken. this will ensure that
|
||||
* the test passes, though
|
||||
*/
|
||||
clutter_actor_paint (clutter_actor_get_stage (data->label));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@ -23,9 +23,6 @@ typedef struct _TestState
|
||||
gint expected_frame;
|
||||
gint completion_count;
|
||||
gboolean passed;
|
||||
guint source_id;
|
||||
GTimeVal prev_tick;
|
||||
gulong msecs_delta;
|
||||
} TestState;
|
||||
|
||||
|
||||
@ -62,16 +59,16 @@ new_frame_cb (ClutterTimeline *timeline,
|
||||
if (current_frame >= (state->expected_frame-TEST_ERROR_TOLERANCE)
|
||||
&& current_frame <= (state->expected_frame+TEST_ERROR_TOLERANCE))
|
||||
{
|
||||
g_test_message ("\nelapsed milliseconds=%-5li "
|
||||
"expected frame=%-4i actual frame=%-4i (OK)\n",
|
||||
g_test_message ("elapsed milliseconds=%-5li "
|
||||
"expected frame=%-4i actual frame=%-4i (OK)",
|
||||
msec_diff,
|
||||
state->expected_frame,
|
||||
current_frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_test_message ("\nelapsed milliseconds=%-5li "
|
||||
"expected frame=%-4i actual frame=%-4i (FAILED)\n",
|
||||
g_test_message ("elapsed milliseconds=%-5li "
|
||||
"expected frame=%-4i actual frame=%-4i (FAILED)",
|
||||
msec_diff,
|
||||
state->expected_frame,
|
||||
current_frame);
|
||||
@ -82,7 +79,7 @@ new_frame_cb (ClutterTimeline *timeline,
|
||||
{
|
||||
state->expected_frame = current_frame + (TEST_TIMELINE_FPS / 4);
|
||||
g_test_message ("Sleeping for 250ms "
|
||||
"so next frame should be (%i + %i) = %i\n",
|
||||
"so next frame should be (%i + %i) = %i",
|
||||
current_frame,
|
||||
(TEST_TIMELINE_FPS / 4),
|
||||
state->expected_frame);
|
||||
@ -92,7 +89,7 @@ new_frame_cb (ClutterTimeline *timeline,
|
||||
{
|
||||
state->expected_frame = current_frame + TEST_TIMELINE_FPS;
|
||||
g_test_message ("Sleeping for 1sec "
|
||||
"so next frame should be (%i + %i) = %i\n",
|
||||
"so next frame should be (%i + %i) = %i",
|
||||
current_frame,
|
||||
TEST_TIMELINE_FPS,
|
||||
state->expected_frame);
|
||||
@ -104,7 +101,7 @@ new_frame_cb (ClutterTimeline *timeline,
|
||||
state->expected_frame += loop_overflow;
|
||||
state->expected_frame -= TEST_TIMELINE_DURATION;
|
||||
g_test_message ("End of timeline reached: "
|
||||
"Wrapping expected frame too %i\n",
|
||||
"Wrapping expected frame too %i",
|
||||
state->expected_frame);
|
||||
}
|
||||
|
||||
@ -134,30 +131,6 @@ completed_cb (ClutterTimeline *timeline,
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frame_tick (gpointer data)
|
||||
{
|
||||
TestState *state = data;
|
||||
GTimeVal cur_tick = { 0, };
|
||||
gulong msecs;
|
||||
|
||||
g_get_current_time (&cur_tick);
|
||||
|
||||
if (state->prev_tick.tv_sec == 0)
|
||||
state->prev_tick = cur_tick;
|
||||
|
||||
msecs = (cur_tick.tv_sec - state->prev_tick.tv_sec) * 1000
|
||||
+ (cur_tick.tv_usec - state->prev_tick.tv_usec) / 1000;
|
||||
|
||||
if (clutter_timeline_is_playing (state->timeline))
|
||||
clutter_timeline_advance_delta (state->timeline, msecs);
|
||||
|
||||
state->msecs_delta = msecs;
|
||||
state->prev_tick = cur_tick;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
test_timeline_interpolate (TestConformSimpleFixture *fixture,
|
||||
gconstpointer data)
|
||||
@ -180,18 +153,11 @@ test_timeline_interpolate (TestConformSimpleFixture *fixture,
|
||||
state.new_frame_counter = 0;
|
||||
state.passed = TRUE;
|
||||
state.expected_frame = 0;
|
||||
state.prev_tick.tv_sec = 0;
|
||||
state.prev_tick.tv_usec = 0;
|
||||
state.msecs_delta = 0;
|
||||
|
||||
state.source_id =
|
||||
clutter_threads_add_frame_source (60, frame_tick, &state);
|
||||
|
||||
g_get_current_time (&state.start_time);
|
||||
clutter_timeline_start (state.timeline);
|
||||
|
||||
clutter_main();
|
||||
|
||||
g_source_remove (state.source_id);
|
||||
g_object_unref (state.timeline);
|
||||
}
|
||||
|
@ -11,25 +11,22 @@ typedef struct _TestState
|
||||
{
|
||||
ClutterTimeline *timeline;
|
||||
gint rewind_count;
|
||||
guint source_id;
|
||||
GTimeVal prev_tick;
|
||||
gulong msecs_delta;
|
||||
} TestState;
|
||||
|
||||
static gboolean
|
||||
watchdog_timeout (TestState *state)
|
||||
{
|
||||
g_test_message ("Watchdog timer kicking in\n");
|
||||
g_test_message ("rewind_count=%i\n", state->rewind_count);
|
||||
g_test_message ("Watchdog timer kicking in");
|
||||
g_test_message ("rewind_count=%i", state->rewind_count);
|
||||
if (state->rewind_count <= 3)
|
||||
{
|
||||
/* The test has hung */
|
||||
g_test_message ("Failed (This test shouldn't have hung!)\n");
|
||||
g_test_message ("Failed (This test shouldn't have hung!)");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_test_message ("Passed\n");
|
||||
g_test_message ("Passed");
|
||||
clutter_main_quit ();
|
||||
}
|
||||
|
||||
@ -45,8 +42,8 @@ new_frame_cb (ClutterTimeline *timeline,
|
||||
|
||||
if (elapsed_time == TEST_TIMELINE_DURATION)
|
||||
{
|
||||
g_test_message ("new-frame signal recieved (end of timeline)\n");
|
||||
g_test_message ("Rewinding timeline\n");
|
||||
g_test_message ("new-frame signal received (end of timeline)");
|
||||
g_test_message ("Rewinding timeline");
|
||||
clutter_timeline_rewind (timeline);
|
||||
state->rewind_count++;
|
||||
}
|
||||
@ -54,45 +51,21 @@ new_frame_cb (ClutterTimeline *timeline,
|
||||
{
|
||||
if (elapsed_time == 0)
|
||||
{
|
||||
g_test_message ("new-frame signal recieved (start of timeline)\n");
|
||||
g_test_message ("new-frame signal received (start of timeline)");
|
||||
}
|
||||
else
|
||||
{
|
||||
g_test_message ("new-frame signal recieved (mid frame)\n");
|
||||
g_test_message ("new-frame signal received (mid frame)");
|
||||
}
|
||||
|
||||
if (state->rewind_count >= 2)
|
||||
{
|
||||
g_test_message ("Sleeping for 1 second\n");
|
||||
g_test_message ("Sleeping for 1 second");
|
||||
g_usleep (1000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frame_tick (gpointer data)
|
||||
{
|
||||
TestState *state = data;
|
||||
GTimeVal cur_tick = { 0, };
|
||||
gulong msecs;
|
||||
|
||||
g_get_current_time (&cur_tick);
|
||||
|
||||
if (state->prev_tick.tv_sec == 0)
|
||||
state->prev_tick = cur_tick;
|
||||
|
||||
msecs = (cur_tick.tv_sec - state->prev_tick.tv_sec) * 1000
|
||||
+ (cur_tick.tv_usec - state->prev_tick.tv_usec) / 1000;
|
||||
|
||||
if (clutter_timeline_is_playing (state->timeline))
|
||||
clutter_timeline_advance_delta (state->timeline, msecs);
|
||||
|
||||
state->msecs_delta = msecs;
|
||||
state->prev_tick = cur_tick;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
test_timeline_rewind (TestConformSimpleFixture *fixture,
|
||||
gconstpointer data)
|
||||
@ -106,22 +79,15 @@ test_timeline_rewind (TestConformSimpleFixture *fixture,
|
||||
G_CALLBACK(new_frame_cb),
|
||||
&state);
|
||||
g_test_message ("Installing a watchdog timeout "
|
||||
"to determine if this test hangs\n");
|
||||
"to determine if this test hangs");
|
||||
g_timeout_add (TEST_WATCHDOG_KICK_IN_SECONDS*1000,
|
||||
(GSourceFunc)watchdog_timeout,
|
||||
&state);
|
||||
state.rewind_count = 0;
|
||||
state.prev_tick.tv_sec = 0;
|
||||
state.prev_tick.tv_usec = 0;
|
||||
state.msecs_delta = 0;
|
||||
|
||||
state.source_id =
|
||||
clutter_threads_add_frame_source (60, frame_tick, &state);
|
||||
|
||||
clutter_timeline_start (state.timeline);
|
||||
|
||||
clutter_main();
|
||||
|
||||
g_source_remove (state.source_id);
|
||||
g_object_unref (state.timeline);
|
||||
}
|
||||
|
@ -181,51 +181,10 @@ delay_cb (gpointer data)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
typedef struct _FrameCounter FrameCounter;
|
||||
|
||||
struct _FrameCounter
|
||||
{
|
||||
GTimeVal prev_tick;
|
||||
gulong msecs_delta;
|
||||
|
||||
GSList *timelines;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
frame_tick (gpointer data)
|
||||
{
|
||||
FrameCounter *counter = data;
|
||||
GTimeVal cur_tick = { 0, };
|
||||
GSList *l;
|
||||
gulong msecs;
|
||||
|
||||
g_get_current_time (&cur_tick);
|
||||
|
||||
if (counter->prev_tick.tv_sec == 0)
|
||||
counter->prev_tick = cur_tick;
|
||||
|
||||
msecs = (cur_tick.tv_sec - counter->prev_tick.tv_sec) * 1000
|
||||
+ (cur_tick.tv_usec - counter->prev_tick.tv_usec) / 1000;
|
||||
|
||||
for (l = counter->timelines; l != NULL; l = l->next)
|
||||
{
|
||||
ClutterTimeline *timeline = l->data;
|
||||
|
||||
if (clutter_timeline_is_playing (timeline))
|
||||
clutter_timeline_advance_delta (timeline, msecs);
|
||||
}
|
||||
|
||||
counter->msecs_delta = msecs;
|
||||
counter->prev_tick = cur_tick;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
test_timeline (TestConformSimpleFixture *fixture,
|
||||
gconstpointer data)
|
||||
{
|
||||
FrameCounter *counter;
|
||||
ClutterTimeline *timeline_1;
|
||||
TimelineData data_1;
|
||||
ClutterTimeline *timeline_2;
|
||||
@ -235,14 +194,11 @@ test_timeline (TestConformSimpleFixture *fixture,
|
||||
gchar **markers;
|
||||
gsize n_markers;
|
||||
guint delay_tag;
|
||||
guint source_id;
|
||||
|
||||
counter = g_new0 (FrameCounter, 1);
|
||||
|
||||
source_id = clutter_threads_add_frame_source (FPS, frame_tick, counter);
|
||||
|
||||
timeline_data_init (&data_1, 1);
|
||||
timeline_1 = clutter_timeline_new (FRAME_COUNT * 1000 / FPS);
|
||||
clutter_timeline_add_marker_at_time (timeline_1, "start-marker",
|
||||
0 * 1000 / FPS);
|
||||
clutter_timeline_add_marker_at_time (timeline_1, "foo", 5 * 1000 / FPS);
|
||||
clutter_timeline_add_marker_at_time (timeline_1, "bar", 5 * 1000 / FPS);
|
||||
clutter_timeline_add_marker_at_time (timeline_1, "baz", 5 * 1000 / FPS);
|
||||
@ -256,8 +212,6 @@ test_timeline (TestConformSimpleFixture *fixture,
|
||||
g_assert (n_markers == 3);
|
||||
g_strfreev (markers);
|
||||
|
||||
counter->timelines = g_slist_prepend (counter->timelines, timeline_1);
|
||||
|
||||
timeline_data_init (&data_2, 2);
|
||||
timeline_2 = clutter_timeline_clone (timeline_1);
|
||||
clutter_timeline_add_marker_at_time (timeline_2, "bar", 2 * 1000 / FPS);
|
||||
@ -267,11 +221,11 @@ test_timeline (TestConformSimpleFixture *fixture,
|
||||
g_assert (strcmp (markers[0], "bar") == 0);
|
||||
g_strfreev (markers);
|
||||
|
||||
counter->timelines = g_slist_prepend (counter->timelines, timeline_2);
|
||||
|
||||
timeline_data_init (&data_3, 3);
|
||||
timeline_3 = clutter_timeline_clone (timeline_1);
|
||||
clutter_timeline_set_direction (timeline_3, CLUTTER_TIMELINE_BACKWARD);
|
||||
clutter_timeline_add_marker_at_time (timeline_3, "start-marker",
|
||||
FRAME_COUNT * 1000 / FPS);
|
||||
clutter_timeline_add_marker_at_time (timeline_3, "foo", 5 * 1000 / FPS);
|
||||
clutter_timeline_add_marker_at_time (timeline_3, "baz", 8 * 1000 / FPS);
|
||||
clutter_timeline_add_marker_at_time (timeline_3, "near-end-marker",
|
||||
@ -279,8 +233,6 @@ test_timeline (TestConformSimpleFixture *fixture,
|
||||
clutter_timeline_add_marker_at_time (timeline_3, "end-marker",
|
||||
0 * 1000 / FPS);
|
||||
|
||||
counter->timelines = g_slist_prepend (counter->timelines, timeline_3);
|
||||
|
||||
g_signal_connect (timeline_1,
|
||||
"marker-reached", G_CALLBACK (timeline_marker_reached_cb),
|
||||
&data_1);
|
||||
@ -359,9 +311,4 @@ test_timeline (TestConformSimpleFixture *fixture,
|
||||
timeline_data_destroy (&data_3);
|
||||
|
||||
g_source_remove (delay_tag);
|
||||
|
||||
g_source_remove (source_id);
|
||||
|
||||
g_slist_free (counter->timelines);
|
||||
g_free (counter);
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ do_events (ClutterActor *stage)
|
||||
{
|
||||
glong i;
|
||||
static gdouble angle = 0;
|
||||
ClutterEvent event;
|
||||
|
||||
for (i = 0; i < n_events; i++)
|
||||
{
|
||||
@ -44,18 +43,13 @@ do_events (ClutterActor *stage)
|
||||
while (angle > M_PI * 2.0)
|
||||
angle -= M_PI * 2.0;
|
||||
|
||||
event.type = CLUTTER_MOTION;
|
||||
event.any.stage = CLUTTER_STAGE (stage);
|
||||
event.any.time = CLUTTER_CURRENT_TIME;
|
||||
event.motion.flags = 0;
|
||||
event.motion.source = NULL;
|
||||
event.motion.x = (gint)(256.0 + 206.0 * cos (angle));
|
||||
event.motion.y = (gint)(256.0 + 206.0 * sin (angle));
|
||||
event.motion.modifier_state = 0;
|
||||
event.motion.axes = NULL;
|
||||
event.motion.device = NULL;
|
||||
|
||||
clutter_event_put (&event);
|
||||
/* If we synthesized events, they would be motion compressed;
|
||||
* calling get_actor_at_position() doesn't have that problem
|
||||
*/
|
||||
clutter_stage_get_actor_at_pos (CLUTTER_STAGE (stage),
|
||||
CLUTTER_PICK_REACTIVE,
|
||||
256.0 + 206.0 * cos (angle),
|
||||
256.0 + 206.0 * sin (angle));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,6 +77,7 @@ main (int argc, char *argv[])
|
||||
int row, col;
|
||||
|
||||
g_setenv ("CLUTTER_VBLANK", "none", FALSE);
|
||||
g_setenv ("CLUTTER_DEFAULT_FPS", "1000", FALSE);
|
||||
|
||||
clutter_init (&argc, &argv);
|
||||
|
||||
|
@ -48,6 +48,7 @@ main (int argc, char *argv[])
|
||||
ClutterActor *group;
|
||||
|
||||
g_setenv ("CLUTTER_VBLANK", "none", FALSE);
|
||||
g_setenv ("CLUTTER_DEFAULT_FPS", "1000", FALSE);
|
||||
|
||||
clutter_init (&argc, &argv);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user