ethumb: cleanup API.
[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
172 struct _Ethumb_Async_Exists
173 {
174    const char *path;
175
176    Ethumb *dup; /* We will work on that one to prevent race and lock */
177
178    Eina_List *callbacks;
179    Ecore_Thread *thread;
180 };
181
182 struct _Ethumb_Exists
183 {
184    Ethumb_Async_Exists *parent;
185    Ethumb_Client *client;
186    Ethumb *dup; /* We don't want to loose parameters so keep them around */
187
188    Ethumb_Client_Thumb_Exists_Cb exists_cb;
189    const void *data;
190 };
191
192 static const char _ethumb_dbus_bus_name[] = "org.enlightenment.Ethumb";
193 static const char _ethumb_dbus_interface[] = "org.enlightenment.Ethumb";
194 static const char _ethumb_dbus_objects_interface[] = "org.enlightenment.Ethumb.objects";
195 static const char _ethumb_dbus_path[] = "/org/enlightenment/Ethumb";
196 static const char fdo_interface[] = "org.freedesktop.DBus";
197 static const char fdo_bus_name[] = "org.freedesktop.DBus";
198 static const char fdo_path[] = "/org/freedesktop/DBus";
199
200 static int _initcount = 0;
201 static Eina_Hash *_exists_request = NULL;
202
203 static void _ethumb_client_generated_cb(void *data, DBusMessage *msg);
204 static void _ethumb_client_get_name_owner(void *data, DBusMessage *msg, DBusError *err);
205
206 static inline bool
207 __dbus_callback_check_and_init(const char *file, int line, const char *function, DBusMessage *msg, DBusMessageIter *itr, DBusError *err)
208 {
209    if (!msg)
210      {
211         ERR("%s:%d:%s() callback without message arguments!",
212                 file, line, function);
213
214         if (err)
215           ERR("%s:%d:%s() an error was reported by server: "
216                   "name=\"%s\", message=\"%s\"",
217                   file, line, function, err->name, err->message);
218
219         return 0;
220      }
221
222    if (!dbus_message_iter_init(msg, itr))
223      {
224         ERR("%s:%d:%s() could not init iterator.",
225                 file, line, function);
226         return 0;
227      }
228
229    return 1;
230 }
231
232 #define _dbus_callback_check_and_init(msg, itr, err)                    \
233   __dbus_callback_check_and_init(__FILE__, __LINE__, __FUNCTION__,      \
234                                  msg, itr, err)
235
236 static inline bool
237 __dbus_iter_type_check(int type, int expected, const char *expected_name)
238 {
239    if (type == expected)
240      return 1;
241
242    ERR("expected type %s (%c) but got %c instead!",
243            expected_name, expected, type);
244
245    return 0;
246 }
247 #define _dbus_iter_type_check(t, e) __dbus_iter_type_check(t, e, #e)
248
249 #define CHECK_NULL_RETURN(ptr, ...)                                     \
250   do                                                                    \
251     {                                                                   \
252        if ((ptr) == NULL)                                               \
253          {                                                              \
254             CRITICAL("%s == NULL!", #ptr);                              \
255             return __VA_ARGS__;                                         \
256          }                                                              \
257     }                                                                   \
258   while (0)
259
260 static void
261 _ethumb_client_free(Ethumb_Client *client)
262 {
263    void *data;
264
265    if (!client->connected)
266      goto end_connection;
267
268    EINA_LIST_FREE(client->pending_add, data)
269      {
270         struct _ethumb_pending_add *pending = data;
271         eina_stringshare_del(pending->file);
272         eina_stringshare_del(pending->key);
273         eina_stringshare_del(pending->thumb);
274         eina_stringshare_del(pending->thumb_key);
275         dbus_pending_call_cancel(pending->pending_call);
276         dbus_pending_call_unref(pending->pending_call);
277         if (pending->free_data)
278           pending->free_data(pending->data);
279         free(pending);
280      }
281
282    EINA_LIST_FREE(client->pending_gen, data)
283      {
284         struct _ethumb_pending_gen *pending = data;
285         eina_stringshare_del(pending->file);
286         eina_stringshare_del(pending->key);
287         eina_stringshare_del(pending->thumb);
288         eina_stringshare_del(pending->thumb_key);
289         if (pending->free_data)
290           pending->free_data(pending->data);
291         free(pending);
292      }
293
294    EINA_LIST_FREE(client->pending_remove, data)
295      {
296         struct _ethumb_pending_remove *pending = data;
297         dbus_pending_call_cancel(pending->pending_call);
298         dbus_pending_call_unref(pending->pending_call);
299         if (pending->free_data)
300           pending->free_data(pending->data);
301         free(pending);
302      }
303
304    if (client->pending_clear)
305      {
306         dbus_pending_call_cancel(client->pending_clear);
307         dbus_pending_call_unref(client->pending_clear);
308      }
309
310 end_connection:
311    if (client->object_path)
312      eina_stringshare_del(client->object_path);
313
314    if (client->pending_new)
315      dbus_pending_call_cancel(client->pending_new);
316
317    if (client->unique_name)
318      eina_stringshare_del(client->unique_name);
319
320    if (client->pending_get_name_owner)
321      dbus_pending_call_cancel(client->pending_get_name_owner);
322
323    if (client->pending_start_service_by_name)
324      dbus_pending_call_cancel(client->pending_start_service_by_name);
325
326    if (client->old_ethumb_conf)
327        ethumb_free(client->old_ethumb_conf);
328
329    ethumb_free(client->ethumb);
330
331    e_dbus_signal_handler_del(client->conn, client->name_owner_changed_handler);
332    if (client->connected)
333      e_dbus_signal_handler_del(client->conn, client->generated_signal);
334    e_dbus_connection_close(client->conn);
335
336    if (client->connect.free_data)
337      client->connect.free_data(client->connect.data);
338    if (client->die.free_data)
339      client->die.free_data(client->die.data);
340
341    free(client);
342 }
343
344 static void
345 _ethumb_async_delete(void *data)
346 {
347    Ethumb_Async_Exists *async = data;
348
349    assert(async->callbacks == NULL);
350    assert(async->thread == NULL);
351
352    ethumb_free(async->dup);
353    eina_stringshare_del(async->path);
354
355    free(async);
356 }
357
358 static void
359 _ethumb_client_name_owner_changed(void *data, DBusMessage *msg)
360 {
361    DBusError err;
362    const char *name, *from, *to;
363    Ethumb_Client *client = data;
364
365    dbus_error_init(&err);
366    if (!dbus_message_get_args(msg, &err,
367        DBUS_TYPE_STRING, &name,
368        DBUS_TYPE_STRING, &from,
369        DBUS_TYPE_STRING, &to,
370        DBUS_TYPE_INVALID))
371      {
372         ERR("could not get NameOwnerChanged arguments: %s: %s",
373             err.name, err.message);
374         dbus_error_free(&err);
375         return;
376      }
377
378    if(!from || !name)
379        return ;
380    if (strcmp(name, _ethumb_dbus_bus_name) != 0)
381      return;
382
383    DBG("NameOwnerChanged from=[%s] to=[%s]", from, to);
384
385    if (from[0] != '\0' && to[0] == '\0')
386      {
387         DBG("exit ethumbd at %s", from);
388         if (client->unique_name && strcmp(client->unique_name, from) != 0)
389           WRN("%s was not the known name %s, ignored.",
390                from, client->unique_name);
391         else if(client->unique_name)
392           {
393              ERR("server exit!!!");
394              if (client->die.cb)
395                {
396                   client->die.cb(client->die.data, client);
397                   client->die.cb = NULL;
398                }
399              if (client->die.free_data)
400                {
401                   client->die.free_data(client->die.data);
402                   client->die.free_data = NULL;
403                   client->die.data = NULL;
404                }
405           }
406      }
407    else
408      DBG("unknown change from %s to %s", from, to);
409 }
410
411 static void
412 _ethumb_client_report_connect(Ethumb_Client *client, Eina_Bool success)
413 {
414    if (!client->connect.cb)
415      {
416         ERR("already called?!");
417         return;
418      }
419
420    client->connect.cb(client->connect.data, client, success);
421    if (client->connect.free_data)
422      {
423         client->connect.free_data(client->connect.data);
424         client->connect.free_data = NULL;
425      }
426    client->connect.cb = NULL;
427    client->connect.data = NULL;
428 }
429
430 static void
431 _ethumb_client_new_cb(void *data, DBusMessage *msg, DBusError *error)
432 {
433    DBusMessageIter iter;
434    const char *opath;
435    int t;
436    Ethumb_Client *client = data;
437
438    client->pending_new = NULL;
439
440    if (!_dbus_callback_check_and_init(msg, &iter, error))
441      goto end_error;
442    t = dbus_message_iter_get_arg_type(&iter);
443    if (!_dbus_iter_type_check(t, DBUS_TYPE_OBJECT_PATH))
444      goto end_error;
445
446    dbus_message_iter_get_basic(&iter, &opath);
447    if (opath[0] == '\0')
448      goto end_error;
449
450    client->object_path = eina_stringshare_add(opath);
451
452    client->generated_signal = e_dbus_signal_handler_add(
453       client->conn, _ethumb_dbus_bus_name, opath,
454       _ethumb_dbus_objects_interface, "generated",
455       _ethumb_client_generated_cb, client);
456
457    _ethumb_client_report_connect(client, 1);
458    return;
459
460 end_error:
461    _ethumb_client_report_connect(client, 0);
462 }
463
464 static void
465 _ethumb_client_call_new(Ethumb_Client *client)
466 {
467    DBusMessage *msg;
468
469    msg = dbus_message_new_method_call(_ethumb_dbus_bus_name, _ethumb_dbus_path,
470                                       _ethumb_dbus_interface, "new");
471    client->pending_new = e_dbus_message_send(client->conn, msg,
472                                              _ethumb_client_new_cb, -1,
473                                              client);
474    dbus_message_unref(msg);
475 }
476
477 static void
478 _ethumb_client_start_server_cb(void *data, DBusMessage *msg, DBusError *err)
479 {
480    Ethumb_Client *client = data;
481    DBusMessageIter iter;
482    dbus_uint32_t ret;
483    int t;
484
485    client->pending_start_service_by_name = NULL;
486
487    if (!_dbus_callback_check_and_init(msg, &iter, err))
488      goto error;
489
490    t = dbus_message_iter_get_arg_type(&iter);
491    if (!_dbus_iter_type_check(t, DBUS_TYPE_UINT32))
492      goto error;
493
494    dbus_message_iter_get_basic(&iter, &ret);
495    if ((ret != 1) && (ret != 2))
496      {
497         ERR("Error starting Ethumbd DBus service by its name: retcode %u",
498             ret);
499         goto error;
500      }
501
502    client->server_started = 1;
503    DBG("Ethumbd DBus service started successfully (%d), now request its name",
504        ret);
505
506    if (client->pending_get_name_owner)
507      {
508         DBG("already requesting name owner, cancel and try again");
509         dbus_pending_call_cancel(client->pending_get_name_owner);
510      }
511
512    client->pending_get_name_owner = e_dbus_get_name_owner
513      (client->conn, _ethumb_dbus_bus_name, _ethumb_client_get_name_owner,
514       client);
515    if (!client->pending_get_name_owner)
516      {
517         ERR("could not create a get_name_owner request.");
518         goto error;
519      }
520
521    return;
522
523  error:
524    ERR("failed to start Ethumbd DBus service by its name.");
525    _ethumb_client_report_connect(client, 0);
526 }
527
528 static void
529 _ethumb_client_start_server(Ethumb_Client *client)
530 {
531    if (client->pending_start_service_by_name)
532      {
533         DBG("already pending start service by name.");
534         return;
535      }
536
537    client->server_started = 0;
538    client->pending_start_service_by_name = e_dbus_start_service_by_name
539      (client->conn, _ethumb_dbus_bus_name, 0, _ethumb_client_start_server_cb,
540       client);
541    if (!client->pending_start_service_by_name)
542      {
543         ERR("could not start service by name!");
544         _ethumb_client_report_connect(client, 0);
545      }
546 }
547
548 static void
549 _ethumb_client_get_name_owner(void *data, DBusMessage *msg, DBusError *err)
550 {
551    DBusMessageIter iter;
552    const char *uid;
553    Ethumb_Client *client = data;
554    int t;
555
556    client->pending_get_name_owner = NULL;
557
558    if (dbus_error_is_set(err) && (!client->server_started))
559      {
560         DBG("could not find server (%s), try to start it...", err->message);
561         _ethumb_client_start_server(client);
562         return;
563      }
564
565    if (!_dbus_callback_check_and_init(msg, &iter, err))
566      goto error;
567
568    t = dbus_message_iter_get_arg_type(&iter);
569    if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING))
570      goto error;
571
572    dbus_message_iter_get_basic(&iter, &uid);
573    if (!uid)
574      {
575         ERR("no name owner!");
576         goto error;
577      }
578
579    DBG("unique name = %s", uid);
580    client->unique_name = eina_stringshare_add(uid);
581
582    _ethumb_client_call_new(client);
583    client->connected = 1;
584    return;
585
586 error:
587    _ethumb_client_report_connect(client, 0);
588 }
589
590 static void
591 _ethumb_client_exists_heavy(void *data, Ecore_Thread *thread __UNUSED__)
592 {
593    Ethumb_Async_Exists *async = data;
594
595    ethumb_thumb_hash(async->dup);
596 }
597
598 static void
599 _ethumb_client_exists_end(void *data, Ecore_Thread *thread __UNUSED__)
600 {
601    Ethumb_Async_Exists *async = data;
602    Ethumb_Exists *cb;
603
604    EINA_LIST_FREE(async->callbacks, cb)
605      {
606         Ethumb *tmp;
607
608         ethumb_thumb_hash_copy(cb->dup, async->dup);
609         tmp = cb->client->ethumb;
610         cb->client->ethumb = cb->dup;
611
612         cb->exists_cb(cb->client, cb,
613                       ethumb_exists(cb->client->ethumb),
614                       (void*) cb->data);
615
616         cb->client->ethumb = tmp;
617         EINA_REFCOUNT_UNREF(cb->client)
618           _ethumb_client_free(cb->client);
619         ethumb_free(cb->dup);
620         free(cb);
621      }
622
623    async->thread = NULL;
624
625    eina_hash_del(_exists_request, async->path, async);
626 }
627
628 /**
629  * @endcond
630  */
631
632 /**
633  * @brief Initialize the Ethumb_Client library.
634  *
635  * @return 1 or greater on success, 0 on error.
636  *
637  * This function sets up all the Ethumb_Client module dependencies. It
638  * returns 0 on failure (that is, when one of the dependency fails to
639  * initialize), otherwise it returns the number of times it has
640  * already been called.
641  *
642  * When Ethumb_Client is not used anymore, call
643  * ethumb_client_shutdown() to shut down the Ethumb_Client library.
644  *
645  * @see ethumb_client_shutdown()
646  * @see ethumb_client_connect()
647  * @see @ref tutorial_ethumb_client
648  */
649 EAPI int
650 ethumb_client_init(void)
651 {
652    if (_initcount)
653      return ++_initcount;
654
655    if (!eina_init())
656      {
657         fprintf(stderr, "ERROR: Could not initialize log module.\n");
658         return 0;
659      }
660    _log_dom = eina_log_domain_register("ethumb_client", EINA_COLOR_YELLOW);
661    if (_log_dom < 0)
662      {
663         EINA_LOG_ERR("Could not register log domain: ethumb_client");
664         eina_shutdown();
665         return 0;
666      }
667
668    ethumb_init();
669    e_dbus_init();
670
671    _exists_request = eina_hash_stringshared_new(_ethumb_async_delete);
672
673    return ++_initcount;
674 }
675
676 /**
677  * @brief Shut down the Ethumb_Client library.
678  *
679  * @return 0 when everything is shut down, 1 or greater if there are
680  *         other users of the Ethumb_Client library pending shutdown.
681  *
682  * This function shuts down the Ethumb_Client library. It returns 0
683  * when it has been called the same number of times than
684  * ethumb_client_init(). In that case it shut down all the
685  * Ethumb_Client modules dependencies.
686  *
687  * Once this function succeeds (that is, @c 0 is returned), you must
688  * not call any of the Eina function anymore. You must call
689  * ethumb_client_init() again to use the Ethumb_Client functions
690  * again.
691  */
692 EAPI int
693 ethumb_client_shutdown(void)
694 {
695    _initcount--;
696    if (_initcount > 0)
697      return _initcount;
698
699    /* should find a non racy solution to closing all pending exists request */
700    eina_hash_free(_exists_request);
701    _exists_request = NULL;
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    const char *path = NULL;
2166    Ethumb_Async_Exists *async = NULL;
2167    Ethumb_Exists *cb = NULL;
2168    Ecore_Thread *t;
2169
2170    EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
2171
2172    ethumb_file_get(client->ethumb, &path, NULL);
2173    if (!path) goto on_error;
2174
2175    async = eina_hash_find(_exists_request, path);
2176    if (!async)
2177      {
2178         async = malloc(sizeof (Ethumb_Async_Exists));
2179         if (!async) goto on_error;
2180
2181         async->path = eina_stringshare_ref(path);
2182         async->callbacks = NULL;
2183         async->dup = ethumb_dup(client->ethumb);
2184
2185         if (!async->dup) goto on_error;
2186
2187         cb = malloc(sizeof (Ethumb_Exists));
2188         if (!cb) goto on_error;
2189
2190         EINA_REFCOUNT_REF(client);
2191         cb->client = client;
2192         cb->dup = ethumb_dup(client->ethumb);
2193         cb->exists_cb = exists_cb;
2194         cb->data = data;
2195         cb->parent = async;
2196
2197         async->callbacks = eina_list_append(async->callbacks, cb);
2198
2199         /* spawn a thread here */
2200         t = ecore_thread_run(_ethumb_client_exists_heavy,
2201                              _ethumb_client_exists_end,
2202                              _ethumb_client_exists_end,
2203                              async);
2204         if (!t) return NULL;
2205         async->thread = t;
2206
2207         eina_hash_direct_add(_exists_request, async->path, async);
2208
2209         return cb;
2210      }
2211
2212    cb = malloc(sizeof (Ethumb_Exists));
2213    if (!cb)
2214      {
2215         async = NULL;
2216         goto on_error;
2217      }
2218
2219    EINA_REFCOUNT_REF(client);
2220    cb->client = client;
2221    cb->dup = ethumb_dup(client->ethumb);
2222    cb->exists_cb = exists_cb;
2223    cb->data = data;
2224    cb->parent = async;
2225
2226    async->callbacks = eina_list_append(async->callbacks, cb);
2227
2228    return cb;
2229
2230  on_error:
2231    exists_cb(client, NULL, EINA_FALSE, (void*) data);
2232
2233    if (async)
2234      {
2235         eina_stringshare_del(async->path);
2236         if (async->dup) ethumb_free(async->dup);
2237         free(async);
2238      }
2239    return NULL;
2240 }
2241
2242 /**
2243  * Cancel an ongoing exists request.
2244  *
2245  * @param exists the request to cancel.
2246  */
2247 EAPI void
2248 ethumb_client_thumb_exists_cancel(Ethumb_Exists *exists)
2249 {
2250    Ethumb_Async_Exists *async = exists->parent;
2251
2252    async->callbacks = eina_list_remove(async->callbacks, exists);
2253    if (eina_list_count(async->callbacks) <= 0)
2254      ecore_thread_cancel(async->thread);
2255
2256    ethumb_free(exists->dup);
2257    EINA_REFCOUNT_UNREF(exists->client)
2258      _ethumb_client_free(exists->client);
2259    free(exists);
2260 }
2261
2262 /**
2263  * Check if an exists request was cancelled.
2264  *
2265  * @param exists the request to check.
2266  * @result return EINA_TRUE if the request was cancelled.
2267  */
2268 EAPI Eina_Bool
2269 ethumb_client_thumb_exists_check(Ethumb_Exists *exists)
2270 {
2271    Ethumb_Async_Exists *async = exists->parent;
2272
2273    if (!async) return EINA_TRUE;
2274
2275    if (async->callbacks) return EINA_FALSE;
2276
2277    return ecore_thread_check(async->thread);
2278 }
2279
2280 /**
2281  * Ask server to generate thumbnail.
2282  *
2283  * This process is asynchronous and will report back from main loop
2284  * using @a generated_cb. One can cancel this request by calling
2285  * ethumb_client_generate_cancel() or
2286  * ethumb_client_generate_cancel_all(), but not that request might be
2287  * processed by server already and no generated files will be removed
2288  * if that is the case.
2289  *
2290  * This will not check if file already exists, this should be done by
2291  * explicitly calling ethumb_client_thumb_exists(). That is, this
2292  * function will override any existing thumbnail.
2293  *
2294  * @param client client instance. Must @b not be @c NULL and client
2295  *        must be connected (after connected_cb is called).
2296  * @param generated_cb function to report generation results.
2297  * @param data context argument to give back to @a generated_cb. May
2298  *        be @c NULL.
2299  * @param data context to give back to @a generate_cb. May be @c
2300  *        NULL.
2301  * @param free_data used to release @a data resources after @a
2302  *        generated_cb is called or user calls
2303  *        ethumb_client_disconnect().
2304  *
2305  * @return identifier or -1 on error. If -1 is returned (error) then
2306  *         @a free_data is @b not called!
2307  *
2308  * @see ethumb_client_connect()
2309  * @see ethumb_client_file_set()
2310  * @see ethumb_client_thumb_exists()
2311  * @see ethumb_client_generate_cancel()
2312  * @see ethumb_client_generate_cancel_all()
2313  */
2314 EAPI int
2315 ethumb_client_generate(Ethumb_Client *client, Ethumb_Client_Generate_Cb generated_cb, const void *data, Eina_Free_Cb free_data)
2316 {
2317    const char *file, *key, *thumb, *thumb_key;
2318    int id;
2319    EINA_SAFETY_ON_NULL_RETURN_VAL(client, -1);
2320    EINA_SAFETY_ON_FALSE_RETURN_VAL(client->connected, -1);
2321
2322    ethumb_file_get(client->ethumb, &file, &key);
2323    if (!file)
2324      {
2325         ERR("no file set.");
2326         return -1;
2327      }
2328
2329    ethumb_thumb_path_get(client->ethumb, &thumb, &thumb_key);
2330
2331    if (client->old_ethumb_conf &&
2332        ethumb_cmp(client->old_ethumb_conf, client->ethumb))
2333      {
2334        ethumb_client_ethumb_setup(client);
2335        ethumb_free(client->old_ethumb_conf);
2336        client->old_ethumb_conf = NULL;
2337      }
2338    id = _ethumb_client_queue_add(client, file, key, thumb, thumb_key,
2339                                  generated_cb, data, free_data);
2340
2341    return id;
2342 }