Welcome Ethumb, it's ready to get out of PROTO.
[framework/uifw/ethumb.git] / src / lib / client / Ethumb_Client.c
1 /**
2  * @file
3  *
4  * Copyright (C) 2009 by ProFUSION embedded systems
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,  but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE.  See the  GNU General Public License
14  * for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  *
21  * @author Rafael Antognolli <antognolli@profusion.mobi>
22  */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include <Eina.h>
27 #include <eina_safety_checks.h>
28 #include <Ethumb.h>
29 #include "Ethumb_Client.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <limits.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <sys/types.h>
37 #include <stdbool.h>
38
39 #ifndef PATH_MAX
40 #define PATH_MAX 4096
41 #endif
42
43 #include <E_DBus.h>
44
45 #define MAX_ID 2000000
46
47 static int _log_dom = -1;
48 #define DBG(...) EINA_LOG_DOM_DBG(_log_dom, __VA_ARGS__)
49 #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__)
50 #define WRN(...) EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
51 #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__)
52
53 struct _Ethumb_Client
54 {
55    Ethumb *ethumb;
56    int id_count;
57
58    E_DBus_Connection *conn;
59    E_DBus_Signal_Handler *name_owner_changed_handler;
60    E_DBus_Signal_Handler *generated_signal;
61    DBusPendingCall *pending_get_name_owner;
62    DBusPendingCall *pending_start_service_by_name;
63    const char *unique_name;
64    DBusPendingCall *pending_new;
65    struct {
66       Ethumb_Client_Connect_Cb cb;
67       void *data;
68       Eina_Free_Cb free_data;
69    } connect;
70    Eina_List *pending_add;
71    Eina_List *pending_remove;
72    Eina_List *pending_gen;
73    DBusPendingCall *pending_clear;
74    DBusPendingCall *pending_setup;
75    struct {
76       Ethumb_Client_Die_Cb cb;
77       void *data;
78       Eina_Free_Cb free_data;
79    } die;
80    const char *object_path;
81
82    Eina_Bool ethumb_dirty : 1;
83    Eina_Bool connected : 1;
84    Eina_Bool server_started : 1;
85 };
86
87 struct _ethumb_pending_add
88 {
89    dbus_int32_t id;
90    const char *file;
91    const char *key;
92    const char *thumb;
93    const char *thumb_key;
94    Ethumb_Client_Generate_Cb generated_cb;
95    void *data;
96    Eina_Free_Cb free_data;
97    DBusPendingCall *pending_call;
98    Ethumb_Client *client;
99 };
100
101 struct _ethumb_pending_remove
102 {
103    dbus_int32_t id;
104    Ethumb_Client_Generate_Cancel_Cb cancel_cb;
105    void *data;
106    Eina_Free_Cb free_data;
107    DBusPendingCall *pending_call;
108    Ethumb_Client *client;
109 };
110
111 struct _ethumb_pending_gen
112 {
113    dbus_int32_t id;
114    const char *file;
115    const char *key;
116    const char *thumb;
117    const char *thumb_key;
118    Ethumb_Client_Generate_Cb generated_cb;
119    void *data;
120    Eina_Free_Cb free_data;
121 };
122
123 static const char _ethumb_dbus_bus_name[] = "org.enlightenment.Ethumb";
124 static const char _ethumb_dbus_interface[] = "org.enlightenment.Ethumb";
125 static const char _ethumb_dbus_objects_interface[] = "org.enlightenment.Ethumb.objects";
126 static const char _ethumb_dbus_path[] = "/org/enlightenment/Ethumb";
127 static const char fdo_interface[] = "org.freedesktop.DBus";
128 static const char fdo_bus_name[] = "org.freedesktop.DBus";
129 static const char fdo_path[] = "/org/freedesktop/DBus";
130
131 static int _initcount = 0;
132
133 static void _ethumb_client_generated_cb(void *data, DBusMessage *msg);
134 static void _ethumb_client_get_name_owner(void *data, DBusMessage *msg, DBusError *err);
135
136 static inline bool
137 __dbus_callback_check_and_init(const char *file, int line, const char *function, DBusMessage *msg, DBusMessageIter *itr, DBusError *err)
138 {
139    if (!msg)
140      {
141         ERR("%s:%d:%s() callback without message arguments!\n",
142                 file, line, function);
143
144         if (err)
145           ERR("%s:%d:%s() an error was reported by server: "
146                   "name=\"%s\", message=\"%s\"\n",
147                   file, line, function, err->name, err->message);
148
149         return 0;
150      }
151
152    if (!dbus_message_iter_init(msg, itr))
153      {
154         ERR("%s:%d:%s() could not init iterator.\n",
155                 file, line, function);
156         return 0;
157      }
158
159    return 1;
160 }
161
162 #define _dbus_callback_check_and_init(msg, itr, err)                    \
163   __dbus_callback_check_and_init(__FILE__, __LINE__, __FUNCTION__,      \
164                                  msg, itr, err)
165
166 static inline bool
167 __dbus_iter_type_check(int type, int expected, const char *expected_name)
168 {
169    if (type == expected)
170      return 1;
171
172    ERR("expected type %s (%c) but got %c instead!\n",
173            expected_name, expected, type);
174
175    return 0;
176 }
177 #define _dbus_iter_type_check(t, e) __dbus_iter_type_check(t, e, #e)
178
179 #define CHECK_NULL_RETURN(ptr, ...)                                     \
180   do                                                                    \
181     {                                                                   \
182        if ((ptr) == NULL)                                               \
183          {                                                              \
184             ERR("%s == NULL!\n", #ptr);         \
185             return __VA_ARGS__;                                         \
186          }                                                              \
187     }                                                                   \
188   while (0)
189
190 static void
191 _ethumb_client_name_owner_changed(void *data, DBusMessage *msg)
192 {
193    DBusError err;
194    const char *name, *from, *to;
195    Ethumb_Client *client = data;
196
197    dbus_error_init(&err);
198    if (!dbus_message_get_args(msg, &err,
199        DBUS_TYPE_STRING, &name,
200        DBUS_TYPE_STRING, &from,
201        DBUS_TYPE_STRING, &to,
202        DBUS_TYPE_INVALID))
203      {
204         ERR("could not get NameOwnerChanged arguments: %s: %s\n",
205             err.name, err.message);
206         dbus_error_free(&err);
207         return;
208      }
209
210    if (strcmp(name, _ethumb_dbus_bus_name) != 0)
211      return;
212
213    DBG("NameOwnerChanged from=[%s] to=[%s]\n", from, to);
214
215    if (from[0] != '\0' && to[0] == '\0')
216      {
217         DBG("exit ethumbd at %s\n", from);
218         if (strcmp(client->unique_name, from) != 0)
219           WRN("%s was not the known name %s, ignored.\n",
220                from, client->unique_name);
221         else
222           {
223              ERR("server exit!!!\n");
224              if (client->die.cb)
225                {
226                   client->die.cb(client->die.data, client);
227                   client->die.cb = NULL;
228                }
229              if (client->die.free_data)
230                {
231                   client->die.free_data(client->die.data);
232                   client->die.free_data = NULL;
233                   client->die.data = NULL;
234                }
235           }
236      }
237    else
238      DBG("unknown change from %s to %s\n", from, to);
239 }
240
241 static void
242 _ethumb_client_report_connect(Ethumb_Client *client, Eina_Bool success)
243 {
244    if (!client->connect.cb)
245      {
246         ERR("already called?!");
247         return;
248      }
249
250    client->connect.cb(client->connect.data, client, success);
251    if (client->connect.free_data)
252      {
253         client->connect.free_data(client->connect.data);
254         client->connect.free_data = NULL;
255      }
256    client->connect.cb = NULL;
257    client->connect.data = NULL;
258 }
259
260 static void
261 _ethumb_client_new_cb(void *data, DBusMessage *msg, DBusError *error)
262 {
263    DBusMessageIter iter;
264    const char *opath;
265    int t;
266    Ethumb_Client *client = data;
267
268    client->pending_new = NULL;
269
270    if (!_dbus_callback_check_and_init(msg, &iter, error))
271      goto end_error;
272    t = dbus_message_iter_get_arg_type(&iter);
273    if (!_dbus_iter_type_check(t, DBUS_TYPE_OBJECT_PATH))
274      goto end_error;
275
276    dbus_message_iter_get_basic(&iter, &opath);
277    if (opath[0] == '\0')
278      goto end_error;
279
280    client->object_path = eina_stringshare_add(opath);
281
282    client->generated_signal = e_dbus_signal_handler_add(
283       client->conn, _ethumb_dbus_bus_name, opath,
284       _ethumb_dbus_objects_interface, "generated",
285       _ethumb_client_generated_cb, client);
286
287    _ethumb_client_report_connect(client, 1);
288    return;
289
290 end_error:
291    _ethumb_client_report_connect(client, 0);
292 }
293
294 static void
295 _ethumb_client_call_new(Ethumb_Client *client)
296 {
297    DBusMessage *msg;
298
299    msg = dbus_message_new_method_call(_ethumb_dbus_bus_name, _ethumb_dbus_path,
300                                       _ethumb_dbus_interface, "new");
301    client->pending_new = e_dbus_message_send(client->conn, msg,
302                                              _ethumb_client_new_cb, -1,
303                                              client);
304    dbus_message_unref(msg);
305 }
306
307 static void
308 _ethumb_client_start_server_cb(void *data, DBusMessage *msg, DBusError *err)
309 {
310    Ethumb_Client *client = data;
311    DBusMessageIter iter;
312    dbus_uint32_t ret;
313    int t;
314
315    client->pending_start_service_by_name = NULL;
316
317    if (!_dbus_callback_check_and_init(msg, &iter, err))
318      goto error;
319
320    t = dbus_message_iter_get_arg_type(&iter);
321    if (!_dbus_iter_type_check(t, DBUS_TYPE_UINT32))
322      goto error;
323
324    dbus_message_iter_get_basic(&iter, &ret);
325    if ((ret != 1) && (ret != 2))
326      {
327         ERR("Error starting Ethumbd DBus service by its name: retcode %u\n",
328             ret);
329         goto error;
330      }
331
332    client->server_started = 1;
333    DBG("Ethumbd DBus service started successfully (%d), now request its name\n",
334        ret);
335
336    if (client->pending_get_name_owner)
337      {
338         DBG("already requesting name owner, cancel and try again\n");
339         dbus_pending_call_cancel(client->pending_get_name_owner);
340      }
341
342    client->pending_get_name_owner = e_dbus_get_name_owner
343      (client->conn, _ethumb_dbus_bus_name, _ethumb_client_get_name_owner,
344       client);
345    if (!client->pending_get_name_owner)
346      {
347         ERR("could not create a get_name_owner request.\n");
348         goto error;
349      }
350
351    return;
352
353  error:
354    ERR("failed to start Ethumbd DBus service by its name.\n");
355    _ethumb_client_report_connect(client, 0);
356 }
357
358 static void
359 _ethumb_client_start_server(Ethumb_Client *client)
360 {
361    if (client->pending_start_service_by_name)
362      {
363         DBG("already pending start service by name.\n");
364         return;
365      }
366
367    client->server_started = 0;
368    client->pending_start_service_by_name = e_dbus_start_service_by_name
369      (client->conn, _ethumb_dbus_bus_name, 0, _ethumb_client_start_server_cb,
370       client);
371    if (!client->pending_start_service_by_name)
372      {
373         ERR("could not start service by name!\n");
374         _ethumb_client_report_connect(client, 0);
375      }
376 }
377
378 static void
379 _ethumb_client_get_name_owner(void *data, DBusMessage *msg, DBusError *err)
380 {
381    DBusMessageIter iter;
382    const char *uid;
383    Ethumb_Client *client = data;
384    int t;
385
386    client->pending_get_name_owner = NULL;
387
388    if (dbus_error_is_set(err) && (!client->server_started))
389      {
390         DBG("could not find server (%s), try to start it...\n", err->message);
391         _ethumb_client_start_server(client);
392         return;
393      }
394
395    if (!_dbus_callback_check_and_init(msg, &iter, err))
396      goto error;
397
398    t = dbus_message_iter_get_arg_type(&iter);
399    if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING))
400      goto error;
401
402    dbus_message_iter_get_basic(&iter, &uid);
403    if (!uid)
404      {
405         ERR("no name owner!\n");
406         goto error;
407      }
408
409    DBG("unique name = %s\n", uid);
410    client->unique_name = eina_stringshare_add(uid);
411
412    _ethumb_client_call_new(client);
413    client->connected = 1;
414    return;
415
416 error:
417    _ethumb_client_report_connect(client, 0);
418 }
419
420 EAPI int
421 ethumb_client_init(void)
422 {
423    if (_initcount)
424      return ++_initcount;
425
426    if (!eina_init())
427      {
428         fprintf(stderr, "ERROR: Could not initialize log module.\n");
429         return 0;
430      }
431    _log_dom = eina_log_domain_register("ethumb_client", EINA_COLOR_YELLOW);
432    if (_log_dom < 0)
433      {
434         EINA_LOG_ERR("Could not register log domain: ethumb_client");
435         eina_shutdown();
436         return 0;
437      }
438
439    ethumb_init();
440    e_dbus_init();
441
442    return ++_initcount;
443 }
444
445 EAPI int
446 ethumb_client_shutdown(void)
447 {
448    _initcount--;
449    if (_initcount > 0)
450      return _initcount;
451
452    e_dbus_shutdown();
453    ethumb_shutdown();
454    eina_log_domain_unregister(_log_dom);
455    _log_dom = -1;
456    eina_shutdown();
457    return _initcount;
458 }
459
460 /**
461  * Connects to Ethumb server and return the client instance.
462  *
463  * This is the "constructor" of Ethumb_Client, where everything
464  * starts.
465  *
466  * If server was down, it is tried to start it using DBus activation,
467  * then the connection is retried.
468  *
469  * This call is asynchronous and will not block, instead it will be in
470  * "not connected" state until @a connect_cb is called with either
471  * success or failure. On failure, then no methods should be
472  * called. On success you're now able to setup and then ask generation
473  * of thumbnails.
474  *
475  * Usually you should listen for server death/disconenction with
476  * ethumb_client_on_server_die_callback_set().
477  *
478  * @param connect_cb function to call to report connection success or
479  *        failure. Do not call any other ethumb_client method until
480  *        this function returns. The first received parameter is the
481  *        given argument @a data. Must @b not be @c NULL. This
482  *        function will not be called if user explicitly calls
483  *        ethumb_client_disconnect().
484  * @param data context to give back to @a connect_cb. May be @c NULL.
485  * @param free_data function used to release @a data resources, if
486  *        any. May be @c NULL. If this function exists, it will be
487  *        called immediately after @a connect_cb is called or if user
488  *        explicitly calls ethumb_client_disconnect() before such
489  *        (that is, don't rely on @a data after @a connect_cb was
490  *        called!)
491  *
492  * @return client instance or NULL if failed. If @a connect_cb is
493  *         missing it returns @c NULL. If it fail for other
494  *         conditions, @c NULL is also returned and @a connect_cb is
495  *         called with @c success=EINA_FALSE. The client instance is
496  *         not ready to be used until @a connect_cb is called.
497  */
498 EAPI Ethumb_Client *
499 ethumb_client_connect(Ethumb_Client_Connect_Cb connect_cb, const void *data, Eina_Free_Cb free_data)
500 {
501    Ethumb_Client *eclient;
502
503    EINA_SAFETY_ON_NULL_RETURN_VAL(connect_cb, NULL);
504
505    eclient = calloc(1, sizeof(*eclient));
506    if (!eclient)
507      {
508         ERR("could not allocate Ethumb_Client structure.\n");
509         goto err;
510      }
511
512    eclient->connect.cb = connect_cb;
513    eclient->connect.data = (void *)data;
514    eclient->connect.free_data = free_data;
515
516    eclient->ethumb = ethumb_new();
517    if (!eclient->ethumb)
518      {
519         ERR("could not create ethumb handler.\n");
520         goto ethumb_new_err;
521      }
522
523    eclient->conn = e_dbus_bus_get(DBUS_BUS_SESSION);
524    if (!eclient->conn)
525      {
526         ERR("could not connect to session bus.\n");
527         goto connection_err;
528      }
529
530    eclient->name_owner_changed_handler = e_dbus_signal_handler_add(
531          eclient->conn, fdo_bus_name, fdo_path, fdo_interface,
532          "NameOwnerChanged", _ethumb_client_name_owner_changed, eclient);
533
534    eclient->pending_get_name_owner = e_dbus_get_name_owner(
535          eclient->conn, _ethumb_dbus_bus_name, _ethumb_client_get_name_owner,
536          eclient);
537    if (!eclient->pending_get_name_owner)
538      {
539         ERR("could not create a get_name_owner request.\n");
540         goto connection_err;
541      }
542
543    return eclient;
544
545 connection_err:
546    ethumb_free(eclient->ethumb);
547 ethumb_new_err:
548    free(eclient);
549 err:
550    connect_cb((void *)data, NULL, EINA_FALSE);
551    if (free_data)
552      free_data((void *)data);
553    return NULL;
554 }
555
556 /**
557  * Disconnect the client, releasing all client resources.
558  *
559  * This is the destructor of Ethumb_Client, after it's disconnected
560  * the client handle is now gone and should not be used.
561  */
562 EAPI void
563 ethumb_client_disconnect(Ethumb_Client *client)
564 {
565    void *data;
566
567    EINA_SAFETY_ON_NULL_RETURN(client);
568
569    if (!client->connected)
570      goto end_connection;
571
572    EINA_LIST_FREE(client->pending_add, data)
573      {
574         struct _ethumb_pending_add *pending = data;
575         eina_stringshare_del(pending->file);
576         eina_stringshare_del(pending->key);
577         eina_stringshare_del(pending->thumb);
578         eina_stringshare_del(pending->thumb_key);
579         dbus_pending_call_cancel(pending->pending_call);
580         dbus_pending_call_unref(pending->pending_call);
581         if (pending->free_data)
582           pending->free_data(pending->data);
583         free(pending);
584      }
585
586    EINA_LIST_FREE(client->pending_gen, data)
587      {
588         struct _ethumb_pending_gen *pending = data;
589         eina_stringshare_del(pending->file);
590         eina_stringshare_del(pending->key);
591         eina_stringshare_del(pending->thumb);
592         eina_stringshare_del(pending->thumb_key);
593         if (pending->free_data)
594           pending->free_data(pending->data);
595         free(pending);
596      }
597
598    EINA_LIST_FREE(client->pending_remove, data)
599      {
600         struct _ethumb_pending_remove *pending = data;
601         dbus_pending_call_cancel(pending->pending_call);
602         dbus_pending_call_unref(pending->pending_call);
603         if (pending->free_data)
604           pending->free_data(pending->data);
605         free(pending);
606      }
607
608    if (client->pending_clear)
609      {
610         dbus_pending_call_cancel(client->pending_clear);
611         dbus_pending_call_unref(client->pending_clear);
612      }
613
614 end_connection:
615    if (client->object_path)
616      eina_stringshare_del(client->object_path);
617
618    if (client->pending_new)
619      dbus_pending_call_cancel(client->pending_new);
620
621    if (client->unique_name)
622      eina_stringshare_del(client->unique_name);
623
624    if (client->pending_get_name_owner)
625      dbus_pending_call_cancel(client->pending_get_name_owner);
626
627    if (client->pending_start_service_by_name)
628      dbus_pending_call_cancel(client->pending_start_service_by_name);
629
630    ethumb_free(client->ethumb);
631
632    e_dbus_signal_handler_del(client->conn, client->name_owner_changed_handler);
633    if (client->connected)
634      e_dbus_signal_handler_del(client->conn, client->generated_signal);
635    e_dbus_connection_close(client->conn);
636
637    if (client->connect.free_data)
638      client->connect.free_data(client->connect.data);
639    if (client->die.free_data)
640      client->die.free_data(client->die.data);
641
642    free(client);
643 }
644
645 /**
646  * Sets the callback to report server died.
647  *
648  * When server dies there is nothing you can do, just release
649  * resources with ethumb_client_disconnect() and probably try to
650  * connect again.
651  *
652  * Usually you should set this callback and handle this case, it does
653  * happen!
654  *
655  * @param client the client instance to monitor. Must @b not be @c
656  *        NULL.
657  * @param server_die_cb function to call back when server dies. The
658  *        first parameter will be the argument @a data. May be @c
659  *        NULL.
660  * @param data context to give back to @a server_die_cb. May be @c
661  *        NULL.
662  * @param free_data used to release @a data resources after @a
663  *        server_die_cb is called or user calls
664  *        ethumb_client_disconnect().
665  */
666 EAPI void
667 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)
668 {
669    EINA_SAFETY_ON_NULL_RETURN(client);
670
671    if (client->die.free_data)
672      client->die.free_data(client->die.data);
673
674    client->die.cb = server_die_cb;
675    client->die.data = (void *)data;
676    client->die.free_data = free_data;
677 }
678
679 static void
680 _ethumb_client_ethumb_setup_cb(void *data, DBusMessage *msg, DBusError *error)
681 {
682    DBusMessageIter iter;
683    int t;
684    dbus_bool_t result = 0;
685    Ethumb_Client *client = data;
686
687    client->pending_setup = NULL;
688
689    if (!_dbus_callback_check_and_init(msg, &iter, error))
690      return;
691
692    t = dbus_message_iter_get_arg_type(&iter);
693    if (!_dbus_iter_type_check(t, DBUS_TYPE_BOOLEAN))
694      return;
695
696    dbus_message_iter_get_basic(&iter, &result);
697 }
698
699 static const char *
700 _ethumb_client_dbus_get_bytearray(DBusMessageIter *iter)
701 {
702    int el_type;
703    int length;
704    DBusMessageIter riter;
705    const char *result;
706
707    el_type = dbus_message_iter_get_element_type(iter);
708    if (el_type != DBUS_TYPE_BYTE)
709      {
710         ERR("not an byte array element.\n");
711         return NULL;
712      }
713
714    dbus_message_iter_recurse(iter, &riter);
715    dbus_message_iter_get_fixed_array(&riter, &result, &length);
716
717    if (result[0] == '\0')
718      return NULL;
719    else
720      return eina_stringshare_add(result);
721 }
722
723 static void
724 _ethumb_client_dbus_append_bytearray(DBusMessageIter *iter, const char *string)
725 {
726    DBusMessageIter viter;
727
728    if (!string)
729      string = "";
730
731    dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &viter);
732    dbus_message_iter_append_fixed_array(&viter, DBUS_TYPE_BYTE, &string,
733                                         strlen(string) + 1);
734    dbus_message_iter_close_container(iter, &viter);
735 }
736
737 /**
738  * Send setup to server.
739  *
740  * This method is called automatically by ethumb_client_generate() if
741  * any property was changed. No need to call it manually.
742  *
743  * @param client client instance. Must @b not be @c NULL and client
744  *        must be connected (after connected_cb is called).
745  */
746 EAPI void
747 ethumb_client_ethumb_setup(Ethumb_Client *client)
748 {
749    DBusMessage *msg;
750    DBusMessageIter iter, aiter, diter, viter, vaiter;
751    Ethumb *e = client->ethumb;
752    const char *entry;
753    dbus_int32_t tw, th, format, aspect, quality, compress;
754    float cx, cy;
755    double t;
756    const char *theme_file, *group, *swallow;
757    const char *directory, *category;
758    double video_time, video_start, video_interval;
759    dbus_int32_t video_ntimes, video_fps, document_page;
760
761    EINA_SAFETY_ON_NULL_RETURN(client);
762    EINA_SAFETY_ON_FALSE_RETURN(client->connected);
763    client->ethumb_dirty = 0;
764
765    msg = dbus_message_new_method_call(_ethumb_dbus_bus_name,
766                                       client->object_path,
767                                       _ethumb_dbus_objects_interface,
768                                       "ethumb_setup");
769    dbus_message_iter_init_append(msg, &iter);
770    dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &aiter);
771
772 #define _open_variant_iter(str_entry, str_type, end_iter)                  \
773    entry = str_entry;                                                      \
774    dbus_message_iter_open_container(&aiter, DBUS_TYPE_DICT_ENTRY, NULL, &diter); \
775    dbus_message_iter_append_basic(&diter, DBUS_TYPE_STRING, &entry);   \
776    dbus_message_iter_open_container(&diter, DBUS_TYPE_VARIANT, str_type,   \
777                                     &end_iter);
778
779 #define _close_variant_iter(end_iter)                                      \
780    dbus_message_iter_close_container(&diter, &end_iter);                   \
781    dbus_message_iter_close_container(&aiter, &diter);
782
783    /* starting array elements */
784
785    _open_variant_iter("size", "(ii)", viter);
786    dbus_message_iter_open_container(&viter, DBUS_TYPE_STRUCT, NULL, &vaiter);
787    ethumb_thumb_size_get(e, &tw, &th);
788    dbus_message_iter_append_basic(&vaiter, DBUS_TYPE_INT32, &tw);
789    dbus_message_iter_append_basic(&vaiter, DBUS_TYPE_INT32, &th);
790    dbus_message_iter_close_container(&viter, &vaiter);
791    _close_variant_iter(viter);
792
793    _open_variant_iter("format", "i", viter);
794    format = ethumb_thumb_format_get(e);
795    dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &format);
796    _close_variant_iter(viter);
797
798    _open_variant_iter("aspect", "i", viter);
799    aspect = ethumb_thumb_aspect_get(e);
800    dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &aspect);
801    _close_variant_iter(viter);
802
803    _open_variant_iter("crop", "(dd)", viter);
804    dbus_message_iter_open_container(&viter, DBUS_TYPE_STRUCT, NULL, &vaiter);
805    ethumb_thumb_crop_align_get(e, &cx, &cy);
806    t = cx;
807    dbus_message_iter_append_basic(&vaiter, DBUS_TYPE_DOUBLE, &t);
808    t = cy;
809    dbus_message_iter_append_basic(&vaiter, DBUS_TYPE_DOUBLE, &t);
810    dbus_message_iter_close_container(&viter, &vaiter);
811    _close_variant_iter(viter);
812
813    _open_variant_iter("quality", "i", viter);
814    quality = ethumb_thumb_quality_get(e);
815    dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &quality);
816    _close_variant_iter(viter);
817
818    _open_variant_iter("compress", "i", viter);
819    compress = ethumb_thumb_compress_get(e);
820    dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &compress);
821    _close_variant_iter(viter);
822
823    _open_variant_iter("frame", "(ayayay)", viter);
824    dbus_message_iter_open_container(&viter, DBUS_TYPE_STRUCT, NULL, &vaiter);
825    ethumb_frame_get(e, &theme_file, &group, &swallow);
826    _ethumb_client_dbus_append_bytearray(&vaiter, theme_file);
827    _ethumb_client_dbus_append_bytearray(&vaiter, group);
828    _ethumb_client_dbus_append_bytearray(&vaiter, swallow);
829    dbus_message_iter_close_container(&viter, &vaiter);
830    _close_variant_iter(viter);
831
832    _open_variant_iter("directory", "ay", viter);
833    directory = ethumb_thumb_dir_path_get(e);
834    _ethumb_client_dbus_append_bytearray(&viter, directory);
835    _close_variant_iter(viter);
836
837    _open_variant_iter("category", "ay", viter);
838    category = ethumb_thumb_category_get(e);
839    _ethumb_client_dbus_append_bytearray(&viter, category);
840    _close_variant_iter(viter);
841
842    _open_variant_iter("video_time", "d", viter);
843    video_time = ethumb_video_time_get(e);
844    dbus_message_iter_append_basic(&viter, DBUS_TYPE_DOUBLE, &video_time);
845    _close_variant_iter(viter);
846
847    _open_variant_iter("video_start", "d", viter);
848    video_start = ethumb_video_start_get(e);
849    dbus_message_iter_append_basic(&viter, DBUS_TYPE_DOUBLE, &video_start);
850    _close_variant_iter(viter);
851
852    _open_variant_iter("video_interval", "d", viter);
853    video_interval = ethumb_video_interval_get(e);
854    dbus_message_iter_append_basic(&viter, DBUS_TYPE_DOUBLE, &video_interval);
855    _close_variant_iter(viter);
856
857    _open_variant_iter("video_ntimes", "i", viter);
858    video_ntimes = ethumb_video_ntimes_get(e);
859    dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &video_ntimes);
860    _close_variant_iter(viter);
861
862    _open_variant_iter("video_fps", "i", viter);
863    video_fps = ethumb_video_fps_get(e);
864    dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &video_fps);
865    _close_variant_iter(viter);
866
867    _open_variant_iter("document_page", "i", viter);
868    document_page = ethumb_document_page_get(e);
869    dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &document_page);
870    _close_variant_iter(viter);
871
872 #undef _open_variant_iter
873 #undef _close_variant_iter
874
875    dbus_message_iter_close_container(&iter, &aiter);
876
877    client->pending_setup = e_dbus_message_send(client->conn, msg,
878                                                _ethumb_client_ethumb_setup_cb,
879                                                -1, client);
880    dbus_message_unref(msg);
881 }
882
883 static void
884 _ethumb_client_generated_cb(void *data, DBusMessage *msg)
885 {
886    DBusMessageIter iter;
887    dbus_int32_t id = -1;
888    const char *thumb;
889    const char *thumb_key;
890    Ethumb_Client *client = data;
891    int t;
892    dbus_bool_t success;
893    Eina_List *l;
894    int found;
895    struct _ethumb_pending_gen *pending;
896
897    dbus_message_iter_init(msg, &iter);
898
899    t = dbus_message_iter_get_arg_type(&iter);
900    if (!_dbus_iter_type_check(t, DBUS_TYPE_INT32))
901      goto end;
902    dbus_message_iter_get_basic(&iter, &id);
903    dbus_message_iter_next(&iter);
904
905    t = dbus_message_iter_get_arg_type(&iter);
906    if (!_dbus_iter_type_check(t, DBUS_TYPE_ARRAY))
907      goto end;
908    thumb = _ethumb_client_dbus_get_bytearray(&iter);
909    dbus_message_iter_next(&iter);
910
911    t = dbus_message_iter_get_arg_type(&iter);
912    if (!_dbus_iter_type_check(t, DBUS_TYPE_ARRAY))
913      goto end;
914    thumb_key = _ethumb_client_dbus_get_bytearray(&iter);
915    dbus_message_iter_next(&iter);
916
917    t = dbus_message_iter_get_arg_type(&iter);
918    if (!_dbus_iter_type_check(t, DBUS_TYPE_BOOLEAN))
919      goto end;
920    dbus_message_iter_get_basic(&iter, &success);
921
922    found = 0;
923    l = client->pending_gen;
924    while (l)
925      {
926         pending = l->data;
927         if (pending->id == id)
928           {
929              found = 1;
930              break;
931           }
932         l = l->next;
933      }
934
935    if (found)
936      {
937         client->pending_gen = eina_list_remove_list(client->pending_gen, l);
938         pending->generated_cb(pending->data, client, id,
939                               pending->file, pending->key,
940                               pending->thumb, pending->thumb_key,
941                               success);
942         if (pending->free_data)
943           pending->free_data(pending->data);
944         eina_stringshare_del(pending->file);
945         eina_stringshare_del(pending->key);
946         eina_stringshare_del(pending->thumb);
947         eina_stringshare_del(pending->thumb_key);
948         free(pending);
949      }
950
951 end:
952    eina_stringshare_del(thumb);
953    eina_stringshare_del(thumb_key);
954 }
955
956 static void
957 _ethumb_client_queue_add_cb(void *data, DBusMessage *msg, DBusError *error)
958 {
959    DBusMessageIter iter;
960    int t;
961    dbus_int32_t id = -1;
962    struct _ethumb_pending_add *pending = data;
963    struct _ethumb_pending_gen *generating;
964    Ethumb_Client *client = pending->client;
965
966    client->pending_add = eina_list_remove(client->pending_add, pending);
967
968    if (!_dbus_callback_check_and_init(msg, &iter, error))
969      goto end;
970
971    t = dbus_message_iter_get_arg_type(&iter);
972    if (!_dbus_iter_type_check(t, DBUS_TYPE_INT32))
973      goto end;
974
975    dbus_message_iter_get_basic(&iter, &id);
976
977    generating = calloc(1, sizeof(*generating));
978    generating->id = id;
979    generating->file = pending->file;
980    generating->key = pending->key;
981    generating->thumb = pending->thumb;
982    generating->thumb_key = pending->thumb_key;
983    generating->generated_cb = pending->generated_cb;
984    generating->data = pending->data;
985    generating->free_data = pending->free_data;
986    client->pending_gen = eina_list_append(client->pending_gen, generating);
987
988 end:
989    free(pending);
990 }
991
992 static int
993 _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)
994 {
995    DBusMessage *msg;
996    DBusMessageIter iter;
997    struct _ethumb_pending_add *pending;
998
999    pending = calloc(1, sizeof(*pending));
1000    pending->id = client->id_count;
1001    pending->file = eina_stringshare_add(file);
1002    pending->key = eina_stringshare_add(key);
1003    pending->thumb = eina_stringshare_add(thumb);
1004    pending->thumb_key = eina_stringshare_add(thumb_key);
1005    pending->generated_cb = generated_cb;
1006    pending->data = (void *)data;
1007    pending->free_data = free_data;
1008    pending->client = client;
1009
1010    client->id_count = (client->id_count + 1) % MAX_ID;
1011
1012    msg = dbus_message_new_method_call(_ethumb_dbus_bus_name,
1013                                       client->object_path,
1014                                       _ethumb_dbus_objects_interface,
1015                                       "queue_add");
1016
1017    dbus_message_iter_init_append(msg, &iter);
1018    dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &pending->id);
1019    _ethumb_client_dbus_append_bytearray(&iter, file);
1020    _ethumb_client_dbus_append_bytearray(&iter, key);
1021    _ethumb_client_dbus_append_bytearray(&iter, thumb);
1022    _ethumb_client_dbus_append_bytearray(&iter, thumb_key);
1023
1024    pending->pending_call = e_dbus_message_send(client->conn, msg,
1025                                                _ethumb_client_queue_add_cb,
1026                                                -1, pending);
1027    client->pending_add = eina_list_append(client->pending_add, pending);
1028    dbus_message_unref(msg);
1029
1030    return pending->id;
1031 }
1032
1033 static void
1034 _ethumb_client_queue_remove_cb(void *data, DBusMessage *msg, DBusError *error)
1035 {
1036    DBusMessageIter iter;
1037    int t;
1038    dbus_bool_t success = 0;
1039    struct _ethumb_pending_remove *pending = data;
1040    Ethumb_Client *client = pending->client;
1041
1042    client->pending_remove = eina_list_remove(client->pending_remove, pending);
1043
1044    if (!_dbus_callback_check_and_init(msg, &iter, error))
1045      goto end;
1046
1047    t = dbus_message_iter_get_arg_type(&iter);
1048    if (!_dbus_iter_type_check(t, DBUS_TYPE_BOOLEAN))
1049      goto end;
1050
1051    dbus_message_iter_get_basic(&iter, &success);
1052
1053 end:
1054    if (pending->cancel_cb)
1055      pending->cancel_cb(pending->data, success);
1056    if (pending->free_data)
1057      pending->free_data(pending->data);
1058    free(pending);
1059 }
1060
1061 /**
1062  * Ask server to cancel generation of thumbnail.
1063  *
1064  * @param client client instance. Must @b not be @c NULL and client
1065  *        must be connected (after connected_cb is called).
1066  * @param id valid id returned by ethumb_client_generate()
1067  * @param cancel_cb function to report cancellation results.
1068  * @param data context argument to give back to @a cancel_cb. May be
1069  *        @c NULL.
1070  * @param data context to give back to @a cancel_cb. May be @c
1071  *        NULL.
1072  * @param free_data used to release @a data resources after @a
1073  *        cancel_cb is called or user calls
1074  *        ethumb_client_disconnect().
1075  */
1076 EAPI void
1077 ethumb_client_generate_cancel(Ethumb_Client *client, int id, Ethumb_Client_Generate_Cancel_Cb cancel_cb, const void *data, Eina_Free_Cb free_data)
1078 {
1079    DBusMessage *msg;
1080    struct _ethumb_pending_remove *pending;
1081    Eina_List *l;
1082    int found;
1083    dbus_int32_t id32 = id;
1084    EINA_SAFETY_ON_NULL_RETURN(client);
1085    EINA_SAFETY_ON_FALSE_RETURN(id >= 0);
1086
1087    pending = calloc(1, sizeof(*pending));
1088    pending->id = id;
1089    pending->cancel_cb = cancel_cb;
1090    pending->data = (void *)data;
1091    pending->free_data = free_data;
1092    pending->client = client;
1093
1094    msg = dbus_message_new_method_call(_ethumb_dbus_bus_name,
1095                                       client->object_path,
1096                                       _ethumb_dbus_objects_interface,
1097                                       "queue_remove");
1098
1099    dbus_message_append_args(msg, DBUS_TYPE_INT32, &id32, DBUS_TYPE_INVALID);
1100    pending->pending_call = e_dbus_message_send(client->conn, msg,
1101                                                _ethumb_client_queue_remove_cb,
1102                                                -1, pending);
1103    client->pending_remove = eina_list_append(client->pending_remove, pending);
1104
1105    found = 0;
1106    l = client->pending_add;
1107    while (l)
1108      {
1109         struct _ethumb_pending_add *pending = l->data;
1110         if (pending->id != id32)
1111           {
1112              l = l->next;
1113              continue;
1114           }
1115         client->pending_add = eina_list_remove_list(client->pending_add, l);
1116         eina_stringshare_del(pending->file);
1117         eina_stringshare_del(pending->key);
1118         eina_stringshare_del(pending->thumb);
1119         eina_stringshare_del(pending->thumb_key);
1120         dbus_pending_call_cancel(pending->pending_call);
1121         dbus_pending_call_unref(pending->pending_call);
1122         if (pending->free_data)
1123           pending->free_data(pending->data);
1124         free(pending);
1125         found = 1;
1126         break;
1127      }
1128
1129    if (found)
1130      goto end;
1131
1132    l = client->pending_gen;
1133    while (l)
1134      {
1135         struct _ethumb_pending_gen *pending = l->data;
1136         if (pending->id != id32)
1137           {
1138              l = l->next;
1139              continue;
1140           }
1141         client->pending_gen = eina_list_remove_list(client->pending_gen, l);
1142         eina_stringshare_del(pending->file);
1143         eina_stringshare_del(pending->key);
1144         eina_stringshare_del(pending->thumb);
1145         eina_stringshare_del(pending->thumb_key);
1146         if (pending->free_data)
1147           pending->free_data(pending->data);
1148         free(pending);
1149         found = 1;
1150         break;
1151      }
1152
1153 end:
1154    dbus_message_unref(msg);
1155 }
1156
1157 static void
1158 _ethumb_client_queue_clear_cb(void *data, DBusMessage *msg __UNUSED__, DBusError *error __UNUSED__)
1159 {
1160    Ethumb_Client *client = data;
1161
1162    client->pending_clear = NULL;
1163 }
1164
1165 /**
1166  * Ask server to cancel generation of all thumbnails.
1167  *
1168  * @param client client instance. Must @b not be @c NULL and client
1169  *        must be connected (after connected_cb is called).
1170  *
1171  * @see ethumb_client_generate_cancel()
1172  */
1173 EAPI void
1174 ethumb_client_generate_cancel_all(Ethumb_Client *client)
1175 {
1176    DBusMessage *msg;
1177    void *data;
1178    EINA_SAFETY_ON_NULL_RETURN(client);
1179
1180    if (client->pending_clear)
1181      return;
1182
1183    EINA_LIST_FREE(client->pending_add, data)
1184      {
1185         struct _ethumb_pending_add *pending = data;
1186         eina_stringshare_del(pending->file);
1187         eina_stringshare_del(pending->key);
1188         eina_stringshare_del(pending->thumb);
1189         eina_stringshare_del(pending->thumb_key);
1190         dbus_pending_call_cancel(pending->pending_call);
1191         dbus_pending_call_unref(pending->pending_call);
1192         if (pending->free_data)
1193           pending->free_data(pending->data);
1194         free(pending);
1195      }
1196
1197    EINA_LIST_FREE(client->pending_gen, data)
1198      {
1199         struct _ethumb_pending_gen *pending = data;
1200         eina_stringshare_del(pending->file);
1201         eina_stringshare_del(pending->key);
1202         eina_stringshare_del(pending->thumb);
1203         eina_stringshare_del(pending->thumb_key);
1204         if (pending->free_data)
1205           pending->free_data(pending->data);
1206         free(pending);
1207      }
1208
1209    msg = dbus_message_new_method_call(_ethumb_dbus_bus_name,
1210                                       client->object_path,
1211                                       _ethumb_dbus_objects_interface,
1212                                       "queue_clear");
1213
1214    client->pending_clear = e_dbus_message_send(client->conn, msg,
1215                                                _ethumb_client_queue_clear_cb,
1216                                                -1, client);
1217
1218    dbus_message_unref(msg);
1219 }
1220
1221 EAPI void
1222 ethumb_client_fdo_set(Ethumb_Client *client, Ethumb_Thumb_FDO_Size s)
1223 {
1224    EINA_SAFETY_ON_NULL_RETURN(client);
1225
1226    client->ethumb_dirty = 1;
1227    ethumb_thumb_fdo_set(client->ethumb, s);
1228 }
1229
1230 EAPI void
1231 ethumb_client_size_set(Ethumb_Client *client, int tw, int th)
1232 {
1233    EINA_SAFETY_ON_NULL_RETURN(client);
1234
1235    client->ethumb_dirty = 1;
1236    ethumb_thumb_size_set(client->ethumb, tw, th);
1237 }
1238
1239 EAPI void
1240 ethumb_client_size_get(const Ethumb_Client *client, int *tw, int *th)
1241 {
1242    if (tw) *tw = 0;
1243    if (th) *th = 0;
1244    EINA_SAFETY_ON_NULL_RETURN(client);
1245
1246    ethumb_thumb_size_get(client->ethumb, tw, th);
1247 }
1248
1249 EAPI void
1250 ethumb_client_format_set(Ethumb_Client *client, Ethumb_Thumb_Format f)
1251 {
1252    EINA_SAFETY_ON_NULL_RETURN(client);
1253
1254    client->ethumb_dirty = 1;
1255    ethumb_thumb_format_set(client->ethumb, f);
1256 }
1257
1258 EAPI Ethumb_Thumb_Format
1259 ethumb_client_format_get(const Ethumb_Client *client)
1260 {
1261    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1262
1263    return ethumb_thumb_format_get(client->ethumb);
1264 }
1265
1266 EAPI void
1267 ethumb_client_aspect_set(Ethumb_Client *client, Ethumb_Thumb_Aspect a)
1268 {
1269    EINA_SAFETY_ON_NULL_RETURN(client);
1270
1271    client->ethumb_dirty = 1;
1272    ethumb_thumb_aspect_set(client->ethumb, a);
1273 }
1274
1275 EAPI Ethumb_Thumb_Aspect
1276 ethumb_client_aspect_get(const Ethumb_Client *client)
1277 {
1278    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1279
1280    return ethumb_thumb_aspect_get(client->ethumb);
1281 }
1282
1283 EAPI void
1284 ethumb_client_crop_align_set(Ethumb_Client *client, float x, float y)
1285 {
1286    EINA_SAFETY_ON_NULL_RETURN(client);
1287
1288    client->ethumb_dirty = 1;
1289    ethumb_thumb_crop_align_set(client->ethumb, x, y);
1290 }
1291
1292 EAPI void
1293 ethumb_client_crop_align_get(const Ethumb_Client *client, float *x, float *y)
1294 {
1295    if (x) *x = 0.0;
1296    if (y) *y = 0.0;
1297    EINA_SAFETY_ON_NULL_RETURN(client);
1298
1299    ethumb_thumb_crop_align_get(client->ethumb, x, y);
1300 }
1301
1302 EAPI void
1303 ethumb_client_quality_set(Ethumb_Client *client, int quality)
1304 {
1305    EINA_SAFETY_ON_NULL_RETURN(client);
1306
1307    ethumb_thumb_quality_set(client->ethumb, quality);
1308 }
1309
1310 EAPI int
1311 ethumb_client_quality_get(const Ethumb_Client *client)
1312 {
1313    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1314
1315    return ethumb_thumb_quality_get(client->ethumb);
1316 }
1317
1318 EAPI void
1319 ethumb_client_compress_set(Ethumb_Client *client, int compress)
1320 {
1321    EINA_SAFETY_ON_NULL_RETURN(client);
1322
1323    ethumb_thumb_compress_set(client->ethumb, compress);
1324 }
1325
1326 EAPI int
1327 ethumb_client_compress_get(const Ethumb_Client *client)
1328 {
1329    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1330
1331    return ethumb_thumb_compress_get(client->ethumb);
1332 }
1333
1334 EAPI Eina_Bool
1335 ethumb_client_frame_set(Ethumb_Client *client, const char *file, const char *group, const char *swallow)
1336 {
1337    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1338
1339    client->ethumb_dirty = 1;
1340    return ethumb_frame_set(client->ethumb, file, group, swallow);
1341 }
1342
1343 EAPI void
1344 ethumb_client_dir_path_set(Ethumb_Client *client, const char *path)
1345 {
1346    EINA_SAFETY_ON_NULL_RETURN(client);
1347
1348    client->ethumb_dirty = 1;
1349    ethumb_thumb_dir_path_set(client->ethumb, path);
1350 }
1351
1352 EAPI const char *
1353 ethumb_client_dir_path_get(const Ethumb_Client *client)
1354 {
1355    EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
1356
1357    return ethumb_thumb_dir_path_get(client->ethumb);
1358 }
1359
1360 EAPI void
1361 ethumb_client_category_set(Ethumb_Client *client, const char *category)
1362 {
1363    EINA_SAFETY_ON_NULL_RETURN(client);
1364
1365    client->ethumb_dirty = 1;
1366    ethumb_thumb_category_set(client->ethumb, category);
1367 }
1368
1369 EAPI const char *
1370 ethumb_client_category_get(const Ethumb_Client *client)
1371 {
1372    EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
1373
1374    return ethumb_thumb_category_get(client->ethumb);
1375 }
1376
1377 EAPI void
1378 ethumb_client_video_time_set(Ethumb_Client *client, float time)
1379 {
1380    EINA_SAFETY_ON_NULL_RETURN(client);
1381
1382    client->ethumb_dirty = 1;
1383    ethumb_video_time_set(client->ethumb, time);
1384 }
1385
1386 EAPI void
1387 ethumb_client_video_start_set(Ethumb_Client *client, float start)
1388 {
1389    EINA_SAFETY_ON_NULL_RETURN(client);
1390
1391    client->ethumb_dirty = 1;
1392    ethumb_video_start_set(client->ethumb, start);
1393 }
1394
1395 EAPI void
1396 ethumb_client_video_interval_set(Ethumb_Client *client, float interval)
1397 {
1398    EINA_SAFETY_ON_NULL_RETURN(client);
1399
1400    client->ethumb_dirty = 1;
1401    ethumb_video_interval_set(client->ethumb, interval);
1402 }
1403
1404 EAPI void
1405 ethumb_client_video_ntimes_set(Ethumb_Client *client, int ntimes)
1406 {
1407    EINA_SAFETY_ON_NULL_RETURN(client);
1408
1409    client->ethumb_dirty = 1;
1410    ethumb_video_ntimes_set(client->ethumb, ntimes);
1411 }
1412
1413 EAPI void
1414 ethumb_client_video_fps_set(Ethumb_Client *client, int fps)
1415 {
1416    EINA_SAFETY_ON_NULL_RETURN(client);
1417
1418    client->ethumb_dirty = 1;
1419    ethumb_video_fps_set(client->ethumb, fps);
1420 }
1421
1422 EAPI void
1423 ethumb_client_document_page_set(Ethumb_Client *client, int page)
1424 {
1425    EINA_SAFETY_ON_NULL_RETURN(client);
1426
1427    client->ethumb_dirty = 1;
1428    ethumb_document_page_set(client->ethumb, page);
1429 }
1430
1431 /**
1432  * Set source file to be thumbnailed.
1433  *
1434  * Calling this function has the side effect of resetting values set
1435  * with ethumb_client_thumb_path_set() or auto-generated with
1436  * ethumb_client_thumb_exists().
1437  *
1438  * @param client the client instance to use. Must @b not be @c
1439  *        NULL. May be pending connected (can be called before @c
1440  *        connected_cb)
1441  * @param path the filesystem path to use. May be @c NULL.
1442  * @param key the extra argument/key inside @a path to read image
1443  *        from. This is only used for formats that allow multiple
1444  *        resources in one file, like EET or Edje (group name).
1445  *
1446  * @return #EINA_TRUE on success, #EINA_FALSE on failure.
1447  */
1448 EAPI Eina_Bool
1449 ethumb_client_file_set(Ethumb_Client *client, const char *path, const char *key)
1450 {
1451    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1452
1453    return ethumb_file_set(client->ethumb, path, key);
1454 }
1455
1456 /**
1457  * Get values set with ethumb_client_file_get()
1458  */
1459 EAPI void
1460 ethumb_client_file_get(Ethumb_Client *client, const char **path, const char **key)
1461 {
1462    if (path) *path = NULL;
1463    if (key) *key = NULL;
1464    EINA_SAFETY_ON_NULL_RETURN(client);
1465
1466    ethumb_file_get(client->ethumb, path, key);
1467 }
1468
1469 /**
1470  * Reset previously set file to @c NULL.
1471  *
1472  * @param client the client instance to use. Must @b not be @c
1473  *        NULL. May be pending connected (can be called before @c
1474  *        connected_cb)
1475  */
1476 EAPI void
1477 ethumb_client_file_free(Ethumb_Client *client)
1478 {
1479    EINA_SAFETY_ON_NULL_RETURN(client);
1480
1481    ethumb_file_free(client->ethumb);
1482 }
1483
1484 /**
1485  * Set a defined path and key to store the thumbnail.
1486  *
1487  * If not explicitly given, the thumbnail path will be auto-generated
1488  * by ethumb_client_thumb_exists() or server using configured
1489  * parameters like size, aspect and category.
1490  *
1491  * Set these to @c NULL to forget previously given values. After
1492  * ethumb_client_file_set() these values will be reset to @c NULL.
1493  */
1494 EAPI void
1495 ethumb_client_thumb_path_set(Ethumb_Client *client, const char *path, const char *key)
1496 {
1497    EINA_SAFETY_ON_NULL_RETURN(client);
1498
1499    ethumb_thumb_path_set(client->ethumb, path, key);
1500 }
1501
1502 /**
1503  * Get the configured thumbnail path.
1504  *
1505  * This returns the value set with ethumb_client_thumb_path_set() or
1506  * auto-generated by ethumb_client_thumb_exists() if it was not set.
1507  *
1508  * @param path where to return configured path. May be @c NULL.  If
1509  *        there was no path configured with
1510  *        ethumb_client_thumb_path_set() and
1511  *        ethumb_client_thumb_exists() was not called, then it will
1512  *        probably return @c NULL. If not @c NULL, then it will be a
1513  *        pointer to a stringshared instance, but @b no references are
1514  *        added (do it with eina_stringshare_ref())!
1515  * @param key where to return configured key. May be @c NULL.  If
1516  *        there was no key configured with
1517  *        ethumb_client_thumb_key_set() and
1518  *        ethumb_client_thumb_exists() was not called, then it will
1519  *        probably return @c NULL. If not @c NULL, then it will be a
1520  *        pointer to a stringshared instance, but @b no references are
1521  *        added (do it with eina_stringshare_ref())!
1522  */
1523 EAPI void
1524 ethumb_client_thumb_path_get(Ethumb_Client *client, const char **path, const char **key)
1525 {
1526    if (path) *path = NULL;
1527    if (key) *key = NULL;
1528    EINA_SAFETY_ON_NULL_RETURN(client);
1529
1530    ethumb_thumb_path_get(client->ethumb, path, key);
1531 }
1532
1533 /**
1534  * Checks whenever file already exists (locally!)
1535  *
1536  * This will check locally (not calling server) if thumbnail already
1537  * exists or not, also calculating the thumbnail path. See
1538  * ethumb_client_thumb_path_get(). Path must be configured with
1539  * ethumb_client_file_set() before using it and the last set file will
1540  * be used!
1541  *
1542  * @param client client instance. Must @b not be @c NULL and client
1543  *        must be configured with ethumb_client_file_set().
1544  *
1545  * @return #EINA_TRUE if it exists, #EINA_FALSE otherwise.
1546  */
1547 EAPI Eina_Bool
1548 ethumb_client_thumb_exists(Ethumb_Client *client)
1549 {
1550    EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1551
1552    return ethumb_exists(client->ethumb);
1553 }
1554
1555 /**
1556  * Ask server to generate thumbnail.
1557  *
1558  * This process is asynchronous and will report back from main loop
1559  * using @a generated_cb. One can cancel this request by calling
1560  * ethumb_client_generate_cancel() or
1561  * ethumb_client_generate_cancel_all(), but not that request might be
1562  * processed by server already and no generated files will be removed
1563  * if that is the case.
1564  *
1565  * This will not check if file already exists, this should be done by
1566  * explicitly calling ethumb_client_thumb_exists(). That is, this
1567  * function will override any existing thumbnail.
1568  *
1569  * @param client client instance. Must @b not be @c NULL and client
1570  *        must be connected (after connected_cb is called).
1571  * @param generated_cb function to report generation results.
1572  * @param data context argument to give back to @a generated_cb. May
1573  *        be @c NULL.
1574  * @param data context to give back to @a generate_cb. May be @c
1575  *        NULL.
1576  * @param free_data used to release @a data resources after @a
1577  *        generated_cb is called or user calls
1578  *        ethumb_client_disconnect().
1579  *
1580  * @return identifier or -1 on error. If -1 is returned (error) then
1581  *         @a free_data is @b not called!
1582  *
1583  * @see ethumb_client_connect()
1584  * @see ethumb_client_file_set()
1585  * @see ethumb_client_thumb_exists()
1586  * @see ethumb_client_generate_cancel()
1587  * @see ethumb_client_generate_cancel_all()
1588  */
1589 EAPI int
1590 ethumb_client_generate(Ethumb_Client *client, Ethumb_Client_Generate_Cb generated_cb, const void *data, Eina_Free_Cb free_data)
1591 {
1592    const char *file, *key, *thumb, *thumb_key;
1593    int id;
1594    EINA_SAFETY_ON_NULL_RETURN_VAL(client, -1);
1595    EINA_SAFETY_ON_FALSE_RETURN_VAL(client->connected, -1);
1596
1597    ethumb_file_get(client->ethumb, &file, &key);
1598    if (!file)
1599      {
1600         ERR("no file set.\n");
1601         return -1;
1602      }
1603
1604    ethumb_thumb_path_get(client->ethumb, &thumb, &thumb_key);
1605
1606    if (client->ethumb_dirty)
1607      ethumb_client_ethumb_setup(client);
1608    id = _ethumb_client_queue_add(client, file, key, thumb, thumb_key,
1609                                  generated_cb, data, free_data);
1610
1611    return id;
1612 }