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