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