discomfitor pushed a commit to branch master.

http://git.enlightenment.org/core/enlightenment.git/commit/?id=d3fe687adabaebc3ba0d662cbcc06746f17fd99e

commit d3fe687adabaebc3ba0d662cbcc06746f17fd99e
Author: Mike Blumenkrantz <zm...@osg.samsung.com>
Date:   Wed Mar 2 15:13:16 2016 -0500

    add a namespaced version of efx
    
    this will eventually be merged into the efl tree but the current release
    cycle is not a good time for it
---
 src/bin/Makefile.mk           |   20 +-
 src/bin/e.h                   |    1 +
 src/bin/e_fm/Makefile.mk      |    1 +
 src/bin/e_main.c              |    9 +
 src/bin/efx/e_Efx.h           | 1076 +++++++++++++++++++++++++++++++++++++++++
 src/bin/efx/e_efx_private.h   |  128 +++++
 src/bin/efx/efx.c             |   65 +++
 src/bin/efx/efx_bumpmapping.c |  186 +++++++
 src/bin/efx/efx_fade.c        |  184 +++++++
 src/bin/efx/efx_helpers.c     |  115 +++++
 src/bin/efx/efx_move.c        |  225 +++++++++
 src/bin/efx/efx_pan.c         |  390 +++++++++++++++
 src/bin/efx/efx_queue.c       |  301 ++++++++++++
 src/bin/efx/efx_resize.c      |  225 +++++++++
 src/bin/efx/efx_rotate.c      |  145 ++++++
 src/bin/efx/efx_spin.c        |  148 ++++++
 src/bin/efx/efx_util.c        |  137 ++++++
 src/bin/efx/efx_zoom.c        |  171 +++++++
 src/modules/Makefile.mk       |    1 +
 19 files changed, 3527 insertions(+), 1 deletion(-)

diff --git a/src/bin/Makefile.mk b/src/bin/Makefile.mk
index 6935c62..121dd03 100644
--- a/src/bin/Makefile.mk
+++ b/src/bin/Makefile.mk
@@ -1,10 +1,26 @@
 DISTCLEANFILES += src/bin/e_fm_shared_types.h
 
+efx_files = \
+src/bin/efx/efx_bumpmapping.c \
+src/bin/efx/efx.c \
+src/bin/efx/efx_fade.c \
+src/bin/efx/efx_helpers.c \
+src/bin/efx/efx_move.c \
+src/bin/efx/efx_pan.c \
+src/bin/efx/e_efx_private.h \
+src/bin/efx/efx_queue.c \
+src/bin/efx/efx_resize.c \
+src/bin/efx/efx_rotate.c \
+src/bin/efx/efx_spin.c \
+src/bin/efx/efx_util.c \
+src/bin/efx/efx_zoom.c
+
 E_CPPFLAGS = \
 -I$(top_builddir) \
 -I$(top_builddir)/src/bin \
 -I$(top_srcdir) \
 -I$(top_srcdir)/src/bin \
+-I$(top_srcdir)/src/bin/efx \
 -I$(top_srcdir)/src/bin/generated \
 @e_cflags@ \
 @cf_cflags@ \
@@ -43,6 +59,7 @@ internal_bin_PROGRAMS += src/bin/enlightenment_ckpasswd
 endif
 
 ENLIGHTENMENTHEADERS = \
+src/bin/efx/e_Efx.h \
 src/bin/e_about.h \
 src/bin/e_acpi.h \
 src/bin/e_actions.h \
@@ -359,7 +376,8 @@ src/bin/e_xkb.c \
 src/bin/e_xinerama.c \
 src/bin/e_zoomap.c \
 src/bin/e_zone.c \
-$(ENLIGHTENMENTHEADERS)
+$(ENLIGHTENMENTHEADERS) \
+$(efx_files)
 
 if ! HAVE_WAYLAND_ONLY
 enlightenment_src += \
diff --git a/src/bin/e.h b/src/bin/e.h
index 6b7ea51..34887bc 100644
--- a/src/bin/e.h
+++ b/src/bin/e.h
@@ -129,6 +129,7 @@ void *alloca (size_t);
 # include <Eio.h>
 # include <Emotion.h>
 # include <Elementary.h>
+# include "e_Efx.h"
 
 # ifdef HAVE_WAYLAND
 #  include <Ecore_Wl2.h>
diff --git a/src/bin/e_fm/Makefile.mk b/src/bin/e_fm/Makefile.mk
index 0eec4f4..218329f 100644
--- a/src/bin/e_fm/Makefile.mk
+++ b/src/bin/e_fm/Makefile.mk
@@ -3,6 +3,7 @@ EFM_CPPFLAGS = \
 -I$(top_builddir)/src/bin \
 -I$(top_srcdir) \
 -I$(top_srcdir)/src/bin \
+-I$(top_srcdir)/src/bin/efx \
 @e_cflags@ \
 @cf_cflags@ \
 @VALGRIND_CFLAGS@ \
diff --git a/src/bin/e_main.c b/src/bin/e_main.c
index 3e36684..2781b86 100644
--- a/src/bin/e_main.c
+++ b/src/bin/e_main.c
@@ -372,6 +372,15 @@ main(int argc, char **argv)
    else
      e_first_frame = NULL;
 
+   TS("EFX Init");
+   if (!e_efx_init())
+     {
+        e_error_message_show(_("Enlightenment cannot initialize EFX!\n"));
+        _e_main_shutdown(-1);
+     }
+   TS("EFX Init Done");
+   _e_main_shutdown_push((void*)e_efx_shutdown);
+
    TS("EIO Init");
    if (!eio_init())
      {
diff --git a/src/bin/efx/e_Efx.h b/src/bin/efx/e_Efx.h
new file mode 100644
index 0000000..f668fdb
--- /dev/null
+++ b/src/bin/efx/e_Efx.h
@@ -0,0 +1,1076 @@
+#ifndef E_EFX_H
+#define E_EFX_H
+
+#include <Evas.h>
+#include <Ecore.h>
+
+#ifdef EAPI
+# undef EAPI
+#endif /* ifdef EAPI */
+
+#ifdef _WIN32
+# ifdef EFL_E_EFX_BUILD
+#  ifdef DLL_EXPORT
+#   define EAPI __declspec(dllexport)
+#  else /* ifdef DLL_EXPORT */
+#   define EAPI
+#  endif /* ! DLL_EXPORT */
+# else /* ifdef EFL_BUILD */
+#  define EAPI __declspec(dllimport)
+# endif /* ! EFL_BUILD */
+#else /* ifdef _WIN32 */
+# ifdef __GNUC__
+#  if __GNUC__ >= 4
+#   define EAPI __attribute__ ((visibility("default")))
+#  else /* if __GNUC__ >= 4 */
+#   define EAPI
+#  endif /* if __GNUC__ >= 4 */
+# else /* ifdef __GNUC__ */
+#  define EAPI
+# endif /* ifdef __GNUC__ */
+#endif /* ! _WIN32 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* ifdef __cplusplus */
+
+/**
+ * @mainpage E_Efx Library Documentation
+ *
+ * @version 1.7.99
+ * @date 2012
+ *
+ * E_Efx is the effects libraries.
+ *
+ * For a better reference, check the following groups:
+ * @li @ref E_Efx
+ * @li @ref E_Efx_Queue
+ * @li @ref E_Efx_Follow
+ * @li @ref E_Efx_Fade
+ * @li @ref E_Efx_Rotation
+ * @li @ref E_Efx_Move
+ * @li @ref E_Efx_Resize
+ *
+ * Please see the @ref authors page for contact details.
+ */
+
+/**
+ * @page authors Authors
+ *
+ * @author Mike Blumenkrantz <michael.blumenkra...@gmail.com>
+ * @author Vincent "caro" Torri  <vtorri at univ-evry dot fr>
+ *
+ * Please contact <enlightenment-de...@lists.sourceforge.net> to get in
+ * contact with the developers and maintainers.
+ */
+
+/**
+ * @file
+ * @brief These routines are used for E_Efx library interaction.
+ */
+
+/**
+ * @defgroup E_Efx General types and functions.
+ *
+ * @{
+ */
+
+/**
+ * Helper macro for simplifying specifying coordinate points as parameters
+ * @param X The x coordinate
+ * @param Y The y coordinate
+ * @ingroup E_Efx
+ */
+#define E_EFX_POINT(X, Y) \
+  &(Evas_Point){(X), (Y)}
+
+/**
+ * Helper macro for simplifying specifying colors as parameters
+ * @param R The red value
+ * @param G The blue value
+ * @param B The green value
+ * @ingroup E_Efx
+ */
+#define E_EFX_COLOR(R, G, B) \
+  &(E_Efx_Color){(R), (G), (B)}
+
+/**
+ * @typedef E_Efx_Map_Data
+ * @ingroup E_Efx
+ */
+typedef struct E_Efx_Map_Data E_Efx_Map_Data;
+
+/**
+ * @struct E_Efx_Map_Data
+ *
+ * This struct is provided to callbacks upon the completion of certain types
+ * of effects.
+ * It contains information about the current position of an object.
+ *
+ * @ingroup E_Efx
+ */
+struct E_Efx_Map_Data
+{
+   double rotation; /**< The current rotation (in degrees) of the object */
+   Evas_Point *rotate_center; /**< The current rotation center for the object 
*/
+   double zoom; /**< The current zoom amount of an object */
+   Evas_Point *zoom_center; /**< The current zoom center for the object */
+   Evas_Point *move_center; /**< The current move vertex for the object */
+   Evas_Point pan; /**< The current pan for the object */
+};
+
+/**
+ * @typedef E_Efx_End_Cb
+ *
+ * This is the callback type used to notify a user about the end of an effect.
+ * It is called instantly upon an effect terminating, but only if the effect
+ * has run to its full duration. Ending callbacks are NOT called upon
+ * stopping/resetting an effect.
+ *
+ * @param data The data passed when starting the effect
+ * @param e The current map data for an object
+ * @param obj The object
+ *
+ * @ingroup E_Efx
+ */
+typedef void (*E_Efx_End_Cb)(void *data, E_Efx_Map_Data *e, Evas_Object *obj);
+
+/**
+ * @enum _E_Efx_Effect_Speed
+ * @typedef E_Efx_Effect_Speed
+ *
+ * These values are used to set the speed at which an effect will occur.
+ * More information can be found by reading about Ecore_Animator objects.
+ *
+ * @ingroup E_Efx
+ */
+typedef enum _E_Efx_Effect_Speed
+{
+   E_EFX_EFFECT_SPEED_LINEAR = ECORE_POS_MAP_LINEAR,
+   E_EFX_EFFECT_SPEED_ACCELERATE = ECORE_POS_MAP_ACCELERATE,
+   E_EFX_EFFECT_SPEED_DECELERATE = ECORE_POS_MAP_DECELERATE,
+   E_EFX_EFFECT_SPEED_SINUSOIDAL = ECORE_POS_MAP_SINUSOIDAL
+} E_Efx_Effect_Speed;
+
+/**
+ * @brief
+ * Initialize the library
+ *
+ * This function must be called before any other e_efx functions.
+ * @return The number of times the library has been initialized,
+ * or @c 0 on failure.
+ *
+ * @see e_efx_shutdown()
+ *
+ * @ingroup E_Efx
+ */
+EAPI int e_efx_init(void);
+
+/**
+ * @brief
+ * Uninitialize the library
+ *
+ * This function unregisters the log domain and performs other cleanup
+ * routines.
+ *
+ * @see e_efx_init()
+ *
+ * @ingroup E_Efx
+ */
+EAPI void e_efx_shutdown(void);
+
+/**
+ * @brief
+ * Attempt to automatically move+resize an object according to its map
+ *
+ * This function attempts to move an object to its mapped position and resize
+ * it according to its mapped zoom. It will, after calculating, reset the zoom
+ * and rotate effects on the object,
+ * preserving only its orientation. Ideally, you will not notice any visible
+ * change after running this function,
+ * but it should be used carefully, as successive effects on a realized
+ * object will likely not behave as intended.
+ *
+ * @param obj The object on which to realize effects
+ *
+ * @ingroup E_Efx
+ */
+EAPI void e_efx_realize(Evas_Object *obj);
+
+
+/**
+ * @brief
+ * Reapply any E_EFX clips
+ *
+ * Call this function any time your object changes its clipping to ensure
+ * that effects which require clips continue to function as expected.
+ *
+ * @param obj The object to reapply clips for
+ *
+ * @ingroup E_Efx
+ */
+EAPI void e_efx_reclip(Evas_Object *obj);
+
+/**
+ * @brief
+ * Add bumpmapping on an object at a point
+ *
+ * This function will generate a bumpmap effect on @p obj at the coordinates
+ * given.
+ * @note The map is generated and displayed immediately upon calling this
+ * function
+ * @warning The calculations for this function are slow, and should not be used
+ * for realtime bumpmap generation; see the bumpmap test for details.
+ *
+ * @param obj The object to generate the map for
+ * @param x The X coordinate to center the map on
+ * @param y The Y coordinate to center the map on
+ * @return @c EINA_TRUE on successful map generation, else @c EINA_FALSE
+ *
+ * @ingroup E_Efx
+ */
+EAPI Eina_Bool e_efx_bumpmap(Evas_Object *obj, Evas_Coord x, Evas_Coord y);
+
+/**
+ * @}
+ */
+
+/**
+ * @typedef E_Efx_Color
+ *
+ * Handle to color used in effects.
+ *
+ * @ingroup E_Efx_Fade
+ */
+typedef struct E_Efx_Color E_Efx_Color;
+
+/**
+ * @struct E_Efx_Color
+ *
+ * This struct contains RGB data for setting colors in effects.
+ *
+ * @ingroup E_Efx_Fade
+ */
+struct E_Efx_Color
+{
+   unsigned char r; /**< Red */
+   unsigned char g; /**< Green */
+   unsigned char b; /**< Blue */
+};
+
+
+/**
+ * @defgroup E_Efx_Queue E_Efx Effects Queue
+ * @ingroup E_Efx
+ *
+ * @{
+ *
+ * With E_Efx it's possible to queue many effects with @ref 
e_efx_queue_append()
+ * and @ref e_efx_queue_prepend() and run them all with @ref e_efx_queue_run(),
+ * making complex animations much easier to be acomplished.
+ */
+
+/**
+ * @typedef E_Efx_Queued_Effect
+ *
+ * Effect handle, used to create and queue an effect.
+ *
+ * @ingroup E_Efx_Queue
+ */
+typedef struct E_Efx_Queued_Effect E_Efx_Queued_Effect;
+
+/**
+ * @typedef E_Efx_Queue_Data
+ *
+ * Queue data handle, used to specify a queued effect.
+ *
+ * @ingroup E_Efx_Queue
+ */
+typedef struct E_Efx_Queue_Data E_Efx_Queue_Data;
+
+/**
+ * Helper macro to perform a C99 cast which simplifies the queue
+ * effect parameter in queue-related functions.
+ * @param EFFECT The effect
+ * @ingroup E_Efx_Queue
+ */
+#define E_EFX_QUEUED_EFFECT(EFFECT) \
+  &(E_Efx_Queued_Effect){EFFECT}
+
+/**
+ * @enum _E_Efx_Effect_Type
+ * @typedef E_Efx_Effect_Type
+ *
+ * These values are used to set the speed at which an effect will occur.
+ * More information can be found by reading about Ecore_Animator objects.
+ *
+ * @ingroup E_Efx_Queue
+ */
+typedef enum _E_Efx_Effect_Type
+{
+   E_EFX_EFFECT_TYPE_ROTATE,
+   E_EFX_EFFECT_TYPE_ZOOM,
+   E_EFX_EFFECT_TYPE_MOVE,
+   E_EFX_EFFECT_TYPE_PAN,
+   E_EFX_EFFECT_TYPE_FADE,
+   E_EFX_EFFECT_TYPE_RESIZE
+} E_Efx_Effect_Type;
+
+/**
+ * @struct E_Efx_Queued_Effect
+ *
+ * This struct contains all the data necessary to create and queue
+ * an effect.
+ *
+ * @ingroup E_Efx_Queue
+ */
+struct E_Efx_Queued_Effect
+{
+   E_Efx_Effect_Type type;
+   union
+     {
+        struct
+        {
+           double degrees;
+           Evas_Point *center;
+        } rotation;
+        struct
+        {
+           double start;
+           double end;
+           Evas_Point *center;
+        } zoom;
+        struct
+        {
+           Evas_Point point;
+        } movement;
+        struct
+        {
+           E_Efx_Color color;
+           unsigned char alpha;
+        } fade;
+        struct
+        {
+           Evas_Point *point;
+           int w, h;
+        } resize;
+     } effect;
+};
+
+/**
+ * @brief
+ * Begin processing the queue
+ *
+ * Call this function after queuing some effects to start the animation
+ * sequence.
+ *
+ * @param obj The object to process the queue for
+ *
+ * @ingroup E_Efx_Queue
+ */
+EAPI void e_efx_queue_run(Evas_Object *obj);
+
+/**
+ * @brief
+ * Append an effect to the end of the queue
+ *
+ * Use this function to add a new effect to the end of the queue.
+ * The queue can be empty when using this function.
+ *
+ * @param obj The object to queue an effect for
+ * @param speed The effect speed
+ * @param effect The parameters of the desired effect
+ * @param total_time The time the effect should occur over
+ * @param cb The optional callback to call after this particular effect has
+ * completed
+ * @param data The data to pass to the callback
+ * @return The queued effect, or @c NULL on failure
+ *
+ * @see e_efx_queue_prepend()
+ * @see e_efx_queue_delete()
+ *
+ * @ingroup E_Efx_Queue
+ */
+EAPI E_Efx_Queue_Data *e_efx_queue_append(Evas_Object *obj, E_Efx_Effect_Speed 
speed, const E_Efx_Queued_Effect *effect, double total_time, E_Efx_End_Cb cb, 
const void *data);
+
+/**
+ * @brief
+ * Add an effect to the start of the queue
+ *
+ * Use this function to add a new effect to the start of the queue.
+ * The queue can be empty when using this function.
+ *
+ * @note If a queued effect is currently executing, the new effect will be
+ * added after it
+ *
+ * @param obj The object to queue an effect for
+ * @param speed The effect speed
+ * @param effect The parameters of the desired effect
+ * @param total_time The time the effect should occur over
+ * @param cb The optional callback to call after this particular effect has
+ * completed
+ * @param data The data to pass to the callback
+ * @return The queued effect, or @c NULL on failure
+ *
+ * @see e_efx_queue_append()
+ * @see e_efx_queue_delete()
+ *
+ * @ingroup E_Efx_Queue
+ */
+EAPI E_Efx_Queue_Data *e_efx_queue_prepend(Evas_Object *obj, 
E_Efx_Effect_Speed speed, const E_Efx_Queued_Effect *effect, double total_time, 
E_Efx_End_Cb cb, const void *data);
+
+/**
+ * @brief
+ * Promote an inactive effect to the start of the queue
+ *
+ * Use this function on an effect that is not currently executing to
+ * move it to the head of the queue.
+ * @note If another effect is currently executing, @p eqd will be moved
+ * directly after it.
+ * @param obj The object owning the effect
+ * @param eqd The effect
+ *
+ * @ingroup E_Efx_Queue
+ */
+EAPI void e_efx_queue_promote(Evas_Object *obj, E_Efx_Queue_Data *eqd);
+
+/**
+ * @brief
+ * Demote an inactive effect to the end of the queue
+ *
+ * Use this function on an effect that is not currently executing to
+ * move it to the tail of the queue.
+ *
+ * @param obj The object owning the effect
+ * @param eqd The effect
+ *
+ * @ingroup E_Efx_Queue
+ */
+EAPI void e_efx_queue_demote(Evas_Object *obj, E_Efx_Queue_Data *eqd);
+
+/**
+ * @brief
+ * Delete an effect from the queue
+ *
+ * This function will delete and unqueue a previously queued effect.
+ * @note Currently executing effects cannot be deleted; they must be manually
+ * stopped using the proper stop/reset function.
+ * @param obj The object owning the effect
+ * @param eqd The effect
+ *
+ * @ingroup E_Efx_Queue
+ */
+EAPI void e_efx_queue_delete(Evas_Object *obj, E_Efx_Queue_Data *eqd);
+
+/**
+ * @brief
+ * Delete all queued effects which are not currently executing
+ *
+ * This function will delete and unqueue all previously queued effects.
+ *
+ * @note Currently executing effects cannot be deleted; they must be manually
+ * stopped using the proper stop/reset function.
+ *
+ * @param obj The object owning the effects
+ *
+ * @ingroup E_Efx_Queue
+ */
+EAPI void e_efx_queue_clear(Evas_Object *obj);
+
+/**
+ * @brief
+ * Attach an effect to other.
+ *
+ * Append @p effect to subeffects list of @p eqd.
+ *
+ * @param eqd The effect
+ * @param speed The effect speed
+ * @param effect The parameters of the desired effect
+ * @param total_time The time the effect should occur over
+ * @param cb The optional callback to call after this particular effect has
+ * completed
+ * @param data The data to pass to the callback
+ * @return @c EINA_TRUE on success or @c EINA_FALSE on error.
+ *
+ * @ingroup E_Efx_Queue
+ */
+EAPI Eina_Bool e_efx_queue_effect_attach(E_Efx_Queue_Data *eqd, 
E_Efx_Effect_Speed speed, const E_Efx_Queued_Effect *effect, double total_time, 
E_Efx_End_Cb cb, const void *data);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup E_Efx_Follow E_Efx Follow
+ * @ingroup E_Efx
+ *
+ * @{
+ *
+ * Using @ref e_efx_follow() it's possible to cause objects to mimic others.
+ */
+
+/**
+ * @brief
+ * Cause one object to mimic the actions of another
+ *
+ * Using this function, @p follower will copy every effect set on @p obj
+ * until @ref e_efx_unfollow() is called on @p follower. Note that passing
+ * a "follower" object as @p obj will cause the object passed as @p follower
+ * to be chained to the follower's top-most "owner" object.
+ *
+ * @note Effects from an "owner" object will supercede any similar effects
+ * explicitly set on a "follower" object, and they will also probably
+ * break each other. Don't chain owners to other owners unless you know
+ * what you are doing.
+ *
+ * @param obj The object to follow the actions of
+ * @param follower The object to do the following
+ * @return @c EINA_TRUE on success, else @c EINA_FALSE
+ *
+ * @ingroup E_Efx_Follow
+ */
+EAPI Eina_Bool e_efx_follow(Evas_Object *obj, Evas_Object *follower);
+
+/**
+ * @brief
+ * Unchain a following object from its owner
+ *
+ * This function will cause @p obj to stop following its owner object.
+ * It takes effect immediately and cannot fail.
+ *
+ * @param obj The follower object
+ *
+ * @ingroup E_Efx_Follow
+ */
+EAPI void e_efx_unfollow(Evas_Object *obj);
+
+/**
+ * @brief
+ * Retrieve the list of following objects
+ *
+ * This function returns a copy of the list of objects following @p obj.
+ * The returned list must be manually freed with eina_list_free.
+ *
+ * @param obj The owner object
+ * @return A list of follower Evas_Objects, or @c NULL
+ *
+ * @ingroup E_Efx_Follow
+ */
+EAPI Eina_List *e_efx_followers_get(Evas_Object *obj);
+
+/**
+ * @brief
+ * Retrieve an object's leader object
+ *
+ * Use this function to return the object which @p obj is currently following.
+ *
+ * @param obj The object
+ * @return The leader object or NULL on failure
+ *
+ * @ingroup E_Efx_Follow
+ */
+EAPI Evas_Object *e_efx_leader_get(Evas_Object *obj);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup E_Efx_Fade E_Efx Fade Effect
+ * @ingroup E_Efx
+ *
+ * @{
+ *
+ * Fade effects are very frequently on elaborated interfaces. Using
+ * @ref e_efx_fade() it's possible to change color and alpha channel leading
+ * to fade in / out transitions.
+ */
+
+/**
+ * Helper macro to simplify specifying a fade effect for queue
+ * @param R Red
+ * @param G Green
+ * @param B Blue
+ * @param A Alpha
+ * @ingroup E_Efx_Fade
+ */
+#define E_EFX_EFFECT_FADE(R, G, B, A) \
+  .type = E_EFX_EFFECT_TYPE_FADE, .effect.fade = { .color = { .r = (R), .g = 
(G), .b = (B) }, .alpha = (A) }
+
+/**
+ * @brief
+ * Commence a fade effect
+ *
+ * Fade is an effect which allows the changing of color tint and alpha through
+ * the use of a clip set on an object. With this effect it is possible to
+ * have an object fade in/out or become tinted with another color.
+ *
+ * @note Black objects will only have their alpha values affected.
+ *
+ * @param obj The object to use for fading
+ * @param speed The speed to fade at
+ * @param ec The color to fade to
+ * @param alpha The alpha to fade to
+ * @param total_time The time the effect should occur over
+ * @param cb The optional callback to call when the effect completes
+ * @param data Optional data to pass to @p cb
+ * @return @c EINA_TRUE on successful queue of the animation, else
+ * @c EINA_FALSE
+ *
+ * @ingroup E_Efx_Fade
+ */
+EAPI Eina_Bool e_efx_fade(Evas_Object *obj, E_Efx_Effect_Speed speed, 
E_Efx_Color *ec, unsigned char alpha, double total_time, E_Efx_End_Cb cb, const 
void *data);
+
+/**
+ * @brief
+ * Stop fading of an object and remove the clip
+ *
+ * Use this function to immediately stop the fade of an object and restore
+ * its original color/alpha.
+ *
+ * @param obj An object
+ *
+ * @ingroup E_Efx_Fade
+ */
+EAPI void e_efx_fade_reset(Evas_Object *obj);
+
+/**
+ * Stop fading of an object
+ *
+ * Use this function to immediately stop the fade of an object without
+ * restoring its original color/alpha.
+ *
+ * @param obj An object
+ *
+ * @ingroup E_Efx_Fade
+ */
+EAPI void e_efx_fade_stop(Evas_Object *obj);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup E_Efx_Rotation E_Efx Rotation Effects
+ * @ingroup E_Efx
+ *
+ * @{
+ *
+ * There are two ways of rotation an object: using @ref e_efx_rotate(),
+ * where the object will rotate per specified degrees around a  central point,
+ * or @ref e_efx_spin_start(), making the object rotates specified degrees per
+ * second until it's explicitly stopped.
+ */
+
+/**
+ * Helper macro to simplify specifying a rotation effect for queue
+ * @param DEGREES Number of degrees to rotate
+ * @param CENTER An Evas_Point* to rotate around, or @c NULL
+ * @ingroup E_Efx_Rotation
+ */
+#define E_EFX_EFFECT_ROTATE(DEGREES, CENTER) \
+  .type = E_EFX_EFFECT_TYPE_ROTATE, .effect.rotation = { .degrees = (DEGREES), 
.center = (CENTER) }
+
+/**
+ * @brief
+ * Rotate an object
+ *
+ * This function allows rotation of an object around an optional point with
+ * specified rotation amount, speed of effect, and duration of effect.
+ *
+ * @note @p cb will ONLY be called upon successful completion of the effect.
+ * @note The actual location of the object will not change; this is a map 
effect
+ *
+ * @param obj The object to rotate
+ * @param speed The speed to rotate at
+ * @param degrees The amount to rotate
+ * @param center The optional point to rotate around
+ * @param total_time The time that the effect should occur over
+ * @param cb The optional callback to call when the effect completes
+ * @param data Optional data to pass to @p cb
+ * @return @c EINA_TRUE on successful queue of the animation, else
+ * @c EINA_FALSE
+ *
+ * @ingroup E_Efx_Rotation
+ */
+EAPI Eina_Bool e_efx_rotate(Evas_Object *obj, E_Efx_Effect_Speed speed, double 
degrees, const Evas_Point *center, double total_time, E_Efx_End_Cb cb, const 
void *data);
+
+/**
+ * @brief
+ * Stop rotation of an object and remove the map
+ *
+ * Use this function to immediately stop the rotation of an object and restore
+ * its original position
+ *
+ * @param obj An object
+ *
+ * @ingroup E_Efx_Rotation
+ */
+EAPI void e_efx_rotate_reset(Evas_Object *obj);
+
+/**
+ * @brief
+ * Stop rotation of an object
+ *
+ * Use this function to immediately stop the rotation of an object without 
restoring its original position
+ * @param obj An object
+ *
+ * @ingroup E_Efx_Rotation
+ */
+EAPI void e_efx_rotate_stop(Evas_Object *obj);
+
+/**
+ * @brief
+ * Spin an object
+ *
+ * This function allows rotation of an object around an optional point.
+ * The object
+ * will rotate at @p dps degrees per second until manually stopped.
+ * @note The actual location of the object will not change; this is a map 
effect
+ *
+ * @param obj The object to rotate
+ * @param dps The degrees per second to rotate
+ * @param center The optional point to rotate around
+ * @return @c EINA_TRUE on successful queue of the animation, else
+ * @c EINA_FALSE
+ *
+ * @ingroup E_Efx_Rotation
+ */
+EAPI Eina_Bool e_efx_spin_start(Evas_Object *obj, long dps, const Evas_Point 
*center);
+
+/**
+ * @brief
+ * Stop rotation of an object and remove the map
+ *
+ * Use this function to immediately stop the rotation of an object and restore
+ * its original position
+ *
+ * @param obj An object
+ *
+ * @ingroup E_Efx_Rotation
+ */
+EAPI void e_efx_spin_reset(Evas_Object *obj);
+
+/**
+ * @brief
+ * Stop rotation of an object
+ *
+ * Use this function to immediately stop the rotation of an object without
+ * restoring its original position
+ *
+ * @param obj An object
+ *
+ * @ingroup E_Efx_Rotation
+ */
+EAPI void e_efx_spin_stop(Evas_Object *obj);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup E_Efx_Zoom E_Efx Zoom Effects
+ * @ingroup E_Efx
+ *
+ * @{
+ *
+ * It's possible to add zoom in / out effects using @ref e_efx_zoom()
+ */
+
+/**
+ * Helper macro to simplify specifying a zoom effect for queue
+ * @param START Starting zoom factor
+ * @param END Ending zoom factor
+ * @param CENTER An Evas_Point* to zoom at, or @c NULL
+ * @ingroup E_Efx_Zoom
+ */
+#define E_EFX_EFFECT_ZOOM(START, END, CENTER) \
+  .type = E_EFX_EFFECT_TYPE_ZOOM, .effect.zoom = { .start = (START), .end = 
(END), .center = (CENTER) }
+
+/**
+ * @brief
+ * Zoom an object
+ *
+ * This function allows zooming of an object at an optional point with
+ * specified zoom amount, starting amount, speed of effect, and time to
+ * complete effect.
+ *
+ * @note @p cb will ONLY be called upon successful completion of the effect.
+ * @note The actual location of the object will not change; this is a map 
effect
+ *
+ * @param obj The object to zoom
+ * @param speed The speed to zoom at
+ * @param starting_zoom The zoom amount to start at, or 0 to use the
+ * previously existing zoom amount (defaults to 1.0)
+ * @param ending_zoom The zoom amount to end at
+ * @param zoom_point The optional point to center the zoom on
+ * @param total_time The time that the effect should occur over
+ * @param cb The optional callback to call when the effect completes
+ * @param data Optional data to pass to @p cb
+ * @return @c EINA_TRUE on successful queue of the animation, else
+ * @c EINA_FALSE
+ *
+ * @ingroup E_Efx_Zoom
+ */
+EAPI Eina_Bool e_efx_zoom(Evas_Object *obj, E_Efx_Effect_Speed speed, double 
starting_zoom, double ending_zoom, const Evas_Point *zoom_point, double 
total_time, E_Efx_End_Cb cb, const void *data);
+
+/**
+ * @brief
+ * Stop zooming of an object and remove the map
+ *
+ * Use this function to immediately stop the zoom of an object and restore its
+ * original position
+ *
+ * @param obj An object
+ *
+ * @ingroup E_Efx_Zoom
+ */
+EAPI void e_efx_zoom_reset(Evas_Object *obj);
+
+/**
+ * @brief
+ * Stop zooming of an object
+ *
+ * Use this function to immediately stop the zoom of an object without
+ * restoring its original position
+ *
+ * @param obj An object
+ *
+ * @ingroup E_Efx_Zoom
+ */
+EAPI void e_efx_zoom_stop(Evas_Object *obj);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup E_Efx_Move E_Efx Movement Effects
+ * @ingroup E_Efx
+ *
+ * @{
+ *
+ * Object movement can be done directly using @ref e_efx_move() or using
+ * @ref e_efx_pan();
+ * A "pan" is a motion which shifts the view of an object from one point to
+ * another.
+ */
+
+/**
+ * Helper macro to simplify specifying a movement effect for queue
+ * @param X x coordinate to move to
+ * @param Y y coordinate to move to
+ * @ingroup E_Efx_Move
+ */
+#define E_EFX_EFFECT_MOVE(X, Y) \
+  .type = E_EFX_EFFECT_TYPE_MOVE, .effect.movement.point = { .x = (X), .y = 
(Y) }
+
+/**
+ * Helper macro to simplify specifying a pan effect for queue
+ * @param X horizontal distance to pan
+ * @param Y vertical distance to pan
+ * @ingroup E_Efx_Move
+ */
+#define E_EFX_EFFECT_PAN(X, Y) \
+  .type = E_EFX_EFFECT_TYPE_PAN, .effect.movement.point = { .x = (X), .y = (Y) 
}
+
+/**
+ * @brief
+ * Move an object
+ *
+ * This function animates the movement of an object to a specified point using
+ * a designated speed of effect and time to complete the effect.
+ *
+ * @note @p cb will ONLY be called upon successful completion of the effect.
+ * @note The actual location of the object WILL change; this is NOT a map 
effect
+ *
+ * @param obj The object to zoom
+ * @param speed The speed to move at
+ * @param end_point The point to move to
+ * @param total_time The time that the effect should occur over
+ * @param cb The optional callback to call when the effect completes
+ * @param data Optional data to pass to @p cb
+ * @return @c EINA_TRUE on successful queue of the animation, else
+ * @c EINA_FALSE
+ *
+ * @ingroup E_Efx_Move
+ */
+EAPI Eina_Bool e_efx_move(Evas_Object *obj, E_Efx_Effect_Speed speed, const 
Evas_Point *end_point, double total_time, E_Efx_End_Cb cb, const void *data);
+
+/**
+ * @brief
+ * Move an object in circle
+ *
+ * This function animates the movement of an object around a specified
+ * point using a designated degrees, speed of effect and time to complete
+ * the effect.
+ *
+ * @note @p cb will ONLY be called upon successful completion of the effect.
+ * @note The actual location of the object WILL change; this is NOT a map 
effect
+ *
+ * @param obj The object to zoom
+ * @param speed The speed to move at
+ * @param center The center point to move around
+ * @param degrees The amount of degrees to move around the center
+ * @param total_time The time that the effect should occur over
+ * @param cb The optional callback to call when the effect completes
+ * @param data Optional data to pass to @p cb
+ * @return @c EINA_TRUE on successful queue of the animation, else
+ * @c EINA_FALSE
+ *
+ * @ingroup E_Efx_Move
+ */
+EAPI Eina_Bool e_efx_move_circle(Evas_Object *obj, E_Efx_Effect_Speed speed, 
const Evas_Point *center, int degrees, double total_time, E_Efx_End_Cb cb, 
const void *data);
+
+/**
+ * @brief
+ * Stop moving of an object
+ *
+ * Use this function to immediately stop the move of an object and restore its
+ * original position
+ *
+ * @param obj An object
+ *
+ * @ingroup E_Efx_Move
+ */
+EAPI void e_efx_move_reset(Evas_Object *obj);
+
+/**
+ * @brief
+ * Stop moving of an object
+ *
+ * Use this function to immediately stop the move of an object without
+ * restoring its original position
+ *
+ * @param obj An object
+ *
+ * @ingroup E_Efx_Move
+ */
+EAPI void e_efx_move_stop(Evas_Object *obj);
+
+/**
+ * @brief
+ * Initialize an object for panning
+ *
+ * Use this function immediately after creating an object if you plan
+ * to place other layers on top of it. This prevents the newly-created pan
+ * object from obscuring other objects. No visible changes will occur from
+ * calling this function.
+ *
+ * @param obj The object to set up for panning
+ * @return @c EINA_TRUE on success, else @c EINA_FALSE
+ *
+ * @ingroup E_Efx_Move
+ */
+EAPI Eina_Bool e_efx_pan_init(Evas_Object *obj);
+
+/**
+ * @brief
+ * Commence a pan effect
+ *
+ * A "pan" is a motion which shifts the view of an object from one point to
+ * another.
+ * This function sets up (calling e_efx_pan_init() if necessary) and runs a pan
+ * effect
+ * which will shift the view of the canvas in the directions specified by
+ * @p distance.
+ *
+ * @param obj The object to use for panning
+ * @param speed The speed to pan at
+ * @param distance The relative X and Y distance to move during the pan
+ * @param total_time The time that the effect should occur over
+ * @param cb The optional callback to call when the effect completes
+ * @param data Optional data to pass to @p cb
+ * @return @c EINA_TRUE on successful queue of the animation, else
+ * @c EINA_FALSE
+ *
+ * @ingroup E_Efx_Move
+ */
+EAPI Eina_Bool e_efx_pan(Evas_Object *obj, E_Efx_Effect_Speed speed, const 
Evas_Point *distance, double total_time, E_Efx_End_Cb cb, const void *data);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup E_Efx_Resize E_Efx Resize Effect
+ * @ingroup E_Efx
+ *
+ * @{
+ *
+ * FIXME DESC
+ *
+ */
+
+/**
+ * Helper macro to simplify specifying a resize effect for queue
+ * @param POSITION The position for the resized top-left corner to end in
+ * @param W The final width of the object
+ * @param H The final height of the object
+ * @ingroup E_Efx_Resize
+ */
+#define E_EFX_EFFECT_RESIZE(POSITION, W, H) \
+  .type = E_EFX_EFFECT_TYPE_RESIZE, .effect.resize = { .point = (POSITION), .w 
= (W), .h = (H) }
+
+/**
+ * @brief
+ * Resize an object
+ *
+ * This function animates the resizing of an object to a specified width and
+ * height using a designated speed of effect and time to complete the effect.
+ *
+ * @note @p cb will ONLY be called upon successful completion of the effect.
+ * @note The actual size of the object WILL change; this is NOT a map effect
+ *
+ * @param obj The object to zoom
+ * @param speed The speed to move at
+ * @param position The point to move to. If not provided, the object won't 
move.
+ * @param w The final object width
+ * @param h The final object height
+ * @param total_time The time that the effect should occur over
+ * @param cb The optional callback to call when the effect completes
+ * @param data Optional data to pass to @p cb
+ * @return @c EINA_TRUE on successful queue of the animation, else
+ * @c EINA_FALSE
+ *
+ * @ingroup E_Efx_Resize
+ */
+EAPI Eina_Bool e_efx_resize(Evas_Object *obj, E_Efx_Effect_Speed speed, const 
Evas_Point *position, int w, int h, double total_time, E_Efx_End_Cb cb, const 
void *data);
+
+/**
+ * @brief
+ * Stop resizing of an object
+ *
+ * Use this function to immediately stop the resize of an object and restore 
its
+ * original size
+ *
+ * @param obj An object
+ *
+ * @ingroup E_Efx_Resize
+ */
+EAPI void e_efx_resize_reset(Evas_Object *obj);
+
+/**
+ * @brief
+ * Stop resizing of an object
+ *
+ * Use this function to immediately stop the resize of an object without
+ * restoring its original size
+ *
+ * @param obj An object
+ *
+ * @ingroup E_Efx_Resize
+ */
+EAPI void e_efx_resize_stop(Evas_Object *obj);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#undef EAPI
+
+#endif
diff --git a/src/bin/efx/e_efx_private.h b/src/bin/efx/e_efx_private.h
new file mode 100644
index 0000000..4b5e6a5
--- /dev/null
+++ b/src/bin/efx/e_efx_private.h
@@ -0,0 +1,128 @@
+#ifndef E_EFX_PRIVATE_H
+#define E_EFX_PRIVATE_H
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <math.h>
+
+#include <Evas.h>
+#include <Ecore.h>
+
+#include "e_Efx.h"
+
+#define DBG(...)            EINA_LOG_DOM_DBG(_e_efx_log_dom, __VA_ARGS__)
+#define INF(...)            EINA_LOG_DOM_INFO(_e_efx_log_dom, __VA_ARGS__)
+#define WRN(...)            EINA_LOG_DOM_WARN(_e_efx_log_dom, __VA_ARGS__)
+#define ERR(...)            EINA_LOG_DOM_ERR(_e_efx_log_dom, __VA_ARGS__)
+#define CRI(...)            EINA_LOG_DOM_CRIT(_e_efx_log_dom, __VA_ARGS__)
+
+#ifdef EAPI
+# undef EAPI
+#endif /* ifdef EAPI */
+
+#ifdef _WIN32
+# ifdef EFL_E_EFX_BUILD
+#  ifdef DLL_EXPORT
+#   define EAPI __declspec(dllexport)
+#  else /* ifdef DLL_EXPORT */
+#   define EAPI
+#  endif /* ! DLL_EXPORT */
+# else /* ifdef EFL_BUILD */
+#  define EAPI __declspec(dllimport)
+# endif /* ! EFL_BUILD */
+#else /* ifdef _WIN32 */
+# ifdef __GNUC__
+#  if __GNUC__ >= 4
+#   define EAPI __attribute__ ((visibility("default")))
+#  else /* if __GNUC__ >= 4 */
+#   define EAPI
+#  endif /* if __GNUC__ >= 4 */
+# else /* ifdef __GNUC__ */
+#  define EAPI
+# endif /* ifdef __GNUC__ */
+#endif /* ! _WIN32 */
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+static const char *e_efx_speed_str[] =
+{
+   "LINEAR", "ACCELERATE", "DECELERATE", "SINUSOIDAL"
+};
+
+extern int _e_efx_log_dom;
+
+typedef struct E_EFX E_EFX;
+
+struct E_EFX
+{
+   Evas_Object *obj;
+   E_EFX *owner;
+   void *spin_data;
+   void *rotate_data;
+   void *zoom_data;
+   void *move_data;
+   void *bumpmap_data;
+   void *pan_data;
+   void *fade_data;
+   void *resize_data;
+   E_Efx_Map_Data map_data;
+   Eina_List *followers;
+   Eina_List *queue;
+};
+
+void _e_efx_zoom_calc(void *, void *, Evas_Object *obj, Evas_Map *map);
+void _e_efx_rotate_calc(void *, void *, Evas_Object *obj, Evas_Map *map);
+void _e_efx_spin_calc(void *, void *, Evas_Object *obj, Evas_Map *map);
+void _e_efx_resize_adjust(E_EFX *e, int *x, int *y);
+
+#define E_EFX_MAPS_APPLY_ALL EINA_TRUE, EINA_TRUE, EINA_TRUE
+#define E_EFX_MAPS_APPLY_ROTATE EINA_TRUE, EINA_FALSE, EINA_FALSE
+#define E_EFX_MAPS_APPLY_SPIN EINA_FALSE, EINA_TRUE, EINA_FALSE
+#define E_EFX_MAPS_APPLY_ZOOM EINA_FALSE, EINA_FALSE, EINA_TRUE
+#define E_EFX_MAPS_APPLY_ROTATE_SPIN EINA_TRUE, EINA_TRUE, EINA_FALSE
+void e_efx_maps_apply(E_EFX *e, Evas_Object *obj, Evas_Map *map, Eina_Bool 
rotate, Eina_Bool spin, Eina_Bool zoom);
+
+E_EFX *e_efx_new(Evas_Object *obj);
+void e_efx_free(E_EFX *e);
+Evas_Map *e_efx_map_new(Evas_Object *obj);
+void e_efx_map_set(Evas_Object *obj, Evas_Map *map);
+Eina_Bool e_efx_rotate_center_init(E_EFX *e, const Evas_Point *center);
+Eina_Bool e_efx_zoom_center_init(E_EFX *e, const Evas_Point *center);
+Eina_Bool e_efx_move_center_init(E_EFX *e, const Evas_Point *center);
+void e_efx_rotate_helper(E_EFX *e, Evas_Object *obj, Evas_Map *map, double 
degrees);
+void e_efx_clip_setup(Evas_Object *obj, Evas_Object *clip);
+void e_efx_fade_reclip(void *efd);
+
+#define E_EFX_QUEUE_CHECK(X) do \
+   { \
+      Eina_Bool run; \
+      E_EFX *ee = (X)->e; \
+      run = e_efx_queue_complete((X)->e, (X)); \
+      if ((X)->cb) (X)->cb((X)->data, &(X)->e->map_data, (X)->e->obj); \
+      if (run) e_efx_queue_process(ee); \
+   } while (0)
+Eina_Bool e_efx_queue_complete(E_EFX *e, void *effect_data);
+void e_efx_queue_process(E_EFX *e);
+
+static inline void
+_size_debug(Evas_Object *obj)
+{
+   Evas_Coord x, y, w, h;
+   evas_object_geometry_get(obj, &x, &y, &w, &h);
+   DBG("%s %p: x=%d,y=%d,w=%d,h=%d", evas_object_visible_get(obj) ? "vis" : 
"hid", obj, x, y, w, h);
+}
+
+static inline void
+_color_debug(Evas_Object *obj)
+{
+   Evas_Coord r, g, b, a;
+   evas_object_color_get(obj, &r, &g, &b, &a);
+   DBG("%d/%d/%d/%d", MIN(r, a), MIN(g, a), MIN(b, a), a);
+}
+
+#define HIT DBG("HIT")
+
+#endif
diff --git a/src/bin/efx/efx.c b/src/bin/efx/efx.c
new file mode 100644
index 0000000..7c2971e
--- /dev/null
+++ b/src/bin/efx/efx.c
@@ -0,0 +1,65 @@
+#include "e_efx_private.h"
+
+int _e_efx_log_dom = -1;
+static int _e_efx_init_count = 0;
+
+static uint64_t _e_efx_obj_count = 0;
+
+E_EFX *
+e_efx_new(Evas_Object *obj)
+{
+   E_EFX *e;
+   e = calloc(1, sizeof(E_EFX));
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
+   e->obj = obj;
+   evas_object_data_set(obj, "e_efx-data", e);
+   _e_efx_obj_count++;
+   return e;
+}
+
+void
+e_efx_free(E_EFX *e)
+{
+   E_EFX *ef;
+   if (e->zoom_data || e->resize_data || e->rotate_data || e->spin_data || 
e->move_data || e->bumpmap_data || e->pan_data || e->fade_data || e->queue) 
return;
+   DBG("freeing e_efx for %p", e->obj);
+   EINA_LIST_FREE(e->followers, ef)
+     e_efx_free(ef);
+   evas_object_data_del(e->obj, "e_efx-data");
+   e_efx_map_set(e->obj, NULL);
+   free(e->map_data.rotate_center);
+   free(e->map_data.zoom_center);
+   if (!(--_e_efx_obj_count))
+     {
+        if (!_e_efx_init_count)
+          e_efx_shutdown();
+     }
+   free(e);
+}
+
+EAPI int
+e_efx_init(void)
+{
+   if (++_e_efx_init_count > 1) return _e_efx_init_count;
+
+   if (eina_init() < 1) goto err;
+
+   _e_efx_log_dom = eina_log_domain_register("e_efx", EINA_COLOR_GREEN);
+   if (_e_efx_log_dom < 0) goto lgerr;
+   return _e_efx_init_count;
+lgerr:
+   eina_shutdown();
+err:
+   return --_e_efx_init_count;
+   (void)e_efx_speed_str;
+}
+
+EAPI void
+e_efx_shutdown(void)
+{
+   if (_e_efx_init_count && (--_e_efx_init_count != 0)) return;
+   if (_e_efx_obj_count) return;
+   eina_log_domain_unregister(_e_efx_log_dom);
+   _e_efx_log_dom = -1;
+   eina_shutdown();
+}
diff --git a/src/bin/efx/efx_bumpmapping.c b/src/bin/efx/efx_bumpmapping.c
new file mode 100644
index 0000000..b254770
--- /dev/null
+++ b/src/bin/efx/efx_bumpmapping.c
@@ -0,0 +1,186 @@
+#include "e_efx_private.h"
+#include <math.h>
+
+#define DATA8 unsigned char
+#define A_VAL(p) (((DATA8 *)(p))[3])
+#define R_VAL(p) (((DATA8 *)(p))[2])
+#define G_VAL(p) (((DATA8 *)(p))[1])
+#define B_VAL(p) (((DATA8 *)(p))[0])
+
+typedef struct E_Efx_Bumpmap_Data
+{
+   E_EFX *e;
+   int x;
+   int y;
+   int z;
+   int depth;
+   int red;
+   int green;
+   int blue;
+   int ambient;
+   unsigned int *img_data;
+} E_Efx_Bumpmap_Data;
+
+static void
+_bumpmap(E_Efx_Bumpmap_Data *ebd)
+{
+   Evas_Object  *o;
+   int w;
+   int h;
+   int i, j;
+
+   int x;
+   int y;
+   int z;
+   int depth;
+   int red;
+   int green;
+   int blue;
+   int ambient;
+
+   int z_2, lightx, lighty;
+   int mx;
+   int my;
+   unsigned int *d1;
+   unsigned int *src;
+   unsigned int *mp;
+   unsigned int *mpy;
+   unsigned int *mpp;
+
+   x = ebd->x;
+   y = ebd->y;
+   z = ebd->z;
+
+   red = ebd->red / 0x100;
+   green = ebd->green / 0x100;
+   blue = ebd->blue / 0x100;
+   ambient = ebd->ambient / 0x100;
+   depth = ebd->depth / 0x100;
+   depth /= (255 * (255 + 255 + 255));
+   z_2 = z * z;
+
+   o = ebd->e->obj;
+   evas_object_image_size_get(o, &w, &h);
+   if ((!w) || (!h)) return;
+
+   d1 = malloc(w * h * sizeof(int));
+   memcpy(d1, ebd->img_data, w * h * sizeof(int));
+   src = d1;
+
+   mpp = ebd->img_data;
+
+   my = h;
+   lighty = -y;
+   for (j = h; --j >= 0;)
+     {
+       mp = mpp;
+       mpp += w;
+       if (--my <= 0)
+         {
+           mpp -= w * h;
+           my = h;
+         }
+       mpy = mpp;
+       mx = w;
+       lightx = -x;
+       i = w - 1;
+       do
+         {
+            double v;
+            int r, g, b, gr, x1, y_1;
+
+            gr = A_VAL(mp) * (R_VAL(mp) + G_VAL(mp) + B_VAL(mp));
+            y_1 = depth * (A_VAL(mpy) * (R_VAL(mpy) +
+                                                G_VAL(mpy) +
+                                                B_VAL(mpy)) - gr);
+            mp++;
+            mpy++;
+            if (--mx <= 0)
+              {
+                mp -= w;
+                mpy -= w;
+                mx = w;
+              }
+            x1 = depth * (A_VAL(mp) * (R_VAL(mp) +
+                                           G_VAL(mp) + B_VAL(mp)) - gr);
+            v = x1 * lightx + y_1 * lighty + z;
+            v /= sqrt(((x1 * x1) + (y_1 * y_1) + 1) * ((lightx * lightx) + 
(lighty * lighty) + z_2));
+            v += ambient;
+            r = v * R_VAL(src) * red;
+            g = v * G_VAL(src) * green;
+            b = v * B_VAL(src) * blue;
+            if (r < 0)
+              r = 0;
+            else if (r > 255)
+              r = 255;
+            if (g < 0)
+              g = 0;
+            else if (g > 255)
+              g = 255;
+            if (b < 0)
+              b = 0;
+            else if (b > 255)
+              b = 255;
+            R_VAL(src) = r;
+            G_VAL(src) = g;
+            B_VAL(src) = b;
+
+            lightx++;
+            src++;
+         } while (--i >= 0);
+       lighty++;
+     }
+
+   evas_object_image_data_set(o, d1);
+   evas_object_image_data_update_add(o, 0, 0, w, h);
+}
+
+EAPI Eina_Bool
+e_efx_bumpmap(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
+{
+   E_EFX *e;
+   E_Efx_Bumpmap_Data *ebd;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e)
+     e = e_efx_new(obj);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, EINA_FALSE);
+
+   if (!e->bumpmap_data)
+     e->bumpmap_data = calloc(1, sizeof(E_Efx_Bumpmap_Data));
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e->bumpmap_data, EINA_FALSE);
+   ebd = e->bumpmap_data;
+   ebd->e = e;
+   ebd->x = x;
+   ebd->y = y;
+   ebd->z = 30;
+   ebd->depth = 0x200;
+   ebd->red = 0x200;
+   ebd->green = 0x200;
+   ebd->blue = 0x200;
+   ebd->ambient = 0;
+   if (!ebd->img_data)
+     {
+       unsigned int *m;
+       int w;
+       int h;
+
+       evas_object_image_size_get(obj, &w, &h);
+       m = (unsigned int *)evas_object_image_data_get(obj, 1);
+       ebd->img_data = (unsigned int *)malloc(w * h * sizeof(unsigned int));
+       if (!ebd->img_data)
+         {
+           free(ebd);
+           return EINA_FALSE;
+         }
+       printf("memcpy\n");
+       memcpy(ebd->img_data, m, (w * h * sizeof(unsigned int)));
+     }
+
+   _bumpmap(ebd);
+
+   return EINA_TRUE;
+   (void)e_efx_speed_str;
+}
diff --git a/src/bin/efx/efx_fade.c b/src/bin/efx/efx_fade.c
new file mode 100644
index 0000000..158b689
--- /dev/null
+++ b/src/bin/efx/efx_fade.c
@@ -0,0 +1,184 @@
+#include "e_efx_private.h"
+
+typedef struct E_Efx_Fade_Data
+{
+   E_EFX *e;
+   E_Efx_Effect_Speed speed;
+   Ecore_Animator *anim;
+   Evas_Object *clip;
+   E_Efx_Color start;
+   E_Efx_Color color;
+   unsigned char alpha[2];
+   E_Efx_End_Cb cb;
+   void *data;
+} E_Efx_Fade_Data;
+
+
+static void
+_clip_setup(void *data EINA_UNUSED, Evas *evas EINA_UNUSED, Evas_Object *obj, 
void *event_info EINA_UNUSED)
+{
+   E_EFX *e;
+   E_Efx_Fade_Data *efd;
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) return;
+   efd = e->fade_data;
+   e_efx_clip_setup(obj, efd->clip);
+}
+
+static void
+_obj_del(E_Efx_Fade_Data *efd, Evas *evas EINA_UNUSED, Evas_Object *obj, void 
*event_info EINA_UNUSED)
+{
+   if (efd->anim) ecore_animator_del(efd->anim);
+   evas_object_event_callback_del_full(efd->e->obj, EVAS_CALLBACK_RESIZE, 
(Evas_Object_Event_Cb)_clip_setup, efd);
+   evas_object_event_callback_del_full(efd->e->obj, EVAS_CALLBACK_MOVE, 
(Evas_Object_Event_Cb)_clip_setup, efd);
+   if (efd->clip)
+     {
+        Evas_Object *clip;
+
+        clip = evas_object_clip_get(efd->clip);
+        evas_object_clip_unset(efd->e->obj);
+        evas_object_del(efd->clip);
+        efd->clip = NULL;
+        if (clip && (!obj)) //obj is only passed during actual del
+          evas_object_clip_set(efd->e->obj, clip);
+     }
+   efd->e->fade_data = NULL;
+   if ((!efd->e->owner) && (!efd->e->followers)) e_efx_free(efd->e);
+   free(efd);
+}
+
+static Eina_Bool
+_fade_cb(E_Efx_Fade_Data *efd, double pos)
+{
+   double factor;
+   unsigned char r, g, b, a;
+
+   if (pos < 1.0)
+     {
+        r = efd->start.r;
+        g = efd->start.g;
+        b = efd->start.b;
+        a = efd->alpha[0];
+        factor = ecore_animator_pos_map(pos, efd->speed, 0, 0);
+        if (efd->color.r != efd->start.r)
+          r -= lround(factor * ((int)efd->start.r - (int)efd->color.r));
+        if (efd->color.g != efd->start.g)
+          g -= lround(factor * ((int)efd->start.g - (int)efd->color.g));
+        if (efd->color.b != efd->start.b)
+          b -= lround(factor * ((int)efd->start.b - (int)efd->color.b));
+        if (efd->alpha[0] != efd->alpha[1])
+          a -= lround(factor * ((int)efd->alpha[0] - (int)efd->alpha[1]));
+        evas_object_color_set(efd->clip, MIN(r, a), MIN(g, a), MIN(b, a), a);
+//        _color_debug(efd->clip);
+        return EINA_TRUE;
+     }
+   else
+     /* lround will usually be off by 1 at the end, so we manually set this 
here */
+     evas_object_color_set(efd->clip, MIN(efd->color.r, efd->alpha[1]), 
MIN(efd->color.g, efd->alpha[1]), MIN(efd->color.b, efd->alpha[1]), 
efd->alpha[1]);
+
+   efd->anim = NULL;
+   E_EFX_QUEUE_CHECK(efd);
+   return EINA_TRUE;
+}
+
+static void
+_fade_stop(Evas_Object *obj, Eina_Bool reset)
+{
+   E_EFX *e;
+   E_Efx_Fade_Data *efd;
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if ((!e) || (!e->fade_data)) return;
+   efd = e->fade_data;
+   if (reset)
+     {
+        evas_object_event_callback_del_full(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, efd);
+        evas_object_event_callback_del_full(obj, EVAS_CALLBACK_RESIZE, 
(Evas_Object_Event_Cb)_clip_setup, efd);
+        evas_object_event_callback_del_full(obj, EVAS_CALLBACK_MOVE, 
(Evas_Object_Event_Cb)_clip_setup, efd);
+        if (e_efx_queue_complete(efd->e, efd))
+          e_efx_queue_process(efd->e);
+        _obj_del(efd, NULL, NULL, NULL);
+        INF("reset faded object %p", obj);
+     }
+   else
+     {
+        INF("stopped faded object %p", obj);
+        if (efd->anim) ecore_animator_del(efd->anim);
+        if (e_efx_queue_complete(efd->e, efd))
+          e_efx_queue_process(efd->e);
+     }
+}
+
+void
+e_efx_fade_reclip(void *fade_data)
+{
+   E_Efx_Fade_Data *efd = fade_data;
+   Evas_Object *clip;
+
+   clip = evas_object_clip_get(efd->e->obj);
+   if (clip == efd->clip) return;
+   evas_object_clip_set(efd->e->obj, efd->clip);
+   if (clip)
+     evas_object_clip_set(efd->clip, clip);
+   e_efx_clip_setup(efd->e->obj, efd->clip);
+}
+
+EAPI Eina_Bool
+e_efx_fade(Evas_Object *obj, E_Efx_Effect_Speed speed, E_Efx_Color *ec, 
unsigned char alpha, double total_time, E_Efx_End_Cb cb, const void *data)
+{
+   E_EFX *e;
+   E_Efx_Fade_Data *efd;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) e = e_efx_new(obj);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, EINA_FALSE);
+   efd = e->fade_data;
+   if (!efd)
+     {
+        e->fade_data = efd = calloc(1, sizeof(E_Efx_Fade_Data));
+        EINA_SAFETY_ON_NULL_RETURN_VAL(efd, EINA_FALSE);
+        evas_object_event_callback_add(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, e->fade_data);
+        evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, 
(Evas_Object_Event_Cb)_clip_setup, e->fade_data);
+        evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, 
(Evas_Object_Event_Cb)_clip_setup, e->fade_data);
+        efd->clip = evas_object_rectangle_add(evas_object_evas_get(obj));
+     }
+
+   efd->e = e;
+   e_efx_fade_reclip(efd);
+   evas_object_show(efd->clip);
+   efd->alpha[1] = alpha;
+   efd->cb = cb;
+   efd->data = (void*)data;
+   evas_object_color_get(efd->clip, (int*)&efd->start.r, (int*)&efd->start.g, 
(int*)&efd->start.b, (int*)&alpha);
+   efd->alpha[0] = (unsigned char)alpha;
+   if (ec)
+     {
+        efd->color.r = ec->r;
+        efd->color.g = ec->g;
+        efd->color.b = ec->b;
+     }
+   else efd->color = (E_Efx_Color){255, 255, 255};
+   INF("fade: %p || %d/%d/%d/%d => %d/%d/%d/%d %s over %gs", obj, 
efd->start.r, efd->start.g, efd->start.b, efd->alpha[0], efd->color.r, 
efd->color.g, efd->color.b, efd->alpha[1], e_efx_speed_str[speed], total_time);
+   if (efd->anim) ecore_animator_del(efd->anim);
+   efd->anim = NULL;
+   if (total_time)
+     efd->anim = ecore_animator_timeline_add(total_time, 
(Ecore_Timeline_Cb)_fade_cb, efd);
+   else
+     _fade_cb(efd, 1.0);
+
+   return EINA_TRUE;
+}
+
+EAPI void
+e_efx_fade_reset(Evas_Object *obj)
+{
+   _fade_stop(obj, EINA_TRUE);
+}
+
+EAPI void
+e_efx_fade_stop(Evas_Object *obj)
+{
+   _fade_stop(obj, EINA_FALSE);
+}
diff --git a/src/bin/efx/efx_helpers.c b/src/bin/efx/efx_helpers.c
new file mode 100644
index 0000000..1dc6d1f
--- /dev/null
+++ b/src/bin/efx/efx_helpers.c
@@ -0,0 +1,115 @@
+#include "e_efx_private.h"
+
+Eina_Bool
+e_efx_rotate_center_init(E_EFX *e, const Evas_Point *center)
+{
+   if (center)
+     {
+        if (!e->map_data.rotate_center) e->map_data.rotate_center = 
malloc(sizeof(Evas_Point));
+        EINA_SAFETY_ON_NULL_RETURN_VAL(e->map_data.rotate_center, EINA_FALSE);
+        e->map_data.rotate_center->x = center->x, e->map_data.rotate_center->y 
= center->y;
+     }
+   else
+     {
+        free(e->map_data.rotate_center);
+        e->map_data.rotate_center = NULL;
+     }
+   return EINA_TRUE;
+}
+
+Eina_Bool
+e_efx_zoom_center_init(E_EFX *e, const Evas_Point *center)
+{
+   if (center)
+     {
+        if (!e->map_data.zoom_center) e->map_data.zoom_center = 
malloc(sizeof(Evas_Point));
+        EINA_SAFETY_ON_NULL_RETURN_VAL(e->map_data.zoom_center, EINA_FALSE);
+        e->map_data.zoom_center->x = center->x, e->map_data.zoom_center->y = 
center->y;
+     }
+   else
+     {
+        free(e->map_data.zoom_center);
+        e->map_data.zoom_center = NULL;
+     }
+   return EINA_TRUE;
+}
+
+Eina_Bool
+e_efx_move_center_init(E_EFX *e, const Evas_Point *center)
+{
+   if (center)
+     {
+        if (!e->map_data.move_center) e->map_data.move_center = 
malloc(sizeof(Evas_Point));
+        EINA_SAFETY_ON_NULL_RETURN_VAL(e->map_data.move_center, EINA_FALSE);
+        e->map_data.move_center->x = center->x, e->map_data.move_center->y = 
center->y;
+     }
+   else
+     {
+        free(e->map_data.move_center);
+        e->map_data.move_center = NULL;
+     }
+   return EINA_TRUE;
+}
+
+Evas_Map *
+e_efx_map_new(Evas_Object *obj)
+{
+   Evas_Map *map;
+
+   map = evas_map_new(4);
+   evas_map_smooth_set(map, EINA_FALSE);
+   evas_map_util_points_populate_from_object(map, obj);
+   return map;
+   (void)e_efx_speed_str;
+}
+
+void
+e_efx_map_set(Evas_Object *obj, Evas_Map *map)
+{
+   evas_object_map_set(obj, map);
+   evas_object_map_enable_set(obj, !!map);
+   if (map) evas_map_free(map);
+}
+
+void
+e_efx_rotate_helper(E_EFX *e, Evas_Object *obj, Evas_Map *map, double degrees)
+{
+
+   if (e->map_data.rotate_center)
+     evas_map_util_rotate(map, degrees, e->map_data.rotate_center->x, 
e->map_data.rotate_center->y);
+   else
+     {
+        Evas_Coord x, y, w, h;
+        evas_object_geometry_get(obj, &x, &y, &w, &h);
+        evas_map_util_rotate(map, degrees, x + (w / 2), y + (h / 2));
+     }
+   //DBG("rotation: %g", degrees);
+//   _size_debug(e->obj);
+}
+
+void
+e_efx_maps_apply(E_EFX *e, Evas_Object *obj, Evas_Map *map, Eina_Bool rotate, 
Eina_Bool spin, Eina_Bool zoom)
+{
+   Eina_Bool new = EINA_FALSE;
+   if ((!e->owner) && (!e->rotate_data) && (!e->spin_data) && (!e->zoom_data)) 
return;
+   if (!map)
+     {
+        map = e_efx_map_new(obj);
+        new = EINA_TRUE;
+     }
+   if (rotate && (e->rotate_data || (e->owner && e->owner->rotate_data))) 
_e_efx_rotate_calc(e->rotate_data, e->owner ? e->owner->rotate_data : NULL, 
obj, map);
+   if (spin && (e->spin_data || (e->owner && e->owner->spin_data))) 
_e_efx_spin_calc(e->spin_data, e->owner ? e->owner->spin_data : NULL, obj, map);
+   if (zoom && (e->zoom_data || (e->owner && e->owner->zoom_data))) 
_e_efx_zoom_calc(e->zoom_data, e->owner ? e->owner->zoom_data : NULL, obj, map);
+   if (new) e_efx_map_set(obj, map);
+//   DBG("%p: %s %s %s", obj, rotate ? "rotate" : "", spin ? "spin" : "", zoom 
? "zoom" : "");
+}
+
+void
+e_efx_clip_setup(Evas_Object *obj, Evas_Object *clip)
+{
+   Evas_Coord x, y, w, h;
+   if ((!obj) || (!clip)) return;
+   evas_object_geometry_get(obj, &x, &y, &w, &h);
+   evas_object_move(clip, x - w, y - h);
+   evas_object_resize(clip, 3 * w, 3 * h);
+}
diff --git a/src/bin/efx/efx_move.c b/src/bin/efx/efx_move.c
new file mode 100644
index 0000000..d837149
--- /dev/null
+++ b/src/bin/efx/efx_move.c
@@ -0,0 +1,225 @@
+#include "e_efx_private.h"
+
+typedef struct E_Efx_Move_Data
+{
+   E_EFX *e;
+   Ecore_Animator *anim;
+   E_Efx_Effect_Speed speed;
+   Evas_Point start;
+   Evas_Point change;
+   Evas_Point current;
+   int degrees;
+   E_Efx_End_Cb cb;
+   void *data;
+} E_Efx_Move_Data;
+
+static void
+_obj_del(E_Efx_Move_Data *emd, Evas *e EINA_UNUSED, Evas_Object *obj 
EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   if (emd->anim) ecore_animator_del(emd->anim);
+   emd->e->move_data = NULL;
+   if ((!emd->e->owner) && (!emd->e->followers)) e_efx_free(emd->e);
+   free(emd);
+}
+
+static void
+_move(Evas_Object *obj, int x, int y)
+{
+   Evas_Coord ox, oy;
+
+   evas_object_geometry_get(obj, &ox, &oy, NULL, NULL);
+   evas_object_move(obj, ox + x, oy + y);
+   //DBG("%p to (%d,%d)", obj, ox + x, oy + y);
+}
+
+static Eina_Bool
+_move_circle_cb(E_Efx_Move_Data *emd, double pos)
+{
+   double pct, degrees;
+   Eina_List *l;
+   E_EFX *e;
+   double x, y, r, rad;
+   Evas_Coord xx, yy, ox, oy, w, h;
+
+
+   pct = ecore_animator_pos_map(pos, emd->speed, 0, 0);
+   degrees = pct * emd->degrees;
+   evas_object_geometry_get(emd->e->obj, &ox, &oy, &w, &h);
+   r = (degrees * M_PI) / 180.0;
+   rad = sqrt((emd->current.x + w/2.0 - emd->e->map_data.move_center->x) * 
(emd->current.x + w/2.0 - emd->e->map_data.move_center->x) +
+              (emd->current.y + h/2.0 - emd->e->map_data.move_center->y) * 
(emd->current.y + h/2.0 - emd->e->map_data.move_center->y));
+   x = emd->e->map_data.move_center->x + rad * cos(r);
+   y = emd->e->map_data.move_center->y + rad * sin(r);
+   x -= (double)w / 2.;
+   y -= (double)h / 2.;
+   xx = lround(x);
+   yy = lround(y);
+   //DBG("move: %g || %g,%g", degrees, x, y);
+   evas_object_move(emd->e->obj, xx, yy);
+   e_efx_maps_apply(emd->e, emd->e->obj, NULL, E_EFX_MAPS_APPLY_ALL);
+   EINA_LIST_FOREACH(emd->e->followers, l, e)
+     {
+        _move(e->obj, xx - ox, yy - oy);
+        e_efx_maps_apply(e, e->obj, NULL, E_EFX_MAPS_APPLY_ALL);
+     }
+
+   if (pos < 1.0) return EINA_TRUE;
+
+   E_EFX_QUEUE_CHECK(emd);
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_move_cb(E_Efx_Move_Data *emd, double pos)
+{
+   int x, y;
+   double pct;
+   Eina_List *l;
+   E_EFX *e;
+
+   pct = ecore_animator_pos_map(pos, emd->speed, 0, 0);
+   x = lround(pct * (double)emd->change.x) - emd->current.x;
+   y = lround(pct * (double)emd->change.y) - emd->current.y;
+   _e_efx_resize_adjust(emd->e, &x, &y);
+   _move(emd->e->obj, x, y);
+   e_efx_maps_apply(emd->e, emd->e->obj, NULL, E_EFX_MAPS_APPLY_ALL);
+   EINA_LIST_FOREACH(emd->e->followers, l, e)
+     {
+        _move(e->obj, x, y);
+        e_efx_maps_apply(e, e->obj, NULL, E_EFX_MAPS_APPLY_ALL);
+     }
+
+   emd->current.x += x;
+   emd->current.y += y;
+   if (pos < 1.0) return EINA_TRUE;
+
+   emd->anim = NULL;
+   E_EFX_QUEUE_CHECK(emd);
+   return EINA_TRUE;
+}
+
+static void
+_move_stop(Evas_Object *obj, Eina_Bool reset)
+{
+   E_EFX *e;
+   E_Efx_Move_Data *emd;
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if ((!e) || (!e->move_data)) return;
+   emd = e->move_data;
+   if (reset)
+     {
+        evas_object_move(obj, emd->start.x, emd->start.y);
+        evas_object_event_callback_del_full(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, emd);
+        if (e_efx_queue_complete(emd->e, emd))
+          e_efx_queue_process(emd->e);
+        _obj_del(emd, NULL, NULL, NULL);
+        INF("reset moved object %p", obj);
+     }
+   else
+     {
+        INF("stopped moved object %p", obj);
+        if (emd->anim) ecore_animator_del(emd->anim);
+        if (e_efx_queue_complete(emd->e, emd))
+          e_efx_queue_process(emd->e);
+     }
+}
+
+EAPI Eina_Bool
+e_efx_move(Evas_Object *obj, E_Efx_Effect_Speed speed, const Evas_Point 
*end_point, double total_time, E_Efx_End_Cb cb, const void *data)
+{
+   E_EFX *e;
+   E_Efx_Move_Data *emd;
+   Evas_Coord x, y;
+ 
+   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
+   if (!end_point) return EINA_FALSE;
+   if (total_time < 0.0) return EINA_FALSE;
+   if (speed > E_EFX_EFFECT_SPEED_SINUSOIDAL) return EINA_FALSE;
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) e = e_efx_new(obj);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, EINA_FALSE);
+
+   evas_object_geometry_get(obj, &x, &y, NULL, NULL);
+   INF("move: %p - (%d,%d) -> (%d,%d) over %gs: %s", obj, x, y, end_point->x, 
end_point->y, total_time, e_efx_speed_str[speed]);
+   if (!total_time)
+     {
+        evas_object_move(obj, end_point->x, end_point->y);
+        return EINA_TRUE;
+     }
+   if (!e->move_data)
+     {
+        e->move_data = calloc(1, sizeof(E_Efx_Move_Data));
+        EINA_SAFETY_ON_NULL_RETURN_VAL(e->move_data, EINA_FALSE);
+        evas_object_event_callback_add(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, e->move_data);
+     }
+   emd = e->move_data;
+   emd->e = e;
+   emd->speed = speed;
+   emd->change.x = end_point->x - x;
+   emd->change.y = end_point->y - y;
+   emd->current.x = emd->current.y = 0;
+   emd->cb = cb;
+   emd->data = (void*)data;
+   if (emd->anim) ecore_animator_del(emd->anim);
+   emd->anim = ecore_animator_timeline_add(total_time, 
(Ecore_Timeline_Cb)_move_cb, emd);
+   return EINA_TRUE;
+}
+
+
+EAPI Eina_Bool
+e_efx_move_circle(Evas_Object *obj, E_Efx_Effect_Speed speed, const Evas_Point 
*center, int degrees, double total_time, E_Efx_End_Cb cb, const void *data)
+{
+   E_EFX *e;
+   E_Efx_Move_Data *emd;
+   Evas_Coord x, y;
+ 
+   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
+   if (!degrees) return EINA_FALSE;
+   if (!center) return EINA_FALSE;
+   if (total_time < 0.0) return EINA_FALSE;
+   if (speed > E_EFX_EFFECT_SPEED_SINUSOIDAL) return EINA_FALSE;
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) e = e_efx_new(obj);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, EINA_FALSE);
+   if (!e_efx_move_center_init(e, center)) return EINA_FALSE;
+
+   evas_object_geometry_get(obj, &x, &y, NULL, NULL);
+   INF("move: %p - (%d,%d) %d over %gs: %s", obj, x, y, degrees, total_time, 
e_efx_speed_str[speed]);
+   if (!total_time)
+     {
+     //   evas_object_move(obj, end_point->x, end_point->y);
+        return EINA_TRUE;
+     }
+   if (!e->move_data)
+     {
+        e->move_data = calloc(1, sizeof(E_Efx_Move_Data));
+        EINA_SAFETY_ON_NULL_RETURN_VAL(e->move_data, EINA_FALSE);
+        evas_object_event_callback_add(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, e->move_data);
+     }
+   emd = e->move_data;
+   emd->e = e;
+   emd->speed = speed;
+   emd->start.x = emd->current.x = x;
+   emd->start.y = emd->current.y = y;
+   emd->degrees = degrees;
+   emd->cb = cb;
+   emd->data = (void*)data;
+   if (emd->anim) ecore_animator_del(emd->anim);
+   emd->anim = ecore_animator_timeline_add(total_time, 
(Ecore_Timeline_Cb)_move_circle_cb, emd);
+   return EINA_TRUE;
+}
+
+EAPI void
+e_efx_move_reset(Evas_Object *obj)
+{
+   _move_stop(obj, EINA_TRUE);
+}
+
+EAPI void
+e_efx_move_stop(Evas_Object *obj)
+{
+   _move_stop(obj, EINA_FALSE);
+}
diff --git a/src/bin/efx/efx_pan.c b/src/bin/efx/efx_pan.c
new file mode 100644
index 0000000..cf16de5
--- /dev/null
+++ b/src/bin/efx/efx_pan.c
@@ -0,0 +1,390 @@
+#include "e_efx_private.h"
+
+#define PAN_FUNC_CHECK Smart_Data *sd; sd = evas_object_smart_data_get(obj); 
if ((!obj) || (!sd) || (evas_object_type_get(obj) && 
strcmp(evas_object_type_get(obj), "e_efx_pan")))
+#define PAN_CB_SETUP Smart_Data *sd; sd = evas_object_smart_data_get(obj); if 
(!sd) return
+
+typedef struct Smart_Data
+{
+   E_EFX *e;
+   Evas_Object *smart_obj;
+   Evas_Object *child_obj;
+   Evas_Coord   x, y, w, h;
+   Evas_Coord   child_w, child_h, px, py, dx, dy;
+} Smart_Data;
+
+typedef struct E_Efx_Pan_Data
+{
+   E_EFX *e;
+   Evas_Object *pan;
+   Ecore_Animator *anim;
+   E_Efx_Effect_Speed speed;
+   Evas_Point change;
+   Evas_Point current;
+   int degrees;
+   E_Efx_End_Cb cb;
+   void *data;
+} E_Efx_Pan_Data;
+
+static void _smart_pan_child_set(Evas_Object *obj, Evas_Object *child);
+
+/* local subsystem functions */
+static void _smart_child_del_hook(void *data, Evas *e, Evas_Object *obj, void 
*event_info);
+static void _smart_child_resize_hook(void *data, Evas *e, Evas_Object *obj, 
void *event_info);
+
+static void _smart_reconfigure(Smart_Data *sd);
+static void _smart_add(Evas_Object *obj);
+static void _smart_del(Evas_Object *obj);
+static void _smart_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y);
+static void _smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h);
+static void _smart_show(Evas_Object *obj);
+static void _smart_hide(Evas_Object *obj);
+static void _smart_color_set(Evas_Object *obj, int r, int g, int b, int a);
+static void _smart_clip_set(Evas_Object *obj, Evas_Object * clip);
+static void _smart_clip_unset(Evas_Object *obj);
+static void _smart_init(void);
+
+/* local subsystem globals */
+static Evas_Smart *_smart = NULL;
+
+/* local subsystem functions */
+static void
+_smart_child_del_hook(void *data, Evas *e EINA_UNUSED, Evas_Object *obj 
EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   Smart_Data *sd;
+
+   sd = data;
+   sd->child_obj = NULL;
+}
+
+static void
+_smart_child_resize_hook(void *data, Evas *e EINA_UNUSED, Evas_Object *obj 
EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   Smart_Data *sd;
+   Evas_Coord w, h;
+
+   sd = data;
+   evas_object_geometry_get(sd->child_obj, NULL, NULL, &w, &h);
+   if ((w != sd->child_w) || (h != sd->child_h))
+     {
+        sd->child_w = w;
+        sd->child_h = h;
+        _smart_reconfigure(sd);
+     }
+}
+
+static void
+_smart_reconfigure(Smart_Data *sd)
+{
+   Eina_List *l;
+   E_EFX *e;
+   Evas_Coord x, y;
+
+   evas_object_move(sd->child_obj, sd->x - sd->px, sd->y - sd->py);
+   e_efx_maps_apply(sd->e, sd->child_obj, NULL, E_EFX_MAPS_APPLY_ALL);
+   //DBG("DELTA: (%d,%d)", sd->dx, sd->dy);
+   EINA_LIST_FOREACH(sd->e->followers, l, e)
+     {
+        evas_object_geometry_get(e->obj, &x, &y, NULL, NULL);
+        evas_object_move(e->obj, x - sd->dx, y - sd->dy);
+        e_efx_maps_apply(e, e->obj, NULL, E_EFX_MAPS_APPLY_ALL);
+//        _size_debug(e->obj);
+     }
+}
+
+static void
+_smart_add(Evas_Object *obj)
+{
+   Smart_Data *sd;
+
+   sd = calloc(1, sizeof(Smart_Data));
+   if (!sd) return;
+   sd->smart_obj = obj;
+   sd->x = 0;
+   sd->y = 0;
+   sd->w = 0;
+   sd->h = 0;
+   evas_object_smart_data_set(obj, sd);
+}
+
+static void
+_smart_del(Evas_Object *obj)
+{
+   PAN_CB_SETUP;
+   _smart_pan_child_set(obj, NULL);
+   free(sd);
+}
+
+static void
+_smart_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
+{
+   PAN_CB_SETUP;
+   sd->x = x;
+   sd->y = y;
+   _smart_reconfigure(sd);
+}
+
+static void
+_smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
+{
+   PAN_CB_SETUP;
+   sd->w = w;
+   sd->h = h;
+   _smart_reconfigure(sd);
+}
+
+static void
+_smart_show(Evas_Object *obj)
+{
+   PAN_CB_SETUP;
+   if (sd->child_obj)
+     evas_object_show(sd->child_obj);
+}
+
+static void
+_smart_hide(Evas_Object *obj)
+{
+   PAN_CB_SETUP;
+   if (sd->child_obj)
+     evas_object_hide(sd->child_obj);
+}
+
+static void
+_smart_color_set(Evas_Object *obj, int r, int g, int b, int a)
+{
+   PAN_CB_SETUP;
+   if (sd->child_obj)
+     evas_object_color_set(sd->child_obj, r, g, b, a);
+}
+
+static void
+_smart_clip_set(Evas_Object *obj, Evas_Object *clip)
+{
+   PAN_CB_SETUP;
+   if (sd->child_obj)
+     evas_object_clip_set(sd->child_obj, clip);
+}
+
+static void
+_smart_clip_unset(Evas_Object *obj)
+{
+   PAN_CB_SETUP;
+   if (sd->child_obj)
+     evas_object_clip_unset(sd->child_obj);
+}
+
+/* never need to touch this */
+
+static void
+_smart_init(void)
+{
+   if (_smart) return;
+   {
+      static const Evas_Smart_Class sc =
+        {
+           "e_efx_pan",
+           EVAS_SMART_CLASS_VERSION,
+           _smart_add,
+           _smart_del,
+           _smart_move,
+           _smart_resize,
+           _smart_show,
+           _smart_hide,
+           _smart_color_set,
+           _smart_clip_set,
+           _smart_clip_unset,
+           NULL,
+           NULL,
+           NULL,
+           NULL,
+           NULL,
+           NULL,
+           NULL
+        };
+      _smart = evas_smart_class_new(&sc);
+   }
+}
+
+
+static Evas_Object *
+_smart_pan_add(Evas *evas)
+{
+   _smart_init();
+   return evas_object_smart_add(evas, _smart);
+}
+
+static void
+_smart_pan_child_set(Evas_Object *obj, Evas_Object *child)
+{
+   PAN_FUNC_CHECK return;
+   if (child == sd->child_obj) return;
+   if (sd->child_obj)
+     {
+        evas_object_clip_unset(sd->child_obj);
+        evas_object_smart_member_del(sd->child_obj);
+        evas_object_event_callback_del_full(sd->child_obj, EVAS_CALLBACK_FREE, 
_smart_child_del_hook, sd);
+        evas_object_event_callback_del_full(sd->child_obj, 
EVAS_CALLBACK_RESIZE, _smart_child_resize_hook, sd);
+        sd->child_obj = NULL;
+     }
+   if (child)
+     {
+        Evas_Coord w, h;
+        int r, g, b, a;
+
+        sd->child_obj = child;
+        evas_object_smart_member_add(sd->child_obj, sd->smart_obj);
+        evas_object_geometry_get(sd->child_obj, NULL, NULL, &w, &h);
+        sd->child_w = w;
+        sd->child_h = h;
+        evas_object_event_callback_add(child, EVAS_CALLBACK_FREE, 
_smart_child_del_hook, sd);
+        evas_object_event_callback_add(child, EVAS_CALLBACK_RESIZE, 
_smart_child_resize_hook, sd);
+        evas_object_color_get(sd->smart_obj, &r, &g, &b, &a);
+        evas_object_color_set(sd->child_obj, r, g, b, a);
+        evas_object_clip_set(sd->child_obj, 
evas_object_clip_get(sd->smart_obj));
+        if (evas_object_visible_get(sd->smart_obj)) 
evas_object_show(sd->child_obj);
+        else evas_object_hide(sd->child_obj);
+        _smart_reconfigure(sd);
+     }
+}
+
+static void
+_smart_pan_set(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
+{
+   PAN_FUNC_CHECK return;
+   if ((x == sd->px) && (y == sd->py)) return;
+   sd->dx = x - sd->px;
+   sd->dy = y - sd->py;
+   sd->px = x;
+   sd->py = y;
+   _smart_reconfigure(sd);
+}
+
+static void
+_smart_pan_get(Evas_Object *obj, Evas_Coord *x, Evas_Coord *y)
+{
+   PAN_FUNC_CHECK return;
+   if (x) *x = sd->px;
+   if (y) *y = sd->py;
+}
+
+/*
+static void
+_smart_pan_max_get(Evas_Object *obj, Evas_Coord *x, Evas_Coord *y)
+{
+   PAN_FUNC_CHECK return;
+   if (x)
+     {
+        if (sd->w < sd->child_w) *x = sd->child_w - sd->w;
+        else *x = 0;
+     }
+   if (y)
+     {
+        if (sd->h < sd->child_h) *y = sd->child_h - sd->h;
+        else *y = 0;
+     }
+}
+*/
+
+static void
+_obj_del(E_Efx_Pan_Data *epd, Evas *e EINA_UNUSED, Evas_Object *obj 
EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   if (epd->anim) ecore_animator_del(epd->anim);
+   if (epd->pan)
+     {
+        evas_object_del(epd->pan);
+        epd->pan = NULL;
+     }
+   epd->e->pan_data = NULL;
+   if ((!epd->e->owner) && (!epd->e->followers)) e_efx_free(epd->e);
+   free(epd);
+}
+
+static Eina_Bool
+_pan_cb(E_Efx_Pan_Data *epd, double pos)
+{
+   int x, y, px = 0, py = 0;
+   double pct;
+
+   pct = ecore_animator_pos_map(pos, epd->speed, 0, 0);
+   x = lround(pct * (double)epd->change.x) - epd->current.x;
+   y = lround(pct * (double)epd->change.y) - epd->current.y;
+   _smart_pan_get(epd->pan, &px, &py);
+   //DBG("PAN: (%d,%d) += (%d,%d)", px, py, x, y);
+   _smart_pan_set(epd->pan, px + x, py + y);
+   epd->e->map_data.pan.x = px + x;
+   epd->e->map_data.pan.y = py + y;
+
+   epd->current.x += x;
+   epd->current.y += y;
+   if (pos < 1.0) return EINA_TRUE;
+
+   epd->anim = NULL;
+   E_EFX_QUEUE_CHECK(epd);
+   return EINA_TRUE;
+}
+
+static E_EFX *
+_e_efx_pan_init(Evas_Object *obj)
+{
+   E_EFX *e;
+   E_Efx_Pan_Data *epd;
+   Smart_Data *sd;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) e = e_efx_new(obj);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
+
+   if (!e->pan_data)
+     {
+        e->pan_data = calloc(1, sizeof(E_Efx_Pan_Data));
+        EINA_SAFETY_ON_NULL_RETURN_VAL(e->pan_data, EINA_FALSE);
+        evas_object_event_callback_add(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, e->pan_data);
+        epd = e->pan_data;
+        epd->pan = _smart_pan_add(evas_object_evas_get(obj));
+        sd = evas_object_smart_data_get(epd->pan);
+        sd->e = e;
+        _smart_pan_child_set(epd->pan, obj);
+        evas_object_show(epd->pan);
+     }
+   return e;
+}
+
+EAPI Eina_Bool
+e_efx_pan_init(Evas_Object *obj)
+{
+   return !!_e_efx_pan_init(obj);
+}
+
+EAPI Eina_Bool
+e_efx_pan(Evas_Object *obj, E_Efx_Effect_Speed speed, const Evas_Point 
*distance, double total_time, E_Efx_End_Cb cb, const void *data)
+{
+   E_EFX *e;
+   E_Efx_Pan_Data *epd;
+   Evas_Coord x = 0, y = 0;
+ 
+   if (!distance) return EINA_FALSE;
+   if (total_time < 0.0) return EINA_FALSE;
+   if (speed > E_EFX_EFFECT_SPEED_SINUSOIDAL) return EINA_FALSE;
+
+   e = _e_efx_pan_init(obj);
+   if (!e) return EINA_FALSE;
+   epd = e->pan_data;
+   epd->e = e;
+   _smart_pan_get(epd->pan, &x, &y);
+   INF("pan: %p - (%d,%d) += (%d,%d) over %gs: %s", obj, x, y, distance->x, 
distance->y, total_time, e_efx_speed_str[speed]);
+   if (!total_time)
+     {
+        _smart_pan_set(epd->pan, x + distance->x, y + distance->y);
+        return EINA_TRUE;
+     }
+
+   epd->speed = speed;
+   epd->change.x = distance->x;
+   epd->change.y = distance->y;
+   epd->current.x = epd->current.y = 0;
+   epd->cb = cb;
+   epd->data = (void*)data;
+   if (epd->anim) ecore_animator_del(epd->anim);
+   epd->anim = ecore_animator_timeline_add(total_time, 
(Ecore_Timeline_Cb)_pan_cb, epd);
+   return EINA_TRUE;
+}
diff --git a/src/bin/efx/efx_queue.c b/src/bin/efx/efx_queue.c
new file mode 100644
index 0000000..fed7302
--- /dev/null
+++ b/src/bin/efx/efx_queue.c
@@ -0,0 +1,301 @@
+#include "e_efx_private.h"
+
+struct E_Efx_Queue_Data
+{
+   E_EFX *e;
+   E_Efx_Queued_Effect effect;
+   E_Efx_Effect_Speed speed;
+   double time;
+   E_Efx_End_Cb cb;
+   void *data;
+
+   Eina_List *subeffects;
+   void *effect_data;
+   Eina_Bool active : 1;
+};
+
+static void
+_queue_advance(E_Efx_Queue_Data *eqd)
+{
+   E_Efx_Queue_Data *run;
+   Eina_List *l;
+
+   INF("queue_advance: %p", eqd->e->obj);
+   switch (eqd->effect.type)
+     {
+      case E_EFX_EFFECT_TYPE_ROTATE:
+        e_efx_rotate(eqd->e->obj, eqd->speed, 
eqd->effect.effect.rotation.degrees, eqd->effect.effect.rotation.center, 
eqd->time, eqd->cb, eqd->data);
+        eqd->effect_data = eqd->e->rotate_data;
+        break;
+      case E_EFX_EFFECT_TYPE_ZOOM:
+        e_efx_zoom(eqd->e->obj, eqd->speed, eqd->effect.effect.zoom.start, 
eqd->effect.effect.zoom.end, eqd->effect.effect.zoom.center, eqd->time, 
eqd->cb, eqd->data);
+        eqd->effect_data = eqd->e->zoom_data;
+        break;
+      case E_EFX_EFFECT_TYPE_MOVE:
+        e_efx_move(eqd->e->obj, eqd->speed, 
&eqd->effect.effect.movement.point, eqd->time, eqd->cb, eqd->data);
+        eqd->effect_data = eqd->e->move_data;
+        break;
+      case E_EFX_EFFECT_TYPE_PAN:
+        e_efx_pan(eqd->e->obj, eqd->speed, &eqd->effect.effect.movement.point, 
eqd->time, eqd->cb, eqd->data);
+        eqd->effect_data = eqd->e->pan_data;
+        break;
+      case E_EFX_EFFECT_TYPE_FADE:
+        e_efx_fade(eqd->e->obj, eqd->speed, &eqd->effect.effect.fade.color, 
eqd->effect.effect.fade.alpha, eqd->time, eqd->cb, eqd->data);
+        eqd->effect_data = eqd->e->fade_data;
+        break;
+      case E_EFX_EFFECT_TYPE_RESIZE:
+      default:
+        e_efx_resize(eqd->e->obj, eqd->speed, eqd->effect.effect.resize.point, 
eqd->effect.effect.resize.w, eqd->effect.effect.resize.h, eqd->time, eqd->cb, 
eqd->data);
+        eqd->effect_data = eqd->e->resize_data;
+     }
+   eqd->active = EINA_TRUE;
+   EINA_LIST_FOREACH(eqd->subeffects, l, run)
+     _queue_advance(run);
+}
+
+void
+e_efx_queue_process(E_EFX *e)
+{
+   E_Efx_Queue_Data *eqd;
+
+   eqd = eina_list_data_get(e->queue);
+   if (!eqd) return;
+   if (eqd->active) return;
+
+   _queue_advance(eqd);
+}
+
+void
+eqd_free(E_Efx_Queue_Data *eqd)
+{
+   E_Efx_Queue_Data *sub;
+   if (!eqd) return;
+   if (eqd->effect.type == E_EFX_EFFECT_TYPE_ROTATE)
+     free(eqd->effect.effect.rotation.center);
+   else if (eqd->effect.type == E_EFX_EFFECT_TYPE_ZOOM)
+     free(eqd->effect.effect.zoom.center);
+   else if (eqd->effect.type == E_EFX_EFFECT_TYPE_RESIZE)
+     free(eqd->effect.effect.resize.point);
+   EINA_LIST_FREE(eqd->subeffects, sub)
+     eqd_free(sub);
+   free(eqd);
+}
+
+Eina_Bool
+e_efx_queue_complete(E_EFX *e, void *effect_data)
+{
+   E_Efx_Queue_Data *eqd;
+
+   eqd = eina_list_data_get(e->queue);
+   if (!eqd)
+     {
+        e_efx_free(e);
+        return EINA_FALSE;
+     }
+   DBG("%p: %p", e->obj, effect_data);
+   if (eqd->effect_data != effect_data) return EINA_FALSE;
+   e->queue = eina_list_remove_list(e->queue, e->queue);
+   eqd_free(eqd);
+   return !!e->queue;
+}
+
+E_Efx_Queue_Data *
+eqd_new(E_EFX *e, E_Efx_Effect_Speed speed, const E_Efx_Queued_Effect *effect, 
double total_time, E_Efx_End_Cb cb, const void *data)
+{
+   E_Efx_Queue_Data *eqd;
+
+   eqd = calloc(1, sizeof(E_Efx_Queue_Data));
+   memcpy(&eqd->effect, effect, sizeof(E_Efx_Queued_Effect));
+   eqd->e = e;
+   if (effect->type == E_EFX_EFFECT_TYPE_ROTATE)
+     {
+        if (effect->effect.rotation.center)
+          {
+             eqd->effect.effect.rotation.center = malloc(sizeof(Evas_Point));
+             if (!eqd->effect.effect.rotation.center) goto error;
+             memcpy(eqd->effect.effect.rotation.center, 
effect->effect.rotation.center, sizeof(Evas_Point));
+          }
+     }
+   else if (effect->type == E_EFX_EFFECT_TYPE_ZOOM)
+     {
+        if (effect->effect.zoom.center)
+          {
+             eqd->effect.effect.zoom.center = malloc(sizeof(Evas_Point));
+             if (!eqd->effect.effect.zoom.center) goto error;
+             memcpy(eqd->effect.effect.zoom.center, 
effect->effect.zoom.center, sizeof(Evas_Point));
+          }
+     }
+   else if (effect->type == E_EFX_EFFECT_TYPE_RESIZE)
+     {
+        if (effect->effect.resize.point)
+          {
+             eqd->effect.effect.resize.point = malloc(sizeof(Evas_Point));
+             if (!eqd->effect.effect.resize.point) goto error;
+             memcpy(eqd->effect.effect.resize.point, 
effect->effect.resize.point, sizeof(Evas_Point));
+          }
+     }
+   eqd->speed = speed;
+   eqd->time = total_time;
+   eqd->cb = cb;
+   eqd->data = (void*)data;
+   return eqd;
+error:
+   free(eqd);
+   e_efx_free(e);
+   return NULL;
+}
+
+
+EAPI void
+e_efx_queue_run(Evas_Object *obj)
+{
+   E_EFX *e;
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) return;
+   e_efx_queue_process(e);
+}
+
+EAPI E_Efx_Queue_Data *
+e_efx_queue_append(Evas_Object *obj, E_Efx_Effect_Speed speed, const 
E_Efx_Queued_Effect *effect, double total_time, E_Efx_End_Cb cb, const void 
*data)
+{
+   E_EFX *e;
+   E_Efx_Queue_Data *eqd;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(effect, NULL);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(total_time >= 0.0, NULL);
+   if (effect->type > E_EFX_EFFECT_TYPE_RESIZE) return NULL;
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) e = e_efx_new(obj);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
+
+   eqd = eqd_new(e, speed, effect, total_time, cb, data);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(eqd, NULL);
+
+   e->queue = eina_list_append(e->queue, eqd);
+   return eqd;
+   (void)e_efx_speed_str;
+}
+
+EAPI E_Efx_Queue_Data *
+e_efx_queue_prepend(Evas_Object *obj, E_Efx_Effect_Speed speed, const 
E_Efx_Queued_Effect *effect, double total_time, E_Efx_End_Cb cb, const void 
*data)
+{
+   E_EFX *e;
+   E_Efx_Queue_Data *eqd, *eqd2;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(effect, NULL);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(total_time >= 0.0, NULL);
+   if (effect->type > E_EFX_EFFECT_TYPE_RESIZE) return NULL;
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) e = e_efx_new(obj);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
+
+   eqd = eqd_new(e, speed, effect, total_time, cb, data);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(eqd, NULL);
+
+   if (e->queue)
+     {
+        eqd2 = eina_list_data_get(e->queue);
+        if (eqd2->active)
+          e->queue = eina_list_append_relative_list(e->queue, eqd, e->queue);
+        else
+          e->queue = eina_list_prepend(e->queue, eqd);
+     }
+   else
+     e->queue = eina_list_append(e->queue, eqd);
+   return eqd;
+   (void)e_efx_speed_str;
+}
+
+EAPI void
+e_efx_queue_promote(Evas_Object *obj, E_Efx_Queue_Data *eqd)
+{
+   E_EFX *e;
+
+   EINA_SAFETY_ON_NULL_RETURN(obj);
+   EINA_SAFETY_ON_NULL_RETURN(eqd);
+   e = evas_object_data_get(obj, "e_efx-data");
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   EINA_SAFETY_ON_NULL_RETURN(e->queue);
+   EINA_SAFETY_ON_TRUE_RETURN(eqd->active);
+
+   if (e->queue->data == eqd) return;
+
+   e->queue = eina_list_remove(e->queue, eqd);
+   e->queue = eina_list_append_relative_list(e->queue, eqd, e->queue);
+}
+
+EAPI void
+e_efx_queue_demote(Evas_Object *obj, E_Efx_Queue_Data *eqd)
+{
+   E_EFX *e;
+
+   EINA_SAFETY_ON_NULL_RETURN(obj);
+   EINA_SAFETY_ON_NULL_RETURN(eqd);
+   e = evas_object_data_get(obj, "e_efx-data");
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   EINA_SAFETY_ON_NULL_RETURN(e->queue);
+   EINA_SAFETY_ON_TRUE_RETURN(eqd->active);
+
+   if (eina_list_last(e->queue)->data == eqd) return;
+
+   e->queue = eina_list_demote_list(e->queue, 
eina_list_data_find_list(e->queue, eqd));
+}
+
+EAPI void
+e_efx_queue_delete(Evas_Object *obj, E_Efx_Queue_Data *eqd)
+{
+   E_EFX *e;
+
+   EINA_SAFETY_ON_NULL_RETURN(obj);
+   EINA_SAFETY_ON_NULL_RETURN(eqd);
+   e = evas_object_data_get(obj, "e_efx-data");
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   EINA_SAFETY_ON_NULL_RETURN(e->queue);
+   EINA_SAFETY_ON_TRUE_RETURN(eqd->active);
+
+   e->queue = eina_list_remove(e->queue, eqd);
+   eqd_free(eqd);
+}
+
+EAPI void
+e_efx_queue_clear(Evas_Object *obj)
+{
+   E_EFX *e;
+   E_Efx_Queue_Data *eqd;
+   Eina_List *l, *ll;
+
+   EINA_SAFETY_ON_NULL_RETURN(obj);
+   e = evas_object_data_get(obj, "e_efx-data");
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   if (!e->queue) return;
+
+   EINA_LIST_FOREACH_SAFE(e->queue, l, ll, eqd)
+     {
+        if (eqd->active) continue;
+        e->queue = eina_list_remove_list(e->queue, l);
+        eqd_free(eqd);
+     }
+}
+
+EAPI Eina_Bool
+e_efx_queue_effect_attach(E_Efx_Queue_Data *eqd, E_Efx_Effect_Speed speed, 
const E_Efx_Queued_Effect *effect, double total_time, E_Efx_End_Cb cb, const 
void *data)
+{
+   E_EFX *e;
+   E_Efx_Queue_Data *sub;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(eqd, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(effect, EINA_FALSE);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(total_time >= 0.0, EINA_FALSE);
+   if (effect->type > E_EFX_EFFECT_TYPE_RESIZE) return EINA_FALSE;
+   e = eqd->e;
+
+   sub = eqd_new(e, speed, effect, total_time, cb, data);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(sub, EINA_FALSE);
+
+   eqd->subeffects = eina_list_append(eqd->subeffects, sub);
+   return EINA_TRUE;
+   (void)e_efx_speed_str;
+}
diff --git a/src/bin/efx/efx_resize.c b/src/bin/efx/efx_resize.c
new file mode 100644
index 0000000..054ba0b
--- /dev/null
+++ b/src/bin/efx/efx_resize.c
@@ -0,0 +1,225 @@
+#include "e_efx_private.h"
+
+typedef enum
+{
+   NONE,
+   TOP_RIGHT,
+   BOTTOM_LEFT,
+   BOTTOM_RIGHT
+} Anchor;
+
+typedef struct E_Efx_Resize_Data
+{
+   E_EFX *e;
+   E_Efx_Effect_Speed speed;
+   Ecore_Animator *anim;
+   int w, h;
+   int start_w, start_h;
+   E_Efx_End_Cb cb;
+   void *data;
+   Anchor anchor_type;
+   Evas_Point anchor;
+   Eina_Bool moving : 1;
+} E_Efx_Resize_Data;
+
+static void
+_obj_del(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, 
void *event_info EINA_UNUSED)
+{
+   E_Efx_Resize_Data *erd = data;
+
+   if (erd->anim) ecore_animator_del(erd->anim);
+   erd->e->resize_data = NULL;
+   if ((!erd->e->owner) && (!erd->e->followers)) e_efx_free(erd->e);
+   free(erd);
+}
+
+static void
+_resize_anchor(E_Efx_Resize_Data *erd)
+{
+   int x = 0, y = 0;
+   int cx, cy;
+
+   if (!erd->anchor_type) return;
+
+   _e_efx_resize_adjust(erd->e, &x, &y);
+   if ((!x) && (!y)) return;
+
+   evas_object_geometry_get(erd->e->obj, &cx, &cy, NULL, NULL);
+   x += cx, y += cy;
+   evas_object_move(erd->e->obj, x, y);
+}
+
+static Eina_Bool
+_resize_cb(E_Efx_Resize_Data *erd, double pos)
+{
+   double factor;
+
+   if (pos < 1.0)
+     {
+        int w, h;
+
+        factor = ecore_animator_pos_map(pos, erd->speed, 0, 0);
+        w = lround(factor * (erd->w - erd->start_w)) + erd->start_w;
+        h = lround(factor * (erd->h - erd->start_h)) + erd->start_h;
+        evas_object_resize(erd->e->obj, w, h);
+        _resize_anchor(erd);
+        return EINA_TRUE;
+     }
+   /* lround will usually be off by 1 at the end, so we manually set this here 
*/
+   evas_object_resize(erd->e->obj, erd->w, erd->h);
+   _resize_anchor(erd);
+
+   erd->anim = NULL;
+   E_EFX_QUEUE_CHECK(erd);
+   return EINA_TRUE;
+}
+
+static void
+_resize_stop(Evas_Object *obj, Eina_Bool reset)
+{
+   E_EFX *e;
+   E_Efx_Resize_Data *erd;
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if ((!e) || (!e->resize_data)) return;
+   erd = e->resize_data;
+   if (reset)
+     {
+        evas_object_resize(obj, erd->start_w, erd->start_h);
+        evas_object_event_callback_del_full(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, erd);
+        if (erd->moving)
+          {
+             erd->moving = 0;
+             e_efx_move_reset(obj);
+          }
+        else if (e_efx_queue_complete(erd->e, erd))
+          e_efx_queue_process(erd->e);
+        _obj_del(erd, NULL, NULL, NULL);
+        INF("reset resized object %p", obj);
+     }
+   else
+     {
+        INF("stopped resized object %p", obj);
+        if (erd->anim) ecore_animator_del(erd->anim);
+        if (erd->moving)
+          {
+             erd->moving = 0;
+             e_efx_move_stop(obj);
+          }
+        if (e_efx_queue_complete(erd->e, erd))
+          e_efx_queue_process(erd->e);
+     }
+}
+
+void
+_e_efx_resize_adjust(E_EFX *e, int *ax, int *ay)
+{
+   E_Efx_Resize_Data *erd = e->resize_data;
+   int x, y, w, h;
+
+   if (!erd) return;
+   evas_object_geometry_get(e->obj, &x, &y, &w, &h);
+   switch (erd->anchor_type)
+     {
+      case TOP_RIGHT:
+        *ax = erd->anchor.x - (x + w);
+        *ay = erd->anchor.y - y;
+        break;
+      case BOTTOM_LEFT:
+        *ax = erd->anchor.x - x;
+        *ay = erd->anchor.y - (y + h);
+        break;
+      case BOTTOM_RIGHT:
+        *ax = erd->anchor.x - (x + w);
+        *ay = erd->anchor.y - (y + h);
+        break;
+      default: break;
+     }
+}
+
+EAPI Eina_Bool
+e_efx_resize(Evas_Object *obj, E_Efx_Effect_Speed speed, const Evas_Point 
*position, int w, int h, double total_time, E_Efx_End_Cb cb, const void *data)
+{
+   E_EFX *e;
+   E_Efx_Resize_Data *erd;
+   int x, y;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(w < 0, EINA_FALSE);
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(h < 0, EINA_FALSE);
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) e = e_efx_new(obj);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, EINA_FALSE);
+   erd = e->resize_data;
+   if (!erd)
+     {
+        e->resize_data = erd = calloc(1, sizeof(E_Efx_Resize_Data));
+        EINA_SAFETY_ON_NULL_RETURN_VAL(erd, EINA_FALSE);
+        evas_object_event_callback_add(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, e->resize_data);
+     }
+
+   erd->e = e;
+   erd->anchor_type = NONE;
+   erd->w = w;
+   erd->h = h;
+   erd->cb = cb;
+   erd->data = (void*)data;
+   evas_object_geometry_get(obj, &x, &y, &erd->start_w, &erd->start_h);
+   INF("resize: %p || %dx%d => %dx%d %s over %gs", obj, erd->start_w, 
erd->start_h, w, h, e_efx_speed_str[speed], total_time);
+   if (position && ((position->x != x) || (position->y != y)))
+     {
+        Evas_Point tr, bl, br;
+        Evas_Point atr, abl, abr;
+
+        tr = (Evas_Point){x + erd->start_w, y};
+        bl = (Evas_Point){x, y + erd->start_h};
+        br = (Evas_Point){x + erd->start_w, y + erd->start_h};
+        atr = (Evas_Point){position->x + w, position->y};
+        abl = (Evas_Point){position->x, position->y + h};
+        abr = (Evas_Point){position->x + w, position->y + h};
+        if (!memcmp(&tr, &atr, sizeof(Evas_Point)))
+          {
+             erd->anchor_type = TOP_RIGHT;
+             erd->anchor = tr;
+          }
+        else if (!memcmp(&bl, &abl, sizeof(Evas_Point)))
+          {
+             erd->anchor_type = BOTTOM_LEFT;
+             erd->anchor = bl;
+          }
+        else if (!memcmp(&br, &abr, sizeof(Evas_Point)))
+          {
+             erd->anchor_type = BOTTOM_RIGHT;
+             erd->anchor = br;
+          }
+
+        if (!e_efx_move(obj, speed, position, total_time, NULL, NULL))
+          {
+             evas_object_event_callback_del_full(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, e->resize_data);
+             free(erd);
+             e->resize_data = NULL;
+             e_efx_free(e);
+             return EINA_FALSE;
+          }
+        else
+          erd->moving = 1;
+     }
+   if (total_time)
+     erd->anim = ecore_animator_timeline_add(total_time, 
(Ecore_Timeline_Cb)_resize_cb, erd);
+   else
+     _resize_cb(erd, 1.0);
+
+   return EINA_TRUE;
+}
+
+EAPI void
+e_efx_resize_reset(Evas_Object *obj)
+{
+   _resize_stop(obj, EINA_TRUE);
+}
+
+EAPI void
+e_efx_resize_stop(Evas_Object *obj)
+{
+   _resize_stop(obj, EINA_FALSE);
+}
diff --git a/src/bin/efx/efx_rotate.c b/src/bin/efx/efx_rotate.c
new file mode 100644
index 0000000..b6239cf
--- /dev/null
+++ b/src/bin/efx/efx_rotate.c
@@ -0,0 +1,145 @@
+#include "e_efx_private.h"
+
+typedef struct E_Efx_Rotate_Data
+{
+   E_EFX *e;
+   Ecore_Animator *anim;
+   E_Efx_Effect_Speed speed;
+   double start_degrees;
+   double degrees;
+   E_Efx_End_Cb cb;
+   void *data;
+} E_Efx_Rotate_Data;
+
+static void
+_obj_del(E_Efx_Rotate_Data *erd, Evas *e EINA_UNUSED, Evas_Object *obj 
EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   if (erd->anim) ecore_animator_del(erd->anim);
+   erd->e->rotate_data = NULL;
+   if ((!erd->e->owner) && (!erd->e->followers)) e_efx_free(erd->e);
+   free(erd);
+}
+
+static Eina_Bool
+_rotate_cb(E_Efx_Rotate_Data *erd, double pos)
+{
+   double degrees;
+   Eina_List *l;
+   E_EFX *e;
+
+   degrees = ecore_animator_pos_map(pos, erd->speed, 0, 0);
+   erd->e->map_data.rotation = degrees * erd->degrees + erd->start_degrees;
+   //DBG("erd->e->map_data.rotation=%g,erd->degrees=%g,erd->start_degrees=%g", 
erd->e->map_data.rotation, erd->degrees, erd->start_degrees);
+   e_efx_maps_apply(erd->e, erd->e->obj, NULL, E_EFX_MAPS_APPLY_ALL);
+   EINA_LIST_FOREACH(erd->e->followers, l, e)
+     e_efx_maps_apply(e, e->obj, NULL, E_EFX_MAPS_APPLY_ALL);
+
+   if (pos < 1.0) return EINA_TRUE;
+
+   erd->anim = NULL;
+   E_EFX_QUEUE_CHECK(erd);
+   return EINA_TRUE;
+}
+
+static void
+_rotate_stop(Evas_Object *obj, Eina_Bool reset)
+{
+   E_EFX *e;
+   E_Efx_Rotate_Data *erd;
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if ((!e) || (!e->rotate_data)) return;
+   erd = e->rotate_data;
+   if (reset)
+     {
+        erd->start_degrees = 0;
+        _rotate_cb(erd, 0);
+        evas_object_event_callback_del_full(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, erd);
+        erd->e->map_data.rotation = 0;
+        if (e_efx_queue_complete(erd->e, erd))
+          e_efx_queue_process(erd->e);
+        _obj_del(erd, NULL, NULL, NULL);
+        INF("reset rotating object %p", obj);
+     }
+   else
+     {
+        if (erd->anim) ecore_animator_del(erd->anim);
+        erd->anim = NULL;
+        INF("stopped rotating object %p", obj);
+        if (e_efx_queue_complete(erd->e, erd))
+          e_efx_queue_process(erd->e);
+     }
+}
+
+void
+_e_efx_rotate_calc(void *data, void *owner, Evas_Object *obj, Evas_Map *map)
+{
+   E_Efx_Rotate_Data *erd = data;
+   E_Efx_Rotate_Data *erd2 = owner;
+   e_efx_rotate_helper(erd2 ? erd2->e : (erd ? erd->e : NULL), obj, map, (erd 
? erd->e->map_data.rotation : 0) + (erd2 ? erd2->e->map_data.rotation : 0));
+}
+
+EAPI Eina_Bool
+e_efx_rotate(Evas_Object *obj, E_Efx_Effect_Speed speed, double degrees, const 
Evas_Point *center, double total_time, E_Efx_End_Cb cb, const void *data)
+{
+   E_EFX *e;
+   E_Efx_Rotate_Data *erd;
+ 
+   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
+   if (!degrees) return EINA_FALSE;
+   if (total_time < 0.0) return EINA_FALSE;
+   if (speed > E_EFX_EFFECT_SPEED_SINUSOIDAL) return EINA_FALSE;
+   /* can't rotate a spinning object, so we stop it first */
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (e)
+     {
+        if (e->spin_data) e_efx_spin_stop(obj);
+        if (e->rotate_data)
+          {
+             erd = e->rotate_data;
+             if (erd->anim) e_efx_rotate_stop(obj);
+          }
+     }
+   else
+     {
+         e = e_efx_new(obj);
+         EINA_SAFETY_ON_NULL_RETURN_VAL(e, EINA_FALSE);
+     }
+
+   if (!e_efx_rotate_center_init(e, center)) return EINA_FALSE;
+   INF("rotate: %p - %g degrees over %gs: %s", obj, degrees, total_time, 
e_efx_speed_str[speed]);
+   if (!e->rotate_data)
+     {
+        e->rotate_data = calloc(1, sizeof(E_Efx_Rotate_Data));
+        EINA_SAFETY_ON_NULL_RETURN_VAL(e->rotate_data, EINA_FALSE);
+        evas_object_event_callback_add(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, e->rotate_data);
+     }
+   erd = e->rotate_data;
+   erd->e = e;
+   erd->speed = speed;
+   erd->degrees = degrees;
+   erd->start_degrees = e->map_data.rotation;
+   erd->cb = cb;
+   erd->data = (void*)data;
+   if (!total_time)
+     {
+        e->map_data.rotation += degrees;
+        _rotate_cb(erd, 1.0);
+        return EINA_TRUE;
+     }
+   if (erd->anim) ecore_animator_del(erd->anim);
+   erd->anim = ecore_animator_timeline_add(total_time, 
(Ecore_Timeline_Cb)_rotate_cb, erd);
+   return EINA_TRUE;
+}
+
+EAPI void
+e_efx_rotate_reset(Evas_Object *obj)
+{
+   _rotate_stop(obj, EINA_TRUE);
+}
+
+EAPI void
+e_efx_rotate_stop(Evas_Object *obj)
+{
+   _rotate_stop(obj, EINA_FALSE);
+}
diff --git a/src/bin/efx/efx_spin.c b/src/bin/efx/efx_spin.c
new file mode 100644
index 0000000..dc36126
--- /dev/null
+++ b/src/bin/efx/efx_spin.c
@@ -0,0 +1,148 @@
+#include "e_efx_private.h"
+
+typedef struct E_Efx_Spin_Data
+{
+   E_EFX *e;
+   Ecore_Animator *anim;
+   long dps;
+   double start;
+   unsigned int frame;
+} E_Efx_Spin_Data;
+
+
+static void
+_obj_del(void *data EINA_UNUSED, Evas *evas EINA_UNUSED, Evas_Object *obj, 
void *event_info EINA_UNUSED)
+{
+   E_EFX *e;
+   E_Efx_Spin_Data *esd;
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) return; /* bug? */
+   esd = e->spin_data;
+   if (esd)
+     {
+        if (esd->anim) ecore_animator_del(esd->anim);
+        e->spin_data = NULL;
+        free(esd);
+     }
+   if ((!e->owner) && (!e->followers)) e_efx_free(e);
+}
+
+static Eina_Bool
+_spin_cb(E_Efx_Spin_Data *esd)
+{
+   double fps;
+   Eina_List *l;
+   E_EFX *e;
+
+   fps = 1.0 / ecore_animator_frametime_get();
+
+   esd->e->map_data.rotation = (double)esd->frame * ((double)esd->dps / fps) + 
esd->start;
+   e_efx_maps_apply(esd->e, esd->e->obj, NULL, E_EFX_MAPS_APPLY_ALL);
+   EINA_LIST_FOREACH(esd->e->followers, l, e)
+     {
+        e_efx_maps_apply(e, e->obj, NULL, E_EFX_MAPS_APPLY_ALL);
+     }
+/*
+   if (esd->frame % (int)fps == 0)
+     DBG("frame: %u || rotate: %g", esd->frame, esd->e->map_data.rotation);
+*/
+   if (!fmod(esd->e->map_data.rotation, 360.0)) esd->frame = 0;
+   esd->frame++; /* FIXME: this may overflow */
+
+   return EINA_TRUE;
+}
+
+static void
+_spin_stop(Evas_Object *obj, Eina_Bool reset)
+{
+   E_EFX *e;
+   E_Efx_Spin_Data *esd;
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if ((!e) || (!e->spin_data)) return;
+   esd = e->spin_data;
+   esd->frame = 0;
+   if (reset)
+     {
+        esd->e->map_data.rotation = esd->start = 0;
+        e_efx_rotate_center_init(esd->e, NULL);
+        _spin_cb(esd);
+        evas_object_event_callback_del_full(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, esd);
+        _obj_del(NULL, NULL, e->obj, NULL);
+        INF("reset spinning object %p", obj);
+     }
+   else
+     {
+        INF("stopped spinning object %p", obj);
+        if (esd->anim) ecore_animator_del(esd->anim);
+        free(esd);
+        e->spin_data = NULL;
+     }
+}
+
+void
+_e_efx_spin_calc(void *data, void *owner, Evas_Object *obj, Evas_Map *map)
+{
+   E_Efx_Spin_Data *esd = data;
+   E_Efx_Spin_Data *esd2 = owner;
+   e_efx_rotate_helper(esd2 ? esd2->e : (esd ? esd->e : NULL), obj, map, (esd 
? esd->e->map_data.rotation : 0) + (esd2 ? esd2->e->map_data.rotation : 0));
+}
+
+EAPI Eina_Bool
+e_efx_spin_start(Evas_Object *obj, long dps, const Evas_Point *center)
+{
+   E_EFX *e;
+   E_Efx_Spin_Data *esd;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(dps, EINA_FALSE);
+   /* must stop rotating if object is in motion */
+   e_efx_rotate_stop(obj);
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) e = e_efx_new(obj);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, EINA_FALSE);
+   if (!e_efx_rotate_center_init(e, center)) return EINA_FALSE;
+   esd = e->spin_data;
+   if (esd)
+     {
+        esd->dps = dps;
+        esd->start = esd->e->map_data.rotation;
+        if (!esd->anim) esd->anim = 
ecore_animator_add((Ecore_Task_Cb)_spin_cb, esd);
+        if (e->map_data.rotate_center)
+          INF("spin modified: %p - %s around (%d,%d) || %lddps", obj, (dps > 
0) ? "clockwise" : "counter-clockwise",
+              e->map_data.rotate_center->x, e->map_data.rotate_center->y, dps);
+        else
+          INF("spin modified: %p - %s || %lddps", obj, (dps > 0) ? "clockwise" 
: "counter-clockwise", dps);
+        return EINA_TRUE;
+     }
+   else
+     {
+        e->spin_data = esd = calloc(1, sizeof(E_Efx_Spin_Data));
+        EINA_SAFETY_ON_NULL_RETURN_VAL(esd, EINA_FALSE);
+        evas_object_event_callback_add(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, e->spin_data);
+     }
+
+   esd->e = e;
+   esd->dps = dps;
+   esd->start = e->map_data.rotation;
+   if (e->map_data.rotate_center)
+     INF("spin: %p - %s around (%d,%d) || %lddps", obj, (dps > 0) ? 
"clockwise" : "counter-clockwise",
+         e->map_data.rotate_center->x, e->map_data.rotate_center->y, dps);
+   else
+     INF("spin: %p - %s || %lddps", obj, (dps > 0) ? "clockwise" : 
"counter-clockwise", dps);
+   esd->anim = ecore_animator_add((Ecore_Task_Cb)_spin_cb, esd);
+   return EINA_TRUE;
+   (void)e_efx_speed_str;
+}
+
+EAPI void
+e_efx_spin_reset(Evas_Object *obj)
+{
+   _spin_stop(obj, EINA_TRUE);
+}
+EAPI void
+e_efx_spin_stop(Evas_Object *obj)
+{
+   _spin_stop(obj, EINA_FALSE);
+}
diff --git a/src/bin/efx/efx_util.c b/src/bin/efx/efx_util.c
new file mode 100644
index 0000000..fc0fc1e
--- /dev/null
+++ b/src/bin/efx/efx_util.c
@@ -0,0 +1,137 @@
+#include "e_efx_private.h"
+
+static void
+_obj_del(void *data EINA_UNUSED, Evas *evas EINA_UNUSED, Evas_Object *obj, 
void *event_info EINA_UNUSED)
+{
+   E_EFX *e;
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) return;
+   if (e->owner)
+     e->owner->followers = eina_list_remove(e->owner->followers, e);
+   e->owner = NULL;
+   e_efx_free(e);
+}
+
+EAPI void
+e_efx_realize(Evas_Object *obj)
+{
+   E_EFX *e;
+   Evas_Coord x, y, ox, oy, w, h;
+   Evas_Point p1, p2;
+   double zw, zh;
+   Evas_Map *map;
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) return;
+   if (!e->map_data.rotate_center) return;
+   evas_object_geometry_get(obj, &ox, &oy, &w, &h);
+   map = (Evas_Map*)evas_object_map_get(obj);
+   if (!map) return;
+   evas_map_point_coord_get(map, 0, &p1.x, &p1.y, NULL);
+   evas_map_point_coord_get(map, 2, &p2.x, &p2.y, NULL);
+   x = lround((double)(p1.x + p2.x) / 2.);
+   y = lround((double)(p1.y + p2.y) / 2.);
+   if (e->map_data.zoom)
+     zw = e->map_data.zoom * w, zh = e->map_data.zoom * h;
+   else
+     zw = w, zh = h;
+   x = lround(x - (zw / 2.));
+   y = lround(y - (zh / 2.));
+   evas_object_move(obj, x, y);
+   evas_object_resize(obj, lround(zw), lround(zh));
+   e->map_data.zoom = 0;
+   free(e->map_data.rotate_center);
+   e->map_data.rotate_center = NULL;
+   map = e_efx_map_new(obj);
+   e_efx_maps_apply(e, obj, map, E_EFX_MAPS_APPLY_ALL);
+   e_efx_map_set(obj, map);
+   INF("realize: %p - (%d,%d)@%dx%d -> (%d,%d)@%dx%d", obj, ox, oy, w, h, x, 
y, (int)zw, (int)zh);
+}
+
+EAPI Eina_Bool
+e_efx_follow(Evas_Object *obj, Evas_Object *follower)
+{
+   E_EFX *e, *ef;
+   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(follower, EINA_FALSE);
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) e = e_efx_new(obj);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, EINA_FALSE);
+   while (e->owner) e = e->owner;
+
+   ef = evas_object_data_get(follower, "e_efx-data");
+   if (ef)
+     {
+        if (ef->owner)
+          {
+             if (ef->owner == e) return EINA_TRUE;
+             ef->owner->followers = eina_list_remove(ef->owner->followers, ef);
+          }
+     }
+   else
+     ef = e_efx_new(follower);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ef, EINA_FALSE);
+   if ((!ef->zoom_data) && (!ef->rotate_data) && (!ef->spin_data) && 
(!ef->move_data) && (!ef->bumpmap_data) && (!ef->pan_data) && (!ef->fade_data))
+     evas_object_event_callback_priority_add(ef->obj, EVAS_CALLBACK_FREE, 
EVAS_CALLBACK_PRIORITY_BEFORE, (Evas_Object_Event_Cb)_obj_del, ef);
+
+   ef->owner = e;
+   e->followers = eina_list_append(e->followers, ef);
+   INF("follow: (owner %p: %u) || (follower %p)", obj, 
eina_list_count(e->followers), follower);
+   return EINA_TRUE;
+   (void)e_efx_speed_str;
+}
+
+EAPI void
+e_efx_unfollow(Evas_Object *obj)
+{
+   E_EFX *e;
+
+   EINA_SAFETY_ON_NULL_RETURN(obj);
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) return;
+   if (!e->owner) return;
+   INF("unfollow: (owner %p) || (follower %p)", e->owner->obj, obj);
+   e->owner->followers = eina_list_remove(e->owner->followers, e);
+   evas_object_event_callback_del_full(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, e);
+   e_efx_free(e->owner);
+   e->owner = NULL;
+   e_efx_free(e);
+}
+
+EAPI Eina_List *
+e_efx_followers_get(Evas_Object *obj)
+{
+   E_EFX *e, *f;
+   Eina_List *l, *ret = NULL;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) return NULL;
+   EINA_LIST_FOREACH(e->followers, l, f)
+     ret = eina_list_append(ret, f->obj);
+   return ret;
+}
+
+EAPI Evas_Object *
+e_efx_leader_get(Evas_Object *obj)
+{
+   E_EFX *e;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) return NULL;
+   return e->owner ? e->owner->obj : NULL;
+}
+
+EAPI void
+e_efx_reclip(Evas_Object *obj)
+{
+   E_EFX *e;
+
+   EINA_SAFETY_ON_NULL_RETURN(obj);
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) return;
+   if (e->fade_data) e_efx_fade_reclip(e->fade_data);
+}
diff --git a/src/bin/efx/efx_zoom.c b/src/bin/efx/efx_zoom.c
new file mode 100644
index 0000000..bb8ba4b
--- /dev/null
+++ b/src/bin/efx/efx_zoom.c
@@ -0,0 +1,171 @@
+#include "e_efx_private.h"
+
+typedef struct E_Efx_Zoom_Data
+{
+   E_EFX *e;
+   Ecore_Animator *anim;
+   E_Efx_Effect_Speed speed;
+   double ending_zoom;
+   double starting_zoom;
+   E_Efx_End_Cb cb;
+   void *data;
+} E_Efx_Zoom_Data;
+
+static void
+_obj_del(E_Efx_Zoom_Data *ezd, Evas *e EINA_UNUSED, Evas_Object *obj 
EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   if (ezd->anim) ecore_animator_del(ezd->anim);
+   ezd->e->zoom_data = NULL;
+   if ((!ezd->e->owner) && (!ezd->e->followers)) e_efx_free(ezd->e);
+   free(ezd);
+}
+
+static void
+_zoom_center_calc(E_EFX *e, Evas_Object *obj, Evas_Coord *x, Evas_Coord *y)
+{
+   Evas_Coord w, h;
+   if (e->map_data.zoom_center)
+     {
+        *x = e->map_data.zoom_center->x;
+        *y = e->map_data.zoom_center->y;
+     }
+   else
+     {
+        evas_object_geometry_get(obj, x, y, &w, &h);
+        *x += (w / 2);
+        *y += (h / 2);
+     }
+}
+
+static void
+_zoom(E_EFX *e, Evas_Object *obj, double zoom)
+{
+   Evas_Map *map;
+   Evas_Coord x, y;
+
+   map = e_efx_map_new(obj);
+   _zoom_center_calc(e, e->obj, &x, &y);
+   //DBG("ZOOM %p: %g: %d,%d", obj, zoom, x, y);
+   evas_map_util_zoom(map, zoom, zoom, x, y);
+   e_efx_maps_apply(e, obj, map, E_EFX_MAPS_APPLY_ROTATE_SPIN);
+   e_efx_map_set(obj, map);
+}
+
+static Eina_Bool
+_zoom_cb(E_Efx_Zoom_Data *ezd, double pos)
+{
+   double zoom;
+   Eina_List *l;
+   E_EFX *e;
+
+   zoom = ecore_animator_pos_map(pos, ezd->speed, 0, 0);
+   ezd->e->map_data.zoom = (zoom * (ezd->ending_zoom - ezd->starting_zoom)) + 
ezd->starting_zoom;
+   //DBG("total: %g || zoom (pos %g): %g || endzoom: %g || startzoom: %g", 
ezd->e->map_data.zoom, zoom, pos, ezd->ending_zoom, ezd->starting_zoom);
+   e_efx_maps_apply(ezd->e, ezd->e->obj, NULL, E_EFX_MAPS_APPLY_ALL);
+   EINA_LIST_FOREACH(ezd->e->followers, l, e)
+     e_efx_maps_apply(e, e->obj, NULL, E_EFX_MAPS_APPLY_ALL);
+
+   if (pos < 1.0) return EINA_TRUE;
+
+   ezd->anim = NULL;
+   E_EFX_QUEUE_CHECK(ezd);
+   return EINA_TRUE;
+}
+
+static void
+_zoom_stop(Evas_Object *obj, Eina_Bool reset)
+{
+   E_EFX *e;
+   E_Efx_Zoom_Data *ezd;
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if ((!e) || (!e->zoom_data)) return;
+   ezd = e->zoom_data;
+   if (reset)
+     {
+        ezd->starting_zoom = 0.0;
+        _zoom(e, obj, 1.0);
+        evas_object_event_callback_del_full(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, ezd);
+        if (e_efx_queue_complete(ezd->e, ezd))
+          e_efx_queue_process(ezd->e);
+        _obj_del(ezd, NULL, NULL, NULL);
+        INF("reset zooming object %p", obj);
+     }
+   else
+     {
+        ecore_animator_del(ezd->anim);
+        ezd->anim = NULL;
+        INF("stopped zooming object %p", obj);
+        if (e_efx_queue_complete(ezd->e, ezd))
+          e_efx_queue_process(ezd->e);
+     }
+}
+
+void
+_e_efx_zoom_calc(void *data, void *owner, Evas_Object *obj, Evas_Map *map)
+{
+   E_Efx_Zoom_Data *ezd = data;
+   E_Efx_Zoom_Data *ezd2 = owner;
+   Evas_Coord x, y;
+   double zoom;
+   if ((ezd2 && (ezd2->e->map_data.zoom <= 0)) || (ezd && 
(ezd->e->map_data.zoom <= 0))) return;
+   _zoom_center_calc(ezd2 ? ezd2->e : ezd->e, ezd2 ? ezd2->e->obj : obj, &x, 
&y);
+   zoom = ezd ? ezd->e->map_data.zoom : 0;
+   if (ezd2) zoom += ezd2->e->map_data.zoom;
+   //DBG("zoom: %g @ (%d,%d)", zoom, x, y);
+   evas_map_util_zoom(map, zoom, zoom, x, y);
+}
+
+EAPI Eina_Bool
+e_efx_zoom(Evas_Object *obj, E_Efx_Effect_Speed speed, double starting_zoom, 
double ending_zoom, const Evas_Point *zoom_point, double total_time, 
E_Efx_End_Cb cb, const void *data)
+{
+   E_EFX *e;
+   E_Efx_Zoom_Data *ezd;
+ 
+   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
+   if (ending_zoom <= 0.0) return EINA_FALSE;
+   if (starting_zoom < 0.0) return EINA_FALSE;
+   if (total_time < 0.0) return EINA_FALSE;
+   if (speed > E_EFX_EFFECT_SPEED_SINUSOIDAL) return EINA_FALSE;
+
+   e = evas_object_data_get(obj, "e_efx-data");
+   if (!e) e = e_efx_new(obj);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, EINA_FALSE);
+   if (!e_efx_zoom_center_init(e, zoom_point)) return EINA_FALSE;
+   INF("zoom: %p - %g%%->%g%% over %gs: %s", obj, (starting_zoom ?: 
e->map_data.zoom) * 100.0, ending_zoom * 100.0, total_time, 
e_efx_speed_str[speed]);
+   if (!e->zoom_data)
+     {
+        e->zoom_data = calloc(1, sizeof(E_Efx_Zoom_Data));
+        EINA_SAFETY_ON_NULL_RETURN_VAL(e->zoom_data, EINA_FALSE);
+        evas_object_event_callback_add(obj, EVAS_CALLBACK_FREE, 
(Evas_Object_Event_Cb)_obj_del, e->zoom_data);
+     }
+   ezd = e->zoom_data;
+   ezd->e = e;
+   ezd->speed = speed;
+   ezd->ending_zoom = ending_zoom;
+   ezd->starting_zoom = starting_zoom ?: ezd->e->map_data.zoom;
+   ezd->cb = cb;
+   ezd->data = (void*)data;
+   if (!total_time)
+     {
+        _zoom_cb(ezd, 1.0);
+        return EINA_TRUE;
+     }
+   if (!ezd->starting_zoom) ezd->starting_zoom = 1.0;
+   _zoom_cb(ezd, 0);
+   if (ezd->anim) ecore_animator_del(ezd->anim);
+   ezd->anim = ecore_animator_timeline_add(total_time, 
(Ecore_Timeline_Cb)_zoom_cb, ezd);
+   return EINA_TRUE;
+}
+
+EAPI void
+e_efx_zoom_reset(Evas_Object *obj)
+{
+   _zoom_stop(obj, EINA_TRUE);
+}
+
+EAPI void
+e_efx_zoom_stop(Evas_Object *obj)
+{
+   _zoom_stop(obj, EINA_FALSE);
+}
diff --git a/src/modules/Makefile.mk b/src/modules/Makefile.mk
index 3ea3148..f4d5f6a 100644
--- a/src/modules/Makefile.mk
+++ b/src/modules/Makefile.mk
@@ -3,6 +3,7 @@ MOD_LDFLAGS = -module -avoid-version
 MOD_CPPFLAGS = -I. \
 -I$(top_srcdir) \
 -I$(top_srcdir)/src/bin \
+-I$(top_srcdir)/src/bin/efx \
 -I$(top_builddir)/src/bin \
 -I$(top_srcdir)/src/modules \
 @e_cflags@ \

-- 


Reply via email to