Ethumb: test if unique_name is not null, If I kill the daemon right after it is start...
[framework/uifw/ethumb.git] / src / lib / client / Ethumb_Client.c
1 /**
2  * @file
3  *
4  * This is the client-server thumbnail library, see @ref
5  * tutorial_ethumb_client.
6  *
7  * Copyright (C) 2009 by ProFUSION embedded systems
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or (at your
12  * option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,  but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE.  See the  GNU General Public License
17  * for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22  * USA.
23  *
24  * @author Rafael Antognolli <antognolli@profusion.mobi>
25  * @author Gustavo Sverzut Barbieri <barbieri@profusion.mobi>
26  */
27
28 /**
29  * @page tutorial_ethumb_client Client-Server Thumbnailing Tutorial
30  *
31  * @section tutorial_ethumb_client_intro Introduction
32  *
33  * Ethumb provides both in process and client-server generation
34  * methods. The advantage of the client-server method is that current
35  * process will not do the heavy operations that may block, stopping
36  * animations and other user interactions. Instead the client library
37  * will configure a local #Ethumb instance and mirrors/controls a
38  * remote process using DBus. The simple operations like most setters
39  * and getters as well as checking for thumbnail existence
40  * (ethumb_client_thumb_exists()) is done locally, while expensive
41  * (ethumb_client_generate()) are done on server and then reported
42  * back to application when it is finished (both success or failure).
43  *
44  * @section tutorial_ethumb_client_connect Connecting to Server
45  *
46  * TODO
47  *
48  * @section tutorial_ethumb_client_generate Requesting Thumbnail Generation
49  *
50  * TODO
51  *
52  * @section tutorial_ethumb_client_setup Setup Extra Thumbnail Parameters
53  *
54  * TODO
55  *
56  * @section tutorial_ethumb_client_server_died Handle Server Disconnection
57  *
58  * TODO
59  */
60
61 /**
62  * @cond LOCAL
63  */
64
65 #ifdef HAVE_CONFIG_H
66 # include "config.h"
67 #endif
68
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <limits.h>
72 #include <string.h>
73 #include <unistd.h>
74 #include <errno.h>
75 #include <sys/types.h>
76 #include <stdbool.h>
77
78 #include <Eina.h>
79 #include <eina_safety_checks.h>
80 #include <E_DBus.h>
81 #include <Ethumb.h>
82
83 #include "Ethumb_Client.h"
84
85 #ifndef PATH_MAX
86 #define PATH_MAX 4096
87 #endif
88
89 #define MAX_ID 2000000
90
91 static int _log_dom = -1;
92 #define DBG(...) EINA_LOG_DOM_DBG(_log_dom, __VA_ARGS__)
93 #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__)
94 #define WRN(...) EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
95 #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__)
96 #define CRITICAL(...) EINA_LOG_DOM_CRIT(_log_dom, __VA_ARGS__)
97
98 struct _Ethumb_Client
99 {
100    Ethumb *ethumb;
101    int id_count;
102
103    E_DBus_Connection *conn;
104    E_DBus_Signal_Handler *name_owner_changed_handler;
105    E_DBus_Signal_Handler *generated_signal;
106    DBusPendingCall *pending_get_name_owner;
107    DBusPendingCall *pending_start_service_by_name;
108    const char *unique_name;
109    DBusPendingCall *pending_new;
110    struct {
111       Ethumb_Client_Connect_Cb cb;
112       void *data;
113       Eina_Free_Cb free_data;
114    } connect;
115    Eina_List *pending_add;
116    Eina_List *pending_remove;
117    Eina_List *pending_gen;
118    DBusPendingCall *pending_clear;
119    DBusPendingCall *pending_setup;
120    struct {
121       Ethumb_Client_Die_Cb cb;
122       void *data;
123       Eina_Free_Cb free_data;
124    } die;
125    const char *object_path;
126
127    Eina_Bool ethumb_dirty : 1;
128    Eina_Bool connected : 1;
129    Eina_Bool server_started : 1;
130 };
131
132 struct _ethumb_pending_add
133 {
134    dbus_int32_t id;
135    const char *file;
136    const char *key;
137    const char *thumb;
138    const char *thumb_key;
139    Ethumb_Client_Generate_Cb generated_cb;
140    void *data;
141    Eina_Free_Cb free_data;
142    DBusPendingCall *pending_call;
143    Ethumb_Client *client;
144 };
145
146 struct _ethumb_pending_remove
147 {
148    dbus_int32_t id;
149    Ethumb_Client_Generate_Cancel_Cb cancel_cb;
150    void *data;
151    Eina_Free_Cb free_data;
152    DBusPendingCall *pending_call;
153    Ethumb_Client *client;
154 };
155
156 struct _ethumb_pending_gen
157 {
158    dbus_int32_t id;
159    const char *file;
160    const char *key;
161    const char *thumb;
162    const char *thumb_key;
163    Ethumb_Client_Generate_Cb generated_cb;
164    void *data;
165    Eina_Free_Cb free_data;
166 };
167
168 static const char _ethumb_dbus_bus_name[] = "org.enlightenment.Ethumb";
169 static const char _ethumb_dbus_interface[] = "org.enlightenment.Ethumb";
170 static const char _ethumb_dbus_objects_interface[] = "org.enlightenment.Ethumb.objects";
171 static const char _ethumb_dbus_path[] = "/org/enlightenment/Ethumb";
172 static const char fdo_interface[] = "org.freedesktop.DBus";
173 static const char fdo_bus_name[] = "org.freedesktop.DBus";
174 static const char fdo_path[] = "/org/freedesktop/DBus";
175
176 static int _initcount = 0;
177
178 static void _ethumb_client_generated_cb(void *data, DBusMessage *msg);
179 static void _ethumb_client_get_name_owner(void *data, DBusMessage *msg, DBusError *err);
180
181 static inline bool
182 __dbus_callback_check_and_init(const char *file, int line, const char *function, DBusMessage *msg, DBusMessageIter *itr, DBusError *err)
183 {
184    if (!msg)
185      {
186         ERR("%s:%d:%s() callback without message arguments!",
187                 file, line, function);
188
189         if (err)
190           ERR("%s:%d:%s() an error was reported by server: "
191                   "name=\"%s\", message=\"%s\"",
192                   file, line, function, err->name, err->message);
193
194         return 0;
195      }
196
197    if (!dbus_message_iter_init(msg, itr))
198      {
199         ERR("%s:%d:%s() could not init iterator.",
200                 file, line, function);
201         return 0;
202      }
203
204    return 1;
205 }
206
207 #define _dbus_callback_check_and_init(msg, itr, err)                    \
208   __dbus_callback_check_and_init(__FILE__, __LINE__, __FUNCTION__,      \
209                                  msg, itr, err)
210
211 static inline bool
212 __dbus_iter_type_check(int type, int expected, const char *expected_name)
213 {
214    if (type == expected)
215      return 1;
216
217    ERR("expected type %s (%c) but got %c instead!",
218            expected_name, expected, type);
219
220    return 0;
221 }
222 #define _dbus_iter_type_check(t, e) __dbus_iter_type_check(t, e, #e)
223
224 #define CHECK_NULL_RETURN(ptr, ...)                                     \
225   do                                                                    \
226     {                                                                   \
227        if ((ptr) == NULL)                                               \
228          {                                                              \
229             CRITICAL("%s == NULL!", #ptr);                              \
230             return __VA_ARGS__;                                         \
231          }                                                              \
232     }                                                                   \
233   while (0)
234
235 static void
236 _ethumb_client_name_owner_changed(void *data, DBusMessage *msg)
237 {
238    DBusError err;
239    const char *name, *from, *to;
240    Ethumb_Client *client = data;
241
242    dbus_error_init(&err);
243    if (!dbus_message_get_args(msg, &err,
244        DBUS_TYPE_STRING, &name,
245        DBUS_TYPE_STRING, &from,
246        DBUS_TYPE_STRING, &to,
247        DBUS_TYPE_INVALID))
248      {
249         ERR("could not get NameOwnerChanged arguments: %s: %s",
250             err.name, err.message);
251         dbus_error_free(&err);
252         return;
253      }
254
255    if(!from || !name)
256        return ;
257    if (strcmp(name, _ethumb_dbus_bus_name) != 0)
258      return;
259
260    DBG("NameOwnerChanged from=[%s] to=[%s]", from, to);
261
262    if (from[0] != '\0' && to[0] == '\0')
263      {
264         DBG("exit ethumbd at %s", from);
265         if (client->unique_name && strcmp(client->unique_name, from) != 0)
266           WRN("%s was not the known name %s, ignored.",
267                from, client->unique_name);
268         else if(client->unique_name)
269           {
270              ERR("server exit!!!");
271              if (client->die.cb)
272                {
273                   client->die.cb(client->die.data, client);
274                   client->die.cb = NULL;
275                }
276              if (client->die.free_data)
277                {
278                   client->die.free_data(client->die.data);
279                   client->die.free_data = NULL;
280                   client->die.data = NULL;
281                }
282           }
283      }
284    else
285      DBG("unknown change from %s to %s", from, to);
286 }
287
288 static void
289 _ethumb_client_report_connect(Ethumb_Client *client, Eina_Bool success)
290 {
291    if (!client->connect.cb)
292      {
293         ERR("already called?!");
294         return;
295      }
296
297    client->connect.cb(client->connect.data, client, success);
298    if (client->connect.free_data)
299      {
300         client->connect.free_data(client->connect.data);
301         client->connect.free_data = NULL;
302      }
303    client->connect.cb = NULL;
304    client->connect.data = NULL;
305 }
306
307 static void
308 _ethumb_client_new_cb(void *data, DBusMessage *msg, DBusError *error)
309 {
310    DBusMessageIter iter;
311    const char *opath;
312    int t;
313    Ethumb_Client *client = data;
314
315    client->pending_new = NULL;
316
317    if (!_dbus_callback_check_and_init(msg, &iter, error))
318      goto end_error;
319    t = dbus_message_iter_get_arg_type(&iter);
320    if (!_dbus_iter_type_check(t, DBUS_TYPE_OBJECT_PATH))
321      goto end_error;
322
323    dbus_message_iter_get_basic(&iter, &opath);
324    if (opath[0] == '\0')
325      goto end_error;
326
327    client->object_path = eina_stringshare_add(opath);
328
329    client->generated_signal = e_dbus_signal_handler_add(
330       client->conn, _ethumb_dbus_bus_name, opath,
331       _ethumb_dbus_objects_interface, "generated",
332       _ethumb_client_generated_cb, client);
333
334    _ethumb_client_report_connect(client, 1);
335    return;
336
337 end_error:
338    _ethumb_client_report_connect(client, 0);
339 }
340
341 static void
342 _ethumb_client_call_new(Ethumb_Client *client)
343 {
344    DBusMessage *msg;
345
346    msg = dbus_message_new_method_call(_ethumb_dbus_bus_name, _ethumb_dbus_path,
347                                       _ethumb_dbus_interface, "new");
348    client->pending_new = e_dbus_message_send(client->conn, msg,
349                                              _ethumb_client_new_cb, -1,
350                                              client);
351    dbus_message_unref(msg);
352 }
353
354 static void
355 _ethumb_client_start_server_cb(void *data, DBusMessage *msg, DBusError *err)
356 {
357    Ethumb_Client *client = data;
358    DBusMessageIter iter;
359    dbus_uint32_t ret;
360    int t;
361
362    client->pending_start_service_by_name = NULL;
363
364    if (!_dbus_callback_check_and_init(msg, &iter, err))
365      goto error;
366
367    t = dbus_message_iter_get_arg_type(&iter);
368    if (!_dbus_iter_type_check(t, DBUS_TYPE_UINT32))
369      goto error;
370
371    dbus_message_iter_get_basic(&iter, &ret);
372    if ((ret != 1) && (ret != 2))
373      {
374         ERR("Error starting Ethumbd DBus service by its name: retcode %u",
375             ret);
376         goto error;
377      }
378
379    client->server_started = 1;
380    DBG("Ethumbd DBus service started successfully (%d), now request its name",
381        ret);
382
383    if (client->pending_get_name_owner)
384      {
385         DBG("already requesting name owner, cancel and try again");
386         dbus_pending_call_cancel(client->pending_get_name_owner);
387      }
388
389    client->pending_get_name_owner = e_dbus_get_name_owner
390      (client->conn, _ethumb_dbus_bus_name, _ethumb_client_get_name_owner,
391       client);
392    if (!client->pending_get_name_owner)
393      {
394         ERR("could not create a get_name_owner request.");
395         goto error;
396      }
397
398    return;
399
400  error:
401    ERR("failed to start Ethumbd DBus service by its name.");
402    _ethumb_client_report_connect(client, 0);
403 }
404
405 static void
406 _ethumb_client_start_server(Ethumb_Client *client)
407 {
408    if (client->pending_start_service_by_name)
409      {
410         DBG("already pending start service by name.");
411         return;
412      }
413
414    client->server_started = 0;
415    client->pending_start_service_by_name = e_dbus_start_service_by_name
416      (client->conn, _ethumb_dbus_bus_name, 0, _ethumb_client_start_server_cb,
417       client);
418    if (!client->pending_start_service_by_name)
419      {
420         ERR("could not start service by name!");
421         _ethumb_client_report_connect(client, 0);
422      }
423 }
424
425 static void
426 _ethumb_client_get_name_owner(void *data, DBusMessage *msg, DBusError *err)
427 {
428    DBusMessageIter iter;
429    const char *uid;
430    Ethumb_Client *client = data;
431    int t;
432
433    client->pending_get_name_owner = NULL;
434
435    if (dbus_error_is_set(err) && (!client->server_started))
436      {
437         DBG("could not find server (%s), try to start it...", err->message);
438         _ethumb_client_start_server(client);
439         return;
440      }
441
442    if (!_dbus_callback_check_and_init(msg, &iter, err))
443      goto error;
444
445    t = dbus_message_iter_get_arg_type(&iter);
446    if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING))
447      goto error;
448
449    dbus_message_iter_get_basic(&iter, &uid);
450    if (!uid)
451      {
452         ERR("no name owner!");
453         goto error;
454      }
455
456    DBG("unique name = %s", uid);
457    client->unique_name = eina_stringshare_add(uid);
458
459    _ethumb_client_call_new(client);
460    client->connected = 1;
461    return;
462
463 error:
464    _ethumb_client_report_connect(client, 0);
465 }
466
467 /**
468  * @endcond
469  */
470
471 /**
472  * @brief Initialize the Ethumb_Client library.
473  *
474  * @return 1 or greater on success, 0 on error.
475  *
476  * This function sets up all the Ethumb_Client module dependencies. It
477  * returns 0 on failure (that is, when one of the dependency fails to
478  * initialize), otherwise it returns the number of times it has
479  * already been called.
480  *
481  * When Ethumb_Client is not used anymore, call
482  * ethumb_client_shutdown() to shut down the Ethumb_Client library.
483  *
484  * @see ethumb_client_shutdown()
485  * @see ethumb_client_connect()
486  * @see @ref tutorial_ethumb_client
487  */
488 EAPI int
489 ethumb_client_init(void)
490 {
491    if (_initcount)
492      return ++_initcount;
493
494    if (!eina_init())
495      {
496         fprintf(stderr, "ERROR: Could not initialize log module.\n");
497         return 0;
498      }
499    _log_dom = eina_log_domain_register("ethumb_client", EINA_COLOR_YELLOW);
500    if (_log_dom < 0)
501      {
502         EINA_LOG_ERR("Could not register log domain: ethumb_client");
503         eina_shutdown();
504         return 0;
505      }
506
507    ethumb_init();
508    e_dbus_init();
509
510    return ++_initcount;
511 }
512
513 /**
514  * @brief Shut down the Ethumb_Client library.
515  *
516  * @return 0 when everything is shut down, 1 or greater if there are
517  *         other users of the Ethumb_Client library pending shutdown.
518  *
519  * This function shuts down the Ethumb_Client library. It returns 0
520  * when it has been called the same number of times than
521  * ethumb_client_init(). In that case it shut down all the
522  * Ethumb_Client modules dependencies.
523  *
524  * Once this function succeeds (that is, @c 0 is returned), you must
525  * not call any of the Eina function anymore. You must call
526  * ethumb_client_init() again to use the Ethumb_Client functions
527  * again.
528  */
529 EAPI int
530 ethumb_client_shutdown(void)
531 {
532    _initcount--;
533    if (_initcount > 0)
534      return _initcount;
535
536    e_dbus_shutdown();
537    ethumb_shutdown();
538    eina_log_domain_unregister(_log_dom);
539    _log_dom = -1;
540    eina_shutdown();
541    return _initcount;
542 }
543
544 /**
545  * Connects to Ethumb server and return the client instance.
546  *
547  * This is the "constructor" of Ethumb_Client, where everything
548  * starts.
549  *
550  * If server was down, it is tried to start it using DBus activation,
551  * then the connection is retried.
552  *
553  * This call is asynchronous and will not block, instead it will be in
554  * "not connected" state until @a connect_cb is called with either
555  * success or failure. On failure, then no methods should be
556  * called. On success you're now able to setup and then ask generation
557  * of thumbnails.
558  *
559  * Usually you should listen for server death/disconenction with
560  * ethumb_client_on_server_die_callback_set().
561  *
562  * @param connect_cb function to call to report connection success or
563  *        failure. Do not call any other ethumb_client method until
564  *        this function returns. The first received parameter is the
565  *        given argument @a data. Must @b not be @c NULL. This
566  *        function will not be called if user explicitly calls
567  *        ethumb_client_disconnect().
568  * @param data context to give back to @a connect_cb. May be @c NULL.
569  * @param free_data function used to release @a data resources, if
570  *        any. May be @c NULL. If this function exists, it will be
571  *        called immediately after @a connect_cb is called or if user
572  *        explicitly calls ethumb_client_disconnect() before such
573  *        (that is, don't rely on @a data after @a connect_cb was
574  *        called!)
575  *
576  * @return client instance or NULL if failed. If @a connect_cb is
577  *         missing it returns @c NULL. If it fail for other
578  *         conditions, @c NULL is also returned and @a connect_cb is
579  *         called with @c success=EINA_FALSE. The client instance is
580  *         not ready to be used until @a connect_cb is called.
581  */
582 EAPI Ethumb_Client *
583 ethumb_client_connect(Ethumb_Client_Connect_Cb connect_cb, const void *data, Eina_Free_Cb free_data)
584 {
585    Ethumb_Client *eclient;
586
587    EINA_SAFETY_ON_NULL_RETURN_VAL(connect_cb, NULL);
588
589    eclient = calloc(1, sizeof(*eclient));
590    if (!eclient)
591      {
592         ERR("could not allocate Ethumb_Client structure.");
593         goto err;
594      }
595
596    eclient->connect.cb = connect_cb;
597    eclient->connect.data = (void *)data;
598    eclient->connect.free_data = free_data;
599
600    eclient->ethumb = ethumb_new();
601    if (!eclient->ethumb)
602      {
603         ERR("could not create ethumb handler.");
604         goto ethumb_new_err;
605      }
606
607    eclient->conn = e_dbus_bus_get(DBUS_BUS_SESSION);
608    if (!eclient->conn)
609      {
610         ERR("could not connect to session bus.");
611         goto connection_err;
612      }
613
614    eclient->name_owner_changed_handler = e_dbus_signal_handler_add(
615          eclient->conn, fdo_bus_name, fdo_path, fdo_interface,
616          "NameOwnerChanged", _ethumb_client_name_owner_changed, eclient);
617
618    eclient->pending_get_name_owner = e_dbus_get_name_owner(
619          eclient->conn, _ethumb_dbus_bus_name, _ethumb_client_get_name_owner,
620          eclient);
621    if (!eclient->pending_get_name_owner)
622      {
623         ERR("could not create a get_name_owner request.");
624         goto connection_err;
625      }
626
627    return eclient;
628
629 connection_err:
630    ethumb_free(eclient->ethumb);
631 ethumb_new_err:
632    free(eclient);
633 err:
634    connect_cb((void *)data, NULL, EINA_FALSE);
635    if (free_data)
636      free_data((void *)data);
637    return NULL;
638 }
639
640 /**
641  * Disconnect the client, releasing all client resources.
642  *
643  * This is the destructor of Ethumb_Client, after it's disconnected
644  * the client handle is now gone and should not be used.
645  *
646  * @param client client instance to be destroyed. Must @b not be @c
647  *        NULL.
648  */
649 EAPI void
650 ethumb_client_disconnect(Ethumb_Client *client)
651 {
652    void *data;
653
654    EINA_SAFETY_ON_NULL_RETURN(client);
655
656    if (!client->connected)
657      goto end_connection;
658
659    EINA_LIST_FREE(client->pending_add, data)
660      {
661         struct _ethumb_pending_add *pending = data;
662         eina_stringshare_del(pending->file);
663         eina_stringshare_del(pending->key);
664         eina_stringshare_del(pending->thumb);
665         eina_stringshare_del(pending->thumb_key);
666         dbus_pending_call_cancel(pending->pending_call);
667         dbus_pending_call_unref(pending->pending_call);
668         if (pending->free_data)
669           pending->free_data(pending->data);
670         free(pending);
671      }
672
673    EINA_LIST_FREE(client->pending_gen, data)
674      {
675         struct _ethumb_pending_gen *pending = data;
676         eina_stringshare_del(pending->file);
677         eina_stringshare_del(pending->key);
678         eina_stringshare_del(pending->thumb);
679         eina_stringshare_del(pending->thumb_key);
680         if (pending->free_data)
681           pending->free_data(pending->data);
682         free(pending);
683      }
684
685    EINA_LIST_FREE(client->pending_remove, data)
686      {
687         struct _ethumb_pending_remove *pending = data;
688         dbus_pending_call_cancel(pending->pending_call);
689         dbus_pending_call_unref(pending->pending_call);
690         if (pending->free_data)
691           pending->free_data(pending->data);
692         free(pending);
693      }
694
695    if (client->pending_clear)
696      {
697         dbus_pending_call_cancel(client->pending_clear);
698         dbus_pending_call_unref(client->pending_clear);
699      }
700
701 end_connection:
702    if (client->object_path)
703      eina_stringshare_del(client->object_path);
704
705    if (client->pending_new)
706      dbus_pending_call_cancel(client->pending_new);
707
708    if (client->unique_name)
709      eina_stringshare_del(client->unique_name);
710
711    if (client->pending_get_name_owner)
712      dbus_pending_call_cancel(client->pending_get_name_owner);
713
714    if (client->pending_start_service_by_name)
715      dbus_pending_call_cancel(client->pending_start_service_by_name);
716
717    ethumb_free(client->ethumb);
718
719    e_dbus_signal_handler_del(client->conn, client->name_owner_changed_handler);
720    if (client->connected)
721      e_dbus_signal_handler_del(client->conn, client->generated_signal);
722    e_dbus_connection_close(client->conn);
723
724    if (client->connect.free_data)
725      client->connect.free_data(client->connect.data);
726    if (client->die.free_data)
727      client->die.free_data(client->die.data);
728
729    free(client);
730 }
731
732 /**
733  * Sets the callback to report server died.
734  *
735  * When server dies there is nothing you can do, just release
736  * resources with ethumb_client_disconnect() and probably try to
737  * connect again.
738  *
739  * Usually you should set this callback and handle this case, it does
740  * happen!
741  *
742  * @param client the client instance to monitor. Must @b not be @c
743  *        NULL.
744  * @param server_die_cb function to call back when server dies. The
745  *        first parameter will be the argument @a data. May be @c
746  *        NULL.
747  * @param data context to give back to @a server_die_cb. May be @c
748  *        NULL.
749  * @param free_data used to release @a data resources after @a
750  *        server_die_cb is called or user calls
751  *        ethumb_client_disconnect().
752  */
753 EAPI void
754 ethumb_client_on_server_die_callback_set(Ethumb_Client *client, Ethumb_Client_Die_Cb server_die_cb, const void *data, Eina_Free_Cb free_data)
755 {
756    EINA_SAFETY_ON_NULL_RETURN(client);
757
758    if (client->die.free_data)
759      client->die.free_data(client->die.data);
760
761    client->die.cb = server_die_cb;
762    client->die.data = (void *)data;
763    client->die.free_data = free_data;
764 }
765
766 /**
767  * @cond LOCAL
768  */
769
770 static void
771 _ethumb_client_ethumb_setup_cb(void *data, DBusMessage *msg, DBusError *error)
772 {
773    DBusMessageIter iter;
774    int t;
775    dbus_bool_t result = 0;
776    Ethumb_Client *client = data;
777
778    client->pending_setup = NULL;
779
780    if (!_dbus_callback_check_and_init(msg, &iter, error))
781      return;
782
783    t = dbus_message_iter_get_arg_type(&iter);
784    if (!_dbus_iter_type_check(t, DBUS_TYPE_BOOLEAN))
785      return;
786
787    dbus_message_iter_get_basic(&iter, &result);
788 }
789
790 static const char *
791 _ethumb_client_dbus_get_bytearray(DBusMessageIter *iter)
792 {
793    int el_type;
794    int length;
795    DBusMessageIter riter;
796    const char *result;
797
798    el_type = dbus_message_iter_get_element_type(iter);
799    if (el_type != DBUS_TYPE_BYTE)
800      {
801         ERR("not an byte array element.");
802         return NULL;
803      }
804
805    dbus_message_iter_recurse(iter, &riter);
806    dbus_message_iter_get_fixed_array(&riter, &result, &length);
807
808    if (result[0] == '\0')
809      return NULL;
810    else
811      return eina_stringshare_add(result);
812 }
813
814 static void
815 _ethumb_client_dbus_append_bytearray(DBusMessageIter *iter, const char *string)
816 {
817    DBusMessageIter viter;
818
819    if (!string)
820      string = "";
821
822    dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &viter);
823    dbus_message_iter_append_fixed_array(&viter, DBUS_TYPE_BYTE, &string,
824                                         strlen(string) + 1);
825    dbus_message_iter_close_container(iter, &viter);
826 }
827
828 /**
829  * @endcond
830  */
831
832 /**
833  * Send setup to server.
834  *
835  * This method is called automatically by ethumb_client_generate() if
836  * any property was changed. No need to call it manually.
837  *
838  * @param client client instance. Must @b not be @c NULL and client
839  *        must be connected (after connected_cb is called).
840  */
841 EAPI void
842 ethumb_client_ethumb_setup(Ethumb_Client *client)
843 {
844    DBusMessage *msg;
845    DBusMessageIter iter, aiter, diter, viter, vaiter;
846    Ethumb *e = client->ethumb;
847    const char *entry;
848    dbus_int32_t tw, th, format, aspect, quality, compress;
849    float cx, cy;
850    double t;
851    const char *theme_file, *group, *swallow;
852    const char *directory, *category;
853    double video_time, video_start, video_interval;
854    dbus_int32_t video_ntimes, video_fps, document_page;
855
856    EINA_SAFETY_ON_NULL_RETURN(client);
857    EINA_SAFETY_ON_FALSE_RETURN(client->connected);
858    client->ethumb_dirty = 0;
859
860    msg = dbus_message_new_method_call(_ethumb_dbus_bus_name,
861                                       client->object_path,
862                                       _ethumb_dbus_objects_interface,
863                                       "ethumb_setup");
864    dbus_message_iter_init_append(msg, &iter);
865    dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &aiter);
866
867 /**
868  * @cond LOCAL
869  */
870 #define _open_variant_iter(str_entry, str_type, end_iter)                  \
871    entry = str_entry;                                                      \
872    dbus_message_iter_open_container(&aiter, DBUS_TYPE_DICT_ENTRY, NULL, &diter); \
873    dbus_message_iter_append_basic(&diter, DBUS_TYPE_STRING, &entry);   \
874    dbus_message_iter_open_container(&diter, DBUS_TYPE_VARIANT, str_type,   \
875                                     &end_iter);
876
877 #define _close_variant_iter(end_iter)                                      \
878    dbus_message_iter_close_container(&diter, &end_iter);                   \
879    dbus_message_iter_close_container(&aiter, &diter);
880 /**
881  * @endcond
882  */
883
884    /* starting array elements */
885
886    _open_variant_iter("size", "(ii)", viter);
887    dbus_message_iter_open_container(&viter, DBUS_TYPE_STRUCT, NULL, &vaiter);
888    ethumb_thumb_size_get(e, &tw, &th);
889    dbus_message_iter_append_basic(&vaiter, DBUS_TYPE_INT32, &tw);
890    dbus_message_iter_append_basic(&vaiter, DBUS_TYPE_INT32, &th);
891    dbus_message_iter_close_container(&viter, &vaiter);
892    _close_variant_iter(viter);
893
894    _open_variant_iter("format", "i", viter);
895    format = ethumb_thumb_format_get(e);
896    dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &format);
897    _close_variant_iter(viter);
898
899    _open_variant_iter("aspect", "i", viter);
900    aspect = ethumb_thumb_aspect_get(e);
901    dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &aspect);
902    _close_variant_iter(viter);
903
904    _open_variant_iter("crop", "(dd)", viter);
905    dbus_message_iter_open_container(&viter, DBUS_TYPE_STRUCT, NULL, &vaiter);
906    ethumb_thumb_crop_align_get(e, &cx, &cy);
907    t = cx;
908    dbus_message_iter_append_basic(&vaiter, DBUS_TYPE_DOUBLE, &t);
909    t = cy;
910    dbus_message_iter_append_basic(&vaiter, DBUS_TYPE_DOUBLE, &t);
911    dbus_message_iter_close_container(&viter, &vaiter);
912    _close_variant_iter(viter);
913
914    _open_variant_iter("quality", "i", viter);
915    quality = ethumb_thumb_quality_get(e);
916    dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &quality);
917    _close_variant_iter(viter);
918
919    _open_variant_iter("compress", "i", viter);
920    compress = ethumb_thumb_compress_get(e);
921    dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &compress);
922    _close_variant_iter(viter);
923
924    _open_variant_iter("frame", "(ayayay)", viter);
925    dbus_message_iter_open_container(&viter, DBUS_TYPE_STRUCT, NULL, &vaiter);
926    ethumb_frame_get(e, &theme_file, &group, &swallow);
927    _ethumb_client_dbus_append_bytearray(&vaiter, theme_file);
928    _ethumb_client_dbus_append_bytearray(&vaiter, group);
929    _ethumb_client_dbus_append_bytearray(&vaiter, swallow);
930    dbus_message_iter_close_container(&viter, &vaiter);
931    _close_variant_iter(viter);
932
933    _open_variant_iter("directory", "ay", viter);
934    directory = ethumb_thumb_dir_path_get(e);
935    _ethumb_client_dbus_append_bytearray(&viter, directory);
936    _close_variant_iter(viter);
937
938    _open_variant_iter("category", "ay", viter);
939    category = ethumb_thumb_category_get(e);
940    _ethumb_client_dbus_append_bytearray(&viter, category);
941    _close_variant_iter(viter);
942
943    _open_variant_iter("video_time", "d", viter);
944    video_time = ethumb_video_time_get(e);
945    dbus_message_iter_append_basic(&viter, DBUS_TYPE_DOUBLE, &video_time);
946    _close_variant_iter(viter);
947
948    _open_variant_iter("video_start", "d", viter);
949    video_start = ethumb_video_start_get(e);
950    dbus_message_iter_append_basic(&viter, DBUS_TYPE_DOUBLE, &video_start);
951    _close_variant_iter(viter);
952
953    _open_variant_iter("video_interval", "d", viter);
954    video_interval = ethumb_video_interval_get(e);
955    dbus_message_iter_append_basic(&viter, DBUS_TYPE_DOUBLE, &video_interval);
956    _close_variant_iter(viter);
957
958    _open_variant_iter("video_ntimes", "u", viter);
959    video_ntimes = ethumb_video_ntimes_get(e);
960    dbus_message_iter_append_basic(&viter, DBUS_TYPE_UINT32, &video_ntimes);
961    _close_variant_iter(viter);
962
963    _open_variant_iter("video_fps", "u", viter);
964    video_fps = ethumb_video_fps_get(e);
965    dbus_message_iter_append_basic(&viter, DBUS_TYPE_UINT32, &video_fps);
966    _close_variant_iter(viter);
967
968    _open_variant_iter("document_page", "u", viter);
969    document_page = ethumb_document_page_get(e);
970    dbus_message_iter_append_basic(&viter, DBUS_TYPE_UINT32, &document_page);
971    _close_variant_iter(viter);
972
973 #undef _open_variant_iter
974 #undef _close_variant_iter
975
976    dbus_message_iter_close_container(&iter, &aiter);
977
978    client->pending_setup = e_dbus_message_send(client->conn, msg,
979                                                _ethumb_client_ethumb_setup_cb,
980                                                -1, client);
981    dbus_message_unref(msg);
982 }
983
984 /**
985  * @cond LOCAL
986  */
987
988 static void
989 _ethumb_client_generated_cb(void *data, DBusMessage *msg)
990 {
991    DBusMessageIter iter;
992    dbus_int32_t id = -1;
993    const char *thumb;
994    const char *thumb_key;
995    Ethumb_Client *client = data;
996    int t;
997    dbus_bool_t success;
998    Eina_List *l;
999    int found;
1000    struct _ethumb_pending_gen *pending;
1001
1002    dbus_message_iter_init(msg, &iter);
1003
1004    t = dbus_message_iter_get_arg_type(&iter);
1005    if (!_dbus_iter_type_check(t, DBUS_TYPE_INT32))
1006      goto end;
1007    dbus_message_iter_get_basic(&iter, &id);
1008    dbus_message_iter_next(&iter);
1009
1010    t = dbus_message_iter_get_arg_type(&iter);
1011    if (!_dbus_iter_type_check(t, DBUS_TYPE_ARRAY))
1012      goto end;
1013    thumb = _ethumb_client_dbus_get_bytearray(&iter);
1014    dbus_message_iter_next(&iter);
1015
1016    t = dbus_message_iter_get_arg_type(&iter);
1017    if (!_dbus_iter_type_check(t, DBUS_TYPE_ARRAY))
1018      goto end;
1019    thumb_key = _ethumb_client_dbus_get_bytearray(&iter);
1020    dbus_message_iter_next(&iter);
1021
1022    t = dbus_message_iter_get_arg_type(&iter);
1023    if (!_dbus_iter_type_check(t, DBUS_TYPE_BOOLEAN))
1024      goto end;
1025    dbus_message_iter_get_basic(&iter, &success);
1026
1027    found = 0;
1028    l = client->pending_gen;
1029    while (l)
1030      {
1031         pending = l->data;
1032         if (pending->id == id)
1033           {
1034              found = 1;
1035              break;
1036           }
1037         l = l->next;
1038      }
1039
1040    if (found)
1041      {
1042         client->pending_gen = eina_list_remove_list(client->pending_gen, l);
1043         pending->generated_cb(pending->data, client, id,
1044                               pending->file, pending->key,
1045                               pending->thumb, pending->thumb_key,
1046                               success);
1047         if (pending->free_data)
1048           pending->free_data(pending->data);
1049         eina_stringshare_del(pending->file);
1050         eina_stringshare_del(pending->key);
1051         eina_stringshare_del(pending->thumb);
1052         eina_stringshare_del(pending->thumb_key);
1053         free(pending);
1054      }
1055
1056 end:
1057    eina_stringshare_del(thumb);
1058    eina_stringshare_del(thumb_key);
1059 }
1060
1061 static void
1062 _ethumb_client_queue_add_cb(void *data, DBusMessage *msg, DBusError *error)
1063 {
1064    DBusMessageIter iter;
1065    int t;
1066    dbus_int32_t id = -1;
1067    struct _ethumb_pending_add *pending = data;
1068    struct _ethumb_pending_gen *generating;
1069    Ethumb_Client *client = pending->client;
1070
1071    client->pending_add = eina_list_remove(client->pending_add, pending);
1072
1073    if (!_dbus_callback_check_and_init(msg, &iter, error))
1074      goto end;
1075
1076    t = dbus_message_iter_get_arg_type(&iter);
1077    if (!_dbus_iter_type_check(t, DBUS_TYPE_INT32))
1078      goto end;
1079
1080    dbus_message_iter_get_basic(&iter, &id);
1081
1082    generating = calloc(1, sizeof(*generating));
1083    generating->id = id;
1084    generating->file = pending->file;
1085    generating->key = pending->key;
1086    generating->thumb = pending->thumb;
1087    generating->thumb_key = pending->thumb_key;
1088    generating->generated_cb = pending->generated_cb;
1089    generating->data = pending->data;
1090    generating->free_data = pending->free_data;
1091    client->pending_gen = eina_list_append(client->pending_gen, generating);
1092
1093 end:
1094    free(pending);
1095 }
1096
1097 static int
1098 _ethumb_client_queue_add(Ethumb_Client *client, const char *file, const char *key, const char *thumb, const char *thumb_key, Ethumb_Client_Generate_Cb generated_cb, const void *data, Eina_Free_Cb free_data)
1099 {
1100    DBusMessage *msg;
1101    DBusMessageIter iter;
1102    struct _ethumb_pending_add *pending;
1103
1104    pending = calloc(1, sizeof(*pending));
1105    pending->id = client->id_count;
1106    pending->file = eina_stringshare_add(file);
1107    pending->key = eina_stringshare_add(key);
1108    pending->thumb = eina_stringshare_add(thumb);
1109    pending->thumb_key = eina_stringshare_add(thumb_key);
1110    pending->generated_cb = generated_cb;
1111    pending->data = (void *)data;
1112    pending->free_data = free_data;
1113    pending->client = client;
1114
1115    client->id_count = (client->id_count + 1) % MAX_ID;
1116
1117    msg = dbus_message_new_method_call(_ethumb_dbus_bus_name,
1118                                       client->object_path,
1119                                       _ethumb_dbus_objects_interface,
1120                                       "queue_add");
1121
1122    dbus_message_iter_init_append(msg, &iter);
1123    dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &pending->id);
1124    _ethumb_client_dbus_append_bytearray(&iter, file);
1125    _ethumb_client_dbus_append_bytearray(&iter, key);
1126    _ethumb_client_dbus_append_bytearray(&iter, thumb);
1127    _ethumb_client_dbus_append_bytearray(&iter, thumb_key);
1128
1129    pending->pending_call = e_dbus_message_send(client->conn, msg,
1130                                                _ethumb_client_queue_add_cb,
1131                                                -1, pending);
1132    client->pending_add = eina_list_append(client->pending_add, pending);
1133    dbus_message_unref(msg);
1134
1135    return pending->id;
1136 }
1137
1138 static void
1139 _ethumb_client_queue_remove_cb(void *data, DBusMessage *msg, DBusError *error)
1140 {
1141    DBusMessageIter iter;
1142    int t;
1143    dbus_bool_t success = 0;
1144    struct _ethumb_pending_remove *pending = data;
1145    Ethumb_Client *client = pending->client;
1146
1147    client->pending_remove = eina_list_remove(client->pending_remove, pending);
1148
1149    if (!_dbus_callback_check_and_init(msg, &iter, error))
1150      goto end;
1151
1152    t = dbus_message_iter_get_arg_type(&iter);
1153    if (!_dbus_iter_type_check(t, DBUS_TYPE_BOOLEAN))
1154      goto end;
1155
1156    dbus_message_iter_get_basic(&iter, &success);
1157
1158 end:
1159    if (pending->cancel_cb)
1160      pending->cancel_cb(pending->data, success);
1161    if (pending->free_data)
1162      pending->free_data(pending->data);
1163    free(pending);
1164 }
1165 /**
1166  * @endcond
1167  */
1168
1169 /**
1170  * Ask server to cancel generation of thumbnail.
1171  *
1172  * @param client client instance. Must @b not be @c NULL and client
1173  *        must be connected (after connected_cb is called).
1174  * @param id valid id returned by ethumb_client_generate()
1175  * @param cancel_cb function to report cancellation results.
1176  * @param data context argument to give back to @a cancel_cb. May be
1177  *        @c NULL.
1178  * @param data context to give back to @a cancel_cb. May be @c
1179  *        NULL.
1180  * @param free_data used to release @a data resources after @a
1181  *        cancel_cb is called or user calls
1182  *        ethumb_client_disconnect().
1183  */
1184 EAPI void
1185 ethumb_client_generate_cancel(Ethumb_Client *client, int id, Ethumb_Client_Generate_Cancel_Cb cancel_cb, const void *data, Eina_Free_Cb free_data)
1186 {
1187    DBusMessage *msg;
1188    struct _ethumb_pending_remove *pending;
1189    Eina_List *l;
1190    int found;
1191    dbus_int32_t id32 = id;
1192    EINA_SAFETY_ON_NULL_RETURN(client);
1193    EINA_SAFETY_ON_FALSE_RETURN(id >= 0);
1194
1195    pending = calloc(1, sizeof(*pending));
1196    pending->id = id;
1197    pending->cancel_cb = cancel_cb;
1198    pending->data = (void *)data;
1199    pending->free_data = free_data;
1200    pending->client = client;
1201
1202    msg = dbus_message_new_method_call(_ethumb_dbus_bus_name,
1203                                       client->object_path,
1204                                       _ethumb_dbus_objects_interface,
1205                                       "queue_remove");
1206
1207    dbus_message_append_args(msg, DBUS_TYPE_INT32, &id32, DBUS_TYPE_INVALID);
1208    pending->pending_call = e_dbus_message_send(client->conn, msg,
1209                                                _ethumb_client_queue_remove_cb,
1210                                                -1, pending);
1211    client->pending_remove = eina_list_append(client->pending_remove, pending);
1212
1213    found = 0;
1214    l = client->pending_add;
1215    while (l)
1216      {
1217         struct _ethumb_pending_add *pending = l->data;
1218         if (pending->id != id32)
1219           {
1220              l = l->next;
1221              continue;
1222           }
1223         client->pending_add = eina_list_remove_list(client->pending_add, l);
1224         eina_stringshare_del(pending->file);
1225         eina_stringshare_del(pending->key);
1226         eina_stringshare_del(pending->thumb);
1227         eina_stringshare_del(pending->thumb_key);
1228         dbus_pending_call_cancel(pending->pending_call);
1229         dbus_pending_call_unref(pending->pending_call);
1230         if (pending->free_data)
1231           pending->free_data(pending->data);
1232         free(pending);
1233         found = 1;
1234         break;
1235      }
1236
1237    if (found)
1238      goto end;
1239
1240    l = client->pending_gen;
1241    while (l)
1242      {
1243         struct _ethumb_pending_gen *pending = l->data;
1244         if (pending->id != id32)
1245           {
1246              l = l->next;
1247              continue;
1248           }
1249         client->pending_gen = eina_list_remove_list(client->pending_gen, l);
1250         eina_stringshare_del(pending->file);
1251         eina_stringshare_del(pending->key);
1252         eina_stringshare_del(pending->thumb);
1253         eina_stringshare_del(pending->thumb_key);
1254         if (pending->free_data)
1255           pending->free_data(pending->data);
1256         free(pending);
1257         found = 1;
1258         break;
1259      }
1260
1261 end:
1262    dbus_message_unref(msg);
1263 }
1264
1265 /**
1266  * @cond LOCAL
1267  */
1268 static void
1269 _ethumb_client_queue_clear_cb(void *data, DBusMessage *msg __UNUSED__, DBusError *error __UNUSED__)
1270 {
1271    Ethumb_Client *client = data;
1272
1273    client->pending_clear = NULL;
1274 }
1275 /**
1276  * @endcond
1277  */
1278
1279 /**
1280  * Ask server to cancel generation of all thumbnails.
1281  *
1282  * @param client client instance. Must @b not be @c NULL and client
1283  *        must be connected (after connected_cb is called).
1284  *
1285  * @see ethumb_client_generate_cancel()
1286  */
1287 EAPI void
1288 ethumb_client_generate_cancel_all(Ethumb_Client *client)
1289 {
1290    DBusMessage *msg;
1291    void *data;
1292    EINA_SAFETY_ON_NULL_RETURN(client);
1293
1294    if (client->pending_clear)
1295      return;
1296
1297    EINA_LIST_FREE(client->pending_add, data)
1298      {
1299         struct _ethumb_pending_add *pending = data;
1300         eina_stringshare_del(pending->file);
1301         eina_stringshare_del(pending->key);
1302         eina_stringshare_del(pending->thumb);
1303         eina_stringshare_del(pending->thumb_key);
1304         dbus_pending_call_cancel(pending->pending_call);
1305         dbus_pending_call_unref(pending->pending_call);
1306         if (pending->free_data)
1307           pending->free_data(pending->data);
1308         free(pending);
1309      }
1310
1311    EINA_LIST_FREE(client->pending_gen, data)
1312      {
1313         struct _ethumb_pending_gen *pending = data;
1314         eina_stringshare_del(pending->file);
1315         eina_stringshare_del(pending->key);
1316         eina_stringshare_del(pending->thumb);
1317         eina_stringshare_del(pending->thumb_key);
1318         if (pending->free_data)
1319           pending->free_data(pending->data);
1320         free(pending);
1321      }
1322
1323    msg = dbus_message_new_method_call(_ethumb_dbus_bus_name,
1324                                       client->object_path,
1325                                       _ethumb_dbus_objects_interface,
1326                                       "queue_clear");
1327
1328    client->pending_clear = e_dbus_message_send(client->conn, msg,
1329                                                _ethumb_client_queue_clear_cb,
1330                                                -1, client);
1331
1332    dbus_message_unref(msg);
1333 }
1334
1335 /**
1336  * Configure future requests to use FreeDesktop.Org preset.
1337  *
1338  * This is a preset to provide freedesktop.org (fdo) standard
1339  * compliant thumbnails. That is, files are stored as JPEG under
1340  * ~/.thumbnails/SIZE, with size being either normal (128x128) or
1341  * large (256x256).
1342  *
1343  * @param client the client instance to use. Must @b not be @c
1344  *        NULL. May be pending connected (can be called before @c
1345  *        connected_cb)
1346  * @param s size identifier, either #ETHUMB_THUMB_NORMAL (0) or
1347  *        #ETHUMB_THUMB_LARGE (1).
1348  *
1349  * @see ethumb_client_size_set()
1350  * @see ethumb_client_aspect_set()
1351  * @see ethumb_client_crop_align_set()
1352  * @see ethumb_client_category_set()
1353  * @see ethumb_client_dir_path_set()
1354  */
1355 EAPI void
1356 ethumb_client_fdo_set(Ethumb_Client *client, Ethumb_Thumb_FDO_Size s)
1357 {
1358    EINA_SAFETY_ON_NULL_RETURN(client);
1359
1360    client->ethumb_dirty = 1;
1361    ethumb_thumb_fdo_set(client->ethumb, s);
1362 }
1363
1364 /**
1365  * Configure future request to use custom size.
1366  *
1367  * @param client the client instance to use. Must @b not be @c
1368  *        NULL. May be pending connected (can be called before @c
1369  *        connected_cb)
1370  * @param tw width, default is 128.
1371  * @param th height, default is 128.
1372  */
1373 EAPI void
1374 ethumb_client_size_set(Ethumb_Client *client, int tw, int th)
1375 {
1376    EINA_SAFETY_ON_NULL_RETURN(client);
1377
1378    client->ethumb_dirty = 1;
1379    ethumb_thumb_size_set(client->ethumb, tw, th);
1380 }
1381
1382 /**
1383  * Retrieve future request to use custom size.
1384  *
1385  * @param client the client instance to use. Must @b not be @c
1386  *        NULL. May be pending connected (can be called before @c
1387  *        connected_cb)
1388  * @param tw where to return width. May be @c NULL.
1389  * @param th where to return height. May be @c NULL.
1390  */
1391 EAPI void
1392 ethumb_client_size_get(const Ethumb_Client *client, int *tw, int *th)
1393 {
1394    if (tw) *tw = 0;
1395    if (th) *th = 0;
1396    EINA_SAFETY_ON_NULL_RETURN(client);
1397
1398    ethumb_thumb_size_get(client->ethumb, tw, th);
1399 }
1400
1401 /**
1402  * Configure format to use for future requests.
1403  *
1404  * @param client the client instance to use. Must @b not be @c
1405  *        NULL. May be pending connected (can be called before @c
1406  *        connected_cb)
1407  * @param f format identifier to use, either #ETHUMB_THUMB_FDO (0),
1408  *        #ETHUMB_THUMB_JPEG (1) or #ETHUMB_THUMB_EET (2). Default is FDO.
1409  */
1410 EAPI void
1411 ethumb_client_format_set(Ethumb_Client *client, Ethumb_Thumb_Format f)
1412 {
1413    EINA_SAFETY_ON_NULL_RETURN(client);
1414
1415    client->ethumb_dirty = 1;
1416    ethumb_thumb_format_set(client->ethumb, f);
1417 }
1418
1419 /**
1420  * Retrieve format to use for future requests.
1421  *
1422  * @param client the client instance to use. Must @b not be @c
1423  *        NULL. May be pending connected (can be called before @c
1424  *        connected_cb)
1425  *
1426  * @return format identifier to use, either #ETHUMB_THUMB_FDO (0),
1427  *         #ETHUMB_THUMB_JPEG (1) or #ETHUMB_THUMB_EET (2).
1428  */
1429 EAPI Ethumb_Thumb_Format
1430 ethumb_client_format_get(const Ethumb_Client *client)
1431 {
1432    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1433
1434    return ethumb_thumb_format_get(client->ethumb);
1435 }
1436
1437 /**
1438  * Configure aspect mode to use.
1439  *
1440  * If aspect is kept (#ETHUMB_THUMB_KEEP_ASPECT), then image will be
1441  * rescaled so the largest dimension is not bigger than it's specified
1442  * size (see ethumb_client_size_get()) and the other dimension is
1443  * resized in the same proportion. Example: size is 256x256, image is
1444  * 1000x500, resulting thumbnail is 256x128.
1445  *
1446  * If aspect is ignored (#ETHUMB_THUMB_IGNORE_ASPECT), then image will
1447  * be distorted to match required thumbnail size. Example: size is
1448  * 256x256, image is 1000x500, resulting thumbnail is 256x256.
1449  *
1450  * If crop is required (#ETHUMB_THUMB_CROP), then image will be
1451  * cropped so the smallest dimension is not bigger than its specified
1452  * size (see ethumb_client_size_get()) and the other dimension will
1453  * overflow, not being visible in the final image. How it will
1454  * overflow is speficied by ethumb_client_crop_align_set()
1455  * alignment. Example: size is 256x256, image is 1000x500, crop
1456  * alignment is 0.5, 0.5, resulting thumbnail is 256x256 with 250
1457  * pixels from left and 250 pixels from right being lost, that is just
1458  * the 500x500 central pixels of image will be considered for scaling.
1459  *
1460  * @param client the client instance to use. Must @b not be @c
1461  *        NULL. May be pending connected (can be called before @c
1462  *        connected_cb)
1463  * @param a aspect mode identifier, either #ETHUMB_THUMB_KEEP_ASPECT (0),
1464  *        #ETHUMB_THUMB_IGNORE_ASPECT (1) or #ETHUMB_THUMB_CROP (2).
1465  */
1466 EAPI void
1467 ethumb_client_aspect_set(Ethumb_Client *client, Ethumb_Thumb_Aspect a)
1468 {
1469    EINA_SAFETY_ON_NULL_RETURN(client);
1470
1471    client->ethumb_dirty = 1;
1472    ethumb_thumb_aspect_set(client->ethumb, a);
1473 }
1474
1475 /**
1476  * Get current aspect in use for requests.
1477  *
1478  * @param client the client instance to use. Must @b not be @c
1479  *        NULL. May be pending connected (can be called before @c
1480  *        connected_cb)
1481  *
1482  * @return aspect in use for future requests.
1483  */
1484 EAPI Ethumb_Thumb_Aspect
1485 ethumb_client_aspect_get(const Ethumb_Client *client)
1486 {
1487    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1488
1489    return ethumb_thumb_aspect_get(client->ethumb);
1490 }
1491
1492 /**
1493  * Configure crop alignment in use for future requests.
1494  *
1495  * @param client the client instance to use. Must @b not be @c
1496  *        NULL. May be pending connected (can be called before @c
1497  *        connected_cb)
1498  * @param x horizontal alignment. 0.0 means left side will be visible
1499  *        or right side is being lost. 1.0 means right side will be
1500  *        visible or left side is being lost. 0.5 means just center is
1501  *        visible, both sides will be lost.  Default is 0.5.
1502  * @param y vertical alignment. 0.0 is top visible, 1.0 is bottom
1503  *        visible, 0.5 is center visible. Default is 0.5
1504  */
1505 EAPI void
1506 ethumb_client_crop_align_set(Ethumb_Client *client, float x, float y)
1507 {
1508    EINA_SAFETY_ON_NULL_RETURN(client);
1509
1510    client->ethumb_dirty = 1;
1511    ethumb_thumb_crop_align_set(client->ethumb, x, y);
1512 }
1513
1514 /**
1515  * Get current crop alignment in use for requests.
1516  *
1517  * @param client the client instance to use. Must @b not be @c
1518  *        NULL. May be pending connected (can be called before @c
1519  *        connected_cb)
1520  * @param x where to return horizontal alignment. May be @c NULL.
1521  * @param y where to return vertical alignment. May be @c NULL.
1522  */
1523 EAPI void
1524 ethumb_client_crop_align_get(const Ethumb_Client *client, float *x, float *y)
1525 {
1526    if (x) *x = 0.0;
1527    if (y) *y = 0.0;
1528    EINA_SAFETY_ON_NULL_RETURN(client);
1529
1530    ethumb_thumb_crop_align_get(client->ethumb, x, y);
1531 }
1532
1533 /**
1534  * Configure quality to be used in thumbnails.
1535  *
1536  * @param client the client instance to use. Must @b not be @c
1537  *        NULL. May be pending connected (can be called before @c
1538  *        connected_cb)
1539  * @param quality value from 0 to 100, default is 80. The effect
1540  *        depends on the format being used, PNG will not use it.
1541  */
1542 EAPI void
1543 ethumb_client_quality_set(Ethumb_Client *client, int quality)
1544 {
1545    EINA_SAFETY_ON_NULL_RETURN(client);
1546
1547    ethumb_thumb_quality_set(client->ethumb, quality);
1548 }
1549
1550 /**
1551  * Get quality to be used in thumbnails.
1552  *
1553  * @param client the client instance to use. Must @b not be @c
1554  *        NULL. May be pending connected (can be called before @c
1555  *        connected_cb)
1556  *
1557  * @return quality value from 0 to 100, default is 80. The effect
1558  *         depends on the format being used, PNG will not use it.
1559  */
1560 EAPI int
1561 ethumb_client_quality_get(const Ethumb_Client *client)
1562 {
1563    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1564
1565    return ethumb_thumb_quality_get(client->ethumb);
1566 }
1567
1568 /**
1569  * Configure compression level used in requests.
1570  *
1571  * @param client the client instance to use. Must @b not be @c
1572  *        NULL. May be pending connected (can be called before @c
1573  *        connected_cb)
1574  * @param compress value from 0 to 9, default is 9. The effect
1575  *        depends on the format being used, JPEG will not use it.
1576  */
1577 EAPI void
1578 ethumb_client_compress_set(Ethumb_Client *client, int compress)
1579 {
1580    EINA_SAFETY_ON_NULL_RETURN(client);
1581
1582    ethumb_thumb_compress_set(client->ethumb, compress);
1583 }
1584
1585 /**
1586  * Get compression level used in requests.
1587  *
1588  * @param client the client instance to use. Must @b not be @c
1589  *        NULL. May be pending connected (can be called before @c
1590  *        connected_cb)
1591  *
1592  * @return compress value from 0 to 9, default is 9. The effect
1593  *         depends on the format being used, JPEG will not use it.
1594  */
1595 EAPI int
1596 ethumb_client_compress_get(const Ethumb_Client *client)
1597 {
1598    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1599
1600    return ethumb_thumb_compress_get(client->ethumb);
1601 }
1602
1603 /**
1604  * Set frame to apply to future thumbnails.
1605  *
1606  * This will create an edje object that will have image swallowed
1607  * in. This can be used to simulate Polaroid or wood frames in the
1608  * generated image. Remeber it is bad to modify the original contents
1609  * of thumbnails, but sometimes it's useful to have it composited and
1610  * avoid runtime overhead.
1611  *
1612  * @param client the client instance to use. Must @b not be @c
1613  *        NULL. May be pending connected (can be called before @c
1614  *        connected_cb)
1615  * @param file file path to edje.
1616  * @param group group inside edje to use.
1617  * @param swallow name of swallow part.
1618  */
1619 EAPI Eina_Bool
1620 ethumb_client_frame_set(Ethumb_Client *client, const char *file, const char *group, const char *swallow)
1621 {
1622    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1623
1624    client->ethumb_dirty = 1;
1625    return ethumb_frame_set(client->ethumb, file, group, swallow);
1626 }
1627
1628 /**
1629  * Configure where to store thumbnails in future requests.
1630  *
1631  * This value will be used to generate thumbnail paths, that is, it
1632  * will be used when ethumb_client_thumb_path_set() was not called
1633  * after last ethumb_client_file_set().
1634  *
1635  * Note that this is the base, a category is added to this path as a
1636  * sub directory. This is not the final directory where files are
1637  * stored, the thumbnail system will account @b category as well, see
1638  * ethumb_client_category_set().
1639  *
1640  * As other options, this value will only be applied to future
1641  * requests.
1642  *
1643  * @param client the client instance to use. Must @b not be @c
1644  *        NULL. May be pending connected (can be called before @c
1645  *        connected_cb)
1646  * @param path base directory where to store thumbnails. Default is
1647  *        ~/.thumbnails
1648  *
1649  * @see ethumb_client_category_set()
1650  */
1651 EAPI void
1652 ethumb_client_dir_path_set(Ethumb_Client *client, const char *path)
1653 {
1654    EINA_SAFETY_ON_NULL_RETURN(client);
1655
1656    client->ethumb_dirty = 1;
1657    ethumb_thumb_dir_path_set(client->ethumb, path);
1658 }
1659
1660 /**
1661  * Get base directory path where to store thumbnails.
1662  *
1663  * @param client the client instance to use. Must @b not be @c
1664  *        NULL. May be pending connected (can be called before @c
1665  *        connected_cb)
1666  *
1667  * @return pointer to internal string with current path. This string
1668  *         should not be modified or freed.
1669  *
1670  * @see ethumb_client_dir_path_set()
1671  */
1672 EAPI const char *
1673 ethumb_client_dir_path_get(const Ethumb_Client *client)
1674 {
1675    EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
1676
1677    return ethumb_thumb_dir_path_get(client->ethumb);
1678 }
1679
1680 /**
1681  * Category directory to store thumbnails.
1682  *
1683  * This value will be used to generate thumbnail paths, that is, it
1684  * will be used when ethumb_client_thumb_path_set() was not called
1685  * after last ethumb_client_file_set().
1686  *
1687  * This is a sub-directory inside base directory
1688  * (ethumb_client_dir_path_set()) that creates a namespace to avoid
1689  * different options resulting in the same file.
1690  *
1691  * As other options, this value will only be applied to future
1692  * requests.
1693  *
1694  * @param client the client instance to use. Must @b not be @c
1695  *        NULL. May be pending connected (can be called before @c
1696  *        connected_cb)
1697  * @param category category sub directory to store thumbnail. Default
1698  *        is either "normal" or "large" for FDO compliant thumbnails
1699  *        or WIDTHxHEIGHT-ASPECT[-FRAMED]-FORMAT. It can be a string
1700  *        or @c NULL to use auto generated names.
1701  *
1702  * @see ethumb_client_dir_path_set()
1703  */
1704 EAPI void
1705 ethumb_client_category_set(Ethumb_Client *client, const char *category)
1706 {
1707    EINA_SAFETY_ON_NULL_RETURN(client);
1708
1709    client->ethumb_dirty = 1;
1710    ethumb_thumb_category_set(client->ethumb, category);
1711 }
1712
1713 /**
1714  * Get category sub-directory  where to store thumbnails.
1715  *
1716  * @param client the client instance to use. Must @b not be @c
1717  *        NULL. May be pending connected (can be called before @c
1718  *        connected_cb)
1719  *
1720  * @return pointer to internal string with current path. This string
1721  *         should not be modified or freed.
1722  *
1723  * @see ethumb_client_category_set()
1724  */
1725 EAPI const char *
1726 ethumb_client_category_get(const Ethumb_Client *client)
1727 {
1728    EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
1729
1730    return ethumb_thumb_category_get(client->ethumb);
1731 }
1732
1733 /**
1734  * Set the video time (duration) in seconds.
1735  *
1736  * @param client the client instance to use. Must @b not be @c
1737  *        NULL. May be pending connected (can be called before @c
1738  *        connected_cb)
1739  * @param time duration (in seconds). Defaults to 3 seconds.
1740  */
1741 EAPI void
1742 ethumb_client_video_time_set(Ethumb_Client *client, float time)
1743 {
1744    EINA_SAFETY_ON_NULL_RETURN(client);
1745
1746    client->ethumb_dirty = 1;
1747    ethumb_video_time_set(client->ethumb, time);
1748 }
1749
1750 /**
1751  * Set initial video position to start thumbnailing, in percentage.
1752  *
1753  * This is useful to avoid thumbnailing the company/producer logo or
1754  * movie opening.
1755  *
1756  * @param client the client instance to use. Must @b not be @c
1757  *        NULL. May be pending connected (can be called before @c
1758  *        connected_cb)
1759  * @param start initial video positon to thumbnail, in percentage (0.0
1760  *        to 1.0, inclusive). Defaults to 10% (0.1).
1761  */
1762 EAPI void
1763 ethumb_client_video_start_set(Ethumb_Client *client, float start)
1764 {
1765    EINA_SAFETY_ON_NULL_RETURN(client);
1766    EINA_SAFETY_ON_FALSE_RETURN(start >= 0.0);
1767    EINA_SAFETY_ON_FALSE_RETURN(start <= 1.0);
1768
1769    client->ethumb_dirty = 1;
1770    ethumb_video_start_set(client->ethumb, start);
1771 }
1772
1773 /**
1774  * Set the video frame interval, in seconds.
1775  *
1776  * This is useful for animated thumbnail and will define skip time
1777  * before going to the next frame. Note that video backends might not
1778  * be able to precisely skip that amount as it will depend on various
1779  * factors, including video encoding.
1780  *
1781  * Although this seems similar to ethumb_client_video_fps_set(), this
1782  * one is the time that will be used to seek. The math is simple, for
1783  * each new frame the video position will be set to:
1784  * ((video_length * start_time) + (interval * current_frame_number)).
1785  *
1786  * @param client the client instance to use. Must @b not be @c
1787  *        NULL. May be pending connected (can be called before @c
1788  *        connected_cb)
1789  * @param interval time between frames, in seconds. Defaults to 0.05
1790  *        seconds.
1791  */
1792 EAPI void
1793 ethumb_client_video_interval_set(Ethumb_Client *client, float interval)
1794 {
1795    EINA_SAFETY_ON_NULL_RETURN(client);
1796
1797    client->ethumb_dirty = 1;
1798    ethumb_video_interval_set(client->ethumb, interval);
1799 }
1800
1801 /**
1802  * Set the number of frames to thumbnail.
1803  *
1804  * This is useful for animated thumbnail and will define how many
1805  * frames the generated file will have.
1806  *
1807  * @param client the client instance to use. Must @b not be @c
1808  *        NULL. May be pending connected (can be called before @c
1809  *        connected_cb)
1810  * @param ntimes number of times, must be greater than zero.
1811  *        Defaults to 3.
1812  */
1813 EAPI void
1814 ethumb_client_video_ntimes_set(Ethumb_Client *client, unsigned int ntimes)
1815 {
1816    EINA_SAFETY_ON_NULL_RETURN(client);
1817    EINA_SAFETY_ON_FALSE_RETURN(ntimes > 0);
1818
1819    client->ethumb_dirty = 1;
1820    ethumb_video_ntimes_set(client->ethumb, ntimes);
1821 }
1822
1823 /**
1824  * Set the number of frames per second to thumbnail the video.
1825  *
1826  * This configures the number of times per seconds the thumbnail will
1827  * use to create thumbnails.
1828  *
1829  * Although this is similar to ethumb_client_video_interval_set(), it
1830  * is the delay used between calling functions thata generates frames,
1831  * while the other is the time used to skip inside the video.
1832  *
1833  * @param client the client instance to use. Must @b not be @c
1834  *        NULL. May be pending connected (can be called before @c
1835  *        connected_cb)
1836  * @param fps number of frames per second to thumbnail. Must be greater
1837  *        than zero. Defaults to 10.
1838  */
1839 EAPI void
1840 ethumb_client_video_fps_set(Ethumb_Client *client, unsigned int fps)
1841 {
1842    EINA_SAFETY_ON_NULL_RETURN(client);
1843    EINA_SAFETY_ON_FALSE_RETURN(fps > 0);
1844
1845    client->ethumb_dirty = 1;
1846    ethumb_video_fps_set(client->ethumb, fps);
1847 }
1848
1849 /**
1850  * Set the page number to thumbnail in paged documents.
1851  *
1852  * @param client the client instance to use. Must @b not be @c
1853  *        NULL. May be pending connected (can be called before @c
1854  *        connected_cb)
1855  * @param page page number, defaults to 0 (first).
1856  */
1857 EAPI void
1858 ethumb_client_document_page_set(Ethumb_Client *client, unsigned int page)
1859 {
1860    EINA_SAFETY_ON_NULL_RETURN(client);
1861
1862    client->ethumb_dirty = 1;
1863    ethumb_document_page_set(client->ethumb, page);
1864 }
1865
1866 /**
1867  * Set source file to be thumbnailed.
1868  *
1869  * Calling this function has the side effect of resetting values set
1870  * with ethumb_client_thumb_path_set() or auto-generated with
1871  * ethumb_client_thumb_exists().
1872  *
1873  * @param client the client instance to use. Must @b not be @c
1874  *        NULL. May be pending connected (can be called before @c
1875  *        connected_cb)
1876  * @param path the filesystem path to use. May be @c NULL.
1877  * @param key the extra argument/key inside @a path to read image
1878  *        from. This is only used for formats that allow multiple
1879  *        resources in one file, like EET or Edje (group name).
1880  *
1881  * @return @c EINA_TRUE on success, @c EINA_FALSE on failure.
1882  */
1883 EAPI Eina_Bool
1884 ethumb_client_file_set(Ethumb_Client *client, const char *path, const char *key)
1885 {
1886    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1887
1888    return ethumb_file_set(client->ethumb, path, key);
1889 }
1890
1891 /**
1892  * Get values set with ethumb_client_file_get()
1893  *
1894  * @param client the client instance to use. Must @b not be @c
1895  *        NULL. May be pending connected (can be called before @c
1896  *        connected_cb)
1897  * @param path where to return configured path. May be @c NULL.  If
1898  *        not @c NULL, then it will be a pointer to a stringshared
1899  *        instance, but @b no references are added (do it with
1900  *        eina_stringshare_ref())!
1901  * @param key where to return configured key. May be @c NULL.If not @c
1902  *        NULL, then it will be a pointer to a stringshared instance,
1903  *        but @b no references are added (do it with
1904  *        eina_stringshare_ref())!
1905  */
1906 EAPI void
1907 ethumb_client_file_get(Ethumb_Client *client, const char **path, const char **key)
1908 {
1909    if (path) *path = NULL;
1910    if (key) *key = NULL;
1911    EINA_SAFETY_ON_NULL_RETURN(client);
1912
1913    ethumb_file_get(client->ethumb, path, key);
1914 }
1915
1916 /**
1917  * Reset previously set file to @c NULL.
1918  *
1919  * @param client the client instance to use. Must @b not be @c
1920  *        NULL. May be pending connected (can be called before @c
1921  *        connected_cb)
1922  */
1923 EAPI void
1924 ethumb_client_file_free(Ethumb_Client *client)
1925 {
1926    EINA_SAFETY_ON_NULL_RETURN(client);
1927
1928    ethumb_file_free(client->ethumb);
1929 }
1930
1931 /**
1932  * Set a defined path and key to store the thumbnail.
1933  *
1934  * If not explicitly given, the thumbnail path will be auto-generated
1935  * by ethumb_client_thumb_exists() or server using configured
1936  * parameters like size, aspect and category.
1937  *
1938  * Set these to @c NULL to forget previously given values. After
1939  * ethumb_client_file_set() these values will be reset to @c NULL.
1940  *
1941  * @param client the client instance to use. Must @b not be @c
1942  *        NULL. May be pending connected (can be called before @c
1943  *        connected_cb)
1944  * @param path force generated thumbnail to the exact given path. If
1945  *        @c NULL, then reverts back to auto-generation.
1946  * @param key force generated thumbnail to the exact given key. If
1947  *        @c NULL, then reverts back to auto-generation.
1948  */
1949 EAPI void
1950 ethumb_client_thumb_path_set(Ethumb_Client *client, const char *path, const char *key)
1951 {
1952    EINA_SAFETY_ON_NULL_RETURN(client);
1953
1954    ethumb_thumb_path_set(client->ethumb, path, key);
1955 }
1956
1957 /**
1958  * Get the configured thumbnail path.
1959  *
1960  * This returns the value set with ethumb_client_thumb_path_set() or
1961  * auto-generated by ethumb_client_thumb_exists() if it was not set.
1962  *
1963  * @param client the client instance to use. Must @b not be @c
1964  *        NULL. May be pending connected (can be called before @c
1965  *        connected_cb)
1966  * @param path where to return configured path. May be @c NULL.  If
1967  *        there was no path configured with
1968  *        ethumb_client_thumb_path_set() and
1969  *        ethumb_client_thumb_exists() was not called, then it will
1970  *        probably return @c NULL. If not @c NULL, then it will be a
1971  *        pointer to a stringshared instance, but @b no references are
1972  *        added (do it with eina_stringshare_ref())!
1973  * @param key where to return configured key. May be @c NULL.  If
1974  *        there was no key configured with
1975  *        ethumb_client_thumb_key_set() and
1976  *        ethumb_client_thumb_exists() was not called, then it will
1977  *        probably return @c NULL. If not @c NULL, then it will be a
1978  *        pointer to a stringshared instance, but @b no references are
1979  *        added (do it with eina_stringshare_ref())!
1980  */
1981 EAPI void
1982 ethumb_client_thumb_path_get(Ethumb_Client *client, const char **path, const char **key)
1983 {
1984    if (path) *path = NULL;
1985    if (key) *key = NULL;
1986    EINA_SAFETY_ON_NULL_RETURN(client);
1987
1988    ethumb_thumb_path_get(client->ethumb, path, key);
1989 }
1990
1991 /**
1992  * Checks whenever file already exists (locally!)
1993  *
1994  * This will check locally (not calling server) if thumbnail already
1995  * exists or not, also calculating the thumbnail path. See
1996  * ethumb_client_thumb_path_get(). Path must be configured with
1997  * ethumb_client_file_set() before using it and the last set file will
1998  * be used!
1999  *
2000  * @param client client instance. Must @b not be @c NULL and client
2001  *        must be configured with ethumb_client_file_set().
2002  *
2003  * @return @c EINA_TRUE if it exists, @c EINA_FALSE otherwise.
2004  */
2005 EAPI Eina_Bool
2006 ethumb_client_thumb_exists(Ethumb_Client *client)
2007 {
2008    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
2009
2010    return ethumb_exists(client->ethumb);
2011 }
2012
2013 /**
2014  * Ask server to generate thumbnail.
2015  *
2016  * This process is asynchronous and will report back from main loop
2017  * using @a generated_cb. One can cancel this request by calling
2018  * ethumb_client_generate_cancel() or
2019  * ethumb_client_generate_cancel_all(), but not that request might be
2020  * processed by server already and no generated files will be removed
2021  * if that is the case.
2022  *
2023  * This will not check if file already exists, this should be done by
2024  * explicitly calling ethumb_client_thumb_exists(). That is, this
2025  * function will override any existing thumbnail.
2026  *
2027  * @param client client instance. Must @b not be @c NULL and client
2028  *        must be connected (after connected_cb is called).
2029  * @param generated_cb function to report generation results.
2030  * @param data context argument to give back to @a generated_cb. May
2031  *        be @c NULL.
2032  * @param data context to give back to @a generate_cb. May be @c
2033  *        NULL.
2034  * @param free_data used to release @a data resources after @a
2035  *        generated_cb is called or user calls
2036  *        ethumb_client_disconnect().
2037  *
2038  * @return identifier or -1 on error. If -1 is returned (error) then
2039  *         @a free_data is @b not called!
2040  *
2041  * @see ethumb_client_connect()
2042  * @see ethumb_client_file_set()
2043  * @see ethumb_client_thumb_exists()
2044  * @see ethumb_client_generate_cancel()
2045  * @see ethumb_client_generate_cancel_all()
2046  */
2047 EAPI int
2048 ethumb_client_generate(Ethumb_Client *client, Ethumb_Client_Generate_Cb generated_cb, const void *data, Eina_Free_Cb free_data)
2049 {
2050    const char *file, *key, *thumb, *thumb_key;
2051    int id;
2052    EINA_SAFETY_ON_NULL_RETURN_VAL(client, -1);
2053    EINA_SAFETY_ON_FALSE_RETURN_VAL(client->connected, -1);
2054
2055    ethumb_file_get(client->ethumb, &file, &key);
2056    if (!file)
2057      {
2058         ERR("no file set.");
2059         return -1;
2060      }
2061
2062    ethumb_thumb_path_get(client->ethumb, &thumb, &thumb_key);
2063
2064    if (client->ethumb_dirty)
2065      ethumb_client_ethumb_setup(client);
2066    id = _ethumb_client_queue_add(client, file, key, thumb, thumb_key,
2067                                  generated_cb, data, free_data);
2068
2069    return id;
2070 }