diff options
Diffstat (limited to 'src/animation/egg-frame-source.c')
-rw-r--r-- | src/animation/egg-frame-source.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/src/animation/egg-frame-source.c b/src/animation/egg-frame-source.c new file mode 100644 index 0000000..5672a05 --- /dev/null +++ b/src/animation/egg-frame-source.c @@ -0,0 +1,130 @@ +/* egg-frame-source.c + * + * Copyright (C) 2010-2016 Christian Hergert <christian@hergert.me> + * + * This file 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.1 of the License, or (at your option) + * any later version. + * + * This file 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 General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "animation/egg-frame-source.h" + +typedef struct +{ + GSource parent; + guint fps; + guint frame_count; + gint64 start_time; +} EggFrameSource; + +static gboolean +egg_frame_source_prepare (GSource *source, + gint *timeout_) +{ + EggFrameSource *fsource = (EggFrameSource *)(gpointer)source; + gint64 current_time; + guint elapsed_time; + guint new_frame_num; + guint frame_time; + + current_time = g_source_get_time(source) / 1000; + elapsed_time = current_time - fsource->start_time; + new_frame_num = elapsed_time * fsource->fps / 1000; + + /* If time has gone backwards or the time since the last frame is + * greater than the two frames worth then reset the time and do a + * frame now */ + if (new_frame_num < fsource->frame_count || + new_frame_num - fsource->frame_count > 2) { + /* Get the frame time rounded up to the nearest ms */ + frame_time = (1000 + fsource->fps - 1) / fsource->fps; + + /* Reset the start time */ + fsource->start_time = current_time; + + /* Move the start time as if one whole frame has elapsed */ + fsource->start_time -= frame_time; + fsource->frame_count = 0; + *timeout_ = 0; + return TRUE; + } else if (new_frame_num > fsource->frame_count) { + *timeout_ = 0; + return TRUE; + } else { + *timeout_ = (fsource->frame_count + 1) * 1000 / fsource->fps - elapsed_time; + return FALSE; + } +} + +static gboolean +egg_frame_source_check (GSource *source) +{ + gint timeout_; + return egg_frame_source_prepare(source, &timeout_); +} + +static gboolean +egg_frame_source_dispatch (GSource *source, + GSourceFunc source_func, + gpointer user_data) +{ + EggFrameSource *fsource = (EggFrameSource *)(gpointer)source; + gboolean ret; + + if ((ret = source_func(user_data))) + fsource->frame_count++; + return ret; +} + +static GSourceFuncs source_funcs = { + egg_frame_source_prepare, + egg_frame_source_check, + egg_frame_source_dispatch, +}; + +/** + * egg_frame_source_add: + * @frames_per_sec: (in): Target frames per second. + * @callback: (in) (scope notified): A #GSourceFunc to execute. + * @user_data: (in): User data for @callback. + * + * Creates a new frame source that will execute when the timeout interval + * for the source has elapsed. The timing will try to synchronize based + * on the end time of the animation. + * + * Returns: A source id that can be removed with g_source_remove(). + */ +guint +egg_frame_source_add (guint frames_per_sec, + GSourceFunc callback, + gpointer user_data) +{ + EggFrameSource *fsource; + GSource *source; + guint ret; + + g_return_val_if_fail (frames_per_sec > 0, 0); + g_return_val_if_fail (frames_per_sec <= 120, 0); + + source = g_source_new(&source_funcs, sizeof(EggFrameSource)); + fsource = (EggFrameSource *)(gpointer)source; + fsource->fps = frames_per_sec; + fsource->frame_count = 0; + fsource->start_time = g_get_monotonic_time() / 1000; + g_source_set_callback(source, callback, user_data, NULL); + g_source_set_name(source, "EggFrameSource"); + + ret = g_source_attach(source, NULL); + g_source_unref(source); + + return ret; +} |