From c2a555939dfd2d14a3409af027789fc13ef60f46 Mon Sep 17 00:00:00 2001 From: Stephen okra Houston Date: Thu, 24 Mar 2016 12:14:39 -0500 Subject: [PATCH] Add new files for thumbnailing --- COPYING.thumbnailer | 27 ++ src/bin/ephoto_ipc.c | 164 ++++++++ src/bin/ephoto_thumb.c | 438 ++++++++++++++++++++++ src/bin/ephoto_thumbnailer.c | 709 +++++++++++++++++++++++++++++++++++ 4 files changed, 1338 insertions(+) create mode 100644 COPYING.thumbnailer create mode 100644 src/bin/ephoto_ipc.c create mode 100644 src/bin/ephoto_thumb.c create mode 100644 src/bin/ephoto_thumbnailer.c diff --git a/COPYING.thumbnailer b/COPYING.thumbnailer new file mode 100644 index 0000000..87c8026 --- /dev/null +++ b/COPYING.thumbnailer @@ -0,0 +1,27 @@ +Ephoto's thumbnailing code is from Enlightenment using the following license: + +Copyright notice for Enlightenment: + +Copyright (C) 2000-2012 Carsten Haitzler and various contributors (see AUTHORS) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/bin/ephoto_ipc.c b/src/bin/ephoto_ipc.c new file mode 100644 index 0000000..df243ca --- /dev/null +++ b/src/bin/ephoto_ipc.c @@ -0,0 +1,164 @@ +#include "ephoto.h" +#undef ERR +#define ERR(...) do { printf(__VA_ARGS__); putc('\n', stdout); } while(0) + +char *e_ipc_socket = NULL; + +#ifdef USE_IPC +/* local subsystem functions */ +static Eina_Bool _e_ipc_cb_client_del(void *data EINA_UNUSED, int type EINA_UNUSED, void *event); +static Eina_Bool _e_ipc_cb_client_data(void *data EINA_UNUSED, int type EINA_UNUSED, void *event); + +/* local subsystem globals */ +static Ecore_Ipc_Server *_e_ipc_server = NULL; +#endif + +/* externally accessible functions */ +int +e_ipc_init(void) +{ + char buf[4096], buf2[128], buf3[4096]; + char *tmp, *user, *base; + int pid, trynum = 0, id1 = 0; + struct stat st; + + tmp = getenv("TMPDIR"); + if (!tmp) tmp = "/tmp"; + base = tmp; + + tmp = getenv("XDG_RUNTIME_DIR"); + if (tmp) + { + if (stat(tmp, &st) == 0) + { + if ((st.st_uid == getuid()) && + ((st.st_mode & (S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)) == + (S_IRWXU | S_IFDIR))) + base = tmp; + else + ERR("XDG_RUNTIME_DIR of '%s' failed permissions check", tmp); + } + else + ERR("XDG_RUNTIME_DIR of '%s' cannot be accessed", tmp); + } + + tmp = getenv("SD_USER_SOCKETS_DIR"); + if (tmp) + { + if (stat(tmp, &st) == 0) + { + if ((st.st_uid == getuid()) && + ((st.st_mode & (S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)) == + (S_IRWXU | S_IFDIR))) + base = tmp; + else + ERR("SD_USER_SOCKETS_DIR of '%s' failed permissions check", tmp); + } + else + ERR("SD_USER_SOCKETS_DIR of '%s' cannot be accessed", tmp); + } + + user = getenv("USER"); + if (!user) + { + int uidint; + + user = "__unknown__"; + uidint = getuid(); + if (uidint >= 0) + { + snprintf(buf2, sizeof(buf2), "%i", uidint); + user = buf2; + } + } + + setenv("EPHOTO_IPC_SOCKET", "", 1); + + pid = (int)getpid(); + for (trynum = 0; trynum <= 4096; trynum++) + { + snprintf(buf, sizeof(buf), "%s/e-%s@%x", + base, user, id1); + if (!mkdir(buf, S_IRWXU)) + { +#ifdef USE_IPC + snprintf(buf3, sizeof(buf3), "%s/%i", + buf, pid); + _e_ipc_server = ecore_ipc_server_add + (ECORE_IPC_LOCAL_SYSTEM, buf3, 0, NULL); + if (_e_ipc_server) +#endif + { + e_ipc_socket = strdup(ecore_file_file_get(buf)); + break; + } + } + id1 = rand(); + } +#ifdef USE_IPC + if (!_e_ipc_server) + { + ERR("Gave up after 4096 sockets in '%s'. All failed", base); + return 0; + } + setenv("EPHOTO_IPC_SOCKET", "", 1); + setenv("EPHOTO_IPC_SOCKET", buf3, 1); + ecore_event_handler_add(ECORE_IPC_EVENT_CLIENT_DEL, + _e_ipc_cb_client_del, NULL); + ecore_event_handler_add(ECORE_IPC_EVENT_CLIENT_DATA, + _e_ipc_cb_client_data, NULL); +#endif + return 1; +} + +int +e_ipc_shutdown(void) +{ +#ifdef USE_IPC + if (_e_ipc_server) + { + ecore_ipc_server_del(_e_ipc_server); + _e_ipc_server = NULL; + } +#endif + free(e_ipc_socket); + return 1; +} + +#ifdef USE_IPC +/* local subsystem globals */ +static Eina_Bool +_e_ipc_cb_client_del(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) +{ + Ecore_Ipc_Event_Client_Del *e; + + e = event; + if (ecore_ipc_client_server_get(e->client) != _e_ipc_server) + return ECORE_CALLBACK_PASS_ON; + /* delete client sruct */ + e_thumb_client_del(e); + ecore_ipc_client_del(e->client); + return ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +_e_ipc_cb_client_data(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) +{ + Ecore_Ipc_Event_Client_Data *e; + + e = event; + if (ecore_ipc_client_server_get(e->client) != _e_ipc_server) + return ECORE_CALLBACK_PASS_ON; + switch (e->major) + { + case EPHOTO_IPC_DOMAIN_THUMB: + e_thumb_client_data(e); + break; + + default: + break; + } + return ECORE_CALLBACK_PASS_ON; +} + +#endif diff --git a/src/bin/ephoto_thumb.c b/src/bin/ephoto_thumb.c new file mode 100644 index 0000000..3421eb9 --- /dev/null +++ b/src/bin/ephoto_thumb.c @@ -0,0 +1,438 @@ +#include "ephoto.h" + +/* Taken from Enlightenment's Internal Thumbnailer */ + +typedef struct _E_Thumb E_Thumb; + +struct _E_Thumb +{ + int objid; + int w, h; + const char *file; + const char *key; + char *sort_id; + unsigned char queued : 1; + unsigned char busy : 1; + unsigned char done : 1; +}; + +/* local subsystem functions */ +static void _e_thumb_gen_begin(int objid, const char *file, const char *key, int w, int h); +static void _e_thumb_gen_end(int objid); +static void _e_thumb_del_hook(void *data, Evas *e, Evas_Object *obj, void *event_info); +static void _e_thumb_hash_add(int objid, Evas_Object *obj); +static void _e_thumb_hash_del(int objid); +static Evas_Object *_e_thumb_hash_find(int objid); +static void _e_thumb_thumbnailers_kill(void); +static void _e_thumb_thumbnailers_kill_cancel(void); +static Eina_Bool _e_thumb_cb_kill(void *data); +static Eina_Bool _e_thumb_cb_exe_event_del(void *data, int type, void *event); + +/* local subsystem globals */ +static Eina_List *_thumbnailers = NULL; +static Eina_List *_thumbnailers_exe = NULL; +static Eina_List *_thumb_queue = NULL; +static int _objid = 0; +static Eina_Hash *_thumbs = NULL; +static int _pending = 0; +static int _num_thumbnailers = 1; +static Ecore_Event_Handler *_exe_del_handler = NULL; +static Ecore_Timer *_kill_timer = NULL; + +/* externally accessible functions */ +int +e_thumb_init(void) +{ + _exe_del_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, + _e_thumb_cb_exe_event_del, + NULL); + _thumbs = eina_hash_string_superfast_new(NULL); + return 1; +} + +int +e_thumb_shutdown(void) +{ + Ecore_Exe *exe_free; + + _e_thumb_thumbnailers_kill_cancel(); + _e_thumb_cb_kill(NULL); + if (_exe_del_handler) ecore_event_handler_del(_exe_del_handler); + _exe_del_handler = NULL; + _thumbnailers = eina_list_free(_thumbnailers); + EINA_LIST_FREE(_thumbnailers_exe, exe_free) + ecore_exe_free(exe_free); + _thumb_queue = eina_list_free(_thumb_queue); + _objid = 0; + eina_hash_free(_thumbs); + _thumbs = NULL; + _pending = 0; + return 1; +} + +static void +_thumb_preloaded(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) +{ + evas_object_smart_callback_call(data, "e_thumb_gen", NULL); +} + +Evas_Object * +e_thumb_icon_add(Evas *evas) +{ + Evas_Object *obj; + E_Thumb *eth; + + obj = elm_icon_add(evas); + elm_image_fill_outside_set(obj, EINA_TRUE); + evas_object_smart_callback_add(obj, "preloaded", _thumb_preloaded, obj); + _objid++; + eth = calloc(1, sizeof(E_Thumb)); + eth->objid = _objid; + eth->w = 64; + eth->h = 64; + evas_object_data_set(obj, "e_thumbdata", eth); + evas_object_event_callback_add(obj, EVAS_CALLBACK_FREE, + _e_thumb_del_hook, NULL); + _e_thumb_hash_add(eth->objid, obj); + return obj; +} + +void +e_thumb_icon_file_set(Evas_Object *obj, const char *file, const char *key) +{ + E_Thumb *eth; + + eth = evas_object_data_get(obj, "e_thumbdata"); + if (!eth) return; + eina_stringshare_replace(ð->file, file); + eina_stringshare_replace(ð->key, key); + free(eth->sort_id); +} + +void +e_thumb_icon_size_set(Evas_Object *obj, int w, int h) +{ + E_Thumb *eth; + + eth = evas_object_data_get(obj, "e_thumbdata"); + if (!eth) return; + if ((w < 1) || (h < 1)) return; + eth->w = w; + eth->h = h; +} + +void +e_thumb_icon_begin(Evas_Object *obj) +{ + E_Thumb *eth, *eth2; + char buf[4096]; + + eth = evas_object_data_get(obj, "e_thumbdata"); + if (!eth) return; + if (eth->queued) return; + if (eth->busy) return; + if (eth->done) return; + if (!eth->file) return; + if (!_thumbnailers) + { + while ((int)eina_list_count(_thumbnailers_exe) < _num_thumbnailers) + { + Ecore_Exe *exe; + + snprintf(buf, sizeof(buf), "%s/ephoto_thumbnail --nice=1", PACKAGE_DATA_DIR); + exe = ecore_exe_run(buf, NULL); + _thumbnailers_exe = eina_list_append(_thumbnailers_exe, exe); + } + _thumb_queue = eina_list_append(_thumb_queue, eth); + eth->queued = 1; + return; + } + EINA_LIST_FREE(_thumb_queue, eth2) + { + eth2->queued = 0; + eth2->busy = 1; + _pending++; + if (_pending == 1) _e_thumb_thumbnailers_kill_cancel(); + _e_thumb_gen_begin(eth2->objid, eth2->file, eth2->key, eth2->w, eth2->h); + } + eth->busy = 1; + _pending++; + if (_pending == 1) _e_thumb_thumbnailers_kill_cancel(); + _e_thumb_gen_begin(eth->objid, eth->file, eth->key, eth->w, eth->h); +} + +void +e_thumb_icon_end(Evas_Object *obj) +{ + E_Thumb *eth; + + eth = evas_object_data_get(obj, "e_thumbdata"); + if (!eth) return; + if (eth->queued) + { + _thumb_queue = eina_list_remove(_thumb_queue, eth); + eth->queued = 0; + } + if (eth->busy) + { + _e_thumb_gen_end(eth->objid); + eth->busy = 0; + _pending--; + if (_pending == 0) _e_thumb_thumbnailers_kill(); + } +} + +void +e_thumb_icon_rethumb(Evas_Object *obj) +{ + E_Thumb *eth; + eth = evas_object_data_get(obj, "e_thumbdata"); + if (!eth) return; + + if (eth->done) eth->done = 0; + else e_thumb_icon_end(obj); + + e_thumb_icon_begin(obj); +} + +#define A(v) (((v) >> 24) & 0xff) +#define R(v) (((v) >> 16) & 0xff) +#define G(v) (((v) >> 8) & 0xff) +#define B(v) (((v)) & 0xff) +#define PIX(p, x, y) p[((y) << 2) + (x)] +#define PIX2(p, x, y) p[((y) << 1) + (x)] + +static void +_e_thumb_key_load(E_Thumb *eth, const char *icon) +{ + Eet_File *ef; + int size = 0; + + ef = eet_open(icon, EET_FILE_MODE_READ); + if (!ef) return; + eth->sort_id = eet_read(ef, "/thumbnail/sort_id", &size); + if (eth->sort_id) + { + if (size > 0) eth->sort_id[size - 1] = 0; + else + { + free(eth->sort_id); + eth->sort_id = NULL; + } + } + eet_close(ef); +} + +const char * +e_thumb_sort_id_get(Evas_Object *obj) +{ + E_Thumb *eth; + eth = evas_object_data_get(obj, "e_thumbdata"); + if (!eth) return ""; + if (!eth->sort_id) return ""; + return eth->sort_id; +} + +void +e_thumb_client_data(Ecore_Ipc_Event_Client_Data *e) +{ + int objid; + char *icon; + E_Thumb *eth; + Evas_Object *obj; + + if (!eina_list_data_find(_thumbnailers, e->client)) + _thumbnailers = eina_list_prepend(_thumbnailers, e->client); + if (e->minor == 2) + { + objid = e->ref; + icon = e->data; + if ((icon) && (e->size > 1) && (icon[e->size - 1] == 0)) + { + obj = _e_thumb_hash_find(objid); + if (obj) + { + eth = evas_object_data_get(obj, "e_thumbdata"); + if (eth) + { + eth->busy = 0; + _pending--; + eth->done = 1; + if (_pending == 0) _e_thumb_thumbnailers_kill(); + if (ecore_file_exists(icon)) + { + elm_image_file_set(obj, icon, "/thumbnail/data"); + _e_thumb_key_load(eth, icon); + elm_image_preload_disabled_set(obj, EINA_FALSE); + } + evas_object_smart_callback_call(obj, "e_thumb_gen", NULL); + } + } + } + } + if (e->minor == 1) + { + /* hello message */ + EINA_LIST_FREE(_thumb_queue, eth) + { + eth->queued = 0; + eth->busy = 1; + _pending++; + if (_pending == 1) _e_thumb_thumbnailers_kill_cancel(); + _e_thumb_gen_begin(eth->objid, eth->file, eth->key, eth->w, eth->h); + } + } +} + +void +e_thumb_client_del(Ecore_Ipc_Event_Client_Del *e) +{ + if (!eina_list_data_find(_thumbnailers, e->client)) return; + _thumbnailers = eina_list_remove(_thumbnailers, e->client); + if ((!_thumbs) && (!_thumbnailers)) _objid = 0; +} + +/* local subsystem functions */ +static void +_e_thumb_gen_begin(int objid, const char *file, const char *key, int w, int h) +{ + char *buf; + int l1, l2; + Ecore_Ipc_Client *cli; + + /* send thumb req */ + l1 = strlen(file); + l2 = 0; + if (key) l2 = strlen(key); + buf = alloca(l1 + 1 + l2 + 1); + strcpy(buf, file); + if (key) strcpy(buf + l1 + 1, key); + else buf[l1 + 1] = 0; + cli = eina_list_data_get(_thumbnailers); + if (!cli) return; + _thumbnailers = eina_list_remove_list(_thumbnailers, _thumbnailers); + _thumbnailers = eina_list_append(_thumbnailers, cli); + ecore_ipc_client_send(cli, EPHOTO_IPC_DOMAIN_THUMB, 1, objid, w, h, buf, l1 + 1 + l2 + 1); +} + +static void +_e_thumb_gen_end(int objid) +{ + Eina_List *l; + Ecore_Ipc_Client *cli; + + /* send thumb cancel */ + EINA_LIST_FOREACH(_thumbnailers, l, cli) + { + ecore_ipc_client_send(cli, EPHOTO_IPC_DOMAIN_THUMB, 2, objid, 0, 0, NULL, 0); + } +} + +static void +_e_thumb_del_hook(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + E_Thumb *eth; + + eth = evas_object_data_get(obj, "e_thumbdata"); + if (!eth) return; + evas_object_data_del(obj, "e_thumbdata"); + _e_thumb_hash_del(eth->objid); + if (eth->busy) + { + _e_thumb_gen_end(eth->objid); + eth->busy = 0; + _pending--; + if (_pending == 0) _e_thumb_thumbnailers_kill(); + } + if (eth->queued) + _thumb_queue = eina_list_remove(_thumb_queue, eth); + if (eth->file) eina_stringshare_del(eth->file); + if (eth->key) eina_stringshare_del(eth->key); + free(eth->sort_id); + free(eth); +} + +static void +_e_thumb_hash_add(int objid, Evas_Object *obj) +{ + char buf[32]; + + snprintf(buf, sizeof(buf), "%i", objid); + eina_hash_add(_thumbs, buf, obj); +} + +static void +_e_thumb_hash_del(int objid) +{ + char buf[32]; + + snprintf(buf, sizeof(buf), "%i", objid); + if (_thumbs) eina_hash_del(_thumbs, buf, NULL); + if ((!_thumbs) && (!_thumbnailers)) _objid = 0; +} + +static Evas_Object * +_e_thumb_hash_find(int objid) +{ + char buf[32]; + + snprintf(buf, sizeof(buf), "%i", objid); + return eina_hash_find(_thumbs, buf); +} + +static void +_e_thumb_thumbnailers_kill(void) +{ + if (_kill_timer) ecore_timer_del(_kill_timer); + _kill_timer = ecore_timer_add(1.0, _e_thumb_cb_kill, NULL); +} + +static void +_e_thumb_thumbnailers_kill_cancel(void) +{ + if (_kill_timer) ecore_timer_del(_kill_timer); + _kill_timer = NULL; +} + +static Eina_Bool +_e_thumb_cb_kill(void *data EINA_UNUSED) +{ + Eina_List *l; + Ecore_Exe *exe; + + EINA_LIST_FOREACH(_thumbnailers_exe, l, exe) + ecore_exe_terminate(exe); + _kill_timer = NULL; + return ECORE_CALLBACK_DONE; +} + +static Eina_Bool +_e_thumb_cb_exe_event_del(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) +{ + Ecore_Exe_Event_Del *ev; + Ecore_Exe *exe; + Eina_List *l; + + ev = event; + EINA_LIST_FOREACH(_thumbnailers_exe, l, exe) + { + if (exe == ev->exe) + { + _thumbnailers_exe = eina_list_remove_list(_thumbnailers_exe, l); + break; + } + } + if ((!_thumbnailers_exe) && (_thumb_queue)) + { + while ((int)eina_list_count(_thumbnailers_exe) < _num_thumbnailers) + { + Ecore_Exe *exe_thumb; + char buf[4096]; + + snprintf(buf, sizeof(buf), "%s/ephoto_thumbnail --nice=1", PACKAGE_DATA_DIR); + exe_thumb = ecore_exe_run(buf, NULL); + _thumbnailers_exe = eina_list_append(_thumbnailers_exe, exe_thumb); + } + } + return ECORE_CALLBACK_PASS_ON; +} diff --git a/src/bin/ephoto_thumbnailer.c b/src/bin/ephoto_thumbnailer.c new file mode 100644 index 0000000..417c332 --- /dev/null +++ b/src/bin/ephoto_thumbnailer.c @@ -0,0 +1,709 @@ +#include "ephoto.h" +#ifdef HAVE_ARPA_INET_H +# include +#endif + +#ifdef HAVE_NETINET_IN_H +# include +#endif + +#define SHSH(n, v) ((((v) << (n)) & 0xffffffff) | ((v) >> (32 - (n)))) + +typedef struct _E_Thumb E_Thumb; + +struct _E_Thumb +{ + int objid; + int w, h; + char *file; + char *key; +}; + +/* local subsystem functions */ +static int _e_ipc_init(void); +static Eina_Bool _e_ipc_cb_server_add(void *data, + int type, + void *event); +static Eina_Bool _e_ipc_cb_server_del(void *data, + int type, + void *event); +static Eina_Bool _e_ipc_cb_server_data(void *data, + int type, + void *event); +static Eina_Bool _e_cb_timer(void *data); +static void _e_thumb_generate(E_Thumb *eth); +static char *_e_thumb_file_id(char *file, + char *key); + +/* local subsystem globals */ +static Ecore_Ipc_Server *_e_ipc_server = NULL; +static Eina_List *_thumblist = NULL; +static Ecore_Timer *_timer = NULL; +static char _thumbdir[4096] = ""; + +/* externally accessible functions */ +int +main(int argc, + char **argv) +{ + int i; + + for (i = 1; i < argc; i++) + { + if ((!strcmp(argv[i], "-h")) || + (!strcmp(argv[i], "-help")) || + (!strcmp(argv[i], "--help"))) + { + printf( + "This is an internal tool for Ephoto.\n" + "do not use it.\n" + ); + exit(0); + } + else if (!strncmp(argv[i], "--nice=", 7)) + { + const char *val; + + val = argv[i] + 7; + if (*val) + { + if (nice(atoi(val)) < 0) perror("nice"); + } + } + } + + ecore_app_no_system_modules(); + ecore_init(); + ecore_app_args_set(argc, (const char **)argv); + eet_init(); + evas_init(); + ecore_evas_init(); + edje_init(); + ecore_file_init(); + ecore_ipc_init(); + + snprintf(_thumbdir, PATH_MAX, "%s/.config/ephoto/thumbnails", getenv("HOME")); + ecore_file_mkpath(_thumbdir); + + if (_e_ipc_init()) ecore_main_loop_begin(); + + if (_e_ipc_server) + { + ecore_ipc_server_del(_e_ipc_server); + _e_ipc_server = NULL; + } + + ecore_ipc_shutdown(); + ecore_file_shutdown(); + ecore_evas_shutdown(); + edje_shutdown(); + evas_shutdown(); + eet_shutdown(); + ecore_shutdown(); + + return 0; +} + +/* local subsystem functions */ +static int +_e_ipc_init(void) +{ + char *sdir; + + sdir = getenv("EPHOTO_IPC_SOCKET"); + if (!sdir) + { + printf("The EPHOTO_IPC_SOCKET environment variable is not set. This is\n" + "exported by Enlightenment to all processes it launches.\n" + "This environment variable must be set and must point to\n" + "Enlightenment's IPC socket file (minus port number).\n"); + return 0; + } + _e_ipc_server = ecore_ipc_server_connect(ECORE_IPC_LOCAL_SYSTEM, sdir, 0, NULL); + if (!_e_ipc_server) return 0; + + ecore_event_handler_add(ECORE_IPC_EVENT_SERVER_ADD, _e_ipc_cb_server_add, NULL); + ecore_event_handler_add(ECORE_IPC_EVENT_SERVER_DEL, _e_ipc_cb_server_del, NULL); + ecore_event_handler_add(ECORE_IPC_EVENT_SERVER_DATA, _e_ipc_cb_server_data, NULL); + + return 1; +} + +static Eina_Bool +_e_ipc_cb_server_add(void *data EINA_UNUSED, + int type EINA_UNUSED, + void *event) +{ + Ecore_Ipc_Event_Server_Add *e; + + e = event; + ecore_ipc_server_send(e->server, + EPHOTO_IPC_DOMAIN_THUMB, + 1 /*hello*/, + 0, 0, 0, NULL, 0); /* send hello */ + return ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +_e_ipc_cb_server_del(void *data EINA_UNUSED, + int type EINA_UNUSED, + void *event EINA_UNUSED) +{ + /* quit now */ + ecore_main_loop_quit(); + return ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +_e_ipc_cb_server_data(void *data EINA_UNUSED, + int type EINA_UNUSED, + void *event) +{ + Ecore_Ipc_Event_Server_Data *e; + E_Thumb *eth; + Eina_List *l; + char *file = NULL; + char *key = NULL; + + e = event; + if (e->major != EPHOTO_IPC_DOMAIN_THUMB) return ECORE_CALLBACK_PASS_ON; + switch (e->minor) + { + case 1: + if (e->data) + { + /* begin thumb */ + /* don't check stuff. since this connects TO E it is connecting */ + /* TO a trusted process that WILL send this message properly */ + /* formatted. if the thumbnailer dies anyway - it's not a big loss */ + /* but it is a sign of a bug in e formatting messages maybe */ + file = e->data; + key = file + strlen(file) + 1; + if (!key[0]) key = NULL; + eth = calloc(1, sizeof(E_Thumb)); + if (eth) + { + eth->objid = e->ref; + eth->w = e->ref_to; + eth->h = e->response; + eth->file = strdup(file); + if (key) eth->key = strdup(key); + _thumblist = eina_list_append(_thumblist, eth); + if (!_timer) _timer = ecore_timer_add(0.001, _e_cb_timer, NULL); + } + } + break; + + case 2: + /* end thumb */ + EINA_LIST_FOREACH(_thumblist, l, eth) + { + if (eth->objid == e->ref) + { + _thumblist = eina_list_remove_list(_thumblist, l); + free(eth->file); + free(eth->key); + free(eth); + break; + } + } + break; + + case 3: + /* quit now */ + ecore_main_loop_quit(); + break; + + default: + break; + } + return ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +_e_cb_timer(void *data EINA_UNUSED) +{ + E_Thumb *eth; + /* + Eina_List *del_list = NULL, *l; + */ + + /* take thumb at head of list */ + if (_thumblist) + { + eth = eina_list_data_get(_thumblist); + _thumblist = eina_list_remove_list(_thumblist, _thumblist); + _e_thumb_generate(eth); + free(eth->file); + free(eth->key); + free(eth); + + if (_thumblist) _timer = ecore_timer_add(0.01, _e_cb_timer, NULL); + else _timer = NULL; + } + else + _timer = NULL; + return ECORE_CALLBACK_CANCEL; +} + +typedef struct _Color Color; + +struct _Color +{ + Color *closest; + int closest_dist; + int use; + unsigned char r, g, b; +}; + +static void +_e_thumb_generate(E_Thumb *eth) +{ + char buf[4096], dbuf[4096], *id, *td, *ext = NULL; + Evas *evas = NULL, *evas_im = NULL; + Ecore_Evas *ee = NULL, *ee_im = NULL; + Evas_Object *im = NULL, *edje = NULL; + Eet_File *ef = NULL; + int iw, ih, alpha, ww, hh; + const unsigned int *data = NULL; + time_t mtime_orig, mtime_thumb; + + id = _e_thumb_file_id(eth->file, eth->key); + if (!id) return; + + td = strdup(id); + if (!td) + { + free(id); + return; + } + td[2] = 0; + + snprintf(dbuf, sizeof(dbuf), "%s/%s", _thumbdir, td); + snprintf(buf, sizeof(buf), "%s/%s/%s-%ix%i.thm", + _thumbdir, td, id + 2, eth->w, eth->h); + free(id); + free(td); + + mtime_orig = ecore_file_mod_time(eth->file); + mtime_thumb = ecore_file_mod_time(buf); + while (mtime_thumb <= mtime_orig) + { + unsigned int *data1; + Eina_Bool sortkey; + Evas_Object *im2, *bg; + + im = NULL; + im2 = NULL; + bg = NULL; + + ecore_file_mkdir(dbuf); + + edje_file_cache_set(0); + edje_collection_cache_set(0); + ee = ecore_evas_buffer_new(1, 1); + evas = ecore_evas_get(ee); + evas_image_cache_set(evas, 0); + evas_font_cache_set(evas, 0); + ww = 0; + hh = 0; + alpha = 1; + ext = strrchr(eth->file, '.'); + + sortkey = EINA_FALSE; + + if ((ext) && (eth->key) && + ((!strcasecmp(ext, ".edj")) || + (!strcasecmp(ext, ".eap")))) + { + ww = eth->w; + hh = eth->h; + im = ecore_evas_object_image_new(ee); + ee_im = evas_object_data_get(im, "Ecore_Evas"); + evas_im = ecore_evas_get(ee_im); + evas_image_cache_set(evas_im, 0); + evas_font_cache_set(evas_im, 0); + evas_object_image_size_set(im, ww * 4, hh * 4); + evas_object_image_fill_set(im, 0, 0, ww, hh); + edje = edje_object_add(evas_im); + if ((eth->key) && + ((!strcmp(eth->key, "e/desktop/background")) || + (!strcmp(eth->key, "e/init/splash")))) + alpha = 0; + if (edje_object_file_set(edje, eth->file, eth->key)) + { + evas_object_move(edje, 0, 0); + evas_object_resize(edje, ww * 4, hh * 4); + evas_object_show(edje); + } + evas_object_move(im, 0, 0); + evas_object_resize(im, ww, hh); + sortkey = EINA_TRUE; + } + else if ((ext) && + ((!strcasecmp(ext, ".ttf")) || + (!strcasecmp(ext, ".pcf")) || + (!strcasecmp(ext, ".bdf")) || + (!strcasecmp(ext, ".ttx")) || + (!strcasecmp(ext, ".pfa")) || + (!strcasecmp(ext, ".pfb")) || + (!strcasecmp(ext, ".afm")) || + (!strcasecmp(ext, ".sfd")) || + (!strcasecmp(ext, ".snf")) || + (!strcasecmp(ext, ".otf")) || + (!strcasecmp(ext, ".psf")) || + (!strcasecmp(ext, ".ttc")) || + (!strcasecmp(ext, ".ttx")) || + (!strcasecmp(ext, ".gsf")) || + (!strcasecmp(ext, ".spd")) + )) + { + Evas_Coord tx = 0, ty = 0, tw = 0, th = 0; + ww = eth->w; + hh = eth->h; + alpha = 0; + + bg = evas_object_rectangle_add(evas); + evas_object_color_set(bg, 96, 96, 96, 255); + evas_object_move(bg, 0, 0); + evas_object_resize(bg, ww, hh); + evas_object_show(bg); + + im = evas_object_text_add(evas); + evas_object_text_font_set(im, eth->file, hh / 4); + evas_object_color_set(im, 192, 192, 192, 255); + evas_object_text_ellipsis_set(im, 0.0); + evas_object_text_text_set(im, "ABCabc"); + evas_object_geometry_get(im, NULL, NULL, &tw, &th); + if (tw > ww) tw = ww; + tx = 0 + ((ww - tw) / 2); + ty = 0 + (((hh / 2) - th) / 2); + evas_object_move(im, tx, ty); + evas_object_resize(im, tw, th); + evas_object_show(im); + + im2 = evas_object_text_add(evas); + evas_object_text_font_set(im2, eth->file, hh / 4); + evas_object_color_set(im2, 255, 255, 255, 255); + evas_object_text_ellipsis_set(im2, 0.0); + evas_object_text_text_set(im2, "123!@?"); + evas_object_geometry_get(im2, NULL, NULL, &tw, &th); + if (tw > ww) tw = ww; + tx = 0 + ((ww - tw) / 2); + ty = (hh / 2) + (((hh / 2) - th) / 2); + evas_object_move(im2, tx, ty); + evas_object_resize(im2, tw, th); + evas_object_show(im2); + } + else if (evas_object_image_extension_can_load_get(ext)) + { + im = evas_object_image_add(evas); + evas_object_image_load_orientation_set(im, EINA_TRUE); + evas_object_image_load_size_set(im, eth->w, eth->h); + evas_object_image_file_set(im, eth->file, NULL); + iw = 0; ih = 0; + evas_object_image_size_get(im, &iw, &ih); + alpha = evas_object_image_alpha_get(im); + if ((iw > 0) && (ih > 0)) + { + ww = eth->w; + hh = (eth->w * ih) / iw; + if (hh > eth->h) + { + hh = eth->h; + ww = (eth->h * iw) / ih; + } + evas_object_image_fill_set(im, 0, 0, ww, hh); + } + evas_object_move(im, 0, 0); + evas_object_resize(im, ww, hh); + sortkey = EINA_TRUE; + } + else + goto end; + + ecore_evas_alpha_set(ee, alpha); + ecore_evas_resize(ee, ww, hh); + evas_object_show(im); + if (ww <= 0) goto end; + data = ecore_evas_buffer_pixels_get(ee); + if (!data) goto end; + ef = eet_open(buf, EET_FILE_MODE_WRITE); + if (!ef) goto end; + eet_write(ef, "/thumbnail/orig_file", + eth->file, strlen(eth->file), 1); + if (eth->key) + eet_write(ef, "/thumbnail/orig_key", + eth->key, strlen(eth->key), 1); + eet_data_image_write(ef, "/thumbnail/data", + (void *)data, ww, hh, alpha, + 0, 91, 1); + if (sortkey) + { + ww = 4; hh = 4; + evas_object_image_fill_set(im, 0, 0, ww, hh); + evas_object_resize(im, ww, hh); + ecore_evas_resize(ee, ww, hh); + data = ecore_evas_buffer_pixels_get(ee); + if (!data) goto end; + + data1 = malloc(ww * hh * sizeof(unsigned int)); + memcpy(data1, data, ww * hh * sizeof(unsigned int)); + ww = 2; hh = 2; + evas_object_image_fill_set(im, 0, 0, ww, hh); + evas_object_resize(im, ww, hh); + ecore_evas_resize(ee, ww, hh); + data = ecore_evas_buffer_pixels_get(ee); + if (data) + { + unsigned int *data2; + + data2 = malloc(ww * hh * sizeof(unsigned int)); + memcpy(data2, data, ww * hh * sizeof(unsigned int)); + ww = 1; hh = 1; + evas_object_image_fill_set(im, 0, 0, ww, hh); + evas_object_resize(im, ww, hh); + ecore_evas_resize(ee, ww, hh); + data = ecore_evas_buffer_pixels_get(ee); + if (data) + { + unsigned int *data3; + unsigned char id2[(21 * 4) + 1]; + int n, i; + int hi, si, vi; + float h, s, v; + const int pat2[4] = + { + 0, 3, 1, 2 + }; + const int pat1[16] = + { + 5, 10, 6, 9, + 0, 15, 3, 12, + 1, 14, 7, 8, + 4, 11, 2, 13 + }; + + /* ww = hh = 1 here */ + data3 = malloc(sizeof(unsigned int)); + memcpy(data3, data, sizeof(unsigned int)); + // sort_id + n = 0; +#define A(v) (((v) >> 24) & 0xff) +#define R(v) (((v) >> 16) & 0xff) +#define G(v) (((v) >> 8) & 0xff) +#define B(v) (((v)) & 0xff) +#define HSV(p) \ + evas_color_rgb_to_hsv(R(p), G(p), B(p), &h, &s, &v); \ + hi = 20 * (h / 360.0); \ + si = 20 * s; \ + vi = 20 * v; \ + if (si < 2) hi = 25; +#define SAVEHSV(h, s, v) \ + id2[n++] = 'a' + h; \ + id2[n++] = 'a' + v; \ + id2[n++] = 'a' + s; +#define SAVEX(x) \ + id2[n++] = 'a' + x; +#if 0 + HSV(data3[0]); + SAVEHSV(hi, si, vi); + for (i = 0; i < 4; i++) + { + HSV(data2[pat2[i]]); + SAVEHSV(hi, si, vi); + } + for (i = 0; i < 16; i++) + { + HSV(data1[pat1[i]]); + SAVEHSV(hi, si, vi); + } +#else + HSV(data3[0]); + SAVEX(hi); + for (i = 0; i < 4; i++) + { + HSV(data2[pat2[i]]); + SAVEX(hi); + } + for (i = 0; i < 16; i++) + { + HSV(data1[pat1[i]]); + SAVEX(hi); + } + HSV(data3[0]); + SAVEX(vi); + for (i = 0; i < 4; i++) + { + HSV(data2[pat2[i]]); + SAVEX(vi); + } + for (i = 0; i < 16; i++) + { + HSV(data1[pat1[i]]); + SAVEX(vi); + } + HSV(data3[0]); + SAVEX(si); + for (i = 0; i < 4; i++) + { + HSV(data2[pat2[i]]); + SAVEX(si); + } + for (i = 0; i < 16; i++) + { + HSV(data1[pat1[i]]); + SAVEX(si); + } +#endif + id2[n++] = 0; + eet_write(ef, "/thumbnail/sort_id", id2, n, 1); + free(data3); + } + free(data2); + } + free(data1); + } +end: + if (ef) eet_close(ef); + + /* will free all */ + if (edje) evas_object_del(edje); + if (ee_im) ecore_evas_free(ee_im); + else if (im) evas_object_del(im); + if (im2) evas_object_del(im2); + if (bg) evas_object_del(bg); + ecore_evas_free(ee); + eet_clearcache(); + break; + } + /* send back path to thumb */ + ecore_ipc_server_send(_e_ipc_server, EPHOTO_IPC_DOMAIN_THUMB, 2, eth->objid, 0, 0, buf, strlen(buf) + 1); +} + +static int +e_sha1_sum(unsigned char *data, int size, unsigned char *dst) +{ + unsigned int digest[5], word[80], wa, wb, wc, wd, we, t; + unsigned char buf[64], *d; + int idx, left, i; + const unsigned int magic[4] = + { + 0x5a827999, + 0x6ed9eba1, + 0x8f1bbcdc, + 0xca62c1d6 + }; + + idx = 0; + digest[0] = 0x67452301; + digest[1] = 0xefcdab89; + digest[2] = 0x98badcfe; + digest[3] = 0x10325476; + digest[4] = 0xc3d2e1f0; + + memset(buf, 0, sizeof(buf)); + for (left = size, d = data; left > 0; left--, d++) + { + if ((idx == 0) && (left < 64)) + { + memset(buf, 0, 60); + buf[60] = (size >> 24) & 0xff; + buf[61] = (size >> 16) & 0xff; + buf[62] = (size >> 8) & 0xff; + buf[63] = (size) & 0xff; + } + buf[idx] = *d; + idx++;; + if ((idx == 64) || (left == 1)) + { + if ((left == 1) && (idx < 64)) buf[idx] = 0x80; + for (i = 0; i < 16; i++) + { + word[i] = (unsigned int)buf[(i * 4) ] << 24; + word[i] |= (unsigned int)buf[(i * 4) + 1] << 16; + word[i] |= (unsigned int)buf[(i * 4) + 2] << 8; + word[i] |= (unsigned int)buf[(i * 4) + 3]; + } + for (i = 16; i < 80; i++) + word[i] = SHSH(1, + word[i - 3 ] ^ word[i - 8 ] ^ + word[i - 14] ^ word[i - 16]); + wa = digest[0]; + wb = digest[1]; + wc = digest[2]; + wd = digest[3]; + we = digest[4]; + for (i = 0; i < 80; i++) + { + if (i < 20) + t = SHSH(5, wa) + ((wb & wc) | ((~wb) & wd)) + + we + word[i] + magic[0]; + else if (i < 40) + t = SHSH(5, wa) + (wb ^ wc ^ wd) + + we + word[i] + magic[1]; + else if (i < 60) + t = SHSH(5, wa) + ((wb & wc) | (wb & wd) | (wc & wd)) + + we + word[i] + magic[2]; + else if (i < 80) + t = SHSH(5, wa) + (wb ^ wc ^ wd) + + we + word[i] + magic[3]; + we = wd; + wd = wc; + wc = SHSH(30, wb); + wb = wa; + wa = t; + } + digest[0] += wa; + digest[1] += wb; + digest[2] += wc; + digest[3] += wd; + digest[4] += we; + idx = 0; + } + } + + t = htonl(digest[0]); digest[0] = t; + t = htonl(digest[1]); digest[1] = t; + t = htonl(digest[2]); digest[2] = t; + t = htonl(digest[3]); digest[3] = t; + t = htonl(digest[4]); digest[4] = t; + + memcpy(dst, digest, 5 * 4); + return 1; +} + +static char * +_e_thumb_file_id(char *file, + char *key) +{ + char s[64]; + const char *chmap = "0123456789abcdef"; + unsigned char *buf, id[20]; + int i, len, lenf; + + len = 0; + lenf = strlen(file); + len += lenf; + len++; + if (key) + { + key += strlen(key); + len++; + } + buf = alloca(len); + + strcpy((char *)buf, file); + if (key) strcpy((char *)(buf + lenf + 1), key); + + e_sha1_sum(buf, len, id); + + for (i = 0; i < 20; i++) + { + s[(i * 2) + 0] = chmap[(id[i] >> 4) & 0xf]; + s[(i * 2) + 1] = chmap[(id[i]) & 0xf]; + } + s[(i * 2)] = 0; + return strdup(s); +} +