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