efl

Форк
0
/
ethumb_client.c 
2369 строк · 71.8 Кб
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 <Eldbus.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 CRI(...)      EINA_LOG_DOM_CRIT(_log_dom, __VA_ARGS__)
97

98
struct _Ethumb_Client
99
{
100
   Ethumb                *ethumb;
101
   int                    id_count;
102
   Ethumb                *old_ethumb_conf;
103
   Eldbus_Connection      *conn;
104
   struct
105
   {
106
      Ethumb_Client_Connect_Cb cb;
107
      void                    *data;
108
      Eina_Free_Cb             free_data;
109
   } connect;
110
   Eina_List             *pending_add;
111
   Eina_List             *pending_remove;
112
   Eina_List             *pending_gen;
113
   Eina_List             *dbus_pending;
114
   struct
115
   {
116
      Ethumb_Client_Die_Cb cb;
117
      void                *data;
118
      Eina_Free_Cb         free_data;
119
   } die;
120
   Eldbus_Proxy           *proxy;
121
   Eldbus_Signal_Handler  *generated_sig_handler;
122
   EINA_REFCOUNT;
123
   Eina_Bool              connected : 1;
124
   Eina_Bool              server_started : 1;
125
   Eina_Bool              invalid : 1;
126
};
127

128
struct _ethumb_pending_add
129
{
130
   int32_t                   id;
131
   const char               *file;
132
   const char               *key;
133
   const char               *thumb;
134
   const char               *thumb_key;
135
   Ethumb_Client_Generate_Cb generated_cb;
136
   void                     *data;
137
   Eina_Free_Cb              free_data;
138
   Eldbus_Pending           *pending_call;
139
   Ethumb_Client            *client;
140
};
141

142
struct _ethumb_pending_remove
143
{
144
   int32_t                          id;
145
   Ethumb_Client_Generate_Cancel_Cb cancel_cb;
146
   void                            *data;
147
   Eina_Free_Cb                     free_data;
148
   Eldbus_Pending                  *pending_call;
149
   Ethumb_Client                   *client;
150
};
151

152
struct _ethumb_pending_gen
153
{
154
   int32_t                   id;
155
   const char               *file;
156
   const char               *key;
157
   const char               *thumb;
158
   const char               *thumb_key;
159
   Ethumb_Client_Generate_Cb generated_cb;
160
   void                     *data;
161
   Eina_Free_Cb              free_data;
162
};
163

164
typedef struct _Ethumb_Async_Exists Ethumb_Async_Exists;
165

166
struct _Ethumb_Async_Exists
167
{
168
   const char   *path;
169

170
   Ethumb       *dup; /* We will work on that one to prevent race and lock */
171

172
   Eina_List    *callbacks;
173
   Ecore_Thread *thread;
174
};
175

176
struct _Ethumb_Exists
177
{
178
   Ethumb_Async_Exists          *parent;
179
   Ethumb_Client                *client;
180
   Ethumb                       *dup; /* We don't want to loose parameters so keep them around */
181

182
   Ethumb_Client_Thumb_Exists_Cb exists_cb;
183
   const void                   *data;
184
};
185

186
static const char _ethumb_dbus_bus_name[] = "org.enlightenment.Ethumb";
187
static const char _ethumb_dbus_interface[] = "org.enlightenment.Ethumb";
188
static const char _ethumb_dbus_objects_interface[] = "org.enlightenment.Ethumb.objects";
189
static const char _ethumb_dbus_path[] = "/org/enlightenment/Ethumb";
190

191
static int _initcount = 0;
192
static Eina_Hash *_exists_request = NULL;
193

194
static void _ethumb_client_generated_cb(void *data, const Eldbus_Message *msg);
195
static void _ethumb_client_call_new(Ethumb_Client *client);
196
static void _ethumb_client_name_owner_changed(void *context, const char *bus, const char *old_id, const char *new_id);
197

198
static void
199
_ethumb_client_free(Ethumb_Client *client)
200
{
201
   void *data;
202
   Eldbus_Object *obj;
203

204
   if (client->invalid)
205
      return;
206

207
   if (client->dbus_pending)
208
     {
209
        Eldbus_Pending *pending;
210
        EINA_LIST_FREE(client->dbus_pending, pending)
211
           eldbus_pending_cancel(pending);
212
     }
213

214
   client->invalid = EINA_TRUE;
215
   EINA_LIST_FREE(client->pending_add, data)
216
     {
217
        struct _ethumb_pending_add *pending = data;
218
        if (pending->pending_call)
219
          {
220
             Eldbus_Pending *call = pending->pending_call;
221

222
             pending->pending_call = NULL;
223
             pending->client = NULL;
224
             eldbus_pending_cancel(call);
225
          }
226
        else
227
          {
228
             pending->client = NULL;
229
             free(pending);
230
          }
231
     }
232

233
   EINA_LIST_FREE(client->pending_gen, data)
234
     {
235
        struct _ethumb_pending_gen *pending = data;
236
        eina_stringshare_del(pending->file);
237
        eina_stringshare_del(pending->key);
238
        eina_stringshare_del(pending->thumb);
239
        eina_stringshare_del(pending->thumb_key);
240
        if (pending->free_data)
241
          pending->free_data(pending->data);
242
        free(pending);
243
     }
244

245
   EINA_LIST_FREE(client->pending_remove, data)
246
     {
247
        struct _ethumb_pending_remove *pending = data;
248
        if (pending->free_data)
249
          pending->free_data(pending->data);
250
        if (pending->pending_call)
251
          {
252
             Eldbus_Pending *call = pending->pending_call;
253

254
             pending->pending_call = NULL;
255
             pending->client = NULL;
256
             eldbus_pending_cancel(call);
257
          }
258
        else
259
          {
260
             pending->client = NULL;
261
             free(pending);
262
          }
263
     }
264

265
   if (client->old_ethumb_conf)
266
     {
267
        ethumb_free(client->old_ethumb_conf);
268
        client->old_ethumb_conf = NULL;
269
     }
270

271
   if (client->ethumb)
272
     {
273
        ethumb_free(client->ethumb);
274
        client->ethumb = NULL;
275
     }
276

277
   if (client->conn)
278
     eldbus_name_owner_changed_callback_del(client->conn,
279
                                            _ethumb_dbus_bus_name,
280
                                            _ethumb_client_name_owner_changed,
281
                                            client);
282
   if (client->generated_sig_handler)
283
     {
284
        eldbus_signal_handler_del(client->generated_sig_handler);
285
        client->generated_sig_handler = NULL;
286
     }
287
   if (client->proxy)
288
     {
289
        obj = eldbus_proxy_object_get(client->proxy);
290
        eldbus_proxy_unref(client->proxy);
291
        client->proxy = NULL;
292
        if (obj) eldbus_object_unref(obj);
293
     }
294
   if (client->conn)
295
     {
296
        eldbus_connection_unref(client->conn);
297
        client->conn = NULL;
298
     }
299

300
   if (client->connect.free_data)
301
     client->connect.free_data(client->connect.data);
302
   if (client->die.free_data)
303
     client->die.free_data(client->die.data);
304

305
   free(client);
306
}
307

308
static void
309
_ethumb_async_delete(void *data)
310
{
311
   Ethumb_Async_Exists *async = data;
312

313
   EINA_SAFETY_ON_FALSE_RETURN(async->callbacks == NULL);
314
   EINA_SAFETY_ON_FALSE_RETURN(async->thread == NULL);
315

316
   ethumb_free(async->dup);
317
   eina_stringshare_del(async->path);
318

319
   free(async);
320
}
321

322
static void
323
_ethumb_client_name_owner_changed(void *context, const char *bus EINA_UNUSED, const char *old_id, const char *new_id)
324
{
325
   Ethumb_Client *client = context;
326

327
   DBG("NameOwnerChanged from=[%s] to=[%s]", old_id, new_id);
328
   if (new_id[0])
329
     {
330
        if (client->connected)
331
          return;
332

333
        client->connected = EINA_TRUE;
334
        INF("Server connected");
335
        _ethumb_client_call_new(client);
336
        return;
337
     }
338
   INF("Server disconnected");
339
   EINA_REFCOUNT_REF(client);
340
   client->connected = EINA_FALSE;
341
   if (client->die.cb)
342
     {
343
        client->die.cb(client->die.data, client);
344
        client->die.cb = NULL;
345
     }
346
   if (client->die.free_data)
347
     {
348
        client->die.free_data(client->die.data);
349
        client->die.free_data = NULL;
350
        client->die.data = NULL;
351
     }
352
   EINA_REFCOUNT_UNREF(client) _ethumb_client_free(client);
353
}
354

355
static void
356
_ethumb_client_report_connect(Ethumb_Client *client, Eina_Bool success)
357
{
358
   if (!client->connect.cb)
359
     {
360
        //ERR("already called?!");
361
        return;
362
     }
363

364
   EINA_REFCOUNT_REF(client);
365
   if (success)
366
     INF("Success connecting to Ethumb server.");
367
   else
368
     ERR("Could not connect to Ethumb server.");
369

370
   client->connect.cb(client->connect.data, client, success);
371
   if (client->connect.free_data)
372
     {
373
        client->connect.free_data(client->connect.data);
374
        client->connect.free_data = NULL;
375
     }
376
   client->connect.cb = NULL;
377
   client->connect.data = NULL;
378
   EINA_REFCOUNT_UNREF(client) _ethumb_client_free(client);
379
}
380

381
static void
382
_ethumb_client_new_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
383
{
384
   const char *errname, *errmsg;
385
   const char *opath;
386
   Ethumb_Client *client = data;
387
   Eldbus_Object *obj;
388

389
   client->dbus_pending = eina_list_remove(client->dbus_pending, pending);
390
   if (eldbus_message_error_get(msg, &errname, &errmsg))
391
     {
392
        ERR("Error: %s %s", errname, errmsg);
393
        _ethumb_client_report_connect(client, 0);
394
        return;
395
     }
396

397
   if (!eldbus_message_arguments_get(msg, "o", &opath))
398
     {
399
        ERR("Error: could not get entry contents");
400
        _ethumb_client_report_connect(client, 0);
401
        return;
402
     }
403

404
   if (client->generated_sig_handler)
405
     {
406
        eldbus_signal_handler_del(client->generated_sig_handler);
407
        client->generated_sig_handler = NULL;
408
     }
409
   if (!client->proxy)
410
     {
411
        obj = eldbus_object_get(client->conn, _ethumb_dbus_bus_name, opath);
412
        client->proxy = eldbus_proxy_get(obj, _ethumb_dbus_objects_interface);
413
     }
414
   client->generated_sig_handler =
415
     eldbus_proxy_signal_handler_add(client->proxy, "generated",
416
                                     _ethumb_client_generated_cb, client);
417
   _ethumb_client_report_connect(client, 1);
418
}
419

420
static void
421
_ethumb_client_call_new(Ethumb_Client *client)
422
{
423
   Eldbus_Message *msg;
424
   Eldbus_Pending *pending;
425
   msg = eldbus_message_method_call_new(_ethumb_dbus_bus_name,
426
                                       _ethumb_dbus_path,
427
                                       _ethumb_dbus_interface, "new");
428
   pending = eldbus_connection_send(client->conn, msg,
429
                                    _ethumb_client_new_cb, client, -1);
430
   if (pending)
431
     client->dbus_pending = eina_list_append(client->dbus_pending, pending);
432
}
433

434
static void
435
_ethumb_client_exists_heavy(void *data, Ecore_Thread *thread EINA_UNUSED)
436
{
437
   Ethumb_Async_Exists *async = data;
438

439
   ethumb_thumb_hash(async->dup);
440
}
441

442
static void
443
_ethumb_client_exists_end(void *data, Ecore_Thread *thread EINA_UNUSED)
444
{
445
   Ethumb_Async_Exists *async = data;
446
   Ethumb_Exists *cb;
447

448
   EINA_LIST_FREE(async->callbacks, cb)
449
     {
450
        Ethumb *tmp;
451

452
        ethumb_thumb_hash_copy(cb->dup, async->dup);
453
        tmp = cb->client->ethumb;
454
        cb->client->ethumb = cb->dup;
455

456
        cb->exists_cb((void *)cb->data,
457
                      cb->client, cb,
458
                      ethumb_exists(cb->client->ethumb));
459

460
        cb->client->ethumb = tmp;
461
        EINA_REFCOUNT_UNREF(cb->client) _ethumb_client_free(cb->client);
462
        ethumb_free(cb->dup);
463
        free(cb);
464
     }
465

466
   async->thread = NULL;
467

468
   eina_hash_del(_exists_request, async->path, async);
469
}
470

471
/**
472
 * @endcond
473
 */
474

475
/**
476
 * @brief Initialize the Ethumb_Client library.
477
 *
478
 * @return 1 or greater on success, 0 on error.
479
 *
480
 * This function sets up all the Ethumb_Client module dependencies. It
481
 * returns 0 on failure (that is, when one of the dependency fails to
482
 * initialize), otherwise it returns the number of times it has
483
 * already been called.
484
 *
485
 * When Ethumb_Client is not used anymore, call
486
 * ethumb_client_shutdown() to shut down the Ethumb_Client library.
487
 *
488
 * @see ethumb_client_shutdown()
489
 * @see ethumb_client_connect()
490
 * @see @ref tutorial_ethumb_client
491
 */
492
EAPI int
493
ethumb_client_init(void)
494
{
495
   if (_initcount)
496
     return ++_initcount;
497

498
   if (!eina_init())
499
     {
500
        fprintf(stderr, "ERROR: Could not initialize log module.\n");
501
        return 0;
502
     }
503
   _log_dom = eina_log_domain_register("ethumb_client", EINA_COLOR_YELLOW);
504
   if (_log_dom < 0)
505
     {
506
        EINA_LOG_ERR("Could not register log domain: ethumb_client");
507
        eina_shutdown();
508
        return 0;
509
     }
510

511
   ethumb_init();
512
   eldbus_init();
513

514
   _exists_request = eina_hash_stringshared_new(_ethumb_async_delete);
515

516
   return ++_initcount;
517
}
518

519
/**
520
 * @brief Shut down the Ethumb_Client library.
521
 *
522
 * @return 0 when everything is shut down, 1 or greater if there are
523
 *         other users of the Ethumb_Client library pending shutdown.
524
 *
525
 * This function shuts down the Ethumb_Client library. It returns 0
526
 * when it has been called the same number of times than
527
 * ethumb_client_init(). In that case it shut down all the
528
 * Ethumb_Client modules dependencies.
529
 *
530
 * Once this function succeeds (that is, @c 0 is returned), you must
531
 * not call any of the Eina function anymore. You must call
532
 * ethumb_client_init() again to use the Ethumb_Client functions
533
 * again.
534
 */
535
EAPI int
536
ethumb_client_shutdown(void)
537
{
538
   _initcount--;
539
   if (_initcount > 0)
540
     return _initcount;
541

542
   /* should find a non racy solution to closing all pending exists request */
543
   eina_hash_free(_exists_request);
544
   _exists_request = NULL;
545

546
   eldbus_shutdown();
547
   ethumb_shutdown();
548
   eina_log_domain_unregister(_log_dom);
549
   _log_dom = -1;
550
   eina_shutdown();
551
   return _initcount;
552
}
553

554
static void
555
_name_start(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED)
556
{
557
   const char *name, *text;
558
   if (eldbus_message_error_get(msg, &name, &text))
559
     {
560
        ERR("Starting ethumb failed %s %s", name, text);
561
        return;
562
     }
563
}
564

565
/**
566
 * Connects to Ethumb server and return the client instance.
567
 *
568
 * This is the "constructor" of Ethumb_Client, where everything
569
 * starts.
570
 *
571
 * If server was down, it is tried to start it using DBus activation,
572
 * then the connection is retried.
573
 *
574
 * This call is asynchronous and will not block, instead it will be in
575
 * "not connected" state until @a connect_cb is called with either
576
 * success or failure. On failure, then no methods should be
577
 * called. On success you're now able to setup and then ask generation
578
 * of thumbnails.
579
 *
580
 * Usually you should listen for server death/disconenction with
581
 * ethumb_client_on_server_die_callback_set().
582
 *
583
 * @param connect_cb function to call to report connection success or
584
 *        failure. Do not call any other ethumb_client method until
585
 *        this function returns. The first received parameter is the
586
 *        given argument @a data. Must @b not be @c NULL. This
587
 *        function will not be called if user explicitly calls
588
 *        ethumb_client_disconnect().
589
 * @param data context to give back to @a connect_cb. May be @c NULL.
590
 * @param free_data function used to release @a data resources, if
591
 *        any. May be @c NULL. If this function exists, it will be
592
 *        called immediately after @a connect_cb is called or if user
593
 *        explicitly calls ethumb_client_disconnect() before such
594
 *        (that is, don't rely on @a data after @a connect_cb was
595
 *        called!)
596
 *
597
 * @return client instance or NULL if failed. If @a connect_cb is
598
 *         missing it returns @c NULL. If it fail for other
599
 *         conditions, @c NULL is also returned and @a connect_cb is
600
 *         called with @c success=EINA_FALSE. The client instance is
601
 *         not ready to be used until @a connect_cb is called.
602
 */
603
EAPI Ethumb_Client *
604
ethumb_client_connect(Ethumb_Client_Connect_Cb connect_cb, const void *data, Eina_Free_Cb free_data)
605
{
606
   Ethumb_Client *eclient;
607

608
   EINA_SAFETY_ON_NULL_RETURN_VAL(connect_cb, NULL);
609

610
   eclient = calloc(1, sizeof(*eclient));
611
   if (!eclient)
612
     {
613
        ERR("could not allocate Ethumb_Client structure.");
614
        goto err;
615
     }
616

617
   eclient->old_ethumb_conf = NULL;
618
   eclient->connect.cb = connect_cb;
619
   eclient->connect.data = (void *)data;
620
   eclient->connect.free_data = free_data;
621

622
   eclient->ethumb = ethumb_new();
623
   if (!eclient->ethumb)
624
     {
625
        ERR("could not create ethumb handler.");
626
        goto ethumb_new_err;
627
     }
628

629
   eclient->conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION);
630
   if (!eclient->conn)
631
     {
632
        ERR("could not connect to session bus.");
633
        goto connection_err;
634
     }
635

636
   if (!eldbus_name_start(eclient->conn, _ethumb_dbus_bus_name, 0, _name_start, NULL))
637
     {
638
        ERR("Failed to start ethumb bus");
639
        goto connection_err;
640
     }
641

642
   eldbus_name_owner_changed_callback_add(eclient->conn, _ethumb_dbus_bus_name,
643
                                         _ethumb_client_name_owner_changed,
644
                                         eclient, EINA_TRUE);
645
   EINA_REFCOUNT_INIT(eclient);
646

647
   return eclient;
648

649
connection_err:
650
   ethumb_free(eclient->ethumb);
651
ethumb_new_err:
652
   free(eclient);
653
err:
654
   connect_cb((void *)data, NULL, EINA_FALSE);
655
   if (free_data)
656
     free_data((void *)data);
657
   return NULL;
658
}
659

660
/**
661
 * Disconnect the client, releasing all client resources.
662
 *
663
 * This is the destructor of Ethumb_Client, after it's disconnected
664
 * the client handle is now gone and should not be used.
665
 *
666
 * @param client client instance to be destroyed. Must @b not be @c
667
 *        NULL.
668
 */
669
EAPI void
670
ethumb_client_disconnect(Ethumb_Client *client)
671
{
672
   EINA_SAFETY_ON_NULL_RETURN(client);
673

674
   EINA_REFCOUNT_UNREF(client) _ethumb_client_free(client);
675
}
676

677
/**
678
 * Sets the callback to report server died.
679
 *
680
 * When server dies there is nothing you can do, just release
681
 * resources with ethumb_client_disconnect() and probably try to
682
 * connect again.
683
 *
684
 * Usually you should set this callback and handle this case, it does
685
 * happen!
686
 *
687
 * @param client the client instance to monitor. Must @b not be @c
688
 *        NULL.
689
 * @param server_die_cb function to call back when server dies. The
690
 *        first parameter will be the argument @a data. May be @c
691
 *        NULL.
692
 * @param data context to give back to @a server_die_cb. May be @c
693
 *        NULL.
694
 * @param free_data used to release @a data resources after @a
695
 *        server_die_cb is called or user calls
696
 *        ethumb_client_disconnect().
697
 */
698
EAPI void
699
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)
700
{
701
   EINA_SAFETY_ON_NULL_RETURN(client);
702

703
   if (client->die.free_data)
704
     client->die.free_data(client->die.data);
705

706
   client->die.cb = server_die_cb;
707
   client->die.data = (void *)data;
708
   client->die.free_data = free_data;
709
}
710

711
/**
712
 * @cond LOCAL
713
 */
714

715
static void
716
_ethumb_client_ethumb_setup_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
717
{
718
   const char *errname, *errmsg;
719
   Eina_Bool result = 0;
720
   Ethumb_Client *client = data;
721

722
   client->dbus_pending = eina_list_remove(client->dbus_pending, pending);
723

724
   if (eldbus_message_error_get(msg, &errname, &errmsg))
725
     {
726
        ERR("Error: %s %s", errname, errmsg);
727
        return;
728
     }
729

730
   if (!eldbus_message_arguments_get(msg, "b", &result))
731
     {
732
        ERR("Error getting arguments");
733
        return;
734
     }
735
   EINA_SAFETY_ON_FALSE_RETURN(result);
736
}
737

738
static const char *
739
_ethumb_client_dbus_get_bytearray(Eldbus_Message_Iter *array)
740
{
741
   int length;
742
   const char *result;
743

744
   if (eldbus_message_iter_fixed_array_get(array, 'y', &result, &length))
745
     return eina_stringshare_add_length(result, length);
746
   else
747
     {
748
        ERR("Not byte array. Signature: %s",
749
            eldbus_message_iter_signature_get(array));
750
        return NULL;
751
     }
752
}
753

754
static void
755
_ethumb_client_dbus_append_bytearray(Eldbus_Message_Iter *parent, const char *string)
756
{
757
   int i, size;
758
   Eldbus_Message_Iter *array;
759

760
   if (!string)
761
     string = "";
762

763
   array = eldbus_message_iter_container_new(parent, 'a', "y");
764
   size = strlen(string) + 1;
765
   for (i = 0; i < size; i++)
766
     eldbus_message_iter_basic_append(array, 'y', string[i]);
767
   eldbus_message_iter_container_close(parent, array);
768
}
769

770
/**
771
 * @endcond
772
 */
773

774
static Eldbus_Message_Iter *
775
_setup_iterator_open(Eldbus_Message_Iter *array, Eldbus_Message_Iter **entry, const char *key, const char *type)
776
{
777
   Eldbus_Message_Iter *variant, *_struct;
778
   eldbus_message_iter_arguments_append(array, "{sv}", &_struct);
779
   eldbus_message_iter_basic_append(_struct, 's', key);
780
   variant = eldbus_message_iter_container_new(_struct, 'v', type);
781

782
   *entry = _struct;
783
   return variant;
784
}
785

786
static void
787
_setup_iterator_close(Eldbus_Message_Iter *array, Eldbus_Message_Iter *entry, Eldbus_Message_Iter *variant)
788
{
789
   eldbus_message_iter_container_close(entry, variant);
790
   eldbus_message_iter_container_close(array, entry);
791
}
792

793
/**
794
 * Send setup to server.
795
 *
796
 * This method is called automatically by ethumb_client_generate() if
797
 * any property was changed. No need to call it manually.
798
 *
799
 * @param client client instance. Must @b not be @c NULL and client
800
 *        must be connected (after connected_cb is called).
801
 */
802
EAPI void
803
ethumb_client_ethumb_setup(Ethumb_Client *client)
804
{
805
   Eldbus_Message *msg;
806
   Eldbus_Message_Iter *array, *main_iter;
807
   Eldbus_Message_Iter *entry, *variant;
808
   Eldbus_Message_Iter *sub_struct;
809
   Ethumb *e = client->ethumb;
810
   int tw, th, format, aspect, orientation, quality, compress;
811
   float cx, cy;
812
   const char *theme_file, *group, *swallow;
813
   const char *directory, *category;
814
   double video_time, video_start, video_interval;
815
   unsigned int video_ntimes, video_fps, document_page;
816
   Eldbus_Pending *pending;
817

818
   EINA_SAFETY_ON_NULL_RETURN(client);
819
   EINA_SAFETY_ON_FALSE_RETURN(client->connected);
820

821
   msg = eldbus_proxy_method_call_new(client->proxy, "ethumb_setup");
822
   main_iter = eldbus_message_iter_get(msg);
823
   eldbus_message_iter_arguments_append(main_iter, "a{sv}", &array);
824

825
   /* starting array elements */
826
   variant = _setup_iterator_open(array, &entry, "size", "(ii)");
827
   eldbus_message_iter_arguments_append(variant, "(ii)", &sub_struct);
828
   ethumb_thumb_size_get(e, &tw, &th);
829
   eldbus_message_iter_arguments_append(sub_struct, "ii", tw, th);
830
   eldbus_message_iter_container_close(variant, sub_struct);
831
   _setup_iterator_close(array, entry, variant);
832

833
   variant = _setup_iterator_open(array, &entry, "format", "i");
834
   format = ethumb_thumb_format_get(e);
835
   eldbus_message_iter_arguments_append(variant, "i", format);
836
   _setup_iterator_close(array, entry, variant);
837

838
   variant = _setup_iterator_open(array, &entry, "aspect", "i");
839
   aspect = ethumb_thumb_aspect_get(e);
840
   eldbus_message_iter_arguments_append(variant, "i", aspect);
841
   _setup_iterator_close(array, entry, variant);
842

843
   variant = _setup_iterator_open(array, &entry, "orientation", "i");
844
   orientation = ethumb_thumb_orientation_get(e);
845
   eldbus_message_iter_arguments_append(variant, "i", orientation);
846
   _setup_iterator_close(array, entry, variant);
847

848
   variant = _setup_iterator_open(array, &entry, "crop", "(dd)");
849
   eldbus_message_iter_arguments_append(variant, "(dd)", &sub_struct);
850
   ethumb_thumb_crop_align_get(e, &cx, &cy);
851
   eldbus_message_iter_arguments_append(sub_struct, "dd", (double)cx, (double)cy);
852
   eldbus_message_iter_container_close(variant, sub_struct);
853
   _setup_iterator_close(array, entry, variant);
854

855
   variant = _setup_iterator_open(array, &entry, "quality", "i");
856
   quality = ethumb_thumb_quality_get(e);
857
   eldbus_message_iter_arguments_append(variant, "i", quality);
858
   _setup_iterator_close(array, entry, variant);
859

860
   variant = _setup_iterator_open(array, &entry, "compress", "i");
861
   compress = ethumb_thumb_compress_get(e);
862
   eldbus_message_iter_arguments_append(variant, "i", compress);
863
   _setup_iterator_close(array, entry, variant);
864

865
   variant = _setup_iterator_open(array, &entry, "frame", "(ayayay)");
866
   eldbus_message_iter_arguments_append(variant, "(ayayay)", &sub_struct);
867
   ethumb_frame_get(e, &theme_file, &group, &swallow);
868
   _ethumb_client_dbus_append_bytearray(sub_struct, theme_file);
869
   _ethumb_client_dbus_append_bytearray(sub_struct, group);
870
   _ethumb_client_dbus_append_bytearray(sub_struct, swallow);
871
   eldbus_message_iter_container_close(variant, sub_struct);
872
   _setup_iterator_close(array, entry, variant);
873

874
   variant = _setup_iterator_open(array, &entry, "directory", "ay");
875
   directory = ethumb_thumb_dir_path_get(e);
876
   _ethumb_client_dbus_append_bytearray(variant, directory);
877
   _setup_iterator_close(array, entry, variant);
878

879
   variant = _setup_iterator_open(array, &entry, "category", "ay");
880
   category = ethumb_thumb_category_get(e);
881
   _ethumb_client_dbus_append_bytearray(variant, category);
882
   _setup_iterator_close(array, entry, variant);
883

884
   variant = _setup_iterator_open(array, &entry, "video_time", "d");
885
   video_time = ethumb_video_time_get(e);
886
   eldbus_message_iter_arguments_append(variant, "d", video_time);
887
   _setup_iterator_close(array, entry, variant);
888

889
   variant = _setup_iterator_open(array, &entry, "video_start", "d");
890
   video_start = ethumb_video_start_get(e);
891
   eldbus_message_iter_arguments_append(variant, "d", video_start);
892
   _setup_iterator_close(array, entry, variant);
893

894
   variant = _setup_iterator_open(array, &entry, "video_interval", "d");
895
   video_interval = ethumb_video_interval_get(e);
896
   eldbus_message_iter_arguments_append(variant, "d", video_interval);
897
   _setup_iterator_close(array, entry, variant);
898

899
   variant = _setup_iterator_open(array, &entry, "video_ntimes", "u");
900
   video_ntimes = ethumb_video_ntimes_get(e);
901
   eldbus_message_iter_arguments_append(variant, "u", video_ntimes);
902
   _setup_iterator_close(array, entry, variant);
903

904
   variant = _setup_iterator_open(array, &entry, "video_fps", "u");
905
   video_fps = ethumb_video_fps_get(e);
906
   eldbus_message_iter_arguments_append(variant, "u", video_fps);
907
   _setup_iterator_close(array, entry, variant);
908

909
   variant = _setup_iterator_open(array, &entry, "document_page", "u");
910
   document_page = ethumb_document_page_get(e);
911
   eldbus_message_iter_arguments_append(variant, "u", document_page);
912
   _setup_iterator_close(array, entry, variant);
913

914
   eldbus_message_iter_container_close(main_iter, array);
915

916
   pending = eldbus_proxy_send(client->proxy, msg,
917
                               _ethumb_client_ethumb_setup_cb, client, -1);
918
   if (pending)
919
     client->dbus_pending = eina_list_append(client->dbus_pending, pending);
920
}
921

922
/**
923
 * @cond LOCAL
924
 */
925
static void
926
_ethumb_client_generated_cb(void *data, const Eldbus_Message *msg)
927
{
928
   int id = -1;
929
   Ethumb_Client *client = data;
930
   Eldbus_Message_Iter *thumb_iter;
931
   Eldbus_Message_Iter *thumb_key_iter;
932
   Eina_Bool success;
933
   int found;
934
   struct _ethumb_pending_gen *pending;
935
   Eina_List *l;
936

937
   if (!client) return;
938
   if (!eldbus_message_arguments_get(msg, "iayayb", &id, &thumb_iter,
939
                                    &thumb_key_iter, &success))
940
     {
941
        ERR("Error getting data from signal.");
942
        return;
943
     }
944

945
   found = 0;
946
   l = client->pending_gen;
947
   while (l)
948
     {
949
        pending = l->data;
950
        if (pending->id == id)
951
          {
952
             found = 1;
953
             break;
954
          }
955
        l = l->next;
956
     }
957

958
   if (found)
959
     {
960
        const char *thumb = _ethumb_client_dbus_get_bytearray(thumb_iter);
961
        const char *thumb_key = _ethumb_client_dbus_get_bytearray(thumb_key_iter);
962

963
        client->pending_gen = eina_list_remove_list(client->pending_gen, l);
964
        if (pending->generated_cb)
965
          pending->generated_cb(pending->data, client, id,
966
                                pending->file, pending->key,
967
                                thumb, thumb_key,
968
                                success);
969
        if (pending->free_data)
970
          pending->free_data(pending->data);
971
        eina_stringshare_del(pending->file);
972
        eina_stringshare_del(pending->key);
973
        eina_stringshare_del(pending->thumb);
974
        eina_stringshare_del(pending->thumb_key);
975
        eina_stringshare_del(thumb);
976
        eina_stringshare_del(thumb_key);
977
        free(pending);
978
     }
979
}
980

981
static void
982
_ethumb_client_queue_add_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *eldbus_pending EINA_UNUSED)
983
{
984
   const char *errname, *errmsg;
985
   int32_t id;
986
   struct _ethumb_pending_add *pending = data;
987
   struct _ethumb_pending_gen *generating;
988
   Ethumb_Client *client = pending->client;
989

990
   pending->pending_call = NULL;
991
   if (!client) goto end;
992
   client->pending_add = eina_list_remove(client->pending_add, pending);
993

994
   if (eldbus_message_error_get(msg, &errname, &errmsg))
995
     {
996
        ERR("Error: %s %s", errname, errmsg);
997
        goto end;
998
     }
999

1000
   if (!eldbus_message_arguments_get(msg, "i", &id))
1001
     {
1002
        ERR("Error getting arguments.");
1003
        goto end;
1004
     }
1005

1006

1007
   generating = calloc(1, sizeof(*generating));
1008
   generating->id = id;
1009
   generating->file = pending->file;
1010
   generating->key = pending->key;
1011
   generating->thumb = pending->thumb;
1012
   generating->thumb_key = pending->thumb_key;
1013
   generating->generated_cb = pending->generated_cb;
1014
   generating->data = pending->data;
1015
   generating->free_data = pending->free_data;
1016
   client->pending_gen = eina_list_append(client->pending_gen, generating);
1017

1018
   free(pending);
1019
   return;
1020

1021
end:
1022
   eina_stringshare_del(pending->file);
1023
   eina_stringshare_del(pending->key);
1024
   eina_stringshare_del(pending->thumb);
1025
   eina_stringshare_del(pending->thumb_key);
1026
   if (pending->free_data)
1027
     pending->free_data(pending->data);
1028
   free(pending);
1029
}
1030

1031
static int
1032
_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)
1033
{
1034
   Eldbus_Message *msg;
1035
   Eldbus_Message_Iter *main_itr;
1036
   struct _ethumb_pending_add *pending;
1037

1038
   pending = calloc(1, sizeof(*pending));
1039
   pending->id = client->id_count;
1040
   pending->file = eina_stringshare_add(file);
1041
   pending->key = eina_stringshare_add(key);
1042
   pending->thumb = eina_stringshare_add(thumb);
1043
   pending->thumb_key = eina_stringshare_add(thumb_key);
1044
   pending->generated_cb = generated_cb;
1045
   pending->data = (void *)data;
1046
   pending->free_data = free_data;
1047
   pending->client = client;
1048

1049
   client->id_count = (client->id_count + 1) % MAX_ID;
1050

1051
   msg = eldbus_proxy_method_call_new(client->proxy, "queue_add");
1052
   main_itr = eldbus_message_iter_get(msg);
1053
   eldbus_message_iter_basic_append(main_itr, 'i', pending->id);
1054
   _ethumb_client_dbus_append_bytearray(main_itr, file);
1055
   _ethumb_client_dbus_append_bytearray(main_itr, key);
1056
   _ethumb_client_dbus_append_bytearray(main_itr, thumb);
1057
   _ethumb_client_dbus_append_bytearray(main_itr, thumb_key);
1058

1059
   client->pending_add = eina_list_append(client->pending_add, pending);
1060

1061
   pending->pending_call = eldbus_proxy_send(client->proxy, msg,
1062
                                             _ethumb_client_queue_add_cb,
1063
                                             pending, -1);
1064

1065
   return pending->id;
1066
}
1067

1068
static void
1069
_ethumb_client_queue_remove_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *eldbus_pending EINA_UNUSED)
1070
{
1071
   Eina_Bool success = EINA_FALSE;
1072
   struct _ethumb_pending_remove *pending = data;
1073
   Ethumb_Client *client = pending->client;
1074
   const char *errname, *errmsg;
1075

1076
   pending->pending_call = NULL;
1077
   if (!client) goto end;
1078
   client->pending_remove = eina_list_remove(client->pending_remove, pending);
1079

1080
   if (eldbus_message_error_get(msg, &errname, &errmsg))
1081
     {
1082
        ERR("Error: %s %s", errname, errmsg);
1083
        goto end;
1084
     }
1085

1086
   if (!eldbus_message_arguments_get(msg, "b", &success))
1087
     {
1088
        ERR("Error getting arguments.");
1089
        goto end;
1090
     }
1091

1092
end:
1093
   if (pending->cancel_cb)
1094
     pending->cancel_cb(pending->data, success);
1095
   if (pending->free_data)
1096
     pending->free_data(pending->data);
1097
   free(pending);
1098
}
1099

1100
/**
1101
 * @endcond
1102
 */
1103

1104
/**
1105
 * Ask server to cancel generation of thumbnail.
1106
 *
1107
 * @param client client instance. Must @b not be @c NULL and client
1108
 *        must be connected (after connected_cb is called).
1109
 * @param id valid id returned by ethumb_client_generate()
1110
 * @param cancel_cb function to report cancellation results.
1111
 * @param data context argument to give back to @a cancel_cb. May be
1112
 *        @c NULL.
1113
 * @param data context to give back to @a cancel_cb. May be @c
1114
 *        NULL.
1115
 * @param free_data used to release @a data resources after @a
1116
 *        cancel_cb is called or user calls
1117
 *        ethumb_client_disconnect().
1118
 */
1119
EAPI void
1120
ethumb_client_generate_cancel(Ethumb_Client *client, int id, Ethumb_Client_Generate_Cancel_Cb cancel_cb, const void *data, Eina_Free_Cb free_data)
1121
{
1122
   struct _ethumb_pending_remove *pending;
1123
   Eina_List *l;
1124
   int found;
1125
   int32_t id32 = id;
1126
   EINA_SAFETY_ON_NULL_RETURN(client);
1127
   EINA_SAFETY_ON_FALSE_RETURN(id >= 0);
1128

1129
   pending = calloc(1, sizeof(*pending));
1130
   pending->id = id;
1131
   pending->cancel_cb = cancel_cb;
1132
   pending->data = (void *)data;
1133
   pending->free_data = free_data;
1134
   pending->client = client;
1135

1136
   pending->pending_call =
1137
     eldbus_proxy_call(client->proxy, "queue_remove",
1138
                       _ethumb_client_queue_remove_cb, pending, -1,
1139
                       "i", pending->id);
1140
   client->pending_remove = eina_list_append(client->pending_remove, pending);
1141

1142
   /*
1143
    * Check if answer was not received yet cancel it
1144
    * callback of queue_add will be called with a error msg
1145
    * and data will be freed
1146
    */
1147
   found = 0;
1148
   l = client->pending_add;
1149
   while (l)
1150
     {
1151
        struct _ethumb_pending_add *pending_add = l->data;
1152
        if (pending_add->id != id32)
1153
          {
1154
             l = l->next;
1155
             continue;
1156
          }
1157
        if (pending_add->pending_call)
1158
          {
1159
             Eldbus_Pending *call = pending_add->pending_call;
1160

1161
             pending_add->pending_call = NULL;
1162
             eldbus_pending_cancel(call);
1163
          }
1164
        found = 1;
1165
        break;
1166
     }
1167

1168
   if (found)
1169
     return;
1170

1171
   //if already received answer only free memory
1172
   l = client->pending_gen;
1173
   while (l)
1174
     {
1175
        struct _ethumb_pending_gen *pending_gen = l->data;
1176
        if (pending_gen->id != id32)
1177
          {
1178
             l = l->next;
1179
             continue;
1180
          }
1181
        client->pending_gen = eina_list_remove_list(client->pending_gen, l);
1182
        eina_stringshare_del(pending_gen->file);
1183
        eina_stringshare_del(pending_gen->key);
1184
        eina_stringshare_del(pending_gen->thumb);
1185
        eina_stringshare_del(pending_gen->thumb_key);
1186
        if (pending_gen->free_data)
1187
          pending_gen->free_data(pending_gen->data);
1188
        free(pending_gen);
1189
        break;
1190
     }
1191
}
1192

1193
/**
1194
 * Ask server to cancel generation of all thumbnails.
1195
 *
1196
 * @param client client instance. Must @b not be @c NULL and client
1197
 *        must be connected (after connected_cb is called).
1198
 *
1199
 * @see ethumb_client_generate_cancel()
1200
 */
1201
EAPI void
1202
ethumb_client_generate_cancel_all(Ethumb_Client *client)
1203
{
1204
   void *data;
1205
   EINA_SAFETY_ON_NULL_RETURN(client);
1206

1207
   EINA_LIST_FREE(client->pending_add, data)
1208
     {
1209
        struct _ethumb_pending_add *pending = data;
1210
        if (pending->pending_call)
1211
          eldbus_pending_cancel(pending->pending_call);
1212
        pending->pending_call = NULL;
1213
        pending->client = NULL;
1214
        free(pending);
1215
     }
1216

1217
   EINA_LIST_FREE(client->pending_gen, data)
1218
     {
1219
        struct _ethumb_pending_gen *pending = data;
1220
        eina_stringshare_del(pending->file);
1221
        eina_stringshare_del(pending->key);
1222
        eina_stringshare_del(pending->thumb);
1223
        eina_stringshare_del(pending->thumb_key);
1224
        if (pending->free_data)
1225
          pending->free_data(pending->data);
1226
        free(pending);
1227
     }
1228

1229
   eldbus_proxy_call(client->proxy, "queue_clear", NULL, NULL, -1, "");
1230
}
1231

1232
/**
1233
 * Configure future requests to use FreeDesktop.Org preset.
1234
 *
1235
 * This is a preset to provide freedesktop.org (fdo) standard
1236
 * compliant thumbnails. That is, files are stored as JPEG under
1237
 * ~/.thumbnails/SIZE, with size being either normal (128x128) or
1238
 * large (256x256).
1239
 *
1240
 * @param client the client instance to use. Must @b not be @c
1241
 *        NULL. May be pending connected (can be called before @c
1242
 *        connected_cb)
1243
 * @param s size identifier, either #ETHUMB_THUMB_NORMAL (0) or
1244
 *        #ETHUMB_THUMB_LARGE (1).
1245
 *
1246
 * @see ethumb_client_size_set()
1247
 * @see ethumb_client_aspect_set()
1248
 * @see ethumb_client_crop_align_set()
1249
 * @see ethumb_client_category_set()
1250
 * @see ethumb_client_dir_path_set()
1251
 */
1252
EAPI void
1253
ethumb_client_fdo_set(Ethumb_Client *client, Ethumb_Thumb_FDO_Size s)
1254
{
1255
   EINA_SAFETY_ON_NULL_RETURN(client);
1256

1257
   if (!client->old_ethumb_conf)
1258
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1259
   ethumb_thumb_fdo_set(client->ethumb, s);
1260
}
1261

1262
/**
1263
 * Configure future request to use custom size.
1264
 *
1265
 * @param client the client instance to use. Must @b not be @c
1266
 *        NULL. May be pending connected (can be called before @c
1267
 *        connected_cb)
1268
 * @param tw width, default is 128.
1269
 * @param th height, default is 128.
1270
 */
1271
EAPI void
1272
ethumb_client_size_set(Ethumb_Client *client, int tw, int th)
1273
{
1274
   EINA_SAFETY_ON_NULL_RETURN(client);
1275

1276
   if (!client->old_ethumb_conf)
1277
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1278
   ethumb_thumb_size_set(client->ethumb, tw, th);
1279
}
1280

1281
/**
1282
 * Retrieve future request to use custom size.
1283
 *
1284
 * @param client the client instance to use. Must @b not be @c
1285
 *        NULL. May be pending connected (can be called before @c
1286
 *        connected_cb)
1287
 * @param tw where to return width. May be @c NULL.
1288
 * @param th where to return height. May be @c NULL.
1289
 */
1290
EAPI void
1291
ethumb_client_size_get(const Ethumb_Client *client, int *tw, int *th)
1292
{
1293
   if (tw) *tw = 0;
1294
   if (th) *th = 0;
1295
   EINA_SAFETY_ON_NULL_RETURN(client);
1296

1297
   ethumb_thumb_size_get(client->ethumb, tw, th);
1298
}
1299

1300
/**
1301
 * Configure format to use for future requests.
1302
 *
1303
 * @param client the client instance to use. Must @b not be @c
1304
 *        NULL. May be pending connected (can be called before @c
1305
 *        connected_cb)
1306
 * @param f format identifier to use, either #ETHUMB_THUMB_FDO (0),
1307
 *        #ETHUMB_THUMB_JPEG (1) or #ETHUMB_THUMB_EET (2). Default is FDO.
1308
 */
1309
EAPI void
1310
ethumb_client_format_set(Ethumb_Client *client, Ethumb_Thumb_Format f)
1311
{
1312
   EINA_SAFETY_ON_NULL_RETURN(client);
1313

1314
   if (!client->old_ethumb_conf)
1315
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1316
   ethumb_thumb_format_set(client->ethumb, f);
1317
}
1318

1319
/**
1320
 * Retrieve format to use for future requests.
1321
 *
1322
 * @param client the client instance to use. Must @b not be @c
1323
 *        NULL. May be pending connected (can be called before @c
1324
 *        connected_cb)
1325
 *
1326
 * @return format identifier to use, either #ETHUMB_THUMB_FDO (0),
1327
 *         #ETHUMB_THUMB_JPEG (1) or #ETHUMB_THUMB_EET (2).
1328
 */
1329
EAPI Ethumb_Thumb_Format
1330
ethumb_client_format_get(const Ethumb_Client *client)
1331
{
1332
   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1333

1334
   return ethumb_thumb_format_get(client->ethumb);
1335
}
1336

1337
/**
1338
 * Configure aspect mode to use.
1339
 *
1340
 * If aspect is kept (#ETHUMB_THUMB_KEEP_ASPECT), then image will be
1341
 * rescaled so the largest dimension is not bigger than it's specified
1342
 * size (see ethumb_client_size_get()) and the other dimension is
1343
 * resized in the same proportion. Example: size is 256x256, image is
1344
 * 1000x500, resulting thumbnail is 256x128.
1345
 *
1346
 * If aspect is ignored (#ETHUMB_THUMB_IGNORE_ASPECT), then image will
1347
 * be distorted to match required thumbnail size. Example: size is
1348
 * 256x256, image is 1000x500, resulting thumbnail is 256x256.
1349
 *
1350
 * If crop is required (#ETHUMB_THUMB_CROP), then image will be
1351
 * cropped so the smallest dimension is not bigger than its specified
1352
 * size (see ethumb_client_size_get()) and the other dimension will
1353
 * overflow, not being visible in the final image. How it will
1354
 * overflow is speficied by ethumb_client_crop_align_set()
1355
 * alignment. Example: size is 256x256, image is 1000x500, crop
1356
 * alignment is 0.5, 0.5, resulting thumbnail is 256x256 with 250
1357
 * pixels from left and 250 pixels from right being lost, that is just
1358
 * the 500x500 central pixels of image will be considered for scaling.
1359
 *
1360
 * @param client the client instance to use. Must @b not be @c
1361
 *        NULL. May be pending connected (can be called before @c
1362
 *        connected_cb)
1363
 * @param a aspect mode identifier, either #ETHUMB_THUMB_KEEP_ASPECT (0),
1364
 *        #ETHUMB_THUMB_IGNORE_ASPECT (1) or #ETHUMB_THUMB_CROP (2).
1365
 */
1366
EAPI void
1367
ethumb_client_aspect_set(Ethumb_Client *client, Ethumb_Thumb_Aspect a)
1368
{
1369
   EINA_SAFETY_ON_NULL_RETURN(client);
1370

1371
   if (!client->old_ethumb_conf)
1372
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1373
   ethumb_thumb_aspect_set(client->ethumb, a);
1374
}
1375

1376
/**
1377
 * Get current aspect in use for requests.
1378
 *
1379
 * @param client the client instance to use. Must @b not be @c
1380
 *        NULL. May be pending connected (can be called before @c
1381
 *        connected_cb)
1382
 *
1383
 * @return aspect in use for future requests.
1384
 */
1385
EAPI Ethumb_Thumb_Aspect
1386
ethumb_client_aspect_get(const Ethumb_Client *client)
1387
{
1388
   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1389

1390
   return ethumb_thumb_aspect_get(client->ethumb);
1391
}
1392

1393
/**
1394
 * Configure orientation to use for future requests.
1395
 *
1396
 * Default value is #ETHUMB_THUMB_ORIENT_ORIGINAL: metadata from the file
1397
 * will be used to orient pixel data.
1398
 *
1399
 * @param client the client instance to use. Must @b not be @c
1400
 *        NULL. May be pending connected (can be called before @c
1401
 *        connected_cb)
1402
 * @param o format identifier to use, either #ETHUMB_THUMB_ORIENT_NONE (0),
1403
 *        #ETHUMB_THUMB_ROTATE_90_CW (1), #ETHUMB_THUMB_ROTATE_180 (2),
1404
 *        #ETHUMB_THUMB_ROTATE_90_CCW (3), #ETHUMB_THUMB_FLIP_HORIZONTAL (4),
1405
 *        #ETHUMB_THUMB_FLIP_VERTICAL (5), #ETHUMB_THUMB_FLIP_TRANSPOSE (6),
1406
 *        #ETHUMB_THUMB_FLIP_TRANSVERSE (7) or #ETHUMB_THUMB_ORIENT_ORIGINAL
1407
 *        (8). Default is ORIGINAL.
1408
 */
1409
EAPI void
1410
ethumb_client_orientation_set(Ethumb_Client *client, Ethumb_Thumb_Orientation o)
1411
{
1412
   EINA_SAFETY_ON_NULL_RETURN(client);
1413

1414
   if (!client->old_ethumb_conf)
1415
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1416
   ethumb_thumb_orientation_set(client->ethumb, o);
1417
}
1418

1419
/**
1420
 * Get current orientation in use for requests.
1421
 *
1422
 * @param client the client instance to use. Must @b not be @c
1423
 *        NULL. May be pending connected (can be called before @c
1424
 *        connected_cb)
1425
 *
1426
 * @return orientation in use for future requests.
1427
 */
1428
EAPI Ethumb_Thumb_Orientation
1429
ethumb_client_orientation_get(const Ethumb_Client *client)
1430
{
1431
   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1432

1433
   return ethumb_thumb_orientation_get(client->ethumb);
1434
}
1435

1436
/**
1437
 * Configure crop alignment in use for future requests.
1438
 *
1439
 * @param client the client instance to use. Must @b not be @c
1440
 *        NULL. May be pending connected (can be called before @c
1441
 *        connected_cb)
1442
 * @param x horizontal alignment. 0.0 means left side will be visible
1443
 *        or right side is being lost. 1.0 means right side will be
1444
 *        visible or left side is being lost. 0.5 means just center is
1445
 *        visible, both sides will be lost.  Default is 0.5.
1446
 * @param y vertical alignment. 0.0 is top visible, 1.0 is bottom
1447
 *        visible, 0.5 is center visible. Default is 0.5
1448
 */
1449
EAPI void
1450
ethumb_client_crop_align_set(Ethumb_Client *client, float x, float y)
1451
{
1452
   EINA_SAFETY_ON_NULL_RETURN(client);
1453

1454
   if (!client->old_ethumb_conf)
1455
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1456
   ethumb_thumb_crop_align_set(client->ethumb, x, y);
1457
}
1458

1459
/**
1460
 * Get current crop alignment in use for requests.
1461
 *
1462
 * @param client the client instance to use. Must @b not be @c
1463
 *        NULL. May be pending connected (can be called before @c
1464
 *        connected_cb)
1465
 * @param x where to return horizontal alignment. May be @c NULL.
1466
 * @param y where to return vertical alignment. May be @c NULL.
1467
 */
1468
EAPI void
1469
ethumb_client_crop_align_get(const Ethumb_Client *client, float *x, float *y)
1470
{
1471
   if (x) *x = 0.0;
1472
   if (y) *y = 0.0;
1473
   EINA_SAFETY_ON_NULL_RETURN(client);
1474

1475
   ethumb_thumb_crop_align_get(client->ethumb, x, y);
1476
}
1477

1478
/**
1479
 * Configure quality to be used in thumbnails.
1480
 *
1481
 * @param client the client instance to use. Must @b not be @c
1482
 *        NULL. May be pending connected (can be called before @c
1483
 *        connected_cb)
1484
 * @param quality value from 0 to 100, default is 80. The effect
1485
 *        depends on the format being used, PNG will not use it.
1486
 */
1487
EAPI void
1488
ethumb_client_quality_set(Ethumb_Client *client, int quality)
1489
{
1490
   EINA_SAFETY_ON_NULL_RETURN(client);
1491

1492
   ethumb_thumb_quality_set(client->ethumb, quality);
1493
}
1494

1495
/**
1496
 * Get quality to be used in thumbnails.
1497
 *
1498
 * @param client the client instance to use. Must @b not be @c
1499
 *        NULL. May be pending connected (can be called before @c
1500
 *        connected_cb)
1501
 *
1502
 * @return quality value from 0 to 100, default is 80. The effect
1503
 *         depends on the format being used, PNG will not use it.
1504
 */
1505
EAPI int
1506
ethumb_client_quality_get(const Ethumb_Client *client)
1507
{
1508
   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1509

1510
   return ethumb_thumb_quality_get(client->ethumb);
1511
}
1512

1513
/**
1514
 * Configure compression level used in requests.
1515
 *
1516
 * @param client the client instance to use. Must @b not be @c
1517
 *        NULL. May be pending connected (can be called before @c
1518
 *        connected_cb)
1519
 * @param compress value from 0 to 9, default is 9. The effect
1520
 *        depends on the format being used, JPEG will not use it.
1521
 */
1522
EAPI void
1523
ethumb_client_compress_set(Ethumb_Client *client, int compress)
1524
{
1525
   EINA_SAFETY_ON_NULL_RETURN(client);
1526

1527
   ethumb_thumb_compress_set(client->ethumb, compress);
1528
}
1529

1530
/**
1531
 * Get compression level used in requests.
1532
 *
1533
 * @param client the client instance to use. Must @b not be @c
1534
 *        NULL. May be pending connected (can be called before @c
1535
 *        connected_cb)
1536
 *
1537
 * @return compress value from 0 to 9, default is 9. The effect
1538
 *         depends on the format being used, JPEG will not use it.
1539
 */
1540
EAPI int
1541
ethumb_client_compress_get(const Ethumb_Client *client)
1542
{
1543
   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1544

1545
   return ethumb_thumb_compress_get(client->ethumb);
1546
}
1547

1548
/**
1549
 * Set frame to apply to future thumbnails.
1550
 *
1551
 * This will create an edje object that will have image swallowed
1552
 * in. This can be used to simulate Polaroid or wood frames in the
1553
 * generated image. Remeber it is bad to modify the original contents
1554
 * of thumbnails, but sometimes it's useful to have it composited and
1555
 * avoid runtime overhead.
1556
 *
1557
 * @param client the client instance to use. Must @b not be @c
1558
 *        NULL. May be pending connected (can be called before @c
1559
 *        connected_cb)
1560
 * @param file file path to edje.
1561
 * @param group group inside edje to use.
1562
 * @param swallow name of swallow part.
1563
 *
1564
 * @return @c EINA_TRUE on success, @c EINA_FALSE on failure.
1565
 */
1566
EAPI Eina_Bool
1567
ethumb_client_frame_set(Ethumb_Client *client, const char *file, const char *group, const char *swallow)
1568
{
1569
   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1570

1571
   if (!client->old_ethumb_conf)
1572
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1573
   return ethumb_frame_set(client->ethumb, file, group, swallow);
1574
}
1575

1576
/**
1577
 * Configure where to store thumbnails in future requests.
1578
 *
1579
 * This value will be used to generate thumbnail paths, that is, it
1580
 * will be used when ethumb_client_thumb_path_set() was not called
1581
 * after last ethumb_client_file_set().
1582
 *
1583
 * Note that this is the base, a category is added to this path as a
1584
 * sub directory. This is not the final directory where files are
1585
 * stored, the thumbnail system will account @b category as well, see
1586
 * ethumb_client_category_set().
1587
 *
1588
 * As other options, this value will only be applied to future
1589
 * requests.
1590
 *
1591
 * @param client the client instance to use. Must @b not be @c
1592
 *        NULL. May be pending connected (can be called before @c
1593
 *        connected_cb)
1594
 * @param path base directory where to store thumbnails. Default is
1595
 *        ~/.thumbnails
1596
 *
1597
 * @see ethumb_client_category_set()
1598
 */
1599
EAPI void
1600
ethumb_client_dir_path_set(Ethumb_Client *client, const char *path)
1601
{
1602
   EINA_SAFETY_ON_NULL_RETURN(client);
1603

1604
   if (!client->old_ethumb_conf)
1605
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1606
   ethumb_thumb_dir_path_set(client->ethumb, path);
1607
}
1608

1609
/**
1610
 * Get base directory path where to store thumbnails.
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
 *
1616
 * @return pointer to internal string with current path. This string
1617
 *         should not be modified or freed.
1618
 *
1619
 * @see ethumb_client_dir_path_set()
1620
 */
1621
EAPI const char *
1622
ethumb_client_dir_path_get(const Ethumb_Client *client)
1623
{
1624
   EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
1625

1626
   return ethumb_thumb_dir_path_get(client->ethumb);
1627
}
1628

1629
/**
1630
 * Category directory to store thumbnails.
1631
 *
1632
 * This value will be used to generate thumbnail paths, that is, it
1633
 * will be used when ethumb_client_thumb_path_set() was not called
1634
 * after last ethumb_client_file_set().
1635
 *
1636
 * This is a sub-directory inside base directory
1637
 * (ethumb_client_dir_path_set()) that creates a namespace to avoid
1638
 * different options resulting in the same file.
1639
 *
1640
 * As other options, this value will only be applied to future
1641
 * requests.
1642
 *
1643
 * @param client the client instance to use. Must @b not be @c
1644
 *        NULL. May be pending connected (can be called before @c
1645
 *        connected_cb)
1646
 * @param category category sub directory to store thumbnail. Default
1647
 *        is either "normal" or "large" for FDO compliant thumbnails
1648
 *        or WIDTHxHEIGHT-ASPECT[-FRAMED]-FORMAT. It can be a string
1649
 *        or @c NULL to use auto generated names.
1650
 *
1651
 * @see ethumb_client_dir_path_set()
1652
 */
1653
EAPI void
1654
ethumb_client_category_set(Ethumb_Client *client, const char *category)
1655
{
1656
   EINA_SAFETY_ON_NULL_RETURN(client);
1657

1658
   if (!client->old_ethumb_conf)
1659
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1660
   ethumb_thumb_category_set(client->ethumb, category);
1661
}
1662

1663
/**
1664
 * Get category sub-directory  where to store thumbnails.
1665
 *
1666
 * @param client the client instance to use. Must @b not be @c
1667
 *        NULL. May be pending connected (can be called before @c
1668
 *        connected_cb)
1669
 *
1670
 * @return pointer to internal string with current path. This string
1671
 *         should not be modified or freed.
1672
 *
1673
 * @see ethumb_client_category_set()
1674
 */
1675
EAPI const char *
1676
ethumb_client_category_get(const Ethumb_Client *client)
1677
{
1678
   EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
1679

1680
   return ethumb_thumb_category_get(client->ethumb);
1681
}
1682

1683
/**
1684
 * Set the video time (duration) in seconds.
1685
 *
1686
 * @param client the client instance to use. Must @b not be @c
1687
 *        NULL. May be pending connected (can be called before @c
1688
 *        connected_cb)
1689
 * @param t duration (in seconds). Defaults to 3 seconds.
1690
 */
1691
EAPI void
1692
ethumb_client_video_time_set(Ethumb_Client *client, float t)
1693
{
1694
   EINA_SAFETY_ON_NULL_RETURN(client);
1695

1696
   if (!client->old_ethumb_conf)
1697
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1698
   ethumb_video_time_set(client->ethumb, t);
1699
}
1700

1701
/**
1702
 * Set initial video position to start thumbnailing, in percentage.
1703
 *
1704
 * This is useful to avoid thumbnailing the company/producer logo or
1705
 * movie opening.
1706
 *
1707
 * @param client the client instance to use. Must @b not be @c
1708
 *        NULL. May be pending connected (can be called before @c
1709
 *        connected_cb)
1710
 * @param start initial video positon to thumbnail, in percentage (0.0
1711
 *        to 1.0, inclusive). Defaults to 10% (0.1).
1712
 */
1713
EAPI void
1714
ethumb_client_video_start_set(Ethumb_Client *client, float start)
1715
{
1716
   EINA_SAFETY_ON_NULL_RETURN(client);
1717
   EINA_SAFETY_ON_FALSE_RETURN(start >= 0.0);
1718
   EINA_SAFETY_ON_FALSE_RETURN(start <= 1.0);
1719

1720
   if (!client->old_ethumb_conf)
1721
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1722
   ethumb_video_start_set(client->ethumb, start);
1723
}
1724

1725
/**
1726
 * Set the video frame interval, in seconds.
1727
 *
1728
 * This is useful for animated thumbnail and will define skip time
1729
 * before going to the next frame. Note that video backends might not
1730
 * be able to precisely skip that amount as it will depend on various
1731
 * factors, including video encoding.
1732
 *
1733
 * Although this seems similar to ethumb_client_video_fps_set(), this
1734
 * one is the time that will be used to seek. The math is simple, for
1735
 * each new frame the video position will be set to:
1736
 * ((video_length * start_time) + (interval * current_frame_number)).
1737
 *
1738
 * @param client the client instance to use. Must @b not be @c
1739
 *        NULL. May be pending connected (can be called before @c
1740
 *        connected_cb)
1741
 * @param interval time between frames, in seconds. Defaults to 0.05
1742
 *        seconds.
1743
 */
1744
EAPI void
1745
ethumb_client_video_interval_set(Ethumb_Client *client, float interval)
1746
{
1747
   EINA_SAFETY_ON_NULL_RETURN(client);
1748

1749
   if (!client->old_ethumb_conf)
1750
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1751
   ethumb_video_interval_set(client->ethumb, interval);
1752
}
1753

1754
/**
1755
 * Set the number of frames to thumbnail.
1756
 *
1757
 * This is useful for animated thumbnail and will define how many
1758
 * frames the generated file will have.
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 ntimes number of times, must be greater than zero.
1764
 *        Defaults to 3.
1765
 */
1766
EAPI void
1767
ethumb_client_video_ntimes_set(Ethumb_Client *client, unsigned int ntimes)
1768
{
1769
   EINA_SAFETY_ON_NULL_RETURN(client);
1770
   EINA_SAFETY_ON_FALSE_RETURN(ntimes > 0);
1771

1772
   if (!client->old_ethumb_conf)
1773
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1774
   ethumb_video_ntimes_set(client->ethumb, ntimes);
1775
}
1776

1777
/**
1778
 * Set the number of frames per second to thumbnail the video.
1779
 *
1780
 * This configures the number of times per seconds the thumbnail will
1781
 * use to create thumbnails.
1782
 *
1783
 * Although this is similar to ethumb_client_video_interval_set(), it
1784
 * is the delay used between calling functions thata generates frames,
1785
 * while the other is the time used to skip inside the video.
1786
 *
1787
 * @param client the client instance to use. Must @b not be @c
1788
 *        NULL. May be pending connected (can be called before @c
1789
 *        connected_cb)
1790
 * @param fps number of frames per second to thumbnail. Must be greater
1791
 *        than zero. Defaults to 10.
1792
 */
1793
EAPI void
1794
ethumb_client_video_fps_set(Ethumb_Client *client, unsigned int fps)
1795
{
1796
   EINA_SAFETY_ON_NULL_RETURN(client);
1797
   EINA_SAFETY_ON_FALSE_RETURN(fps > 0);
1798

1799
   if (!client->old_ethumb_conf)
1800
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1801
   ethumb_video_fps_set(client->ethumb, fps);
1802
}
1803

1804
/**
1805
 * Set the page number to thumbnail in paged documents.
1806
 *
1807
 * @param client the client instance to use. Must @b not be @c
1808
 *        NULL. May be pending connected (can be called before @c
1809
 *        connected_cb)
1810
 * @param page page number, defaults to 0 (first).
1811
 */
1812
EAPI void
1813
ethumb_client_document_page_set(Ethumb_Client *client, unsigned int page)
1814
{
1815
   EINA_SAFETY_ON_NULL_RETURN(client);
1816

1817
   if (!client->old_ethumb_conf)
1818
     client->old_ethumb_conf = ethumb_dup(client->ethumb);
1819
   ethumb_document_page_set(client->ethumb, page);
1820
}
1821

1822
/**
1823
 * Set source file to be thumbnailed.
1824
 *
1825
 * Calling this function has the side effect of resetting values set
1826
 * with ethumb_client_thumb_path_set() or auto-generated with
1827
 * ethumb_client_thumb_exists().
1828
 *
1829
 * @param client the client instance to use. Must @b not be @c
1830
 *        NULL. May be pending connected (can be called before @c
1831
 *        connected_cb)
1832
 * @param path the filesystem path to use. May be @c NULL.
1833
 * @param key the extra argument/key inside @a path to read image
1834
 *        from. This is only used for formats that allow multiple
1835
 *        resources in one file, like EET or Edje (group name).
1836
 *
1837
 * @return @c EINA_TRUE on success, @c EINA_FALSE on failure.
1838
 */
1839
EAPI Eina_Bool
1840
ethumb_client_file_set(Ethumb_Client *client, const char *path, const char *key)
1841
{
1842
   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
1843

1844
   return ethumb_file_set(client->ethumb, path, key);
1845
}
1846

1847
/**
1848
 * Get values set with ethumb_client_file_get()
1849
 *
1850
 * @param client the client instance to use. Must @b not be @c
1851
 *        NULL. May be pending connected (can be called before @c
1852
 *        connected_cb)
1853
 * @param path where to return configured path. May be @c NULL.  If
1854
 *        not @c NULL, then it will be a pointer to a stringshared
1855
 *        instance, but @b no references are added (do it with
1856
 *        eina_stringshare_ref())!
1857
 * @param key where to return configured key. May be @c NULL.If not @c
1858
 *        NULL, then it will be a pointer to a stringshared instance,
1859
 *        but @b no references are added (do it with
1860
 *        eina_stringshare_ref())!
1861
 */
1862
EAPI void
1863
ethumb_client_file_get(Ethumb_Client *client, const char **path, const char **key)
1864
{
1865
   if (path) *path = NULL;
1866
   if (key) *key = NULL;
1867
   EINA_SAFETY_ON_NULL_RETURN(client);
1868

1869
   ethumb_file_get(client->ethumb, path, key);
1870
}
1871

1872
/**
1873
 * Reset previously set file to @c NULL.
1874
 *
1875
 * @param client the client instance to use. Must @b not be @c
1876
 *        NULL. May be pending connected (can be called before @c
1877
 *        connected_cb)
1878
 */
1879
EAPI void
1880
ethumb_client_file_free(Ethumb_Client *client)
1881
{
1882
   EINA_SAFETY_ON_NULL_RETURN(client);
1883

1884
   ethumb_file_free(client->ethumb);
1885
}
1886

1887
/**
1888
 * Set a defined path and key to store the thumbnail.
1889
 *
1890
 * If not explicitly given, the thumbnail path will be auto-generated
1891
 * by ethumb_client_thumb_exists() or server using configured
1892
 * parameters like size, aspect and category.
1893
 *
1894
 * Set these to @c NULL to forget previously given values. After
1895
 * ethumb_client_file_set() these values will be reset to @c NULL.
1896
 *
1897
 * @param client the client instance to use. Must @b not be @c
1898
 *        NULL. May be pending connected (can be called before @c
1899
 *        connected_cb)
1900
 * @param path force generated thumbnail to the exact given path. If
1901
 *        @c NULL, then reverts back to auto-generation.
1902
 * @param key force generated thumbnail to the exact given key. If
1903
 *        @c NULL, then reverts back to auto-generation.
1904
 */
1905
EAPI void
1906
ethumb_client_thumb_path_set(Ethumb_Client *client, const char *path, const char *key)
1907
{
1908
   EINA_SAFETY_ON_NULL_RETURN(client);
1909

1910
   ethumb_thumb_path_set(client->ethumb, path, key);
1911
}
1912

1913
/**
1914
 * Get the configured thumbnail path.
1915
 *
1916
 * This returns the value set with ethumb_client_thumb_path_set() or
1917
 * auto-generated by ethumb_client_thumb_exists() if it was not set.
1918
 *
1919
 * @param client the client instance to use. Must @b not be @c
1920
 *        NULL. May be pending connected (can be called before @c
1921
 *        connected_cb)
1922
 * @param path where to return configured path. May be @c NULL.  If
1923
 *        there was no path configured with
1924
 *        ethumb_client_thumb_path_set() and
1925
 *        ethumb_client_thumb_exists() was not called, then it will
1926
 *        probably return @c NULL. If not @c NULL, then it will be a
1927
 *        pointer to a stringshared instance, but @b no references are
1928
 *        added (do it with eina_stringshare_ref())!
1929
 * @param key where to return configured key. May be @c NULL.  If
1930
 *        there was no key configured with
1931
 *        ethumb_client_thumb_key_set() and
1932
 *        ethumb_client_thumb_exists() was not called, then it will
1933
 *        probably return @c NULL. If not @c NULL, then it will be a
1934
 *        pointer to a stringshared instance, but @b no references are
1935
 *        added (do it with eina_stringshare_ref())!
1936
 */
1937
EAPI void
1938
ethumb_client_thumb_path_get(Ethumb_Client *client, const char **path, const char **key)
1939
{
1940
   if (path) *path = NULL;
1941
   if (key) *key = NULL;
1942
   EINA_SAFETY_ON_NULL_RETURN(client);
1943

1944
   ethumb_thumb_path_get(client->ethumb, path, key);
1945
}
1946

1947
/**
1948
 * Checks whenever file already exists (locally!)
1949
 *
1950
 * This will check locally (not calling server) if thumbnail already
1951
 * exists or not, also calculating the thumbnail path. See
1952
 * ethumb_client_thumb_path_get(). Path must be configured with
1953
 * ethumb_client_file_set() before using it and the last set file will
1954
 * be used!
1955
 *
1956
 * @param client client instance. Must @b not be @c NULL and client
1957
 *        must be configured with ethumb_client_file_set().
1958
 *
1959
 * @return @c NULL on failure, a valid Ethumb_Exists pointer otherwise
1960
 */
1961
EAPI Ethumb_Exists *
1962
ethumb_client_thumb_exists(Ethumb_Client *client, Ethumb_Client_Thumb_Exists_Cb exists_cb, const void *data)
1963
{
1964
   const char *path = NULL;
1965
   Ethumb_Async_Exists *async = NULL;
1966
   Ethumb_Exists *cb = NULL;
1967
   Ecore_Thread *t;
1968

1969
   EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
1970

1971
   ethumb_file_get(client->ethumb, &path, NULL);
1972
   if (!path) goto on_error;
1973

1974
   async = eina_hash_find(_exists_request, path);
1975
   if (!async)
1976
     {
1977
        async = malloc(sizeof (Ethumb_Async_Exists));
1978
        if (!async) goto on_error;
1979

1980
        async->path = eina_stringshare_ref(path);
1981
        async->callbacks = NULL;
1982
        async->dup = ethumb_dup(client->ethumb);
1983

1984
        if (!async->dup) goto on_error;
1985

1986
        cb = malloc(sizeof (Ethumb_Exists));
1987
        if (!cb) goto on_error;
1988

1989
        EINA_REFCOUNT_REF(client);
1990
        cb->client = client;
1991
        cb->dup = ethumb_dup(client->ethumb);
1992
        cb->exists_cb = exists_cb;
1993
        cb->data = data;
1994
        cb->parent = async;
1995

1996
        async->callbacks = eina_list_append(async->callbacks, cb);
1997

1998
        /* spawn a thread here */
1999
        t = ecore_thread_run(_ethumb_client_exists_heavy,
2000
                             _ethumb_client_exists_end,
2001
                             _ethumb_client_exists_end,
2002
                             async);
2003
        if (!t) return NULL;
2004
        async->thread = t;
2005

2006
        eina_hash_direct_add(_exists_request, async->path, async);
2007

2008
        return cb;
2009
     }
2010

2011
   cb = malloc(sizeof (Ethumb_Exists));
2012
   if (!cb)
2013
     {
2014
        async = NULL;
2015
        goto on_error;
2016
     }
2017

2018
   EINA_REFCOUNT_REF(client);
2019
   cb->client = client;
2020
   cb->dup = ethumb_dup(client->ethumb);
2021
   cb->exists_cb = exists_cb;
2022
   cb->data = data;
2023
   cb->parent = async;
2024

2025
   async->callbacks = eina_list_append(async->callbacks, cb);
2026

2027
   return cb;
2028

2029
on_error:
2030
   exists_cb((void *)data, client, NULL, EINA_FALSE);
2031

2032
   if (async)
2033
     {
2034
        eina_stringshare_del(async->path);
2035
        if (async->dup) ethumb_free(async->dup);
2036
        free(async);
2037
     }
2038
   return NULL;
2039
}
2040

2041
/**
2042
 * Cancel an ongoing exists request.
2043
 *
2044
 * @param exists the request to cancel.
2045
 */
2046
EAPI void
2047
ethumb_client_thumb_exists_cancel(Ethumb_Exists *exists)
2048
{
2049
   Ethumb_Async_Exists *async = exists->parent;
2050

2051
   async->callbacks = eina_list_remove(async->callbacks, exists);
2052
   if (eina_list_count(async->callbacks) <= 0)
2053
     ecore_thread_cancel(async->thread);
2054

2055
   ethumb_free(exists->dup);
2056
   EINA_REFCOUNT_UNREF(exists->client) _ethumb_client_free(exists->client);
2057
   free(exists);
2058
}
2059

2060
/**
2061
 * Check if an exists request was cancelled.
2062
 *
2063
 * @param exists the request to check.
2064
 * @result return EINA_TRUE if the request was cancelled.
2065
 */
2066
EAPI Eina_Bool
2067
ethumb_client_thumb_exists_check(Ethumb_Exists *exists)
2068
{
2069
   Ethumb_Async_Exists *async = exists->parent;
2070

2071
   if (!async) return EINA_TRUE;
2072

2073
   if (async->callbacks) return EINA_FALSE;
2074

2075
   return ecore_thread_check(async->thread);
2076
}
2077

2078
/**
2079
 * Ask server to generate thumbnail.
2080
 *
2081
 * This process is asynchronous and will report back from main loop
2082
 * using @a generated_cb. One can cancel this request by calling
2083
 * ethumb_client_generate_cancel() or
2084
 * ethumb_client_generate_cancel_all(), but not that request might be
2085
 * processed by server already and no generated files will be removed
2086
 * if that is the case.
2087
 *
2088
 * This will not check if file already exists, this should be done by
2089
 * explicitly calling ethumb_client_thumb_exists(). That is, this
2090
 * function will override any existing thumbnail.
2091
 *
2092
 * @param client client instance. Must @b not be @c NULL and client
2093
 *        must be connected (after connected_cb is called).
2094
 * @param generated_cb function to report generation results.
2095
 * @param data context argument to give back to @a generated_cb. May
2096
 *        be @c NULL.
2097
 * @param data context to give back to @a generate_cb. May be @c
2098
 *        NULL.
2099
 * @param free_data used to release @a data resources after @a
2100
 *        generated_cb is called or user calls
2101
 *        ethumb_client_disconnect().
2102
 *
2103
 * @return identifier or -1 on error. If -1 is returned (error) then
2104
 *         @a free_data is @b not called!
2105
 *
2106
 * @see ethumb_client_connect()
2107
 * @see ethumb_client_file_set()
2108
 * @see ethumb_client_thumb_exists()
2109
 * @see ethumb_client_generate_cancel()
2110
 * @see ethumb_client_generate_cancel_all()
2111
 */
2112
EAPI int
2113
ethumb_client_generate(Ethumb_Client *client, Ethumb_Client_Generate_Cb generated_cb, const void *data, Eina_Free_Cb free_data)
2114
{
2115
   const char *file, *key, *thumb, *thumb_key;
2116
   int id;
2117
   EINA_SAFETY_ON_NULL_RETURN_VAL(client, -1);
2118
   EINA_SAFETY_ON_FALSE_RETURN_VAL(client->connected, -1);
2119

2120
   ethumb_file_get(client->ethumb, &file, &key);
2121
   if (!file)
2122
     {
2123
        ERR("no file set.");
2124
        return -1;
2125
     }
2126

2127
   ethumb_thumb_path_get(client->ethumb, &thumb, &thumb_key);
2128

2129
   if (client->old_ethumb_conf &&
2130
       ethumb_cmp(client->old_ethumb_conf, client->ethumb))
2131
     {
2132
        ethumb_client_ethumb_setup(client);
2133
        ethumb_free(client->old_ethumb_conf);
2134
        client->old_ethumb_conf = NULL;
2135
     }
2136
   id = _ethumb_client_queue_add(client, file, key, thumb, thumb_key,
2137
                                 generated_cb, data, free_data);
2138
   return id;
2139
}
2140

2141
struct _Ethumb_Client_Async
2142
{
2143
   Ethumb_Exists               *exists;
2144
   Ethumb_Client               *client;
2145
   Ethumb                      *dup;
2146

2147
   Ethumb_Client_Async_Done_Cb  done;
2148
   Ethumb_Client_Async_Error_Cb error;
2149
   const void                  *data;
2150

2151
   int                          id;
2152
};
2153

2154
static Ecore_Idler *idler[2] = { NULL, NULL };
2155
static Eina_List *pending = NULL;
2156
static Eina_List *idle_tasks[2] = { NULL, NULL };
2157

2158
static void
2159
_ethumb_client_async_free(Ethumb_Client_Async *async)
2160
{
2161
   Ethumb_Client *client = async->client;
2162
   ethumb_free(async->dup);
2163
   free(async);
2164
   if (client)
2165
     {
2166
        EINA_REFCOUNT_UNREF(client) _ethumb_client_free(client);
2167
     }
2168
}
2169

2170
static void
2171
_ethumb_client_thumb_finish(void *data,
2172
                            Ethumb_Client *client, int id,
2173
                            const char *file EINA_UNUSED, const char *key EINA_UNUSED,
2174
                            const char *thumb_path, const char *thumb_key,
2175
                            Eina_Bool success)
2176
{
2177
   Ethumb_Client_Async *async = data;
2178

2179
   EINA_SAFETY_ON_FALSE_RETURN(async->id == id);
2180

2181
   if (success)
2182
     {
2183
        async->done(client, thumb_path, thumb_key, (void *)async->data);
2184
     }
2185
   else
2186
     {
2187
        async->error(client, (void *)async->data);
2188
     }
2189

2190
   pending = eina_list_remove(pending, async);
2191
   _ethumb_client_async_free(async);
2192
}
2193

2194
static Eina_Bool
2195
_ethumb_client_thumb_generate_idler(void *data EINA_UNUSED)
2196
{
2197
   Ethumb_Client_Async *async;
2198
   Eina_List *l1, *l2;
2199

2200
   EINA_LIST_FOREACH_SAFE (idle_tasks[1], l1, l2, async)
2201
     {
2202
        Ethumb *tmp;
2203

2204
        idle_tasks[1] = eina_list_remove_list(idle_tasks[1], l1);
2205

2206
        tmp = async->client->ethumb;
2207
        async->client->ethumb = async->dup;
2208

2209
        async->id = ethumb_client_generate(async->client, _ethumb_client_thumb_finish, async, NULL);
2210
        if (async->id == -1)
2211
          {
2212
             async->error(async->client, (void *)async->data);
2213
             async->client->ethumb = tmp;
2214
             _ethumb_client_async_free(async);
2215
             async = NULL;
2216
          }
2217
        else
2218
          {
2219
             async->client->ethumb = tmp;
2220
          }
2221

2222
        if (async)
2223
          pending = eina_list_append(pending, async);
2224

2225
        if (ecore_time_get() - ecore_loop_time_get() > ecore_animator_frametime_get() * 0.5)
2226
          return EINA_TRUE;
2227
     }
2228

2229
   idler[1] = NULL;
2230
   return EINA_FALSE;
2231
}
2232

2233
static void
2234
_ethumb_client_thumb_exists(void *data, Ethumb_Client *client, Ethumb_Exists *request, Eina_Bool exists)
2235
{
2236
   Ethumb_Client_Async *async = data;
2237

2238
   if (request == NULL)
2239
     return;
2240

2241
   EINA_SAFETY_ON_FALSE_RETURN(async->exists == request);
2242

2243
   async->exists = NULL;
2244
   pending = eina_list_remove(pending, async);
2245

2246
   if (exists)
2247
     {
2248
        const char *thumb_path;
2249
        const char *thumb_key;
2250

2251
        ethumb_client_thumb_path_get(client, &thumb_path, &thumb_key);
2252
        async->done(client, thumb_path, thumb_key, (void *)async->data);
2253
        _ethumb_client_async_free(async);
2254
     }
2255
   else
2256
     {
2257
        idle_tasks[1] = eina_list_append(idle_tasks[1], async);
2258

2259
        if (!idler[1])
2260
          idler[1] = ecore_idler_add(_ethumb_client_thumb_generate_idler, NULL);
2261
     }
2262
}
2263

2264
static Eina_Bool
2265
_ethumb_client_thumb_exists_idler(void *data EINA_UNUSED)
2266
{
2267
   Ethumb_Client_Async *async;
2268
   Eina_List *l1, *l2;
2269

2270
   EINA_LIST_FOREACH_SAFE (idle_tasks[0], l1, l2, async)
2271
     {
2272
        Ethumb *tmp;
2273

2274
        idle_tasks[0] = eina_list_remove_list(idle_tasks[0], l1);
2275

2276
        tmp = async->client->ethumb;
2277
        async->client->ethumb = async->dup;
2278

2279
        async->exists = ethumb_client_thumb_exists(async->client, _ethumb_client_thumb_exists, async);
2280
        if (!async->exists)
2281
          {
2282
             async->error(async->client, (void *)async->data);
2283
             async->client->ethumb = tmp;
2284
             _ethumb_client_async_free(async);
2285
             continue;
2286
          }
2287

2288
        async->client->ethumb = tmp;
2289

2290
        pending = eina_list_append(pending, async);
2291

2292
        if (ecore_time_get() - ecore_loop_time_get() > ecore_animator_frametime_get() * 0.5)
2293
          return EINA_TRUE;
2294
     }
2295

2296
   idler[0] = NULL;
2297
   return EINA_FALSE;
2298
}
2299

2300
EAPI Ethumb_Client_Async *
2301
ethumb_client_thumb_async_get(Ethumb_Client *client,
2302
                              Ethumb_Client_Async_Done_Cb done,
2303
                              Ethumb_Client_Async_Error_Cb error,
2304
                              const void *data)
2305
{
2306
   Ethumb_Client_Async *async;
2307

2308
   EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
2309

2310
   async = malloc(sizeof (Ethumb_Client_Async));
2311
   if (!async)
2312
     {
2313
        error(client, (void *)data);
2314
        return NULL;
2315
     }
2316

2317
   EINA_REFCOUNT_REF(client);
2318
   async->client = client;
2319
   async->dup = ethumb_dup(client->ethumb);
2320
   async->done = done;
2321
   async->error = error;
2322
   async->data = data;
2323
   async->exists = NULL;
2324
   async->id = -1;
2325

2326
   idle_tasks[0] = eina_list_append(idle_tasks[0], async);
2327

2328
   if (!idler[0])
2329
     idler[0] = ecore_idler_add(_ethumb_client_thumb_exists_idler, NULL);
2330

2331
   return async;
2332
}
2333

2334
EAPI void
2335
ethumb_client_thumb_async_cancel(Ethumb_Client *client, Ethumb_Client_Async *request)
2336
{
2337
   const char *path;
2338

2339
   EINA_SAFETY_ON_NULL_RETURN(client);
2340
   EINA_SAFETY_ON_NULL_RETURN(request);
2341

2342
   ethumb_file_get(request->dup, &path, NULL);
2343

2344
   if (request->exists)
2345
     {
2346
        ethumb_client_thumb_exists_cancel(request->exists);
2347
        request->exists = NULL;
2348

2349
        pending = eina_list_remove(pending, request);
2350
     }
2351
   else if (request->id != -1)
2352
     {
2353
        Ethumb *tmp = request->client->ethumb;
2354
        request->client->ethumb = request->dup;
2355

2356
        ethumb_client_generate_cancel(request->client, request->id, NULL, NULL, NULL);
2357

2358
        request->client->ethumb = tmp;
2359

2360
        pending = eina_list_remove(pending, request);
2361
     }
2362
   else
2363
     {
2364
        idle_tasks[0] = eina_list_remove(idle_tasks[0], request);
2365
        idle_tasks[1] = eina_list_remove(idle_tasks[1], request);
2366
     }
2367

2368
   _ethumb_client_async_free(request);
2369
}
2370

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.