make clang happy
[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 = NULL;
994    const char *thumb_key = NULL;
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    if (thumb) eina_stringshare_del(thumb);
1058    if (thumb_key) 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         break;
1258      }
1259
1260 end:
1261    dbus_message_unref(msg);
1262 }
1263
1264 /**
1265  * @cond LOCAL
1266  */
1267 static void
1268 _ethumb_client_queue_clear_cb(void *data, DBusMessage *msg __UNUSED__, DBusError *error __UNUSED__)
1269 {
1270    Ethumb_Client *client = data;
1271
1272    client->pending_clear = NULL;
1273 }
1274 /**
1275  * @endcond
1276  */
1277
1278 /**
1279  * Ask server to cancel generation of all thumbnails.
1280  *
1281  * @param client client instance. Must @b not be @c NULL and client
1282  *        must be connected (after connected_cb is called).
1283  *
1284  * @see ethumb_client_generate_cancel()
1285  */
1286 EAPI void
1287 ethumb_client_generate_cancel_all(Ethumb_Client *client)
1288 {
1289    DBusMessage *msg;
1290    void *data;
1291    EINA_SAFETY_ON_NULL_RETURN(client);
1292
1293    if (client->pending_clear)
1294      return;
1295
1296    EINA_LIST_FREE(client->pending_add, data)
1297      {
1298         struct _ethumb_pending_add *pending = data;
1299         eina_stringshare_del(pending->file);
1300         eina_stringshare_del(pending->key);
1301         eina_stringshare_del(pending->thumb);
1302         eina_stringshare_del(pending->thumb_key);
1303         dbus_pending_call_cancel(pending->pending_call);
1304         dbus_pending_call_unref(pending->pending_call);
1305         if (pending->free_data)
1306           pending->free_data(pending->data);
1307         free(pending);
1308      }
1309
1310    EINA_LIST_FREE(client->pending_gen, data)
1311      {
1312         struct _ethumb_pending_gen *pending = data;
1313         eina_stringshare_del(pending->file);
1314         eina_stringshare_del(pending->key);
1315         eina_stringshare_del(pending->thumb);
1316         eina_stringshare_del(pending->thumb_key);
1317         if (pending->free_data)
1318           pending->free_data(pending->data);
1319         free(pending);
1320      }
1321
1322    msg = dbus_message_new_method_call(_ethumb_dbus_bus_name,
1323                                       client->object_path,
1324                                       _ethumb_dbus_objects_interface,
1325                                       "queue_clear");
1326
1327    client->pending_clear = e_dbus_message_send(client->conn, msg,
1328                                                _ethumb_client_queue_clear_cb,
1329                                                -1, client);
1330
1331    dbus_message_unref(msg);
1332 }
1333
1334 /**
1335  * Configure future requests to use FreeDesktop.Org preset.
1336  *
1337  * This is a preset to provide freedesktop.org (fdo) standard
1338  * compliant thumbnails. That is, files are stored as JPEG under
1339  * ~/.thumbnails/SIZE, with size being either normal (128x128) or
1340  * large (256x256).
1341  *
1342  * @param client the client instance to use. Must @b not be @c
1343  *        NULL. May be pending connected (can be called before @c
1344  *        connected_cb)
1345  * @param s size identifier, either #ETHUMB_THUMB_NORMAL (0) or
1346  *        #ETHUMB_THUMB_LARGE (1).
1347  *
1348  * @see ethumb_client_size_set()
1349  * @see ethumb_client_aspect_set()
1350  * @see ethumb_client_crop_align_set()
1351  * @see ethumb_client_category_set()
1352  * @see ethumb_client_dir_path_set()
1353  */
1354 EAPI void
1355 ethumb_client_fdo_set(Ethumb_Client *client, Ethumb_Thumb_FDO_Size s)
1356 {
1357    EINA_SAFETY_ON_NULL_RETURN(client);
1358
1359    client->ethumb_dirty = 1;
1360    ethumb_thumb_fdo_set(client->ethumb, s);
1361 }
1362
1363 /**
1364  * Configure future request to use custom size.
1365  *
1366  * @param client the client instance to use. Must @b not be @c
1367  *        NULL. May be pending connected (can be called before @c
1368  *        connected_cb)
1369  * @param tw width, default is 128.
1370  * @param th height, default is 128.
1371  */
1372 EAPI void
1373 ethumb_client_size_set(Ethumb_Client *client, int tw, int th)
1374 {
1375    EINA_SAFETY_ON_NULL_RETURN(client);
1376
1377    client->ethumb_dirty = 1;
1378    ethumb_thumb_size_set(client->ethumb, tw, th);
1379 }
1380
1381 /**
1382  * Retrieve future request to use custom size.
1383  *
1384  * @param client the client instance to use. Must @b not be @c
1385  *        NULL. May be pending connected (can be called before @c
1386  *        connected_cb)
1387  * @param tw where to return width. May be @c NULL.
1388  * @param th where to return height. May be @c NULL.
1389  */
1390 EAPI void
1391 ethumb_client_size_get(const Ethumb_Client *client, int *tw, int *th)
1392 {
1393    if (tw) *tw = 0;
1394    if (th) *th = 0;
1395    EINA_SAFETY_ON_NULL_RETURN(client);
1396
1397    ethumb_thumb_size_get(client->ethumb, tw, th);
1398 }
1399
1400 /**
1401  * Configure format to use for future requests.
1402  *
1403  * @param client the client instance to use. Must @b not be @c
1404  *        NULL. May be pending connected (can be called before @c
1405  *        connected_cb)
1406  * @param f format identifier to use, either #ETHUMB_THUMB_FDO (0),
1407  *        #ETHUMB_THUMB_JPEG (1) or #ETHUMB_THUMB_EET (2). Default is FDO.
1408  */
1409 EAPI void
1410 ethumb_client_format_set(Ethumb_Client *client, Ethumb_Thumb_Format f)
1411 {
1412    EINA_SAFETY_ON_NULL_RETURN(client);
1413
1414    client->ethumb_dirty = 1;
1415    ethumb_thumb_format_set(client->ethumb, f);
1416 }
1417
1418 /**
1419  * Retrieve format to use for future requests.
1420  *
1421  * @param client the client instance to use. Must @b not be @c
1422  *        NULL. May be pending connected (can be called before @c
1423  *        connected_cb)
1424  *
1425  * @return format identifier to use, either #ETHUMB_THUMB_FDO (0),
1426  *         #ETHUMB_THUMB_JPEG (1) or #ETHUMB_THUMB_EET (2).
1427  */
1428 EAPI Ethumb_Thumb_Format
1429 ethumb_client_format_get(const Ethumb_Client *client)
1430 {
1431    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1432
1433    return ethumb_thumb_format_get(client->ethumb);
1434 }
1435
1436 /**
1437  * Configure aspect mode to use.
1438  *
1439  * If aspect is kept (#ETHUMB_THUMB_KEEP_ASPECT), then image will be
1440  * rescaled so the largest dimension is not bigger than it's specified
1441  * size (see ethumb_client_size_get()) and the other dimension is
1442  * resized in the same proportion. Example: size is 256x256, image is
1443  * 1000x500, resulting thumbnail is 256x128.
1444  *
1445  * If aspect is ignored (#ETHUMB_THUMB_IGNORE_ASPECT), then image will
1446  * be distorted to match required thumbnail size. Example: size is
1447  * 256x256, image is 1000x500, resulting thumbnail is 256x256.
1448  *
1449  * If crop is required (#ETHUMB_THUMB_CROP), then image will be
1450  * cropped so the smallest dimension is not bigger than its specified
1451  * size (see ethumb_client_size_get()) and the other dimension will
1452  * overflow, not being visible in the final image. How it will
1453  * overflow is speficied by ethumb_client_crop_align_set()
1454  * alignment. Example: size is 256x256, image is 1000x500, crop
1455  * alignment is 0.5, 0.5, resulting thumbnail is 256x256 with 250
1456  * pixels from left and 250 pixels from right being lost, that is just
1457  * the 500x500 central pixels of image will be considered for scaling.
1458  *
1459  * @param client the client instance to use. Must @b not be @c
1460  *        NULL. May be pending connected (can be called before @c
1461  *        connected_cb)
1462  * @param a aspect mode identifier, either #ETHUMB_THUMB_KEEP_ASPECT (0),
1463  *        #ETHUMB_THUMB_IGNORE_ASPECT (1) or #ETHUMB_THUMB_CROP (2).
1464  */
1465 EAPI void
1466 ethumb_client_aspect_set(Ethumb_Client *client, Ethumb_Thumb_Aspect a)
1467 {
1468    EINA_SAFETY_ON_NULL_RETURN(client);
1469
1470    client->ethumb_dirty = 1;
1471    ethumb_thumb_aspect_set(client->ethumb, a);
1472 }
1473
1474 /**
1475  * Get current aspect in use for requests.
1476  *
1477  * @param client the client instance to use. Must @b not be @c
1478  *        NULL. May be pending connected (can be called before @c
1479  *        connected_cb)
1480  *
1481  * @return aspect in use for future requests.
1482  */
1483 EAPI Ethumb_Thumb_Aspect
1484 ethumb_client_aspect_get(const Ethumb_Client *client)
1485 {
1486    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1487
1488    return ethumb_thumb_aspect_get(client->ethumb);
1489 }
1490
1491 /**
1492  * Configure crop alignment in use for future requests.
1493  *
1494  * @param client the client instance to use. Must @b not be @c
1495  *        NULL. May be pending connected (can be called before @c
1496  *        connected_cb)
1497  * @param x horizontal alignment. 0.0 means left side will be visible
1498  *        or right side is being lost. 1.0 means right side will be
1499  *        visible or left side is being lost. 0.5 means just center is
1500  *        visible, both sides will be lost.  Default is 0.5.
1501  * @param y vertical alignment. 0.0 is top visible, 1.0 is bottom
1502  *        visible, 0.5 is center visible. Default is 0.5
1503  */
1504 EAPI void
1505 ethumb_client_crop_align_set(Ethumb_Client *client, float x, float y)
1506 {
1507    EINA_SAFETY_ON_NULL_RETURN(client);
1508
1509    client->ethumb_dirty = 1;
1510    ethumb_thumb_crop_align_set(client->ethumb, x, y);
1511 }
1512
1513 /**
1514  * Get current crop alignment in use for requests.
1515  *
1516  * @param client the client instance to use. Must @b not be @c
1517  *        NULL. May be pending connected (can be called before @c
1518  *        connected_cb)
1519  * @param x where to return horizontal alignment. May be @c NULL.
1520  * @param y where to return vertical alignment. May be @c NULL.
1521  */
1522 EAPI void
1523 ethumb_client_crop_align_get(const Ethumb_Client *client, float *x, float *y)
1524 {
1525    if (x) *x = 0.0;
1526    if (y) *y = 0.0;
1527    EINA_SAFETY_ON_NULL_RETURN(client);
1528
1529    ethumb_thumb_crop_align_get(client->ethumb, x, y);
1530 }
1531
1532 /**
1533  * Configure quality to be used in thumbnails.
1534  *
1535  * @param client the client instance to use. Must @b not be @c
1536  *        NULL. May be pending connected (can be called before @c
1537  *        connected_cb)
1538  * @param quality value from 0 to 100, default is 80. The effect
1539  *        depends on the format being used, PNG will not use it.
1540  */
1541 EAPI void
1542 ethumb_client_quality_set(Ethumb_Client *client, int quality)
1543 {
1544    EINA_SAFETY_ON_NULL_RETURN(client);
1545
1546    ethumb_thumb_quality_set(client->ethumb, quality);
1547 }
1548
1549 /**
1550  * Get quality to be used in thumbnails.
1551  *
1552  * @param client the client instance to use. Must @b not be @c
1553  *        NULL. May be pending connected (can be called before @c
1554  *        connected_cb)
1555  *
1556  * @return quality value from 0 to 100, default is 80. The effect
1557  *         depends on the format being used, PNG will not use it.
1558  */
1559 EAPI int
1560 ethumb_client_quality_get(const Ethumb_Client *client)
1561 {
1562    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1563
1564    return ethumb_thumb_quality_get(client->ethumb);
1565 }
1566
1567 /**
1568  * Configure compression level used in requests.
1569  *
1570  * @param client the client instance to use. Must @b not be @c
1571  *        NULL. May be pending connected (can be called before @c
1572  *        connected_cb)
1573  * @param compress value from 0 to 9, default is 9. The effect
1574  *        depends on the format being used, JPEG will not use it.
1575  */
1576 EAPI void
1577 ethumb_client_compress_set(Ethumb_Client *client, int compress)
1578 {
1579    EINA_SAFETY_ON_NULL_RETURN(client);
1580
1581    ethumb_thumb_compress_set(client->ethumb, compress);
1582 }
1583
1584 /**
1585  * Get compression level used in requests.
1586  *
1587  * @param client the client instance to use. Must @b not be @c
1588  *        NULL. May be pending connected (can be called before @c
1589  *        connected_cb)
1590  *
1591  * @return compress value from 0 to 9, default is 9. The effect
1592  *         depends on the format being used, JPEG will not use it.
1593  */
1594 EAPI int
1595 ethumb_client_compress_get(const Ethumb_Client *client)
1596 {
1597    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1598
1599    return ethumb_thumb_compress_get(client->ethumb);
1600 }
1601
1602 /**
1603  * Set frame to apply to future thumbnails.
1604  *
1605  * This will create an edje object that will have image swallowed
1606  * in. This can be used to simulate Polaroid or wood frames in the
1607  * generated image. Remeber it is bad to modify the original contents
1608  * of thumbnails, but sometimes it's useful to have it composited and
1609  * avoid runtime overhead.
1610  *
1611  * @param client the client instance to use. Must @b not be @c
1612  *        NULL. May be pending connected (can be called before @c
1613  *        connected_cb)
1614  * @param file file path to edje.
1615  * @param group group inside edje to use.
1616  * @param swallow name of swallow part.
1617  */
1618 EAPI Eina_Bool
1619 ethumb_client_frame_set(Ethumb_Client *client, const char *file, const char *group, const char *swallow)
1620 {
1621    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1622
1623    client->ethumb_dirty = 1;
1624    return ethumb_frame_set(client->ethumb, file, group, swallow);
1625 }
1626
1627 /**
1628  * Configure where to store thumbnails in future requests.
1629  *
1630  * This value will be used to generate thumbnail paths, that is, it
1631  * will be used when ethumb_client_thumb_path_set() was not called
1632  * after last ethumb_client_file_set().
1633  *
1634  * Note that this is the base, a category is added to this path as a
1635  * sub directory. This is not the final directory where files are
1636  * stored, the thumbnail system will account @b category as well, see
1637  * ethumb_client_category_set().
1638  *
1639  * As other options, this value will only be applied to future
1640  * requests.
1641  *
1642  * @param client the client instance to use. Must @b not be @c
1643  *        NULL. May be pending connected (can be called before @c
1644  *        connected_cb)
1645  * @param path base directory where to store thumbnails. Default is
1646  *        ~/.thumbnails
1647  *
1648  * @see ethumb_client_category_set()
1649  */
1650 EAPI void
1651 ethumb_client_dir_path_set(Ethumb_Client *client, const char *path)
1652 {
1653    EINA_SAFETY_ON_NULL_RETURN(client);
1654
1655    client->ethumb_dirty = 1;
1656    ethumb_thumb_dir_path_set(client->ethumb, path);
1657 }
1658
1659 /**
1660  * Get base directory path where to store thumbnails.
1661  *
1662  * @param client the client instance to use. Must @b not be @c
1663  *        NULL. May be pending connected (can be called before @c
1664  *        connected_cb)
1665  *
1666  * @return pointer to internal string with current path. This string
1667  *         should not be modified or freed.
1668  *
1669  * @see ethumb_client_dir_path_set()
1670  */
1671 EAPI const char *
1672 ethumb_client_dir_path_get(const Ethumb_Client *client)
1673 {
1674    EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
1675
1676    return ethumb_thumb_dir_path_get(client->ethumb);
1677 }
1678
1679 /**
1680  * Category directory to store thumbnails.
1681  *
1682  * This value will be used to generate thumbnail paths, that is, it
1683  * will be used when ethumb_client_thumb_path_set() was not called
1684  * after last ethumb_client_file_set().
1685  *
1686  * This is a sub-directory inside base directory
1687  * (ethumb_client_dir_path_set()) that creates a namespace to avoid
1688  * different options resulting in the same file.
1689  *
1690  * As other options, this value will only be applied to future
1691  * requests.
1692  *
1693  * @param client the client instance to use. Must @b not be @c
1694  *        NULL. May be pending connected (can be called before @c
1695  *        connected_cb)
1696  * @param category category sub directory to store thumbnail. Default
1697  *        is either "normal" or "large" for FDO compliant thumbnails
1698  *        or WIDTHxHEIGHT-ASPECT[-FRAMED]-FORMAT. It can be a string
1699  *        or @c NULL to use auto generated names.
1700  *
1701  * @see ethumb_client_dir_path_set()
1702  */
1703 EAPI void
1704 ethumb_client_category_set(Ethumb_Client *client, const char *category)
1705 {
1706    EINA_SAFETY_ON_NULL_RETURN(client);
1707
1708    client->ethumb_dirty = 1;
1709    ethumb_thumb_category_set(client->ethumb, category);
1710 }
1711
1712 /**
1713  * Get category sub-directory  where to store thumbnails.
1714  *
1715  * @param client the client instance to use. Must @b not be @c
1716  *        NULL. May be pending connected (can be called before @c
1717  *        connected_cb)
1718  *
1719  * @return pointer to internal string with current path. This string
1720  *         should not be modified or freed.
1721  *
1722  * @see ethumb_client_category_set()
1723  */
1724 EAPI const char *
1725 ethumb_client_category_get(const Ethumb_Client *client)
1726 {
1727    EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
1728
1729    return ethumb_thumb_category_get(client->ethumb);
1730 }
1731
1732 /**
1733  * Set the video time (duration) in seconds.
1734  *
1735  * @param client the client instance to use. Must @b not be @c
1736  *        NULL. May be pending connected (can be called before @c
1737  *        connected_cb)
1738  * @param time duration (in seconds). Defaults to 3 seconds.
1739  */
1740 EAPI void
1741 ethumb_client_video_time_set(Ethumb_Client *client, float time)
1742 {
1743    EINA_SAFETY_ON_NULL_RETURN(client);
1744
1745    client->ethumb_dirty = 1;
1746    ethumb_video_time_set(client->ethumb, time);
1747 }
1748
1749 /**
1750  * Set initial video position to start thumbnailing, in percentage.
1751  *
1752  * This is useful to avoid thumbnailing the company/producer logo or
1753  * movie opening.
1754  *
1755  * @param client the client instance to use. Must @b not be @c
1756  *        NULL. May be pending connected (can be called before @c
1757  *        connected_cb)
1758  * @param start initial video positon to thumbnail, in percentage (0.0
1759  *        to 1.0, inclusive). Defaults to 10% (0.1).
1760  */
1761 EAPI void
1762 ethumb_client_video_start_set(Ethumb_Client *client, float start)
1763 {
1764    EINA_SAFETY_ON_NULL_RETURN(client);
1765    EINA_SAFETY_ON_FALSE_RETURN(start >= 0.0);
1766    EINA_SAFETY_ON_FALSE_RETURN(start <= 1.0);
1767
1768    client->ethumb_dirty = 1;
1769    ethumb_video_start_set(client->ethumb, start);
1770 }
1771
1772 /**
1773  * Set the video frame interval, in seconds.
1774  *
1775  * This is useful for animated thumbnail and will define skip time
1776  * before going to the next frame. Note that video backends might not
1777  * be able to precisely skip that amount as it will depend on various
1778  * factors, including video encoding.
1779  *
1780  * Although this seems similar to ethumb_client_video_fps_set(), this
1781  * one is the time that will be used to seek. The math is simple, for
1782  * each new frame the video position will be set to:
1783  * ((video_length * start_time) + (interval * current_frame_number)).
1784  *
1785  * @param client the client instance to use. Must @b not be @c
1786  *        NULL. May be pending connected (can be called before @c
1787  *        connected_cb)
1788  * @param interval time between frames, in seconds. Defaults to 0.05
1789  *        seconds.
1790  */
1791 EAPI void
1792 ethumb_client_video_interval_set(Ethumb_Client *client, float interval)
1793 {
1794    EINA_SAFETY_ON_NULL_RETURN(client);
1795
1796    client->ethumb_dirty = 1;
1797    ethumb_video_interval_set(client->ethumb, interval);
1798 }
1799
1800 /**
1801  * Set the number of frames to thumbnail.
1802  *
1803  * This is useful for animated thumbnail and will define how many
1804  * frames the generated file will have.
1805  *
1806  * @param client the client instance to use. Must @b not be @c
1807  *        NULL. May be pending connected (can be called before @c
1808  *        connected_cb)
1809  * @param ntimes number of times, must be greater than zero.
1810  *        Defaults to 3.
1811  */
1812 EAPI void
1813 ethumb_client_video_ntimes_set(Ethumb_Client *client, unsigned int ntimes)
1814 {
1815    EINA_SAFETY_ON_NULL_RETURN(client);
1816    EINA_SAFETY_ON_FALSE_RETURN(ntimes > 0);
1817
1818    client->ethumb_dirty = 1;
1819    ethumb_video_ntimes_set(client->ethumb, ntimes);
1820 }
1821
1822 /**
1823  * Set the number of frames per second to thumbnail the video.
1824  *
1825  * This configures the number of times per seconds the thumbnail will
1826  * use to create thumbnails.
1827  *
1828  * Although this is similar to ethumb_client_video_interval_set(), it
1829  * is the delay used between calling functions thata generates frames,
1830  * while the other is the time used to skip inside the video.
1831  *
1832  * @param client the client instance to use. Must @b not be @c
1833  *        NULL. May be pending connected (can be called before @c
1834  *        connected_cb)
1835  * @param fps number of frames per second to thumbnail. Must be greater
1836  *        than zero. Defaults to 10.
1837  */
1838 EAPI void
1839 ethumb_client_video_fps_set(Ethumb_Client *client, unsigned int fps)
1840 {
1841    EINA_SAFETY_ON_NULL_RETURN(client);
1842    EINA_SAFETY_ON_FALSE_RETURN(fps > 0);
1843
1844    client->ethumb_dirty = 1;
1845    ethumb_video_fps_set(client->ethumb, fps);
1846 }
1847
1848 /**
1849  * Set the page number to thumbnail in paged documents.
1850  *
1851  * @param client the client instance to use. Must @b not be @c
1852  *        NULL. May be pending connected (can be called before @c
1853  *        connected_cb)
1854  * @param page page number, defaults to 0 (first).
1855  */
1856 EAPI void
1857 ethumb_client_document_page_set(Ethumb_Client *client, unsigned int page)
1858 {
1859    EINA_SAFETY_ON_NULL_RETURN(client);
1860
1861    client->ethumb_dirty = 1;
1862    ethumb_document_page_set(client->ethumb, page);
1863 }
1864
1865 /**
1866  * Set source file to be thumbnailed.
1867  *
1868  * Calling this function has the side effect of resetting values set
1869  * with ethumb_client_thumb_path_set() or auto-generated with
1870  * ethumb_client_thumb_exists().
1871  *
1872  * @param client the client instance to use. Must @b not be @c
1873  *        NULL. May be pending connected (can be called before @c
1874  *        connected_cb)
1875  * @param path the filesystem path to use. May be @c NULL.
1876  * @param key the extra argument/key inside @a path to read image
1877  *        from. This is only used for formats that allow multiple
1878  *        resources in one file, like EET or Edje (group name).
1879  *
1880  * @return @c EINA_TRUE on success, @c EINA_FALSE on failure.
1881  */
1882 EAPI Eina_Bool
1883 ethumb_client_file_set(Ethumb_Client *client, const char *path, const char *key)
1884 {
1885    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1886
1887    return ethumb_file_set(client->ethumb, path, key);
1888 }
1889
1890 /**
1891  * Get values set with ethumb_client_file_get()
1892  *
1893  * @param client the client instance to use. Must @b not be @c
1894  *        NULL. May be pending connected (can be called before @c
1895  *        connected_cb)
1896  * @param path where to return configured path. May be @c NULL.  If
1897  *        not @c NULL, then it will be a pointer to a stringshared
1898  *        instance, but @b no references are added (do it with
1899  *        eina_stringshare_ref())!
1900  * @param key where to return configured key. May be @c NULL.If not @c
1901  *        NULL, then it will be a pointer to a stringshared instance,
1902  *        but @b no references are added (do it with
1903  *        eina_stringshare_ref())!
1904  */
1905 EAPI void
1906 ethumb_client_file_get(Ethumb_Client *client, const char **path, const char **key)
1907 {
1908    if (path) *path = NULL;
1909    if (key) *key = NULL;
1910    EINA_SAFETY_ON_NULL_RETURN(client);
1911
1912    ethumb_file_get(client->ethumb, path, key);
1913 }
1914
1915 /**
1916  * Reset previously set file to @c NULL.
1917  *
1918  * @param client the client instance to use. Must @b not be @c
1919  *        NULL. May be pending connected (can be called before @c
1920  *        connected_cb)
1921  */
1922 EAPI void
1923 ethumb_client_file_free(Ethumb_Client *client)
1924 {
1925    EINA_SAFETY_ON_NULL_RETURN(client);
1926
1927    ethumb_file_free(client->ethumb);
1928 }
1929
1930 /**
1931  * Set a defined path and key to store the thumbnail.
1932  *
1933  * If not explicitly given, the thumbnail path will be auto-generated
1934  * by ethumb_client_thumb_exists() or server using configured
1935  * parameters like size, aspect and category.
1936  *
1937  * Set these to @c NULL to forget previously given values. After
1938  * ethumb_client_file_set() these values will be reset to @c NULL.
1939  *
1940  * @param client the client instance to use. Must @b not be @c
1941  *        NULL. May be pending connected (can be called before @c
1942  *        connected_cb)
1943  * @param path force generated thumbnail to the exact given path. If
1944  *        @c NULL, then reverts back to auto-generation.
1945  * @param key force generated thumbnail to the exact given key. If
1946  *        @c NULL, then reverts back to auto-generation.
1947  */
1948 EAPI void
1949 ethumb_client_thumb_path_set(Ethumb_Client *client, const char *path, const char *key)
1950 {
1951    EINA_SAFETY_ON_NULL_RETURN(client);
1952
1953    ethumb_thumb_path_set(client->ethumb, path, key);
1954 }
1955
1956 /**
1957  * Get the configured thumbnail path.
1958  *
1959  * This returns the value set with ethumb_client_thumb_path_set() or
1960  * auto-generated by ethumb_client_thumb_exists() if it was not set.
1961  *
1962  * @param client the client instance to use. Must @b not be @c
1963  *        NULL. May be pending connected (can be called before @c
1964  *        connected_cb)
1965  * @param path where to return configured path. May be @c NULL.  If
1966  *        there was no path configured with
1967  *        ethumb_client_thumb_path_set() and
1968  *        ethumb_client_thumb_exists() was not called, then it will
1969  *        probably return @c NULL. If not @c NULL, then it will be a
1970  *        pointer to a stringshared instance, but @b no references are
1971  *        added (do it with eina_stringshare_ref())!
1972  * @param key where to return configured key. May be @c NULL.  If
1973  *        there was no key configured with
1974  *        ethumb_client_thumb_key_set() and
1975  *        ethumb_client_thumb_exists() was not called, then it will
1976  *        probably return @c NULL. If not @c NULL, then it will be a
1977  *        pointer to a stringshared instance, but @b no references are
1978  *        added (do it with eina_stringshare_ref())!
1979  */
1980 EAPI void
1981 ethumb_client_thumb_path_get(Ethumb_Client *client, const char **path, const char **key)
1982 {
1983    if (path) *path = NULL;
1984    if (key) *key = NULL;
1985    EINA_SAFETY_ON_NULL_RETURN(client);
1986
1987    ethumb_thumb_path_get(client->ethumb, path, key);
1988 }
1989
1990 /**
1991  * Checks whenever file already exists (locally!)
1992  *
1993  * This will check locally (not calling server) if thumbnail already
1994  * exists or not, also calculating the thumbnail path. See
1995  * ethumb_client_thumb_path_get(). Path must be configured with
1996  * ethumb_client_file_set() before using it and the last set file will
1997  * be used!
1998  *
1999  * @param client client instance. Must @b not be @c NULL and client
2000  *        must be configured with ethumb_client_file_set().
2001  *
2002  * @return @c EINA_TRUE if it exists, @c EINA_FALSE otherwise.
2003  */
2004 EAPI Eina_Bool
2005 ethumb_client_thumb_exists(Ethumb_Client *client)
2006 {
2007    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
2008
2009    return ethumb_exists(client->ethumb);
2010 }
2011
2012 /**
2013  * Ask server to generate thumbnail.
2014  *
2015  * This process is asynchronous and will report back from main loop
2016  * using @a generated_cb. One can cancel this request by calling
2017  * ethumb_client_generate_cancel() or
2018  * ethumb_client_generate_cancel_all(), but not that request might be
2019  * processed by server already and no generated files will be removed
2020  * if that is the case.
2021  *
2022  * This will not check if file already exists, this should be done by
2023  * explicitly calling ethumb_client_thumb_exists(). That is, this
2024  * function will override any existing thumbnail.
2025  *
2026  * @param client client instance. Must @b not be @c NULL and client
2027  *        must be connected (after connected_cb is called).
2028  * @param generated_cb function to report generation results.
2029  * @param data context argument to give back to @a generated_cb. May
2030  *        be @c NULL.
2031  * @param data context to give back to @a generate_cb. May be @c
2032  *        NULL.
2033  * @param free_data used to release @a data resources after @a
2034  *        generated_cb is called or user calls
2035  *        ethumb_client_disconnect().
2036  *
2037  * @return identifier or -1 on error. If -1 is returned (error) then
2038  *         @a free_data is @b not called!
2039  *
2040  * @see ethumb_client_connect()
2041  * @see ethumb_client_file_set()
2042  * @see ethumb_client_thumb_exists()
2043  * @see ethumb_client_generate_cancel()
2044  * @see ethumb_client_generate_cancel_all()
2045  */
2046 EAPI int
2047 ethumb_client_generate(Ethumb_Client *client, Ethumb_Client_Generate_Cb generated_cb, const void *data, Eina_Free_Cb free_data)
2048 {
2049    const char *file, *key, *thumb, *thumb_key;
2050    int id;
2051    EINA_SAFETY_ON_NULL_RETURN_VAL(client, -1);
2052    EINA_SAFETY_ON_FALSE_RETURN_VAL(client->connected, -1);
2053
2054    ethumb_file_get(client->ethumb, &file, &key);
2055    if (!file)
2056      {
2057         ERR("no file set.");
2058         return -1;
2059      }
2060
2061    ethumb_thumb_path_get(client->ethumb, &thumb, &thumb_key);
2062
2063    if (client->ethumb_dirty)
2064      ethumb_client_ethumb_setup(client);
2065    id = _ethumb_client_queue_add(client, file, key, thumb, thumb_key,
2066                                  generated_cb, data, free_data);
2067
2068    return id;
2069 }