Blame clutter/clutter-timeline.c

Packit 31ecd5
/*
Packit 31ecd5
 * Clutter.
Packit 31ecd5
 *
Packit 31ecd5
 * An OpenGL based 'interactive canvas' library.
Packit 31ecd5
 *
Packit 31ecd5
 * Authored By Matthew Allum  <mallum@openedhand.com>
Packit 31ecd5
 *
Packit 31ecd5
 * Copyright (C) 2006 OpenedHand
Packit 31ecd5
 *
Packit 31ecd5
 * This library is free software; you can redistribute it and/or
Packit 31ecd5
 * modify it under the terms of the GNU Lesser General Public
Packit 31ecd5
 * License as published by the Free Software Foundation; either
Packit 31ecd5
 * version 2 of the License, or (at your option) any later version.
Packit 31ecd5
 *
Packit 31ecd5
 * This library is distributed in the hope that it will be useful,
Packit 31ecd5
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 31ecd5
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 31ecd5
 * Lesser General Public License for more details.
Packit 31ecd5
 *
Packit 31ecd5
 * You should have received a copy of the GNU Lesser General Public
Packit 31ecd5
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Packit 31ecd5
 */
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * SECTION:clutter-timeline
Packit 31ecd5
 * @short_description: A class for time-based events
Packit 31ecd5
 * @see_also: #ClutterAnimation, #ClutterAnimator, #ClutterState
Packit 31ecd5
 *
Packit 31ecd5
 * #ClutterTimeline is a base class for managing time-based event that cause
Packit 31ecd5
 * Clutter to redraw a stage, such as animations.
Packit 31ecd5
 *
Packit 31ecd5
 * Each #ClutterTimeline instance has a duration: once a timeline has been
Packit 31ecd5
 * started, using clutter_timeline_start(), it will emit a signal that can
Packit 31ecd5
 * be used to update the state of the actors.
Packit 31ecd5
 *
Packit 31ecd5
 * It is important to note that #ClutterTimeline is not a generic API for
Packit 31ecd5
 * calling closures after an interval; each Timeline is tied into the master
Packit 31ecd5
 * clock used to drive the frame cycle. If you need to schedule a closure
Packit 31ecd5
 * after an interval, see clutter_threads_add_timeout() instead.
Packit 31ecd5
 *
Packit 31ecd5
 * Users of #ClutterTimeline should connect to the #ClutterTimeline::new-frame
Packit 31ecd5
 * signal, which is emitted each time a timeline is advanced during the maste
Packit 31ecd5
 * clock iteration. The #ClutterTimeline::new-frame signal provides the time
Packit 31ecd5
 * elapsed since the beginning of the timeline, in milliseconds. A normalized
Packit 31ecd5
 * progress value can be obtained by calling clutter_timeline_get_progress().
Packit 31ecd5
 * By using clutter_timeline_get_delta() it is possible to obtain the wallclock
Packit 31ecd5
 * time elapsed since the last emission of the #ClutterTimeline::new-frame
Packit 31ecd5
 * signal.
Packit 31ecd5
 *
Packit 31ecd5
 * Initial state can be set up by using the #ClutterTimeline::started signal,
Packit 31ecd5
 * while final state can be set up by using the #ClutterTimeline::stopped
Packit 31ecd5
 * signal. The #ClutterTimeline guarantees the emission of at least a single
Packit 31ecd5
 * #ClutterTimeline::new-frame signal, as well as the emission of the
Packit 31ecd5
 * #ClutterTimeline::completed signal every time the #ClutterTimeline reaches
Packit 31ecd5
 * its #ClutterTimeline:duration.
Packit 31ecd5
 *
Packit 31ecd5
 * It is possible to connect to specific points in the timeline progress by
Packit 31ecd5
 * adding markers using clutter_timeline_add_marker_at_time() and connecting
Packit 31ecd5
 * to the #ClutterTimeline::marker-reached signal.
Packit 31ecd5
 *
Packit 31ecd5
 * Timelines can be made to loop once they reach the end of their duration, by
Packit 31ecd5
 * using clutter_timeline_set_repeat_count(); a looping timeline will still
Packit 31ecd5
 * emit the #ClutterTimeline::completed signal once it reaches the end of its
Packit 31ecd5
 * duration at each repeat. If you want to be notified of the end of the last
Packit 31ecd5
 * repeat, use the #ClutterTimeline::stopped signal.
Packit 31ecd5
 *
Packit 31ecd5
 * Timelines have a #ClutterTimeline:direction: the default direction is
Packit 31ecd5
 * %CLUTTER_TIMELINE_FORWARD, and goes from 0 to the duration; it is possible
Packit 31ecd5
 * to change the direction to %CLUTTER_TIMELINE_BACKWARD, and have the timeline
Packit 31ecd5
 * go from the duration to 0. The direction can be automatically reversed
Packit 31ecd5
 * when reaching completion by using the #ClutterTimeline:auto-reverse property.
Packit 31ecd5
 *
Packit 31ecd5
 * Timelines are used in the Clutter animation framework by classes like
Packit 31ecd5
 * #ClutterAnimation, #ClutterAnimator, and #ClutterState.
Packit 31ecd5
 *
Packit 31ecd5
 * ## Defining Timelines in ClutterScript
Packit 31ecd5
 *
Packit 31ecd5
 * A #ClutterTimeline can be described in #ClutterScript like any
Packit 31ecd5
 * other object. Additionally, it is possible to define markers directly
Packit 31ecd5
 * inside the JSON definition by using the `markers` JSON object member,
Packit 31ecd5
 * such as:
Packit 31ecd5
 *
Packit 31ecd5
 * |[
Packit 31ecd5
{
Packit 31ecd5
  "type" : "ClutterTimeline",
Packit 31ecd5
  "duration" : 1000,
Packit 31ecd5
  "markers" : [
Packit 31ecd5
    { "name" : "quarter", "time" : 250 },
Packit 31ecd5
    { "name" : "half-time", "time" : 500 },
Packit 31ecd5
    { "name" : "three-quarters", "time" : 750 }
Packit 31ecd5
  ]
Packit 31ecd5
}
Packit 31ecd5
 * ]|
Packit 31ecd5
 */
Packit 31ecd5
Packit 31ecd5
#ifdef HAVE_CONFIG_H
Packit 31ecd5
#include "config.h"
Packit 31ecd5
#endif
Packit 31ecd5
Packit 31ecd5
#include "clutter-timeline.h"
Packit 31ecd5
Packit 31ecd5
#include "clutter-debug.h"
Packit 31ecd5
#include "clutter-easing.h"
Packit 31ecd5
#include "clutter-enum-types.h"
Packit 31ecd5
#include "clutter-main.h"
Packit 31ecd5
#include "clutter-marshal.h"
Packit 31ecd5
#include "clutter-master-clock.h"
Packit 31ecd5
#include "clutter-private.h"
Packit 31ecd5
#include "clutter-scriptable.h"
Packit 31ecd5
Packit 31ecd5
#include "deprecated/clutter-timeline.h"
Packit 31ecd5
Packit 31ecd5
struct _ClutterTimelinePrivate
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelineDirection direction;
Packit 31ecd5
Packit 31ecd5
  guint delay_id;
Packit 31ecd5
Packit 31ecd5
  /* The total length in milliseconds of this timeline */
Packit 31ecd5
  guint duration;
Packit 31ecd5
  guint delay;
Packit 31ecd5
Packit 31ecd5
  /* The current amount of elapsed time */
Packit 31ecd5
  gint64 elapsed_time;
Packit 31ecd5
Packit 31ecd5
  /* The elapsed time since the last frame was fired */
Packit 31ecd5
  gint64 msecs_delta;
Packit 31ecd5
Packit 31ecd5
  GHashTable *markers_by_name;
Packit 31ecd5
Packit 31ecd5
  /* Time we last advanced the elapsed time and showed a frame */
Packit 31ecd5
  gint64 last_frame_time;
Packit 31ecd5
Packit 31ecd5
  /* How many times the timeline should repeat */
Packit 31ecd5
  gint repeat_count;
Packit 31ecd5
Packit 31ecd5
  /* The number of times the timeline has repeated */
Packit 31ecd5
  gint current_repeat;
Packit 31ecd5
Packit 31ecd5
  ClutterTimelineProgressFunc progress_func;
Packit 31ecd5
  gpointer progress_data;
Packit 31ecd5
  GDestroyNotify progress_notify;
Packit 31ecd5
  ClutterAnimationMode progress_mode;
Packit 31ecd5
Packit 31ecd5
  /* step() parameters */
Packit 31ecd5
  gint n_steps;
Packit 31ecd5
  ClutterStepMode step_mode;
Packit 31ecd5
Packit 31ecd5
  /* cubic-bezier() parameters */
Packit 31ecd5
  ClutterPoint cb_1;
Packit 31ecd5
  ClutterPoint cb_2;
Packit 31ecd5
Packit 31ecd5
  guint is_playing         : 1;
Packit 31ecd5
Packit 31ecd5
  /* If we've just started playing and haven't yet gotten
Packit 31ecd5
   * a tick from the master clock
Packit 31ecd5
   */
Packit 31ecd5
  guint waiting_first_tick : 1;
Packit 31ecd5
  guint auto_reverse       : 1;
Packit 31ecd5
};
Packit 31ecd5
Packit 31ecd5
typedef struct {
Packit 31ecd5
  gchar *name;
Packit 31ecd5
  GQuark quark;
Packit 31ecd5
Packit 31ecd5
  union {
Packit 31ecd5
    guint msecs;
Packit 31ecd5
    gdouble progress;
Packit 31ecd5
  } data;
Packit 31ecd5
Packit 31ecd5
  guint is_relative : 1;
Packit 31ecd5
} TimelineMarker;
Packit 31ecd5
Packit 31ecd5
enum
Packit 31ecd5
{
Packit 31ecd5
  PROP_0,
Packit 31ecd5
Packit 31ecd5
  PROP_LOOP,
Packit 31ecd5
  PROP_DELAY,
Packit 31ecd5
  PROP_DURATION,
Packit 31ecd5
  PROP_DIRECTION,
Packit 31ecd5
  PROP_AUTO_REVERSE,
Packit 31ecd5
  PROP_REPEAT_COUNT,
Packit 31ecd5
  PROP_PROGRESS_MODE,
Packit 31ecd5
Packit 31ecd5
  PROP_LAST
Packit 31ecd5
};
Packit 31ecd5
Packit 31ecd5
static GParamSpec *obj_props[PROP_LAST] = { NULL, };
Packit 31ecd5
Packit 31ecd5
enum
Packit 31ecd5
{
Packit 31ecd5
  NEW_FRAME,
Packit 31ecd5
  STARTED,
Packit 31ecd5
  PAUSED,
Packit 31ecd5
  COMPLETED,
Packit 31ecd5
  MARKER_REACHED,
Packit 31ecd5
  STOPPED,
Packit 31ecd5
Packit 31ecd5
  LAST_SIGNAL
Packit 31ecd5
};
Packit 31ecd5
Packit 31ecd5
static guint timeline_signals[LAST_SIGNAL] = { 0, };
Packit 31ecd5
Packit 31ecd5
static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
Packit 31ecd5
Packit 31ecd5
G_DEFINE_TYPE_WITH_CODE (ClutterTimeline, clutter_timeline, G_TYPE_OBJECT,
Packit 31ecd5
                         G_ADD_PRIVATE (ClutterTimeline)
Packit 31ecd5
                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
Packit 31ecd5
                                                clutter_scriptable_iface_init))
Packit 31ecd5
Packit 31ecd5
static TimelineMarker *
Packit 31ecd5
timeline_marker_new_time (const gchar *name,
Packit 31ecd5
                          guint        msecs)
Packit 31ecd5
{
Packit 31ecd5
  TimelineMarker *marker = g_slice_new (TimelineMarker);
Packit 31ecd5
Packit 31ecd5
  marker->name = g_strdup (name);
Packit 31ecd5
  marker->quark = g_quark_from_string (marker->name);
Packit 31ecd5
  marker->is_relative = FALSE;
Packit 31ecd5
  marker->data.msecs = msecs;
Packit 31ecd5
Packit 31ecd5
  return marker;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static TimelineMarker *
Packit 31ecd5
timeline_marker_new_progress (const gchar *name,
Packit 31ecd5
                              gdouble      progress)
Packit 31ecd5
{
Packit 31ecd5
  TimelineMarker *marker = g_slice_new (TimelineMarker);
Packit 31ecd5
Packit 31ecd5
  marker->name = g_strdup (name);
Packit 31ecd5
  marker->quark = g_quark_from_string (marker->name);
Packit 31ecd5
  marker->is_relative = TRUE;
Packit 31ecd5
  marker->data.progress = CLAMP (progress, 0.0, 1.0);
Packit 31ecd5
Packit 31ecd5
  return marker;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
timeline_marker_free (gpointer data)
Packit 31ecd5
{
Packit 31ecd5
  if (G_LIKELY (data))
Packit 31ecd5
    {
Packit 31ecd5
      TimelineMarker *marker = data;
Packit 31ecd5
Packit 31ecd5
      g_free (marker->name);
Packit 31ecd5
      g_slice_free (TimelineMarker, marker);
Packit 31ecd5
    }
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/*< private >
Packit 31ecd5
 * clutter_timeline_add_marker_internal:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @marker: a TimelineMarker
Packit 31ecd5
 *
Packit 31ecd5
 * Adds @marker into the hash table of markers for @timeline.
Packit 31ecd5
 *
Packit 31ecd5
 * The TimelineMarker will either be added or, in case of collisions
Packit 31ecd5
 * with another existing marker, freed. In any case, this function
Packit 31ecd5
 * assumes the ownership of the passed @marker.
Packit 31ecd5
 */
Packit 31ecd5
static inline void
Packit 31ecd5
clutter_timeline_add_marker_internal (ClutterTimeline *timeline,
Packit 31ecd5
                                      TimelineMarker  *marker)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv = timeline->priv;
Packit 31ecd5
  TimelineMarker *old_marker;
Packit 31ecd5
Packit 31ecd5
  /* create the hash table that will hold the markers */
Packit 31ecd5
  if (G_UNLIKELY (priv->markers_by_name == NULL))
Packit 31ecd5
    priv->markers_by_name = g_hash_table_new_full (g_str_hash, g_str_equal,
Packit 31ecd5
                                                   NULL,
Packit 31ecd5
                                                   timeline_marker_free);
Packit 31ecd5
Packit 31ecd5
  old_marker = g_hash_table_lookup (priv->markers_by_name, marker->name);
Packit 31ecd5
  if (old_marker != NULL)
Packit 31ecd5
    {
Packit 31ecd5
      guint msecs;
Packit 31ecd5
Packit 31ecd5
      if (old_marker->is_relative)
Packit 31ecd5
        msecs = old_marker->data.progress * priv->duration;
Packit 31ecd5
      else
Packit 31ecd5
        msecs = old_marker->data.msecs;
Packit 31ecd5
Packit 31ecd5
      g_warning ("A marker named '%s' already exists at time %d",
Packit 31ecd5
                 old_marker->name,
Packit 31ecd5
                 msecs);
Packit 31ecd5
      timeline_marker_free (marker);
Packit 31ecd5
      return;
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  g_hash_table_insert (priv->markers_by_name, marker->name, marker);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static inline void
Packit 31ecd5
clutter_timeline_set_loop_internal (ClutterTimeline *timeline,
Packit 31ecd5
                                    gboolean         loop)
Packit 31ecd5
{
Packit 31ecd5
  gint old_repeat_count;
Packit 31ecd5
Packit 31ecd5
  old_repeat_count = timeline->priv->repeat_count;
Packit 31ecd5
Packit 31ecd5
  if (loop)
Packit 31ecd5
    clutter_timeline_set_repeat_count (timeline, -1);
Packit 31ecd5
  else
Packit 31ecd5
    clutter_timeline_set_repeat_count (timeline, 0);
Packit 31ecd5
Packit 31ecd5
  if (old_repeat_count != timeline->priv->repeat_count)
Packit 31ecd5
    g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_LOOP]);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/* Scriptable */
Packit 31ecd5
typedef struct _ParseClosure {
Packit 31ecd5
  ClutterTimeline *timeline;
Packit 31ecd5
  ClutterScript *script;
Packit 31ecd5
  GValue *value;
Packit 31ecd5
  gboolean result;
Packit 31ecd5
} ParseClosure;
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
parse_timeline_markers (JsonArray *array,
Packit 31ecd5
                        guint      index_,
Packit 31ecd5
                        JsonNode  *element,
Packit 31ecd5
                        gpointer   data)
Packit 31ecd5
{
Packit 31ecd5
  ParseClosure *clos = data;
Packit 31ecd5
  JsonObject *object;
Packit 31ecd5
  TimelineMarker *marker;
Packit 31ecd5
  GList *markers;
Packit 31ecd5
Packit 31ecd5
  if (JSON_NODE_TYPE (element) != JSON_NODE_OBJECT)
Packit 31ecd5
    {
Packit 31ecd5
      g_warning ("The 'markers' member of a ClutterTimeline description "
Packit 31ecd5
                 "should be an array of objects, but the element %d of the "
Packit 31ecd5
                 "array is of type '%s'. The element will be ignored.",
Packit 31ecd5
                 index_,
Packit 31ecd5
                 json_node_type_name (element));
Packit 31ecd5
      return;
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  object = json_node_get_object (element);
Packit 31ecd5
Packit 31ecd5
  if (!(json_object_has_member (object, "name") &&
Packit 31ecd5
        (json_object_has_member (object, "time") ||
Packit 31ecd5
         json_object_has_member (object, "progress"))))
Packit 31ecd5
    {
Packit 31ecd5
      g_warning ("The marker definition in a ClutterTimeline description "
Packit 31ecd5
                 "must be an object with the 'name' and either the 'time' "
Packit 31ecd5
                 "or the 'progress' members, but the element %d of the "
Packit 31ecd5
                 "'markers' array does not have any of them.",
Packit 31ecd5
                 index_);
Packit 31ecd5
      return;
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  if (G_IS_VALUE (clos->value))
Packit 31ecd5
    markers = g_value_get_pointer (clos->value);
Packit 31ecd5
  else
Packit 31ecd5
    {
Packit 31ecd5
      g_value_init (clos->value, G_TYPE_POINTER);
Packit 31ecd5
      markers = NULL;
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  if (json_object_has_member (object, "time"))
Packit 31ecd5
    marker = timeline_marker_new_time (json_object_get_string_member (object, "name"),
Packit 31ecd5
                                       json_object_get_int_member (object, "time"));
Packit 31ecd5
  else
Packit 31ecd5
    marker = timeline_marker_new_progress (json_object_get_string_member (object, "name"),
Packit 31ecd5
                                           json_object_get_double_member (object, "progress"));
Packit 31ecd5
Packit 31ecd5
  markers = g_list_prepend (markers, marker);
Packit 31ecd5
Packit 31ecd5
  g_value_set_pointer (clos->value, markers);
Packit 31ecd5
Packit 31ecd5
  clos->result = TRUE;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static gboolean
Packit 31ecd5
clutter_timeline_parse_custom_node (ClutterScriptable *scriptable,
Packit 31ecd5
                                    ClutterScript     *script,
Packit 31ecd5
                                    GValue            *value,
Packit 31ecd5
                                    const gchar       *name,
Packit 31ecd5
                                    JsonNode          *node)
Packit 31ecd5
{
Packit 31ecd5
  ParseClosure clos;
Packit 31ecd5
Packit 31ecd5
  if (strcmp (name, "markers") != 0)
Packit 31ecd5
    return FALSE;
Packit 31ecd5
Packit 31ecd5
  if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
Packit 31ecd5
    return FALSE;
Packit 31ecd5
Packit 31ecd5
  clos.timeline = CLUTTER_TIMELINE (scriptable);
Packit 31ecd5
  clos.script = script;
Packit 31ecd5
  clos.value = value;
Packit 31ecd5
  clos.result = FALSE;
Packit 31ecd5
Packit 31ecd5
  json_array_foreach_element (json_node_get_array (node),
Packit 31ecd5
                              parse_timeline_markers,
Packit 31ecd5
                              &clos);
Packit 31ecd5
Packit 31ecd5
  return clos.result;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
clutter_timeline_set_custom_property (ClutterScriptable *scriptable,
Packit 31ecd5
                                      ClutterScript     *script,
Packit 31ecd5
                                      const gchar       *name,
Packit 31ecd5
                                      const GValue      *value)
Packit 31ecd5
{
Packit 31ecd5
  if (strcmp (name, "markers") == 0)
Packit 31ecd5
    {
Packit 31ecd5
      ClutterTimeline *timeline = CLUTTER_TIMELINE (scriptable);
Packit 31ecd5
      GList *markers = g_value_get_pointer (value);
Packit 31ecd5
      GList *m;
Packit 31ecd5
Packit 31ecd5
      /* the list was created through prepend() */
Packit 31ecd5
      markers = g_list_reverse (markers);
Packit 31ecd5
Packit 31ecd5
      for (m = markers; m != NULL; m = m->next)
Packit 31ecd5
        clutter_timeline_add_marker_internal (timeline, m->data);
Packit 31ecd5
Packit 31ecd5
      g_list_free (markers);
Packit 31ecd5
    }
Packit 31ecd5
  else
Packit 31ecd5
    g_object_set_property (G_OBJECT (scriptable), name, value);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
clutter_scriptable_iface_init (ClutterScriptableIface *iface)
Packit 31ecd5
{
Packit 31ecd5
  iface->parse_custom_node = clutter_timeline_parse_custom_node;
Packit 31ecd5
  iface->set_custom_property = clutter_timeline_set_custom_property;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/* Object */
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
clutter_timeline_set_property (GObject      *object,
Packit 31ecd5
			       guint         prop_id,
Packit 31ecd5
			       const GValue *value,
Packit 31ecd5
			       GParamSpec   *pspec)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimeline *timeline = CLUTTER_TIMELINE (object);
Packit 31ecd5
Packit 31ecd5
  switch (prop_id)
Packit 31ecd5
    {
Packit 31ecd5
    case PROP_LOOP:
Packit 31ecd5
      clutter_timeline_set_loop_internal (timeline, g_value_get_boolean (value));
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    case PROP_DELAY:
Packit 31ecd5
      clutter_timeline_set_delay (timeline, g_value_get_uint (value));
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    case PROP_DURATION:
Packit 31ecd5
      clutter_timeline_set_duration (timeline, g_value_get_uint (value));
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    case PROP_DIRECTION:
Packit 31ecd5
      clutter_timeline_set_direction (timeline, g_value_get_enum (value));
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    case PROP_AUTO_REVERSE:
Packit 31ecd5
      clutter_timeline_set_auto_reverse (timeline, g_value_get_boolean (value));
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    case PROP_REPEAT_COUNT:
Packit 31ecd5
      clutter_timeline_set_repeat_count (timeline, g_value_get_int (value));
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    case PROP_PROGRESS_MODE:
Packit 31ecd5
      clutter_timeline_set_progress_mode (timeline, g_value_get_enum (value));
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    default:
Packit 31ecd5
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit 31ecd5
      break;
Packit 31ecd5
    }
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
clutter_timeline_get_property (GObject    *object,
Packit 31ecd5
			       guint       prop_id,
Packit 31ecd5
			       GValue     *value,
Packit 31ecd5
			       GParamSpec *pspec)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimeline *timeline = CLUTTER_TIMELINE (object);
Packit 31ecd5
  ClutterTimelinePrivate *priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  switch (prop_id)
Packit 31ecd5
    {
Packit 31ecd5
    case PROP_LOOP:
Packit 31ecd5
      g_value_set_boolean (value, priv->repeat_count != 0);
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    case PROP_DELAY:
Packit 31ecd5
      g_value_set_uint (value, priv->delay);
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    case PROP_DURATION:
Packit 31ecd5
      g_value_set_uint (value, clutter_timeline_get_duration (timeline));
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    case PROP_DIRECTION:
Packit 31ecd5
      g_value_set_enum (value, priv->direction);
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    case PROP_AUTO_REVERSE:
Packit 31ecd5
      g_value_set_boolean (value, priv->auto_reverse);
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    case PROP_REPEAT_COUNT:
Packit 31ecd5
      g_value_set_int (value, priv->repeat_count);
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    case PROP_PROGRESS_MODE:
Packit 31ecd5
      g_value_set_enum (value, priv->progress_mode);
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    default:
Packit 31ecd5
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit 31ecd5
      break;
Packit 31ecd5
    }
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
clutter_timeline_finalize (GObject *object)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimeline *self = CLUTTER_TIMELINE (object);
Packit 31ecd5
  ClutterTimelinePrivate *priv = self->priv;
Packit 31ecd5
  ClutterMasterClock *master_clock;
Packit 31ecd5
Packit 31ecd5
  if (priv->markers_by_name)
Packit 31ecd5
    g_hash_table_destroy (priv->markers_by_name);
Packit 31ecd5
Packit 31ecd5
  if (priv->is_playing)
Packit 31ecd5
    {
Packit 31ecd5
      master_clock = _clutter_master_clock_get_default ();
Packit 31ecd5
      _clutter_master_clock_remove_timeline (master_clock, self);
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  G_OBJECT_CLASS (clutter_timeline_parent_class)->finalize (object);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
clutter_timeline_dispose (GObject *object)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimeline *self = CLUTTER_TIMELINE(object);
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  priv = self->priv;
Packit 31ecd5
Packit 31ecd5
  if (priv->delay_id)
Packit 31ecd5
    {
Packit 31ecd5
      g_source_remove (priv->delay_id);
Packit 31ecd5
      priv->delay_id = 0;
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  if (priv->progress_notify != NULL)
Packit 31ecd5
    {
Packit 31ecd5
      priv->progress_notify (priv->progress_data);
Packit 31ecd5
      priv->progress_func = NULL;
Packit 31ecd5
      priv->progress_data = NULL;
Packit 31ecd5
      priv->progress_notify = NULL;
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  G_OBJECT_CLASS (clutter_timeline_parent_class)->dispose (object);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
clutter_timeline_class_init (ClutterTimelineClass *klass)
Packit 31ecd5
{
Packit 31ecd5
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
Packit 31ecd5
Packit 31ecd5
  /**
Packit 31ecd5
   * ClutterTimeline:loop:
Packit 31ecd5
   *
Packit 31ecd5
   * Whether the timeline should automatically rewind and restart.
Packit 31ecd5
   *
Packit 31ecd5
   * As a side effect, setting this property to %TRUE will set the
Packit 31ecd5
   * #ClutterTimeline:repeat-count property to -1, while setting this
Packit 31ecd5
   * property to %FALSE will set the #ClutterTimeline:repeat-count
Packit 31ecd5
   * property to 0.
Packit 31ecd5
   *
Packit 31ecd5
   * Deprecated: 1.10: Use the #ClutterTimeline:repeat-count property instead.
Packit 31ecd5
   */
Packit 31ecd5
  obj_props[PROP_LOOP] =
Packit 31ecd5
    g_param_spec_boolean ("loop",
Packit 31ecd5
                          P_("Loop"),
Packit 31ecd5
                          P_("Should the timeline automatically restart"),
Packit 31ecd5
                          FALSE,
Packit 31ecd5
                          CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED);
Packit 31ecd5
Packit 31ecd5
  /**
Packit 31ecd5
   * ClutterTimeline:delay:
Packit 31ecd5
   *
Packit 31ecd5
   * A delay, in milliseconds, that should be observed by the
Packit 31ecd5
   * timeline before actually starting.
Packit 31ecd5
   *
Packit 31ecd5
   * Since: 0.4
Packit 31ecd5
   */
Packit 31ecd5
  obj_props[PROP_DELAY] =
Packit 31ecd5
    g_param_spec_uint ("delay",
Packit 31ecd5
                       P_("Delay"),
Packit 31ecd5
                       P_("Delay before start"),
Packit 31ecd5
                       0, G_MAXUINT,
Packit 31ecd5
                       0,
Packit 31ecd5
                       CLUTTER_PARAM_READWRITE);
Packit 31ecd5
Packit 31ecd5
  /**
Packit 31ecd5
   * ClutterTimeline:duration:
Packit 31ecd5
   *
Packit 31ecd5
   * Duration of the timeline in milliseconds, depending on the
Packit 31ecd5
   * ClutterTimeline:fps value.
Packit 31ecd5
   *
Packit 31ecd5
   * Since: 0.6
Packit 31ecd5
   */
Packit 31ecd5
  obj_props[PROP_DURATION] =
Packit 31ecd5
    g_param_spec_uint ("duration",
Packit 31ecd5
                       P_("Duration"),
Packit 31ecd5
                       P_("Duration of the timeline in milliseconds"),
Packit 31ecd5
                       0, G_MAXUINT,
Packit 31ecd5
                       1000,
Packit 31ecd5
                       CLUTTER_PARAM_READWRITE);
Packit 31ecd5
Packit 31ecd5
  /**
Packit 31ecd5
   * ClutterTimeline:direction:
Packit 31ecd5
   *
Packit 31ecd5
   * The direction of the timeline, either %CLUTTER_TIMELINE_FORWARD or
Packit 31ecd5
   * %CLUTTER_TIMELINE_BACKWARD.
Packit 31ecd5
   *
Packit 31ecd5
   * Since: 0.6
Packit 31ecd5
   */
Packit 31ecd5
  obj_props[PROP_DIRECTION] =
Packit 31ecd5
    g_param_spec_enum ("direction",
Packit 31ecd5
                       P_("Direction"),
Packit 31ecd5
                       P_("Direction of the timeline"),
Packit 31ecd5
                       CLUTTER_TYPE_TIMELINE_DIRECTION,
Packit 31ecd5
                       CLUTTER_TIMELINE_FORWARD,
Packit 31ecd5
                       CLUTTER_PARAM_READWRITE);
Packit 31ecd5
Packit 31ecd5
  /**
Packit 31ecd5
   * ClutterTimeline:auto-reverse:
Packit 31ecd5
   *
Packit 31ecd5
   * If the direction of the timeline should be automatically reversed
Packit 31ecd5
   * when reaching the end.
Packit 31ecd5
   *
Packit 31ecd5
   * Since: 1.6
Packit 31ecd5
   */
Packit 31ecd5
  obj_props[PROP_AUTO_REVERSE] =
Packit 31ecd5
    g_param_spec_boolean ("auto-reverse",
Packit 31ecd5
                          P_("Auto Reverse"),
Packit 31ecd5
                          P_("Whether the direction should be reversed when reaching the end"),
Packit 31ecd5
                          FALSE,
Packit 31ecd5
                          CLUTTER_PARAM_READWRITE);
Packit 31ecd5
Packit 31ecd5
  /**
Packit 31ecd5
   * ClutterTimeline:repeat-count:
Packit 31ecd5
   *
Packit 31ecd5
   * Defines how many times the timeline should repeat.
Packit 31ecd5
   *
Packit 31ecd5
   * If the repeat count is 0, the timeline does not repeat.
Packit 31ecd5
   *
Packit 31ecd5
   * If the repeat count is set to -1, the timeline will repeat until it is
Packit 31ecd5
   * stopped.
Packit 31ecd5
   *
Packit 31ecd5
   * Since: 1.10
Packit 31ecd5
   */
Packit 31ecd5
  obj_props[PROP_REPEAT_COUNT] =
Packit 31ecd5
    g_param_spec_int ("repeat-count",
Packit 31ecd5
                      P_("Repeat Count"),
Packit 31ecd5
                      P_("How many times the timeline should repeat"),
Packit 31ecd5
                      -1, G_MAXINT,
Packit 31ecd5
                      0,
Packit 31ecd5
                      CLUTTER_PARAM_READWRITE);
Packit 31ecd5
Packit 31ecd5
  /**
Packit 31ecd5
   * ClutterTimeline:progress-mode:
Packit 31ecd5
   *
Packit 31ecd5
   * Controls the way a #ClutterTimeline computes the normalized progress.
Packit 31ecd5
   *
Packit 31ecd5
   * Since: 1.10
Packit 31ecd5
   */
Packit 31ecd5
  obj_props[PROP_PROGRESS_MODE] =
Packit 31ecd5
    g_param_spec_enum ("progress-mode",
Packit 31ecd5
                       P_("Progress Mode"),
Packit 31ecd5
                       P_("How the timeline should compute the progress"),
Packit 31ecd5
                       CLUTTER_TYPE_ANIMATION_MODE,
Packit 31ecd5
                       CLUTTER_LINEAR,
Packit 31ecd5
                       CLUTTER_PARAM_READWRITE);
Packit 31ecd5
Packit 31ecd5
  object_class->dispose = clutter_timeline_dispose;
Packit 31ecd5
  object_class->finalize = clutter_timeline_finalize;
Packit 31ecd5
  object_class->set_property = clutter_timeline_set_property;
Packit 31ecd5
  object_class->get_property = clutter_timeline_get_property;
Packit 31ecd5
  g_object_class_install_properties (object_class, PROP_LAST, obj_props);
Packit 31ecd5
Packit 31ecd5
  /**
Packit 31ecd5
   * ClutterTimeline::new-frame:
Packit 31ecd5
   * @timeline: the timeline which received the signal
Packit 31ecd5
   * @msecs: the elapsed time between 0 and duration
Packit 31ecd5
   *
Packit 31ecd5
   * The ::new-frame signal is emitted for each timeline running
Packit 31ecd5
   * timeline before a new frame is drawn to give animations a chance
Packit 31ecd5
   * to update the scene.
Packit 31ecd5
   */
Packit 31ecd5
  timeline_signals[NEW_FRAME] =
Packit 31ecd5
    g_signal_new (I_("new-frame"),
Packit 31ecd5
		  G_TYPE_FROM_CLASS (object_class),
Packit 31ecd5
		  G_SIGNAL_RUN_LAST,
Packit 31ecd5
		  G_STRUCT_OFFSET (ClutterTimelineClass, new_frame),
Packit 31ecd5
		  NULL, NULL,
Packit 31ecd5
		  _clutter_marshal_VOID__INT,
Packit 31ecd5
		  G_TYPE_NONE,
Packit 31ecd5
		  1, G_TYPE_INT);
Packit 31ecd5
  /**
Packit 31ecd5
   * ClutterTimeline::completed:
Packit 31ecd5
   * @timeline: the #ClutterTimeline which received the signal
Packit 31ecd5
   *
Packit 31ecd5
   * The #ClutterTimeline::completed signal is emitted when the timeline's
Packit 31ecd5
   * elapsed time reaches the value of the #ClutterTimeline:duration
Packit 31ecd5
   * property.
Packit 31ecd5
   *
Packit 31ecd5
   * This signal will be emitted even if the #ClutterTimeline is set to be
Packit 31ecd5
   * repeating.
Packit 31ecd5
   *
Packit 31ecd5
   * If you want to get notification on whether the #ClutterTimeline has
Packit 31ecd5
   * been stopped or has finished its run, including its eventual repeats,
Packit 31ecd5
   * you should use the #ClutterTimeline::stopped signal instead.
Packit 31ecd5
   */
Packit 31ecd5
  timeline_signals[COMPLETED] =
Packit 31ecd5
    g_signal_new (I_("completed"),
Packit 31ecd5
		  G_TYPE_FROM_CLASS (object_class),
Packit 31ecd5
		  G_SIGNAL_RUN_LAST,
Packit 31ecd5
		  G_STRUCT_OFFSET (ClutterTimelineClass, completed),
Packit 31ecd5
		  NULL, NULL,
Packit 31ecd5
		  _clutter_marshal_VOID__VOID,
Packit 31ecd5
		  G_TYPE_NONE, 0);
Packit 31ecd5
  /**
Packit 31ecd5
   * ClutterTimeline::started:
Packit 31ecd5
   * @timeline: the #ClutterTimeline which received the signal
Packit 31ecd5
   *
Packit 31ecd5
   * The ::started signal is emitted when the timeline starts its run.
Packit 31ecd5
   * This might be as soon as clutter_timeline_start() is invoked or
Packit 31ecd5
   * after the delay set in the ClutterTimeline:delay property has
Packit 31ecd5
   * expired.
Packit 31ecd5
   */
Packit 31ecd5
  timeline_signals[STARTED] =
Packit 31ecd5
    g_signal_new (I_("started"),
Packit 31ecd5
		  G_TYPE_FROM_CLASS (object_class),
Packit 31ecd5
		  G_SIGNAL_RUN_LAST,
Packit 31ecd5
		  G_STRUCT_OFFSET (ClutterTimelineClass, started),
Packit 31ecd5
		  NULL, NULL,
Packit 31ecd5
		  _clutter_marshal_VOID__VOID,
Packit 31ecd5
		  G_TYPE_NONE, 0);
Packit 31ecd5
  /**
Packit 31ecd5
   * ClutterTimeline::paused:
Packit 31ecd5
   * @timeline: the #ClutterTimeline which received the signal
Packit 31ecd5
   *
Packit 31ecd5
   * The ::paused signal is emitted when clutter_timeline_pause() is invoked.
Packit 31ecd5
   */
Packit 31ecd5
  timeline_signals[PAUSED] =
Packit 31ecd5
    g_signal_new (I_("paused"),
Packit 31ecd5
		  G_TYPE_FROM_CLASS (object_class),
Packit 31ecd5
		  G_SIGNAL_RUN_LAST,
Packit 31ecd5
		  G_STRUCT_OFFSET (ClutterTimelineClass, paused),
Packit 31ecd5
		  NULL, NULL,
Packit 31ecd5
		  _clutter_marshal_VOID__VOID,
Packit 31ecd5
		  G_TYPE_NONE, 0);
Packit 31ecd5
  /**
Packit 31ecd5
   * ClutterTimeline::marker-reached:
Packit 31ecd5
   * @timeline: the #ClutterTimeline which received the signal
Packit 31ecd5
   * @marker_name: the name of the marker reached
Packit 31ecd5
   * @msecs: the elapsed time
Packit 31ecd5
   *
Packit 31ecd5
   * The ::marker-reached signal is emitted each time a timeline
Packit 31ecd5
   * reaches a marker set with
Packit 31ecd5
   * clutter_timeline_add_marker_at_time(). This signal is detailed
Packit 31ecd5
   * with the name of the marker as well, so it is possible to connect
Packit 31ecd5
   * a callback to the ::marker-reached signal for a specific marker
Packit 31ecd5
   * with:
Packit 31ecd5
   *
Packit 31ecd5
   * <informalexample><programlisting>
Packit 31ecd5
   *   clutter_timeline_add_marker_at_time (timeline, "foo", 500);
Packit 31ecd5
   *   clutter_timeline_add_marker_at_time (timeline, "bar", 750);
Packit 31ecd5
   *
Packit 31ecd5
   *   g_signal_connect (timeline, "marker-reached",
Packit 31ecd5
   *                     G_CALLBACK (each_marker_reached), NULL);
Packit 31ecd5
   *   g_signal_connect (timeline, "marker-reached::foo",
Packit 31ecd5
   *                     G_CALLBACK (foo_marker_reached), NULL);
Packit 31ecd5
   *   g_signal_connect (timeline, "marker-reached::bar",
Packit 31ecd5
   *                     G_CALLBACK (bar_marker_reached), NULL);
Packit 31ecd5
   * </programlisting></informalexample>
Packit 31ecd5
   *
Packit 31ecd5
   * In the example, the first callback will be invoked for both
Packit 31ecd5
   * the "foo" and "bar" marker, while the second and third callbacks
Packit 31ecd5
   * will be invoked for the "foo" or "bar" markers, respectively.
Packit 31ecd5
   *
Packit 31ecd5
   * Since: 0.8
Packit 31ecd5
   */
Packit 31ecd5
  timeline_signals[MARKER_REACHED] =
Packit 31ecd5
    g_signal_new (I_("marker-reached"),
Packit 31ecd5
                  G_TYPE_FROM_CLASS (object_class),
Packit 31ecd5
                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE |
Packit 31ecd5
                  G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS,
Packit 31ecd5
                  G_STRUCT_OFFSET (ClutterTimelineClass, marker_reached),
Packit 31ecd5
                  NULL, NULL,
Packit 31ecd5
                  _clutter_marshal_VOID__STRING_INT,
Packit 31ecd5
                  G_TYPE_NONE, 2,
Packit 31ecd5
                  G_TYPE_STRING,
Packit 31ecd5
                  G_TYPE_INT);
Packit 31ecd5
  /**
Packit 31ecd5
   * ClutterTimeline::stopped:
Packit 31ecd5
   * @timeline: the #ClutterTimeline that emitted the signal
Packit 31ecd5
   * @is_finished: %TRUE if the signal was emitted at the end of the
Packit 31ecd5
   *   timeline.
Packit 31ecd5
   *
Packit 31ecd5
   * The #ClutterTimeline::stopped signal is emitted when the timeline
Packit 31ecd5
   * has been stopped, either because clutter_timeline_stop() has been
Packit 31ecd5
   * called, or because it has been exhausted.
Packit 31ecd5
   *
Packit 31ecd5
   * This is different from the #ClutterTimeline::completed signal,
Packit 31ecd5
   * which gets emitted after every repeat finishes.
Packit 31ecd5
   *
Packit 31ecd5
   * If the #ClutterTimeline has is marked as infinitely repeating,
Packit 31ecd5
   * this signal will never be emitted.
Packit 31ecd5
   *
Packit 31ecd5
   * Since: 1.12
Packit 31ecd5
   */
Packit 31ecd5
  timeline_signals[STOPPED] =
Packit 31ecd5
    g_signal_new (I_("stopped"),
Packit 31ecd5
		  G_TYPE_FROM_CLASS (object_class),
Packit 31ecd5
		  G_SIGNAL_RUN_LAST,
Packit 31ecd5
		  G_STRUCT_OFFSET (ClutterTimelineClass, stopped),
Packit 31ecd5
		  NULL, NULL,
Packit 31ecd5
		  _clutter_marshal_VOID__BOOLEAN,
Packit 31ecd5
		  G_TYPE_NONE, 1,
Packit 31ecd5
                  G_TYPE_BOOLEAN);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
clutter_timeline_init (ClutterTimeline *self)
Packit 31ecd5
{
Packit 31ecd5
  self->priv = clutter_timeline_get_instance_private (self);
Packit 31ecd5
Packit 31ecd5
  self->priv->progress_mode = CLUTTER_LINEAR;
Packit 31ecd5
Packit 31ecd5
  /* default steps() parameters are 1, end */
Packit 31ecd5
  self->priv->n_steps = 1;
Packit 31ecd5
  self->priv->step_mode = CLUTTER_STEP_MODE_END;
Packit 31ecd5
Packit 31ecd5
  /* default cubic-bezier() paramereters are (0, 0, 1, 1) */
Packit 31ecd5
  clutter_point_init (&self->priv->cb_1, 0, 0);
Packit 31ecd5
  clutter_point_init (&self->priv->cb_2, 1, 1);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
struct CheckIfMarkerHitClosure
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimeline *timeline;
Packit 31ecd5
  ClutterTimelineDirection direction;
Packit 31ecd5
  gint new_time;
Packit 31ecd5
  gint duration;
Packit 31ecd5
  gint delta;
Packit 31ecd5
};
Packit 31ecd5
Packit 31ecd5
static gboolean
Packit 31ecd5
have_passed_time (const struct CheckIfMarkerHitClosure *data,
Packit 31ecd5
                  gint msecs)
Packit 31ecd5
{
Packit 31ecd5
  /* Ignore markers that are outside the duration of the timeline */
Packit 31ecd5
  if (msecs < 0 || msecs > data->duration)
Packit 31ecd5
    return FALSE;
Packit 31ecd5
Packit 31ecd5
  if (data->direction == CLUTTER_TIMELINE_FORWARD)
Packit 31ecd5
    {
Packit 31ecd5
      /* We need to special case when a marker is added at the
Packit 31ecd5
         beginning of the timeline */
Packit 31ecd5
      if (msecs == 0 &&
Packit 31ecd5
          data->delta > 0 &&
Packit 31ecd5
          data->new_time - data->delta <= 0)
Packit 31ecd5
        return TRUE;
Packit 31ecd5
Packit 31ecd5
      /* Otherwise it's just a simple test if the time is in range of
Packit 31ecd5
         the previous time and the new time */
Packit 31ecd5
      return (msecs > data->new_time - data->delta &&
Packit 31ecd5
              msecs <= data->new_time);
Packit 31ecd5
    }
Packit 31ecd5
  else
Packit 31ecd5
    {
Packit 31ecd5
      /* We need to special case when a marker is added at the
Packit 31ecd5
         end of the timeline */
Packit 31ecd5
      if (msecs == data->duration &&
Packit 31ecd5
          data->delta > 0 &&
Packit 31ecd5
          data->new_time + data->delta >= data->duration)
Packit 31ecd5
        return TRUE;
Packit 31ecd5
Packit 31ecd5
      /* Otherwise it's just a simple test if the time is in range of
Packit 31ecd5
         the previous time and the new time */
Packit 31ecd5
      return (msecs >= data->new_time &&
Packit 31ecd5
              msecs < data->new_time + data->delta);
Packit 31ecd5
    }
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
check_if_marker_hit (const gchar *name,
Packit 31ecd5
                     TimelineMarker *marker,
Packit 31ecd5
                     struct CheckIfMarkerHitClosure *data)
Packit 31ecd5
{
Packit 31ecd5
  gint msecs;
Packit 31ecd5
Packit 31ecd5
  if (marker->is_relative)
Packit 31ecd5
    msecs = (gdouble) data->duration * marker->data.progress;
Packit 31ecd5
  else
Packit 31ecd5
    msecs = marker->data.msecs;
Packit 31ecd5
Packit 31ecd5
  if (have_passed_time (data, msecs))
Packit 31ecd5
    {
Packit 31ecd5
      CLUTTER_NOTE (SCHEDULER, "Marker '%s' reached", name);
Packit 31ecd5
Packit 31ecd5
      g_signal_emit (data->timeline, timeline_signals[MARKER_REACHED],
Packit 31ecd5
                     marker->quark,
Packit 31ecd5
                     name,
Packit 31ecd5
                     msecs);
Packit 31ecd5
    }
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
check_markers (ClutterTimeline *timeline,
Packit 31ecd5
               gint delta)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv = timeline->priv;
Packit 31ecd5
  struct CheckIfMarkerHitClosure data;
Packit 31ecd5
Packit 31ecd5
  /* shortcircuit here if we don't have any marker installed */
Packit 31ecd5
  if (priv->markers_by_name == NULL)
Packit 31ecd5
    return;
Packit 31ecd5
Packit 31ecd5
  /* store the details of the timeline so that changing them in a
Packit 31ecd5
     marker signal handler won't affect which markers are hit */
Packit 31ecd5
  data.timeline = timeline;
Packit 31ecd5
  data.direction = priv->direction;
Packit 31ecd5
  data.new_time = priv->elapsed_time;
Packit 31ecd5
  data.duration = priv->duration;
Packit 31ecd5
  data.delta = delta;
Packit 31ecd5
Packit 31ecd5
  g_hash_table_foreach (priv->markers_by_name,
Packit 31ecd5
                        (GHFunc) check_if_marker_hit,
Packit 31ecd5
                        &data);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
emit_frame_signal (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  /* see bug https://bugzilla.gnome.org/show_bug.cgi?id=654066 */
Packit 31ecd5
  gint elapsed = (gint) priv->elapsed_time;
Packit 31ecd5
Packit 31ecd5
  CLUTTER_NOTE (SCHEDULER, "Emitting ::new-frame signal on timeline[%p]", timeline);
Packit 31ecd5
Packit 31ecd5
  g_signal_emit (timeline, timeline_signals[NEW_FRAME], 0, elapsed);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static gboolean
Packit 31ecd5
is_complete (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  return (priv->direction == CLUTTER_TIMELINE_FORWARD
Packit 31ecd5
          ? priv->elapsed_time >= priv->duration
Packit 31ecd5
          : priv->elapsed_time <= 0);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
set_is_playing (ClutterTimeline *timeline,
Packit 31ecd5
                gboolean         is_playing)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv = timeline->priv;
Packit 31ecd5
  ClutterMasterClock *master_clock;
Packit 31ecd5
Packit 31ecd5
  is_playing = !!is_playing;
Packit 31ecd5
Packit 31ecd5
  if (is_playing == priv->is_playing)
Packit 31ecd5
    return;
Packit 31ecd5
Packit 31ecd5
  priv->is_playing = is_playing;
Packit 31ecd5
Packit 31ecd5
  master_clock = _clutter_master_clock_get_default ();
Packit 31ecd5
  if (priv->is_playing)
Packit 31ecd5
    {
Packit 31ecd5
      priv->waiting_first_tick = TRUE;
Packit 31ecd5
      priv->current_repeat = 0;
Packit 31ecd5
      _clutter_master_clock_add_timeline (master_clock, timeline);
Packit 31ecd5
    }
Packit 31ecd5
  else
Packit 31ecd5
    _clutter_master_clock_remove_timeline (master_clock, timeline);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static gboolean
Packit 31ecd5
clutter_timeline_do_frame (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  g_object_ref (timeline);
Packit 31ecd5
Packit 31ecd5
  CLUTTER_NOTE (SCHEDULER, "Timeline [%p] activated (elapsed time: %ld, "
Packit 31ecd5
                "duration: %ld, msecs_delta: %ld)\n",
Packit 31ecd5
                timeline,
Packit 31ecd5
                (long) priv->elapsed_time,
Packit 31ecd5
                (long) priv->duration,
Packit 31ecd5
                (long) priv->msecs_delta);
Packit 31ecd5
Packit 31ecd5
  /* Advance time */
Packit 31ecd5
  if (priv->direction == CLUTTER_TIMELINE_FORWARD)
Packit 31ecd5
    priv->elapsed_time += priv->msecs_delta;
Packit 31ecd5
  else
Packit 31ecd5
    priv->elapsed_time -= priv->msecs_delta;
Packit 31ecd5
Packit 31ecd5
  /* If we have not reached the end of the timeline: */
Packit 31ecd5
  if (!is_complete (timeline))
Packit 31ecd5
    {
Packit 31ecd5
      /* Emit the signal */
Packit 31ecd5
      emit_frame_signal (timeline);
Packit 31ecd5
      check_markers (timeline, priv->msecs_delta);
Packit 31ecd5
Packit 31ecd5
      g_object_unref (timeline);
Packit 31ecd5
Packit 31ecd5
      return priv->is_playing;
Packit 31ecd5
    }
Packit 31ecd5
  else
Packit 31ecd5
    {
Packit 31ecd5
      /* Handle loop or stop */
Packit 31ecd5
      ClutterTimelineDirection saved_direction = priv->direction;
Packit 31ecd5
      gint elapsed_time_delta = priv->msecs_delta;
Packit 31ecd5
      guint overflow_msecs = priv->elapsed_time;
Packit 31ecd5
      gint end_msecs;
Packit 31ecd5
Packit 31ecd5
      /* Update the current elapsed time in case the signal handlers
Packit 31ecd5
       * want to take a peek. If we clamp elapsed time, then we need
Packit 31ecd5
       * to correpondingly reduce elapsed_time_delta to reflect the correct
Packit 31ecd5
       * range of times */
Packit 31ecd5
      if (priv->direction == CLUTTER_TIMELINE_FORWARD)
Packit 31ecd5
	{
Packit 31ecd5
	  elapsed_time_delta -= (priv->elapsed_time - priv->duration);
Packit 31ecd5
	  priv->elapsed_time = priv->duration;
Packit 31ecd5
	}
Packit 31ecd5
      else if (priv->direction == CLUTTER_TIMELINE_BACKWARD)
Packit 31ecd5
	{
Packit 31ecd5
	  elapsed_time_delta -= - priv->elapsed_time;
Packit 31ecd5
	  priv->elapsed_time = 0;
Packit 31ecd5
	}
Packit 31ecd5
Packit 31ecd5
      end_msecs = priv->elapsed_time;
Packit 31ecd5
Packit 31ecd5
      /* Emit the signal */
Packit 31ecd5
      emit_frame_signal (timeline);
Packit 31ecd5
      check_markers (timeline, elapsed_time_delta);
Packit 31ecd5
Packit 31ecd5
      /* Did the signal handler modify the elapsed time? */
Packit 31ecd5
      if (priv->elapsed_time != end_msecs)
Packit 31ecd5
        {
Packit 31ecd5
          g_object_unref (timeline);
Packit 31ecd5
          return TRUE;
Packit 31ecd5
        }
Packit 31ecd5
Packit 31ecd5
      /* Note: If the new-frame signal handler paused the timeline
Packit 31ecd5
       * on the last frame we will still go ahead and send the
Packit 31ecd5
       * completed signal */
Packit 31ecd5
      CLUTTER_NOTE (SCHEDULER,
Packit 31ecd5
                    "Timeline [%p] completed (cur: %ld, tot: %ld)",
Packit 31ecd5
                    timeline,
Packit 31ecd5
                    (long) priv->elapsed_time,
Packit 31ecd5
                    (long) priv->msecs_delta);
Packit 31ecd5
Packit 31ecd5
      if (priv->is_playing &&
Packit 31ecd5
          (priv->repeat_count == 0 ||
Packit 31ecd5
           priv->repeat_count == priv->current_repeat))
Packit 31ecd5
        {
Packit 31ecd5
          /* We stop the timeline now, so that the completed signal handler
Packit 31ecd5
           * may choose to re-start the timeline
Packit 31ecd5
           *
Packit 31ecd5
           * XXX Perhaps we should do this earlier, and regardless of
Packit 31ecd5
           * priv->repeat_count. Are we limiting the things that could be
Packit 31ecd5
           * done in the above new-frame signal handler?
Packit 31ecd5
           */
Packit 31ecd5
          set_is_playing (timeline, FALSE);
Packit 31ecd5
Packit 31ecd5
          g_signal_emit (timeline, timeline_signals[COMPLETED], 0);
Packit 31ecd5
          g_signal_emit (timeline, timeline_signals[STOPPED], 0, TRUE);
Packit 31ecd5
        }
Packit 31ecd5
      else
Packit 31ecd5
        g_signal_emit (timeline, timeline_signals[COMPLETED], 0);
Packit 31ecd5
Packit 31ecd5
      priv->current_repeat += 1;
Packit 31ecd5
Packit 31ecd5
      if (priv->auto_reverse)
Packit 31ecd5
        {
Packit 31ecd5
          /* :auto-reverse changes the direction of the timeline */
Packit 31ecd5
          if (priv->direction == CLUTTER_TIMELINE_FORWARD)
Packit 31ecd5
            priv->direction = CLUTTER_TIMELINE_BACKWARD;
Packit 31ecd5
          else
Packit 31ecd5
            priv->direction = CLUTTER_TIMELINE_FORWARD;
Packit 31ecd5
Packit 31ecd5
          g_object_notify_by_pspec (G_OBJECT (timeline),
Packit 31ecd5
                                    obj_props[PROP_DIRECTION]);
Packit 31ecd5
        }
Packit 31ecd5
Packit 31ecd5
      /* Again check to see if the user has manually played with
Packit 31ecd5
       * the elapsed time, before we finally stop or loop the timeline */
Packit 31ecd5
Packit 31ecd5
      if (priv->elapsed_time != end_msecs &&
Packit 31ecd5
          !(/* Except allow changing time from 0 -> duration (or vice-versa)
Packit 31ecd5
               since these are considered equivalent */
Packit 31ecd5
            (priv->elapsed_time == 0 && end_msecs == priv->duration) ||
Packit 31ecd5
            (priv->elapsed_time == priv->duration && end_msecs == 0)
Packit 31ecd5
          ))
Packit 31ecd5
        {
Packit 31ecd5
          g_object_unref (timeline);
Packit 31ecd5
          return TRUE;
Packit 31ecd5
        }
Packit 31ecd5
Packit 31ecd5
      if (priv->repeat_count != 0)
Packit 31ecd5
        {
Packit 31ecd5
          /* We try and interpolate smoothly around a loop */
Packit 31ecd5
          if (saved_direction == CLUTTER_TIMELINE_FORWARD)
Packit 31ecd5
            priv->elapsed_time = overflow_msecs - priv->duration;
Packit 31ecd5
          else
Packit 31ecd5
            priv->elapsed_time = priv->duration + overflow_msecs;
Packit 31ecd5
Packit 31ecd5
          /* Or if the direction changed, we try and bounce */
Packit 31ecd5
          if (priv->direction != saved_direction)
Packit 31ecd5
            priv->elapsed_time = priv->duration - priv->elapsed_time;
Packit 31ecd5
Packit 31ecd5
          /* If we have overflowed then we are changing the elapsed
Packit 31ecd5
             time without emitting the new frame signal so we need to
Packit 31ecd5
             check for markers again */
Packit 31ecd5
          check_markers (timeline,
Packit 31ecd5
                         priv->direction == CLUTTER_TIMELINE_FORWARD
Packit 31ecd5
                           ? priv->elapsed_time
Packit 31ecd5
                           : priv->duration - priv->elapsed_time);
Packit 31ecd5
Packit 31ecd5
          g_object_unref (timeline);
Packit 31ecd5
          return TRUE;
Packit 31ecd5
        }
Packit 31ecd5
      else
Packit 31ecd5
        {
Packit 31ecd5
          clutter_timeline_rewind (timeline);
Packit 31ecd5
Packit 31ecd5
          g_object_unref (timeline);
Packit 31ecd5
          return FALSE;
Packit 31ecd5
        }
Packit 31ecd5
    }
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static gboolean
Packit 31ecd5
delay_timeout_func (gpointer data)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimeline *timeline = data;
Packit 31ecd5
  ClutterTimelinePrivate *priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  priv->delay_id = 0;
Packit 31ecd5
  priv->msecs_delta = 0;
Packit 31ecd5
  set_is_playing (timeline, TRUE);
Packit 31ecd5
Packit 31ecd5
  g_signal_emit (timeline, timeline_signals[STARTED], 0);
Packit 31ecd5
Packit 31ecd5
  return FALSE;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_start:
Packit 31ecd5
 * @timeline: A #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Starts the #ClutterTimeline playing.
Packit 31ecd5
 **/
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_start (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (priv->delay_id || priv->is_playing)
Packit 31ecd5
    return;
Packit 31ecd5
Packit 31ecd5
  if (priv->duration == 0)
Packit 31ecd5
    return;
Packit 31ecd5
Packit 31ecd5
  if (priv->delay)
Packit 31ecd5
    priv->delay_id = clutter_threads_add_timeout (priv->delay,
Packit 31ecd5
                                                  delay_timeout_func,
Packit 31ecd5
                                                  timeline);
Packit 31ecd5
  else
Packit 31ecd5
    {
Packit 31ecd5
      priv->msecs_delta = 0;
Packit 31ecd5
      set_is_playing (timeline, TRUE);
Packit 31ecd5
Packit 31ecd5
      g_signal_emit (timeline, timeline_signals[STARTED], 0);
Packit 31ecd5
    }
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_pause:
Packit 31ecd5
 * @timeline: A #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Pauses the #ClutterTimeline on current frame
Packit 31ecd5
 **/
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_pause (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (priv->delay_id == 0 && !priv->is_playing)
Packit 31ecd5
    return;
Packit 31ecd5
Packit 31ecd5
  if (priv->delay_id)
Packit 31ecd5
    {
Packit 31ecd5
      g_source_remove (priv->delay_id);
Packit 31ecd5
      priv->delay_id = 0;
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  priv->msecs_delta = 0;
Packit 31ecd5
  set_is_playing (timeline, FALSE);
Packit 31ecd5
Packit 31ecd5
  g_signal_emit (timeline, timeline_signals[PAUSED], 0);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_stop:
Packit 31ecd5
 * @timeline: A #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Stops the #ClutterTimeline and moves to frame 0
Packit 31ecd5
 **/
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_stop (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  gboolean was_playing;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
Packit 31ecd5
  /* we check the is_playing here because pause() will return immediately
Packit 31ecd5
   * if the timeline wasn't playing, so we don't know if it was actually
Packit 31ecd5
   * stopped, and yet we still don't want to emit a ::stopped signal if
Packit 31ecd5
   * the timeline was not playing in the first place.
Packit 31ecd5
   */
Packit 31ecd5
  was_playing = timeline->priv->is_playing;
Packit 31ecd5
Packit 31ecd5
  clutter_timeline_pause (timeline);
Packit 31ecd5
  clutter_timeline_rewind (timeline);
Packit 31ecd5
Packit 31ecd5
  if (was_playing)
Packit 31ecd5
    g_signal_emit (timeline, timeline_signals[STOPPED], 0, FALSE);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_set_loop:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @loop: %TRUE for enable looping
Packit 31ecd5
 *
Packit 31ecd5
 * Sets whether @timeline should loop.
Packit 31ecd5
 *
Packit 31ecd5
 * This function is equivalent to calling clutter_timeline_set_repeat_count()
Packit 31ecd5
 * with -1 if @loop is %TRUE, and with 0 if @loop is %FALSE.
Packit 31ecd5
 *
Packit 31ecd5
 * Deprecated: 1.10: Use clutter_timeline_set_repeat_count() instead.
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_set_loop (ClutterTimeline *timeline,
Packit 31ecd5
			   gboolean         loop)
Packit 31ecd5
{
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
Packit 31ecd5
  clutter_timeline_set_loop_internal (timeline, loop);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_get_loop:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Gets whether @timeline is looping
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: %TRUE if the timeline is looping
Packit 31ecd5
 *
Packit 31ecd5
 * Deprecated: 1.10: Use clutter_timeline_get_repeat_count() instead.
Packit 31ecd5
 */
Packit 31ecd5
gboolean
Packit 31ecd5
clutter_timeline_get_loop (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
Packit 31ecd5
Packit 31ecd5
  return timeline->priv->repeat_count != 0;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_rewind:
Packit 31ecd5
 * @timeline: A #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Rewinds #ClutterTimeline to the first frame if its direction is
Packit 31ecd5
 * %CLUTTER_TIMELINE_FORWARD and the last frame if it is
Packit 31ecd5
 * %CLUTTER_TIMELINE_BACKWARD.
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_rewind (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (priv->direction == CLUTTER_TIMELINE_FORWARD)
Packit 31ecd5
    clutter_timeline_advance (timeline, 0);
Packit 31ecd5
  else if (priv->direction == CLUTTER_TIMELINE_BACKWARD)
Packit 31ecd5
    clutter_timeline_advance (timeline, priv->duration);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_skip:
Packit 31ecd5
 * @timeline: A #ClutterTimeline
Packit 31ecd5
 * @msecs: Amount of time to skip
Packit 31ecd5
 *
Packit 31ecd5
 * Advance timeline by the requested time in milliseconds
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_skip (ClutterTimeline *timeline,
Packit 31ecd5
                       guint            msecs)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (priv->direction == CLUTTER_TIMELINE_FORWARD)
Packit 31ecd5
    {
Packit 31ecd5
      priv->elapsed_time += msecs;
Packit 31ecd5
Packit 31ecd5
      if (priv->elapsed_time > priv->duration)
Packit 31ecd5
        priv->elapsed_time = 1;
Packit 31ecd5
    }
Packit 31ecd5
  else if (priv->direction == CLUTTER_TIMELINE_BACKWARD)
Packit 31ecd5
    {
Packit 31ecd5
      priv->elapsed_time -= msecs;
Packit 31ecd5
Packit 31ecd5
      if (priv->elapsed_time < 1)
Packit 31ecd5
        priv->elapsed_time = priv->duration - 1;
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  priv->msecs_delta = 0;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_advance:
Packit 31ecd5
 * @timeline: A #ClutterTimeline
Packit 31ecd5
 * @msecs: Time to advance to
Packit 31ecd5
 *
Packit 31ecd5
 * Advance timeline to the requested point. The point is given as a
Packit 31ecd5
 * time in milliseconds since the timeline started.
Packit 31ecd5
 *
Packit 31ecd5
 * The @timeline will not emit the #ClutterTimeline::new-frame
Packit 31ecd5
 * signal for the given time. The first ::new-frame signal after the call to
Packit 31ecd5
 * clutter_timeline_advance() will be emit the skipped markers.
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_advance (ClutterTimeline *timeline,
Packit 31ecd5
                          guint            msecs)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  priv->elapsed_time = CLAMP (msecs, 0, priv->duration);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_get_elapsed_time:
Packit 31ecd5
 * @timeline: A #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Request the current time position of the timeline.
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: current elapsed time in milliseconds.
Packit 31ecd5
 */
Packit 31ecd5
guint
Packit 31ecd5
clutter_timeline_get_elapsed_time (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
Packit 31ecd5
Packit 31ecd5
  return timeline->priv->elapsed_time;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_is_playing:
Packit 31ecd5
 * @timeline: A #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Queries state of a #ClutterTimeline.
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: %TRUE if timeline is currently playing
Packit 31ecd5
 */
Packit 31ecd5
gboolean
Packit 31ecd5
clutter_timeline_is_playing (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
Packit 31ecd5
Packit 31ecd5
  return timeline->priv->is_playing;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_clone:
Packit 31ecd5
 * @timeline: #ClutterTimeline to duplicate.
Packit 31ecd5
 *
Packit 31ecd5
 * Create a new #ClutterTimeline instance which has property values
Packit 31ecd5
 * matching that of supplied timeline. The cloned timeline will not
Packit 31ecd5
 * be started and will not be positioned to the current position of
Packit 31ecd5
 * the original @timeline: you will have to start it with
Packit 31ecd5
 * clutter_timeline_start().
Packit 31ecd5
 *
Packit 31ecd5
 * The only cloned properties are:
Packit 31ecd5
 *
Packit 31ecd5
 *  - #ClutterTimeline:duration
Packit 31ecd5
 *  - #ClutterTimeline:loop
Packit 31ecd5
 *  - #ClutterTimeline:delay
Packit 31ecd5
 *  - #ClutterTimeline:direction
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: (transfer full): a new #ClutterTimeline, cloned
Packit 31ecd5
 *   from @timeline
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.4
Packit 31ecd5
 *
Packit 31ecd5
 * Deprecated: 1.10: Use clutter_timeline_new() or g_object_new()
Packit 31ecd5
 *   instead
Packit 31ecd5
 */
Packit 31ecd5
ClutterTimeline *
Packit 31ecd5
clutter_timeline_clone (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
Packit 31ecd5
Packit 31ecd5
  return g_object_new (CLUTTER_TYPE_TIMELINE,
Packit 31ecd5
                       "duration", timeline->priv->duration,
Packit 31ecd5
                       "loop", timeline->priv->repeat_count != 0,
Packit 31ecd5
                       "delay", timeline->priv->delay,
Packit 31ecd5
                       "direction", timeline->priv->direction,
Packit 31ecd5
                       NULL);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_new:
Packit 31ecd5
 * @msecs: Duration of the timeline in milliseconds
Packit 31ecd5
 *
Packit 31ecd5
 * Creates a new #ClutterTimeline with a duration of @msecs.
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: the newly created #ClutterTimeline instance. Use
Packit 31ecd5
 *   g_object_unref() when done using it
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.6
Packit 31ecd5
 */
Packit 31ecd5
ClutterTimeline *
Packit 31ecd5
clutter_timeline_new (guint msecs)
Packit 31ecd5
{
Packit 31ecd5
  return g_object_new (CLUTTER_TYPE_TIMELINE,
Packit 31ecd5
                       "duration", msecs,
Packit 31ecd5
                       NULL);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_get_delay:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Retrieves the delay set using clutter_timeline_set_delay().
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: the delay in milliseconds.
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.4
Packit 31ecd5
 */
Packit 31ecd5
guint
Packit 31ecd5
clutter_timeline_get_delay (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
Packit 31ecd5
Packit 31ecd5
  return timeline->priv->delay;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_set_delay:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @msecs: delay in milliseconds
Packit 31ecd5
 *
Packit 31ecd5
 * Sets the delay, in milliseconds, before @timeline should start.
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.4
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_set_delay (ClutterTimeline *timeline,
Packit 31ecd5
                            guint            msecs)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (priv->delay != msecs)
Packit 31ecd5
    {
Packit 31ecd5
      priv->delay = msecs;
Packit 31ecd5
      g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_DELAY]);
Packit 31ecd5
    }
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_get_duration:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Retrieves the duration of a #ClutterTimeline in milliseconds.
Packit 31ecd5
 * See clutter_timeline_set_duration().
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: the duration of the timeline, in milliseconds.
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.6
Packit 31ecd5
 */
Packit 31ecd5
guint
Packit 31ecd5
clutter_timeline_get_duration (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  return priv->duration;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_set_duration:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @msecs: duration of the timeline in milliseconds
Packit 31ecd5
 *
Packit 31ecd5
 * Sets the duration of the timeline, in milliseconds. The speed
Packit 31ecd5
 * of the timeline depends on the ClutterTimeline:fps setting.
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.6
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_set_duration (ClutterTimeline *timeline,
Packit 31ecd5
                               guint            msecs)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
  g_return_if_fail (msecs > 0);
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (priv->duration != msecs)
Packit 31ecd5
    {
Packit 31ecd5
      priv->duration = msecs;
Packit 31ecd5
Packit 31ecd5
      g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_DURATION]);
Packit 31ecd5
    }
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_get_progress:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * The position of the timeline in a normalized [-1, 2] interval.
Packit 31ecd5
 *
Packit 31ecd5
 * The return value of this function is determined by the progress
Packit 31ecd5
 * mode set using clutter_timeline_set_progress_mode(), or by the
Packit 31ecd5
 * progress function set using clutter_timeline_set_progress_func().
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: the normalized current position in the timeline.
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.6
Packit 31ecd5
 */
Packit 31ecd5
gdouble
Packit 31ecd5
clutter_timeline_get_progress (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0.0);
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  /* short-circuit linear progress */
Packit 31ecd5
  if (priv->progress_func == NULL)
Packit 31ecd5
    return (gdouble) priv->elapsed_time / (gdouble) priv->duration;
Packit 31ecd5
  else
Packit 31ecd5
    return priv->progress_func (timeline,
Packit 31ecd5
                                (gdouble) priv->elapsed_time,
Packit 31ecd5
                                (gdouble) priv->duration,
Packit 31ecd5
                                priv->progress_data);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_get_direction:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Retrieves the direction of the timeline set with
Packit 31ecd5
 * clutter_timeline_set_direction().
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: the direction of the timeline
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.6
Packit 31ecd5
 */
Packit 31ecd5
ClutterTimelineDirection
Packit 31ecd5
clutter_timeline_get_direction (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline),
Packit 31ecd5
                        CLUTTER_TIMELINE_FORWARD);
Packit 31ecd5
Packit 31ecd5
  return timeline->priv->direction;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_set_direction:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @direction: the direction of the timeline
Packit 31ecd5
 *
Packit 31ecd5
 * Sets the direction of @timeline, either %CLUTTER_TIMELINE_FORWARD or
Packit 31ecd5
 * %CLUTTER_TIMELINE_BACKWARD.
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.6
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_set_direction (ClutterTimeline          *timeline,
Packit 31ecd5
                                ClutterTimelineDirection  direction)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (priv->direction != direction)
Packit 31ecd5
    {
Packit 31ecd5
      priv->direction = direction;
Packit 31ecd5
Packit 31ecd5
      if (priv->elapsed_time == 0)
Packit 31ecd5
        priv->elapsed_time = priv->duration;
Packit 31ecd5
Packit 31ecd5
      g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_DIRECTION]);
Packit 31ecd5
    }
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_get_delta:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Retrieves the amount of time elapsed since the last
Packit 31ecd5
 * ClutterTimeline::new-frame signal.
Packit 31ecd5
 *
Packit 31ecd5
 * This function is only useful inside handlers for the ::new-frame
Packit 31ecd5
 * signal, and its behaviour is undefined if the timeline is not
Packit 31ecd5
 * playing.
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: the amount of time in milliseconds elapsed since the
Packit 31ecd5
 * last frame
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.6
Packit 31ecd5
 */
Packit 31ecd5
guint
Packit 31ecd5
clutter_timeline_get_delta (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
Packit 31ecd5
Packit 31ecd5
  if (!clutter_timeline_is_playing (timeline))
Packit 31ecd5
    return 0;
Packit 31ecd5
Packit 31ecd5
  return timeline->priv->msecs_delta;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
void
Packit 31ecd5
_clutter_timeline_advance (ClutterTimeline *timeline,
Packit 31ecd5
                           gint64           tick_time)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  g_object_ref (timeline);
Packit 31ecd5
Packit 31ecd5
  CLUTTER_NOTE (SCHEDULER,
Packit 31ecd5
                "Timeline [%p] advancing (cur: %ld, tot: %ld, "
Packit 31ecd5
                "tick_time: %lu)",
Packit 31ecd5
                timeline,
Packit 31ecd5
                (long) priv->elapsed_time,
Packit 31ecd5
                (long) priv->msecs_delta,
Packit 31ecd5
                (long) tick_time);
Packit 31ecd5
Packit 31ecd5
  priv->msecs_delta = tick_time;
Packit 31ecd5
  priv->is_playing = TRUE;
Packit 31ecd5
Packit 31ecd5
  clutter_timeline_do_frame (timeline);
Packit 31ecd5
Packit 31ecd5
  priv->is_playing = FALSE;
Packit 31ecd5
Packit 31ecd5
  g_object_unref (timeline);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/*< private >
Packit 31ecd5
 * clutter_timeline_do_tick
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @tick_time: time of advance
Packit 31ecd5
 *
Packit 31ecd5
 * Advances @timeline based on the time passed in @tick_time. This
Packit 31ecd5
 * function is called by the master clock. The @timeline will use this
Packit 31ecd5
 * interval to emit the #ClutterTimeline::new-frame signal and
Packit 31ecd5
 * eventually skip frames.
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
_clutter_timeline_do_tick (ClutterTimeline *timeline,
Packit 31ecd5
                           gint64           tick_time)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  CLUTTER_NOTE (SCHEDULER,
Packit 31ecd5
                "Timeline [%p] ticked (elapsed_time: %ld, msecs_delta: %ld, "
Packit 31ecd5
                "last_frame_time: %ld, tick_time: %ld)",
Packit 31ecd5
                timeline,
Packit 31ecd5
                (long) priv->elapsed_time,
Packit 31ecd5
                (long) priv->msecs_delta,
Packit 31ecd5
                (long) priv->last_frame_time,
Packit 31ecd5
                (long) tick_time);
Packit 31ecd5
Packit 31ecd5
  /* Check the is_playing variable before performing the timeline tick.
Packit 31ecd5
   * This is necessary, as if a timeline is stopped in response to a
Packit 31ecd5
   * master-clock generated signal of a different timeline, this code can
Packit 31ecd5
   * still be reached.
Packit 31ecd5
   */
Packit 31ecd5
  if (!priv->is_playing)
Packit 31ecd5
    return;
Packit 31ecd5
Packit 31ecd5
  if (priv->waiting_first_tick)
Packit 31ecd5
    {
Packit 31ecd5
      priv->last_frame_time = tick_time;
Packit 31ecd5
      priv->msecs_delta = 0;
Packit 31ecd5
      priv->waiting_first_tick = FALSE;
Packit 31ecd5
      clutter_timeline_do_frame (timeline);
Packit 31ecd5
    }
Packit 31ecd5
  else
Packit 31ecd5
    {
Packit 31ecd5
      gint64 msecs;
Packit 31ecd5
Packit 31ecd5
      msecs = tick_time - priv->last_frame_time;
Packit 31ecd5
Packit 31ecd5
      /* if the clock rolled back between ticks we need to
Packit 31ecd5
       * account for it; the best course of action, since the
Packit 31ecd5
       * clock roll back can happen by any arbitrary amount
Packit 31ecd5
       * of milliseconds, is to drop a frame here
Packit 31ecd5
       */
Packit 31ecd5
      if (msecs < 0)
Packit 31ecd5
        {
Packit 31ecd5
          priv->last_frame_time = tick_time;
Packit 31ecd5
          return;
Packit 31ecd5
        }
Packit 31ecd5
Packit 31ecd5
      if (msecs != 0)
Packit 31ecd5
	{
Packit 31ecd5
	  /* Avoid accumulating error */
Packit 31ecd5
          priv->last_frame_time += msecs;
Packit 31ecd5
	  priv->msecs_delta = msecs;
Packit 31ecd5
	  clutter_timeline_do_frame (timeline);
Packit 31ecd5
	}
Packit 31ecd5
    }
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_add_marker:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @marker_name: the unique name for this marker
Packit 31ecd5
 * @progress: the normalized value of the position of the martke
Packit 31ecd5
 *
Packit 31ecd5
 * Adds a named marker that will be hit when the timeline has reached
Packit 31ecd5
 * the specified @progress.
Packit 31ecd5
 *
Packit 31ecd5
 * Markers are unique string identifiers for a given position on the
Packit 31ecd5
 * timeline. Once @timeline reaches the given @progress of its duration,
Packit 31ecd5
 * if will emit a ::marker-reached signal for each marker attached to
Packit 31ecd5
 * that particular point.
Packit 31ecd5
 *
Packit 31ecd5
 * A marker can be removed with clutter_timeline_remove_marker(). The
Packit 31ecd5
 * timeline can be advanced to a marker using
Packit 31ecd5
 * clutter_timeline_advance_to_marker().
Packit 31ecd5
 *
Packit 31ecd5
 * See also: clutter_timeline_add_marker_at_time()
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 1.14
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_add_marker (ClutterTimeline *timeline,
Packit 31ecd5
                             const gchar     *marker_name,
Packit 31ecd5
                             gdouble          progress)
Packit 31ecd5
{
Packit 31ecd5
  TimelineMarker *marker;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
  g_return_if_fail (marker_name != NULL);
Packit 31ecd5
Packit 31ecd5
  marker = timeline_marker_new_progress (marker_name, progress);
Packit 31ecd5
  clutter_timeline_add_marker_internal (timeline, marker);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_add_marker_at_time:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @marker_name: the unique name for this marker
Packit 31ecd5
 * @msecs: position of the marker in milliseconds
Packit 31ecd5
 *
Packit 31ecd5
 * Adds a named marker that will be hit when the timeline has been
Packit 31ecd5
 * running for @msecs milliseconds.
Packit 31ecd5
 *
Packit 31ecd5
 * Markers are unique string identifiers for a given position on the
Packit 31ecd5
 * timeline. Once @timeline reaches the given @msecs, it will emit
Packit 31ecd5
 * a ::marker-reached signal for each marker attached to that position.
Packit 31ecd5
 *
Packit 31ecd5
 * A marker can be removed with clutter_timeline_remove_marker(). The
Packit 31ecd5
 * timeline can be advanced to a marker using
Packit 31ecd5
 * clutter_timeline_advance_to_marker().
Packit 31ecd5
 *
Packit 31ecd5
 * See also: clutter_timeline_add_marker()
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.8
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_add_marker_at_time (ClutterTimeline *timeline,
Packit 31ecd5
                                     const gchar     *marker_name,
Packit 31ecd5
                                     guint            msecs)
Packit 31ecd5
{
Packit 31ecd5
  TimelineMarker *marker;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
  g_return_if_fail (marker_name != NULL);
Packit 31ecd5
  g_return_if_fail (msecs <= clutter_timeline_get_duration (timeline));
Packit 31ecd5
Packit 31ecd5
  marker = timeline_marker_new_time (marker_name, msecs);
Packit 31ecd5
  clutter_timeline_add_marker_internal (timeline, marker);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
struct CollectMarkersClosure
Packit 31ecd5
{
Packit 31ecd5
  guint duration;
Packit 31ecd5
  guint msecs;
Packit 31ecd5
  GArray *markers;
Packit 31ecd5
};
Packit 31ecd5
Packit 31ecd5
static void
Packit 31ecd5
collect_markers (const gchar *key,
Packit 31ecd5
                 TimelineMarker *marker,
Packit 31ecd5
                 struct CollectMarkersClosure *data)
Packit 31ecd5
{
Packit 31ecd5
  guint msecs;
Packit 31ecd5
Packit 31ecd5
  if (marker->is_relative)
Packit 31ecd5
    msecs = marker->data.progress * data->duration;
Packit 31ecd5
  else
Packit 31ecd5
    msecs = marker->data.msecs;
Packit 31ecd5
Packit 31ecd5
  if (msecs == data->msecs)
Packit 31ecd5
    {
Packit 31ecd5
      gchar *name_copy = g_strdup (key);
Packit 31ecd5
      g_array_append_val (data->markers, name_copy);
Packit 31ecd5
    }
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_list_markers:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @msecs: the time to check, or -1
Packit 31ecd5
 * @n_markers: the number of markers returned
Packit 31ecd5
 *
Packit 31ecd5
 * Retrieves the list of markers at time @msecs. If @msecs is a
Packit 31ecd5
 * negative integer, all the markers attached to @timeline will be
Packit 31ecd5
 * returned.
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: (transfer full) (array zero-terminated=1 length=n_markers):
Packit 31ecd5
 *   a newly allocated, %NULL terminated string array containing the names
Packit 31ecd5
 *   of the markers. Use g_strfreev() when done.
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.8
Packit 31ecd5
 */
Packit 31ecd5
gchar **
Packit 31ecd5
clutter_timeline_list_markers (ClutterTimeline *timeline,
Packit 31ecd5
                               gint             msecs,
Packit 31ecd5
                               gsize           *n_markers)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
  gchar **retval = NULL;
Packit 31ecd5
  gsize i;
Packit 31ecd5
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (G_UNLIKELY (priv->markers_by_name == NULL))
Packit 31ecd5
    {
Packit 31ecd5
      if (n_markers)
Packit 31ecd5
        *n_markers = 0;
Packit 31ecd5
Packit 31ecd5
      return NULL;
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  if (msecs < 0)
Packit 31ecd5
    {
Packit 31ecd5
      GList *markers, *l;
Packit 31ecd5
Packit 31ecd5
      markers = g_hash_table_get_keys (priv->markers_by_name);
Packit 31ecd5
      retval = g_new0 (gchar*, g_list_length (markers) + 1);
Packit 31ecd5
Packit 31ecd5
      for (i = 0, l = markers; l != NULL; i++, l = l->next)
Packit 31ecd5
        retval[i] = g_strdup (l->data);
Packit 31ecd5
Packit 31ecd5
      g_list_free (markers);
Packit 31ecd5
    }
Packit 31ecd5
  else
Packit 31ecd5
    {
Packit 31ecd5
      struct CollectMarkersClosure data;
Packit 31ecd5
Packit 31ecd5
      data.duration = priv->duration;
Packit 31ecd5
      data.msecs = msecs;
Packit 31ecd5
      data.markers = g_array_new (TRUE, FALSE, sizeof (gchar *));
Packit 31ecd5
Packit 31ecd5
      g_hash_table_foreach (priv->markers_by_name,
Packit 31ecd5
                            (GHFunc) collect_markers,
Packit 31ecd5
                            &data);
Packit 31ecd5
Packit 31ecd5
      i = data.markers->len;
Packit 31ecd5
      retval = (gchar **) (void *) g_array_free (data.markers, FALSE);
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  if (n_markers)
Packit 31ecd5
    *n_markers = i;
Packit 31ecd5
Packit 31ecd5
  return retval;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_advance_to_marker:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @marker_name: the name of the marker
Packit 31ecd5
 *
Packit 31ecd5
 * Advances @timeline to the time of the given @marker_name.
Packit 31ecd5
 *
Packit 31ecd5
 * Like clutter_timeline_advance(), this function will not
Packit 31ecd5
 * emit the #ClutterTimeline::new-frame for the time where @marker_name
Packit 31ecd5
 * is set, nor it will emit #ClutterTimeline::marker-reached for
Packit 31ecd5
 * @marker_name.
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.8
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_advance_to_marker (ClutterTimeline *timeline,
Packit 31ecd5
                                    const gchar     *marker_name)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
  TimelineMarker *marker;
Packit 31ecd5
  guint msecs;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
  g_return_if_fail (marker_name != NULL);
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (G_UNLIKELY (priv->markers_by_name == NULL))
Packit 31ecd5
    {
Packit 31ecd5
      g_warning ("No marker named '%s' found.", marker_name);
Packit 31ecd5
      return;
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  marker = g_hash_table_lookup (priv->markers_by_name, marker_name);
Packit 31ecd5
  if (marker == NULL)
Packit 31ecd5
    {
Packit 31ecd5
      g_warning ("No marker named '%s' found.", marker_name);
Packit 31ecd5
      return;
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  if (marker->is_relative)
Packit 31ecd5
    msecs = marker->data.progress * priv->duration;
Packit 31ecd5
  else
Packit 31ecd5
    msecs = marker->data.msecs;
Packit 31ecd5
Packit 31ecd5
  clutter_timeline_advance (timeline, msecs);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_remove_marker:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @marker_name: the name of the marker to remove
Packit 31ecd5
 *
Packit 31ecd5
 * Removes @marker_name, if found, from @timeline.
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.8
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_remove_marker (ClutterTimeline *timeline,
Packit 31ecd5
                                const gchar     *marker_name)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
  TimelineMarker *marker;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
  g_return_if_fail (marker_name != NULL);
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (G_UNLIKELY (priv->markers_by_name == NULL))
Packit 31ecd5
    {
Packit 31ecd5
      g_warning ("No marker named '%s' found.", marker_name);
Packit 31ecd5
      return;
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  marker = g_hash_table_lookup (priv->markers_by_name, marker_name);
Packit 31ecd5
  if (!marker)
Packit 31ecd5
    {
Packit 31ecd5
      g_warning ("No marker named '%s' found.", marker_name);
Packit 31ecd5
      return;
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  /* this will take care of freeing the marker as well */
Packit 31ecd5
  g_hash_table_remove (priv->markers_by_name, marker_name);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_has_marker:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @marker_name: the name of the marker
Packit 31ecd5
 *
Packit 31ecd5
 * Checks whether @timeline has a marker set with the given name.
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: %TRUE if the marker was found
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 0.8
Packit 31ecd5
 */
Packit 31ecd5
gboolean
Packit 31ecd5
clutter_timeline_has_marker (ClutterTimeline *timeline,
Packit 31ecd5
                             const gchar     *marker_name)
Packit 31ecd5
{
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
Packit 31ecd5
  g_return_val_if_fail (marker_name != NULL, FALSE);
Packit 31ecd5
Packit 31ecd5
  if (G_UNLIKELY (timeline->priv->markers_by_name == NULL))
Packit 31ecd5
    return FALSE;
Packit 31ecd5
Packit 31ecd5
  return NULL != g_hash_table_lookup (timeline->priv->markers_by_name,
Packit 31ecd5
                                      marker_name);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_set_auto_reverse:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @reverse: %TRUE if the @timeline should reverse the direction
Packit 31ecd5
 *
Packit 31ecd5
 * Sets whether @timeline should reverse the direction after the
Packit 31ecd5
 * emission of the #ClutterTimeline::completed signal.
Packit 31ecd5
 *
Packit 31ecd5
 * Setting the #ClutterTimeline:auto-reverse property to %TRUE is the
Packit 31ecd5
 * equivalent of connecting a callback to the #ClutterTimeline::completed
Packit 31ecd5
 * signal and changing the direction of the timeline from that callback;
Packit 31ecd5
 * for instance, this code:
Packit 31ecd5
 *
Packit 31ecd5
 * |[
Packit 31ecd5
 * static void
Packit 31ecd5
 * reverse_timeline (ClutterTimeline *timeline)
Packit 31ecd5
 * {
Packit 31ecd5
 *   ClutterTimelineDirection dir = clutter_timeline_get_direction (timeline);
Packit 31ecd5
 *
Packit 31ecd5
 *   if (dir == CLUTTER_TIMELINE_FORWARD)
Packit 31ecd5
 *     dir = CLUTTER_TIMELINE_BACKWARD;
Packit 31ecd5
 *   else
Packit 31ecd5
 *     dir = CLUTTER_TIMELINE_FORWARD;
Packit 31ecd5
 *
Packit 31ecd5
 *   clutter_timeline_set_direction (timeline, dir);
Packit 31ecd5
 * }
Packit 31ecd5
 * ...
Packit 31ecd5
 *   timeline = clutter_timeline_new (1000);
Packit 31ecd5
 *   clutter_timeline_set_repeat_count (timeline, -1);
Packit 31ecd5
 *   g_signal_connect (timeline, "completed",
Packit 31ecd5
 *                     G_CALLBACK (reverse_timeline),
Packit 31ecd5
 *                     NULL);
Packit 31ecd5
 * ]|
Packit 31ecd5
 *
Packit 31ecd5
 * can be effectively replaced by:
Packit 31ecd5
 *
Packit 31ecd5
 * |[
Packit 31ecd5
 *   timeline = clutter_timeline_new (1000);
Packit 31ecd5
 *   clutter_timeline_set_repeat_count (timeline, -1);
Packit 31ecd5
 *   clutter_timeline_set_auto_reverse (timeline);
Packit 31ecd5
 * ]|
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 1.6
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_set_auto_reverse (ClutterTimeline *timeline,
Packit 31ecd5
                                   gboolean         reverse)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
Packit 31ecd5
  reverse = !!reverse;
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (priv->auto_reverse != reverse)
Packit 31ecd5
    {
Packit 31ecd5
      priv->auto_reverse = reverse;
Packit 31ecd5
Packit 31ecd5
      g_object_notify_by_pspec (G_OBJECT (timeline),
Packit 31ecd5
                                obj_props[PROP_AUTO_REVERSE]);
Packit 31ecd5
    }
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_get_auto_reverse:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Retrieves the value set by clutter_timeline_set_auto_reverse().
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: %TRUE if the timeline should automatically reverse, and
Packit 31ecd5
 *   %FALSE otherwise
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 1.6
Packit 31ecd5
 */
Packit 31ecd5
gboolean
Packit 31ecd5
clutter_timeline_get_auto_reverse (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
Packit 31ecd5
Packit 31ecd5
  return timeline->priv->auto_reverse;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_set_repeat_count:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @count: the number of times the timeline should repeat
Packit 31ecd5
 *
Packit 31ecd5
 * Sets the number of times the @timeline should repeat.
Packit 31ecd5
 *
Packit 31ecd5
 * If @count is 0, the timeline never repeats.
Packit 31ecd5
 *
Packit 31ecd5
 * If @count is -1, the timeline will always repeat until
Packit 31ecd5
 * it's stopped.
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 1.10
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_set_repeat_count (ClutterTimeline *timeline,
Packit 31ecd5
                                   gint             count)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
  g_return_if_fail (count >= -1);
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (priv->repeat_count != count)
Packit 31ecd5
    {
Packit 31ecd5
      priv->repeat_count = count;
Packit 31ecd5
Packit 31ecd5
      g_object_notify_by_pspec (G_OBJECT (timeline),
Packit 31ecd5
                                obj_props[PROP_REPEAT_COUNT]);
Packit 31ecd5
    }
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_get_repeat_count:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Retrieves the number set using clutter_timeline_set_repeat_count().
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: the number of repeats
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 1.10
Packit 31ecd5
 */
Packit 31ecd5
gint
Packit 31ecd5
clutter_timeline_get_repeat_count (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
Packit 31ecd5
Packit 31ecd5
  return timeline->priv->repeat_count;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_set_progress_func:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @func: (scope notified) (allow-none): a progress function, or %NULL
Packit 31ecd5
 * @data: (closure): data to pass to @func
Packit 31ecd5
 * @notify: a function to be called when the progress function is removed
Packit 31ecd5
 *    or the timeline is disposed
Packit 31ecd5
 *
Packit 31ecd5
 * Sets a custom progress function for @timeline. The progress function will
Packit 31ecd5
 * be called by clutter_timeline_get_progress() and will be used to compute
Packit 31ecd5
 * the progress value based on the elapsed time and the total duration of the
Packit 31ecd5
 * timeline.
Packit 31ecd5
 *
Packit 31ecd5
 * If @func is not %NULL, the #ClutterTimeline:progress-mode property will
Packit 31ecd5
 * be set to %CLUTTER_CUSTOM_MODE.
Packit 31ecd5
 *
Packit 31ecd5
 * If @func is %NULL, any previously set progress function will be unset, and
Packit 31ecd5
 * the #ClutterTimeline:progress-mode property will be set to %CLUTTER_LINEAR.
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 1.10
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_set_progress_func (ClutterTimeline             *timeline,
Packit 31ecd5
                                    ClutterTimelineProgressFunc  func,
Packit 31ecd5
                                    gpointer                     data,
Packit 31ecd5
                                    GDestroyNotify               notify)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (priv->progress_notify != NULL)
Packit 31ecd5
    priv->progress_notify (priv->progress_data);
Packit 31ecd5
Packit 31ecd5
  priv->progress_func = func;
Packit 31ecd5
  priv->progress_data = data;
Packit 31ecd5
  priv->progress_notify = notify;
Packit 31ecd5
Packit 31ecd5
  if (priv->progress_func != NULL)
Packit 31ecd5
    priv->progress_mode = CLUTTER_CUSTOM_MODE;
Packit 31ecd5
  else
Packit 31ecd5
    priv->progress_mode = CLUTTER_LINEAR;
Packit 31ecd5
Packit 31ecd5
  g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_PROGRESS_MODE]);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
static gdouble
Packit 31ecd5
clutter_timeline_progress_func (ClutterTimeline *timeline,
Packit 31ecd5
                                gdouble          elapsed,
Packit 31ecd5
                                gdouble          duration,
Packit 31ecd5
                                gpointer         user_data G_GNUC_UNUSED)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  /* parametrized easing functions need to be handled separately */
Packit 31ecd5
  switch (priv->progress_mode)
Packit 31ecd5
    {
Packit 31ecd5
    case CLUTTER_STEPS:
Packit 31ecd5
      if (priv->step_mode == CLUTTER_STEP_MODE_START)
Packit 31ecd5
        return clutter_ease_steps_start (elapsed, duration, priv->n_steps);
Packit 31ecd5
      else if (priv->step_mode == CLUTTER_STEP_MODE_END)
Packit 31ecd5
        return clutter_ease_steps_end (elapsed, duration, priv->n_steps);
Packit 31ecd5
      else
Packit 31ecd5
        g_assert_not_reached ();
Packit 31ecd5
      break;
Packit 31ecd5
Packit 31ecd5
    case CLUTTER_STEP_START:
Packit 31ecd5
      return clutter_ease_steps_start (elapsed, duration, 1);
Packit 31ecd5
Packit 31ecd5
    case CLUTTER_STEP_END:
Packit 31ecd5
      return clutter_ease_steps_end (elapsed, duration, 1);
Packit 31ecd5
Packit 31ecd5
    case CLUTTER_CUBIC_BEZIER:
Packit 31ecd5
      return clutter_ease_cubic_bezier (elapsed, duration,
Packit 31ecd5
                                        priv->cb_1.x, priv->cb_1.y,
Packit 31ecd5
                                        priv->cb_2.x, priv->cb_2.y);
Packit 31ecd5
Packit 31ecd5
    case CLUTTER_EASE:
Packit 31ecd5
      return clutter_ease_cubic_bezier (elapsed, duration,
Packit 31ecd5
                                        0.25, 0.1, 0.25, 1.0);
Packit 31ecd5
Packit 31ecd5
    case CLUTTER_EASE_IN:
Packit 31ecd5
      return clutter_ease_cubic_bezier (elapsed, duration,
Packit 31ecd5
                                        0.42, 0.0, 1.0, 1.0);
Packit 31ecd5
Packit 31ecd5
    case CLUTTER_EASE_OUT:
Packit 31ecd5
      return clutter_ease_cubic_bezier (elapsed, duration,
Packit 31ecd5
                                        0.0, 0.0, 0.58, 1.0);
Packit 31ecd5
Packit 31ecd5
    case CLUTTER_EASE_IN_OUT:
Packit 31ecd5
      return clutter_ease_cubic_bezier (elapsed, duration,
Packit 31ecd5
                                        0.42, 0.0, 0.58, 1.0);
Packit 31ecd5
Packit 31ecd5
    default:
Packit 31ecd5
      break;
Packit 31ecd5
    }
Packit 31ecd5
Packit 31ecd5
  return clutter_easing_for_mode (priv->progress_mode, elapsed, duration);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_set_progress_mode:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @mode: the progress mode, as a #ClutterAnimationMode
Packit 31ecd5
 *
Packit 31ecd5
 * Sets the progress function using a value from the #ClutterAnimationMode
Packit 31ecd5
 * enumeration. The @mode cannot be %CLUTTER_CUSTOM_MODE or bigger than
Packit 31ecd5
 * %CLUTTER_ANIMATION_LAST.
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 1.10
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_set_progress_mode (ClutterTimeline      *timeline,
Packit 31ecd5
                                    ClutterAnimationMode  mode)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
  g_return_if_fail (mode < CLUTTER_ANIMATION_LAST);
Packit 31ecd5
  g_return_if_fail (mode != CLUTTER_CUSTOM_MODE);
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (priv->progress_mode == mode)
Packit 31ecd5
    return;
Packit 31ecd5
Packit 31ecd5
  if (priv->progress_notify != NULL)
Packit 31ecd5
    priv->progress_notify (priv->progress_data);
Packit 31ecd5
Packit 31ecd5
  priv->progress_mode = mode;
Packit 31ecd5
Packit 31ecd5
  /* short-circuit linear progress */
Packit 31ecd5
  if (priv->progress_mode != CLUTTER_LINEAR)
Packit 31ecd5
    priv->progress_func = clutter_timeline_progress_func;
Packit 31ecd5
  else
Packit 31ecd5
    priv->progress_func = NULL;
Packit 31ecd5
Packit 31ecd5
  priv->progress_data = NULL;
Packit 31ecd5
  priv->progress_notify = NULL;
Packit 31ecd5
Packit 31ecd5
  g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_PROGRESS_MODE]);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_get_progress_mode:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Retrieves the progress mode set using clutter_timeline_set_progress_mode()
Packit 31ecd5
 * or clutter_timeline_set_progress_func().
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: a #ClutterAnimationMode
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 1.10
Packit 31ecd5
 */
Packit 31ecd5
ClutterAnimationMode
Packit 31ecd5
clutter_timeline_get_progress_mode (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), CLUTTER_LINEAR);
Packit 31ecd5
Packit 31ecd5
  return timeline->priv->progress_mode;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_get_duration_hint:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Retrieves the full duration of the @timeline, taking into account the
Packit 31ecd5
 * current value of the #ClutterTimeline:repeat-count property.
Packit 31ecd5
 *
Packit 31ecd5
 * If the #ClutterTimeline:repeat-count property is set to -1, this function
Packit 31ecd5
 * will return %G_MAXINT64.
Packit 31ecd5
 *
Packit 31ecd5
 * The returned value is to be considered a hint, and it's only valid
Packit 31ecd5
 * as long as the @timeline hasn't been changed.
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: the full duration of the #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 1.10
Packit 31ecd5
 */
Packit 31ecd5
gint64
Packit 31ecd5
clutter_timeline_get_duration_hint (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (priv->repeat_count == 0)
Packit 31ecd5
    return priv->duration;
Packit 31ecd5
  else if (priv->repeat_count < 0)
Packit 31ecd5
    return G_MAXINT64;
Packit 31ecd5
  else
Packit 31ecd5
    return priv->repeat_count * priv->duration;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_get_current_repeat:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 *
Packit 31ecd5
 * Retrieves the current repeat for a timeline.
Packit 31ecd5
 *
Packit 31ecd5
 * Repeats start at 0.
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: the current repeat
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 1.10
Packit 31ecd5
 */
Packit 31ecd5
gint
Packit 31ecd5
clutter_timeline_get_current_repeat (ClutterTimeline *timeline)
Packit 31ecd5
{
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
Packit 31ecd5
Packit 31ecd5
  return timeline->priv->current_repeat;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_set_step_progress:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @n_steps: the number of steps
Packit 31ecd5
 * @step_mode: whether the change should happen at the start
Packit 31ecd5
 *   or at the end of the step
Packit 31ecd5
 *
Packit 31ecd5
 * Sets the #ClutterTimeline:progress-mode of the @timeline to %CLUTTER_STEPS
Packit 31ecd5
 * and provides the parameters of the step function.
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 1.12
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_set_step_progress (ClutterTimeline *timeline,
Packit 31ecd5
                                    gint             n_steps,
Packit 31ecd5
                                    ClutterStepMode  step_mode)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
  g_return_if_fail (n_steps > 0);
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  if (priv->progress_mode == CLUTTER_STEPS &&
Packit 31ecd5
      priv->n_steps == n_steps &&
Packit 31ecd5
      priv->step_mode == step_mode)
Packit 31ecd5
    return;
Packit 31ecd5
Packit 31ecd5
  priv->n_steps = n_steps;
Packit 31ecd5
  priv->step_mode = step_mode;
Packit 31ecd5
  clutter_timeline_set_progress_mode (timeline, CLUTTER_STEPS);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_get_step_progress:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @n_steps: (out): return location for the number of steps, or %NULL
Packit 31ecd5
 * @step_mode: (out): return location for the value change policy,
Packit 31ecd5
 *   or %NULL
Packit 31ecd5
 *
Packit 31ecd5
 * Retrieves the parameters of the step progress mode used by @timeline.
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: %TRUE if the @timeline is using a step progress
Packit 31ecd5
 *   mode, and %FALSE otherwise
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 1.12
Packit 31ecd5
 */
Packit 31ecd5
gboolean
Packit 31ecd5
clutter_timeline_get_step_progress (ClutterTimeline *timeline,
Packit 31ecd5
                                    gint            *n_steps,
Packit 31ecd5
                                    ClutterStepMode *step_mode)
Packit 31ecd5
{
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
Packit 31ecd5
Packit 31ecd5
  if (!(timeline->priv->progress_mode == CLUTTER_STEPS ||
Packit 31ecd5
        timeline->priv->progress_mode == CLUTTER_STEP_START ||
Packit 31ecd5
        timeline->priv->progress_mode == CLUTTER_STEP_END))
Packit 31ecd5
    return FALSE;
Packit 31ecd5
Packit 31ecd5
  if (n_steps != NULL)
Packit 31ecd5
    *n_steps = timeline->priv->n_steps;
Packit 31ecd5
Packit 31ecd5
  if (step_mode != NULL)
Packit 31ecd5
    *step_mode = timeline->priv->step_mode;
Packit 31ecd5
Packit 31ecd5
  return TRUE;
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_set_cubic_bezier_progress:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @c_1: the first control point for the cubic bezier
Packit 31ecd5
 * @c_2: the second control point for the cubic bezier
Packit 31ecd5
 *
Packit 31ecd5
 * Sets the #ClutterTimeline:progress-mode of @timeline
Packit 31ecd5
 * to %CLUTTER_CUBIC_BEZIER, and sets the two control
Packit 31ecd5
 * points for the cubic bezier.
Packit 31ecd5
 *
Packit 31ecd5
 * The cubic bezier curve is between (0, 0) and (1, 1). The X coordinate
Packit 31ecd5
 * of the two control points must be in the [ 0, 1 ] range, while the
Packit 31ecd5
 * Y coordinate of the two control points can exceed this range.
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 1.12
Packit 31ecd5
 */
Packit 31ecd5
void
Packit 31ecd5
clutter_timeline_set_cubic_bezier_progress (ClutterTimeline    *timeline,
Packit 31ecd5
                                            const ClutterPoint *c_1,
Packit 31ecd5
                                            const ClutterPoint *c_2)
Packit 31ecd5
{
Packit 31ecd5
  ClutterTimelinePrivate *priv;
Packit 31ecd5
Packit 31ecd5
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
Packit 31ecd5
  g_return_if_fail (c_1 != NULL && c_2 != NULL);
Packit 31ecd5
Packit 31ecd5
  priv = timeline->priv;
Packit 31ecd5
Packit 31ecd5
  priv->cb_1 = *c_1;
Packit 31ecd5
  priv->cb_2 = *c_2;
Packit 31ecd5
Packit 31ecd5
  /* ensure the range on the X coordinate */
Packit 31ecd5
  priv->cb_1.x = CLAMP (priv->cb_1.x, 0.f, 1.f);
Packit 31ecd5
  priv->cb_2.x = CLAMP (priv->cb_2.x, 0.f, 1.f);
Packit 31ecd5
Packit 31ecd5
  clutter_timeline_set_progress_mode (timeline, CLUTTER_CUBIC_BEZIER);
Packit 31ecd5
}
Packit 31ecd5
Packit 31ecd5
/**
Packit 31ecd5
 * clutter_timeline_get_cubic_bezier_progress:
Packit 31ecd5
 * @timeline: a #ClutterTimeline
Packit 31ecd5
 * @c_1: (out caller-allocates): return location for the first control
Packit 31ecd5
 *   point of the cubic bezier, or %NULL
Packit 31ecd5
 * @c_2: (out caller-allocates): return location for the second control
Packit 31ecd5
 *   point of the cubic bezier, or %NULL
Packit 31ecd5
 *
Packit 31ecd5
 * Retrieves the control points for the cubic bezier progress mode.
Packit 31ecd5
 *
Packit 31ecd5
 * Return value: %TRUE if the @timeline is using a cubic bezier progress
Packit 31ecd5
 *   more, and %FALSE otherwise
Packit 31ecd5
 *
Packit 31ecd5
 * Since: 1.12
Packit 31ecd5
 */
Packit 31ecd5
gboolean
Packit 31ecd5
clutter_timeline_get_cubic_bezier_progress (ClutterTimeline *timeline,
Packit 31ecd5
                                            ClutterPoint    *c_1,
Packit 31ecd5
                                            ClutterPoint    *c_2)
Packit 31ecd5
{
Packit 31ecd5
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
Packit 31ecd5
Packit 31ecd5
  if (!(timeline->priv->progress_mode == CLUTTER_CUBIC_BEZIER ||
Packit 31ecd5
        timeline->priv->progress_mode == CLUTTER_EASE ||
Packit 31ecd5
        timeline->priv->progress_mode == CLUTTER_EASE_IN ||
Packit 31ecd5
        timeline->priv->progress_mode == CLUTTER_EASE_OUT ||
Packit 31ecd5
        timeline->priv->progress_mode == CLUTTER_EASE_IN_OUT))
Packit 31ecd5
    return FALSE;
Packit 31ecd5
Packit 31ecd5
  if (c_1 != NULL)
Packit 31ecd5
    *c_1 = timeline->priv->cb_1;
Packit 31ecd5
Packit 31ecd5
  if (c_2 != NULL)
Packit 31ecd5
    *c_2 = timeline->priv->cb_2;
Packit 31ecd5
Packit 31ecd5
  return TRUE;
Packit 31ecd5
}