2006-05-29 04:59:36 -04:00
|
|
|
/*
|
|
|
|
* Clutter.
|
|
|
|
*
|
|
|
|
* An OpenGL based 'interactive canvas' library.
|
|
|
|
*
|
|
|
|
* Authored By Matthew Allum <mallum@openedhand.com>
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006 OpenedHand
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2010-03-01 07:56:10 -05:00
|
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
2006-05-29 04:59:36 -04:00
|
|
|
*/
|
|
|
|
|
2006-06-21 18:34:25 -04:00
|
|
|
/**
|
|
|
|
* SECTION:clutter-timeline
|
2008-06-12 07:43:25 -04:00
|
|
|
* @short_description: A class for time-based events
|
2007-06-21 10:19:56 -04:00
|
|
|
*
|
2011-11-27 07:16:32 -05:00
|
|
|
* #ClutterTimeline is a base class for managing time-based event that cause
|
|
|
|
* Clutter to redraw a stage, such as animations.
|
|
|
|
*
|
|
|
|
* Each #ClutterTimeline instance has a duration: once a timeline has been
|
|
|
|
* started, using clutter_timeline_start(), it will emit a signal that can
|
|
|
|
* be used to update the state of the actors.
|
|
|
|
*
|
|
|
|
* It is important to note that #ClutterTimeline is not a generic API for
|
|
|
|
* calling closures after an interval; each Timeline is tied into the master
|
|
|
|
* clock used to drive the frame cycle. If you need to schedule a closure
|
|
|
|
* after an interval, see clutter_threads_add_timeout() instead.
|
|
|
|
*
|
|
|
|
* Users of #ClutterTimeline should connect to the #ClutterTimeline::new-frame
|
|
|
|
* signal, which is emitted each time a timeline is advanced during the maste
|
|
|
|
* clock iteration. The #ClutterTimeline::new-frame signal provides the time
|
|
|
|
* elapsed since the beginning of the timeline, in milliseconds. A normalized
|
|
|
|
* progress value can be obtained by calling clutter_timeline_get_progress().
|
|
|
|
* By using clutter_timeline_get_delta() it is possible to obtain the wallclock
|
|
|
|
* time elapsed since the last emission of the #ClutterTimeline::new-frame
|
|
|
|
* signal.
|
|
|
|
*
|
|
|
|
* Initial state can be set up by using the #ClutterTimeline::started signal,
|
2012-06-13 04:09:56 -04:00
|
|
|
* while final state can be set up by using the #ClutterTimeline::stopped
|
2011-11-27 07:16:32 -05:00
|
|
|
* signal. The #ClutterTimeline guarantees the emission of at least a single
|
|
|
|
* #ClutterTimeline::new-frame signal, as well as the emission of the
|
2012-06-13 04:09:56 -04:00
|
|
|
* #ClutterTimeline::completed signal every time the #ClutterTimeline reaches
|
|
|
|
* its #ClutterTimeline:duration.
|
2011-11-27 07:16:32 -05:00
|
|
|
*
|
|
|
|
* It is possible to connect to specific points in the timeline progress by
|
2014-03-17 19:07:58 -04:00
|
|
|
* adding markers using clutter_timeline_add_marker_at_time() and connecting
|
|
|
|
* to the #ClutterTimeline::marker-reached signal.
|
2011-11-27 07:16:32 -05:00
|
|
|
*
|
2012-02-13 09:45:06 -05:00
|
|
|
* Timelines can be made to loop once they reach the end of their duration, by
|
|
|
|
* using clutter_timeline_set_repeat_count(); a looping timeline will still
|
|
|
|
* emit the #ClutterTimeline::completed signal once it reaches the end of its
|
2012-06-13 04:09:56 -04:00
|
|
|
* duration at each repeat. If you want to be notified of the end of the last
|
|
|
|
* repeat, use the #ClutterTimeline::stopped signal.
|
2011-11-27 07:16:32 -05:00
|
|
|
*
|
|
|
|
* Timelines have a #ClutterTimeline:direction: the default direction is
|
|
|
|
* %CLUTTER_TIMELINE_FORWARD, and goes from 0 to the duration; it is possible
|
|
|
|
* to change the direction to %CLUTTER_TIMELINE_BACKWARD, and have the timeline
|
|
|
|
* go from the duration to 0. The direction can be automatically reversed
|
|
|
|
* when reaching completion by using the #ClutterTimeline:auto-reverse property.
|
|
|
|
*
|
|
|
|
* Timelines are used in the Clutter animation framework by classes like
|
2020-04-09 11:54:45 -04:00
|
|
|
* #ClutterTransition.
|
2011-11-27 07:16:32 -05:00
|
|
|
*
|
2014-03-17 19:07:58 -04:00
|
|
|
* ## Defining Timelines in ClutterScript
|
|
|
|
*
|
|
|
|
* A #ClutterTimeline can be described in #ClutterScript like any
|
|
|
|
* other object. Additionally, it is possible to define markers directly
|
|
|
|
* inside the JSON definition by using the `markers` JSON object member,
|
|
|
|
* such as:
|
|
|
|
*
|
|
|
|
* |[
|
2011-11-27 07:16:32 -05:00
|
|
|
{
|
|
|
|
"type" : "ClutterTimeline",
|
|
|
|
"duration" : 1000,
|
|
|
|
"markers" : [
|
|
|
|
{ "name" : "quarter", "time" : 250 },
|
|
|
|
{ "name" : "half-time", "time" : 500 },
|
|
|
|
{ "name" : "three-quarters", "time" : 750 }
|
|
|
|
]
|
|
|
|
}
|
2014-03-17 19:07:58 -04:00
|
|
|
* ]|
|
2006-06-21 18:34:25 -04:00
|
|
|
*/
|
|
|
|
|
2016-05-05 10:21:51 -04:00
|
|
|
#include "clutter-build-config.h"
|
2006-06-21 18:34:25 -04:00
|
|
|
|
2012-03-07 07:34:06 -05:00
|
|
|
#include "clutter-timeline.h"
|
2020-04-17 03:03:44 -04:00
|
|
|
#include "deprecated/clutter-timeline.h"
|
2012-03-07 07:34:06 -05:00
|
|
|
|
2009-03-21 16:39:32 -04:00
|
|
|
#include "clutter-debug.h"
|
2012-02-17 11:06:28 -05:00
|
|
|
#include "clutter-easing.h"
|
2009-03-21 16:39:32 -04:00
|
|
|
#include "clutter-enum-types.h"
|
2020-03-25 13:16:39 -04:00
|
|
|
#include "clutter-frame-clock.h"
|
2006-05-29 04:59:36 -04:00
|
|
|
#include "clutter-main.h"
|
2006-06-22 08:05:51 -04:00
|
|
|
#include "clutter-marshal.h"
|
2006-11-21 Emmanuele Bassi <ebassi@openedhand.com>
* configure.ac: Enable debug messages also when
--enable-debug is set to "minimum".
* clutter/Makefile.am:
* clutter/clutter-debug.h: Move all debugging macros inside
this private header; make all debug macros depend on the
CLUTTER_ENABLE_DEBUG compile time define, controlled by
the --enable-debug configure switch; add G_LOG_DOMAIN define.
* clutter/clutter-main.c: Clean up the debug stuff; add
command line argument parsing using GOption; the debug
messages now are triggered like this:
CLUTTER_DEBUG=section:section:... clutter-app
or like this:
clutter-app --clutter-debug=section:section:...
where "section" is one of the sections listed in clutter-main.c,
or "all", for all sections; each section is bound to a flag,
which can be used to define a domain when adding a debug note
using the CLUTTER_NOTE() macro; the old CLUTTER_DBG() macro is
just a wrapper around that, under the CLUTTER_DEBUG_MISC domain;
CLUTTER_NOTE() is used like this:
CLUTTER_NOTE (DOMAIN, log-function);
where log function is g_printerr(), g_message(), g_warning(),
g_critical() or directly g_log() - for instance:
CLUTTER_NOTE (PANGO, g_warning ("Cache miss: %d", glyph));
will print the warning only if the "pango" flag has been
set to the CLUTTER_DEBUG envvar or passed to the --clutter-debug
command line argument.
similar to CLUTTER_SHOW_FPS, there's also the --clutter-show-fps
command line switch; also, the --display and --screen command
line switches have been added: the first overrides the DISPLAY
envvar and the second controls the X screen used by Clutter to
get the root window on the display.
* clutter/clutter-main.h:
* clutter/clutter-main.c: Add extended support for GOption
in Clutter; use clutter_init_with_args() to let Clutter
parse your own command line arguments; use instead
clutter_get_option_group() to get the GOptionGroup used by
Clutter if you want to do the parsing yourself with
g_option_context_parse(). The init sequence has been verified,
updated and moved into common functions where possible.
* clutter/pango/pangoclutter-render.c:
* clutter/*.c: Include "clutter-debug.h" where needed; use
CLUTTER_NOTE() instead of CLUTTER_DBG().
* examples/super-oh.c: Use the new clutter_init_with_args()
function, and add a --num-hands command line switch to
the SuperOH example code controlling the number of hands at
runtime.
2006-11-21 16:27:53 -05:00
|
|
|
#include "clutter-private.h"
|
2011-11-27 07:16:32 -05:00
|
|
|
#include "clutter-scriptable.h"
|
2020-03-25 12:54:18 -04:00
|
|
|
#include "clutter-timeline-private.h"
|
2012-03-07 07:34:06 -05:00
|
|
|
|
2006-11-15 18:37:53 -05:00
|
|
|
struct _ClutterTimelinePrivate
|
2006-05-29 04:59:36 -04:00
|
|
|
{
|
2007-11-15 12:03:55 -05:00
|
|
|
ClutterTimelineDirection direction;
|
|
|
|
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
ClutterFrameClock *custom_frame_clock;
|
2020-03-25 13:16:39 -04:00
|
|
|
ClutterFrameClock *frame_clock;
|
|
|
|
|
2020-04-17 03:00:18 -04:00
|
|
|
ClutterActor *actor;
|
|
|
|
gulong actor_destroy_handler_id;
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
gulong actor_stage_views_handler_id;
|
2020-04-17 03:00:18 -04:00
|
|
|
|
2007-06-07 06:26:18 -04:00
|
|
|
guint delay_id;
|
2007-10-12 04:17:00 -04:00
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
/* The total length in milliseconds of this timeline */
|
|
|
|
guint duration;
|
2007-06-07 06:26:18 -04:00
|
|
|
guint delay;
|
2006-11-15 18:37:53 -05:00
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
/* The current amount of elapsed time */
|
2009-09-16 06:55:04 -04:00
|
|
|
gint64 elapsed_time;
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
/* The elapsed time since the last frame was fired */
|
2009-09-16 06:55:04 -04:00
|
|
|
gint64 msecs_delta;
|
2006-11-15 18:37:53 -05:00
|
|
|
|
2008-03-18 13:50:45 -04:00
|
|
|
GHashTable *markers_by_name;
|
|
|
|
|
2009-06-08 13:00:09 -04:00
|
|
|
/* Time we last advanced the elapsed time and showed a frame */
|
2010-11-07 10:57:33 -05:00
|
|
|
gint64 last_frame_time;
|
2009-06-08 13:00:09 -04:00
|
|
|
|
2012-02-13 09:45:06 -05:00
|
|
|
/* How many times the timeline should repeat */
|
|
|
|
gint repeat_count;
|
|
|
|
|
|
|
|
/* The number of times the timeline has repeated */
|
|
|
|
gint current_repeat;
|
|
|
|
|
2012-02-17 11:06:28 -05:00
|
|
|
ClutterTimelineProgressFunc progress_func;
|
|
|
|
gpointer progress_data;
|
|
|
|
GDestroyNotify progress_notify;
|
|
|
|
ClutterAnimationMode progress_mode;
|
|
|
|
|
2012-07-19 21:55:35 -04:00
|
|
|
/* step() parameters */
|
timeline: Add support for step() progress
The CSS3 Transitions specification from the W3C defines the possibility
of using a parametrized step() timing function, with the following
prototype:
steps(n_steps, [ start | end ])
where @n_steps represents the number of steps used to divide an interval
between 0 and 1; the 'start' and 'end' tokens describe whether the value
change should happen at the start of the transition, or at the end.
For instance, the "steps(3, start)" timing function has the following
profile:
1 | x
| |
| x---|
| ' |
| x---' |
| ' |
0 |---' |
Whereas the "steps(3, end)" timing function has the following profile:
1 | x---|
| ' |
| x---' |
| ' |
x---' |
| |
0 | |
Since ClutterTimeline uses an enumeration for controlling the progress
mode, we need additional API to define the parameters of the steps()
progress; for this reason, we need a CLUTTER_STEPS enumeration value,
and a method for setting the number of steps and the value transition
policy.
The CSS3 Transitions spec helpfully also defines a step-start and a
step-end shorthands, which expand to step(1, start) and step(1, end)
respectively; we can provide a CLUTTER_STEP_START and CLUTTER_STEP_END
enumeration values for those.
2012-07-19 19:47:48 -04:00
|
|
|
gint n_steps;
|
|
|
|
ClutterStepMode step_mode;
|
|
|
|
|
2012-07-19 21:55:35 -04:00
|
|
|
/* cubic-bezier() parameters */
|
2019-02-20 09:53:44 -05:00
|
|
|
graphene_point_t cb_1;
|
|
|
|
graphene_point_t cb_2;
|
2012-07-19 21:55:35 -04:00
|
|
|
|
2010-11-17 10:27:42 -05:00
|
|
|
guint is_playing : 1;
|
|
|
|
|
|
|
|
/* If we've just started playing and haven't yet gotten
|
|
|
|
* a tick from the master clock
|
|
|
|
*/
|
2009-06-08 13:00:09 -04:00
|
|
|
guint waiting_first_tick : 1;
|
2010-12-17 07:04:11 -05:00
|
|
|
guint auto_reverse : 1;
|
2006-05-29 04:59:36 -04:00
|
|
|
};
|
|
|
|
|
2008-03-18 13:50:45 -04:00
|
|
|
typedef struct {
|
|
|
|
gchar *name;
|
|
|
|
GQuark quark;
|
2013-02-20 17:14:46 -05:00
|
|
|
|
|
|
|
union {
|
|
|
|
guint msecs;
|
|
|
|
gdouble progress;
|
|
|
|
} data;
|
|
|
|
|
|
|
|
guint is_relative : 1;
|
2008-03-18 13:50:45 -04:00
|
|
|
} TimelineMarker;
|
|
|
|
|
2006-05-29 04:59:36 -04:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
PROP_0,
|
2007-10-12 04:17:00 -04:00
|
|
|
|
2020-04-17 03:00:18 -04:00
|
|
|
PROP_ACTOR,
|
2007-10-16 09:41:34 -04:00
|
|
|
PROP_DELAY,
|
2007-11-15 12:03:55 -05:00
|
|
|
PROP_DURATION,
|
2010-06-21 05:20:32 -04:00
|
|
|
PROP_DIRECTION,
|
2010-12-17 07:04:11 -05:00
|
|
|
PROP_AUTO_REVERSE,
|
2012-02-13 09:45:06 -05:00
|
|
|
PROP_REPEAT_COUNT,
|
2012-02-17 11:06:28 -05:00
|
|
|
PROP_PROGRESS_MODE,
|
2020-03-25 13:16:39 -04:00
|
|
|
PROP_FRAME_CLOCK,
|
2010-06-21 05:20:32 -04:00
|
|
|
|
|
|
|
PROP_LAST
|
2006-05-29 04:59:36 -04:00
|
|
|
};
|
|
|
|
|
2010-11-17 10:27:42 -05:00
|
|
|
static GParamSpec *obj_props[PROP_LAST] = { NULL, };
|
2010-06-21 05:20:32 -04:00
|
|
|
|
2006-05-29 04:59:36 -04:00
|
|
|
enum
|
|
|
|
{
|
2006-11-15 18:37:53 -05:00
|
|
|
NEW_FRAME,
|
|
|
|
STARTED,
|
|
|
|
PAUSED,
|
|
|
|
COMPLETED,
|
2008-03-18 13:50:45 -04:00
|
|
|
MARKER_REACHED,
|
2012-05-25 19:41:14 -04:00
|
|
|
STOPPED,
|
2006-11-15 18:37:53 -05:00
|
|
|
|
2006-05-29 04:59:36 -04:00
|
|
|
LAST_SIGNAL
|
|
|
|
};
|
|
|
|
|
2009-03-21 16:39:32 -04:00
|
|
|
static guint timeline_signals[LAST_SIGNAL] = { 0, };
|
2006-05-29 04:59:36 -04:00
|
|
|
|
2013-07-03 09:14:01 -04:00
|
|
|
static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
|
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (ClutterTimeline, clutter_timeline, G_TYPE_OBJECT,
|
|
|
|
G_ADD_PRIVATE (ClutterTimeline)
|
|
|
|
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
|
|
|
|
clutter_scriptable_iface_init))
|
|
|
|
|
2008-03-18 13:50:45 -04:00
|
|
|
static TimelineMarker *
|
2013-02-20 17:14:46 -05:00
|
|
|
timeline_marker_new_time (const gchar *name,
|
|
|
|
guint msecs)
|
2008-03-18 13:50:45 -04:00
|
|
|
{
|
2013-02-20 17:14:46 -05:00
|
|
|
TimelineMarker *marker = g_slice_new (TimelineMarker);
|
2008-03-18 13:50:45 -04:00
|
|
|
|
|
|
|
marker->name = g_strdup (name);
|
|
|
|
marker->quark = g_quark_from_string (marker->name);
|
2013-02-20 17:14:46 -05:00
|
|
|
marker->is_relative = FALSE;
|
|
|
|
marker->data.msecs = msecs;
|
|
|
|
|
|
|
|
return marker;
|
|
|
|
}
|
|
|
|
|
|
|
|
static TimelineMarker *
|
|
|
|
timeline_marker_new_progress (const gchar *name,
|
|
|
|
gdouble progress)
|
|
|
|
{
|
|
|
|
TimelineMarker *marker = g_slice_new (TimelineMarker);
|
|
|
|
|
|
|
|
marker->name = g_strdup (name);
|
|
|
|
marker->quark = g_quark_from_string (marker->name);
|
|
|
|
marker->is_relative = TRUE;
|
|
|
|
marker->data.progress = CLAMP (progress, 0.0, 1.0);
|
2008-03-18 13:50:45 -04:00
|
|
|
|
|
|
|
return marker;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
timeline_marker_free (gpointer data)
|
|
|
|
{
|
|
|
|
if (G_LIKELY (data))
|
|
|
|
{
|
|
|
|
TimelineMarker *marker = data;
|
|
|
|
|
|
|
|
g_free (marker->name);
|
|
|
|
g_slice_free (TimelineMarker, marker);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-27 07:16:32 -05:00
|
|
|
/*< private >
|
|
|
|
* clutter_timeline_add_marker_internal:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @marker: a TimelineMarker
|
|
|
|
*
|
|
|
|
* Adds @marker into the hash table of markers for @timeline.
|
|
|
|
*
|
|
|
|
* The TimelineMarker will either be added or, in case of collisions
|
|
|
|
* with another existing marker, freed. In any case, this function
|
|
|
|
* assumes the ownership of the passed @marker.
|
|
|
|
*/
|
|
|
|
static inline void
|
|
|
|
clutter_timeline_add_marker_internal (ClutterTimeline *timeline,
|
|
|
|
TimelineMarker *marker)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
TimelineMarker *old_marker;
|
|
|
|
|
|
|
|
/* create the hash table that will hold the markers */
|
|
|
|
if (G_UNLIKELY (priv->markers_by_name == NULL))
|
|
|
|
priv->markers_by_name = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
|
|
NULL,
|
|
|
|
timeline_marker_free);
|
|
|
|
|
|
|
|
old_marker = g_hash_table_lookup (priv->markers_by_name, marker->name);
|
|
|
|
if (old_marker != NULL)
|
|
|
|
{
|
2013-02-20 17:14:46 -05:00
|
|
|
guint msecs;
|
|
|
|
|
|
|
|
if (old_marker->is_relative)
|
|
|
|
msecs = old_marker->data.progress * priv->duration;
|
|
|
|
else
|
|
|
|
msecs = old_marker->data.msecs;
|
|
|
|
|
2011-11-27 07:16:32 -05:00
|
|
|
g_warning ("A marker named '%s' already exists at time %d",
|
|
|
|
old_marker->name,
|
2013-02-20 17:14:46 -05:00
|
|
|
msecs);
|
2011-11-27 07:16:32 -05:00
|
|
|
timeline_marker_free (marker);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_hash_table_insert (priv->markers_by_name, marker->name, marker);
|
|
|
|
}
|
|
|
|
|
2020-04-17 03:00:18 -04:00
|
|
|
static void
|
|
|
|
on_actor_destroyed (ClutterActor *actor,
|
|
|
|
ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
|
|
|
|
priv->actor = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_get_actor:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
*
|
|
|
|
* Get the actor the timeline is associated with.
|
|
|
|
*
|
|
|
|
* Returns: (transfer none): the associated #ClutterActor
|
|
|
|
*/
|
|
|
|
ClutterActor *
|
|
|
|
clutter_timeline_get_actor (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
|
|
|
|
return priv->actor;
|
|
|
|
}
|
|
|
|
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
static void
|
|
|
|
maybe_add_timeline (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
|
|
|
|
if (!priv->frame_clock)
|
|
|
|
return;
|
|
|
|
|
|
|
|
clutter_frame_clock_add_timeline (priv->frame_clock, timeline);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
maybe_remove_timeline (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
|
|
|
|
if (!priv->frame_clock)
|
|
|
|
return;
|
|
|
|
|
|
|
|
clutter_frame_clock_remove_timeline (priv->frame_clock, timeline);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
set_frame_clock_internal (ClutterTimeline *timeline,
|
|
|
|
ClutterFrameClock *frame_clock)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
|
|
|
|
if (priv->frame_clock == frame_clock)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (priv->frame_clock && priv->is_playing)
|
|
|
|
maybe_remove_timeline (timeline);
|
|
|
|
|
|
|
|
g_set_object (&priv->frame_clock, frame_clock);
|
|
|
|
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (timeline),
|
|
|
|
obj_props[PROP_FRAME_CLOCK]);
|
|
|
|
|
|
|
|
if (priv->is_playing)
|
|
|
|
maybe_add_timeline (timeline);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
update_frame_clock (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
ClutterFrameClock *frame_clock;
|
|
|
|
|
|
|
|
if (priv->actor)
|
|
|
|
frame_clock = clutter_actor_pick_frame_clock (priv->actor);
|
|
|
|
else
|
|
|
|
frame_clock = NULL;
|
|
|
|
|
|
|
|
set_frame_clock_internal (timeline, frame_clock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_actor_stage_views_changed (ClutterActor *actor,
|
|
|
|
ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
update_frame_clock (timeline);
|
|
|
|
}
|
|
|
|
|
2020-04-17 03:00:18 -04:00
|
|
|
/**
|
|
|
|
* clutter_timeline_set_actor:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @actor: (nullable): a #ClutterActor
|
|
|
|
*
|
|
|
|
* Set the actor the timeline is associated with.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_timeline_set_actor (ClutterTimeline *timeline,
|
|
|
|
ClutterActor *actor)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
g_return_if_fail (!actor || (actor && !priv->custom_frame_clock));
|
|
|
|
|
2020-04-17 03:00:18 -04:00
|
|
|
if (priv->actor)
|
|
|
|
{
|
|
|
|
g_clear_signal_handler (&priv->actor_destroy_handler_id, priv->actor);
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
g_clear_signal_handler (&priv->actor_stage_views_handler_id, priv->actor);
|
|
|
|
priv->actor = NULL;
|
|
|
|
|
|
|
|
if (priv->is_playing)
|
|
|
|
maybe_remove_timeline (timeline);
|
|
|
|
|
|
|
|
priv->frame_clock = NULL;
|
2020-04-17 03:00:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
priv->actor = actor;
|
|
|
|
|
|
|
|
if (priv->actor)
|
|
|
|
{
|
|
|
|
priv->actor_destroy_handler_id =
|
|
|
|
g_signal_connect (priv->actor, "destroy",
|
|
|
|
G_CALLBACK (on_actor_destroyed),
|
|
|
|
timeline);
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
priv->actor_stage_views_handler_id =
|
|
|
|
g_signal_connect (priv->actor, "stage-views-changed",
|
|
|
|
G_CALLBACK (on_actor_stage_views_changed),
|
|
|
|
timeline);
|
2020-04-17 03:00:18 -04:00
|
|
|
}
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
|
|
|
|
update_frame_clock (timeline);
|
2020-04-17 03:00:18 -04:00
|
|
|
}
|
|
|
|
|
2011-11-27 07:16:32 -05:00
|
|
|
/* Scriptable */
|
|
|
|
typedef struct _ParseClosure {
|
|
|
|
ClutterTimeline *timeline;
|
|
|
|
ClutterScript *script;
|
|
|
|
GValue *value;
|
|
|
|
gboolean result;
|
|
|
|
} ParseClosure;
|
|
|
|
|
|
|
|
static void
|
|
|
|
parse_timeline_markers (JsonArray *array,
|
|
|
|
guint index_,
|
|
|
|
JsonNode *element,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
ParseClosure *clos = data;
|
|
|
|
JsonObject *object;
|
|
|
|
TimelineMarker *marker;
|
|
|
|
GList *markers;
|
|
|
|
|
|
|
|
if (JSON_NODE_TYPE (element) != JSON_NODE_OBJECT)
|
|
|
|
{
|
|
|
|
g_warning ("The 'markers' member of a ClutterTimeline description "
|
|
|
|
"should be an array of objects, but the element %d of the "
|
|
|
|
"array is of type '%s'. The element will be ignored.",
|
|
|
|
index_,
|
|
|
|
json_node_type_name (element));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
object = json_node_get_object (element);
|
|
|
|
|
|
|
|
if (!(json_object_has_member (object, "name") &&
|
2013-02-20 17:14:46 -05:00
|
|
|
(json_object_has_member (object, "time") ||
|
|
|
|
json_object_has_member (object, "progress"))))
|
2011-11-27 07:16:32 -05:00
|
|
|
{
|
|
|
|
g_warning ("The marker definition in a ClutterTimeline description "
|
2013-02-20 17:14:46 -05:00
|
|
|
"must be an object with the 'name' and either the 'time' "
|
|
|
|
"or the 'progress' members, but the element %d of the "
|
|
|
|
"'markers' array does not have any of them.",
|
2011-11-27 07:16:32 -05:00
|
|
|
index_);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (G_IS_VALUE (clos->value))
|
|
|
|
markers = g_value_get_pointer (clos->value);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_value_init (clos->value, G_TYPE_POINTER);
|
|
|
|
markers = NULL;
|
|
|
|
}
|
|
|
|
|
2013-02-20 17:14:46 -05:00
|
|
|
if (json_object_has_member (object, "time"))
|
|
|
|
marker = timeline_marker_new_time (json_object_get_string_member (object, "name"),
|
|
|
|
json_object_get_int_member (object, "time"));
|
|
|
|
else
|
|
|
|
marker = timeline_marker_new_progress (json_object_get_string_member (object, "name"),
|
|
|
|
json_object_get_double_member (object, "progress"));
|
2011-11-27 07:16:32 -05:00
|
|
|
|
|
|
|
markers = g_list_prepend (markers, marker);
|
|
|
|
|
|
|
|
g_value_set_pointer (clos->value, markers);
|
|
|
|
|
|
|
|
clos->result = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
clutter_timeline_parse_custom_node (ClutterScriptable *scriptable,
|
|
|
|
ClutterScript *script,
|
|
|
|
GValue *value,
|
|
|
|
const gchar *name,
|
|
|
|
JsonNode *node)
|
|
|
|
{
|
|
|
|
ParseClosure clos;
|
|
|
|
|
|
|
|
if (strcmp (name, "markers") != 0)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
clos.timeline = CLUTTER_TIMELINE (scriptable);
|
|
|
|
clos.script = script;
|
|
|
|
clos.value = value;
|
|
|
|
clos.result = FALSE;
|
|
|
|
|
|
|
|
json_array_foreach_element (json_node_get_array (node),
|
|
|
|
parse_timeline_markers,
|
|
|
|
&clos);
|
|
|
|
|
|
|
|
return clos.result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_timeline_set_custom_property (ClutterScriptable *scriptable,
|
|
|
|
ClutterScript *script,
|
|
|
|
const gchar *name,
|
|
|
|
const GValue *value)
|
|
|
|
{
|
|
|
|
if (strcmp (name, "markers") == 0)
|
|
|
|
{
|
|
|
|
ClutterTimeline *timeline = CLUTTER_TIMELINE (scriptable);
|
|
|
|
GList *markers = g_value_get_pointer (value);
|
|
|
|
GList *m;
|
|
|
|
|
|
|
|
/* the list was created through prepend() */
|
|
|
|
markers = g_list_reverse (markers);
|
|
|
|
|
|
|
|
for (m = markers; m != NULL; m = m->next)
|
|
|
|
clutter_timeline_add_marker_internal (timeline, m->data);
|
|
|
|
|
|
|
|
g_list_free (markers);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
g_object_set_property (G_OBJECT (scriptable), name, value);
|
|
|
|
}
|
|
|
|
|
2019-09-20 10:21:00 -04:00
|
|
|
void
|
2019-09-20 10:13:03 -04:00
|
|
|
clutter_timeline_cancel_delay (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
g_clear_handle_id (&timeline->priv->delay_id, g_source_remove);
|
|
|
|
}
|
2011-11-27 07:16:32 -05:00
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_scriptable_iface_init (ClutterScriptableIface *iface)
|
|
|
|
{
|
|
|
|
iface->parse_custom_node = clutter_timeline_parse_custom_node;
|
|
|
|
iface->set_custom_property = clutter_timeline_set_custom_property;
|
|
|
|
}
|
|
|
|
|
2006-08-13 19:55:52 -04:00
|
|
|
/* Object */
|
|
|
|
|
2007-10-12 04:17:00 -04:00
|
|
|
static void
|
|
|
|
clutter_timeline_set_property (GObject *object,
|
2006-05-29 04:59:36 -04:00
|
|
|
guint prop_id,
|
2007-10-12 04:17:00 -04:00
|
|
|
const GValue *value,
|
2006-05-29 04:59:36 -04:00
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
2010-11-17 10:27:42 -05:00
|
|
|
ClutterTimeline *timeline = CLUTTER_TIMELINE (object);
|
2006-05-29 04:59:36 -04:00
|
|
|
|
2007-10-12 04:17:00 -04:00
|
|
|
switch (prop_id)
|
2006-05-29 04:59:36 -04:00
|
|
|
{
|
2020-04-17 03:00:18 -04:00
|
|
|
case PROP_ACTOR:
|
|
|
|
clutter_timeline_set_actor (timeline, g_value_get_object (value));
|
|
|
|
break;
|
|
|
|
|
2007-06-07 06:26:18 -04:00
|
|
|
case PROP_DELAY:
|
2010-11-17 10:27:42 -05:00
|
|
|
clutter_timeline_set_delay (timeline, g_value_get_uint (value));
|
2007-06-07 06:26:18 -04:00
|
|
|
break;
|
2009-01-27 05:31:53 -05:00
|
|
|
|
2007-10-16 09:41:34 -04:00
|
|
|
case PROP_DURATION:
|
|
|
|
clutter_timeline_set_duration (timeline, g_value_get_uint (value));
|
|
|
|
break;
|
2009-01-27 05:31:53 -05:00
|
|
|
|
2007-11-15 12:03:55 -05:00
|
|
|
case PROP_DIRECTION:
|
|
|
|
clutter_timeline_set_direction (timeline, g_value_get_enum (value));
|
|
|
|
break;
|
2009-01-27 05:31:53 -05:00
|
|
|
|
2010-12-17 07:04:11 -05:00
|
|
|
case PROP_AUTO_REVERSE:
|
|
|
|
clutter_timeline_set_auto_reverse (timeline, g_value_get_boolean (value));
|
2010-11-17 10:27:42 -05:00
|
|
|
break;
|
|
|
|
|
2012-02-13 09:45:06 -05:00
|
|
|
case PROP_REPEAT_COUNT:
|
|
|
|
clutter_timeline_set_repeat_count (timeline, g_value_get_int (value));
|
|
|
|
break;
|
|
|
|
|
2012-02-17 11:06:28 -05:00
|
|
|
case PROP_PROGRESS_MODE:
|
|
|
|
clutter_timeline_set_progress_mode (timeline, g_value_get_enum (value));
|
|
|
|
break;
|
|
|
|
|
2020-03-25 13:16:39 -04:00
|
|
|
case PROP_FRAME_CLOCK:
|
|
|
|
clutter_timeline_set_frame_clock (timeline, g_value_get_object (value));
|
|
|
|
break;
|
|
|
|
|
2006-05-29 04:59:36 -04:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
2008-03-18 13:50:45 -04:00
|
|
|
}
|
2006-05-29 04:59:36 -04:00
|
|
|
}
|
|
|
|
|
2007-10-12 04:17:00 -04:00
|
|
|
static void
|
|
|
|
clutter_timeline_get_property (GObject *object,
|
2006-05-29 04:59:36 -04:00
|
|
|
guint prop_id,
|
2007-10-12 04:17:00 -04:00
|
|
|
GValue *value,
|
2006-05-29 04:59:36 -04:00
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
2010-11-17 10:27:42 -05:00
|
|
|
ClutterTimeline *timeline = CLUTTER_TIMELINE (object);
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
2006-05-29 04:59:36 -04:00
|
|
|
|
2007-10-12 04:17:00 -04:00
|
|
|
switch (prop_id)
|
2006-05-29 04:59:36 -04:00
|
|
|
{
|
2020-04-17 03:00:18 -04:00
|
|
|
case PROP_ACTOR:
|
|
|
|
g_value_set_object (value, priv->actor);
|
|
|
|
break;
|
|
|
|
|
2007-06-07 06:26:18 -04:00
|
|
|
case PROP_DELAY:
|
|
|
|
g_value_set_uint (value, priv->delay);
|
|
|
|
break;
|
2009-01-27 05:31:53 -05:00
|
|
|
|
2007-10-16 09:41:34 -04:00
|
|
|
case PROP_DURATION:
|
2008-08-01 07:09:43 -04:00
|
|
|
g_value_set_uint (value, clutter_timeline_get_duration (timeline));
|
2007-10-16 09:41:34 -04:00
|
|
|
break;
|
2009-01-27 05:31:53 -05:00
|
|
|
|
2007-11-15 12:03:55 -05:00
|
|
|
case PROP_DIRECTION:
|
|
|
|
g_value_set_enum (value, priv->direction);
|
|
|
|
break;
|
2009-01-27 05:31:53 -05:00
|
|
|
|
2010-12-17 07:04:11 -05:00
|
|
|
case PROP_AUTO_REVERSE:
|
|
|
|
g_value_set_boolean (value, priv->auto_reverse);
|
2010-11-17 10:27:42 -05:00
|
|
|
break;
|
|
|
|
|
2012-02-13 09:45:06 -05:00
|
|
|
case PROP_REPEAT_COUNT:
|
|
|
|
g_value_set_int (value, priv->repeat_count);
|
|
|
|
break;
|
|
|
|
|
2012-02-17 11:06:28 -05:00
|
|
|
case PROP_PROGRESS_MODE:
|
|
|
|
g_value_set_enum (value, priv->progress_mode);
|
|
|
|
break;
|
|
|
|
|
2020-03-25 13:16:39 -04:00
|
|
|
case PROP_FRAME_CLOCK:
|
|
|
|
g_value_set_object (value, priv->frame_clock);
|
|
|
|
break;
|
|
|
|
|
2006-05-29 04:59:36 -04:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-17 03:03:44 -04:00
|
|
|
static void
|
|
|
|
clutter_timeline_constructed (GObject *object)
|
|
|
|
{
|
|
|
|
ClutterTimeline *timeline = CLUTTER_TIMELINE (object);
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
|
|
|
|
g_warn_if_fail (priv->actor || priv->frame_clock);
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (clutter_timeline_parent_class)->constructed (object);
|
|
|
|
}
|
|
|
|
|
2007-10-12 04:17:00 -04:00
|
|
|
static void
|
2006-05-29 04:59:36 -04:00
|
|
|
clutter_timeline_finalize (GObject *object)
|
|
|
|
{
|
2009-03-21 16:39:32 -04:00
|
|
|
ClutterTimeline *self = CLUTTER_TIMELINE (object);
|
|
|
|
ClutterTimelinePrivate *priv = self->priv;
|
2008-03-18 13:50:45 -04:00
|
|
|
|
2009-01-23 12:17:36 -05:00
|
|
|
if (priv->markers_by_name)
|
|
|
|
g_hash_table_destroy (priv->markers_by_name);
|
2008-03-18 13:50:45 -04:00
|
|
|
|
2009-06-06 17:44:40 -04:00
|
|
|
if (priv->is_playing)
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
maybe_remove_timeline (self);
|
2009-03-21 16:39:32 -04:00
|
|
|
|
2020-03-25 13:16:39 -04:00
|
|
|
g_clear_object (&priv->frame_clock);
|
|
|
|
|
2006-05-29 04:59:36 -04:00
|
|
|
G_OBJECT_CLASS (clutter_timeline_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
2007-10-12 04:17:00 -04:00
|
|
|
static void
|
2006-05-29 04:59:36 -04:00
|
|
|
clutter_timeline_dispose (GObject *object)
|
|
|
|
{
|
|
|
|
ClutterTimeline *self = CLUTTER_TIMELINE(object);
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
priv = self->priv;
|
|
|
|
|
2019-09-20 10:13:03 -04:00
|
|
|
clutter_timeline_cancel_delay (self);
|
2007-06-07 06:26:18 -04:00
|
|
|
|
2020-04-17 03:00:18 -04:00
|
|
|
if (priv->actor)
|
|
|
|
{
|
|
|
|
g_clear_signal_handler (&priv->actor_destroy_handler_id, priv->actor);
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
g_clear_signal_handler (&priv->actor_stage_views_handler_id, priv->actor);
|
2020-04-17 03:00:18 -04:00
|
|
|
priv->actor = NULL;
|
|
|
|
}
|
|
|
|
|
2012-02-17 11:06:28 -05:00
|
|
|
if (priv->progress_notify != NULL)
|
|
|
|
{
|
|
|
|
priv->progress_notify (priv->progress_data);
|
|
|
|
priv->progress_func = NULL;
|
|
|
|
priv->progress_data = NULL;
|
|
|
|
priv->progress_notify = NULL;
|
|
|
|
}
|
|
|
|
|
2006-05-29 04:59:36 -04:00
|
|
|
G_OBJECT_CLASS (clutter_timeline_parent_class)->dispose (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_timeline_class_init (ClutterTimelineClass *klass)
|
|
|
|
{
|
2007-06-09 09:20:00 -04:00
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
2006-05-29 04:59:36 -04:00
|
|
|
|
2020-04-17 03:00:18 -04:00
|
|
|
/**
|
|
|
|
* ClutterTimeline::actor:
|
|
|
|
*
|
|
|
|
* The actor the timeline is associated with. This will determine what frame
|
|
|
|
* clock will drive it.
|
|
|
|
*/
|
|
|
|
obj_props[PROP_ACTOR] =
|
|
|
|
g_param_spec_object ("actor",
|
|
|
|
P_("Actor"),
|
|
|
|
P_("Associated ClutterActor"),
|
|
|
|
CLUTTER_TYPE_ACTOR,
|
|
|
|
G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE);
|
2007-06-07 06:26:18 -04:00
|
|
|
/**
|
|
|
|
* ClutterTimeline:delay:
|
|
|
|
*
|
|
|
|
* A delay, in milliseconds, that should be observed by the
|
|
|
|
* timeline before actually starting.
|
|
|
|
*
|
|
|
|
* Since: 0.4
|
|
|
|
*/
|
2010-10-15 10:24:27 -04:00
|
|
|
obj_props[PROP_DELAY] =
|
|
|
|
g_param_spec_uint ("delay",
|
|
|
|
P_("Delay"),
|
|
|
|
P_("Delay before start"),
|
|
|
|
0, G_MAXUINT,
|
|
|
|
0,
|
|
|
|
CLUTTER_PARAM_READWRITE);
|
2009-01-23 12:14:39 -05:00
|
|
|
|
2007-10-16 09:41:34 -04:00
|
|
|
/**
|
|
|
|
* ClutterTimeline:duration:
|
|
|
|
*
|
|
|
|
* Duration of the timeline in milliseconds, depending on the
|
|
|
|
* ClutterTimeline:fps value.
|
|
|
|
*
|
|
|
|
* Since: 0.6
|
|
|
|
*/
|
2010-10-15 10:24:27 -04:00
|
|
|
obj_props[PROP_DURATION] =
|
|
|
|
g_param_spec_uint ("duration",
|
|
|
|
P_("Duration"),
|
|
|
|
P_("Duration of the timeline in milliseconds"),
|
|
|
|
0, G_MAXUINT,
|
|
|
|
1000,
|
|
|
|
CLUTTER_PARAM_READWRITE);
|
2009-01-23 12:14:39 -05:00
|
|
|
|
2007-11-15 12:03:55 -05:00
|
|
|
/**
|
|
|
|
* ClutterTimeline:direction:
|
2008-05-12 11:18:38 -04:00
|
|
|
*
|
2007-11-15 12:03:55 -05:00
|
|
|
* The direction of the timeline, either %CLUTTER_TIMELINE_FORWARD or
|
|
|
|
* %CLUTTER_TIMELINE_BACKWARD.
|
|
|
|
*
|
|
|
|
* Since: 0.6
|
|
|
|
*/
|
2010-10-15 10:24:27 -04:00
|
|
|
obj_props[PROP_DIRECTION] =
|
|
|
|
g_param_spec_enum ("direction",
|
|
|
|
P_("Direction"),
|
|
|
|
P_("Direction of the timeline"),
|
|
|
|
CLUTTER_TYPE_TIMELINE_DIRECTION,
|
|
|
|
CLUTTER_TIMELINE_FORWARD,
|
|
|
|
CLUTTER_PARAM_READWRITE);
|
|
|
|
|
2010-11-17 10:27:42 -05:00
|
|
|
/**
|
2010-12-17 07:04:11 -05:00
|
|
|
* ClutterTimeline:auto-reverse:
|
2010-11-17 10:27:42 -05:00
|
|
|
*
|
2010-12-17 07:04:11 -05:00
|
|
|
* If the direction of the timeline should be automatically reversed
|
|
|
|
* when reaching the end.
|
2010-11-17 10:27:42 -05:00
|
|
|
*
|
|
|
|
* Since: 1.6
|
|
|
|
*/
|
2010-12-17 07:04:11 -05:00
|
|
|
obj_props[PROP_AUTO_REVERSE] =
|
|
|
|
g_param_spec_boolean ("auto-reverse",
|
|
|
|
P_("Auto Reverse"),
|
|
|
|
P_("Whether the direction should be reversed when reaching the end"),
|
2010-11-17 10:27:42 -05:00
|
|
|
FALSE,
|
|
|
|
CLUTTER_PARAM_READWRITE);
|
|
|
|
|
2012-02-13 09:45:06 -05:00
|
|
|
/**
|
|
|
|
* ClutterTimeline:repeat-count:
|
|
|
|
*
|
|
|
|
* Defines how many times the timeline should repeat.
|
|
|
|
*
|
|
|
|
* If the repeat count is 0, the timeline does not repeat.
|
|
|
|
*
|
|
|
|
* If the repeat count is set to -1, the timeline will repeat until it is
|
|
|
|
* stopped.
|
|
|
|
*
|
|
|
|
* Since: 1.10
|
|
|
|
*/
|
|
|
|
obj_props[PROP_REPEAT_COUNT] =
|
|
|
|
g_param_spec_int ("repeat-count",
|
|
|
|
P_("Repeat Count"),
|
|
|
|
P_("How many times the timeline should repeat"),
|
|
|
|
-1, G_MAXINT,
|
|
|
|
0,
|
|
|
|
CLUTTER_PARAM_READWRITE);
|
|
|
|
|
2012-02-17 11:06:28 -05:00
|
|
|
/**
|
|
|
|
* ClutterTimeline:progress-mode:
|
|
|
|
*
|
|
|
|
* Controls the way a #ClutterTimeline computes the normalized progress.
|
|
|
|
*
|
|
|
|
* Since: 1.10
|
|
|
|
*/
|
|
|
|
obj_props[PROP_PROGRESS_MODE] =
|
|
|
|
g_param_spec_enum ("progress-mode",
|
|
|
|
P_("Progress Mode"),
|
|
|
|
P_("How the timeline should compute the progress"),
|
|
|
|
CLUTTER_TYPE_ANIMATION_MODE,
|
|
|
|
CLUTTER_LINEAR,
|
|
|
|
CLUTTER_PARAM_READWRITE);
|
|
|
|
|
2020-03-25 13:16:39 -04:00
|
|
|
/**
|
|
|
|
* ClutterTimeline:frame-clock:
|
|
|
|
*
|
|
|
|
* The frame clock driving the timeline.
|
|
|
|
*/
|
|
|
|
obj_props[PROP_FRAME_CLOCK] =
|
|
|
|
g_param_spec_object ("frame-clock",
|
|
|
|
"Frame clock",
|
|
|
|
"Frame clock driving the timeline",
|
|
|
|
CLUTTER_TYPE_FRAME_CLOCK,
|
|
|
|
G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE);
|
|
|
|
|
2012-02-17 11:06:28 -05:00
|
|
|
object_class->dispose = clutter_timeline_dispose;
|
|
|
|
object_class->finalize = clutter_timeline_finalize;
|
2020-04-17 03:03:44 -04:00
|
|
|
object_class->constructed = clutter_timeline_constructed;
|
2010-10-15 10:24:27 -04:00
|
|
|
object_class->set_property = clutter_timeline_set_property;
|
|
|
|
object_class->get_property = clutter_timeline_get_property;
|
2012-02-17 11:06:28 -05:00
|
|
|
g_object_class_install_properties (object_class, PROP_LAST, obj_props);
|
2006-05-29 04:59:36 -04:00
|
|
|
|
2006-12-13 13:12:09 -05:00
|
|
|
/**
|
|
|
|
* ClutterTimeline::new-frame:
|
|
|
|
* @timeline: the timeline which received the signal
|
2009-06-04 08:05:12 -04:00
|
|
|
* @msecs: the elapsed time between 0 and duration
|
2006-12-13 13:12:09 -05:00
|
|
|
*
|
2009-06-04 08:05:12 -04:00
|
|
|
* The ::new-frame signal is emitted for each timeline running
|
|
|
|
* timeline before a new frame is drawn to give animations a chance
|
|
|
|
* to update the scene.
|
2006-12-13 13:12:09 -05:00
|
|
|
*/
|
2006-11-15 18:37:53 -05:00
|
|
|
timeline_signals[NEW_FRAME] =
|
2009-01-27 05:32:15 -05:00
|
|
|
g_signal_new (I_("new-frame"),
|
2006-05-29 04:59:36 -04:00
|
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
G_STRUCT_OFFSET (ClutterTimelineClass, new_frame),
|
2019-07-24 12:17:06 -04:00
|
|
|
NULL, NULL, NULL,
|
2007-10-12 04:17:00 -04:00
|
|
|
G_TYPE_NONE,
|
2006-05-29 04:59:36 -04:00
|
|
|
1, G_TYPE_INT);
|
2007-07-01 12:44:24 -04:00
|
|
|
/**
|
|
|
|
* ClutterTimeline::completed:
|
|
|
|
* @timeline: the #ClutterTimeline which received the signal
|
|
|
|
*
|
2012-02-17 11:06:28 -05:00
|
|
|
* The #ClutterTimeline::completed signal is emitted when the timeline's
|
|
|
|
* elapsed time reaches the value of the #ClutterTimeline:duration
|
|
|
|
* property.
|
|
|
|
*
|
|
|
|
* This signal will be emitted even if the #ClutterTimeline is set to be
|
|
|
|
* repeating.
|
2012-05-25 19:41:14 -04:00
|
|
|
*
|
|
|
|
* If you want to get notification on whether the #ClutterTimeline has
|
|
|
|
* been stopped or has finished its run, including its eventual repeats,
|
|
|
|
* you should use the #ClutterTimeline::stopped signal instead.
|
2007-07-01 12:44:24 -04:00
|
|
|
*/
|
2006-11-15 18:37:53 -05:00
|
|
|
timeline_signals[COMPLETED] =
|
2009-01-27 05:32:15 -05:00
|
|
|
g_signal_new (I_("completed"),
|
2006-05-29 04:59:36 -04:00
|
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
G_STRUCT_OFFSET (ClutterTimelineClass, completed),
|
2019-07-24 12:17:06 -04:00
|
|
|
NULL, NULL, NULL,
|
2006-06-22 08:05:51 -04:00
|
|
|
G_TYPE_NONE, 0);
|
2007-07-01 12:44:24 -04:00
|
|
|
/**
|
|
|
|
* ClutterTimeline::started:
|
|
|
|
* @timeline: the #ClutterTimeline which received the signal
|
|
|
|
*
|
|
|
|
* The ::started signal is emitted when the timeline starts its run.
|
|
|
|
* This might be as soon as clutter_timeline_start() is invoked or
|
|
|
|
* after the delay set in the ClutterTimeline:delay property has
|
|
|
|
* expired.
|
|
|
|
*/
|
2006-11-15 18:37:53 -05:00
|
|
|
timeline_signals[STARTED] =
|
2009-01-27 05:32:15 -05:00
|
|
|
g_signal_new (I_("started"),
|
2006-06-22 08:05:51 -04:00
|
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
G_STRUCT_OFFSET (ClutterTimelineClass, started),
|
2019-07-24 12:17:06 -04:00
|
|
|
NULL, NULL, NULL,
|
2006-06-22 08:05:51 -04:00
|
|
|
G_TYPE_NONE, 0);
|
2007-07-01 12:44:24 -04:00
|
|
|
/**
|
|
|
|
* ClutterTimeline::paused:
|
|
|
|
* @timeline: the #ClutterTimeline which received the signal
|
|
|
|
*
|
|
|
|
* The ::paused signal is emitted when clutter_timeline_pause() is invoked.
|
|
|
|
*/
|
2006-11-15 18:37:53 -05:00
|
|
|
timeline_signals[PAUSED] =
|
2009-01-27 05:32:15 -05:00
|
|
|
g_signal_new (I_("paused"),
|
2006-06-22 08:05:51 -04:00
|
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
G_STRUCT_OFFSET (ClutterTimelineClass, paused),
|
2019-07-24 12:17:06 -04:00
|
|
|
NULL, NULL, NULL,
|
2006-05-29 04:59:36 -04:00
|
|
|
G_TYPE_NONE, 0);
|
2008-03-18 13:50:45 -04:00
|
|
|
/**
|
|
|
|
* ClutterTimeline::marker-reached:
|
|
|
|
* @timeline: the #ClutterTimeline which received the signal
|
|
|
|
* @marker_name: the name of the marker reached
|
2009-06-04 08:05:12 -04:00
|
|
|
* @msecs: the elapsed time
|
2008-03-18 13:50:45 -04:00
|
|
|
*
|
|
|
|
* The ::marker-reached signal is emitted each time a timeline
|
2009-06-04 08:05:12 -04:00
|
|
|
* reaches a marker set with
|
|
|
|
* clutter_timeline_add_marker_at_time(). This signal is detailed
|
|
|
|
* with the name of the marker as well, so it is possible to connect
|
|
|
|
* a callback to the ::marker-reached signal for a specific marker
|
|
|
|
* with:
|
2008-03-18 13:50:45 -04:00
|
|
|
*
|
|
|
|
* <informalexample><programlisting>
|
2009-06-04 08:05:12 -04:00
|
|
|
* clutter_timeline_add_marker_at_time (timeline, "foo", 500);
|
|
|
|
* clutter_timeline_add_marker_at_time (timeline, "bar", 750);
|
2008-03-18 13:50:45 -04:00
|
|
|
*
|
|
|
|
* g_signal_connect (timeline, "marker-reached",
|
|
|
|
* G_CALLBACK (each_marker_reached), NULL);
|
|
|
|
* g_signal_connect (timeline, "marker-reached::foo",
|
|
|
|
* G_CALLBACK (foo_marker_reached), NULL);
|
|
|
|
* g_signal_connect (timeline, "marker-reached::bar",
|
|
|
|
* G_CALLBACK (bar_marker_reached), NULL);
|
|
|
|
* </programlisting></informalexample>
|
|
|
|
*
|
|
|
|
* In the example, the first callback will be invoked for both
|
|
|
|
* the "foo" and "bar" marker, while the second and third callbacks
|
|
|
|
* will be invoked for the "foo" or "bar" markers, respectively.
|
|
|
|
*
|
|
|
|
* Since: 0.8
|
|
|
|
*/
|
|
|
|
timeline_signals[MARKER_REACHED] =
|
2009-01-27 05:32:15 -05:00
|
|
|
g_signal_new (I_("marker-reached"),
|
2008-03-18 13:50:45 -04:00
|
|
|
G_TYPE_FROM_CLASS (object_class),
|
2009-01-23 12:14:39 -05:00
|
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE |
|
|
|
|
G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS,
|
2008-03-18 13:50:45 -04:00
|
|
|
G_STRUCT_OFFSET (ClutterTimelineClass, marker_reached),
|
|
|
|
NULL, NULL,
|
2010-06-11 10:49:46 -04:00
|
|
|
_clutter_marshal_VOID__STRING_INT,
|
2008-03-18 13:50:45 -04:00
|
|
|
G_TYPE_NONE, 2,
|
|
|
|
G_TYPE_STRING,
|
2008-06-15 08:50:05 -04:00
|
|
|
G_TYPE_INT);
|
2012-05-25 19:41:14 -04:00
|
|
|
/**
|
|
|
|
* ClutterTimeline::stopped:
|
|
|
|
* @timeline: the #ClutterTimeline that emitted the signal
|
|
|
|
* @is_finished: %TRUE if the signal was emitted at the end of the
|
|
|
|
* timeline.
|
|
|
|
*
|
|
|
|
* The #ClutterTimeline::stopped signal is emitted when the timeline
|
|
|
|
* has been stopped, either because clutter_timeline_stop() has been
|
|
|
|
* called, or because it has been exhausted.
|
|
|
|
*
|
|
|
|
* This is different from the #ClutterTimeline::completed signal,
|
|
|
|
* which gets emitted after every repeat finishes.
|
|
|
|
*
|
|
|
|
* If the #ClutterTimeline has is marked as infinitely repeating,
|
|
|
|
* this signal will never be emitted.
|
|
|
|
*
|
|
|
|
* Since: 1.12
|
|
|
|
*/
|
|
|
|
timeline_signals[STOPPED] =
|
|
|
|
g_signal_new (I_("stopped"),
|
|
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
2013-03-04 19:46:45 -05:00
|
|
|
G_STRUCT_OFFSET (ClutterTimelineClass, stopped),
|
2019-07-24 12:17:06 -04:00
|
|
|
NULL, NULL, NULL,
|
2012-05-25 19:41:14 -04:00
|
|
|
G_TYPE_NONE, 1,
|
|
|
|
G_TYPE_BOOLEAN);
|
2006-05-29 04:59:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_timeline_init (ClutterTimeline *self)
|
|
|
|
{
|
2013-07-03 09:14:01 -04:00
|
|
|
self->priv = clutter_timeline_get_instance_private (self);
|
2008-03-18 13:50:45 -04:00
|
|
|
|
2013-07-03 09:14:01 -04:00
|
|
|
self->priv->progress_mode = CLUTTER_LINEAR;
|
2012-07-19 21:55:35 -04:00
|
|
|
|
|
|
|
/* default steps() parameters are 1, end */
|
2013-07-03 09:14:01 -04:00
|
|
|
self->priv->n_steps = 1;
|
|
|
|
self->priv->step_mode = CLUTTER_STEP_MODE_END;
|
2012-07-19 21:55:35 -04:00
|
|
|
|
|
|
|
/* default cubic-bezier() paramereters are (0, 0, 1, 1) */
|
2019-02-20 09:53:44 -05:00
|
|
|
graphene_point_init (&self->priv->cb_1, 0, 0);
|
|
|
|
graphene_point_init (&self->priv->cb_2, 1, 1);
|
2006-05-29 04:59:36 -04:00
|
|
|
}
|
|
|
|
|
2009-06-11 07:14:53 -04:00
|
|
|
struct CheckIfMarkerHitClosure
|
|
|
|
{
|
|
|
|
ClutterTimeline *timeline;
|
|
|
|
ClutterTimelineDirection direction;
|
|
|
|
gint new_time;
|
|
|
|
gint duration;
|
|
|
|
gint delta;
|
|
|
|
};
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
have_passed_time (const struct CheckIfMarkerHitClosure *data,
|
|
|
|
gint msecs)
|
|
|
|
{
|
|
|
|
/* Ignore markers that are outside the duration of the timeline */
|
|
|
|
if (msecs < 0 || msecs > data->duration)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (data->direction == CLUTTER_TIMELINE_FORWARD)
|
|
|
|
{
|
|
|
|
/* We need to special case when a marker is added at the
|
|
|
|
beginning of the timeline */
|
|
|
|
if (msecs == 0 &&
|
|
|
|
data->delta > 0 &&
|
|
|
|
data->new_time - data->delta <= 0)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
/* Otherwise it's just a simple test if the time is in range of
|
|
|
|
the previous time and the new time */
|
2013-02-20 17:14:46 -05:00
|
|
|
return (msecs > data->new_time - data->delta &&
|
|
|
|
msecs <= data->new_time);
|
2009-06-11 07:14:53 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* We need to special case when a marker is added at the
|
|
|
|
end of the timeline */
|
|
|
|
if (msecs == data->duration &&
|
|
|
|
data->delta > 0 &&
|
|
|
|
data->new_time + data->delta >= data->duration)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
/* Otherwise it's just a simple test if the time is in range of
|
|
|
|
the previous time and the new time */
|
2013-02-20 17:14:46 -05:00
|
|
|
return (msecs >= data->new_time &&
|
|
|
|
msecs < data->new_time + data->delta);
|
2009-06-11 07:14:53 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
static void
|
|
|
|
check_if_marker_hit (const gchar *name,
|
|
|
|
TimelineMarker *marker,
|
2009-06-11 07:14:53 -04:00
|
|
|
struct CheckIfMarkerHitClosure *data)
|
2009-06-04 08:05:12 -04:00
|
|
|
{
|
2013-02-20 17:14:46 -05:00
|
|
|
gint msecs;
|
|
|
|
|
|
|
|
if (marker->is_relative)
|
|
|
|
msecs = (gdouble) data->duration * marker->data.progress;
|
|
|
|
else
|
|
|
|
msecs = marker->data.msecs;
|
|
|
|
|
|
|
|
if (have_passed_time (data, msecs))
|
2009-06-04 08:05:12 -04:00
|
|
|
{
|
|
|
|
CLUTTER_NOTE (SCHEDULER, "Marker '%s' reached", name);
|
|
|
|
|
2009-06-11 07:14:53 -04:00
|
|
|
g_signal_emit (data->timeline, timeline_signals[MARKER_REACHED],
|
2009-06-04 08:05:12 -04:00
|
|
|
marker->quark,
|
|
|
|
name,
|
2013-02-20 17:14:46 -05:00
|
|
|
msecs);
|
2009-06-04 08:05:12 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-03 07:42:17 -05:00
|
|
|
static void
|
2009-06-11 07:14:53 -04:00
|
|
|
check_markers (ClutterTimeline *timeline,
|
|
|
|
gint delta)
|
2008-11-03 07:42:17 -05:00
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
2009-06-11 07:14:53 -04:00
|
|
|
struct CheckIfMarkerHitClosure data;
|
2008-11-03 07:42:17 -05:00
|
|
|
|
2009-01-27 05:27:33 -05:00
|
|
|
/* shortcircuit here if we don't have any marker installed */
|
2009-06-04 08:05:12 -04:00
|
|
|
if (priv->markers_by_name == NULL)
|
2009-01-23 12:17:36 -05:00
|
|
|
return;
|
|
|
|
|
2009-06-11 07:14:53 -04:00
|
|
|
/* store the details of the timeline so that changing them in a
|
|
|
|
marker signal handler won't affect which markers are hit */
|
|
|
|
data.timeline = timeline;
|
|
|
|
data.direction = priv->direction;
|
|
|
|
data.new_time = priv->elapsed_time;
|
|
|
|
data.duration = priv->duration;
|
|
|
|
data.delta = delta;
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
g_hash_table_foreach (priv->markers_by_name,
|
|
|
|
(GHFunc) check_if_marker_hit,
|
2009-06-11 07:14:53 -04:00
|
|
|
&data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
emit_frame_signal (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
|
2011-07-18 08:45:55 -04:00
|
|
|
/* see bug https://bugzilla.gnome.org/show_bug.cgi?id=654066 */
|
|
|
|
gint elapsed = (gint) priv->elapsed_time;
|
|
|
|
|
2012-03-21 12:07:29 -04:00
|
|
|
CLUTTER_NOTE (SCHEDULER, "Emitting ::new-frame signal on timeline[%p]", timeline);
|
|
|
|
|
2011-07-18 08:45:55 -04:00
|
|
|
g_signal_emit (timeline, timeline_signals[NEW_FRAME], 0, elapsed);
|
2008-11-03 07:42:17 -05:00
|
|
|
}
|
|
|
|
|
2009-01-27 05:33:44 -05:00
|
|
|
static gboolean
|
|
|
|
is_complete (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
return (priv->direction == CLUTTER_TIMELINE_FORWARD
|
|
|
|
? priv->elapsed_time >= priv->duration
|
|
|
|
: priv->elapsed_time <= 0);
|
2009-01-27 05:33:44 -05:00
|
|
|
}
|
|
|
|
|
2009-06-06 17:44:40 -04:00
|
|
|
static void
|
|
|
|
set_is_playing (ClutterTimeline *timeline,
|
|
|
|
gboolean is_playing)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
|
2012-05-25 19:41:14 -04:00
|
|
|
is_playing = !!is_playing;
|
2009-06-06 17:44:40 -04:00
|
|
|
|
|
|
|
if (is_playing == priv->is_playing)
|
|
|
|
return;
|
|
|
|
|
|
|
|
priv->is_playing = is_playing;
|
2012-05-25 19:41:14 -04:00
|
|
|
|
2009-06-06 17:44:40 -04:00
|
|
|
if (priv->is_playing)
|
2009-06-08 13:00:09 -04:00
|
|
|
{
|
|
|
|
priv->waiting_first_tick = TRUE;
|
2012-02-13 09:45:06 -05:00
|
|
|
priv->current_repeat = 0;
|
2020-03-25 13:16:39 -04:00
|
|
|
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
maybe_add_timeline (timeline);
|
2009-06-08 13:00:09 -04:00
|
|
|
}
|
2009-06-06 17:44:40 -04:00
|
|
|
else
|
2020-03-25 13:16:39 -04:00
|
|
|
{
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
maybe_remove_timeline (timeline);
|
2020-03-25 13:16:39 -04:00
|
|
|
}
|
2009-06-06 17:44:40 -04:00
|
|
|
}
|
|
|
|
|
2006-05-29 04:59:36 -04:00
|
|
|
static gboolean
|
2009-06-08 13:00:09 -04:00
|
|
|
clutter_timeline_do_frame (ClutterTimeline *timeline)
|
2006-05-29 04:59:36 -04:00
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
2007-08-19 15:09:20 -04:00
|
|
|
g_object_ref (timeline);
|
2007-10-12 04:17:00 -04:00
|
|
|
|
2015-09-17 11:27:14 -04:00
|
|
|
CLUTTER_NOTE (SCHEDULER, "Timeline [%p] activated (elapsed time: %ld, "
|
|
|
|
"duration: %ld, msecs_delta: %ld)\n",
|
2011-11-15 11:07:13 -05:00
|
|
|
timeline,
|
2015-09-17 11:27:14 -04:00
|
|
|
(long) priv->elapsed_time,
|
|
|
|
(long) priv->duration,
|
|
|
|
(long) priv->msecs_delta);
|
2007-10-12 04:17:00 -04:00
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
/* Advance time */
|
2007-11-15 12:03:55 -05:00
|
|
|
if (priv->direction == CLUTTER_TIMELINE_FORWARD)
|
2009-06-04 08:05:12 -04:00
|
|
|
priv->elapsed_time += priv->msecs_delta;
|
2007-11-15 12:03:55 -05:00
|
|
|
else
|
2009-06-04 08:05:12 -04:00
|
|
|
priv->elapsed_time -= priv->msecs_delta;
|
2006-08-15 16:28:41 -04:00
|
|
|
|
2008-03-06 20:00:00 -05:00
|
|
|
/* If we have not reached the end of the timeline: */
|
2009-01-27 05:33:44 -05:00
|
|
|
if (!is_complete (timeline))
|
2006-05-29 04:59:36 -04:00
|
|
|
{
|
2008-11-03 07:42:17 -05:00
|
|
|
/* Emit the signal */
|
2009-06-11 05:55:52 -04:00
|
|
|
emit_frame_signal (timeline);
|
2009-06-11 07:14:53 -04:00
|
|
|
check_markers (timeline, priv->msecs_delta);
|
2008-03-18 13:50:45 -04:00
|
|
|
|
2008-03-06 20:00:00 -05:00
|
|
|
g_object_unref (timeline);
|
2012-02-13 09:45:06 -05:00
|
|
|
|
|
|
|
return priv->is_playing;
|
2008-03-06 20:00:00 -05:00
|
|
|
}
|
2008-03-18 13:50:45 -04:00
|
|
|
else
|
2008-03-06 20:00:00 -05:00
|
|
|
{
|
2008-03-18 13:50:45 -04:00
|
|
|
/* Handle loop or stop */
|
2009-06-11 05:55:52 -04:00
|
|
|
ClutterTimelineDirection saved_direction = priv->direction;
|
2009-06-11 07:14:53 -04:00
|
|
|
gint elapsed_time_delta = priv->msecs_delta;
|
2009-06-11 05:55:52 -04:00
|
|
|
guint overflow_msecs = priv->elapsed_time;
|
2009-06-04 08:05:12 -04:00
|
|
|
gint end_msecs;
|
2008-05-12 11:18:38 -04:00
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
/* Update the current elapsed time in case the signal handlers
|
2009-06-11 05:55:52 -04:00
|
|
|
* want to take a peek. If we clamp elapsed time, then we need
|
2009-06-11 07:14:53 -04:00
|
|
|
* to correpondingly reduce elapsed_time_delta to reflect the correct
|
2009-06-11 05:55:52 -04:00
|
|
|
* range of times */
|
|
|
|
if (priv->direction == CLUTTER_TIMELINE_FORWARD)
|
2019-12-07 05:56:02 -05:00
|
|
|
{
|
|
|
|
elapsed_time_delta -= (priv->elapsed_time - priv->duration);
|
|
|
|
priv->elapsed_time = priv->duration;
|
|
|
|
}
|
2007-11-19 10:25:56 -05:00
|
|
|
else if (priv->direction == CLUTTER_TIMELINE_BACKWARD)
|
2019-12-07 05:56:02 -05:00
|
|
|
{
|
|
|
|
elapsed_time_delta -= - priv->elapsed_time;
|
|
|
|
priv->elapsed_time = 0;
|
|
|
|
}
|
2008-03-18 13:50:45 -04:00
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
end_msecs = priv->elapsed_time;
|
2008-03-06 20:00:00 -05:00
|
|
|
|
2009-06-11 05:55:52 -04:00
|
|
|
/* Emit the signal */
|
|
|
|
emit_frame_signal (timeline);
|
2009-06-11 07:14:53 -04:00
|
|
|
check_markers (timeline, elapsed_time_delta);
|
2008-03-06 20:00:00 -05:00
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
/* Did the signal handler modify the elapsed time? */
|
|
|
|
if (priv->elapsed_time != end_msecs)
|
2008-03-06 20:00:00 -05:00
|
|
|
{
|
|
|
|
g_object_unref (timeline);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Note: If the new-frame signal handler paused the timeline
|
|
|
|
* on the last frame we will still go ahead and send the
|
|
|
|
* completed signal */
|
2007-11-19 10:25:56 -05:00
|
|
|
CLUTTER_NOTE (SCHEDULER,
|
2009-09-16 06:55:04 -04:00
|
|
|
"Timeline [%p] completed (cur: %ld, tot: %ld)",
|
2007-11-19 10:25:56 -05:00
|
|
|
timeline,
|
2009-09-16 06:55:04 -04:00
|
|
|
(long) priv->elapsed_time,
|
|
|
|
(long) priv->msecs_delta);
|
2007-11-19 10:25:56 -05:00
|
|
|
|
2012-02-13 09:45:06 -05:00
|
|
|
if (priv->is_playing &&
|
|
|
|
(priv->repeat_count == 0 ||
|
|
|
|
priv->repeat_count == priv->current_repeat))
|
2008-03-06 20:00:00 -05:00
|
|
|
{
|
2012-02-13 09:45:06 -05:00
|
|
|
/* We stop the timeline now, so that the completed signal handler
|
2008-05-12 11:18:38 -04:00
|
|
|
* may choose to re-start the timeline
|
2008-03-06 20:00:00 -05:00
|
|
|
*
|
2012-02-13 09:45:06 -05:00
|
|
|
* XXX Perhaps we should do this earlier, and regardless of
|
|
|
|
* priv->repeat_count. Are we limiting the things that could be
|
|
|
|
* done in the above new-frame signal handler?
|
|
|
|
*/
|
2012-05-25 19:41:14 -04:00
|
|
|
set_is_playing (timeline, FALSE);
|
2012-06-12 17:44:42 -04:00
|
|
|
|
|
|
|
g_signal_emit (timeline, timeline_signals[COMPLETED], 0);
|
2012-05-25 19:41:14 -04:00
|
|
|
g_signal_emit (timeline, timeline_signals[STOPPED], 0, TRUE);
|
2008-03-06 20:00:00 -05:00
|
|
|
}
|
2012-06-12 17:44:42 -04:00
|
|
|
else
|
|
|
|
g_signal_emit (timeline, timeline_signals[COMPLETED], 0);
|
2008-03-06 20:00:00 -05:00
|
|
|
|
2012-03-19 14:05:29 -04:00
|
|
|
priv->current_repeat += 1;
|
|
|
|
|
2010-12-17 07:04:11 -05:00
|
|
|
if (priv->auto_reverse)
|
2010-11-17 10:27:42 -05:00
|
|
|
{
|
2010-12-17 07:04:11 -05:00
|
|
|
/* :auto-reverse changes the direction of the timeline */
|
2010-11-17 10:27:42 -05:00
|
|
|
if (priv->direction == CLUTTER_TIMELINE_FORWARD)
|
|
|
|
priv->direction = CLUTTER_TIMELINE_BACKWARD;
|
|
|
|
else
|
|
|
|
priv->direction = CLUTTER_TIMELINE_FORWARD;
|
|
|
|
|
2011-03-03 06:35:46 -05:00
|
|
|
g_object_notify_by_pspec (G_OBJECT (timeline),
|
2010-11-17 10:27:42 -05:00
|
|
|
obj_props[PROP_DIRECTION]);
|
|
|
|
}
|
|
|
|
|
2008-03-06 20:00:00 -05:00
|
|
|
/* Again check to see if the user has manually played with
|
2009-06-04 08:05:12 -04:00
|
|
|
* the elapsed time, before we finally stop or loop the timeline */
|
2006-05-29 04:59:36 -04:00
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
if (priv->elapsed_time != end_msecs &&
|
|
|
|
!(/* Except allow changing time from 0 -> duration (or vice-versa)
|
2008-03-06 20:00:00 -05:00
|
|
|
since these are considered equivalent */
|
2009-06-04 08:05:12 -04:00
|
|
|
(priv->elapsed_time == 0 && end_msecs == priv->duration) ||
|
|
|
|
(priv->elapsed_time == priv->duration && end_msecs == 0)
|
2008-03-06 20:00:00 -05:00
|
|
|
))
|
|
|
|
{
|
|
|
|
g_object_unref (timeline);
|
|
|
|
return TRUE;
|
|
|
|
}
|
2007-08-19 15:09:20 -04:00
|
|
|
|
2012-02-13 09:45:06 -05:00
|
|
|
if (priv->repeat_count != 0)
|
2009-06-11 05:55:52 -04:00
|
|
|
{
|
|
|
|
/* We try and interpolate smoothly around a loop */
|
|
|
|
if (saved_direction == CLUTTER_TIMELINE_FORWARD)
|
|
|
|
priv->elapsed_time = overflow_msecs - priv->duration;
|
|
|
|
else
|
|
|
|
priv->elapsed_time = priv->duration + overflow_msecs;
|
2008-03-06 20:00:00 -05:00
|
|
|
|
2009-06-11 05:55:52 -04:00
|
|
|
/* Or if the direction changed, we try and bounce */
|
|
|
|
if (priv->direction != saved_direction)
|
|
|
|
priv->elapsed_time = priv->duration - priv->elapsed_time;
|
|
|
|
|
2009-06-11 07:14:53 -04:00
|
|
|
/* If we have overflowed then we are changing the elapsed
|
|
|
|
time without emitting the new frame signal so we need to
|
|
|
|
check for markers again */
|
|
|
|
check_markers (timeline,
|
2010-11-17 10:27:42 -05:00
|
|
|
priv->direction == CLUTTER_TIMELINE_FORWARD
|
|
|
|
? priv->elapsed_time
|
|
|
|
: priv->duration - priv->elapsed_time);
|
2009-06-11 07:14:53 -04:00
|
|
|
|
2009-06-11 05:55:52 -04:00
|
|
|
g_object_unref (timeline);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
clutter_timeline_rewind (timeline);
|
|
|
|
|
|
|
|
g_object_unref (timeline);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2008-03-06 20:00:00 -05:00
|
|
|
}
|
|
|
|
}
|
2007-08-19 15:09:20 -04:00
|
|
|
|
2007-06-07 06:26:18 -04:00
|
|
|
static gboolean
|
|
|
|
delay_timeout_func (gpointer data)
|
|
|
|
{
|
|
|
|
ClutterTimeline *timeline = data;
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
|
|
|
|
priv->delay_id = 0;
|
2009-03-21 16:39:32 -04:00
|
|
|
priv->msecs_delta = 0;
|
2009-06-06 17:44:40 -04:00
|
|
|
set_is_playing (timeline, TRUE);
|
2007-10-12 04:17:00 -04:00
|
|
|
|
2007-06-07 06:26:18 -04:00
|
|
|
g_signal_emit (timeline, timeline_signals[STARTED], 0);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2006-05-29 04:59:36 -04:00
|
|
|
/**
|
|
|
|
* clutter_timeline_start:
|
|
|
|
* @timeline: A #ClutterTimeline
|
|
|
|
*
|
|
|
|
* Starts the #ClutterTimeline playing.
|
|
|
|
**/
|
|
|
|
void
|
|
|
|
clutter_timeline_start (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
2006-06-22 08:05:51 -04:00
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
2007-10-12 04:17:00 -04:00
|
|
|
|
2006-05-29 04:59:36 -04:00
|
|
|
priv = timeline->priv;
|
|
|
|
|
2009-03-21 16:39:32 -04:00
|
|
|
if (priv->delay_id || priv->is_playing)
|
2007-06-07 06:26:18 -04:00
|
|
|
return;
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
if (priv->duration == 0)
|
2007-10-16 09:41:34 -04:00
|
|
|
return;
|
|
|
|
|
2007-06-07 06:26:18 -04:00
|
|
|
if (priv->delay)
|
2009-03-21 16:39:32 -04:00
|
|
|
priv->delay_id = clutter_threads_add_timeout (priv->delay,
|
|
|
|
delay_timeout_func,
|
|
|
|
timeline);
|
2007-06-07 06:26:18 -04:00
|
|
|
else
|
|
|
|
{
|
2009-03-21 16:39:32 -04:00
|
|
|
priv->msecs_delta = 0;
|
2009-06-06 17:44:40 -04:00
|
|
|
set_is_playing (timeline, TRUE);
|
2007-06-09 09:20:00 -04:00
|
|
|
|
2007-06-07 06:26:18 -04:00
|
|
|
g_signal_emit (timeline, timeline_signals[STARTED], 0);
|
|
|
|
}
|
2006-05-29 04:59:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_pause:
|
|
|
|
* @timeline: A #ClutterTimeline
|
|
|
|
*
|
|
|
|
* Pauses the #ClutterTimeline on current frame
|
|
|
|
**/
|
|
|
|
void
|
|
|
|
clutter_timeline_pause (ClutterTimeline *timeline)
|
|
|
|
{
|
2007-06-07 06:26:18 -04:00
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
2006-05-29 04:59:36 -04:00
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
|
2007-06-07 06:26:18 -04:00
|
|
|
priv = timeline->priv;
|
|
|
|
|
2019-09-20 10:13:03 -04:00
|
|
|
clutter_timeline_cancel_delay (timeline);
|
2007-06-07 06:26:18 -04:00
|
|
|
|
2019-09-20 10:28:22 -04:00
|
|
|
if (!priv->is_playing)
|
|
|
|
return;
|
|
|
|
|
2009-03-21 16:39:32 -04:00
|
|
|
priv->msecs_delta = 0;
|
2009-06-06 17:44:40 -04:00
|
|
|
set_is_playing (timeline, FALSE);
|
2006-06-22 08:05:51 -04:00
|
|
|
|
2006-11-15 18:37:53 -05:00
|
|
|
g_signal_emit (timeline, timeline_signals[PAUSED], 0);
|
2006-05-29 04:59:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_stop:
|
|
|
|
* @timeline: A #ClutterTimeline
|
|
|
|
*
|
|
|
|
* Stops the #ClutterTimeline and moves to frame 0
|
|
|
|
**/
|
|
|
|
void
|
|
|
|
clutter_timeline_stop (ClutterTimeline *timeline)
|
|
|
|
{
|
2012-05-25 19:41:14 -04:00
|
|
|
gboolean was_playing;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
|
|
|
|
/* we check the is_playing here because pause() will return immediately
|
|
|
|
* if the timeline wasn't playing, so we don't know if it was actually
|
|
|
|
* stopped, and yet we still don't want to emit a ::stopped signal if
|
|
|
|
* the timeline was not playing in the first place.
|
|
|
|
*/
|
|
|
|
was_playing = timeline->priv->is_playing;
|
|
|
|
|
2006-05-29 04:59:36 -04:00
|
|
|
clutter_timeline_pause (timeline);
|
|
|
|
clutter_timeline_rewind (timeline);
|
2012-05-25 19:41:14 -04:00
|
|
|
|
|
|
|
if (was_playing)
|
|
|
|
g_signal_emit (timeline, timeline_signals[STOPPED], 0, FALSE);
|
2006-05-29 04:59:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_rewind:
|
|
|
|
* @timeline: A #ClutterTimeline
|
|
|
|
*
|
2008-02-04 09:35:47 -05:00
|
|
|
* Rewinds #ClutterTimeline to the first frame if its direction is
|
2009-01-27 05:27:33 -05:00
|
|
|
* %CLUTTER_TIMELINE_FORWARD and the last frame if it is
|
|
|
|
* %CLUTTER_TIMELINE_BACKWARD.
|
|
|
|
*/
|
2006-05-29 04:59:36 -04:00
|
|
|
void
|
|
|
|
clutter_timeline_rewind (ClutterTimeline *timeline)
|
|
|
|
{
|
2007-11-15 12:03:55 -05:00
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
2006-06-22 08:05:51 -04:00
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
2007-10-12 04:17:00 -04:00
|
|
|
|
2007-11-15 12:03:55 -05:00
|
|
|
priv = timeline->priv;
|
|
|
|
|
|
|
|
if (priv->direction == CLUTTER_TIMELINE_FORWARD)
|
|
|
|
clutter_timeline_advance (timeline, 0);
|
|
|
|
else if (priv->direction == CLUTTER_TIMELINE_BACKWARD)
|
2009-06-04 08:05:12 -04:00
|
|
|
clutter_timeline_advance (timeline, priv->duration);
|
2006-05-29 04:59:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2006-06-22 09:56:04 -04:00
|
|
|
* clutter_timeline_skip:
|
2006-05-29 04:59:36 -04:00
|
|
|
* @timeline: A #ClutterTimeline
|
2009-06-04 08:05:12 -04:00
|
|
|
* @msecs: Amount of time to skip
|
2006-05-29 04:59:36 -04:00
|
|
|
*
|
2009-06-04 08:05:12 -04:00
|
|
|
* Advance timeline by the requested time in milliseconds
|
2009-01-27 05:27:33 -05:00
|
|
|
*/
|
2006-05-29 04:59:36 -04:00
|
|
|
void
|
2006-11-15 18:37:53 -05:00
|
|
|
clutter_timeline_skip (ClutterTimeline *timeline,
|
2009-06-04 08:05:12 -04:00
|
|
|
guint msecs)
|
2006-05-29 04:59:36 -04:00
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
2007-11-15 12:03:55 -05:00
|
|
|
if (priv->direction == CLUTTER_TIMELINE_FORWARD)
|
|
|
|
{
|
2009-06-04 08:05:12 -04:00
|
|
|
priv->elapsed_time += msecs;
|
2007-11-15 12:03:55 -05:00
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
if (priv->elapsed_time > priv->duration)
|
|
|
|
priv->elapsed_time = 1;
|
2007-11-15 12:03:55 -05:00
|
|
|
}
|
|
|
|
else if (priv->direction == CLUTTER_TIMELINE_BACKWARD)
|
|
|
|
{
|
2009-06-04 08:05:12 -04:00
|
|
|
priv->elapsed_time -= msecs;
|
2007-11-15 12:03:55 -05:00
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
if (priv->elapsed_time < 1)
|
|
|
|
priv->elapsed_time = priv->duration - 1;
|
2007-11-15 12:03:55 -05:00
|
|
|
}
|
2009-03-21 16:39:32 -04:00
|
|
|
|
|
|
|
priv->msecs_delta = 0;
|
2006-05-29 04:59:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_advance:
|
|
|
|
* @timeline: A #ClutterTimeline
|
2009-06-04 08:05:12 -04:00
|
|
|
* @msecs: Time to advance to
|
2006-05-29 04:59:36 -04:00
|
|
|
*
|
2009-06-04 08:05:12 -04:00
|
|
|
* Advance timeline to the requested point. The point is given as a
|
|
|
|
* time in milliseconds since the timeline started.
|
2009-01-27 06:18:09 -05:00
|
|
|
*
|
2014-03-17 19:07:58 -04:00
|
|
|
* The @timeline will not emit the #ClutterTimeline::new-frame
|
2009-06-04 08:05:12 -04:00
|
|
|
* signal for the given time. The first ::new-frame signal after the call to
|
|
|
|
* clutter_timeline_advance() will be emit the skipped markers.
|
2009-01-27 05:27:33 -05:00
|
|
|
*/
|
2006-05-29 04:59:36 -04:00
|
|
|
void
|
2006-11-15 18:37:53 -05:00
|
|
|
clutter_timeline_advance (ClutterTimeline *timeline,
|
2009-06-04 08:05:12 -04:00
|
|
|
guint msecs)
|
2006-05-29 04:59:36 -04:00
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
priv->elapsed_time = CLAMP (msecs, 0, priv->duration);
|
2006-05-29 04:59:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-06-04 08:05:12 -04:00
|
|
|
* clutter_timeline_get_elapsed_time:
|
2006-05-29 04:59:36 -04:00
|
|
|
* @timeline: A #ClutterTimeline
|
|
|
|
*
|
2009-06-04 08:05:12 -04:00
|
|
|
* Request the current time position of the timeline.
|
2006-05-29 04:59:36 -04:00
|
|
|
*
|
2009-06-04 08:05:12 -04:00
|
|
|
* Return value: current elapsed time in milliseconds.
|
2009-01-27 05:27:33 -05:00
|
|
|
*/
|
2006-05-29 04:59:36 -04:00
|
|
|
guint
|
2009-06-04 08:05:12 -04:00
|
|
|
clutter_timeline_get_elapsed_time (ClutterTimeline *timeline)
|
2006-05-29 04:59:36 -04:00
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
return timeline->priv->elapsed_time;
|
2006-11-15 18:37:53 -05:00
|
|
|
}
|
|
|
|
|
2006-06-01 17:37:28 -04:00
|
|
|
/**
|
|
|
|
* clutter_timeline_is_playing:
|
|
|
|
* @timeline: A #ClutterTimeline
|
|
|
|
*
|
2009-01-27 05:27:33 -05:00
|
|
|
* Queries state of a #ClutterTimeline.
|
2006-06-01 17:37:28 -04:00
|
|
|
*
|
2009-01-27 05:27:33 -05:00
|
|
|
* Return value: %TRUE if timeline is currently playing
|
2006-06-01 17:37:28 -04:00
|
|
|
*/
|
|
|
|
gboolean
|
|
|
|
clutter_timeline_is_playing (ClutterTimeline *timeline)
|
|
|
|
{
|
2006-06-22 08:05:51 -04:00
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
|
2007-10-12 04:17:00 -04:00
|
|
|
|
2009-03-21 16:39:32 -04:00
|
|
|
return timeline->priv->is_playing;
|
2006-06-01 17:37:28 -04:00
|
|
|
}
|
|
|
|
|
2007-10-16 09:41:34 -04:00
|
|
|
/**
|
2009-06-09 09:47:36 -04:00
|
|
|
* clutter_timeline_new:
|
2020-04-17 02:51:27 -04:00
|
|
|
* @duration_ms: Duration of the timeline in milliseconds
|
2007-10-16 09:41:34 -04:00
|
|
|
*
|
2020-04-17 02:51:27 -04:00
|
|
|
* Creates a new #ClutterTimeline with a duration of @duration_ms milli seconds.
|
2007-10-16 09:41:34 -04:00
|
|
|
*
|
2009-05-06 11:42:57 -04:00
|
|
|
* Return value: the newly created #ClutterTimeline instance. Use
|
|
|
|
* g_object_unref() when done using it
|
2007-10-16 09:41:34 -04:00
|
|
|
*
|
|
|
|
* Since: 0.6
|
|
|
|
*/
|
|
|
|
ClutterTimeline *
|
2020-04-17 02:51:27 -04:00
|
|
|
clutter_timeline_new (guint duration_ms)
|
2007-10-16 09:41:34 -04:00
|
|
|
{
|
|
|
|
return g_object_new (CLUTTER_TYPE_TIMELINE,
|
2020-04-17 02:51:27 -04:00
|
|
|
"duration", duration_ms,
|
2007-10-16 09:41:34 -04:00
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
2020-04-17 03:00:18 -04:00
|
|
|
/**
|
|
|
|
* clutter_timeline_new_for_actor:
|
|
|
|
* @actor: The #ClutterActor the timeline is associated with
|
|
|
|
* @duration_ms: Duration of the timeline in milliseconds
|
|
|
|
*
|
|
|
|
* Creates a new #ClutterTimeline with a duration of @duration milli seconds.
|
|
|
|
*
|
|
|
|
* Return value: the newly created #ClutterTimeline instance. Use
|
|
|
|
* g_object_unref() when done using it
|
|
|
|
*/
|
|
|
|
ClutterTimeline *
|
|
|
|
clutter_timeline_new_for_actor (ClutterActor *actor,
|
|
|
|
unsigned int duration_ms)
|
|
|
|
{
|
|
|
|
return g_object_new (CLUTTER_TYPE_TIMELINE,
|
|
|
|
"duration", duration_ms,
|
|
|
|
"actor", actor,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
2020-03-25 13:16:39 -04:00
|
|
|
/**
|
|
|
|
* clutter_timeline_new_for_frame_clock:
|
|
|
|
* @frame_clock: The #ClutterFrameClock the timeline is driven by
|
|
|
|
* @duration_ms: Duration of the timeline in milliseconds
|
|
|
|
*
|
2020-04-17 03:00:18 -04:00
|
|
|
* Creates a new #ClutterTimeline with a duration of @duration_ms milli seconds.
|
2020-03-25 13:16:39 -04:00
|
|
|
*
|
|
|
|
* Return value: the newly created #ClutterTimeline instance. Use
|
|
|
|
* g_object_unref() when done using it
|
|
|
|
*/
|
|
|
|
ClutterTimeline *
|
|
|
|
clutter_timeline_new_for_frame_clock (ClutterFrameClock *frame_clock,
|
|
|
|
unsigned int duration_ms)
|
|
|
|
{
|
|
|
|
return g_object_new (CLUTTER_TYPE_TIMELINE,
|
|
|
|
"duration", duration_ms,
|
|
|
|
"frame-clock", frame_clock,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
2007-06-07 06:26:18 -04:00
|
|
|
/**
|
|
|
|
* clutter_timeline_get_delay:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
*
|
|
|
|
* Retrieves the delay set using clutter_timeline_set_delay().
|
|
|
|
*
|
|
|
|
* Return value: the delay in milliseconds.
|
|
|
|
*
|
|
|
|
* Since: 0.4
|
|
|
|
*/
|
|
|
|
guint
|
|
|
|
clutter_timeline_get_delay (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
|
|
|
|
|
|
|
|
return timeline->priv->delay;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_set_delay:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @msecs: delay in milliseconds
|
|
|
|
*
|
|
|
|
* Sets the delay, in milliseconds, before @timeline should start.
|
|
|
|
*
|
|
|
|
* Since: 0.4
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_timeline_set_delay (ClutterTimeline *timeline,
|
|
|
|
guint msecs)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
|
|
|
if (priv->delay != msecs)
|
|
|
|
{
|
|
|
|
priv->delay = msecs;
|
2011-03-03 06:35:46 -05:00
|
|
|
g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_DELAY]);
|
2007-06-07 06:26:18 -04:00
|
|
|
}
|
|
|
|
}
|
2007-10-16 09:41:34 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_get_duration:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
*
|
|
|
|
* Retrieves the duration of a #ClutterTimeline in milliseconds.
|
|
|
|
* See clutter_timeline_set_duration().
|
|
|
|
*
|
|
|
|
* Return value: the duration of the timeline, in milliseconds.
|
|
|
|
*
|
|
|
|
* Since: 0.6
|
|
|
|
*/
|
|
|
|
guint
|
|
|
|
clutter_timeline_get_duration (ClutterTimeline *timeline)
|
|
|
|
{
|
2008-08-01 07:09:43 -04:00
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
2007-10-16 09:41:34 -04:00
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
|
|
|
|
|
2008-08-01 07:09:43 -04:00
|
|
|
priv = timeline->priv;
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
return priv->duration;
|
2007-10-16 09:41:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_set_duration:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @msecs: duration of the timeline in milliseconds
|
|
|
|
*
|
|
|
|
* Sets the duration of the timeline, in milliseconds. The speed
|
|
|
|
* of the timeline depends on the ClutterTimeline:fps setting.
|
|
|
|
*
|
|
|
|
* Since: 0.6
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_timeline_set_duration (ClutterTimeline *timeline,
|
|
|
|
guint msecs)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
2009-06-04 08:05:12 -04:00
|
|
|
g_return_if_fail (msecs > 0);
|
2007-10-16 09:41:34 -04:00
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
if (priv->duration != msecs)
|
|
|
|
{
|
|
|
|
priv->duration = msecs;
|
2007-10-16 09:41:34 -04:00
|
|
|
|
2011-03-03 06:35:46 -05:00
|
|
|
g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_DURATION]);
|
2009-06-04 08:05:12 -04:00
|
|
|
}
|
2007-10-16 09:41:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_get_progress:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
*
|
2012-02-17 11:06:28 -05:00
|
|
|
* The position of the timeline in a normalized [-1, 2] interval.
|
|
|
|
*
|
|
|
|
* The return value of this function is determined by the progress
|
|
|
|
* mode set using clutter_timeline_set_progress_mode(), or by the
|
|
|
|
* progress function set using clutter_timeline_set_progress_func().
|
2007-10-16 09:41:34 -04:00
|
|
|
*
|
2012-02-17 11:06:28 -05:00
|
|
|
* Return value: the normalized current position in the timeline.
|
2007-10-16 09:41:34 -04:00
|
|
|
*
|
|
|
|
* Since: 0.6
|
|
|
|
*/
|
|
|
|
gdouble
|
|
|
|
clutter_timeline_get_progress (ClutterTimeline *timeline)
|
|
|
|
{
|
2009-03-09 13:05:13 -04:00
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0.0);
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
2007-10-16 09:41:34 -04:00
|
|
|
|
2012-02-17 11:06:28 -05:00
|
|
|
/* short-circuit linear progress */
|
|
|
|
if (priv->progress_func == NULL)
|
|
|
|
return (gdouble) priv->elapsed_time / (gdouble) priv->duration;
|
|
|
|
else
|
|
|
|
return priv->progress_func (timeline,
|
|
|
|
(gdouble) priv->elapsed_time,
|
|
|
|
(gdouble) priv->duration,
|
|
|
|
priv->progress_data);
|
2007-10-16 09:41:34 -04:00
|
|
|
}
|
|
|
|
|
2007-11-15 12:03:55 -05:00
|
|
|
/**
|
|
|
|
* clutter_timeline_get_direction:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
*
|
|
|
|
* Retrieves the direction of the timeline set with
|
|
|
|
* clutter_timeline_set_direction().
|
|
|
|
*
|
|
|
|
* Return value: the direction of the timeline
|
|
|
|
*
|
|
|
|
* Since: 0.6
|
|
|
|
*/
|
|
|
|
ClutterTimelineDirection
|
|
|
|
clutter_timeline_get_direction (ClutterTimeline *timeline)
|
|
|
|
{
|
2009-03-09 13:05:13 -04:00
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline),
|
|
|
|
CLUTTER_TIMELINE_FORWARD);
|
2007-11-15 12:03:55 -05:00
|
|
|
|
|
|
|
return timeline->priv->direction;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_set_direction:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @direction: the direction of the timeline
|
|
|
|
*
|
|
|
|
* Sets the direction of @timeline, either %CLUTTER_TIMELINE_FORWARD or
|
|
|
|
* %CLUTTER_TIMELINE_BACKWARD.
|
|
|
|
*
|
|
|
|
* Since: 0.6
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_timeline_set_direction (ClutterTimeline *timeline,
|
|
|
|
ClutterTimelineDirection direction)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
|
|
|
if (priv->direction != direction)
|
|
|
|
{
|
|
|
|
priv->direction = direction;
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
if (priv->elapsed_time == 0)
|
|
|
|
priv->elapsed_time = priv->duration;
|
2007-11-15 12:03:55 -05:00
|
|
|
|
2011-03-03 06:35:46 -05:00
|
|
|
g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_DIRECTION]);
|
2007-11-15 12:03:55 -05:00
|
|
|
}
|
2007-10-16 09:41:34 -04:00
|
|
|
}
|
2007-11-30 08:20:15 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_get_delta:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
*
|
2009-06-04 08:05:12 -04:00
|
|
|
* Retrieves the amount of time elapsed since the last
|
|
|
|
* ClutterTimeline::new-frame signal.
|
2007-11-30 08:20:15 -05:00
|
|
|
*
|
|
|
|
* This function is only useful inside handlers for the ::new-frame
|
|
|
|
* signal, and its behaviour is undefined if the timeline is not
|
|
|
|
* playing.
|
|
|
|
*
|
2009-06-04 08:05:12 -04:00
|
|
|
* Return value: the amount of time in milliseconds elapsed since the
|
|
|
|
* last frame
|
2007-11-30 08:20:15 -05:00
|
|
|
*
|
|
|
|
* Since: 0.6
|
|
|
|
*/
|
|
|
|
guint
|
2009-06-04 08:05:12 -04:00
|
|
|
clutter_timeline_get_delta (ClutterTimeline *timeline)
|
2007-11-30 08:20:15 -05:00
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
|
|
|
|
|
|
|
|
if (!clutter_timeline_is_playing (timeline))
|
2009-06-04 08:05:12 -04:00
|
|
|
return 0;
|
2007-11-30 08:20:15 -05:00
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
return timeline->priv->msecs_delta;
|
2007-11-30 08:20:15 -05:00
|
|
|
}
|
2008-03-18 13:50:45 -04:00
|
|
|
|
2012-03-21 12:07:29 -04:00
|
|
|
void
|
|
|
|
_clutter_timeline_advance (ClutterTimeline *timeline,
|
|
|
|
gint64 tick_time)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
|
|
|
|
g_object_ref (timeline);
|
|
|
|
|
2015-09-17 11:27:14 -04:00
|
|
|
CLUTTER_NOTE (SCHEDULER,
|
|
|
|
"Timeline [%p] advancing (cur: %ld, tot: %ld, "
|
|
|
|
"tick_time: %lu)",
|
|
|
|
timeline,
|
|
|
|
(long) priv->elapsed_time,
|
|
|
|
(long) priv->msecs_delta,
|
|
|
|
(long) tick_time);
|
|
|
|
|
2012-03-21 12:07:29 -04:00
|
|
|
priv->msecs_delta = tick_time;
|
|
|
|
priv->is_playing = TRUE;
|
|
|
|
|
|
|
|
clutter_timeline_do_frame (timeline);
|
|
|
|
|
|
|
|
priv->is_playing = FALSE;
|
|
|
|
|
|
|
|
g_object_unref (timeline);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*< private >
|
2009-06-08 13:00:09 -04:00
|
|
|
* clutter_timeline_do_tick
|
2009-03-21 16:39:32 -04:00
|
|
|
* @timeline: a #ClutterTimeline
|
2009-06-08 13:00:09 -04:00
|
|
|
* @tick_time: time of advance
|
2009-03-21 16:39:32 -04:00
|
|
|
*
|
2010-11-07 10:57:33 -05:00
|
|
|
* Advances @timeline based on the time passed in @tick_time. This
|
2009-06-08 13:00:09 -04:00
|
|
|
* function is called by the master clock. The @timeline will use this
|
|
|
|
* interval to emit the #ClutterTimeline::new-frame signal and
|
|
|
|
* eventually skip frames.
|
2009-03-21 16:39:32 -04:00
|
|
|
*/
|
2009-06-03 09:03:25 -04:00
|
|
|
void
|
2010-11-07 10:57:33 -05:00
|
|
|
_clutter_timeline_do_tick (ClutterTimeline *timeline,
|
|
|
|
gint64 tick_time)
|
2009-03-21 16:39:32 -04:00
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
2015-09-17 11:27:14 -04:00
|
|
|
CLUTTER_NOTE (SCHEDULER,
|
|
|
|
"Timeline [%p] ticked (elapsed_time: %ld, msecs_delta: %ld, "
|
|
|
|
"last_frame_time: %ld, tick_time: %ld)",
|
|
|
|
timeline,
|
|
|
|
(long) priv->elapsed_time,
|
|
|
|
(long) priv->msecs_delta,
|
|
|
|
(long) priv->last_frame_time,
|
|
|
|
(long) tick_time);
|
|
|
|
|
2011-01-18 10:48:29 -05:00
|
|
|
/* Check the is_playing variable before performing the timeline tick.
|
|
|
|
* This is necessary, as if a timeline is stopped in response to a
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
* frame clock generated signal of a different timeline, this code can
|
2011-01-18 10:48:29 -05:00
|
|
|
* still be reached.
|
|
|
|
*/
|
|
|
|
if (!priv->is_playing)
|
|
|
|
return;
|
|
|
|
|
2009-06-08 13:00:09 -04:00
|
|
|
if (priv->waiting_first_tick)
|
|
|
|
{
|
2010-11-07 10:57:33 -05:00
|
|
|
priv->last_frame_time = tick_time;
|
2011-01-24 06:29:59 -05:00
|
|
|
priv->msecs_delta = 0;
|
2009-06-08 13:00:09 -04:00
|
|
|
priv->waiting_first_tick = FALSE;
|
2011-01-24 06:29:59 -05:00
|
|
|
clutter_timeline_do_frame (timeline);
|
2009-06-08 13:00:09 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-09-16 06:55:04 -04:00
|
|
|
gint64 msecs;
|
|
|
|
|
2010-11-07 10:57:33 -05:00
|
|
|
msecs = tick_time - priv->last_frame_time;
|
2009-09-16 06:55:04 -04:00
|
|
|
|
|
|
|
/* if the clock rolled back between ticks we need to
|
|
|
|
* account for it; the best course of action, since the
|
|
|
|
* clock roll back can happen by any arbitrary amount
|
|
|
|
* of milliseconds, is to drop a frame here
|
|
|
|
*/
|
|
|
|
if (msecs < 0)
|
|
|
|
{
|
2010-11-07 10:57:33 -05:00
|
|
|
priv->last_frame_time = tick_time;
|
2009-09-16 06:55:04 -04:00
|
|
|
return;
|
|
|
|
}
|
2009-03-21 16:39:32 -04:00
|
|
|
|
2009-06-08 13:00:09 -04:00
|
|
|
if (msecs != 0)
|
2019-12-07 05:56:02 -05:00
|
|
|
{
|
|
|
|
/* Avoid accumulating error */
|
2010-11-07 10:57:33 -05:00
|
|
|
priv->last_frame_time += msecs;
|
2019-12-07 05:56:02 -05:00
|
|
|
priv->msecs_delta = msecs;
|
|
|
|
clutter_timeline_do_frame (timeline);
|
|
|
|
}
|
2009-06-08 13:00:09 -04:00
|
|
|
}
|
2009-03-21 16:39:32 -04:00
|
|
|
}
|
|
|
|
|
2013-02-20 17:14:46 -05:00
|
|
|
/**
|
|
|
|
* clutter_timeline_add_marker:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @marker_name: the unique name for this marker
|
|
|
|
* @progress: the normalized value of the position of the martke
|
|
|
|
*
|
|
|
|
* Adds a named marker that will be hit when the timeline has reached
|
|
|
|
* the specified @progress.
|
|
|
|
*
|
|
|
|
* Markers are unique string identifiers for a given position on the
|
|
|
|
* timeline. Once @timeline reaches the given @progress of its duration,
|
|
|
|
* if will emit a ::marker-reached signal for each marker attached to
|
|
|
|
* that particular point.
|
|
|
|
*
|
|
|
|
* A marker can be removed with clutter_timeline_remove_marker(). The
|
|
|
|
* timeline can be advanced to a marker using
|
|
|
|
* clutter_timeline_advance_to_marker().
|
|
|
|
*
|
|
|
|
* See also: clutter_timeline_add_marker_at_time()
|
|
|
|
*
|
|
|
|
* Since: 1.14
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_timeline_add_marker (ClutterTimeline *timeline,
|
|
|
|
const gchar *marker_name,
|
|
|
|
gdouble progress)
|
|
|
|
{
|
|
|
|
TimelineMarker *marker;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
g_return_if_fail (marker_name != NULL);
|
|
|
|
|
|
|
|
marker = timeline_marker_new_progress (marker_name, progress);
|
|
|
|
clutter_timeline_add_marker_internal (timeline, marker);
|
|
|
|
}
|
|
|
|
|
2008-03-18 13:50:45 -04:00
|
|
|
/**
|
2009-06-04 08:05:12 -04:00
|
|
|
* clutter_timeline_add_marker_at_time:
|
2008-03-18 13:50:45 -04:00
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @marker_name: the unique name for this marker
|
2009-06-04 08:05:12 -04:00
|
|
|
* @msecs: position of the marker in milliseconds
|
2008-03-18 13:50:45 -04:00
|
|
|
*
|
2009-06-04 08:05:12 -04:00
|
|
|
* Adds a named marker that will be hit when the timeline has been
|
2013-02-20 17:14:46 -05:00
|
|
|
* running for @msecs milliseconds.
|
|
|
|
*
|
|
|
|
* Markers are unique string identifiers for a given position on the
|
|
|
|
* timeline. Once @timeline reaches the given @msecs, it will emit
|
|
|
|
* a ::marker-reached signal for each marker attached to that position.
|
2008-03-18 13:50:45 -04:00
|
|
|
*
|
|
|
|
* A marker can be removed with clutter_timeline_remove_marker(). The
|
|
|
|
* timeline can be advanced to a marker using
|
|
|
|
* clutter_timeline_advance_to_marker().
|
|
|
|
*
|
2013-02-20 17:14:46 -05:00
|
|
|
* See also: clutter_timeline_add_marker()
|
|
|
|
*
|
2008-03-18 13:50:45 -04:00
|
|
|
* Since: 0.8
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_timeline_add_marker_at_time (ClutterTimeline *timeline,
|
|
|
|
const gchar *marker_name,
|
|
|
|
guint msecs)
|
|
|
|
{
|
2011-11-27 07:16:32 -05:00
|
|
|
TimelineMarker *marker;
|
|
|
|
|
2008-03-18 13:50:45 -04:00
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
g_return_if_fail (marker_name != NULL);
|
|
|
|
g_return_if_fail (msecs <= clutter_timeline_get_duration (timeline));
|
|
|
|
|
2013-02-20 17:14:46 -05:00
|
|
|
marker = timeline_marker_new_time (marker_name, msecs);
|
2011-11-27 07:16:32 -05:00
|
|
|
clutter_timeline_add_marker_internal (timeline, marker);
|
2009-06-04 08:05:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
struct CollectMarkersClosure
|
|
|
|
{
|
2013-02-20 17:14:46 -05:00
|
|
|
guint duration;
|
2009-06-04 08:05:12 -04:00
|
|
|
guint msecs;
|
|
|
|
GArray *markers;
|
|
|
|
};
|
2008-03-18 13:50:45 -04:00
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
static void
|
|
|
|
collect_markers (const gchar *key,
|
|
|
|
TimelineMarker *marker,
|
|
|
|
struct CollectMarkersClosure *data)
|
|
|
|
{
|
2013-02-20 17:14:46 -05:00
|
|
|
guint msecs;
|
|
|
|
|
|
|
|
if (marker->is_relative)
|
|
|
|
msecs = marker->data.progress * data->duration;
|
|
|
|
else
|
|
|
|
msecs = marker->data.msecs;
|
|
|
|
|
|
|
|
if (msecs == data->msecs)
|
2009-06-04 08:05:12 -04:00
|
|
|
{
|
|
|
|
gchar *name_copy = g_strdup (key);
|
|
|
|
g_array_append_val (data->markers, name_copy);
|
|
|
|
}
|
2008-03-18 13:50:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_list_markers:
|
|
|
|
* @timeline: a #ClutterTimeline
|
2009-06-04 08:05:12 -04:00
|
|
|
* @msecs: the time to check, or -1
|
2008-03-18 13:50:45 -04:00
|
|
|
* @n_markers: the number of markers returned
|
|
|
|
*
|
2011-09-02 08:54:57 -04:00
|
|
|
* Retrieves the list of markers at time @msecs. If @msecs is a
|
2008-03-18 13:50:45 -04:00
|
|
|
* negative integer, all the markers attached to @timeline will be
|
|
|
|
* returned.
|
|
|
|
*
|
2010-09-03 07:14:50 -04:00
|
|
|
* Return value: (transfer full) (array zero-terminated=1 length=n_markers):
|
|
|
|
* a newly allocated, %NULL terminated string array containing the names
|
|
|
|
* of the markers. Use g_strfreev() when done.
|
2008-03-18 13:50:45 -04:00
|
|
|
*
|
|
|
|
* Since: 0.8
|
|
|
|
*/
|
|
|
|
gchar **
|
|
|
|
clutter_timeline_list_markers (ClutterTimeline *timeline,
|
2009-06-04 08:05:12 -04:00
|
|
|
gint msecs,
|
2008-06-10 02:37:46 -04:00
|
|
|
gsize *n_markers)
|
2008-03-18 13:50:45 -04:00
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
gchar **retval = NULL;
|
2008-06-10 02:37:46 -04:00
|
|
|
gsize i;
|
2008-03-18 13:50:45 -04:00
|
|
|
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
if (G_UNLIKELY (priv->markers_by_name == NULL))
|
2009-01-23 12:17:36 -05:00
|
|
|
{
|
2009-02-20 06:36:54 -05:00
|
|
|
if (n_markers)
|
|
|
|
*n_markers = 0;
|
|
|
|
|
2009-01-23 12:17:36 -05:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
if (msecs < 0)
|
2008-03-18 13:50:45 -04:00
|
|
|
{
|
|
|
|
GList *markers, *l;
|
|
|
|
|
|
|
|
markers = g_hash_table_get_keys (priv->markers_by_name);
|
|
|
|
retval = g_new0 (gchar*, g_list_length (markers) + 1);
|
|
|
|
|
|
|
|
for (i = 0, l = markers; l != NULL; i++, l = l->next)
|
|
|
|
retval[i] = g_strdup (l->data);
|
|
|
|
|
|
|
|
g_list_free (markers);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-06-04 08:05:12 -04:00
|
|
|
struct CollectMarkersClosure data;
|
2008-03-18 13:50:45 -04:00
|
|
|
|
2013-02-20 17:14:46 -05:00
|
|
|
data.duration = priv->duration;
|
2009-06-04 08:05:12 -04:00
|
|
|
data.msecs = msecs;
|
|
|
|
data.markers = g_array_new (TRUE, FALSE, sizeof (gchar *));
|
2008-03-18 13:50:45 -04:00
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
g_hash_table_foreach (priv->markers_by_name,
|
|
|
|
(GHFunc) collect_markers,
|
|
|
|
&data);
|
|
|
|
|
|
|
|
i = data.markers->len;
|
2012-01-16 18:49:49 -05:00
|
|
|
retval = (gchar **) (void *) g_array_free (data.markers, FALSE);
|
2008-03-18 13:50:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (n_markers)
|
|
|
|
*n_markers = i;
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_advance_to_marker:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @marker_name: the name of the marker
|
|
|
|
*
|
2009-06-04 08:05:12 -04:00
|
|
|
* Advances @timeline to the time of the given @marker_name.
|
2008-03-18 13:50:45 -04:00
|
|
|
*
|
2014-03-17 19:07:58 -04:00
|
|
|
* Like clutter_timeline_advance(), this function will not
|
2009-06-04 08:05:12 -04:00
|
|
|
* emit the #ClutterTimeline::new-frame for the time where @marker_name
|
2009-01-27 06:25:06 -05:00
|
|
|
* is set, nor it will emit #ClutterTimeline::marker-reached for
|
2014-03-17 19:07:58 -04:00
|
|
|
* @marker_name.
|
2009-01-27 06:25:06 -05:00
|
|
|
*
|
2008-03-18 13:50:45 -04:00
|
|
|
* Since: 0.8
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_timeline_advance_to_marker (ClutterTimeline *timeline,
|
|
|
|
const gchar *marker_name)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
TimelineMarker *marker;
|
2013-02-20 17:14:46 -05:00
|
|
|
guint msecs;
|
2008-03-18 13:50:45 -04:00
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
g_return_if_fail (marker_name != NULL);
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
if (G_UNLIKELY (priv->markers_by_name == NULL))
|
2009-01-23 12:17:36 -05:00
|
|
|
{
|
2009-03-17 10:12:01 -04:00
|
|
|
g_warning ("No marker named '%s' found.", marker_name);
|
2009-01-23 12:17:36 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-03-18 13:50:45 -04:00
|
|
|
marker = g_hash_table_lookup (priv->markers_by_name, marker_name);
|
2013-02-20 17:14:46 -05:00
|
|
|
if (marker == NULL)
|
2008-03-18 13:50:45 -04:00
|
|
|
{
|
2009-03-17 10:12:01 -04:00
|
|
|
g_warning ("No marker named '%s' found.", marker_name);
|
2008-03-18 13:50:45 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-02-20 17:14:46 -05:00
|
|
|
if (marker->is_relative)
|
|
|
|
msecs = marker->data.progress * priv->duration;
|
|
|
|
else
|
|
|
|
msecs = marker->data.msecs;
|
|
|
|
|
|
|
|
clutter_timeline_advance (timeline, msecs);
|
2008-03-18 13:50:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_remove_marker:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @marker_name: the name of the marker to remove
|
|
|
|
*
|
|
|
|
* Removes @marker_name, if found, from @timeline.
|
|
|
|
*
|
|
|
|
* Since: 0.8
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_timeline_remove_marker (ClutterTimeline *timeline,
|
|
|
|
const gchar *marker_name)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
TimelineMarker *marker;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
g_return_if_fail (marker_name != NULL);
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
if (G_UNLIKELY (priv->markers_by_name == NULL))
|
2009-01-23 12:17:36 -05:00
|
|
|
{
|
2009-03-17 10:12:01 -04:00
|
|
|
g_warning ("No marker named '%s' found.", marker_name);
|
2009-01-23 12:17:36 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-03-18 13:50:45 -04:00
|
|
|
marker = g_hash_table_lookup (priv->markers_by_name, marker_name);
|
|
|
|
if (!marker)
|
|
|
|
{
|
2009-03-17 10:12:01 -04:00
|
|
|
g_warning ("No marker named '%s' found.", marker_name);
|
2008-03-18 13:50:45 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* this will take care of freeing the marker as well */
|
|
|
|
g_hash_table_remove (priv->markers_by_name, marker_name);
|
|
|
|
}
|
2008-03-18 18:07:17 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_has_marker:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @marker_name: the name of the marker
|
|
|
|
*
|
|
|
|
* Checks whether @timeline has a marker set with the given name.
|
|
|
|
*
|
|
|
|
* Return value: %TRUE if the marker was found
|
|
|
|
*
|
|
|
|
* Since: 0.8
|
|
|
|
*/
|
|
|
|
gboolean
|
|
|
|
clutter_timeline_has_marker (ClutterTimeline *timeline,
|
|
|
|
const gchar *marker_name)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
|
|
|
|
g_return_val_if_fail (marker_name != NULL, FALSE);
|
|
|
|
|
2009-06-04 08:05:12 -04:00
|
|
|
if (G_UNLIKELY (timeline->priv->markers_by_name == NULL))
|
2009-01-23 12:17:36 -05:00
|
|
|
return FALSE;
|
|
|
|
|
2008-03-18 18:07:17 -04:00
|
|
|
return NULL != g_hash_table_lookup (timeline->priv->markers_by_name,
|
|
|
|
marker_name);
|
|
|
|
}
|
2010-11-17 10:27:42 -05:00
|
|
|
|
|
|
|
/**
|
2010-12-17 07:04:11 -05:00
|
|
|
* clutter_timeline_set_auto_reverse:
|
2010-11-17 10:27:42 -05:00
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @reverse: %TRUE if the @timeline should reverse the direction
|
|
|
|
*
|
|
|
|
* Sets whether @timeline should reverse the direction after the
|
|
|
|
* emission of the #ClutterTimeline::completed signal.
|
|
|
|
*
|
2011-09-02 10:49:05 -04:00
|
|
|
* Setting the #ClutterTimeline:auto-reverse property to %TRUE is the
|
2010-11-17 10:27:42 -05:00
|
|
|
* equivalent of connecting a callback to the #ClutterTimeline::completed
|
|
|
|
* signal and changing the direction of the timeline from that callback;
|
|
|
|
* for instance, this code:
|
|
|
|
*
|
|
|
|
* |[
|
|
|
|
* static void
|
|
|
|
* reverse_timeline (ClutterTimeline *timeline)
|
|
|
|
* {
|
|
|
|
* ClutterTimelineDirection dir = clutter_timeline_get_direction (timeline);
|
|
|
|
*
|
|
|
|
* if (dir == CLUTTER_TIMELINE_FORWARD)
|
|
|
|
* dir = CLUTTER_TIMELINE_BACKWARD;
|
|
|
|
* else
|
|
|
|
* dir = CLUTTER_TIMELINE_FORWARD;
|
|
|
|
*
|
|
|
|
* clutter_timeline_set_direction (timeline, dir);
|
|
|
|
* }
|
|
|
|
* ...
|
|
|
|
* timeline = clutter_timeline_new (1000);
|
2012-02-13 09:45:06 -05:00
|
|
|
* clutter_timeline_set_repeat_count (timeline, -1);
|
2010-11-17 10:27:42 -05:00
|
|
|
* g_signal_connect (timeline, "completed",
|
|
|
|
* G_CALLBACK (reverse_timeline),
|
|
|
|
* NULL);
|
|
|
|
* ]|
|
|
|
|
*
|
|
|
|
* can be effectively replaced by:
|
|
|
|
*
|
|
|
|
* |[
|
|
|
|
* timeline = clutter_timeline_new (1000);
|
2012-02-13 09:45:06 -05:00
|
|
|
* clutter_timeline_set_repeat_count (timeline, -1);
|
2010-12-17 07:04:11 -05:00
|
|
|
* clutter_timeline_set_auto_reverse (timeline);
|
2010-11-17 10:27:42 -05:00
|
|
|
* ]|
|
|
|
|
*
|
|
|
|
* Since: 1.6
|
|
|
|
*/
|
|
|
|
void
|
2010-12-17 07:04:11 -05:00
|
|
|
clutter_timeline_set_auto_reverse (ClutterTimeline *timeline,
|
|
|
|
gboolean reverse)
|
2010-11-17 10:27:42 -05:00
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
|
|
|
|
reverse = !!reverse;
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
2010-12-17 07:04:11 -05:00
|
|
|
if (priv->auto_reverse != reverse)
|
2010-11-17 10:27:42 -05:00
|
|
|
{
|
2010-12-17 07:04:11 -05:00
|
|
|
priv->auto_reverse = reverse;
|
2010-11-17 10:27:42 -05:00
|
|
|
|
2011-03-03 06:35:46 -05:00
|
|
|
g_object_notify_by_pspec (G_OBJECT (timeline),
|
2010-12-17 07:04:11 -05:00
|
|
|
obj_props[PROP_AUTO_REVERSE]);
|
2010-11-17 10:27:42 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-12-17 07:04:11 -05:00
|
|
|
* clutter_timeline_get_auto_reverse:
|
2010-11-17 10:27:42 -05:00
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
*
|
2010-12-17 07:04:11 -05:00
|
|
|
* Retrieves the value set by clutter_timeline_set_auto_reverse().
|
2010-11-17 10:27:42 -05:00
|
|
|
*
|
2010-12-17 07:04:11 -05:00
|
|
|
* Return value: %TRUE if the timeline should automatically reverse, and
|
2010-11-17 10:27:42 -05:00
|
|
|
* %FALSE otherwise
|
|
|
|
*
|
|
|
|
* Since: 1.6
|
|
|
|
*/
|
|
|
|
gboolean
|
2010-12-17 07:04:11 -05:00
|
|
|
clutter_timeline_get_auto_reverse (ClutterTimeline *timeline)
|
2010-11-17 10:27:42 -05:00
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
|
|
|
|
|
2010-12-17 07:04:11 -05:00
|
|
|
return timeline->priv->auto_reverse;
|
2010-11-17 10:27:42 -05:00
|
|
|
}
|
2012-02-13 09:45:06 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_set_repeat_count:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @count: the number of times the timeline should repeat
|
|
|
|
*
|
|
|
|
* Sets the number of times the @timeline should repeat.
|
|
|
|
*
|
|
|
|
* If @count is 0, the timeline never repeats.
|
|
|
|
*
|
|
|
|
* If @count is -1, the timeline will always repeat until
|
|
|
|
* it's stopped.
|
|
|
|
*
|
|
|
|
* Since: 1.10
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_timeline_set_repeat_count (ClutterTimeline *timeline,
|
|
|
|
gint count)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
g_return_if_fail (count >= -1);
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
|
|
|
if (priv->repeat_count != count)
|
|
|
|
{
|
|
|
|
priv->repeat_count = count;
|
|
|
|
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (timeline),
|
|
|
|
obj_props[PROP_REPEAT_COUNT]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_get_repeat_count:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
*
|
|
|
|
* Retrieves the number set using clutter_timeline_set_repeat_count().
|
|
|
|
*
|
|
|
|
* Return value: the number of repeats
|
|
|
|
*
|
|
|
|
* Since: 1.10
|
|
|
|
*/
|
|
|
|
gint
|
|
|
|
clutter_timeline_get_repeat_count (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
|
|
|
|
|
|
|
|
return timeline->priv->repeat_count;
|
|
|
|
}
|
2012-02-17 11:06:28 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_set_progress_func:
|
|
|
|
* @timeline: a #ClutterTimeline
|
2012-02-21 07:34:57 -05:00
|
|
|
* @func: (scope notified) (allow-none): a progress function, or %NULL
|
2012-02-17 11:06:28 -05:00
|
|
|
* @data: (closure): data to pass to @func
|
|
|
|
* @notify: a function to be called when the progress function is removed
|
|
|
|
* or the timeline is disposed
|
|
|
|
*
|
|
|
|
* Sets a custom progress function for @timeline. The progress function will
|
|
|
|
* be called by clutter_timeline_get_progress() and will be used to compute
|
|
|
|
* the progress value based on the elapsed time and the total duration of the
|
|
|
|
* timeline.
|
|
|
|
*
|
|
|
|
* If @func is not %NULL, the #ClutterTimeline:progress-mode property will
|
|
|
|
* be set to %CLUTTER_CUSTOM_MODE.
|
|
|
|
*
|
|
|
|
* If @func is %NULL, any previously set progress function will be unset, and
|
|
|
|
* the #ClutterTimeline:progress-mode property will be set to %CLUTTER_LINEAR.
|
|
|
|
*
|
|
|
|
* Since: 1.10
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_timeline_set_progress_func (ClutterTimeline *timeline,
|
|
|
|
ClutterTimelineProgressFunc func,
|
|
|
|
gpointer data,
|
|
|
|
GDestroyNotify notify)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
|
|
|
if (priv->progress_notify != NULL)
|
|
|
|
priv->progress_notify (priv->progress_data);
|
|
|
|
|
|
|
|
priv->progress_func = func;
|
|
|
|
priv->progress_data = data;
|
|
|
|
priv->progress_notify = notify;
|
|
|
|
|
|
|
|
if (priv->progress_func != NULL)
|
|
|
|
priv->progress_mode = CLUTTER_CUSTOM_MODE;
|
|
|
|
else
|
|
|
|
priv->progress_mode = CLUTTER_LINEAR;
|
|
|
|
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_PROGRESS_MODE]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gdouble
|
|
|
|
clutter_timeline_progress_func (ClutterTimeline *timeline,
|
|
|
|
gdouble elapsed,
|
|
|
|
gdouble duration,
|
|
|
|
gpointer user_data G_GNUC_UNUSED)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv = timeline->priv;
|
|
|
|
|
timeline: Add support for step() progress
The CSS3 Transitions specification from the W3C defines the possibility
of using a parametrized step() timing function, with the following
prototype:
steps(n_steps, [ start | end ])
where @n_steps represents the number of steps used to divide an interval
between 0 and 1; the 'start' and 'end' tokens describe whether the value
change should happen at the start of the transition, or at the end.
For instance, the "steps(3, start)" timing function has the following
profile:
1 | x
| |
| x---|
| ' |
| x---' |
| ' |
0 |---' |
Whereas the "steps(3, end)" timing function has the following profile:
1 | x---|
| ' |
| x---' |
| ' |
x---' |
| |
0 | |
Since ClutterTimeline uses an enumeration for controlling the progress
mode, we need additional API to define the parameters of the steps()
progress; for this reason, we need a CLUTTER_STEPS enumeration value,
and a method for setting the number of steps and the value transition
policy.
The CSS3 Transitions spec helpfully also defines a step-start and a
step-end shorthands, which expand to step(1, start) and step(1, end)
respectively; we can provide a CLUTTER_STEP_START and CLUTTER_STEP_END
enumeration values for those.
2012-07-19 19:47:48 -04:00
|
|
|
/* parametrized easing functions need to be handled separately */
|
|
|
|
switch (priv->progress_mode)
|
|
|
|
{
|
|
|
|
case CLUTTER_STEPS:
|
|
|
|
if (priv->step_mode == CLUTTER_STEP_MODE_START)
|
|
|
|
return clutter_ease_steps_start (elapsed, duration, priv->n_steps);
|
|
|
|
else if (priv->step_mode == CLUTTER_STEP_MODE_END)
|
|
|
|
return clutter_ease_steps_end (elapsed, duration, priv->n_steps);
|
|
|
|
else
|
|
|
|
g_assert_not_reached ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CLUTTER_STEP_START:
|
|
|
|
return clutter_ease_steps_start (elapsed, duration, 1);
|
|
|
|
|
|
|
|
case CLUTTER_STEP_END:
|
|
|
|
return clutter_ease_steps_end (elapsed, duration, 1);
|
|
|
|
|
2012-07-19 21:55:35 -04:00
|
|
|
case CLUTTER_CUBIC_BEZIER:
|
|
|
|
return clutter_ease_cubic_bezier (elapsed, duration,
|
|
|
|
priv->cb_1.x, priv->cb_1.y,
|
|
|
|
priv->cb_2.x, priv->cb_2.y);
|
|
|
|
|
|
|
|
case CLUTTER_EASE:
|
|
|
|
return clutter_ease_cubic_bezier (elapsed, duration,
|
|
|
|
0.25, 0.1, 0.25, 1.0);
|
|
|
|
|
|
|
|
case CLUTTER_EASE_IN:
|
|
|
|
return clutter_ease_cubic_bezier (elapsed, duration,
|
|
|
|
0.42, 0.0, 1.0, 1.0);
|
|
|
|
|
|
|
|
case CLUTTER_EASE_OUT:
|
|
|
|
return clutter_ease_cubic_bezier (elapsed, duration,
|
|
|
|
0.0, 0.0, 0.58, 1.0);
|
|
|
|
|
|
|
|
case CLUTTER_EASE_IN_OUT:
|
|
|
|
return clutter_ease_cubic_bezier (elapsed, duration,
|
|
|
|
0.42, 0.0, 0.58, 1.0);
|
|
|
|
|
timeline: Add support for step() progress
The CSS3 Transitions specification from the W3C defines the possibility
of using a parametrized step() timing function, with the following
prototype:
steps(n_steps, [ start | end ])
where @n_steps represents the number of steps used to divide an interval
between 0 and 1; the 'start' and 'end' tokens describe whether the value
change should happen at the start of the transition, or at the end.
For instance, the "steps(3, start)" timing function has the following
profile:
1 | x
| |
| x---|
| ' |
| x---' |
| ' |
0 |---' |
Whereas the "steps(3, end)" timing function has the following profile:
1 | x---|
| ' |
| x---' |
| ' |
x---' |
| |
0 | |
Since ClutterTimeline uses an enumeration for controlling the progress
mode, we need additional API to define the parameters of the steps()
progress; for this reason, we need a CLUTTER_STEPS enumeration value,
and a method for setting the number of steps and the value transition
policy.
The CSS3 Transitions spec helpfully also defines a step-start and a
step-end shorthands, which expand to step(1, start) and step(1, end)
respectively; we can provide a CLUTTER_STEP_START and CLUTTER_STEP_END
enumeration values for those.
2012-07-19 19:47:48 -04:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-04-11 09:11:28 -04:00
|
|
|
return clutter_easing_for_mode (priv->progress_mode, elapsed, duration);
|
2012-02-17 11:06:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_set_progress_mode:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @mode: the progress mode, as a #ClutterAnimationMode
|
|
|
|
*
|
|
|
|
* Sets the progress function using a value from the #ClutterAnimationMode
|
|
|
|
* enumeration. The @mode cannot be %CLUTTER_CUSTOM_MODE or bigger than
|
|
|
|
* %CLUTTER_ANIMATION_LAST.
|
|
|
|
*
|
|
|
|
* Since: 1.10
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_timeline_set_progress_mode (ClutterTimeline *timeline,
|
|
|
|
ClutterAnimationMode mode)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
g_return_if_fail (mode < CLUTTER_ANIMATION_LAST);
|
|
|
|
g_return_if_fail (mode != CLUTTER_CUSTOM_MODE);
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
|
|
|
if (priv->progress_mode == mode)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (priv->progress_notify != NULL)
|
|
|
|
priv->progress_notify (priv->progress_data);
|
|
|
|
|
|
|
|
priv->progress_mode = mode;
|
|
|
|
|
|
|
|
/* short-circuit linear progress */
|
|
|
|
if (priv->progress_mode != CLUTTER_LINEAR)
|
|
|
|
priv->progress_func = clutter_timeline_progress_func;
|
|
|
|
else
|
|
|
|
priv->progress_func = NULL;
|
|
|
|
|
|
|
|
priv->progress_data = NULL;
|
|
|
|
priv->progress_notify = NULL;
|
|
|
|
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_PROGRESS_MODE]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_get_progress_mode:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
*
|
|
|
|
* Retrieves the progress mode set using clutter_timeline_set_progress_mode()
|
|
|
|
* or clutter_timeline_set_progress_func().
|
|
|
|
*
|
|
|
|
* Return value: a #ClutterAnimationMode
|
|
|
|
*
|
|
|
|
* Since: 1.10
|
|
|
|
*/
|
|
|
|
ClutterAnimationMode
|
|
|
|
clutter_timeline_get_progress_mode (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), CLUTTER_LINEAR);
|
|
|
|
|
|
|
|
return timeline->priv->progress_mode;
|
|
|
|
}
|
2012-03-15 09:50:28 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_get_duration_hint:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
*
|
|
|
|
* Retrieves the full duration of the @timeline, taking into account the
|
|
|
|
* current value of the #ClutterTimeline:repeat-count property.
|
|
|
|
*
|
|
|
|
* If the #ClutterTimeline:repeat-count property is set to -1, this function
|
|
|
|
* will return %G_MAXINT64.
|
|
|
|
*
|
|
|
|
* The returned value is to be considered a hint, and it's only valid
|
|
|
|
* as long as the @timeline hasn't been changed.
|
|
|
|
*
|
|
|
|
* Return value: the full duration of the #ClutterTimeline
|
|
|
|
*
|
|
|
|
* Since: 1.10
|
|
|
|
*/
|
|
|
|
gint64
|
|
|
|
clutter_timeline_get_duration_hint (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
|
|
|
if (priv->repeat_count == 0)
|
|
|
|
return priv->duration;
|
|
|
|
else if (priv->repeat_count < 0)
|
|
|
|
return G_MAXINT64;
|
|
|
|
else
|
|
|
|
return priv->repeat_count * priv->duration;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_get_current_repeat:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
*
|
|
|
|
* Retrieves the current repeat for a timeline.
|
|
|
|
*
|
|
|
|
* Repeats start at 0.
|
|
|
|
*
|
|
|
|
* Return value: the current repeat
|
|
|
|
*
|
|
|
|
* Since: 1.10
|
|
|
|
*/
|
|
|
|
gint
|
|
|
|
clutter_timeline_get_current_repeat (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
|
|
|
|
|
|
|
|
return timeline->priv->current_repeat;
|
|
|
|
}
|
timeline: Add support for step() progress
The CSS3 Transitions specification from the W3C defines the possibility
of using a parametrized step() timing function, with the following
prototype:
steps(n_steps, [ start | end ])
where @n_steps represents the number of steps used to divide an interval
between 0 and 1; the 'start' and 'end' tokens describe whether the value
change should happen at the start of the transition, or at the end.
For instance, the "steps(3, start)" timing function has the following
profile:
1 | x
| |
| x---|
| ' |
| x---' |
| ' |
0 |---' |
Whereas the "steps(3, end)" timing function has the following profile:
1 | x---|
| ' |
| x---' |
| ' |
x---' |
| |
0 | |
Since ClutterTimeline uses an enumeration for controlling the progress
mode, we need additional API to define the parameters of the steps()
progress; for this reason, we need a CLUTTER_STEPS enumeration value,
and a method for setting the number of steps and the value transition
policy.
The CSS3 Transitions spec helpfully also defines a step-start and a
step-end shorthands, which expand to step(1, start) and step(1, end)
respectively; we can provide a CLUTTER_STEP_START and CLUTTER_STEP_END
enumeration values for those.
2012-07-19 19:47:48 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_set_step_progress:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @n_steps: the number of steps
|
|
|
|
* @step_mode: whether the change should happen at the start
|
|
|
|
* or at the end of the step
|
|
|
|
*
|
|
|
|
* Sets the #ClutterTimeline:progress-mode of the @timeline to %CLUTTER_STEPS
|
|
|
|
* and provides the parameters of the step function.
|
|
|
|
*
|
|
|
|
* Since: 1.12
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_timeline_set_step_progress (ClutterTimeline *timeline,
|
|
|
|
gint n_steps,
|
|
|
|
ClutterStepMode step_mode)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
g_return_if_fail (n_steps > 0);
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
|
|
|
if (priv->progress_mode == CLUTTER_STEPS &&
|
|
|
|
priv->n_steps == n_steps &&
|
|
|
|
priv->step_mode == step_mode)
|
|
|
|
return;
|
|
|
|
|
|
|
|
priv->n_steps = n_steps;
|
|
|
|
priv->step_mode = step_mode;
|
|
|
|
clutter_timeline_set_progress_mode (timeline, CLUTTER_STEPS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_get_step_progress:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @n_steps: (out): return location for the number of steps, or %NULL
|
|
|
|
* @step_mode: (out): return location for the value change policy,
|
|
|
|
* or %NULL
|
|
|
|
*
|
|
|
|
* Retrieves the parameters of the step progress mode used by @timeline.
|
|
|
|
*
|
|
|
|
* Return value: %TRUE if the @timeline is using a step progress
|
|
|
|
* mode, and %FALSE otherwise
|
|
|
|
*
|
|
|
|
* Since: 1.12
|
|
|
|
*/
|
|
|
|
gboolean
|
|
|
|
clutter_timeline_get_step_progress (ClutterTimeline *timeline,
|
|
|
|
gint *n_steps,
|
|
|
|
ClutterStepMode *step_mode)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
|
|
|
|
|
2014-02-10 12:39:30 -05:00
|
|
|
if (!(timeline->priv->progress_mode == CLUTTER_STEPS ||
|
|
|
|
timeline->priv->progress_mode == CLUTTER_STEP_START ||
|
|
|
|
timeline->priv->progress_mode == CLUTTER_STEP_END))
|
timeline: Add support for step() progress
The CSS3 Transitions specification from the W3C defines the possibility
of using a parametrized step() timing function, with the following
prototype:
steps(n_steps, [ start | end ])
where @n_steps represents the number of steps used to divide an interval
between 0 and 1; the 'start' and 'end' tokens describe whether the value
change should happen at the start of the transition, or at the end.
For instance, the "steps(3, start)" timing function has the following
profile:
1 | x
| |
| x---|
| ' |
| x---' |
| ' |
0 |---' |
Whereas the "steps(3, end)" timing function has the following profile:
1 | x---|
| ' |
| x---' |
| ' |
x---' |
| |
0 | |
Since ClutterTimeline uses an enumeration for controlling the progress
mode, we need additional API to define the parameters of the steps()
progress; for this reason, we need a CLUTTER_STEPS enumeration value,
and a method for setting the number of steps and the value transition
policy.
The CSS3 Transitions spec helpfully also defines a step-start and a
step-end shorthands, which expand to step(1, start) and step(1, end)
respectively; we can provide a CLUTTER_STEP_START and CLUTTER_STEP_END
enumeration values for those.
2012-07-19 19:47:48 -04:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (n_steps != NULL)
|
|
|
|
*n_steps = timeline->priv->n_steps;
|
|
|
|
|
|
|
|
if (step_mode != NULL)
|
|
|
|
*step_mode = timeline->priv->step_mode;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
2012-07-19 21:55:35 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_set_cubic_bezier_progress:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @c_1: the first control point for the cubic bezier
|
|
|
|
* @c_2: the second control point for the cubic bezier
|
|
|
|
*
|
|
|
|
* Sets the #ClutterTimeline:progress-mode of @timeline
|
|
|
|
* to %CLUTTER_CUBIC_BEZIER, and sets the two control
|
|
|
|
* points for the cubic bezier.
|
|
|
|
*
|
|
|
|
* The cubic bezier curve is between (0, 0) and (1, 1). The X coordinate
|
|
|
|
* of the two control points must be in the [ 0, 1 ] range, while the
|
|
|
|
* Y coordinate of the two control points can exceed this range.
|
|
|
|
*
|
|
|
|
* Since: 1.12
|
|
|
|
*/
|
|
|
|
void
|
2019-02-20 09:53:44 -05:00
|
|
|
clutter_timeline_set_cubic_bezier_progress (ClutterTimeline *timeline,
|
|
|
|
const graphene_point_t *c_1,
|
|
|
|
const graphene_point_t *c_2)
|
2012-07-19 21:55:35 -04:00
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
g_return_if_fail (c_1 != NULL && c_2 != NULL);
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
|
|
|
priv->cb_1 = *c_1;
|
|
|
|
priv->cb_2 = *c_2;
|
2013-04-05 10:35:15 -04:00
|
|
|
|
|
|
|
/* ensure the range on the X coordinate */
|
|
|
|
priv->cb_1.x = CLAMP (priv->cb_1.x, 0.f, 1.f);
|
|
|
|
priv->cb_2.x = CLAMP (priv->cb_2.x, 0.f, 1.f);
|
|
|
|
|
2012-07-19 21:55:35 -04:00
|
|
|
clutter_timeline_set_progress_mode (timeline, CLUTTER_CUBIC_BEZIER);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_get_cubic_bezier_progress:
|
|
|
|
* @timeline: a #ClutterTimeline
|
|
|
|
* @c_1: (out caller-allocates): return location for the first control
|
|
|
|
* point of the cubic bezier, or %NULL
|
|
|
|
* @c_2: (out caller-allocates): return location for the second control
|
|
|
|
* point of the cubic bezier, or %NULL
|
|
|
|
*
|
|
|
|
* Retrieves the control points for the cubic bezier progress mode.
|
|
|
|
*
|
|
|
|
* Return value: %TRUE if the @timeline is using a cubic bezier progress
|
|
|
|
* more, and %FALSE otherwise
|
|
|
|
*
|
|
|
|
* Since: 1.12
|
|
|
|
*/
|
|
|
|
gboolean
|
2019-02-20 09:53:44 -05:00
|
|
|
clutter_timeline_get_cubic_bezier_progress (ClutterTimeline *timeline,
|
|
|
|
graphene_point_t *c_1,
|
|
|
|
graphene_point_t *c_2)
|
2012-07-19 21:55:35 -04:00
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
|
|
|
|
|
2014-02-10 12:39:30 -05:00
|
|
|
if (!(timeline->priv->progress_mode == CLUTTER_CUBIC_BEZIER ||
|
|
|
|
timeline->priv->progress_mode == CLUTTER_EASE ||
|
|
|
|
timeline->priv->progress_mode == CLUTTER_EASE_IN ||
|
|
|
|
timeline->priv->progress_mode == CLUTTER_EASE_OUT ||
|
|
|
|
timeline->priv->progress_mode == CLUTTER_EASE_IN_OUT))
|
2012-07-19 21:55:35 -04:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (c_1 != NULL)
|
|
|
|
*c_1 = timeline->priv->cb_1;
|
|
|
|
|
|
|
|
if (c_2 != NULL)
|
|
|
|
*c_2 = timeline->priv->cb_2;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
2020-03-25 13:16:39 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_timeline_get_frame_clock: (skip)
|
|
|
|
*/
|
|
|
|
ClutterFrameClock *
|
|
|
|
clutter_timeline_get_frame_clock (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
|
|
|
|
|
|
|
|
return timeline->priv->frame_clock;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
clutter_timeline_set_frame_clock (ClutterTimeline *timeline,
|
|
|
|
ClutterFrameClock *frame_clock)
|
|
|
|
{
|
|
|
|
ClutterTimelinePrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
|
|
|
|
priv = timeline->priv;
|
|
|
|
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
g_assert (!frame_clock || (frame_clock && !priv->actor));
|
|
|
|
g_return_if_fail (!frame_clock || (frame_clock && !priv->actor));
|
2020-03-25 13:16:39 -04:00
|
|
|
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
priv->custom_frame_clock = frame_clock;
|
|
|
|
if (!priv->actor)
|
|
|
|
set_frame_clock_internal (timeline, frame_clock);
|
2020-03-25 13:16:39 -04:00
|
|
|
}
|