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