[gnome-shell/eos3.8: 134/255] Add wobbly windows effect
- From: Matthew Leeds <mwleeds src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/eos3.8: 134/255] Add wobbly windows effect
- Date: Wed, 10 Jun 2020 19:09:14 +0000 (UTC)
commit b3e87f7e6360148a3fe0be320b085e5906d2fa23
Author: Sam Spilsbury <sam endlessm com>
Date: Wed Jan 31 15:28:44 2018 +0000
Add wobbly windows effect
Link to libendless_wobbly (from libanimation) and add a new
EndlessShellFXWobbly class to bridge between a clutter effect
and the wobbly::Model/wobbly::Anchor.
Use endless_shell_fx_wobbly_grab to grab a point on the mesh
and move it around.
Use endless_shell_fx_wobbly_ungrab to release a point on the mesh.
Each of the above functions may only be called once whilst in the
opposing state and cannot be called again whilst in the same state.
https://phabricator.endlessm.com/T17123
https://phabricator.endlessm.com/T18131
meson.build | 12 +-
src/meson.build | 31 ++-
src/wobbly-effect.c | 592 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/wobbly-effect.h | 119 +++++++++++
tests/meson.build | 1 +
5 files changed, 750 insertions(+), 5 deletions(-)
---
diff --git a/meson.build b/meson.build
index e4166addb2..b62505cba2 100644
--- a/meson.build
+++ b/meson.build
@@ -112,12 +112,20 @@ if enable_recorder
recorder_deps += [gst_dep, gst_base_dep, gtk_dep, x11_dep]
endif
+cc = meson.get_compiler('c')
+
+m_dep = cc.find_library('m', required: false)
+
# Endless-specific: Metrics
eosmetrics_dep = dependency('eosmetrics-0')
# Endless-specific: parental controls
malcontent_dep = dependency('malcontent-0', version: malcontent_req, required: true)
+# Endless-specific: Custom animations
+libanimation_glib_dep = dependency('libanimation-glib-0')
+libeos_shell_fx_deps = [clutter_dep, libanimation_glib_dep, m_dep]
+
nm_deps = []
if get_option('networkmanager')
nm_deps += dependency('libnm', version: nm_req)
@@ -150,10 +158,6 @@ sassc = find_program('sassc')
gjs = find_program('gjs')
appstream_util = find_program('appstream-util', required: false)
-cc = meson.get_compiler('c')
-
-m_dep = cc.find_library('m', required: false)
-
cdata = configuration_data()
cdata.set_quoted('GETTEXT_PACKAGE', meson.project_name())
cdata.set_quoted('VERSION', meson.project_version())
diff --git a/src/meson.build b/src/meson.build
index d40906e4a0..451ec6ebca 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -59,6 +59,7 @@ gnome_shell_deps = [
gcr_dep,
libsystemd_dep,
eosmetrics_dep,
+ libanimation_glib_dep,
]
gnome_shell_deps += nm_deps
@@ -176,6 +177,34 @@ if enable_recorder
libshell_private_headers += ['shell-recorder-src.h']
endif
+libeos_shell_fx_sources = [
+ 'wobbly-effect.c',
+ 'wobbly-effect.h'
+]
+
+libeos_shell_fx = library('eos-shell-fx',
+ sources: libeos_shell_fx_sources,
+ dependencies: libeos_shell_fx_deps,
+ include_directories: conf_inc,
+ build_rpath: mutter_typelibdir,
+ install_rpath: mutter_typelibdir,
+ install_dir: pkglibdir,
+ install: true
+)
+
+libeos_shell_fx_gir = gnome.generate_gir(libeos_shell_fx,
+ sources: libeos_shell_fx_sources,
+ nsversion: '1.0',
+ namespace: 'EndlessShellFX',
+ includes: ['Clutter-@0@'.format(mutter_api_version)],
+ dependencies: [mutter_dep],
+ extra_args: ['--quiet'],
+ install_dir_gir: pkgdatadir,
+ install_dir_typelib: pkglibdir,
+ install: true
+)
+
+libeos_shell_fx_dep = declare_dependency(link_with: libeos_shell_fx)
libshell_enums = gnome.mkenums_simple('shell-enum-types',
sources: libshell_public_headers
@@ -207,7 +236,7 @@ libshell_no_gir_sources += dbus_generated
libshell = library('gnome-shell',
sources: libshell_gir_sources + libshell_no_gir_sources,
- dependencies: gnome_shell_deps + [libshell_menu_dep, libst_dep, mutter_dep, gnome_desktop_dep, m_dep],
+ dependencies: gnome_shell_deps + [libeos_shell_fx_dep, libshell_menu_dep, libst_dep, mutter_dep,
gnome_desktop_dep, m_dep],
include_directories: [conf_inc, st_inc, include_directories('tray')],
c_args: gnome_shell_cflags,
link_with: [libtray],
diff --git a/src/wobbly-effect.c b/src/wobbly-effect.c
new file mode 100644
index 0000000000..b0dd567acb
--- /dev/null
+++ b/src/wobbly-effect.c
@@ -0,0 +1,592 @@
+/*
+ * wobbly-effect.c
+ *
+ * Copyright © 2013-2019 Endless Mobile, Inc.
+ *
+ * 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
+ * licence 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
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Sam Spilsbury <sam endlessm com>
+ */
+
+#include <math.h>
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <clutter/clutter.h>
+
+#include <animation-glib/vector.h>
+#include <animation-glib/wobbly/anchor.h>
+#include <animation-glib/wobbly/model.h>
+
+#include "wobbly-effect.h"
+
+typedef struct _EndlessShellFXWobblyPrivate
+{
+ float slowdown_factor;
+ double spring_constant;
+ double friction;
+ double movement_range;
+ AnimationWobblyModel *model;
+ AnimationWobblyAnchor *anchor;
+ gint64 last_msecs;
+ guint timeout_id;
+ guint width_changed_signal;
+ guint height_changed_signal;
+ gboolean ungrab_pending;
+} EndlessShellFXWobblyPrivate;
+
+enum
+{
+ PROP_0,
+
+ PROP_SPRING_K,
+ PROP_FRICTION,
+ PROP_SLOWDOWN_FACTOR,
+ PROP_OBJECT_MOVEMENT_RANGE,
+
+ PROP_LAST
+};
+
+static GParamSpec *object_properties[PROP_LAST];
+
+G_DEFINE_TYPE_WITH_PRIVATE (EndlessShellFXWobbly,
+ endless_shell_fx_wobbly,
+ CLUTTER_TYPE_DEFORM_EFFECT)
+
+/* This constant is used to deal with rounding error in computing
+ * paint boxes. See also
https://gitlab.gnome.org/GNOME/mutter/blob/master/clutter/clutter/clutter-paint-volume.c#L1212 */
+#define PAINT_BOX_OFFSET 1
+
+static void
+endless_shell_fx_get_untransformed_paint_box_from_existing_volume (ClutterActor *actor,
+ const ClutterPaintVolume *volume,
+ ClutterActorBox *box)
+{
+ graphene_point3d_t origin;
+
+ /* We don't have access to the stage projection matrix
+ * so the best we can do is hope here that the volume is
+ * two dimensional and orthogonal. */
+ clutter_paint_volume_get_origin (volume, &origin);
+
+ box->x1 = floor (origin.x + clutter_actor_get_x (actor)) - PAINT_BOX_OFFSET;
+ box->y1 = floor (origin.y + clutter_actor_get_y (actor)) - PAINT_BOX_OFFSET;
+ box->x2 = box->x1 + ceil (clutter_paint_volume_get_width (volume)) + PAINT_BOX_OFFSET * 2;
+ box->y2 = box->y1 + ceil (clutter_paint_volume_get_height (volume)) + PAINT_BOX_OFFSET * 2;
+}
+
+static gboolean
+endless_shell_fx_get_untransformed_paint_box (ClutterActor *actor,
+ ClutterActorBox *box)
+{
+ /* Get the actor's paint volume, bypassing affine
+ * transformations which would usually be applied
+ * if we just queried the paint box. */
+ const ClutterPaintVolume *volume = clutter_actor_get_paint_volume (actor);
+
+ if (volume == NULL)
+ return FALSE;
+
+ endless_shell_fx_get_untransformed_paint_box_from_existing_volume (actor,
+ volume,
+ box);
+
+ return TRUE;
+}
+
+static void
+endless_shell_fx_get_actor_only_paint_box_rect (EndlessShellFXWobbly *effect,
+ ClutterActor *actor,
+ gfloat *paint_box_x,
+ gfloat *paint_box_y,
+ gfloat *paint_box_width,
+ gfloat *paint_box_height)
+{
+ /* We want the size of the paint box and not the actor
+ * size, because that's going to be the size of the
+ * texture. However, we only want the size of the
+ * paint box when we're just considering the
+ * actor alone */
+ ClutterActorBox rect;
+
+ /* If endless_shell_fx_get_untransformed_paint_box fails
+ * we should fall back to the actor size at this point */
+ if (endless_shell_fx_get_untransformed_paint_box (actor, &rect))
+ {
+ clutter_actor_box_get_origin (&rect, paint_box_x, paint_box_y);
+ clutter_actor_box_get_size (&rect, paint_box_width, paint_box_height);
+ }
+ else
+ {
+ clutter_actor_get_size (actor, paint_box_width, paint_box_height);
+ clutter_actor_get_position (actor, paint_box_x, paint_box_y);
+ }
+}
+
+static gboolean
+endless_shell_fx_wobbly_get_paint_volume (ClutterEffect *effect,
+ ClutterPaintVolume *volume)
+{
+ ClutterActorMeta *meta = CLUTTER_ACTOR_META (effect);
+ ClutterActor *actor = clutter_actor_meta_get_actor (meta);
+ EndlessShellFXWobbly *wobbly_effect = ENDLESS_SHELL_FX_WOBBLY (effect);
+ EndlessShellFXWobblyPrivate *priv =
+ endless_shell_fx_wobbly_get_instance_private (wobbly_effect);
+
+ /* We assume that the parent's get_paint_volume method always returns
+ * TRUE here. */
+ CLUTTER_EFFECT_CLASS (endless_shell_fx_wobbly_parent_class)->modify_paint_volume (effect, volume);
+
+ if (priv->model && clutter_actor_meta_get_enabled (meta))
+ {
+ ClutterActorBox box;
+ float actor_x, actor_y;
+
+ endless_shell_fx_get_untransformed_paint_box_from_existing_volume (actor, volume, &box);
+ clutter_actor_get_position (actor, &actor_x, &actor_y);
+
+ AnimationVector offset = { box.x1 - actor_x, box.y1 - actor_y };
+ AnimationVector extremes[4];
+
+ animation_wobbly_model_query_extremes (priv->model,
+ &extremes[0],
+ &extremes[1],
+ &extremes[2],
+ &extremes[3]);
+
+ float x1 = MIN (extremes[0].x, extremes[2].x);
+ float y1 = MIN (extremes[0].y, extremes[1].y);
+ float x2 = MAX (extremes[1].x, extremes[3].x);
+ float y2 = MAX (extremes[2].y, extremes[3].y);
+
+ ClutterActorBox const extremesBox =
+ {
+ floor (x1 + offset.x),
+ floor (y1 + offset.y),
+ ceil (x2 + offset.x),
+ ceil (y2 + offset.x)
+ };
+
+ clutter_paint_volume_union_box (volume, &extremesBox);
+ }
+
+ return TRUE;
+}
+
+static void
+endless_shell_fx_wobbly_deform_vertex (ClutterDeformEffect *effect,
+ gfloat x G_GNUC_UNUSED,
+ gfloat y G_GNUC_UNUSED,
+ CoglTextureVertex *vertex)
+{
+ EndlessShellFXWobbly *wobbly_effect = ENDLESS_SHELL_FX_WOBBLY (effect);
+ EndlessShellFXWobblyPrivate *priv =
+ endless_shell_fx_wobbly_get_instance_private (wobbly_effect);
+
+ /* The reversal of ty and tx here is intentional */
+ AnimationVector uv = { vertex->ty, vertex->tx };
+ AnimationVector deformed;
+ animation_wobbly_model_deform_texcoords (priv->model,
+ uv,
+ &deformed);
+ vertex->x = deformed.x;
+ vertex->y = deformed.y;
+}
+
+static void
+remove_anchor_if_pending (EndlessShellFXWobblyPrivate *priv)
+{
+ if (priv->ungrab_pending)
+ {
+ g_clear_object (&priv->anchor);
+ priv->ungrab_pending = FALSE;
+ }
+}
+
+/* It turns out that clutter doesn't contain any mechanism whatsoever
+ * to do timeline-less animations. We're just using a timeout here
+ * to keep performing animations on the actor */
+static gboolean
+endless_shell_fx_wobbly_new_frame (gpointer user_data)
+{
+ EndlessShellFXWobbly *wobbly_effect = ENDLESS_SHELL_FX_WOBBLY (user_data);
+ EndlessShellFXWobblyPrivate *priv =
+ endless_shell_fx_wobbly_get_instance_private (wobbly_effect);
+ gint64 msecs = g_get_monotonic_time ();
+
+ static const unsigned int ms_to_us = 1000;
+
+ g_assert (priv->model);
+
+ /* Wraparound, priv->last_msecs -= G_MAXINT64.
+ * We make priv->last_msecs negative so that subtracting it
+ * from msecs results in the correct delta */
+ if (G_UNLIKELY (priv->last_msecs > msecs))
+ priv->last_msecs -= G_MAXINT64;
+
+ gint64 msecs_delta = (msecs - priv->last_msecs ) / ms_to_us;
+ priv->last_msecs = msecs;
+
+ /* If there was no time movement, then we can't really step or remove
+ * models in a way that makes sense, so don't do it */
+ if (msecs_delta)
+ {
+ if (animation_wobbly_model_step (priv->model, msecs_delta / priv->slowdown_factor))
+ {
+ clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (wobbly_effect), TRUE);
+ clutter_deform_effect_invalidate (CLUTTER_DEFORM_EFFECT (wobbly_effect));
+ }
+ else
+ {
+ remove_anchor_if_pending (priv);
+
+ /* Also disable the effect */
+ clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (wobbly_effect), FALSE);
+
+ /* Finally, return false so that we don't keep animating */
+ priv->timeout_id = -1;
+ return FALSE;
+ }
+ }
+
+ /* We always want to return true even if there was no time delta */
+ return TRUE;
+}
+
+static void
+endless_shell_fx_wobbly_ensure_timeline (EndlessShellFXWobbly *wobbly_effect)
+{
+ EndlessShellFXWobblyPrivate *priv =
+ endless_shell_fx_wobbly_get_instance_private (wobbly_effect);
+
+ if (priv->timeout_id == -1)
+ {
+ static const unsigned int frame_length_ms = 16; // 60 / 1000;
+
+ priv->last_msecs = g_get_monotonic_time ();
+ priv->timeout_id = g_timeout_add (frame_length_ms, endless_shell_fx_wobbly_new_frame, wobbly_effect);
+ }
+}
+
+void
+endless_shell_fx_wobbly_grab (EndlessShellFXWobbly *effect,
+ double x,
+ double y)
+{
+ ClutterActor *actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
+ EndlessShellFXWobblyPrivate *priv =
+ endless_shell_fx_wobbly_get_instance_private (effect);
+
+ g_assert (!priv->anchor || priv->ungrab_pending);
+
+ /* Either ungrab here or at the end of the animation */
+ remove_anchor_if_pending (priv);
+
+ if (priv->model)
+ {
+ /* Make sure to update the model geometry and move
+ * to the right position, it may have changed
+ * in the meantime */
+ float actor_paint_box_width, actor_paint_box_height;
+ endless_shell_fx_get_actor_only_paint_box_rect (effect,
+ actor,
+ NULL,
+ NULL,
+ &actor_paint_box_width,
+ &actor_paint_box_height);
+
+ AnimationVector position = { 0, 0 };
+ AnimationVector size = { actor_paint_box_width, actor_paint_box_height };
+
+ animation_wobbly_model_resize (priv->model, size);
+ animation_wobbly_model_move_to (priv->model, position);
+
+ endless_shell_fx_wobbly_ensure_timeline (effect);
+
+ float actor_x, actor_y;
+ clutter_actor_get_position (actor, &actor_x, &actor_y);
+
+ AnimationVector anchor_position = { x - actor_x, y - actor_y };
+
+ priv->anchor = animation_wobbly_model_grab_anchor (priv->model, anchor_position);
+ }
+}
+
+void
+endless_shell_fx_wobbly_ungrab (EndlessShellFXWobbly *effect)
+{
+ EndlessShellFXWobblyPrivate *priv =
+ endless_shell_fx_wobbly_get_instance_private (effect);
+
+ g_assert (priv->anchor && !priv->ungrab_pending);
+
+ /* Don't immediately ungrab. We can be a little bit more
+ * clever here and make the ungrab pending on the completion
+ * of the animation */
+ if (priv->timeout_id != -1)
+ priv->ungrab_pending = TRUE;
+ else
+ g_clear_object (&priv->anchor);
+}
+
+void
+endless_shell_fx_wobbly_move_by (EndlessShellFXWobbly *effect,
+ double dx,
+ double dy)
+{
+ EndlessShellFXWobblyPrivate *priv =
+ endless_shell_fx_wobbly_get_instance_private (effect);
+
+ if (priv->anchor)
+ {
+ AnimationVector delta = { dx, dy };
+
+ endless_shell_fx_wobbly_ensure_timeline (effect);
+ animation_wobbly_anchor_move_by (priv->anchor, delta);
+
+ AnimationVector reverse_delta = delta;
+ reverse_delta.x *= -1;
+ reverse_delta.y *= -1;
+
+ /* Now move the entire model back - this ensures that
+ * we stay in sync with the actor's relative position */
+ animation_wobbly_model_move_by (priv->model, reverse_delta);
+ }
+}
+
+static void
+endless_shell_fx_wobbly_size_changed (GObject *object,
+ GParamSpec *spec G_GNUC_UNUSED,
+ gpointer user_data)
+{
+ ClutterActor *actor = CLUTTER_ACTOR (object);
+ EndlessShellFXWobbly *effect = ENDLESS_SHELL_FX_WOBBLY (user_data);
+ EndlessShellFXWobblyPrivate *priv =
+ endless_shell_fx_wobbly_get_instance_private (effect);
+
+ /* We don't ensure a timeline here because we only want to redistribute
+ * non-anchor points if we're already grabbed, which the wobbly effect will
+ * do internally anyways */
+ if (priv->model)
+ {
+ float actor_paint_box_width, actor_paint_box_height;
+ endless_shell_fx_get_actor_only_paint_box_rect (effect,
+ actor,
+ NULL,
+ NULL,
+ &actor_paint_box_width,
+ &actor_paint_box_height);
+
+ /* If we have any pending anchors, we should release them now -
+ * the model move and resize code explicitly does not move
+ * anchors around (because that'd put them out of sync with
+ * the cursor) */
+ remove_anchor_if_pending (priv);
+
+ AnimationVector actor_size = { actor_paint_box_width, actor_paint_box_height };
+ AnimationVector actor_position = { 0.0, 0.0 };
+
+ animation_wobbly_model_resize (priv->model, actor_size);
+ animation_wobbly_model_move_to (priv->model, actor_position);
+ }
+}
+
+static void
+endless_shell_fx_wobbly_set_actor (ClutterActorMeta *actor_meta,
+ ClutterActor *actor)
+{
+ ClutterActor *prev_actor = clutter_actor_meta_get_actor (actor_meta);
+
+ CLUTTER_ACTOR_META_CLASS (endless_shell_fx_wobbly_parent_class)->set_actor (actor_meta, actor);
+
+ EndlessShellFXWobbly *wobbly_effect = ENDLESS_SHELL_FX_WOBBLY (actor_meta);
+ EndlessShellFXWobblyPrivate *priv =
+ endless_shell_fx_wobbly_get_instance_private (wobbly_effect);
+
+ g_clear_object (&priv->anchor);
+ g_clear_object (&priv->model);
+
+ priv->ungrab_pending = FALSE;
+
+ if (priv->timeout_id != -1)
+ {
+ g_source_remove (priv->timeout_id);
+ priv->timeout_id = -1;
+ }
+
+ if (prev_actor)
+ {
+ g_signal_handler_disconnect (prev_actor, priv->width_changed_signal);
+ priv->width_changed_signal = 0;
+
+ g_signal_handler_disconnect (prev_actor, priv->height_changed_signal);
+ priv->height_changed_signal = 0;
+ }
+
+ if (actor)
+ {
+ float actor_paint_box_width, actor_paint_box_height;
+ endless_shell_fx_get_actor_only_paint_box_rect (wobbly_effect,
+ actor,
+ NULL,
+ NULL,
+ &actor_paint_box_width,
+ &actor_paint_box_height);
+
+ AnimationVector actor_position = { 0, 0 };
+ AnimationVector actor_size = { actor_paint_box_width, actor_paint_box_height };
+
+ priv->model = animation_wobbly_model_new (actor_position,
+ actor_size,
+ priv->spring_constant,
+ priv->friction,
+ priv->movement_range);
+
+ priv->width_changed_signal =
+ g_signal_connect_object (actor,
+ "notify::width",
+ G_CALLBACK (endless_shell_fx_wobbly_size_changed),
+ wobbly_effect,
+ G_CONNECT_AFTER);
+ priv->height_changed_signal =
+ g_signal_connect_object (actor,
+ "notify::height",
+ G_CALLBACK (endless_shell_fx_wobbly_size_changed),
+ wobbly_effect,
+ G_CONNECT_AFTER);
+ }
+
+ /* Whatever the actor, ensure that the effect is disabled at this point */
+ clutter_actor_meta_set_enabled (actor_meta, FALSE);
+}
+
+static void
+endless_shell_fx_wobbly_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EndlessShellFXWobbly *wobbly_effect = ENDLESS_SHELL_FX_WOBBLY (object);
+ EndlessShellFXWobblyPrivate *priv =
+ endless_shell_fx_wobbly_get_instance_private (wobbly_effect);
+
+ switch (prop_id)
+ {
+ case PROP_SPRING_K:
+ priv->spring_constant = g_value_get_double (value);
+
+ if (priv->model != NULL)
+ animation_wobbly_model_set_spring_k (priv->model, priv->spring_constant);
+ break;
+ case PROP_FRICTION:
+ priv->friction = g_value_get_double (value);
+
+ if (priv->model != NULL)
+ animation_wobbly_model_set_friction (priv->model, priv->friction);
+ break;
+ case PROP_SLOWDOWN_FACTOR:
+ priv->slowdown_factor = g_value_get_double (value);
+ break;
+ case PROP_OBJECT_MOVEMENT_RANGE:
+ priv->movement_range = g_value_get_double (value);
+
+ if (priv->model != NULL)
+ animation_wobbly_model_set_maximum_range (priv->model, priv->movement_range);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+endless_shell_fx_wobbly_finalize (GObject *object)
+{
+ EndlessShellFXWobbly *wobbly_effect = ENDLESS_SHELL_FX_WOBBLY (object);
+ EndlessShellFXWobblyPrivate *priv =
+ endless_shell_fx_wobbly_get_instance_private (wobbly_effect);
+
+ g_clear_object (&priv->model);
+
+ if (priv->timeout_id != -1)
+ {
+ g_source_remove (priv->timeout_id);
+ priv->timeout_id = -1;
+ }
+
+ G_OBJECT_CLASS (endless_shell_fx_wobbly_parent_class)->finalize (object);
+}
+
+static void
+endless_shell_fx_wobbly_class_init (EndlessShellFXWobblyClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
+ ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
+ ClutterDeformEffectClass *deform_class = CLUTTER_DEFORM_EFFECT_CLASS (klass);
+
+ object_class->set_property = endless_shell_fx_wobbly_set_property;
+ object_class->finalize = endless_shell_fx_wobbly_finalize;
+
+ meta_class->set_actor = endless_shell_fx_wobbly_set_actor;
+
+ effect_class->modify_paint_volume = endless_shell_fx_wobbly_get_paint_volume;
+
+ deform_class->deform_vertex = endless_shell_fx_wobbly_deform_vertex;
+
+ object_properties[PROP_SPRING_K] =
+ g_param_spec_double ("spring-k",
+ "Spring Constant",
+ "How springy the model is",
+ 2.0f, 10.0f, 8.0f,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT);
+
+ object_properties[PROP_FRICTION] =
+ g_param_spec_double ("friction",
+ "Friction Constant",
+ "How much friction force should be applied to moving objects",
+ 2.0f, 10.0f, 3.0f,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT);
+
+ object_properties[PROP_SLOWDOWN_FACTOR] =
+ g_param_spec_double ("slowdown-factor",
+ "Slowdown Factor",
+ "How much to slow the model's timesteps down",
+ 1.0f, 5.0f, 1.0f,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT);
+
+ object_properties[PROP_OBJECT_MOVEMENT_RANGE] =
+ g_param_spec_double ("object-movement-range",
+ "Object Movement Range",
+ "How much objects are allowed to move around",
+ 10.0f, 500.0f, 100.0f,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT);
+
+ g_object_class_install_properties (object_class, PROP_LAST, object_properties);
+}
+
+static void
+endless_shell_fx_wobbly_init (EndlessShellFXWobbly *effect)
+{
+ EndlessShellFXWobblyPrivate *priv =
+ endless_shell_fx_wobbly_get_instance_private (effect);
+
+ priv->timeout_id = -1;
+}
+
+ClutterEffect *
+endless_shell_fx_wobbly_new (void)
+{
+ return g_object_new (ENDLESS_SHELL_FX_TYPE_WOBBLY, NULL);
+}
diff --git a/src/wobbly-effect.h b/src/wobbly-effect.h
new file mode 100644
index 0000000000..bed1ad4639
--- /dev/null
+++ b/src/wobbly-effect.h
@@ -0,0 +1,119 @@
+/*
+ * wobbly-effect.h
+ *
+ * Copyright © 2013-2019 Endless Mobile, Inc.
+ *
+ * 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
+ * licence 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
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Sam Spilsbury <sam endlessm com>
+ */
+
+#ifndef ENDLESS_SHELL_FX_WOBBLY_H
+#define ENDLESS_SHELL_FX_WOBBLY_H
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define ENDLESS_SHELL_FX_TYPE_WOBBLY endless_shell_fx_wobbly_get_type ()
+
+#define ENDLESS_SHELL_FX_WOBBLY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+ ENDLESS_SHELL_FX_TYPE_WOBBLY, EndlessShellFXWobbly))
+
+#define ENDLESS_SHELL_FX_WOBBLY_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_CAST((obj) \
+ ENDLESS_SHELL_FX_TYPE_WOBBLY, EndlessShellFXWobblyClass))
+
+#define ENDLESS_SHELL_FX_WOBBLY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), \
+ ENDLESS_SHELL_FX_TYPE_WOBBLY, EndlessShellFXWobblyClass))
+
+typedef struct _EndlessShellFXWobbly EndlessShellFXWobbly;
+typedef struct _EndlessShellFXWobblyClass EndlessShellFXWobblyClass;
+
+struct _EndlessShellFXWobbly
+{
+ ClutterDeformEffect parent;
+};
+
+struct _EndlessShellFXWobblyClass
+{
+ ClutterDeformEffectClass parent_class;
+};
+
+GType endless_shell_fx_wobbly_get_type (void);
+
+/**
+ * endless_shell_fx_wobbly_grab:
+ * @effect: An #EndlessShellFXWobbly
+ * @x: The x-coordinate on the mesh to grab, specified relative to the
+ * upper-left corner of the mesh
+ * @y: The y-coordinate on the mesh to grab, specified relative to the
+ * upper-left corner of the mesh.
+ *
+ * Grabs the anchor specified by @x and @y on the mesh. While
+ * the mesh is in this state, this point will move immediately,
+ * causing spring forces to be applied to other points on the mesh
+ *
+ * It is a precondition violation to call this function when the mesh is
+ * already grabbed.
+ *
+ */
+void endless_shell_fx_wobbly_grab (EndlessShellFXWobbly *effect,
+ double x,
+ double y);
+
+/**
+ * endless_shell_fx_wobbly_ungrab:
+ * @effect: An #EndlessShellFXWobbly
+ * Removes the current grab. When the actor is moved, the mesh will
+ * move uniformly.
+ *
+ * It is a precondition violation to call this function when the mesh is
+ * not grabbed.
+ */
+void endless_shell_fx_wobbly_ungrab (EndlessShellFXWobbly *effect);
+
+/**
+ * endless_shell_fx_wobbly_move_by:
+ * @effect: An #EndlessShellFXWobbly
+ * @dx: A delta-x coordinate to move the mesh by
+ * @dy: A delta-y coordinate to move the mesh by
+ *
+ * Moves the mesh by @dx and @dy
+ *
+ * If the mesh is grabbed, then spring forces will be applied causing
+ * some points on the mesh to move more slowly than others. The nature
+ * of the moment will depend on the window's maximization state.
+ *
+ */
+void endless_shell_fx_wobbly_move_by (EndlessShellFXWobbly *effect,
+ double dx,
+ double dy);
+
+/**
+ * endless_shell_fx_wobbly_new:
+ *
+ * Creates a new #ClutterEffect which makes the window "wobble"
+ * on a spring mesh for the actor
+ *
+ * Returns: (transfer full): A new #ClutterEffect
+ */
+ClutterEffect * endless_shell_fx_wobbly_new (void);
+
+G_END_DECLS
+
+#endif
diff --git a/tests/meson.build b/tests/meson.build
index 1e84f423ca..a9c38164b3 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -9,6 +9,7 @@ run_test = configure_file(
testenv = environment()
testenv.set('GSETTINGS_SCHEMA_DIR', join_paths(meson.build_root(), 'data'))
+testenv.prepend('LD_LIBRARY_PATH', meson.build_root() + '/src')
foreach test : ['insertSorted', 'jsParse', 'markup', 'params', 'url']
test(test, run_test,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]