ethumb: make exists client API async.
[framework/uifw/ethumb.git] / src / lib / client / Ethumb_Client.c
index 1ddd8d4..3e05fac 100644 (file)
@@ -6,20 +6,19 @@
  *
  * Copyright (C) 2009 by ProFUSION embedded systems
  *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or (at your
- * option) any later version.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
  *
- * This program is distributed in the hope that it will be useful,  but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the  GNU General Public License
- * for more details.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
  *
  * @author Rafael Antognolli <antognolli@profusion.mobi>
  * @author Gustavo Sverzut Barbieri <barbieri@profusion.mobi>
  */
 
 #ifdef HAVE_CONFIG_H
-#include "config.h"
+# include "config.h"
 #endif
-#include <Eina.h>
-#include <eina_safety_checks.h>
-#include <Ethumb.h>
-#include "Ethumb_Client.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <limits.h>
 #include <sys/types.h>
 #include <stdbool.h>
 
+#include <Eina.h>
+#include <eina_safety_checks.h>
+#include <E_DBus.h>
+#include <Ethumb.h>
+#include <Ecore.h>
+
+#include "Ethumb_Client.h"
+
 #ifndef PATH_MAX
 #define PATH_MAX 4096
 #endif
 
-#include <E_DBus.h>
-
 #define MAX_ID 2000000
 
 static int _log_dom = -1;
@@ -91,6 +93,7 @@ static int _log_dom = -1;
 #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__)
 #define WRN(...) EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
 #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__)
+#define CRITICAL(...) EINA_LOG_DOM_CRIT(_log_dom, __VA_ARGS__)
 
 struct _Ethumb_Client
 {
@@ -121,9 +124,12 @@ struct _Ethumb_Client
    } die;
    const char *object_path;
 
+   int refcount;
+
    Eina_Bool ethumb_dirty : 1;
    Eina_Bool connected : 1;
    Eina_Bool server_started : 1;
+   Eina_Bool delete_me : 1;
 };
 
 struct _ethumb_pending_add
@@ -162,6 +168,22 @@ struct _ethumb_pending_gen
    Eina_Free_Cb free_data;
 };
 
+typedef struct _Ethumb_Async_Exists Ethumb_Async_Exists;
+
+struct _Ethumb_Async_Exists
+{
+   Ethumb *dup;
+   Ethumb_Client *source;
+
+   Ethumb_Client_Thumb_Exists_Cb exists_cb;
+   const void *data;
+
+   Ecore_Thread *thread;
+   int refcount;
+
+   Eina_Bool exists : 1;
+};
+
 static const char _ethumb_dbus_bus_name[] = "org.enlightenment.Ethumb";
 static const char _ethumb_dbus_interface[] = "org.enlightenment.Ethumb";
 static const char _ethumb_dbus_objects_interface[] = "org.enlightenment.Ethumb.objects";
@@ -180,12 +202,12 @@ __dbus_callback_check_and_init(const char *file, int line, const char *function,
 {
    if (!msg)
      {
-       ERR("%s:%d:%s() callback without message arguments!\n",
+       ERR("%s:%d:%s() callback without message arguments!",
                file, line, function);
 
        if (err)
          ERR("%s:%d:%s() an error was reported by server: "
-                 "name=\"%s\", message=\"%s\"\n",
+                 "name=\"%s\", message=\"%s\"",
                  file, line, function, err->name, err->message);
 
        return 0;
@@ -193,7 +215,7 @@ __dbus_callback_check_and_init(const char *file, int line, const char *function,
 
    if (!dbus_message_iter_init(msg, itr))
      {
-       ERR("%s:%d:%s() could not init iterator.\n",
+       ERR("%s:%d:%s() could not init iterator.",
                file, line, function);
        return 0;
      }
@@ -211,7 +233,7 @@ __dbus_iter_type_check(int type, int expected, const char *expected_name)
    if (type == expected)
      return 1;
 
-   ERR("expected type %s (%c) but got %c instead!\n",
+   ERR("expected type %s (%c) but got %c instead!",
           expected_name, expected, type);
 
    return 0;
@@ -223,7 +245,7 @@ __dbus_iter_type_check(int type, int expected, const char *expected_name)
     {                                                                  \
        if ((ptr) == NULL)                                              \
         {                                                              \
-           ERR("%s == NULL!\n", #ptr);         \
+           CRITICAL("%s == NULL!", #ptr);                              \
            return __VA_ARGS__;                                         \
         }                                                              \
     }                                                                  \
@@ -243,26 +265,28 @@ _ethumb_client_name_owner_changed(void *data, DBusMessage *msg)
        DBUS_TYPE_STRING, &to,
        DBUS_TYPE_INVALID))
      {
-       ERR("could not get NameOwnerChanged arguments: %s: %s\n",
+       ERR("could not get NameOwnerChanged arguments: %s: %s",
            err.name, err.message);
        dbus_error_free(&err);
        return;
      }
 
+   if(!from || !name)
+       return ;
    if (strcmp(name, _ethumb_dbus_bus_name) != 0)
      return;
 
-   DBG("NameOwnerChanged from=[%s] to=[%s]\n", from, to);
+   DBG("NameOwnerChanged from=[%s] to=[%s]", from, to);
 
    if (from[0] != '\0' && to[0] == '\0')
      {
-       DBG("exit ethumbd at %s\n", from);
-       if (strcmp(client->unique_name, from) != 0)
-         WRN("%s was not the known name %s, ignored.\n",
+       DBG("exit ethumbd at %s", from);
+       if (client->unique_name && strcmp(client->unique_name, from) != 0)
+         WRN("%s was not the known name %s, ignored.",
               from, client->unique_name);
-       else
+       else if(client->unique_name)
          {
-            ERR("server exit!!!\n");
+            ERR("server exit!!!");
             if (client->die.cb)
               {
                  client->die.cb(client->die.data, client);
@@ -277,7 +301,7 @@ _ethumb_client_name_owner_changed(void *data, DBusMessage *msg)
          }
      }
    else
-     DBG("unknown change from %s to %s\n", from, to);
+     DBG("unknown change from %s to %s", from, to);
 }
 
 static void
@@ -366,18 +390,18 @@ _ethumb_client_start_server_cb(void *data, DBusMessage *msg, DBusError *err)
    dbus_message_iter_get_basic(&iter, &ret);
    if ((ret != 1) && (ret != 2))
      {
-       ERR("Error starting Ethumbd DBus service by its name: retcode %u\n",
+       ERR("Error starting Ethumbd DBus service by its name: retcode %u",
            ret);
        goto error;
      }
 
    client->server_started = 1;
-   DBG("Ethumbd DBus service started successfully (%d), now request its name\n",
+   DBG("Ethumbd DBus service started successfully (%d), now request its name",
        ret);
 
    if (client->pending_get_name_owner)
      {
-       DBG("already requesting name owner, cancel and try again\n");
+       DBG("already requesting name owner, cancel and try again");
        dbus_pending_call_cancel(client->pending_get_name_owner);
      }
 
@@ -386,14 +410,14 @@ _ethumb_client_start_server_cb(void *data, DBusMessage *msg, DBusError *err)
       client);
    if (!client->pending_get_name_owner)
      {
-       ERR("could not create a get_name_owner request.\n");
+       ERR("could not create a get_name_owner request.");
        goto error;
      }
 
    return;
 
  error:
-   ERR("failed to start Ethumbd DBus service by its name.\n");
+   ERR("failed to start Ethumbd DBus service by its name.");
    _ethumb_client_report_connect(client, 0);
 }
 
@@ -402,7 +426,7 @@ _ethumb_client_start_server(Ethumb_Client *client)
 {
    if (client->pending_start_service_by_name)
      {
-       DBG("already pending start service by name.\n");
+       DBG("already pending start service by name.");
        return;
      }
 
@@ -412,7 +436,7 @@ _ethumb_client_start_server(Ethumb_Client *client)
       client);
    if (!client->pending_start_service_by_name)
      {
-       ERR("could not start service by name!\n");
+       ERR("could not start service by name!");
        _ethumb_client_report_connect(client, 0);
      }
 }
@@ -429,7 +453,7 @@ _ethumb_client_get_name_owner(void *data, DBusMessage *msg, DBusError *err)
 
    if (dbus_error_is_set(err) && (!client->server_started))
      {
-       DBG("could not find server (%s), try to start it...\n", err->message);
+       DBG("could not find server (%s), try to start it...", err->message);
        _ethumb_client_start_server(client);
        return;
      }
@@ -444,11 +468,11 @@ _ethumb_client_get_name_owner(void *data, DBusMessage *msg, DBusError *err)
    dbus_message_iter_get_basic(&iter, &uid);
    if (!uid)
      {
-       ERR("no name owner!\n");
+       ERR("no name owner!");
        goto error;
      }
 
-   DBG("unique name = %s\n", uid);
+   DBG("unique name = %s", uid);
    client->unique_name = eina_stringshare_add(uid);
 
    _ethumb_client_call_new(client);
@@ -459,10 +483,75 @@ error:
    _ethumb_client_report_connect(client, 0);
 }
 
+static void
+_ethumb_client_exists_heavy(void *data, Ecore_Thread *thread)
+{
+   Ethumb_Async_Exists *async = data;
+
+   async->exists = ethumb_exists(async->dup);
+}
+
+static void
+_ethumb_client_exists_end(void *data, Ecore_Thread *thread)
+{
+   Ethumb_Async_Exists *async = data;
+   Ethumb *tmp = async->source->ethumb;
+
+   async->source->ethumb = async->dup;
+   async->source->ethumb_dirty = ethumb_cmp(tmp, async->dup);
+   async->exists_cb(async->source, (Ethumb_Exists*) async, async->exists, (void*) async->data);
+   async->source->ethumb = tmp;
+
+   ethumb_free(async->dup);
+   async->source->refcount--;
+
+   if (async->source->delete_me == EINA_TRUE)
+     ethumb_client_disconnect(async->source);
+
+   free(async);
+}
+
+static void
+_ethumb_client_exists_cancel(void *data, Ecore_Thread *thread)
+{
+   Ethumb_Async_Exists *async = data;
+   Ethumb *tmp = async->source->ethumb;
+
+   async->source->ethumb = async->dup;
+   async->source->ethumb_dirty = ethumb_cmp(tmp, async->dup);
+   async->exists_cb(async->source, (Ethumb_Exists*) async, EINA_FALSE, (void*) async->data);
+   async->source->ethumb = tmp;
+
+   ethumb_free(async->dup);
+   async->source->refcount--;
+
+   if (async->source->delete_me == EINA_TRUE)
+     ethumb_client_disconnect(async->source);
+
+   free(async);
+}
+
 /**
  * @endcond
  */
 
+/**
+ * @brief Initialize the Ethumb_Client library.
+ *
+ * @return 1 or greater on success, 0 on error.
+ *
+ * This function sets up all the Ethumb_Client module dependencies. It
+ * returns 0 on failure (that is, when one of the dependency fails to
+ * initialize), otherwise it returns the number of times it has
+ * already been called.
+ *
+ * When Ethumb_Client is not used anymore, call
+ * ethumb_client_shutdown() to shut down the Ethumb_Client library.
+ *
+ * @see ethumb_client_shutdown()
+ * @see ethumb_client_connect()
+ * @see @ref tutorial_ethumb_client
+ */
 EAPI int
 ethumb_client_init(void)
 {
@@ -488,6 +577,22 @@ ethumb_client_init(void)
    return ++_initcount;
 }
 
+/**
+ * @brief Shut down the Ethumb_Client library.
+ *
+ * @return 0 when everything is shut down, 1 or greater if there are
+ *         other users of the Ethumb_Client library pending shutdown.
+ *
+ * This function shuts down the Ethumb_Client library. It returns 0
+ * when it has been called the same number of times than
+ * ethumb_client_init(). In that case it shut down all the
+ * Ethumb_Client modules dependencies.
+ *
+ * Once this function succeeds (that is, @c 0 is returned), you must
+ * not call any of the Eina function anymore. You must call
+ * ethumb_client_init() again to use the Ethumb_Client functions
+ * again.
+ */
 EAPI int
 ethumb_client_shutdown(void)
 {
@@ -551,7 +656,7 @@ ethumb_client_connect(Ethumb_Client_Connect_Cb connect_cb, const void *data, Ein
    eclient = calloc(1, sizeof(*eclient));
    if (!eclient)
      {
-       ERR("could not allocate Ethumb_Client structure.\n");
+       ERR("could not allocate Ethumb_Client structure.");
        goto err;
      }
 
@@ -562,14 +667,14 @@ ethumb_client_connect(Ethumb_Client_Connect_Cb connect_cb, const void *data, Ein
    eclient->ethumb = ethumb_new();
    if (!eclient->ethumb)
      {
-       ERR("could not create ethumb handler.\n");
+       ERR("could not create ethumb handler.");
        goto ethumb_new_err;
      }
 
    eclient->conn = e_dbus_bus_get(DBUS_BUS_SESSION);
    if (!eclient->conn)
      {
-       ERR("could not connect to session bus.\n");
+       ERR("could not connect to session bus.");
        goto connection_err;
      }
 
@@ -582,7 +687,7 @@ ethumb_client_connect(Ethumb_Client_Connect_Cb connect_cb, const void *data, Ein
         eclient);
    if (!eclient->pending_get_name_owner)
      {
-       ERR("could not create a get_name_owner request.\n");
+       ERR("could not create a get_name_owner request.");
        goto connection_err;
      }
 
@@ -604,6 +709,9 @@ err:
  *
  * This is the destructor of Ethumb_Client, after it's disconnected
  * the client handle is now gone and should not be used.
+ *
+ * @param client client instance to be destroyed. Must @b not be @c
+ *        NULL.
  */
 EAPI void
 ethumb_client_disconnect(Ethumb_Client *client)
@@ -612,6 +720,10 @@ ethumb_client_disconnect(Ethumb_Client *client)
 
    EINA_SAFETY_ON_NULL_RETURN(client);
 
+   client->delete_me = EINA_TRUE;
+   if (client->refcount > 0)
+     return ;
+
    if (!client->connected)
      goto end_connection;
 
@@ -757,7 +869,7 @@ _ethumb_client_dbus_get_bytearray(DBusMessageIter *iter)
    el_type = dbus_message_iter_get_element_type(iter);
    if (el_type != DBUS_TYPE_BYTE)
      {
-       ERR("not an byte array element.\n");
+       ERR("not an byte array element.");
        return NULL;
      }
 
@@ -804,7 +916,7 @@ ethumb_client_ethumb_setup(Ethumb_Client *client)
    DBusMessageIter iter, aiter, diter, viter, vaiter;
    Ethumb *e = client->ethumb;
    const char *entry;
-   dbus_int32_t tw, th, format, aspect, quality, compress;
+   dbus_int32_t tw, th, format, aspect, orientation, quality, compress;
    float cx, cy;
    double t;
    const char *theme_file, *group, *swallow;
@@ -860,6 +972,11 @@ ethumb_client_ethumb_setup(Ethumb_Client *client)
    dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &aspect);
    _close_variant_iter(viter);
 
+   _open_variant_iter("orientation", "i", viter);
+   orientation = ethumb_thumb_orientation_get(e);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &orientation);
+   _close_variant_iter(viter);
+
    _open_variant_iter("crop", "(dd)", viter);
    dbus_message_iter_open_container(&viter, DBUS_TYPE_STRUCT, NULL, &vaiter);
    ethumb_thumb_crop_align_get(e, &cx, &cy);
@@ -914,19 +1031,19 @@ ethumb_client_ethumb_setup(Ethumb_Client *client)
    dbus_message_iter_append_basic(&viter, DBUS_TYPE_DOUBLE, &video_interval);
    _close_variant_iter(viter);
 
-   _open_variant_iter("video_ntimes", "i", viter);
+   _open_variant_iter("video_ntimes", "u", viter);
    video_ntimes = ethumb_video_ntimes_get(e);
-   dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &video_ntimes);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_UINT32, &video_ntimes);
    _close_variant_iter(viter);
 
-   _open_variant_iter("video_fps", "i", viter);
+   _open_variant_iter("video_fps", "u", viter);
    video_fps = ethumb_video_fps_get(e);
-   dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &video_fps);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_UINT32, &video_fps);
    _close_variant_iter(viter);
 
-   _open_variant_iter("document_page", "i", viter);
+   _open_variant_iter("document_page", "u", viter);
    document_page = ethumb_document_page_get(e);
-   dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &document_page);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_UINT32, &document_page);
    _close_variant_iter(viter);
 
 #undef _open_variant_iter
@@ -949,8 +1066,8 @@ _ethumb_client_generated_cb(void *data, DBusMessage *msg)
 {
    DBusMessageIter iter;
    dbus_int32_t id = -1;
-   const char *thumb;
-   const char *thumb_key;
+   const char *thumb = NULL;
+   const char *thumb_key = NULL;
    Ethumb_Client *client = data;
    int t;
    dbus_bool_t success;
@@ -1013,8 +1130,8 @@ _ethumb_client_generated_cb(void *data, DBusMessage *msg)
      }
 
 end:
-   eina_stringshare_del(thumb);
-   eina_stringshare_del(thumb_key);
+   if (thumb) eina_stringshare_del(thumb);
+   if (thumb_key) eina_stringshare_del(thumb_key);
 }
 
 static void
@@ -1173,22 +1290,22 @@ ethumb_client_generate_cancel(Ethumb_Client *client, int id, Ethumb_Client_Gener
    l = client->pending_add;
    while (l)
      {
-       struct _ethumb_pending_add *pending = l->data;
-       if (pending->id != id32)
+       struct _ethumb_pending_add *pending_add = l->data;
+       if (pending_add->id != id32)
          {
             l = l->next;
             continue;
          }
        client->pending_add = eina_list_remove_list(client->pending_add, l);
-       eina_stringshare_del(pending->file);
-       eina_stringshare_del(pending->key);
-       eina_stringshare_del(pending->thumb);
-       eina_stringshare_del(pending->thumb_key);
-       dbus_pending_call_cancel(pending->pending_call);
-       dbus_pending_call_unref(pending->pending_call);
-       if (pending->free_data)
-         pending->free_data(pending->data);
-       free(pending);
+       eina_stringshare_del(pending_add->file);
+       eina_stringshare_del(pending_add->key);
+       eina_stringshare_del(pending_add->thumb);
+       eina_stringshare_del(pending_add->thumb_key);
+       dbus_pending_call_cancel(pending_add->pending_call);
+       dbus_pending_call_unref(pending_add->pending_call);
+       if (pending_add->free_data)
+         pending_add->free_data(pending_add->data);
+       free(pending_add);
        found = 1;
        break;
      }
@@ -1199,21 +1316,20 @@ ethumb_client_generate_cancel(Ethumb_Client *client, int id, Ethumb_Client_Gener
    l = client->pending_gen;
    while (l)
      {
-       struct _ethumb_pending_gen *pending = l->data;
-       if (pending->id != id32)
+       struct _ethumb_pending_gen *pending_gen = l->data;
+       if (pending_gen->id != id32)
          {
             l = l->next;
             continue;
          }
        client->pending_gen = eina_list_remove_list(client->pending_gen, l);
-       eina_stringshare_del(pending->file);
-       eina_stringshare_del(pending->key);
-       eina_stringshare_del(pending->thumb);
-       eina_stringshare_del(pending->thumb_key);
-       if (pending->free_data)
-         pending->free_data(pending->data);
-       free(pending);
-       found = 1;
+       eina_stringshare_del(pending_gen->file);
+       eina_stringshare_del(pending_gen->key);
+       eina_stringshare_del(pending_gen->thumb);
+       eina_stringshare_del(pending_gen->thumb_key);
+       if (pending_gen->free_data)
+         pending_gen->free_data(pending_gen->data);
+       free(pending_gen);
        break;
      }
 
@@ -1299,6 +1415,9 @@ ethumb_client_generate_cancel_all(Ethumb_Client *client)
  * ~/.thumbnails/SIZE, with size being either normal (128x128) or
  * large (256x256).
  *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
  * @param s size identifier, either #ETHUMB_THUMB_NORMAL (0) or
  *        #ETHUMB_THUMB_LARGE (1).
  *
@@ -1320,8 +1439,11 @@ ethumb_client_fdo_set(Ethumb_Client *client, Ethumb_Thumb_FDO_Size s)
 /**
  * Configure future request to use custom size.
  *
- * @param w width, default is 128.
- * @param h height, default is 128.
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param tw width, default is 128.
+ * @param th height, default is 128.
  */
 EAPI void
 ethumb_client_size_set(Ethumb_Client *client, int tw, int th)
@@ -1335,8 +1457,11 @@ ethumb_client_size_set(Ethumb_Client *client, int tw, int th)
 /**
  * Retrieve future request to use custom size.
  *
- * @param w where to return width. May be #NULL.
- * @param h where to return height. May be #NULL.
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param tw where to return width. May be @c NULL.
+ * @param th where to return height. May be @c NULL.
  */
 EAPI void
 ethumb_client_size_get(const Ethumb_Client *client, int *tw, int *th)
@@ -1351,6 +1476,9 @@ ethumb_client_size_get(const Ethumb_Client *client, int *tw, int *th)
 /**
  * Configure format to use for future requests.
  *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
  * @param f format identifier to use, either #ETHUMB_THUMB_FDO (0),
  *        #ETHUMB_THUMB_JPEG (1) or #ETHUMB_THUMB_EET (2). Default is FDO.
  */
@@ -1366,6 +1494,10 @@ ethumb_client_format_set(Ethumb_Client *client, Ethumb_Thumb_Format f)
 /**
  * Retrieve format to use for future requests.
  *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
  * @return format identifier to use, either #ETHUMB_THUMB_FDO (0),
  *         #ETHUMB_THUMB_JPEG (1) or #ETHUMB_THUMB_EET (2).
  */
@@ -1400,6 +1532,9 @@ ethumb_client_format_get(const Ethumb_Client *client)
  * pixels from left and 250 pixels from right being lost, that is just
  * the 500x500 central pixels of image will be considered for scaling.
  *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
  * @param a aspect mode identifier, either #ETHUMB_THUMB_KEEP_ASPECT (0),
  *        #ETHUMB_THUMB_IGNORE_ASPECT (1) or #ETHUMB_THUMB_CROP (2).
  */
@@ -1415,6 +1550,10 @@ ethumb_client_aspect_set(Ethumb_Client *client, Ethumb_Thumb_Aspect a)
 /**
  * Get current aspect in use for requests.
  *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
  * @return aspect in use for future requests.
  */
 EAPI Ethumb_Thumb_Aspect
@@ -1426,8 +1565,53 @@ ethumb_client_aspect_get(const Ethumb_Client *client)
 }
 
 /**
+ * Configure orientation to use for future requests.
+ *
+ * Default value is #ETHUMB_THUMB_ORIENT_ORIGINAL: metadata from the file
+ * will be used to orient pixel data.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param f format identifier to use, either #ETHUMB_THUMB_ORIENT_NONE (0),
+ *        #ETHUMB_THUMB_ROTATE_90_CW (1), #ETHUMB_THUMB_ROTATE_180 (2),
+ *        #ETHUMB_THUMB_ROTATE_90_CCW (3), #ETHUMB_THUMB_FLIP_HORIZONTAL (4),
+ *        #ETHUMB_THUMB_FLIP_VERTICAL (5), #ETHUMB_THUMB_FLIP_TRANSPOSE (6),
+ *        #ETHUMB_THUMB_FLIP_TRANSVERSE (7) or #ETHUMB_THUMB_ORIENT_ORIGINAL
+ *        (8). Default is ORIGINAL.
+ */
+EAPI void
+ethumb_client_orientation_set(Ethumb_Client *client, Ethumb_Thumb_Orientation o)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   client->ethumb_dirty = 1;
+   ethumb_thumb_orientation_set(client->ethumb, o);
+}
+
+/**
+ * Get current orientation in use for requests.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
+ * @return orientation in use for future requests.
+ */
+EAPI Ethumb_Thumb_Orientation
+ethumb_client_orientation_get(const Ethumb_Client *client)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
+
+   return ethumb_thumb_orientation_get(client->ethumb);
+}
+
+/**
  * Configure crop alignment in use for future requests.
  *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
  * @param x horizontal alignment. 0.0 means left side will be visible
  *        or right side is being lost. 1.0 means right side will be
  *        visible or left side is being lost. 0.5 means just center is
@@ -1447,8 +1631,11 @@ ethumb_client_crop_align_set(Ethumb_Client *client, float x, float y)
 /**
  * Get current crop alignment in use for requests.
  *
- * @param x where to return horizontal alignment. May be #NULL.
- * @param y where to return vertical alignment. May be #NULL.
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param x where to return horizontal alignment. May be @c NULL.
+ * @param y where to return vertical alignment. May be @c NULL.
  */
 EAPI void
 ethumb_client_crop_align_get(const Ethumb_Client *client, float *x, float *y)
@@ -1463,6 +1650,9 @@ ethumb_client_crop_align_get(const Ethumb_Client *client, float *x, float *y)
 /**
  * Configure quality to be used in thumbnails.
  *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
  * @param quality value from 0 to 100, default is 80. The effect
  *        depends on the format being used, PNG will not use it.
  */
@@ -1477,6 +1667,10 @@ ethumb_client_quality_set(Ethumb_Client *client, int quality)
 /**
  * Get quality to be used in thumbnails.
  *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
  * @return quality value from 0 to 100, default is 80. The effect
  *         depends on the format being used, PNG will not use it.
  */
@@ -1491,6 +1685,9 @@ ethumb_client_quality_get(const Ethumb_Client *client)
 /**
  * Configure compression level used in requests.
  *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
  * @param compress value from 0 to 9, default is 9. The effect
  *        depends on the format being used, JPEG will not use it.
  */
@@ -1505,6 +1702,10 @@ ethumb_client_compress_set(Ethumb_Client *client, int compress)
 /**
  * Get compression level used in requests.
  *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
  * @return compress value from 0 to 9, default is 9. The effect
  *         depends on the format being used, JPEG will not use it.
  */
@@ -1525,6 +1726,9 @@ ethumb_client_compress_get(const Ethumb_Client *client)
  * of thumbnails, but sometimes it's useful to have it composited and
  * avoid runtime overhead.
  *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
  * @param file file path to edje.
  * @param group group inside edje to use.
  * @param swallow name of swallow part.
@@ -1541,11 +1745,25 @@ ethumb_client_frame_set(Ethumb_Client *client, const char *file, const char *gro
 /**
  * Configure where to store thumbnails in future requests.
  *
+ * This value will be used to generate thumbnail paths, that is, it
+ * will be used when ethumb_client_thumb_path_set() was not called
+ * after last ethumb_client_file_set().
+ *
  * Note that this is the base, a category is added to this path as a
- * sub directory.
+ * sub directory. This is not the final directory where files are
+ * stored, the thumbnail system will account @b category as well, see
+ * ethumb_client_category_set().
+ *
+ * As other options, this value will only be applied to future
+ * requests.
  *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
  * @param path base directory where to store thumbnails. Default is
  *        ~/.thumbnails
+ *
+ * @see ethumb_client_category_set()
  */
 EAPI void
 ethumb_client_dir_path_set(Ethumb_Client *client, const char *path)
@@ -1556,6 +1774,18 @@ ethumb_client_dir_path_set(Ethumb_Client *client, const char *path)
    ethumb_thumb_dir_path_set(client->ethumb, path);
 }
 
+/**
+ * Get base directory path where to store thumbnails.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
+ * @return pointer to internal string with current path. This string
+ *         should not be modified or freed.
+ *
+ * @see ethumb_client_dir_path_set()
+ */
 EAPI const char *
 ethumb_client_dir_path_get(const Ethumb_Client *client)
 {
@@ -1567,10 +1797,26 @@ ethumb_client_dir_path_get(const Ethumb_Client *client)
 /**
  * Category directory to store thumbnails.
  *
+ * This value will be used to generate thumbnail paths, that is, it
+ * will be used when ethumb_client_thumb_path_set() was not called
+ * after last ethumb_client_file_set().
+ *
+ * This is a sub-directory inside base directory
+ * (ethumb_client_dir_path_set()) that creates a namespace to avoid
+ * different options resulting in the same file.
+ *
+ * As other options, this value will only be applied to future
+ * requests.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
  * @param category category sub directory to store thumbnail. Default
  *        is either "normal" or "large" for FDO compliant thumbnails
  *        or WIDTHxHEIGHT-ASPECT[-FRAMED]-FORMAT. It can be a string
- *        or None to use auto generated names.
+ *        or @c NULL to use auto generated names.
+ *
+ * @see ethumb_client_dir_path_set()
  */
 EAPI void
 ethumb_client_category_set(Ethumb_Client *client, const char *category)
@@ -1581,6 +1827,18 @@ ethumb_client_category_set(Ethumb_Client *client, const char *category)
    ethumb_thumb_category_set(client->ethumb, category);
 }
 
+/**
+ * Get category sub-directory  where to store thumbnails.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
+ * @return pointer to internal string with current path. This string
+ *         should not be modified or freed.
+ *
+ * @see ethumb_client_category_set()
+ */
 EAPI const char *
 ethumb_client_category_get(const Ethumb_Client *client)
 {
@@ -1589,24 +1847,65 @@ ethumb_client_category_get(const Ethumb_Client *client)
    return ethumb_thumb_category_get(client->ethumb);
 }
 
+/**
+ * Set the video time (duration) in seconds.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param t duration (in seconds). Defaults to 3 seconds.
+ */
 EAPI void
-ethumb_client_video_time_set(Ethumb_Client *client, float time)
+ethumb_client_video_time_set(Ethumb_Client *client, float t)
 {
    EINA_SAFETY_ON_NULL_RETURN(client);
 
    client->ethumb_dirty = 1;
-   ethumb_video_time_set(client->ethumb, time);
+   ethumb_video_time_set(client->ethumb, t);
 }
 
+/**
+ * Set initial video position to start thumbnailing, in percentage.
+ *
+ * This is useful to avoid thumbnailing the company/producer logo or
+ * movie opening.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param start initial video positon to thumbnail, in percentage (0.0
+ *        to 1.0, inclusive). Defaults to 10% (0.1).
+ */
 EAPI void
 ethumb_client_video_start_set(Ethumb_Client *client, float start)
 {
    EINA_SAFETY_ON_NULL_RETURN(client);
+   EINA_SAFETY_ON_FALSE_RETURN(start >= 0.0);
+   EINA_SAFETY_ON_FALSE_RETURN(start <= 1.0);
 
    client->ethumb_dirty = 1;
    ethumb_video_start_set(client->ethumb, start);
 }
 
+/**
+ * Set the video frame interval, in seconds.
+ *
+ * This is useful for animated thumbnail and will define skip time
+ * before going to the next frame. Note that video backends might not
+ * be able to precisely skip that amount as it will depend on various
+ * factors, including video encoding.
+ *
+ * Although this seems similar to ethumb_client_video_fps_set(), this
+ * one is the time that will be used to seek. The math is simple, for
+ * each new frame the video position will be set to:
+ * ((video_length * start_time) + (interval * current_frame_number)).
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param interval time between frames, in seconds. Defaults to 0.05
+ *        seconds.
+ */
 EAPI void
 ethumb_client_video_interval_set(Ethumb_Client *client, float interval)
 {
@@ -1616,26 +1915,64 @@ ethumb_client_video_interval_set(Ethumb_Client *client, float interval)
    ethumb_video_interval_set(client->ethumb, interval);
 }
 
+/**
+ * Set the number of frames to thumbnail.
+ *
+ * This is useful for animated thumbnail and will define how many
+ * frames the generated file will have.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param ntimes number of times, must be greater than zero.
+ *        Defaults to 3.
+ */
 EAPI void
-ethumb_client_video_ntimes_set(Ethumb_Client *client, int ntimes)
+ethumb_client_video_ntimes_set(Ethumb_Client *client, unsigned int ntimes)
 {
    EINA_SAFETY_ON_NULL_RETURN(client);
+   EINA_SAFETY_ON_FALSE_RETURN(ntimes > 0);
 
    client->ethumb_dirty = 1;
    ethumb_video_ntimes_set(client->ethumb, ntimes);
 }
 
+/**
+ * Set the number of frames per second to thumbnail the video.
+ *
+ * This configures the number of times per seconds the thumbnail will
+ * use to create thumbnails.
+ *
+ * Although this is similar to ethumb_client_video_interval_set(), it
+ * is the delay used between calling functions thata generates frames,
+ * while the other is the time used to skip inside the video.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param fps number of frames per second to thumbnail. Must be greater
+ *        than zero. Defaults to 10.
+ */
 EAPI void
-ethumb_client_video_fps_set(Ethumb_Client *client, int fps)
+ethumb_client_video_fps_set(Ethumb_Client *client, unsigned int fps)
 {
    EINA_SAFETY_ON_NULL_RETURN(client);
+   EINA_SAFETY_ON_FALSE_RETURN(fps > 0);
 
    client->ethumb_dirty = 1;
    ethumb_video_fps_set(client->ethumb, fps);
 }
 
+/**
+ * Set the page number to thumbnail in paged documents.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param page page number, defaults to 0 (first).
+ */
 EAPI void
-ethumb_client_document_page_set(Ethumb_Client *client, int page)
+ethumb_client_document_page_set(Ethumb_Client *client, unsigned int page)
 {
    EINA_SAFETY_ON_NULL_RETURN(client);
 
@@ -1658,7 +1995,7 @@ ethumb_client_document_page_set(Ethumb_Client *client, int page)
  *        from. This is only used for formats that allow multiple
  *        resources in one file, like EET or Edje (group name).
  *
- * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ * @return @c EINA_TRUE on success, @c EINA_FALSE on failure.
  */
 EAPI Eina_Bool
 ethumb_client_file_set(Ethumb_Client *client, const char *path, const char *key)
@@ -1670,6 +2007,18 @@ ethumb_client_file_set(Ethumb_Client *client, const char *path, const char *key)
 
 /**
  * Get values set with ethumb_client_file_get()
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param path where to return configured path. May be @c NULL.  If
+ *        not @c NULL, then it will be a pointer to a stringshared
+ *        instance, but @b no references are added (do it with
+ *        eina_stringshare_ref())!
+ * @param key where to return configured key. May be @c NULL.If not @c
+ *        NULL, then it will be a pointer to a stringshared instance,
+ *        but @b no references are added (do it with
+ *        eina_stringshare_ref())!
  */
 EAPI void
 ethumb_client_file_get(Ethumb_Client *client, const char **path, const char **key)
@@ -1705,6 +2054,14 @@ ethumb_client_file_free(Ethumb_Client *client)
  *
  * Set these to @c NULL to forget previously given values. After
  * ethumb_client_file_set() these values will be reset to @c NULL.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param path force generated thumbnail to the exact given path. If
+ *        @c NULL, then reverts back to auto-generation.
+ * @param key force generated thumbnail to the exact given key. If
+ *        @c NULL, then reverts back to auto-generation.
  */
 EAPI void
 ethumb_client_thumb_path_set(Ethumb_Client *client, const char *path, const char *key)
@@ -1720,6 +2077,9 @@ ethumb_client_thumb_path_set(Ethumb_Client *client, const char *path, const char
  * This returns the value set with ethumb_client_thumb_path_set() or
  * auto-generated by ethumb_client_thumb_exists() if it was not set.
  *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
  * @param path where to return configured path. May be @c NULL.  If
  *        there was no path configured with
  *        ethumb_client_thumb_path_set() and
@@ -1757,14 +2117,69 @@ ethumb_client_thumb_path_get(Ethumb_Client *client, const char **path, const cha
  * @param client client instance. Must @b not be @c NULL and client
  *        must be configured with ethumb_client_file_set().
  *
- * @return #EINA_TRUE if it exists, #EINA_FALSE otherwise.
+ * @return @c EINA_TRUE if it exists, @c EINA_FALSE otherwise.
+ */
+EAPI Ethumb_Exists *
+ethumb_client_thumb_exists(Ethumb_Client *client, Ethumb_Client_Thumb_Exists_Cb exists_cb, const void *data)
+{
+   Ethumb_Async_Exists *async;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
+
+   async = malloc(sizeof (Ethumb_Async_Exists));
+   if (!async)
+     {
+        exists_cb(client, NULL, EINA_FALSE, (void*) data);
+        return NULL;
+     }
+
+   async->dup = ethumb_dup(client->ethumb);
+   async->source = client;
+   async->source->refcount++;
+   async->exists_cb = exists_cb;
+   async->data = data;
+   async->exists = EINA_FALSE;
+
+   async->refcount = 1;
+   async->thread = ecore_thread_run(_ethumb_client_exists_heavy,
+                                   _ethumb_client_exists_end,
+                                   _ethumb_client_exists_cancel,
+                                   async);
+
+   return (Ethumb_Exists*) async;
+}
+
+/**
+ * Cancel an ongoing exists request.
+ *
+ * @param exists the request to cancel.
+ */
+EAPI void
+ethumb_client_thumb_exists_cancel(Ethumb_Exists *exists)
+{
+   Ethumb_Async_Exists *async = (Ethumb_Async_Exists*) exists;
+
+   async->refcount--;
+
+   if (async->refcount > 0) return ;
+
+   ecore_thread_cancel(async->thread);
+}
+
+/**
+ * Check if an exists request was cancelled.
+ *
+ * @param exists the request to check.
+ * @result return EINA_TRUE if the request was cancelled.
  */
 EAPI Eina_Bool
-ethumb_client_thumb_exists(Ethumb_Client *client)
+ethumb_client_thumb_exists_check(Ethumb_Exists *exists)
 {
-   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
+   Ethumb_Async_Exists *async = (Ethumb_Async_Exists*) exists;
+
+   if (!async) return EINA_TRUE;
 
-   return ethumb_exists(client->ethumb);
+   return ecore_thread_check(async->thread);
 }
 
 /**
@@ -1812,7 +2227,7 @@ ethumb_client_generate(Ethumb_Client *client, Ethumb_Client_Generate_Cb generate
    ethumb_file_get(client->ethumb, &file, &key);
    if (!file)
      {
-       ERR("no file set.\n");
+       ERR("no file set.");
        return -1;
      }