EFL 1.7 svn doobies
authorraster <raster@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Thu, 30 Aug 2012 09:54:57 +0000 (09:54 +0000)
committerraster <raster@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Thu, 30 Aug 2012 09:54:57 +0000 (09:54 +0000)
git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/branches/ethumb-1.7@75862 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

70 files changed:
.gitignore [new file with mode: 0644]
ABOUT-NLS [new file with mode: 0644]
AUTHORS [new file with mode: 0644]
BUGS [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
data/Makefile.am [new file with mode: 0644]
data/frames/Makefile.am [new file with mode: 0644]
data/frames/default.edc [new file with mode: 0644]
data/frames/images/border-0.jpg [new file with mode: 0644]
doc/Doxyfile.in [new file with mode: 0644]
doc/Makefile.am [new file with mode: 0644]
doc/e.css [new file with mode: 0644]
doc/foot.html [new file with mode: 0644]
doc/head.html [new file with mode: 0644]
doc/img/e.png [new file with mode: 0644]
doc/img/edoxy.css [new file with mode: 0644]
doc/img/foot_bg.png [new file with mode: 0644]
doc/img/head_bg.png [new file with mode: 0644]
doc/img/menu_bg.png [new file with mode: 0644]
doc/img/menu_bg_current.png [new file with mode: 0644]
doc/img/menu_bg_hover.png [new file with mode: 0644]
doc/img/menu_bg_last.png [new file with mode: 0644]
doc/img/menu_bg_unsel.png [new file with mode: 0644]
ethumb.pc.in [new file with mode: 0644]
ethumb.spec.in [new file with mode: 0644]
ethumb_client.pc.in [new file with mode: 0644]
m4/Makefile.am [new file with mode: 0644]
m4/ac-modules.m4 [new file with mode: 0644]
m4/ac_attribute.m4 [new file with mode: 0644]
m4/as-expand.m4 [new file with mode: 0644]
m4/efl_binary.m4 [new file with mode: 0644]
m4/efl_doxygen.m4 [new file with mode: 0644]
m4/iconv.m4 [new file with mode: 0644]
m4/lib-ld.m4 [new file with mode: 0644]
m4/lib-link.m4 [new file with mode: 0644]
m4/lib-prefix.m4 [new file with mode: 0644]
m4/progtest.m4 [new file with mode: 0644]
org.enlightenment.Ethumb.service.in [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/bin/Makefile.am [new file with mode: 0644]
src/bin/ethumb.c [new file with mode: 0644]
src/bin/ethumbd.c [new file with mode: 0644]
src/bin/ethumbd_child.c [new file with mode: 0644]
src/bin/ethumbd_client.c [new file with mode: 0644]
src/bin/ethumbd_private.h [new file with mode: 0644]
src/lib/Ethumb.h [new file with mode: 0644]
src/lib/Ethumb_Plugin.h [new file with mode: 0644]
src/lib/Makefile.am [new file with mode: 0644]
src/lib/client/Ethumb_Client.h [new file with mode: 0644]
src/lib/client/Makefile.am [new file with mode: 0644]
src/lib/client/ethumb_client.c [new file with mode: 0644]
src/lib/ethumb.c [new file with mode: 0644]
src/lib/ethumb_private.h [new file with mode: 0644]
src/lib/md5.c [new file with mode: 0644]
src/lib/md5.h [new file with mode: 0644]
src/plugins/Makefile.am [new file with mode: 0644]
src/plugins/emotion/Makefile.am [new file with mode: 0644]
src/plugins/emotion/emotion.c [new file with mode: 0644]
src/plugins/emotion/template.edc [new file with mode: 0644]
src/plugins/epdf/Makefile.am [new file with mode: 0644]
src/plugins/epdf/epdf.c [new file with mode: 0644]
src/tests/Makefile.am [new file with mode: 0644]
src/tests/ethumb_dbus.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..323f173
--- /dev/null
@@ -0,0 +1,85 @@
+*~
+*.o
+*.swp
+*.swo
+INSTALL
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache/
+compile
+config.cache
+config.cache-env
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+data/Makefile
+data/Makefile.in
+data/frames/Makefile
+data/frames/Makefile.in
+data/frames/default.edj
+depcomp
+doc/Doxyfile
+doc/Makefile
+doc/Makefile.in
+ethumb.pc
+ethumb.spec
+ethumb_client.pc
+install-sh
+libtool
+ltmain.sh
+m4/Makefile
+m4/Makefile.in
+m4/libtool.m4
+m4/ltoptions.m4
+m4/ltsugar.m4
+m4/ltversion.m4
+m4/lt~obsolete.m4
+missing
+org.enlightenment.Ethumb.service
+src/Makefile
+src/Makefile.in
+src/bin/.deps/
+src/bin/.libs/
+src/bin/Makefile
+src/bin/Makefile.in
+src/bin/ethumb
+src/bin/ethumbd
+src/bin/ethumbd_client
+src/bin/ethumbd_slave
+src/lib/.deps/
+src/lib/.libs/
+src/lib/Makefile
+src/lib/Makefile.in
+src/lib/client/.deps/
+src/lib/client/.libs/
+src/lib/client/Makefile
+src/lib/client/Makefile.in
+src/lib/client/ethumb_client.lo
+src/lib/client/libethumb_client.la
+src/lib/ethumb.lo
+src/lib/libethumb.la
+src/lib/md5.lo
+src/plugins/Makefile
+src/plugins/Makefile.in
+src/plugins/emotion/.deps/
+src/plugins/emotion/.libs/
+src/plugins/emotion/Makefile
+src/plugins/emotion/Makefile.in
+src/plugins/emotion/emotion.la
+src/plugins/emotion/emotion_la-emotion.lo
+src/plugins/emotion/emotion_template.edj
+src/plugins/epdf/.deps/
+src/plugins/epdf/.libs/
+src/plugins/epdf/Makefile
+src/plugins/epdf/Makefile.in
+src/plugins/epdf/epdf.la
+src/plugins/epdf/epdf_la-epdf.lo
+src/tests/.deps/
+src/tests/Makefile
+src/tests/Makefile.in
+stamp-h1
diff --git a/ABOUT-NLS b/ABOUT-NLS
new file mode 100644 (file)
index 0000000..83bc72e
--- /dev/null
+++ b/ABOUT-NLS
@@ -0,0 +1,1068 @@
+1 Notes on the Free Translation Project
+***************************************
+
+Free software is going international!  The Free Translation Project is
+a way to get maintainers of free software, translators, and users all
+together, so that free software will gradually become able to speak many
+languages.  A few packages already provide translations for their
+messages.
+
+   If you found this `ABOUT-NLS' file inside a distribution, you may
+assume that the distributed package does use GNU `gettext' internally,
+itself available at your nearest GNU archive site.  But you do _not_
+need to install GNU `gettext' prior to configuring, installing or using
+this package with messages translated.
+
+   Installers will find here some useful hints.  These notes also
+explain how users should proceed for getting the programs to use the
+available translations.  They tell how people wanting to contribute and
+work on translations can contact the appropriate team.
+
+   When reporting bugs in the `intl/' directory or bugs which may be
+related to internationalization, you should tell about the version of
+`gettext' which is used.  The information can be found in the
+`intl/VERSION' file, in internationalized packages.
+
+1.1 Quick configuration advice
+==============================
+
+If you want to exploit the full power of internationalization, you
+should configure it using
+
+     ./configure --with-included-gettext
+
+to force usage of internationalizing routines provided within this
+package, despite the existence of internationalizing capabilities in the
+operating system where this package is being installed.  So far, only
+the `gettext' implementation in the GNU C library version 2 provides as
+many features (such as locale alias, message inheritance, automatic
+charset conversion or plural form handling) as the implementation here.
+It is also not possible to offer this additional functionality on top
+of a `catgets' implementation.  Future versions of GNU `gettext' will
+very likely convey even more functionality.  So it might be a good idea
+to change to GNU `gettext' as soon as possible.
+
+   So you need _not_ provide this option if you are using GNU libc 2 or
+you have installed a recent copy of the GNU gettext package with the
+included `libintl'.
+
+1.2 INSTALL Matters
+===================
+
+Some packages are "localizable" when properly installed; the programs
+they contain can be made to speak your own native language.  Most such
+packages use GNU `gettext'.  Other packages have their own ways to
+internationalization, predating GNU `gettext'.
+
+   By default, this package will be installed to allow translation of
+messages.  It will automatically detect whether the system already
+provides the GNU `gettext' functions.  If not, the included GNU
+`gettext' library will be used.  This library is wholly contained
+within this package, usually in the `intl/' subdirectory, so prior
+installation of the GNU `gettext' package is _not_ required.
+Installers may use special options at configuration time for changing
+the default behaviour.  The commands:
+
+     ./configure --with-included-gettext
+     ./configure --disable-nls
+
+will, respectively, bypass any pre-existing `gettext' to use the
+internationalizing routines provided within this package, or else,
+_totally_ disable translation of messages.
+
+   When you already have GNU `gettext' installed on your system and run
+configure without an option for your new package, `configure' will
+probably detect the previously built and installed `libintl.a' file and
+will decide to use this.  This might not be desirable.  You should use
+the more recent version of the GNU `gettext' library.  I.e. if the file
+`intl/VERSION' shows that the library which comes with this package is
+more recent, you should use
+
+     ./configure --with-included-gettext
+
+to prevent auto-detection.
+
+   The configuration process will not test for the `catgets' function
+and therefore it will not be used.  The reason is that even an
+emulation of `gettext' on top of `catgets' could not provide all the
+extensions of the GNU `gettext' library.
+
+   Internationalized packages usually have many `po/LL.po' files, where
+LL gives an ISO 639 two-letter code identifying the language.  Unless
+translations have been forbidden at `configure' time by using the
+`--disable-nls' switch, all available translations are installed
+together with the package.  However, the environment variable `LINGUAS'
+may be set, prior to configuration, to limit the installed set.
+`LINGUAS' should then contain a space separated list of two-letter
+codes, stating which languages are allowed.
+
+1.3 Using This Package
+======================
+
+As a user, if your language has been installed for this package, you
+only have to set the `LANG' environment variable to the appropriate
+`LL_CC' combination.  If you happen to have the `LC_ALL' or some other
+`LC_xxx' environment variables set, you should unset them before
+setting `LANG', otherwise the setting of `LANG' will not have the
+desired effect.  Here `LL' is an ISO 639 two-letter language code, and
+`CC' is an ISO 3166 two-letter country code.  For example, let's
+suppose that you speak German and live in Germany.  At the shell
+prompt, merely execute `setenv LANG de_DE' (in `csh'),
+`export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash').
+This can be done from your `.login' or `.profile' file, once and for
+all.
+
+   You might think that the country code specification is redundant.
+But in fact, some languages have dialects in different countries.  For
+example, `de_AT' is used for Austria, and `pt_BR' for Brazil.  The
+country code serves to distinguish the dialects.
+
+   The locale naming convention of `LL_CC', with `LL' denoting the
+language and `CC' denoting the country, is the one use on systems based
+on GNU libc.  On other systems, some variations of this scheme are
+used, such as `LL' or `LL_CC.ENCODING'.  You can get the list of
+locales supported by your system for your language by running the
+command `locale -a | grep '^LL''.
+
+   Not all programs have translations for all languages.  By default, an
+English message is shown in place of a nonexistent translation.  If you
+understand other languages, you can set up a priority list of languages.
+This is done through a different environment variable, called
+`LANGUAGE'.  GNU `gettext' gives preference to `LANGUAGE' over `LANG'
+for the purpose of message handling, but you still need to have `LANG'
+set to the primary language; this is required by other parts of the
+system libraries.  For example, some Swedish users who would rather
+read translations in German than English for when Swedish is not
+available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'.
+
+   Special advice for Norwegian users: The language code for Norwegian
+bokma*l changed from `no' to `nb' recently (in 2003).  During the
+transition period, while some message catalogs for this language are
+installed under `nb' and some older ones under `no', it's recommended
+for Norwegian users to set `LANGUAGE' to `nb:no' so that both newer and
+older translations are used.
+
+   In the `LANGUAGE' environment variable, but not in the `LANG'
+environment variable, `LL_CC' combinations can be abbreviated as `LL'
+to denote the language's main dialect.  For example, `de' is equivalent
+to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT'
+(Portuguese as spoken in Portugal) in this context.
+
+1.4 Translating Teams
+=====================
+
+For the Free Translation Project to be a success, we need interested
+people who like their own language and write it well, and who are also
+able to synergize with other translators speaking the same language.
+Each translation team has its own mailing list.  The up-to-date list of
+teams can be found at the Free Translation Project's homepage,
+`http://translationproject.org/', in the "Teams" area.
+
+   If you'd like to volunteer to _work_ at translating messages, you
+should become a member of the translating team for your own language.
+The subscribing address is _not_ the same as the list itself, it has
+`-request' appended.  For example, speakers of Swedish can send a
+message to `sv-request@li.org', having this message body:
+
+     subscribe
+
+   Keep in mind that team members are expected to participate
+_actively_ in translations, or at solving translational difficulties,
+rather than merely lurking around.  If your team does not exist yet and
+you want to start one, or if you are unsure about what to do or how to
+get started, please write to `coordinator@translationproject.org' to
+reach the coordinator for all translator teams.
+
+   The English team is special.  It works at improving and uniformizing
+the terminology in use.  Proven linguistic skills are praised more than
+programming skills, here.
+
+1.5 Available Packages
+======================
+
+Languages are not equally supported in all packages.  The following
+matrix shows the current state of internationalization, as of November
+2007.  The matrix shows, in regard of each package, for which languages
+PO files have been submitted to translation coordination, with a
+translation percentage of at least 50%.
+
+     Ready PO files       af am ar az be bg bs ca cs cy da de el en en_GB eo
+                        +----------------------------------------------------+
+     Compendium         |                      []       [] []        []      |
+     a2ps               |             []                [] [] []     []      |
+     aegis              |                                  ()                |
+     ant-phone          |                                  ()                |
+     anubis             |                                  []                |
+     ap-utils           |                                                    |
+     aspell             |                      [] []    [] []        []      |
+     bash               |                                                 [] |
+     bfd                |                                                    |
+     bibshelf           |                                  []                |
+     binutils           |                                                    |
+     bison              |                               [] []                |
+     bison-runtime      |                                  []                |
+     bluez-pin          | []                      []       [] []          [] |
+     cflow              |                               []                   |
+     clisp              |                               [] []    []          |
+     console-tools      |                         []       []                |
+     coreutils          |                []    [] []       []                |
+     cpio               |                                                    |
+     cpplib             |                      []       [] []                |
+     cryptonit          |                                  []                |
+     dialog             |                                                    |
+     diffutils          |                      [] []    [] [] []          [] |
+     doodle             |                                  []                |
+     e2fsprogs          |                         []       []                |
+     enscript           |                      []       [] []        []      |
+     fetchmail          |                      []       [] () []     []      |
+     findutils          |                []                                  |
+     findutils_stable   |                []    []       []                   |
+     flex               |                      []       [] []                |
+     fslint             |                                                    |
+     gas                |                                                    |
+     gawk               |                      []       [] []                |
+     gcal               |                      []                            |
+     gcc                |                                  []                |
+     gettext-examples   | []                   []          [] []          [] |
+     gettext-runtime    |             []       []       [] []             [] |
+     gettext-tools      |                      []          []                |
+     gip                |                []                                  |
+     gliv               |                []                []                |
+     glunarclock        |                []                                  |
+     gmult              | []                               []                |
+     gnubiff            |                                  ()                |
+     gnucash            |                      [] []       () ()     []      |
+     gnuedu             |                                                    |
+     gnulib             |                []                                  |
+     gnunet             |                                                    |
+     gnunet-gtk         |                                                    |
+     gnutls             |                                  []                |
+     gpe-aerial         |                         []       []                |
+     gpe-beam           |                         []       []                |
+     gpe-calendar       |                                                    |
+     gpe-clock          |                         []       []                |
+     gpe-conf           |                         []       []                |
+     gpe-contacts       |                                                    |
+     gpe-edit           |                         []                         |
+     gpe-filemanager    |                                                    |
+     gpe-go             |                         []                         |
+     gpe-login          |                         []       []                |
+     gpe-ownerinfo      |                         []       []                |
+     gpe-package        |                                                    |
+     gpe-sketchbook     |                         []       []                |
+     gpe-su             |                         []       []                |
+     gpe-taskmanager    |                         []       []                |
+     gpe-timesheet      |                         []                         |
+     gpe-today          |                         []       []                |
+     gpe-todo           |                                                    |
+     gphoto2            |                         []    [] []        []      |
+     gprof              |                               [] []                |
+     gpsdrive           |                                                    |
+     gramadoir          | []                               []                |
+     grep               |                         []                      [] |
+     gretl              |                                  ()                |
+     gsasl              |                                                    |
+     gss                |                                                    |
+     gst-plugins-bad    |                []             []                   |
+     gst-plugins-base   |                []             []                   |
+     gst-plugins-good   |                []    []       []                   |
+     gst-plugins-ugly   |                []             []                   |
+     gstreamer          | []             []    [] []    [] []        []      |
+     gtick              |                                  ()                |
+     gtkam              |             []          []    [] []                |
+     gtkorphan          |                []                []                |
+     gtkspell           |             []                   [] []          [] |
+     gutenprint         |                               []                   |
+     hello              |                []    []       [] []             [] |
+     herrie             |                                  []                |
+     hylafax            |                                                    |
+     idutils            |                               [] []                |
+     indent             |                      [] []       []             [] |
+     iso_15924          |                                                    |
+     iso_3166           |       []    [] [] [] [] [] [] [] [] []          [] |
+     iso_3166_2         |                                                    |
+     iso_4217           |                         []    [] []                |
+     iso_639            |                         []    [] []             [] |
+     jpilot             |                         []                         |
+     jtag               |                                                    |
+     jwhois             |                                                    |
+     kbd                |                         []    [] [] []             |
+     keytouch           |                      []          []                |
+     keytouch-editor    |                                  []                |
+     keytouch-keyboa... |                      []                            |
+     latrine            |                                  ()                |
+     ld                 |                               []                   |
+     leafpad            |                []    [] []       [] []             |
+     libc               |                      [] []    [] []                |
+     libexif            |                                  []                |
+     libextractor       |                                  []                |
+     libgpewidget       |                         []    [] []                |
+     libgpg-error       |                                  []                |
+     libgphoto2         |                               [] []                |
+     libgphoto2_port    |                               [] []                |
+     libgsasl           |                                                    |
+     libiconv           |                                  []             [] |
+     libidn             |                         []    []                [] |
+     lifelines          |                               [] ()                |
+     lilypond           |                                  []                |
+     lingoteach         |                                                    |
+     lprng              |                                                    |
+     lynx               |                      [] []    [] []                |
+     m4                 |                         []    [] [] []             |
+     mailfromd          |                                                    |
+     mailutils          |                      []                            |
+     make               |                               [] []                |
+     man-db             |                      []       [] []                |
+     minicom            |                         []    [] []                |
+     nano               |                []    []          []                |
+     opcodes            |                                  []                |
+     parted             |                         []       []                |
+     pilot-qof          |                                                    |
+     popt               |                         []    [] []                |
+     psmisc             |                []                                  |
+     pwdutils           |                                                    |
+     qof                |                                                    |
+     radius             |                      []                            |
+     recode             |             []       []       [] [] []          [] |
+     rpm                |                               []                   |
+     screem             |                                                    |
+     scrollkeeper       |          [] []       [] [] [] [] []        []      |
+     sed                |                      []          []             [] |
+     shared-mime-info   |                []    [] []    [] () []     []   [] |
+     sharutils          |                []    [] []    [] [] []             |
+     shishi             |                                                    |
+     skencil            |                               [] ()                |
+     solfege            |                                                    |
+     soundtracker       |                               [] []                |
+     sp                 |                                  []                |
+     system-tools-ba... |       []       [] [] [] []    [] [] []     []      |
+     tar                |                []                []                |
+     texinfo            |                               [] []             [] |
+     tin                |                                  ()        ()      |
+     tuxpaint           | []             []             [] []        []   [] |
+     unicode-han-tra... |                                                    |
+     unicode-transla... |                                                    |
+     util-linux         |                      [] []    [] []                |
+     util-linux-ng      |                      [] []    [] []                |
+     vorbis-tools       |                         []                         |
+     wastesedge         |                                  ()                |
+     wdiff              |                      []       [] []        []      |
+     wget               |                      [] []       []                |
+     xchat              |             [] []    [] []       [] []     []      |
+     xkeyboard-config   |                []                                  |
+     xpad               |                []             []           []      |
+                        +----------------------------------------------------+
+                          af am ar az be bg bs ca cs cy da de el en en_GB eo
+                           6  0  2  1  8 26  2 40 48  2 56 88 15  1  15   18
+
+                          es et eu fa fi fr  ga gl gu he hi hr hu id is it
+                        +--------------------------------------------------+
+     Compendium         | []          [] []  []                []          |
+     a2ps               |    []       [] []                             () |
+     aegis              |                                                  |
+     ant-phone          |                []                                |
+     anubis             |                []                                |
+     ap-utils           |             [] []                                |
+     aspell             |                []  []                         [] |
+     bash               | []                                               |
+     bfd                | []          []                                   |
+     bibshelf           | []                 []                         [] |
+     binutils           | []          [] []                                |
+     bison              | [] []          []  []                   []    [] |
+     bison-runtime      |    []          []  []                   []    [] |
+     bluez-pin          |             [] []  []                [] []       |
+     cflow              |                    []                            |
+     clisp              | []             []                                |
+     console-tools      |                                                  |
+     coreutils          | [] []       [] []  []                []          |
+     cpio               | []             []  []                            |
+     cpplib             | []             []                                |
+     cryptonit          |                []                                |
+     dialog             |       []           []                         [] |
+     diffutils          | []          [] []  [] []    []       [] []    [] |
+     doodle             |                    []                         [] |
+     e2fsprogs          | []             []                             [] |
+     enscript           |                []  []             []             |
+     fetchmail          | []                                               |
+     findutils          |    []              []                []          |
+     findutils_stable   |    []          []  []                []          |
+     flex               | []             []  []                            |
+     fslint             |                                                  |
+     gas                | []             []                                |
+     gawk               | []             []  []       []                () |
+     gcal               | []             []                                |
+     gcc                | []                                               |
+     gettext-examples   | []          [] []  []                [] []    [] |
+     gettext-runtime    | []          [] []  []                   []    [] |
+     gettext-tools      | []    []       []                             [] |
+     gip                | []    []       []  []                            |
+     gliv               |                ()                                |
+     glunarclock        |             []     []                []          |
+     gmult              |       []       []                             [] |
+     gnubiff            |                ()                             () |
+     gnucash            | ()             ()                    ()          |
+     gnuedu             | []                                               |
+     gnulib             | [] []              []                            |
+     gnunet             |                                                  |
+     gnunet-gtk         |                                                  |
+     gnutls             |                                                  |
+     gpe-aerial         | []             []                                |
+     gpe-beam           | []             []                                |
+     gpe-calendar       |                                                  |
+     gpe-clock          | []          [] []                    []          |
+     gpe-conf           |                []                                |
+     gpe-contacts       | []             []                                |
+     gpe-edit           | []             []                    [] []       |
+     gpe-filemanager    | []                                               |
+     gpe-go             | []             []                    []          |
+     gpe-login          | []             []                    []          |
+     gpe-ownerinfo      | []          [] []                    [] []       |
+     gpe-package        | []                                               |
+     gpe-sketchbook     | []             []                                |
+     gpe-su             | []          [] []                    []          |
+     gpe-taskmanager    | []          [] []                                |
+     gpe-timesheet      | []             []  []                   []       |
+     gpe-today          | []          [] []  []                            |
+     gpe-todo           | []                                               |
+     gphoto2            | []          [] []                    []       [] |
+     gprof              | []          [] []  []                   []       |
+     gpsdrive           |    []                                            |
+     gramadoir          |                []  []                            |
+     grep               | []          []     []                            |
+     gretl              | []    []       []                             () |
+     gsasl              |                    []                   []       |
+     gss                |                []  []                            |
+     gst-plugins-bad    | []          []                       []       [] |
+     gst-plugins-base   | []          []                       []       [] |
+     gst-plugins-good   | []    []    []                       []       [] |
+     gst-plugins-ugly   | []          []                       []       [] |
+     gstreamer          |             []                       []       [] |
+     gtick              |             []     []                         [] |
+     gtkam              | []             []                    []       [] |
+     gtkorphan          |                []                             [] |
+     gtkspell           | []    []    [] []  []                []       [] |
+     gutenprint         |                                      []          |
+     hello              | [] [] [] [] [] []  [] []    []    [] [] []    [] |
+     herrie             |                    []                            |
+     hylafax            |                                                  |
+     idutils            |                []  []                [] []    [] |
+     indent             | [] [] []    [] []  [] []             [] []    [] |
+     iso_15924          |                []                                |
+     iso_3166           | [] [] []    [] []     [] [] [] [] [] [] []    [] |
+     iso_3166_2         |                []                                |
+     iso_4217           | [] []       [] []                    []       [] |
+     iso_639            | []       [] [] []  []                []          |
+     jpilot             | []             []                                |
+     jtag               |                []                                |
+     jwhois             | []             []                    [] []    [] |
+     kbd                | []             []                                |
+     keytouch           |                []  []                         [] |
+     keytouch-editor    |                    []                            |
+     keytouch-keyboa... |                    []                         [] |
+     latrine            |                    []                         [] |
+     ld                 | []          [] []  []                            |
+     leafpad            | []             []  []       []       []       [] |
+     libc               | []          [] []     []             []          |
+     libexif            | []                                               |
+     libextractor       |                    []                            |
+     libgpewidget       | []             []  []                [] []       |
+     libgpg-error       |                []                                |
+     libgphoto2         | []             []                             [] |
+     libgphoto2_port    |                []                             [] |
+     libgsasl           |                []  []                            |
+     libiconv           |    []       []     []                            |
+     libidn             |                []                             [] |
+     lifelines          |                ()                                |
+     lilypond           | []          [] []                                |
+     lingoteach         |                []                       []    [] |
+     lprng              |                                                  |
+     lynx               |    []                                []       [] |
+     m4                 |                []  [] []                []       |
+     mailfromd          |                                                  |
+     mailutils          | []             []                                |
+     make               | []          [] []  [] []    []    []    []       |
+     man-db             |                                               [] |
+     minicom            | []          [] []                    []          |
+     nano               | []    []       []  [] []             []       [] |
+     opcodes            | []          [] []  []                            |
+     parted             |                []                       []    [] |
+     pilot-qof          |                                                  |
+     popt               |                []  [] []                   []    |
+     psmisc             |                                      []       [] |
+     pwdutils           |                                                  |
+     qof                |                                         []       |
+     radius             | []             []                                |
+     recode             | []             []  [] []    []       [] []    [] |
+     rpm                |                []                       []       |
+     screem             |                                                  |
+     scrollkeeper       | []          []                       []          |
+     sed                | [] []          []  []                []          |
+     shared-mime-info   | []    []    [] []                    []       [] |
+     sharutils          | [] []       [] []  [] []             []       [] |
+     shishi             |                []                                |
+     skencil            | []             []                                |
+     solfege            |                                               [] |
+     soundtracker       | []             []                             [] |
+     sp                 |                []                                |
+     system-tools-ba... | []    []    [] []  []             [] [] []    [] |
+     tar                |    [] []    []     []                []          |
+     texinfo            |                []           []       []          |
+     tin                |    []          ()                                |
+     tuxpaint           |                    []                []          |
+     unicode-han-tra... |                                                  |
+     unicode-transla... |                []  []                            |
+     util-linux         | [] []       [] []                    [] []    [] |
+     util-linux-ng      | [] []       [] []                    [] []    [] |
+     vorbis-tools       |                                                  |
+     wastesedge         |                ()                                |
+     wdiff              | [] []          []  [] []             [] []    [] |
+     wget               |    []       [] []  []             [] [] []    [] |
+     xchat              | []          [] []        []    []    []       [] |
+     xkeyboard-config   | []          [] []                    []          |
+     xpad               | []                 []                []          |
+                        +--------------------------------------------------+
+                          es et eu fa fi fr  ga gl gu he hi hr hu id is it
+                          85 22 14  2 48 101 61 12  2  8  2  6 53 29  1 52
+
+                          ja ka ko ku ky lg lt lv mk mn ms mt nb ne nl  nn
+                        +--------------------------------------------------+
+     Compendium         |                                           []     |
+     a2ps               |       ()                      []          []     |
+     aegis              |                                           ()     |
+     ant-phone          |                                           []     |
+     anubis             |                               []    []    []     |
+     ap-utils           |                               []                 |
+     aspell             |                            []             []     |
+     bash               |                                           []     |
+     bfd                |                                                  |
+     bibshelf           |                               []                 |
+     binutils           |                                                  |
+     bison              |                               []    []    []     |
+     bison-runtime      |                               []    []    []     |
+     bluez-pin          |          []                   []          []     |
+     cflow              |                                                  |
+     clisp              |                                           []     |
+     console-tools      |                                                  |
+     coreutils          |                                           []     |
+     cpio               |                                           []     |
+     cpplib             |                                           []     |
+     cryptonit          |                                           []     |
+     dialog             |                               []          []     |
+     diffutils          | []                            []          []     |
+     doodle             |                                                  |
+     e2fsprogs          |                                           []     |
+     enscript           |                                           []     |
+     fetchmail          | []                                        []     |
+     findutils          |                                           []     |
+     findutils_stable   |                                           []     |
+     flex               |       []                                  []     |
+     fslint             |                                                  |
+     gas                |                                                  |
+     gawk               | []                                        []     |
+     gcal               |                                                  |
+     gcc                |                                                  |
+     gettext-examples   | []                            []          []     |
+     gettext-runtime    | []    []                                  []     |
+     gettext-tools      | []    []                                         |
+     gip                |                               []          []     |
+     gliv               |                                           []     |
+     glunarclock        |                               []          []     |
+     gmult              | []                            []          []     |
+     gnubiff            |                                                  |
+     gnucash            | ()                                  () ()        |
+     gnuedu             |                                                  |
+     gnulib             | []                                        []     |
+     gnunet             |                                                  |
+     gnunet-gtk         |                                                  |
+     gnutls             |                               []                 |
+     gpe-aerial         |                                           []     |
+     gpe-beam           |                                           []     |
+     gpe-calendar       | []                                               |
+     gpe-clock          | []    []                                  []     |
+     gpe-conf           | []    []                                  []     |
+     gpe-contacts       |       []                                         |
+     gpe-edit           | []    []                                  []     |
+     gpe-filemanager    | []    []                                         |
+     gpe-go             | []    []                                  []     |
+     gpe-login          | []    []                                  []     |
+     gpe-ownerinfo      | []                                        []     |
+     gpe-package        | []    []                                         |
+     gpe-sketchbook     |       []                                  []     |
+     gpe-su             | []    []                                  []     |
+     gpe-taskmanager    | []    [] []                               []     |
+     gpe-timesheet      |                                           []     |
+     gpe-today          | []                                        []     |
+     gpe-todo           | []                                               |
+     gphoto2            | []                                        []     |
+     gprof              |                               []                 |
+     gpsdrive           |                                           []     |
+     gramadoir          |                                           ()     |
+     grep               |             []                            []     |
+     gretl              |                                                  |
+     gsasl              |                                           []     |
+     gss                |                                                  |
+     gst-plugins-bad    |                                           []     |
+     gst-plugins-base   |                                           []     |
+     gst-plugins-good   |                                           []     |
+     gst-plugins-ugly   |                                           []     |
+     gstreamer          |                                           []     |
+     gtick              |                                           []     |
+     gtkam              | []                                        []     |
+     gtkorphan          |                                           []     |
+     gtkspell           |                            []             []     |
+     gutenprint         |                                           []     |
+     hello              | [] [] []                      []    []    []  [] |
+     herrie             |                                           []     |
+     hylafax            |                                                  |
+     idutils            |                                           []     |
+     indent             | []                                        []     |
+     iso_15924          |                                           []     |
+     iso_3166           | []    [] []       []    []          []    []  [] |
+     iso_3166_2         |                                           []     |
+     iso_4217           | []                []                      []     |
+     iso_639            | []                []                      []  [] |
+     jpilot             | ()                                        ()     |
+     jtag               |                                                  |
+     jwhois             |                                           []     |
+     kbd                |                                           []     |
+     keytouch           |                                           []     |
+     keytouch-editor    |                                           []     |
+     keytouch-keyboa... |                                                  |
+     latrine            |                                           []     |
+     ld                 |                                                  |
+     leafpad            | []                []                             |
+     libc               | []    []                                  []     |
+     libexif            |                                                  |
+     libextractor       |                                                  |
+     libgpewidget       |                                           []     |
+     libgpg-error       |                                                  |
+     libgphoto2         | []                                               |
+     libgphoto2_port    | []                                               |
+     libgsasl           |                                           []     |
+     libiconv           |                                           []     |
+     libidn             | []                                        []     |
+     lifelines          |                                           []     |
+     lilypond           |                                           []     |
+     lingoteach         |                                           []     |
+     lprng              |                                                  |
+     lynx               | []                                        []     |
+     m4                 | []                                        []     |
+     mailfromd          |                                                  |
+     mailutils          |                                                  |
+     make               | []    []                                  []     |
+     man-db             |                                                  |
+     minicom            | []                                               |
+     nano               |                               []    []    []     |
+     opcodes            |                                           []     |
+     parted             | []                                        []     |
+     pilot-qof          |                                                  |
+     popt               | []    []                                  []     |
+     psmisc             | []                                  []    []     |
+     pwdutils           |                                                  |
+     qof                |                                                  |
+     radius             |                                                  |
+     recode             |                                           []     |
+     rpm                | []    []                                         |
+     screem             | []                                               |
+     scrollkeeper       |                                     [] [] []  [] |
+     sed                | []                                        []     |
+     shared-mime-info   | []    []          []          []    []    []  [] |
+     sharutils          | []                                        []     |
+     shishi             |                                                  |
+     skencil            |                                                  |
+     solfege            |                                     ()        () |
+     soundtracker       |                                                  |
+     sp                 | ()                                               |
+     system-tools-ba... | []    []          []                      []     |
+     tar                | []          []                            []     |
+     texinfo            |                                     []    []     |
+     tin                |                                                  |
+     tuxpaint           |                                     ()    []  [] |
+     unicode-han-tra... |                                                  |
+     unicode-transla... |                                                  |
+     util-linux         | []                                        []     |
+     util-linux-ng      | []                                        []     |
+     vorbis-tools       |                                                  |
+     wastesedge         |                                           []     |
+     wdiff              |                               []    []           |
+     wget               | []                                        []     |
+     xchat              | []    []                []                []     |
+     xkeyboard-config   |    [] []                                  []     |
+     xpad               |       []                      []          []     |
+                        +--------------------------------------------------+
+                          ja ka ko ku ky lg lt lv mk mn ms mt nb ne nl  nn
+                          51  2 25  3  2  0  6  0  2  2 20  0 11  1 103  6
+
+                          or pa pl pt pt_BR rm ro ru rw sk sl sq sr sv  ta
+                        +--------------------------------------------------+
+     Compendium         |          []  []      []       []          []     |
+     a2ps               |       ()     []      [] []       []    [] []     |
+     aegis              |                      () ()                       |
+     ant-phone          |                      []                   []     |
+     anubis             |       []             [] []                       |
+     ap-utils           |       ()                                         |
+     aspell             |                      [] []    []                 |
+     bash               |       []                      []                 |
+     bfd                |                                                  |
+     bibshelf           |                                           []     |
+     binutils           |                         []    []                 |
+     bison              |       []     []      [] []                []     |
+     bison-runtime      |       []     []      []          []       []     |
+     bluez-pin          |       []     []   [] [] []    [] []    [] []     |
+     cflow              |       []                                         |
+     clisp              |                         []                       |
+     console-tools      |                         []                       |
+     coreutils          |       []                []       []       []     |
+     cpio               |       []                []                []     |
+     cpplib             |                                           []     |
+     cryptonit          |              []                           []     |
+     dialog             |                                           []     |
+     diffutils          |       []     []      [] []             [] []     |
+     doodle             |                                     []    []     |
+     e2fsprogs          |       []                                  []     |
+     enscript           |              []      [] []       []       []     |
+     fetchmail          |       []                []          []           |
+     findutils          |       [] []                               []     |
+     findutils_stable   |       [] []          []       [] []       []     |
+     flex               |       []     []      [] []                []     |
+     fslint             |                                           []     |
+     gas                |                                                  |
+     gawk               |       []     []      []                   []     |
+     gcal               |                                           []     |
+     gcc                |                                        [] []     |
+     gettext-examples   |       [] []          [] []    [] []    [] []     |
+     gettext-runtime    |       [] []          [] []    [] []    [] []     |
+     gettext-tools      |       []             [] []    [] []    [] []     |
+     gip                |                   []          []       [] []     |
+     gliv               |       []     []      [] []    []          []     |
+     glunarclock        |              []      [] []    []       [] []     |
+     gmult              |                   [] []                [] []     |
+     gnubiff            |                      ()                   []     |
+     gnucash            |       ()                                  []     |
+     gnuedu             |                                                  |
+     gnulib             |       []                         []       []     |
+     gnunet             |                                                  |
+     gnunet-gtk         |                                           []     |
+     gnutls             |       []                                  []     |
+     gpe-aerial         |          []  []      [] []       []    [] []     |
+     gpe-beam           |          []  []      [] []       []    [] []     |
+     gpe-calendar       |                         []       []    [] []     |
+     gpe-clock          |          []  []      [] []    [] []    [] []     |
+     gpe-conf           |          []  []      [] []    [] []       []     |
+     gpe-contacts       |                      [] []       []    [] []     |
+     gpe-edit           |       [] []  []      [] []    [] []    [] []     |
+     gpe-filemanager    |                                  []       []     |
+     gpe-go             |       []     []      [] []    [] []    [] []     |
+     gpe-login          |          []  []      [] []    [] []    [] []     |
+     gpe-ownerinfo      |          []  []      [] []    [] []    [] []     |
+     gpe-package        |                                  []       []     |
+     gpe-sketchbook     |          []  []      [] []    [] []    [] []     |
+     gpe-su             |          []  []      [] []    [] []    [] []     |
+     gpe-taskmanager    |          []  []      [] []    [] []    [] []     |
+     gpe-timesheet      |          []  []      [] []    [] []    [] []     |
+     gpe-today          |          []  []      [] []    [] []    [] []     |
+     gpe-todo           |                         []       []    [] []     |
+     gphoto2            |    [] []             []       []       [] []     |
+     gprof              |              []      []                   []     |
+     gpsdrive           |                         []                []     |
+     gramadoir          |                               []          []     |
+     grep               |       []                      [] []       []     |
+     gretl              |       [] []  []                                  |
+     gsasl              |       []                               [] []     |
+     gss                |       []             []       []          []     |
+     gst-plugins-bad    |       []     []                           []     |
+     gst-plugins-base   |       []                                  []     |
+     gst-plugins-good   |       []                                  []     |
+     gst-plugins-ugly   |       []     []                           []     |
+     gstreamer          |       []                            [] [] []     |
+     gtick              |                         []                       |
+     gtkam              |    [] []     []         []                []     |
+     gtkorphan          |                                           []     |
+     gtkspell           |              []   [] [] []    [] []    [] []     |
+     gutenprint         |                                           []     |
+     hello              |       []     []      [] []    [] []    [] []     |
+     herrie             |       []                []                []     |
+     hylafax            |                                                  |
+     idutils            |       []     []      [] []                []     |
+     indent             |       []     []      [] []    []       [] []     |
+     iso_15924          |                                                  |
+     iso_3166           |    [] [] []  []      [] [] [] [] [] [] [] []  [] |
+     iso_3166_2         |                                                  |
+     iso_4217           |       [] []             [] []    []    [] []     |
+     iso_639            |       []                [] [] [] []    [] []     |
+     jpilot             |                                                  |
+     jtag               |                               []                 |
+     jwhois             |       []     []      []                   []     |
+     kbd                |       []             []                   []     |
+     keytouch           |                                           []     |
+     keytouch-editor    |                                           []     |
+     keytouch-keyboa... |                                           []     |
+     latrine            |                                                  |
+     ld                 |                                           []     |
+     leafpad            |       [] []             []    []          []  [] |
+     libc               |       []                []    []          []     |
+     libexif            |       []                      []                 |
+     libextractor       |                      []                   []     |
+     libgpewidget       |       [] []  []      []       [] []    [] []     |
+     libgpg-error       |       []             []                   []     |
+     libgphoto2         |       []                                         |
+     libgphoto2_port    |       []                []                []     |
+     libgsasl           |       []             []                [] []     |
+     libiconv           |                                  []    [] []     |
+     libidn             |       []                               [] ()     |
+     lifelines          |       []                                  []     |
+     lilypond           |                                                  |
+     lingoteach         |              []                                  |
+     lprng              |       []                                         |
+     lynx               |              []         []                []     |
+     m4                 |       []     []      [] []                []     |
+     mailfromd          |       []                                         |
+     mailutils          |       []                []                []     |
+     make               |       []     []         []                []     |
+     man-db             |       []             [] []                []     |
+     minicom            |       []     []      [] []                []     |
+     nano               |              []      [] []                []     |
+     opcodes            |                      []                   []     |
+     parted             |       []                                         |
+     pilot-qof          |                                                  |
+     popt               |       [] []             []                []     |
+     psmisc             |       []                                  []     |
+     pwdutils           |       []                                  []     |
+     qof                |              []                           []     |
+     radius             |       []                []                       |
+     recode             |       [] []  []      [] []       []       []     |
+     rpm                |       [] []             []                []     |
+     screem             |                                                  |
+     scrollkeeper       |       []             [] []    []    [] [] []     |
+     sed                |       [] []  []      [] []    [] []    [] []     |
+     shared-mime-info   |       [] []  []                     [] [] []     |
+     sharutils          |       []                []             [] []     |
+     shishi             |       []                                         |
+     skencil            |          []  []                           []     |
+     solfege            |              []                                  |
+     soundtracker       |                               []          []     |
+     sp                 |                                                  |
+     system-tools-ba... |    [] [] []  []      []             [] [] []  [] |
+     tar                |       []                []       []       []     |
+     texinfo            |       []             [] []                []     |
+     tin                |                         ()                       |
+     tuxpaint           |       [] []                      [] [] [] []     |
+     unicode-han-tra... |                                                  |
+     unicode-transla... |                                                  |
+     util-linux         |              []         []       []       []     |
+     util-linux-ng      |              []         []       []       []     |
+     vorbis-tools       |                         []                       |
+     wastesedge         |                                                  |
+     wdiff              |       []     []      [] []    [] []       []     |
+     wget               |          []             []    []          []     |
+     xchat              |    []                   []    [] [] [] [] []     |
+     xkeyboard-config   |                               [] []       []     |
+     xpad               |                               [] []       []     |
+                        +--------------------------------------------------+
+                          or pa pl pt pt_BR rm ro ru rw sk sl sq sr sv  ta
+                           0  5 77 31  53    4 58 72  3 45 46  9 45 122  3
+
+                          tg th tk tr uk ven vi  wa xh zh_CN zh_HK zh_TW zu
+                        +---------------------------------------------------+
+     Compendium         |          []        []         []          []      | 19
+     a2ps               |          [] []     []                             | 19
+     aegis              |                    []                             |  1
+     ant-phone          |          []        []                             |  6
+     anubis             |          [] []     []                             | 11
+     ap-utils           |             ()     []                             |  4
+     aspell             |             []     []  []                         | 16
+     bash               |          []                                       |  6
+     bfd                |                                                   |  2
+     bibshelf           |                    []                             |  7
+     binutils           |          [] []     []                     []      |  9
+     bison              |          [] []     []                     []      | 20
+     bison-runtime      |             []     []         []          []      | 18
+     bluez-pin          |          [] []     []  []     []          []      | 28
+     cflow              |             []     []                             |  5
+     clisp              |                                                   |  9
+     console-tools      |          []        []                             |  5
+     coreutils          |          [] []     []                             | 18
+     cpio               |          [] []     []         []                  | 11
+     cpplib             |          [] []     []         []          []      | 12
+     cryptonit          |                    []                             |  6
+     dialog             |                    []  []     []                  |  9
+     diffutils          |          [] []     []         []          []      | 29
+     doodle             |                    []                             |  6
+     e2fsprogs          |          []        []                             | 10
+     enscript           |          [] []     []                             | 16
+     fetchmail          |          []        []                             | 12
+     findutils          |          [] []     []                             | 11
+     findutils_stable   |          [] []     []                     []      | 18
+     flex               |          []        []                             | 15
+     fslint             |                    []                             |  2
+     gas                |          []                                       |  3
+     gawk               |          []        []         []                  | 16
+     gcal               |          []                                       |  5
+     gcc                |          []                   []          []      |  7
+     gettext-examples   |          [] []     []         []    []    []      | 29
+     gettext-runtime    |          [] []     []         []    []    []      | 28
+     gettext-tools      |          [] []     []         []          []      | 20
+     gip                |                    []                     []      | 13
+     gliv               |          []        []                             | 11
+     glunarclock        |                    []  []                 []      | 15
+     gmult              |          []        []         []          []      | 16
+     gnubiff            |                    []                             |  2
+     gnucash            |          () []                                    |  5
+     gnuedu             |                    []                             |  2
+     gnulib             |                    []                             | 10
+     gnunet             |                                                   |  0
+     gnunet-gtk         |          []        []                             |  3
+     gnutls             |                                                   |  4
+     gpe-aerial         |                    []         []                  | 14
+     gpe-beam           |                    []         []                  | 14
+     gpe-calendar       |                    []  []                         |  7
+     gpe-clock          |          []        []  []     []                  | 21
+     gpe-conf           |                    []  []     []                  | 16
+     gpe-contacts       |                    []         []                  | 10
+     gpe-edit           |          []        []  []     []          []      | 22
+     gpe-filemanager    |                    []  []                         |  7
+     gpe-go             |          []        []  []     []                  | 19
+     gpe-login          |          []        []  []     []          []      | 21
+     gpe-ownerinfo      |          []        []         []          []      | 21
+     gpe-package        |                    []                             |  6
+     gpe-sketchbook     |          []        []                             | 16
+     gpe-su             |          []        []  []     []                  | 21
+     gpe-taskmanager    |          []        []  []     []                  | 21
+     gpe-timesheet      |          []        []         []          []      | 18
+     gpe-today          |          []        []  []     []          []      | 21
+     gpe-todo           |                    []  []                         |  8
+     gphoto2            |             []     []         []          []      | 21
+     gprof              |          []        []                             | 13
+     gpsdrive           |                    []                             |  5
+     gramadoir          |                    []                             |  7
+     grep               |                    []                             | 12
+     gretl              |                                                   |  6
+     gsasl              |                    []         []          []      |  9
+     gss                |                    []                             |  7
+     gst-plugins-bad    |             []     []         []                  | 13
+     gst-plugins-base   |             []     []                             | 11
+     gst-plugins-good   |             []     []         []    []    []      | 16
+     gst-plugins-ugly   |             []     []         []                  | 13
+     gstreamer          |          [] []     []                             | 18
+     gtick              |             []     []                             |  7
+     gtkam              |                    []                             | 16
+     gtkorphan          |                    []                             |  7
+     gtkspell           |             []     []  []     []    []    []      | 27
+     gutenprint         |                                                   |  4
+     hello              |          [] []     []         []          []      | 38
+     herrie             |          []        []                             |  8
+     hylafax            |                                                   |  0
+     idutils            |          []        []                             | 15
+     indent             |          [] []     []         []          []      | 28
+     iso_15924          |                    []         []                  |  4
+     iso_3166           |    [] [] [] []     []  []     []    []    []      | 54
+     iso_3166_2         |                    []         []                  |  4
+     iso_4217           |    []    []        []         []    []            | 24
+     iso_639            |             []     []  []     []    []            | 26
+     jpilot             |          [] []     []         []                  |  7
+     jtag               |                    []                             |  3
+     jwhois             |          []        []                     []      | 13
+     kbd                |          [] []     []                             | 13
+     keytouch           |                    []                             |  8
+     keytouch-editor    |                    []                             |  5
+     keytouch-keyboa... |                    []                             |  5
+     latrine            |          []        []                             |  5
+     ld                 |          []        []         []          []      | 10
+     leafpad            |          [] []     []         []          []      | 24
+     libc               |          []                   []          []      | 19
+     libexif            |                    []                             |  5
+     libextractor       |                    []                             |  5
+     libgpewidget       |                    []  []     []                  | 20
+     libgpg-error       |                    []                             |  6
+     libgphoto2         |             []     []                             |  9
+     libgphoto2_port    |             []     []                     []      | 11
+     libgsasl           |                    []                             |  8
+     libiconv           |                    []  []                         | 11
+     libidn             |                    []         []                  | 11
+     lifelines          |                                                   |  4
+     lilypond           |                    []                             |  6
+     lingoteach         |                    []                             |  6
+     lprng              |                    []                             |  2
+     lynx               |          [] []     []                             | 15
+     m4                 |                    []         []          []      | 18
+     mailfromd          |             []     []                             |  3
+     mailutils          |             []     []                             |  8
+     make               |          []        []         []                  | 20
+     man-db             |                    []                             |  9
+     minicom            |                    []                             | 14
+     nano               |                    []         []          []      | 20
+     opcodes            |          []        []                             | 10
+     parted             |          [] []                            []      | 11
+     pilot-qof          |                    []                             |  1
+     popt               |          []        []         []          []      | 18
+     psmisc             |                    []         []                  | 10
+     pwdutils           |                    []                             |  3
+     qof                |                    []                             |  4
+     radius             |             []     []                             |  7
+     recode             |          []        []         []                  | 25
+     rpm                |          [] []     []                     []      | 13
+     screem             |                    []                             |  2
+     scrollkeeper       |          [] []     []                     []      | 26
+     sed                |          []        []         []          []      | 23
+     shared-mime-info   |             []     []         []                  | 29
+     sharutils          |          []        []                     []      | 23
+     shishi             |                    []                             |  3
+     skencil            |                    []                             |  7
+     solfege            |                    []                             |  3
+     soundtracker       |          []        []                             |  9
+     sp                 |          []                                       |  3
+     system-tools-ba... |    []    [] []     []     []  []          []      | 38
+     tar                |          [] []     []                             | 17
+     texinfo            |          []        []         []                  | 15
+     tin                |                                                   |  1
+     tuxpaint           |                    []  []                 []      | 19
+     unicode-han-tra... |                                                   |  0
+     unicode-transla... |                                                   |  2
+     util-linux         |          [] []     []                             | 20
+     util-linux-ng      |          [] []     []                             | 20
+     vorbis-tools       |             []     []                             |  4
+     wastesedge         |                                                   |  1
+     wdiff              |          []        []                             | 23
+     wget               |          []        []                     []      | 20
+     xchat              |             []     []         []          []      | 29
+     xkeyboard-config   |          [] []     []                             | 14
+     xpad               |                    []         []          []      | 15
+                        +---------------------------------------------------+
+       76 teams           tg th tk tr uk ven vi  wa xh zh_CN zh_HK zh_TW zu
+      163 domains          0  3  1 74 51  0  143 21  1  57     7    45    0  2036
+
+   Some counters in the preceding matrix are higher than the number of
+visible blocks let us expect.  This is because a few extra PO files are
+used for implementing regional variants of languages, or language
+dialects.
+
+   For a PO file in the matrix above to be effective, the package to
+which it applies should also have been internationalized and
+distributed as such by its maintainer.  There might be an observable
+lag between the mere existence a PO file and its wide availability in a
+distribution.
+
+   If November 2007 seems to be old, you may fetch a more recent copy
+of this `ABOUT-NLS' file on most GNU archive sites.  The most
+up-to-date matrix with full percentage details can be found at
+`http://translationproject.org/extra/matrix.html'.
+
+1.6 Using `gettext' in new packages
+===================================
+
+If you are writing a freely available program and want to
+internationalize it you are welcome to use GNU `gettext' in your
+package.  Of course you have to respect the GNU Library General Public
+License which covers the use of the GNU `gettext' library.  This means
+in particular that even non-free programs can use `libintl' as a shared
+library, whereas only free software can use `libintl' as a static
+library or use modified versions of `libintl'.
+
+   Once the sources are changed appropriately and the setup can handle
+the use of `gettext' the only thing missing are the translations.  The
+Free Translation Project is also available for packages which are not
+developed inside the GNU project.  Therefore the information given above
+applies also for every other Free Software Project.  Contact
+`coordinator@translationproject.org' to make the `.pot' files available
+to the translation teams.
+
diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..458f5a6
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,4 @@
+Rafael Antognolli <antognolli@profusion.mobi>
+Cedric Bail <cedric.bail@free.fr>
+Mikael SANS <sans.mikael@gmail.com>
+Jérôme Pinot <ngc891@gmail.com>
diff --git a/BUGS b/BUGS
new file mode 100644 (file)
index 0000000..a48563d
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,7 @@
+Possible error when calculating edje frame size for thumbnails.
+
+Since edje_object_size_min_calc() still doesn't retuns the actual edje object
+size, it's possible that a part from the edje frame stay outside the picture.
+This should be fixed soon, with a new call to get the current edje object size
+or changing something on the API to tell ethumb that the frame has some border.
+                        - antognolli, 30-03-2009
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..dc6c456
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,487 @@
+This library is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License.
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+details.
+
+Below is a copy of the GNU Lesser General Public License that is distributed
+along with this library. If you do not have a copy below, write to the Free
+Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
+02110-1301  USA
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent modules,
+and to copy and distribute the resulting executable under terms of your
+choice, provided that you also meet, for each linked independent module,
+the terms and conditions of the license of that module. An independent
+module is a module which is not derived from or based on this library.
+If you modify this library, you may extend this exception to your version
+of the library, but you are not obliged to do so. If you do not wish to
+do so, delete this exception statement from your version.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..4396c44
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,7 @@
+2012-04-26 Carsten Haitzler (The Rasterman)
+
+       * 1.0.0 release
+
+2012-04-30 Jérôme Pinot
+
+       * Fix build out of tree configuration
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..65c29ac
--- /dev/null
@@ -0,0 +1,41 @@
+MAINTAINERCLEANFILES = \
+       Makefile.in \
+       aclocal.m4 \
+       compile \
+       config.guess \
+       config.h.in \
+       config.sub \
+       configure \
+       depcomp \
+       install-sh \
+       ltmain.sh \
+       missing \
+       $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.gz \
+       $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.bz2 \
+       $(PACKAGE_TARNAME)-$(PACKAGE_VERSION)-doc.tar.bz2
+
+EXTRA_DIST = \
+autogen.sh  \
+ethumb.spec
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = ethumb.pc
+
+
+if USE_MODULE_ETHUMBD
+
+pkgconfig_DATA += ethumb_client.pc
+servicedir = $(dbusservicedir)
+service_DATA = org.enlightenment.Ethumb.service
+
+endif
+
+SUBDIRS = m4 src data doc
+
+ACLOCAL_AMFLAGS = -I m4
+
+.PHONY: doc
+
+doc:
+       @echo "entering doc/"
+       $(MAKE) -C doc doc
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..c60a9e6
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,14 @@
+Ethumb 1.7.0
+
+Changes since Ethumb 1.0.0:
+-------------------------
+
+Additions:
+
+Fixes:
+       - build out of tree.
+
+Improvements:
+
+Removal:
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..2b6236e
--- /dev/null
+++ b/README
@@ -0,0 +1,69 @@
+Ethumb 1.7.0
+
+******************************************************************************
+
+ FOR ANY ISSUES PLEASE EMAIL:
+ enlightenment-devel@lists.sourceforge.net
+
+******************************************************************************
+  
+Ethumb - Thumbnail generation library
+
+
+FEATURES
+========
+
+ * create thumbnails with a predefined frame (possibly an edje frame);
+ * have an option to create fdo-like thumbnails;
+ * have a client/server utility.
+
+
+API
+===
+
+It's possible to set the following properties of thumbnails:
+
+ * size
+ * format (jpeg, png, eet...)
+ * aspect:
+    * have crop?
+    * crop alignment?
+ * video:
+    * video_time
+ * document:
+    * page
+ * frame: edje file, group and swallow part to use when generating
+   thumbnails
+ * directory: directory where to save thumbnails
+ * category: to be used as DIRECTORY/CATEGORY/md5.format
+
+Path generation should provide the following:
+
+ * If no path to save the thumbnail is specified, the following is used:
+   * if CATEGORY, return ~/.thumbnail/CATEGORY/md5.format
+   * else if size (128x128 or 256x256), format (png), aspect (keep
+     aspect, no crop) and no frame matches, return
+     ~/.thumbnail/{normal,large}/md5.png
+   * else return WxH-FORMAT-[framed-]ASPECT
+
+Client server provides the following:
+ * multiple client support
+ * per-client configuration, avoid exchanging parameters over and over
+   again
+ * per-client queue, when client disconnect (ie: dies), remove whole
+   queue
+ * all clients have same priority, so queue is mixed for processing
+ * cancel thumb generation request
+ * communication over (for now) dbus and (future) ecore-ipc and unix
+   sockets
+ * interface of client library is independent of the communication
+   method selected
+
+------------------------------------------------------------------------------
+COMPILING AND INSTALLING:
+
+  ./configure
+  make
+(do this as root unless you are installing in your users directories):
+  make install
+  
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..6a6dde2
--- /dev/null
+++ b/TODO
@@ -0,0 +1,38 @@
+ * documentation (based on python bindings)
+   * Ethumb (adapt most bits based on Ethumb_Client)
+   * Ethumb_Client (mostly done)
+   * finish ethumb_client tutorial
+
+ * memory handling:
+   * zero pointer parameters before failing on SAFETY macros (file_get, etc)
+
+ * ethumb_object:
+   smart object (ethumb_object_add())that given a file_set() will
+   automatically use Ethumb_Client to check if thumbnail exists or
+   generate a new one, do evas_object_image_preload() and then show
+   the object.
+
+   To save memory and avoid thousands of Ethumb_Client, this should be
+   shared among all Evas_Object that take a reference when object is
+   created, releasing the reference after thumbnail is generated. When
+   the last user releases it, start a timer to disconnect in X seconds
+   (5-10 seconds is good enough).
+
+   Similar to Ethumb_Client, it should expose all Ethumb properties,
+   they would be cached locally and on smart_calculate() they should
+   be dispatched to server for a new thumbnail if source file pat set.
+
+ * elm_thumbnail:
+   using ethumb_object, this can be based on E17 ethumb object that
+   uses edje and can have the theme set, so on load a nice animation
+   can happen, animate a throbber while it generates, etc. It may
+   return ethumb_object (Evas_Object) if user wants to set fancy
+   parameters.
+
+ * unit tests, be them in C or Python, covering:
+   * thumbnail generation respect parameters (size, format...)
+   * respect fdo standards (hash, location, etc, uri with spaces...)
+   * server died recovery, just add timeout and kill server before expires
+
+ * examples:
+   * error handling, including reconnection.
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..00116ea
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+rm -rf autom4te.cache
+rm -f aclocal.m4 ltmain.sh
+
+touch README
+touch ABOUT-NLS
+
+echo "Running aclocal..." ; aclocal $ACLOCAL_FLAGS -I m4 || exit 1
+echo "Running autoheader..." ; autoheader || exit 1
+echo "Running autoconf..." ; autoconf || exit 1
+echo "Running libtoolize..." ; (libtoolize --copy --automake || glibtoolize --automake) || exit 1
+echo "Running automake..." ; automake --add-missing --copy --gnu || exit 1
+
+W=0
+
+rm -f config.cache-env.tmp
+echo "OLD_PARM=\"$@\"" >> config.cache-env.tmp
+echo "OLD_CFLAGS=\"$CFLAGS\"" >> config.cache-env.tmp
+echo "OLD_PATH=\"$PATH\"" >> config.cache-env.tmp
+echo "OLD_PKG_CONFIG_PATH=\"$PKG_CONFIG_PATH\"" >> config.cache-env.tmp
+echo "OLD_LDFLAGS=\"$LDFLAGS\"" >> config.cache-env.tmp
+
+cmp config.cache-env.tmp config.cache-env >> /dev/null
+if [ $? -ne 0 ]; then
+       W=1;
+fi
+
+if [ $W -ne 0 ]; then
+       echo "Cleaning configure cache...";
+       rm -f config.cache config.cache-env
+       mv config.cache-env.tmp config.cache-env
+else
+       rm -f config.cache-env.tmp
+fi
+
+if [ -z "$NOCONFIGURE" ]; then
+       ./configure -C "$@"
+fi
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..7535909
--- /dev/null
@@ -0,0 +1,279 @@
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+m4_define([v_maj], [1])
+m4_define([v_min], [7])
+m4_define([v_mic], [0])
+m4_define([v_rev], m4_esyscmd([(svnversion "${SVN_REPO_PATH:-.}" | grep -v '\(export\|Unversioned directory\)' || echo 0) | awk -F : '{printf("%s\n", $1);}' | tr -d ' :MSP\n']))
+m4_if(v_rev, [0], [m4_define([v_rev], m4_esyscmd([git log 2> /dev/null | (grep -m1 git-svn-id || echo 0) | sed -e 's/.*@\([0-9]*\).*/\1/' | tr -d '\n']))])
+##--   When released, remove the dnl on the below line
+m4_undefine([v_rev])
+##--   When doing snapshots - change soname. remove dnl on below line
+dnl m4_define([relname], [ver-pre-svn-09])
+dnl m4_define([v_rel], [-release relname])
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+m4_ifdef([v_rev], [m4_define([v_ver], [v_maj.v_min.v_mic.v_rev])],
+[m4_define([v_ver], [v_maj.v_min.v_mic])])
+m4_define([lt_cur], m4_eval(v_maj + v_min))
+m4_define([lt_rev], v_mic)
+m4_define([lt_age], v_min)
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+
+AC_INIT([ethumb], [v_ver], [enlightenment-devel@lists.sourceforge.net])
+AC_PREREQ([2.52])
+AC_CONFIG_SRCDIR([configure.ac])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CANONICAL_BUILD
+AC_CANONICAL_HOST
+AC_ISC_POSIX
+
+AC_CONFIG_HEADERS([config.h])
+AH_TOP([
+#ifndef EFL_CONFIG_H__
+#define EFL_CONFIG_H__
+])
+AH_BOTTOM([
+#endif /* EFL_CONFIG_H__ */
+])
+
+AM_INIT_AUTOMAKE([1.6 dist-bzip2])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+_XTERM_COLORS
+
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+m4_ifdef([v_rev], , [m4_define([v_rev], [0])])
+m4_ifdef([v_rel], , [m4_define([v_rel], [])])
+AC_DEFINE_UNQUOTED(VMAJ, [v_maj], [Major version])
+AC_DEFINE_UNQUOTED(VMIN, [v_min], [Minor version])
+AC_DEFINE_UNQUOTED(VMIC, [v_mic], [Micro version])
+AC_DEFINE_UNQUOTED(VREV, [v_rev], [Revison])
+version_info="lt_cur:lt_rev:lt_age"
+release_info="v_rel"
+AC_SUBST(version_info)
+AC_SUBST(release_info)
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+VMAJ=v_maj
+AC_SUBST(VMAJ)
+
+dnl we just have set the version info, then:
+AC_SUBST(version_info)
+
+AC_PROG_CC
+AC_PROG_CC_STDC
+AM_PROG_CC_C_O
+AC_HEADER_STDC
+AC_C___ATTRIBUTE__
+dnl AC_C_CONST is bogus, its tests fails on -Wall -O1 ( uninitialized values)
+dnl AC_C_CONST
+
+AC_LIBTOOL_WIN32_DLL
+define([AC_LIBTOOL_LANG_CXX_CONFIG], [:])
+define([AC_LIBTOOL_LANG_GCJ_CONFIG], [:])
+define([AC_LIBTOOL_LANG_F77_CONFIG], [:])
+AC_PROG_LIBTOOL
+
+#themesdir="${datadir}/ethumb/data/frames"
+#AC_SUBST(themesdir)
+#AS_AC_EXPAND(THEMESDIR, $themesdir)
+#AC_DEFINE_UNQUOTED(THEMESDIR, ["$THEMESDIR"], [Where frame theme files are installed.]
+#)
+
+#AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)
+#AC_DEFINE_UNQUOTED(SYSCONFDIR, ["$SYSCONFDIR"], [Where system configuration is stored])
+
+EFL_CHECK_DOXYGEN([build_doc="yes"], [build_doc="no"])
+
+with_max_log_level="<unset>"
+AC_ARG_WITH(internal-maximum-log-level,
+   [AC_HELP_STRING([--with-internal-maximum-log-level=NUMBER],
+                   [limit eina internal log level to the given number, any call to EINA_LOG() with values greater than this will be compiled out, ignoring runtime settings, but saving function calls.])],
+   [
+    if test "x${withval}" != "xno"; then
+       if echo "${withval}" | grep '^[[0-9]]\+$' >/dev/null 2>/dev/null; then
+          AC_MSG_NOTICE([ignoring any EINA_LOG() with level greater than ${withval}])
+          AC_DEFINE_UNQUOTED(EINA_LOG_LEVEL_MAXIMUM, ${withval}, [if set, logging is limited to this amount.])
+          with_max_log_level="${withval}"
+       else
+          AC_MSG_ERROR([--with-internal-maximum-log-level takes a decimal number, got "${withval}" instead.])
+       fi
+    fi
+    ], [:])
+
+EFL_ETHUMB_BUILD=""
+EFL_ETHUMB_CLIENT_BUILD=""
+lt_enable_auto_import=""
+case "$host_os" in
+   mingw* | cegcc*)
+      EFL_ETHUMB_BUILD="-DEFL_ETHUMB_BUILD"
+      EFL_ETHUMB_CLIENT_BUILD="-DEFL_ETHUMB_CLIENT_BUILD"
+      lt_enable_auto_import="-Wl,--enable-auto-import"
+   ;;
+esac
+AC_SUBST(EFL_ETHUMB_BUILD)
+AC_SUBST(EFL_ETHUMB_CLIENT_BUILD)
+AC_SUBST(lt_enable_auto_import)
+
+PKG_CHECK_MODULES(EINA, [eina >= 1.6.99])
+PKG_CHECK_MODULES(EVAS, [evas >= 1.6.99])
+PKG_CHECK_MODULES(ECORE, [ecore >= 1.6.99])
+PKG_CHECK_MODULES(ECORE_EVAS, [ecore-evas >= 1.6.99])
+PKG_CHECK_MODULES(ECORE_FILE, [ecore-file >= 1.6.99 ])
+PKG_CHECK_MODULES(EDJE, [edje >= 1.6.99])
+PKG_CHECK_MODULES(EET, [eet >= 1.6.99])
+EFL_WITH_BIN([edje], [edje-cc], [edje_cc])
+
+requirement_ethumb="eina >= 1.6.99 evas >= 1.6.99 ecore >= 1.6.99 ecore-evas >= 1.6.99 ecore-file >= 1.6.99 edje >= 1.6.99"
+requirement_ethumb_client=""
+
+AM_CONDITIONAL(HAVE_EMOTION, false)
+define([CHECK_MODULE_EMOTION],
+[
+        AC_ETH_CHECK_PKG(EMOTION, emotion, [], [EMOTION=false])
+])
+
+AC_ETH_OPTIONAL_MODULE([emotion], true, [CHECK_MODULE_EMOTION])
+
+if $USE_MODULE_EMOTION ; then
+        requirement_ethumb="$requirement_ethumb emotion"
+fi
+
+AM_CONDITIONAL(HAVE_EPDF, false)
+define([CHECK_MODULE_EPDF],
+[
+        AC_ETH_CHECK_PKG(EPDF, epdf, [], [EPDF=false])
+])
+
+AC_ETH_OPTIONAL_MODULE([epdf], true, [CHECK_MODULE_EPDF])
+if $USE_MODULE_EPDF ; then
+        requirement_ethumb="$requirement_ethumb epdf"
+fi
+
+AM_CONDITIONAL(HAVE_EDBUS, false)
+define([CHECK_MODULE_ETHUMBD],
+[
+        AC_ETH_CHECK_PKG(EDBUS, edbus, [], [ETHUMBD=false])
+])
+
+AC_ETH_OPTIONAL_MODULE([ethumbd], true, [CHECK_MODULE_ETHUMBD])
+if $USE_MODULE_ETHUMBD ; then
+        dbusservicedir=${datadir}/dbus-1/services
+        AC_ARG_WITH([dbus-services],
+                    AC_HELP_STRING([--with-dbus-services=DBUS_SERVICES], [specify a directory to store dbus service files.]),
+                    [
+                      dbusservicedir=$withval
+                    ]
+                   )
+fi
+
+
+AM_CONDITIONAL(HAVE_LIBEXIF, false)
+define([CHECK_MODULE_LIBEXIF],
+[
+        AC_ETH_CHECK_PKG(LIBEXIF, libexif, [], [LIBEXIF=false])
+])
+
+AC_ETH_OPTIONAL_MODULE([libexif], true, [CHECK_MODULE_LIBEXIF])
+if $USE_MODULE_LIBEXIF ; then
+        requirement_ethumb="$requirement_ethumb libexif"
+fi
+
+AC_COMPILE_IFELSE(
+   [AC_LANG_PROGRAM(
+       [[
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+       ]],
+       [[
+size_t tmp = listxattr("/", NULL, 0);
+tmp = getxattr("/", "user.ethumb.md5", NULL, 0);
+setxattr("/", "user.ethumb.md5", NULL, 0, 0);
+       ]])],
+    [
+      AC_DEFINE(HAVE_XATTR, 1, [Define to 1 if you have 'listxattr', 'setxattr' and 'getxattr'])
+      have_xattr="yes"
+    ],
+    [have_xattr="no"])
+
+AC_MSG_CHECKING([for Xattr])
+AC_MSG_RESULT([${have_xattr}])
+
+AC_SUBST(requirement_ethumb)
+AC_SUBST(requirement_ethumb_client)
+AC_SUBST(dbusservicedir)
+
+AC_CONFIG_FILES([
+ethumb.pc
+ethumb.spec
+ethumb_client.pc
+org.enlightenment.Ethumb.service
+Makefile
+src/Makefile
+src/bin/Makefile
+src/lib/Makefile
+src/lib/client/Makefile
+src/plugins/Makefile
+src/plugins/emotion/Makefile
+src/plugins/epdf/Makefile
+src/tests/Makefile
+data/Makefile
+data/frames/Makefile
+m4/Makefile
+doc/Makefile
+doc/Doxyfile
+])
+
+AC_OUTPUT
+
+txt_strip() {
+        echo "[$]@" | sed -e 's/^[[ \t]]*\([[^ \t]]*\)[[ \t]]*$/\1/g'
+}
+
+MODS=""
+for mod in $OPTIONAL_MODULES; do
+        MODS="$MODS ${COLOR_HGREEN}+$mod${COLOR_END}"
+done
+MODS=$(txt_strip $MODS)
+
+UNUSED_MODS=""
+for mod in $UNUSED_OPTIONAL_MODULES; do
+        UNUSED_MODS="$UNUSED_MODS ${COLOR_HRED}-$mod${COLOR_END}"
+done
+UNUSED_MODS=$(txt_strip $UNUSED_MODS)
+
+cat <<SUMMARY_EOF
+
+Summary:
+ * project..........: $PACKAGE $VERSION
+ * prefix...........: $(txt_strip $prefix)
+ * DBus services dir: $(txt_strip $dbusservicedir)
+ * CFLAGS...........: $(txt_strip $CFLAGS)
+ * LDFLAGS..........: $(txt_strip $LDFLAGS)
+
+Configuration Options Summary:
+
+ * use xattr........: ${have_xattr}
+ * maximum log level: ${with_max_log_level}
+ * documentation....: ${build_doc}
+
+SUMMARY_EOF
+
+if test "x$MODS" != "x"; then
+   echo -e " * enabled modules..: $MODS"
+fi
+
+if test "x$UNUSED_MODS" != "x"; then
+   echo -e " * disabled modules.: $UNUSED_MODS"
+fi
+
+cat << HINT_EOF
+
+now type:
+
+Now type 'make' ('gmake' on some systems) to compile $PACKAGE, and
+then afterwards as root (or the user who will install this), type
+'make install'. Change users with 'su' or 'sudo' appropriately.
+
+HINT_EOF
diff --git a/data/Makefile.am b/data/Makefile.am
new file mode 100644 (file)
index 0000000..1f3459f
--- /dev/null
@@ -0,0 +1,3 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = frames
diff --git a/data/frames/Makefile.am b/data/frames/Makefile.am
new file mode 100644 (file)
index 0000000..fefa784
--- /dev/null
@@ -0,0 +1,20 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+EDJE_CC = @edje_cc@
+EDJE_FLAGS = -v \
+       -id $(top_srcdir)/data/frames/images
+
+filesdir = ${datadir}/ethumb/data/frames
+files_DATA = default.edj
+
+EXTRA_DIST = \
+       default.edc \
+       images/border-0.jpg
+
+default.edj: Makefile $(EXTRADIST)
+       $(EDJE_CC) $(EDJE_FLAGS) \
+               $(top_srcdir)/data/frames/default.edc \
+               $(top_builddir)/data/frames/default.edj
+
+clean-local:
+       rm -f *.edj
diff --git a/data/frames/default.edc b/data/frames/default.edc
new file mode 100644 (file)
index 0000000..5341263
--- /dev/null
@@ -0,0 +1,46 @@
+collections {
+   images {
+      image: "border-0.jpg" COMP;
+   }
+
+   group {
+      name: "frame/default";
+
+      parts {
+         part {
+            name: "img";
+            type: SWALLOW;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+            }
+         } // img
+
+         part {
+            name: "border";
+            type: IMAGE;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               color: 224 224 224 255;
+               rel1 {
+                  to: "img";
+                  relative: 0.0 0.0;
+                  offset: 0 0;
+               }
+               rel2 {
+                  to: "img";
+                  relative: 1.0 1.0;
+                  offset: -1 -1;
+               }
+               image {
+                  normal: "border-0.jpg";
+                  border: 2 2 2 2;
+                  middle: 0;
+               }
+            }
+         } // border
+
+      }
+   }
+}
diff --git a/data/frames/images/border-0.jpg b/data/frames/images/border-0.jpg
new file mode 100644 (file)
index 0000000..ee66879
Binary files /dev/null and b/data/frames/images/border-0.jpg differ
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
new file mode 100644 (file)
index 0000000..5e32ca8
--- /dev/null
@@ -0,0 +1,1349 @@
+# Doxyfile 1.5.5
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file 
+# that follow. The default is UTF-8 which is also the encoding used for all 
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the 
+# iconv built into libc) for the transcoding. See 
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = Ethumb
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = @PACKAGE_VERSION@
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = .
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of 
+# source files, where putting all generated files in the same directory would 
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, 
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), 
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, 
+# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, 
+# and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is 
+# used as the annotated text. Otherwise, the brief description is used as-is. 
+# If left blank, the following values are used ("$name" is automatically 
+# replaced with the name of the entity): "The $name class" "The $name widget" 
+# "The $name file" "is" "provides" "specifies" "contains" 
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
+# inherited members of a class in the documentation of that class as if those 
+# members were ordinary class members. Constructors, destructors and assignment 
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = 
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like regular Qt-style comments 
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
+# interpret the first line (until the first dot) of a Qt-style 
+# comment as the brief description. If set to NO, the comments 
+# will behave just like regular Qt-style comments (thus requiring 
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
+# a new page for each member. If set to NO, the documentation of a member will 
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 2
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
+# sources only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Java. For instance, namespaces will be presented as packages, qualified 
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 
+# sources. Doxygen will then generate output that is tailored for 
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
+# to include (a tag file for) the STL sources as input, then you should 
+# set this tag to YES in order to let doxygen match functions declarations and 
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
+# func(std::string) {}). This also make the inheritance and collaboration 
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 
+# Doxygen will parse them like normal C++ but will assume all classes use public 
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 
+# is documented as struct, union, or enum with the name of the typedef. So 
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct 
+# with name TypeT. When disabled the typedef will appear as a member of a file, 
+# namespace, or class. And the struct will be named TypeS. This can typically 
+# be useful for C code in case the coding convention dictates that all compound 
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = NO
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be 
+# extracted and appear in the documentation as a namespace called 
+# 'anonymous_namespace{file}', where file will be replaced with the base 
+# name of the file that contains the anonymous namespace. By default 
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
+# hierarchy of group names into alphabetical order. If set to NO (the default) 
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = NO
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
+# doxygen should invoke to get the current version for each file (typically from 
+# the version control system). Doxygen will invoke the program by executing (via 
+# popen()) the command <command> <input-file>, where <command> is the value of 
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
+# provided by doxygen. Whatever the program writes to standard output 
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for 
+# functions that are documented, but have no documentation for their parameters 
+# or return value. If set to NO (the default) doxygen will only warn about 
+# wrong or incomplete parameter documentation, but not about the absence of 
+# documentation.
+
+WARN_NO_PARAMDOC       = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text. Optionally the format may contain 
+# $version, which will be replaced by the version of the file (if it could 
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = @top_srcdir@/src/lib
+
+# This tag can be used to specify the character encoding of the source files 
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
+# also the default input encoding. Doxygen uses libiconv (or the iconv built 
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS          = *.c *.h *.x
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
+# directories that are symbolic links (a Unix filesystem feature) are excluded 
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories. Note that the wildcards are matched 
+# against the file with absolute path, so to exclude all test directories 
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       = 
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
+# (namespaces, classes, functions, etc.) that should be excluded from the 
+# output. The symbol name can be a fully qualified name, a word, or if the 
+# wildcard * is used, a substring. Examples: ANamespace, AClass, 
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = doc/img
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.  Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code 
+# will point to the HTML generated by the htags(1) tool instead of doxygen 
+# built-in source browser. The htags tool is part of GNU's global source 
+# tagging system (see http://www.gnu.org/software/global/global.html). You 
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 2
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = @srcdir@/head.html 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = @srcdir@/foot.html
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = @srcdir@/e.css
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files 
+# will be generated that can be used as input for Apple's Xcode 3 
+# integrated development environment, introduced with OSX 10.5 (Leopard). 
+# To create a documentation set, doxygen will generate a Makefile in the 
+# HTML output directory. Running make will produce the docset in that 
+# directory and running "make install" will install the docset in 
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 
+# it at startup.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 
+# feed. A documentation feed provides an umbrella under which multiple 
+# documentation sets from a single provider (such as a company or product suite) 
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 
+# should uniquely identify the documentation set bundle. This should be a 
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
+# documentation will contain sections that can be hidden and shown after the 
+# page has loaded. For this to work a browser that supports 
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = YES 
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 1
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = YES
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = NO
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED             = __UNUSED__=
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse 
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
+# or super classes. Setting the tag to NO turns the diagrams off. Note that 
+# this option is superseded by the HAVE_DOT option below. This is only a 
+# fallback. It is recommended to install and use dot, since it yields more 
+# powerful graphs.
+
+CLASS_DIAGRAMS         = NO
+
+# You can define message sequence charts within doxygen comments using the \msc 
+# command. Doxygen will then run the mscgen tool (see 
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where 
+# the mscgen tool resides. If left empty the tool is assumed to be found in the 
+# default search path.
+
+MSCGEN_PATH            = 
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = NO
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = NO
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = NO
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then 
+# doxygen will generate a call dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable call graphs 
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 
+# doxygen will generate a caller dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable caller 
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = NO
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# then doxygen will show the dependencies a directory has on other directories 
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
+# nodes that will be shown in the graph. If the number of nodes in a graph 
+# becomes larger than this value, doxygen will truncate the graph, which is 
+# visualized by representing a node as a red box. Note that doxygen if the 
+# number of direct children of the root node in a graph is already larger than 
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes 
+# that lay further from the root node will be omitted. Note that setting this 
+# option to 1 or 2 may greatly reduce the computation time needed for large 
+# code bases. Also note that the size of a graph can be further restricted by 
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
+# background. This is enabled by default, which results in a transparent 
+# background. Warning: Depending on the platform used, enabling this option 
+# may lead to badly anti-aliased labels on the edges of a graph (i.e. they 
+# become hard to read).
+
+DOT_TRANSPARENT        = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
+# files in one run (i.e. multiple -o and -T options on the command line). This 
+# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644 (file)
index 0000000..aa62036
--- /dev/null
@@ -0,0 +1,34 @@
+
+MAINTAINERCLEANFILES = Makefile.in
+
+.PHONY: doc
+
+PACKAGE_DOCNAME = $(PACKAGE_TARNAME)-$(PACKAGE_VERSION)-doc
+
+if EFL_BUILD_DOC
+
+doc-clean:
+       rm -rf html/ latex/ man/ xml/ $(PACKAGE_DOCNAME).tar*
+
+doc: all doc-clean
+       $(efl_doxygen)
+       cp $(srcdir)/img/* html/
+       cp $(srcdir)/img/* latex/
+       rm -rf $(PACKAGE_DOCNAME).tar*
+       mkdir -p $(PACKAGE_DOCNAME)/doc
+       cp -R html/ latex/ man/ $(PACKAGE_DOCNAME)/doc
+       tar cf $(PACKAGE_DOCNAME).tar $(PACKAGE_DOCNAME)/
+       bzip2 -9 $(PACKAGE_DOCNAME).tar
+       rm -rf $(PACKAGE_DOCNAME)/
+       mv $(PACKAGE_DOCNAME).tar.bz2 $(top_builddir)
+
+clean-local: doc-clean
+
+else
+
+doc:
+       @echo "Documentation not built. Run ./configure --help"
+
+endif
+
+EXTRA_DIST = Doxyfile $(wildcard img/*.*) e.css head.html foot.html
diff --git a/doc/e.css b/doc/e.css
new file mode 100644 (file)
index 0000000..8697a3a
--- /dev/null
+++ b/doc/e.css
@@ -0,0 +1,218 @@
+/*
+    Author:
+        Andres Blanc <andresblanc@gmail.com>
+       DaveMDS Andreoli <dave@gurumeditation.it>
+
+    Supported Browsers:
+        ie7, opera9, konqueror4 and firefox3
+
+        Please use a different file for ie6, ie5, etc. hacks.
+*/
+
+
+/* Necessary to place the footer at the bottom of the page */
+html, body {
+       height: 100%;
+       margin: 0px;
+       padding: 0px;
+}
+
+#container {
+       min-height: 100%;
+       height: auto !important;
+       height: 100%;
+       margin: 0 auto -53px;
+}
+
+#footer, #push {
+       height: 53px;
+}
+
+
+* html #container {
+       height: 100%;
+}
+
+/* Prevent floating elements overflowing containers */
+.clear {
+       clear: both;
+       width: 0px;
+       height: 0px;
+}
+
+/* Flexible & centered layout from 750 to 960 pixels */
+.layout {
+       max-width: 960px;
+       min-width: 760px;
+       margin-left: auto;
+       margin-right: auto;
+}
+
+body {
+       /*font-family: Lucida Grande, Helvetica, sans-serif;*/
+       font-family: "Bitstream Vera","Vera","Trebuchet MS",Trebuchet,Tahoma,sans-serif
+}
+
+/* Prevent design overflowing the viewport in small resolutions */
+#container {
+       padding-right: 17px;
+       padding-left: 17px;
+       background-image: url(head_bg.png);
+       background-repeat: repeat-x;
+}
+
+#header {
+       width: 100%;
+       height: 102px;
+}
+
+#header h1 {
+       width: 63px;
+       height: 63px;
+       background-image: url(e.png);
+       background-repeat: no-repeat;
+       position: absolute;
+       margin: 0px;
+}
+
+#header h1 span {
+       display: none;
+}
+
+#header h2 {
+       display: none;
+}
+
+/* .menu-container is used to set properties common to .menu and .submenu */
+#header .menu-container {
+}
+
+#header .menu-container ul {
+       list-style-type: none;
+       list-style-position: inside;
+       margin: 0;
+}
+
+#header .menu-container li {
+       display: block;
+       float: right;
+}
+
+#header .menu {
+       height: 63px;
+       display: block;
+       background-image: url(menu_bg.png);
+       background-repeat: repeat-x;
+}
+
+#header .menu ul {
+       height: 100%;
+       display: block;
+       background-image: url(menu_bg_last.png);
+       background-repeat: no-repeat;
+       background-position: top right;
+       padding-right: 17px;
+}
+
+#header .menu li {
+       height: 100%;
+       text-align: center;
+       background-image: url(menu_bg_unsel.png);
+       background-repeat: no-repeat;
+}
+
+#header .menu a {
+       height: 100%;
+       display: block;
+       color: #cdcdcd;
+       text-decoration: none;
+       font-size: 10pt;
+       line-height: 59px;
+       text-align: center;
+       padding: 0px 15px 0px 15px;
+}
+
+#header .menu li:hover {
+       background-image: url(menu_bg_hover.png);
+       background-repeat: no-repeat;
+}
+
+#header .menu li:hover a {
+       color: #FFFFFF;
+}
+
+#header .menu li.current {
+       background-image: url(menu_bg_current.png);
+       background-repeat: no-repeat;
+}
+
+#header .menu li.current a {
+       color: #646464;
+}
+
+
+/* Hide all the submenus but the current */
+#header .submenu ul {
+       display: none;
+}
+
+#header .submenu .current {
+       display: block;
+}
+
+#header .submenu {
+       font: bold 10px verdana,'Bitstream Vera Sans',helvetica,arial,sans-serif;
+       margin-top: 10px;
+}
+
+#header .submenu a {
+       color: #888888;
+       text-decoration: none;
+       font-size: 0.9em;
+       line-height: 15px;
+       padding:0px 5px 0px 5px;
+}
+
+#header .submenu a:hover {
+       color: #444444;
+}
+
+#header .submenu li {
+       border-left: 1px solid #DDDDDD;
+}
+
+#header .submenu li:last-child {
+       border-left: 0;
+}
+
+#header .doxytitle {
+       position: absolute;
+       font-size: 1.8em;
+       font-weight: bold;
+       color: #444444;
+       line-height: 35px;
+}
+
+#header small {
+       font-size: 0.4em;
+}
+
+#footer {
+       background-image: url(foot_bg.png);
+       width: 100%;
+}
+
+#footer table {
+       width: 100%;
+       text-align: center;
+       white-space: nowrap;
+       padding: 5px 30px 5px 30px;
+       font-size: 0.8em;
+       font-family: "Bitstream Vera","Vera","Trebuchet MS",Trebuchet,Tahoma,sans-serif;
+       color: #888888;
+}
+
+#footer td.copyright {
+       width: 100%;
+}
+
diff --git a/doc/foot.html b/doc/foot.html
new file mode 100644 (file)
index 0000000..78ef911
--- /dev/null
@@ -0,0 +1,19 @@
+ <div id="push"></div>
+ </div> <!-- #content -->
+  </div> <!-- .layout -->
+ </div> <!-- #container -->
+  <div id="footer">
+    <table><tr>
+      <td class="poweredby"><img src="doxygen.png"></td>
+      <td class="copyright">Copyright &copy;$year Enlightenment</td>
+      <td class="generated">Docs generated $datetime</td>
+    </tr></table>
+  </div>
+
+
+</body>
+</html>
diff --git a/doc/head.html b/doc/head.html
new file mode 100644 (file)
index 0000000..44a5551
--- /dev/null
@@ -0,0 +1,64 @@
+<html>
+<head>
+    <title>$title</title>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8">
+    <meta name="author" content="Andres Blanc" >
+    
+    <link rel="icon" href="img/favicon.png" type="image/x-icon">
+    <link rel="shortcut icon" href="img/favicon.png" type="image/x-icon">
+    <link rel="icon" href="img/favicon.png" type="image/ico">
+    <link rel="shortcut icon" href="img/favicon.png" type="image/ico">
+
+    <link rel="stylesheet" type="text/css" media="screen" href="e.css">
+    <link rel="stylesheet" type="text/css" media="screen" href="edoxy.css">
+</head>
+
+<body>
+
+<div id="container">
+
+<div id="header">
+<div class="layout">
+    
+    <h1><span>Enlightenment</span></h1>
+    <h2><span>Beauty at your fingertips</span></h2>
+
+    <div class="menu-container">
+        <div class="menu">
+            <ul>
+               <li class="current"><a href="http://web.enlightenment.org/p.php?p=docs">Docs</a></li>
+                <li><a href="http://trac.enlightenment.org/e">Tracker</a></li>
+                <li><a href="http://www.enlightenment.org/p.php?p=contact">Contact</a></li>
+                <li><a href="http://www.enlightenment.org/p.php?p=contribute">Contribute</a></li>
+                <li><a href="http://www.enlightenment.org/p.php?p=support">Support</a></li>
+                <li><a href="http://www.enlightenment.org/p.php?p=download">Download</a></li>
+                <li><a href="http://www.enlightenment.org/p.php?p=about">About</a></li>
+                <li><a href="http://www.enlightenment.org/p.php?p=news">News</a></li>
+                <li><a href="http://www.enlightenment.org/">Home</a></li>
+            </ul>
+        </div>
+    </div>
+
+    <div class="doxytitle">
+        $projectname Documentation <small>at $date</small>
+    </div>
+
+    <div class="menu-container">
+        <div class="submenu">
+            <ul class="current">
+                <li><a href="pages.html">Tutorials</a></li>
+<!--                <li><a href="globals.html">Globals</a></li>-->
+<!--                <li><a href="files.html">Files</a></li>-->
+                <li><a href="modules.html">Modules</a></li>
+               <li class="current"><a  href="index.html">Main Page</a></li>
+            </ul>
+        </div>
+    </div>
+
+
+    <div class="clear"></div>
+</div>
+</div>
+
+<div id="content">
+<div class="layout">
diff --git a/doc/img/e.png b/doc/img/e.png
new file mode 100644 (file)
index 0000000..b3884a5
Binary files /dev/null and b/doc/img/e.png differ
diff --git a/doc/img/edoxy.css b/doc/img/edoxy.css
new file mode 100644 (file)
index 0000000..3caf7a9
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ * This file contain a custom doxygen style to match e.org graphics
+ */
+
+
+
+/* BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV {
+       font-family: Geneva, Arial, Helvetica, sans-serif;
+}*/ 
+BODY, TD {
+       font-size: 12px;
+}
+H1 {
+       text-align: center;
+       font-size: 160%;
+}
+H2 {
+       font-size: 120%;
+}
+H3 {
+       font-size: 100%;
+}
+CAPTION { 
+       font-weight: bold 
+}
+DIV.qindex {
+       width: 100%;
+       background-color: #e8eef2;
+       border: 1px solid #84b0c7;
+       text-align: center;
+       margin: 2px;
+       padding: 2px;
+       line-height: 140%;
+}
+DIV.navpath {
+       width: 100%;
+       background-color: #e8eef2;
+       border: 1px solid #84b0c7;
+       text-align: center;
+       margin: 2px;
+       padding: 2px;
+       line-height: 140%;
+}
+DIV.navtab {
+       background-color: #e8eef2;
+       border: 1px solid #84b0c7;
+       text-align: center;
+       margin: 2px;
+       margin-right: 15px;
+       padding: 2px;
+}
+TD.navtab {
+       font-size: 70%;
+}
+A.qindex {
+       text-decoration: none;
+       font-weight: bold;
+       color: #1A419D;
+}
+A.qindex:visited {
+       text-decoration: none;
+       font-weight: bold;
+       color: #1A419D
+}
+A.qindex:hover {
+       text-decoration: none;
+       background-color: #ddddff;
+}
+A.qindexHL {
+       text-decoration: none;
+       font-weight: bold;
+       background-color: #6666cc;
+       color: #ffffff;
+       border: 1px double #9295C2;
+}
+A.qindexHL:hover {
+       text-decoration: none;
+       background-color: #6666cc;
+       color: #ffffff;
+}
+A.qindexHL:visited { 
+       text-decoration: none; 
+       background-color: #6666cc; 
+       color: #ffffff 
+}
+A.el { 
+       text-decoration: none; 
+       font-weight: bold 
+}
+A.elRef { 
+       font-weight: bold 
+}
+A.code:link { 
+       text-decoration: none; 
+       font-weight: normal; 
+       color: #0000FF
+}
+A.code:visited { 
+       text-decoration: none; 
+       font-weight: normal; 
+       color: #0000FF
+}
+A.codeRef:link { 
+       font-weight: normal; 
+       color: #0000FF
+}
+A.codeRef:visited { 
+       font-weight: normal; 
+       color: #0000FF
+}
+A:hover, A:visited:hover { 
+       text-decoration: none;  
+       /* background-color: #f2f2ff; */
+       color: #000055;
+}
+A.anchor {
+       color: #000;
+}
+DL.el { 
+       margin-left: -1cm 
+}
+.fragment {
+       font-family: monospace, fixed;
+       font-size: 95%;
+}
+PRE.fragment {
+       border: 1px solid #CCCCCC;
+       background-color: #f5f5f5;
+       margin-top: 4px;
+       margin-bottom: 4px;
+       margin-left: 2px;
+       margin-right: 8px;
+       padding-left: 6px;
+       padding-right: 6px;
+       padding-top: 4px;
+       padding-bottom: 4px;
+}
+DIV.ah { 
+       background-color: black; 
+       font-weight: bold; 
+       color: #ffffff; 
+       margin-bottom: 3px; 
+       margin-top: 3px 
+}
+
+DIV.groupHeader {
+       margin-left: 16px;
+       margin-top: 12px;
+       margin-bottom: 6px;
+       font-weight: bold;
+}
+DIV.groupText { 
+       margin-left: 16px; 
+       font-style: italic; 
+       font-size: 90% 
+}
+/*BODY {
+       background: white;
+       color: black;
+       margin-right: 20px;
+       margin-left: 20px;
+}*/
+TD.indexkey {
+       background-color: #e8eef2;
+       font-weight: bold;
+       padding-right  : 10px;
+       padding-top    : 2px;
+       padding-left   : 10px;
+       padding-bottom : 2px;
+       margin-left    : 0px;
+       margin-right   : 0px;
+       margin-top     : 2px;
+       margin-bottom  : 2px;
+       border: 1px solid #CCCCCC;
+}
+TD.indexvalue {
+       background-color: #e8eef2;
+       font-style: italic;
+       padding-right  : 10px;
+       padding-top    : 2px;
+       padding-left   : 10px;
+       padding-bottom : 2px;
+       margin-left    : 0px;
+       margin-right   : 0px;
+       margin-top     : 2px;
+       margin-bottom  : 2px;
+       border: 1px solid #CCCCCC;
+}
+TR.memlist {
+       background-color: #f0f0f0; 
+}
+P.formulaDsp { 
+       text-align: center; 
+}
+IMG.formulaDsp {
+}
+IMG.formulaInl { 
+       vertical-align: middle; 
+}
+SPAN.keyword       { color: #008000 }
+SPAN.keywordtype   { color: #604020 }
+SPAN.keywordflow   { color: #e08000 }
+SPAN.comment       { color: #800000 }
+SPAN.preprocessor  { color: #806020 }
+SPAN.stringliteral { color: #002080 }
+SPAN.charliteral   { color: #008080 }
+SPAN.vhdldigit     { color: #ff00ff }
+SPAN.vhdlchar      { color: #000000 }
+SPAN.vhdlkeyword   { color: #700070 }
+SPAN.vhdllogic     { color: #ff0000 }
+
+.mdescLeft {
+       padding: 0px 8px 4px 8px;
+       font-size: 80%;
+       font-style: italic;
+       background-color: #FAFAFA;
+       border-top: 1px none #E0E0E0;
+       border-right: 1px none #E0E0E0;
+       border-bottom: 1px none #E0E0E0;
+       border-left: 1px none #E0E0E0;
+       margin: 0px;
+}
+.mdescRight {
+        padding: 0px 8px 4px 8px;
+       font-size: 80%;
+       font-style: italic;
+       background-color: #FAFAFA;
+       border-top: 1px none #E0E0E0;
+       border-right: 1px none #E0E0E0;
+       border-bottom: 1px none #E0E0E0;
+       border-left: 1px none #E0E0E0;
+       margin: 0px;
+}
+.memItemLeft {
+       padding: 1px 0px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: solid;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.memItemRight {
+       padding: 1px 8px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: solid;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.memTemplItemLeft {
+       padding: 1px 0px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: none;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.memTemplItemRight {
+       padding: 1px 8px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: none;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.memTemplParams {
+       padding: 1px 0px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: solid;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       color: #606060;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.search { 
+       color: #003399;
+       font-weight: bold;
+}
+FORM.search {
+       margin-bottom: 0px;
+       margin-top: 0px;
+}
+INPUT.search { 
+       font-size: 75%;
+       color: #000080;
+       font-weight: normal;
+       background-color: #e8eef2;
+}
+TD.tiny { 
+       font-size: 75%;
+}
+a {
+       color: #1A41A8;
+}
+a:visited {
+       color: #2A3798;
+}
+.dirtab { 
+       padding: 4px;
+       border-collapse: collapse;
+       border: 1px solid #84b0c7;
+}
+TH.dirtab { 
+       background: #e8eef2;
+       font-weight: bold;
+}
+HR { 
+       height: 1px;
+       border: none;
+       border-top: 1px solid black;
+}
+
+/* Style for detailed member documentation */
+.memtemplate {
+       font-size: 80%;
+       color: #606060;
+       font-weight: normal;
+       margin-left: 3px;
+} 
+.memnav { 
+       background-color: #e8eef2;
+       border: 1px solid #84b0c7;
+       text-align: center;
+       margin: 2px;
+       margin-right: 15px;
+       padding: 2px;
+}
+.memitem {
+       padding: 4px;
+       background-color: #eef3f5;
+       border-width: 1px;
+       border-style: solid;
+       border-color: #dedeee;
+       -moz-border-radius: 8px 8px 8px 8px;
+}
+.memname {
+       white-space: nowrap;
+       font-weight: bold;
+}
+.memdoc{
+       padding-left: 10px;
+}
+.memproto {
+       background-color: #d5e1e8;
+       width: 100%;
+       border-width: 1px;
+       border-style: solid;
+       border-color: #84b0c7;
+       font-weight: bold;
+       -moz-border-radius: 8px 8px 8px 8px;
+}
+.paramkey {
+       text-align: right;
+}
+.paramtype {
+       white-space: nowrap;
+}
+.paramname {
+       color: #602020;
+       font-style: italic;
+       white-space: nowrap;
+}
+/* End Styling for detailed member documentation */
+
+/* for the tree view */
+.ftvtree {
+       font-family: sans-serif;
+       margin:0.5em;
+}
+/* these are for tree view when used as main index */
+.directory { 
+       font-size: 9pt; 
+       font-weight: bold; 
+}
+.directory h3 { 
+       margin: 0px; 
+       margin-top: 1em; 
+       font-size: 11pt; 
+}
+
+/* The following two styles can be used to replace the root node title */
+/* with an image of your choice.  Simply uncomment the next two styles, */
+/* specify the name of your image and be sure to set 'height' to the */
+/* proper pixel height of your image. */
+
+/* .directory h3.swap { */
+/*     height: 61px; */
+/*     background-repeat: no-repeat; */
+/*     background-image: url("yourimage.gif"); */
+/* } */
+/* .directory h3.swap span { */
+/*     display: none; */
+/* } */
+
+.directory > h3 { 
+       margin-top: 0; 
+}
+.directory p { 
+       margin: 0px; 
+       white-space: nowrap; 
+}
+.directory div { 
+       display: none; 
+       margin: 0px; 
+}
+.directory img { 
+       vertical-align: -30%; 
+}
+/* these are for tree view when not used as main index */
+.directory-alt { 
+       font-size: 100%; 
+       font-weight: bold; 
+}
+.directory-alt h3 { 
+       margin: 0px; 
+       margin-top: 1em; 
+       font-size: 11pt; 
+}
+.directory-alt > h3 { 
+       margin-top: 0; 
+}
+.directory-alt p { 
+       margin: 0px; 
+       white-space: nowrap; 
+}
+.directory-alt div { 
+       display: none; 
+       margin: 0px; 
+}
+.directory-alt img { 
+       vertical-align: -30%; 
+}
+
diff --git a/doc/img/foot_bg.png b/doc/img/foot_bg.png
new file mode 100644 (file)
index 0000000..b24f3a4
Binary files /dev/null and b/doc/img/foot_bg.png differ
diff --git a/doc/img/head_bg.png b/doc/img/head_bg.png
new file mode 100644 (file)
index 0000000..081dc13
Binary files /dev/null and b/doc/img/head_bg.png differ
diff --git a/doc/img/menu_bg.png b/doc/img/menu_bg.png
new file mode 100644 (file)
index 0000000..e978743
Binary files /dev/null and b/doc/img/menu_bg.png differ
diff --git a/doc/img/menu_bg_current.png b/doc/img/menu_bg_current.png
new file mode 100644 (file)
index 0000000..de97c92
Binary files /dev/null and b/doc/img/menu_bg_current.png differ
diff --git a/doc/img/menu_bg_hover.png b/doc/img/menu_bg_hover.png
new file mode 100644 (file)
index 0000000..3fd851d
Binary files /dev/null and b/doc/img/menu_bg_hover.png differ
diff --git a/doc/img/menu_bg_last.png b/doc/img/menu_bg_last.png
new file mode 100644 (file)
index 0000000..88c116c
Binary files /dev/null and b/doc/img/menu_bg_last.png differ
diff --git a/doc/img/menu_bg_unsel.png b/doc/img/menu_bg_unsel.png
new file mode 100644 (file)
index 0000000..50e5fd8
Binary files /dev/null and b/doc/img/menu_bg_unsel.png differ
diff --git a/ethumb.pc.in b/ethumb.pc.in
new file mode 100644 (file)
index 0000000..233e2ed
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: ethumb
+Description: Thumbnail Generator Library
+Requires: @requirement_ethumb@
+Version: @VERSION@
+Libs: -L${libdir} -lethumb
+Cflags: -I${includedir}/ethumb-@VMAJ@
diff --git a/ethumb.spec.in b/ethumb.spec.in
new file mode 100644 (file)
index 0000000..4022866
--- /dev/null
@@ -0,0 +1,69 @@
+%{!?_rel:%{expand:%%global _rel 0.enl%{?dist}}}
+%define _missing_doc_files_terminate_build 0
+
+Summary: Thumbnail Generation Library
+Name: @PACKAGE@
+Version: @VERSION@
+Release: %{_rel}
+License: LGPLv2.1
+Group: System Environment/Libraries
+Source: http://download.enlightenment.org/releases/%{name}-%{version}.tar.gz
+Packager: %{?_packager:%{_packager}}%{!?_packager:Rui Miguel Silva Seabra <rms@1407.org>}
+Vendor: %{?_vendorinfo:%{_vendorinfo}}%{!?_vendorinfo:The Enlightenment Project (http://www.enlightenment.org/)}
+Distribution: %{?_distribution:%{_distribution}}%{!?_distribution:%{_vendor}}
+URL: http://www.enlightenment.org/
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+
+%description
+Ethumb is a thumbnail generation library
+
+%package devel
+Summary: Ethumb headers, static libraries, documentation and test programs
+Group: System Environment/Libraries
+Requires: %{name} = %{version}
+
+%description devel
+Headers, static libraries, test programs and documentation for Ethumb
+
+%prep
+%setup -q
+
+%build
+%{configure} --prefix=%{_prefix}
+%{__make} %{?_smp_mflags} %{?mflags}
+
+%install
+%{__make} %{?mflags_install} DESTDIR=$RPM_BUILD_ROOT install
+
+%clean
+test "x$RPM_BUILD_ROOT" != "x/" && rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/ldconfig
+
+%postun
+/sbin/ldconfig
+
+%files
+%defattr(-, root, root)
+%doc AUTHORS COPYING README
+%{_libdir}/*.so.*
+%{_bindir}/ethumb
+%{_bindir}/ethumbd
+%{_bindir}/ethumbd_client
+%{_datadir}/dbus-1/services/org.enlightenment.Ethumb.service
+%{_datadir}/ethumb/data/frames/default.edj
+
+%files devel
+%defattr(-, root, root)
+%{_includedir}/*
+%{_libdir}/*.a
+%{_libdir}/*.so
+%{_libdir}/*.la
+%{_libdir}/pkgconfig/*
+%{_libdir}/ethumb/plugins/*.so
+%{_libdir}/ethumb/plugins/*.la
+%{_libdir}/ethumb/plugins/data/emotion_template.edj
+%{_libexecdir}/ethumbd_slave
+
+%changelog
diff --git a/ethumb_client.pc.in b/ethumb_client.pc.in
new file mode 100644 (file)
index 0000000..c9eebc6
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: ethumb_client
+Description: Thumbnail Client Library
+Requires: @requirement_ethumb_client@
+Version: @VERSION@
+Libs: -L${libdir} -lethumb_client
+Cflags: -I${includedir}/ethumb-@VMAJ@
diff --git a/m4/Makefile.am b/m4/Makefile.am
new file mode 100644 (file)
index 0000000..688b1e4
--- /dev/null
@@ -0,0 +1,9 @@
+MAINTAINERCLEANFILES = Makefile.in
+EXTRA_DIST = iconv.m4 \
+       lib-ld.m4 \
+       lib-link.m4 \
+       lib-prefix.m4 \
+       progtest.m4 \
+       as-expand.m4 \
+       ac-modules.m4 \
+       efl_doxygen.m4
diff --git a/m4/ac-modules.m4 b/m4/ac-modules.m4
new file mode 100644 (file)
index 0000000..b1088f1
--- /dev/null
@@ -0,0 +1,168 @@
+dnl _XTERM_COLORS
+define([_XTERM_COLORS],
+[
+        # Check for XTerm and define some colors
+        if test "x$TERM" = "xxterm"; then
+                COLOR_PREF="\0033\0133"
+                COLOR_H="${COLOR_PREF}1m"
+                COLOR_HGREEN="${COLOR_PREF}1;32m"
+                COLOR_HRED="${COLOR_PREF}1;31m"
+                COLOR_GREEN="${COLOR_PREF}32m"
+                COLOR_RED="${COLOR_PREF}31m"
+                COLOR_YELLOW="${COLOR_PREF}1;33m"
+                COLOR_END="${COLOR_PREF}0m"
+        else
+                COLOR_H=""
+                COLOR_HGREEN=""
+                COLOR_HRED=""
+                COLOR_GREEN=""
+                COLOR_RED=""
+                COLOR_YELLOW=""
+                COLOR_END=""
+        fi
+])
+
+dnl AC_ETH_CHECK_PKG(name, lib [>= version], [action-if, [action-not]])
+dnl   improved version of PKG_CHECK_MODULES, it does the same checking
+dnl   and defines HAVE_[name]=yes/no and also exports
+dnl   [name]_CFLAGS and [name]_LIBS.
+dnl
+dnl   if action-not isn't provided, AC_MSG_ERROR will be used.
+dnl
+dnl   Checks:
+dnl       lib >= version
+dnl
+dnl   Provides:
+dnl       - HAVE_[name]=yes|no
+dnl       - [name]_CFLAGS: if HAVE_[name]=yes
+dnl       - [name]_LIBS: if HAVE_[name]=yes
+dnl       - [name]_VERSION: if HAVE_[name]=yes
+dnl
+AC_DEFUN([AC_ETH_CHECK_PKG],
+[
+# ----------------------------------------------------------------------
+# BEGIN: Check library with pkg-config: $1 (pkg-config=$2)
+#
+
+        PKG_CHECK_MODULES([$1], [$2],
+                          [
+                                HAVE_[$1]=yes
+                                [pkg_name]=$(echo "[$2]" | cut -d\   -f1)
+                                [$1]_VERSION=$($PKG_CONFIG --modversion $pkg_name)
+                                AC_SUBST([$1]_VERSION)
+                                AC_SUBST([$1]_CFLAGS)
+                                AC_SUBST([$1]_LIBS)
+                                ifelse([$3], , :, [$3])
+                          ],
+                          [
+                                HAVE_[$1]=no
+                                ifelse([$4], , AC_MSG_ERROR(you need [$2] development installed!), AC_MSG_RESULT(no); [$4])
+                          ])
+        AM_CONDITIONAL(HAVE_[$1], test x$HAVE_[$1] = xyes)
+        AC_SUBST(HAVE_[$1])
+        if test x$HAVE_[$1] = xyes; then
+                AC_DEFINE_UNQUOTED(HAVE_[$1], 1, Package [$1] ($2) found.)
+        fi
+
+#
+# END: Check library with pkg-config: $1 (pkg-config=$2)
+# ----------------------------------------------------------------------
+])
+
+dnl AC_ETH_OPTIONAL_MODULE(name, [initial-status, [check-if-enabled]])
+dnl   Defines configure argument --<enable|disable>-[name] to enable an
+dnl   optional module called 'name'.
+dnl
+dnl   If initial-status is true, then it's enabled by default and option
+dnl   will be called --disable-[name], otherwise it's disabled and option
+dnl   is --enable-[name].
+dnl
+dnl   If module is enabled, then check-if-enabled will be executed. This
+dnl   may change the contents of shell variable NAME (uppercase version of
+dnl   name, with underscores instead of dashed) to something different than
+dnl   "true" to disable module.
+dnl
+dnl Parameters:
+dnl     - name: module name to use. It will be converted to have dashes (-)
+dnl          instead of underscores, and will be in lowercase.
+dnl     - initial-status: true or false, states if module is enabled or
+dnl          disabled by default.
+dnl     - check-if-enabled: macro to be expanded inside check for enabled
+dnl           module.
+dnl
+dnl Provides:
+dnl     - USE_MODULE_[name]=true|false [make, shell]
+dnl     - USE_MODULE_[name]=1 if enabled [config.h]
+dnl
+AC_DEFUN([AC_ETH_OPTIONAL_MODULE],
+[
+# ----------------------------------------------------------------------
+# BEGIN: Check for optional module: $1 (default: $2)
+#
+        m4_pushdef([MODNAME], [m4_bpatsubst(m4_toupper([$1]), -, _)])dnl
+        m4_pushdef([modname_opt], [m4_bpatsubst(m4_tolower([$1]), _, -)])
+        m4_pushdef([INITVAL], [m4_default([$2], [false])])dnl
+        m4_pushdef([ENABLE_HELP], AS_HELP_STRING([--enable-modname_opt],
+                               [enable optional module modname_opt. Default is disabled.])
+                )dnl
+        m4_pushdef([DISABLE_HELP], AS_HELP_STRING([--disable-modname_opt],
+                               [disable optional module modname_opt. Default is enabled.])
+                )dnl
+        m4_pushdef([HELP_STR], m4_if(INITVAL, [true], [DISABLE_HELP], [ENABLE_HELP]))dnl
+        m4_pushdef([NOT_INITVAL], m4_if(INITVAL, [true], [false], [true]))dnl
+
+        USING_MODULES=1
+
+        MODNAME=INITVAL
+        AC_ARG_ENABLE(modname_opt, HELP_STR, [MODNAME=${enableval:-NOT_INITVAL}])
+        if test x[$]MODNAME = xyes || test x[$]MODNAME = x1; then
+                MODNAME=true
+        fi
+        if test x[$]MODNAME = xno || test x[$]MODNAME = x0; then
+                MODNAME=false
+        fi
+
+        _XTERM_COLORS
+
+        # Check list for optional module $1
+        if test x[$]MODNAME = xtrue; then
+                ifelse([$3], , , [
+echo
+echo "checking optional module modname_opt:"
+# BEGIN: User checks
+$3
+# END: User checks
+if test x[$]MODNAME = xfalse; then
+        echo -e "optional module modname_opt ${COLOR_HRED}failed${COLOR_END} checks."
+else
+        echo -e "optional module modname_opt passed checks."
+fi
+echo
+])
+
+                if test x[$]MODNAME = xfalse; then
+                        echo -e "${COLOR_YELLOW}Warning:${COLOR_END} optional module ${COLOR_H}modname_opt${COLOR_END} disabled by extra checks."
+                fi
+        fi
+
+        # Check if user checks succeeded
+        if test x[$]MODNAME = xtrue; then
+                [OPTIONAL_MODULES]="$[OPTIONAL_MODULES] modname_opt"
+                AC_DEFINE_UNQUOTED(USE_MODULE_[]MODNAME, 1, Use module modname_opt)
+        else
+                [UNUSED_OPTIONAL_MODULES]="$[UNUSED_OPTIONAL_MODULES] modname_opt"
+        fi
+
+        AM_CONDITIONAL(USE_MODULE_[]MODNAME, test x[$]MODNAME = xtrue)
+        AC_SUBST(USE_MODULE_[]MODNAME)
+        USE_MODULE_[]MODNAME=[$]MODNAME
+
+        m4_popdef([HELP_STR])dnl
+        m4_popdef([DISABLE_HELP])dnl
+        m4_popdef([ENABLE_HELP])dnl
+        m4_popdef([INITVAL])dnl
+        m4_popdef([MODNAME])
+#
+# END: Check for optional module: $1 ($2)
+# ----------------------------------------------------------------------
+])
diff --git a/m4/ac_attribute.m4 b/m4/ac_attribute.m4
new file mode 100644 (file)
index 0000000..23479a9
--- /dev/null
@@ -0,0 +1,47 @@
+dnl Copyright (C) 2004-2008 Kim Woelders
+dnl Copyright (C) 2008 Vincent Torri <vtorri at univ-evry dot fr>
+dnl That code is public domain and can be freely used or copied.
+dnl Originally snatched from somewhere...
+
+dnl Macro for checking if the compiler supports __attribute__
+
+dnl Usage: AC_C___ATTRIBUTE__
+dnl call AC_DEFINE for HAVE___ATTRIBUTE__ and __UNUSED__
+dnl if the compiler supports __attribute__, HAVE___ATTRIBUTE__ is
+dnl defined to 1 and __UNUSED__ is defined to __attribute__((unused))
+dnl otherwise, HAVE___ATTRIBUTE__ is not defined and __UNUSED__ is
+dnl defined to nothing.
+
+AC_DEFUN([AC_C___ATTRIBUTE__],
+[
+
+AC_MSG_CHECKING([for __attribute__])
+
+AC_CACHE_VAL([ac_cv___attribute__],
+   [AC_TRY_COMPILE(
+       [
+#include <stdlib.h>
+
+int func(int x);
+int foo(int x __attribute__ ((unused)))
+{
+   exit(1);
+}
+       ],
+       [],
+       [ac_cv___attribute__="yes"],
+       [ac_cv___attribute__="no"]
+    )])
+
+AC_MSG_RESULT($ac_cv___attribute__)
+
+if test "x${ac_cv___attribute__}" = "xyes" ; then
+   AC_DEFINE([HAVE___ATTRIBUTE__], [1], [Define to 1 if your compiler has __attribute__])
+   AC_DEFINE([__UNUSED__], [__attribute__((unused))], [Macro declaring a function argument to be unused])
+  else
+    AC_DEFINE([__UNUSED__], [], [Macro declaring a function argument to be unused])
+fi
+
+])
+
+dnl End of ac_attribute.m4
diff --git a/m4/as-expand.m4 b/m4/as-expand.m4
new file mode 100644 (file)
index 0000000..7b7e4a8
--- /dev/null
@@ -0,0 +1,43 @@
+dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR)
+dnl
+dnl example
+dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)
+dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local
+
+AC_DEFUN([AS_AC_EXPAND],
+[
+  EXP_VAR=[$1]
+  FROM_VAR=[$2]
+
+  dnl first expand prefix and exec_prefix if necessary
+  prefix_save=$prefix
+  exec_prefix_save=$exec_prefix
+
+  dnl if no prefix given, then use /usr/local, the default prefix
+  if test "x$prefix" = "xNONE"; then
+    prefix=$ac_default_prefix
+  fi
+  dnl if no exec_prefix given, then use prefix
+  if test "x$exec_prefix" = "xNONE"; then
+    exec_prefix=$prefix
+  fi
+
+  full_var="$FROM_VAR"
+  dnl loop until it doesn't change anymore
+  while true; do
+    new_full_var="`eval echo $full_var`"
+    dnl if test "x$new_full_var" = "x$full_var"; then
+    if test "x${new_full_var:0:1}" != "x\$"; then
+        break;
+    fi
+    full_var=$new_full_var
+  done
+
+  dnl clean up
+  full_var=$new_full_var
+  AC_SUBST([$1], "$full_var")
+
+  dnl restore prefix and exec_prefix
+  prefix=$prefix_save
+  exec_prefix=$exec_prefix_save
+])
diff --git a/m4/efl_binary.m4 b/m4/efl_binary.m4
new file mode 100644 (file)
index 0000000..c774688
--- /dev/null
@@ -0,0 +1,71 @@
+dnl Copyright (C) 2010 Vincent Torri <vtorri at univ-evry dot fr>
+dnl That code is public domain and can be freely used or copied.
+
+dnl Macro that check if a binary is built or not
+
+dnl Usage: EFL_ENABLE_BIN(binary)
+dnl Call AC_SUBST(BINARY_PRG) (BINARY is the uppercase of binary, - being transformed into _)
+dnl Define have_binary (- is transformed into _)
+dnl Define conditional BUILD_BINARY (BINARY is the uppercase of binary, - being transformed into _)
+
+AC_DEFUN([EFL_ENABLE_BIN],
+[
+
+m4_pushdef([UP], m4_translit([[$1]], [-a-z], [_A-Z]))dnl
+m4_pushdef([DOWN], m4_translit([[$1]], [-A-Z], [_a-z]))dnl
+
+have_[]m4_defn([DOWN])="yes"
+
+dnl configure option
+
+AC_ARG_ENABLE([$1],
+   [AC_HELP_STRING([--disable-$1], [disable building of ]DOWN)],
+   [
+    if test "x${enableval}" = "xyes" ; then
+       have_[]m4_defn([DOWN])="yes"
+    else
+       have_[]m4_defn([DOWN])="no"
+    fi
+   ])
+
+AC_MSG_CHECKING([whether to build ]DOWN[ binary])
+AC_MSG_RESULT([$have_[]m4_defn([DOWN])])
+
+if test "x$have_[]m4_defn([DOWN])" = "xyes"; then
+   UP[]_PRG=DOWN[${EXEEXT}]
+fi
+
+AC_SUBST(UP[]_PRG)
+
+AM_CONDITIONAL(BUILD_[]UP, test "x$have_[]m4_defn([DOWN])" = "xyes")
+
+AS_IF([test "x$have_[]m4_defn([DOWN])" = "xyes"], [$2], [$3])
+
+])
+
+
+dnl Macro that check if a binary is built or not
+
+dnl Usage: EFL_WITH_BIN(package, binary, default_value)
+dnl Call AC_SUBST(_binary) (_binary is the lowercase of binary, - being transformed into _ by default, or the value set by the user)
+
+AC_DEFUN([EFL_WITH_BIN],
+[
+
+m4_pushdef([DOWN], m4_translit([[$2]], [-A-Z], [_a-z]))dnl
+
+dnl configure option
+
+AC_ARG_WITH([$2],
+   [AC_HELP_STRING([--with-$2=PATH], [specify a specific path to ]DOWN[ @<:@default=$3@:>@])],
+   [_efl_with_binary=${withval}],
+   [_efl_with_binary=$(pkg-config --variable=prefix $1)/bin/$3])
+
+DOWN=${_efl_with_binary}
+AC_MSG_NOTICE(DOWN[ set to ${_efl_with_binary}])
+
+with_binary_[]m4_defn([DOWN])=${_efl_with_binary}
+
+AC_SUBST(DOWN)
+
+])
diff --git a/m4/efl_doxygen.m4 b/m4/efl_doxygen.m4
new file mode 100644 (file)
index 0000000..d83ed68
--- /dev/null
@@ -0,0 +1,97 @@
+dnl Copyright (C) 2008 Vincent Torri <vtorri at univ-evry dot fr>
+dnl That code is public domain and can be freely used or copied.
+
+dnl Macro that check if doxygen is available or not.
+
+dnl EFL_CHECK_DOXYGEN([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl Test for the doxygen program
+dnl Defines efl_doxygen
+dnl Defines the automake conditionnal EFL_BUILD_DOC
+dnl
+AC_DEFUN([EFL_CHECK_DOXYGEN],
+[
+
+dnl
+dnl Disable the build of the documentation
+dnl
+AC_ARG_ENABLE([doc],
+   [AC_HELP_STRING(
+       [--disable-doc],
+       [Disable documentation build @<:@default=enabled@:>@])],
+   [
+    if test "x${enableval}" = "xyes" ; then
+       efl_enable_doc="yes"
+    else
+       efl_enable_doc="no"
+    fi
+   ],
+   [efl_enable_doc="yes"])
+
+AC_MSG_CHECKING([whether to build documentation])
+AC_MSG_RESULT([${efl_enable_doc}])
+
+if test "x${efl_enable_doc}" = "xyes" ; then
+
+dnl Specify the file name, without path
+
+   efl_doxygen="doxygen"
+
+   AC_ARG_WITH([doxygen],
+      [AC_HELP_STRING(
+          [--with-doxygen=FILE],
+          [doxygen program to use @<:@default=doxygen@:>@])],
+
+dnl Check the given doxygen program.
+
+      [efl_doxygen=${withval}
+       AC_CHECK_PROG([efl_have_doxygen],
+          [${efl_doxygen}],
+          [yes],
+          [no])
+       if test "x${efl_have_doxygen}" = "xno" ; then
+          echo "WARNING:"
+          echo "The doxygen program you specified:"
+          echo "${efl_doxygen}"
+          echo "was not found.  Please check the path and make sure "
+          echo "the program exists and is executable."
+          AC_MSG_WARN([no doxygen detected. Documentation will not be built])
+       fi
+      ],
+      [AC_CHECK_PROG([efl_have_doxygen],
+          [${efl_doxygen}],
+          [yes],
+          [no])
+       if test "x${efl_have_doxygen}" = "xno" ; then
+          echo "WARNING:"
+          echo "The doxygen program was not found in your execute path."
+          echo "You may have doxygen installed somewhere not covered by your path."
+          echo ""
+          echo "If this is the case make sure you have the packages installed, AND"
+          echo "that the doxygen program is in your execute path (see your"
+          echo "shell manual page on setting the \$PATH environment variable), OR"
+          echo "alternatively, specify the program to use with --with-doxygen."
+          AC_MSG_WARN([no doxygen detected. Documentation will not be built])
+       fi
+      ])
+fi
+
+dnl
+dnl Substitution
+dnl
+AC_SUBST([efl_doxygen])
+
+if ! test "x${efl_have_doxygen}" = "xyes" ; then
+   efl_enable_doc="no"
+fi
+
+AM_CONDITIONAL(EFL_BUILD_DOC, test "x${efl_enable_doc}" = "xyes")
+
+if test "x${efl_enable_doc}" = "xyes" ; then
+  m4_default([$1], [:])
+else
+  m4_default([$2], [:])
+fi
+
+])
+
+dnl End of efl_doxygen.m4
diff --git a/m4/iconv.m4 b/m4/iconv.m4
new file mode 100644 (file)
index 0000000..66bc76f
--- /dev/null
@@ -0,0 +1,180 @@
+# iconv.m4 serial AM6 (gettext-0.17)
+dnl Copyright (C) 2000-2002, 2007 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+AC_DEFUN([AM_ICONV_LINKFLAGS_BODY],
+[
+  dnl Prerequisites of AC_LIB_LINKFLAGS_BODY.
+  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+  AC_REQUIRE([AC_LIB_RPATH])
+
+  dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV
+  dnl accordingly.
+  AC_LIB_LINKFLAGS_BODY([iconv])
+])
+
+AC_DEFUN([AM_ICONV_LINK],
+[
+  dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and
+  dnl those with the standalone portable GNU libiconv installed).
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV
+  dnl accordingly.
+  AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY])
+
+  dnl Add $INCICONV to CPPFLAGS before performing the following checks,
+  dnl because if the user has installed libiconv and not disabled its use
+  dnl via --without-libiconv-prefix, he wants to use it. The first
+  dnl AC_TRY_LINK will then fail, the second AC_TRY_LINK will succeed.
+  am_save_CPPFLAGS="$CPPFLAGS"
+  AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV])
+
+  AC_CACHE_CHECK([for iconv], am_cv_func_iconv, [
+    am_cv_func_iconv="no, consider installing GNU libiconv"
+    am_cv_lib_iconv=no
+    AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+      [iconv_t cd = iconv_open("","");
+       iconv(cd,NULL,NULL,NULL,NULL);
+       iconv_close(cd);],
+      am_cv_func_iconv=yes)
+    if test "$am_cv_func_iconv" != yes; then
+      am_save_LIBS="$LIBS"
+      LIBS="$LIBS $LIBICONV"
+      AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+        [iconv_t cd = iconv_open("","");
+         iconv(cd,NULL,NULL,NULL,NULL);
+         iconv_close(cd);],
+        am_cv_lib_iconv=yes
+        am_cv_func_iconv=yes)
+      LIBS="$am_save_LIBS"
+    fi
+  ])
+  if test "$am_cv_func_iconv" = yes; then
+    AC_CACHE_CHECK([for working iconv], am_cv_func_iconv_works, [
+      dnl This tests against bugs in AIX 5.1 and HP-UX 11.11.
+      am_save_LIBS="$LIBS"
+      if test $am_cv_lib_iconv = yes; then
+        LIBS="$LIBS $LIBICONV"
+      fi
+      AC_TRY_RUN([
+#include <iconv.h>
+#include <string.h>
+int main ()
+{
+  /* Test against AIX 5.1 bug: Failures are not distinguishable from successful
+     returns.  */
+  {
+    iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8");
+    if (cd_utf8_to_88591 != (iconv_t)(-1))
+      {
+        static const char input[] = "\342\202\254"; /* EURO SIGN */
+        char buf[10];
+        const char *inptr = input;
+        size_t inbytesleft = strlen (input);
+        char *outptr = buf;
+        size_t outbytesleft = sizeof (buf);
+        size_t res = iconv (cd_utf8_to_88591,
+                            (char **) &inptr, &inbytesleft,
+                            &outptr, &outbytesleft);
+        if (res == 0)
+          return 1;
+      }
+  }
+#if 0 /* This bug could be worked around by the caller.  */
+  /* Test against HP-UX 11.11 bug: Positive return value instead of 0.  */
+  {
+    iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591");
+    if (cd_88591_to_utf8 != (iconv_t)(-1))
+      {
+        static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
+        char buf[50];
+        const char *inptr = input;
+        size_t inbytesleft = strlen (input);
+        char *outptr = buf;
+        size_t outbytesleft = sizeof (buf);
+        size_t res = iconv (cd_88591_to_utf8,
+                            (char **) &inptr, &inbytesleft,
+                            &outptr, &outbytesleft);
+        if ((int)res > 0)
+          return 1;
+      }
+  }
+#endif
+  /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is
+     provided.  */
+  if (/* Try standardized names.  */
+      iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1)
+      /* Try IRIX, OSF/1 names.  */
+      && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1)
+      /* Try AIX names.  */
+      && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1)
+      /* Try HP-UX names.  */
+      && iconv_open ("utf8", "eucJP") == (iconv_t)(-1))
+    return 1;
+  return 0;
+}], [am_cv_func_iconv_works=yes], [am_cv_func_iconv_works=no],
+        [case "$host_os" in
+           aix* | hpux*) am_cv_func_iconv_works="guessing no" ;;
+           *)            am_cv_func_iconv_works="guessing yes" ;;
+         esac])
+      LIBS="$am_save_LIBS"
+    ])
+    case "$am_cv_func_iconv_works" in
+      *no) am_func_iconv=no am_cv_lib_iconv=no ;;
+      *)   am_func_iconv=yes ;;
+    esac
+  else
+    am_func_iconv=no am_cv_lib_iconv=no
+  fi
+  if test "$am_func_iconv" = yes; then
+    AC_DEFINE(HAVE_ICONV, 1,
+      [Define if you have the iconv() function and it works.])
+  fi
+  if test "$am_cv_lib_iconv" = yes; then
+    AC_MSG_CHECKING([how to link with libiconv])
+    AC_MSG_RESULT([$LIBICONV])
+  else
+    dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV
+    dnl either.
+    CPPFLAGS="$am_save_CPPFLAGS"
+    LIBICONV=
+    LTLIBICONV=
+  fi
+  AC_SUBST(LIBICONV)
+  AC_SUBST(LTLIBICONV)
+])
+
+AC_DEFUN([AM_ICONV],
+[
+  AM_ICONV_LINK
+  if test "$am_cv_func_iconv" = yes; then
+    AC_MSG_CHECKING([for iconv declaration])
+    AC_CACHE_VAL(am_cv_proto_iconv, [
+      AC_TRY_COMPILE([
+#include <stdlib.h>
+#include <iconv.h>
+extern
+#ifdef __cplusplus
+"C"
+#endif
+#if defined(__STDC__) || defined(__cplusplus)
+size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
+#else
+size_t iconv();
+#endif
+], [], am_cv_proto_iconv_arg1="", am_cv_proto_iconv_arg1="const")
+      am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"])
+    am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'`
+    AC_MSG_RESULT([$]{ac_t:-
+         }[$]am_cv_proto_iconv)
+    AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1,
+      [Define as const if the declaration of iconv() needs const.])
+  fi
+])
diff --git a/m4/lib-ld.m4 b/m4/lib-ld.m4
new file mode 100644 (file)
index 0000000..96c4e2c
--- /dev/null
@@ -0,0 +1,110 @@
+# lib-ld.m4 serial 3 (gettext-0.13)
+dnl Copyright (C) 1996-2003 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Subroutines of libtool.m4,
+dnl with replacements s/AC_/AC_LIB/ and s/lt_cv/acl_cv/ to avoid collision
+dnl with libtool.m4.
+
+dnl From libtool-1.4. Sets the variable with_gnu_ld to yes or no.
+AC_DEFUN([AC_LIB_PROG_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], acl_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  acl_cv_prog_gnu_ld=yes ;;
+*)
+  acl_cv_prog_gnu_ld=no ;;
+esac])
+with_gnu_ld=$acl_cv_prog_gnu_ld
+])
+
+dnl From libtool-1.4. Sets the variable LD.
+AC_DEFUN([AC_LIB_PROG_LD],
+[AC_ARG_WITH(gnu-ld,
+[  --with-gnu-ld           assume the C compiler uses GNU ld [default=no]],
+test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no)
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  AC_MSG_CHECKING([for ld used by GCC])
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [[\\/]* | [A-Za-z]:[\\/]*)]
+      [re_direlt='/[^/][^/]*/\.\./']
+      # Canonicalize the path of ld
+      ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+      while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+       ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  AC_MSG_CHECKING([for GNU ld])
+else
+  AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(acl_cv_path_LD,
+[if test -z "$LD"; then
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      acl_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some GNU ld's only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in
+      *GNU* | *'with BFD'*)
+       test "$with_gnu_ld" != no && break ;;
+      *)
+       test "$with_gnu_ld" != yes && break ;;
+      esac
+    fi
+  done
+  IFS="$ac_save_ifs"
+else
+  acl_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$acl_cv_path_LD"
+if test -n "$LD"; then
+  AC_MSG_RESULT($LD)
+else
+  AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+AC_LIB_PROG_LD_GNU
+])
diff --git a/m4/lib-link.m4 b/m4/lib-link.m4
new file mode 100644 (file)
index 0000000..e3d26fc
--- /dev/null
@@ -0,0 +1,709 @@
+# lib-link.m4 serial 13 (gettext-0.17)
+dnl Copyright (C) 2001-2007 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+AC_PREREQ(2.54)
+
+dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and
+dnl the libraries corresponding to explicit and implicit dependencies.
+dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and
+dnl augments the CPPFLAGS variable.
+dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname
+dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem.
+AC_DEFUN([AC_LIB_LINKFLAGS],
+[
+  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+  AC_REQUIRE([AC_LIB_RPATH])
+  define([Name],[translit([$1],[./-], [___])])
+  define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+                               [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+  AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [
+    AC_LIB_LINKFLAGS_BODY([$1], [$2])
+    ac_cv_lib[]Name[]_libs="$LIB[]NAME"
+    ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME"
+    ac_cv_lib[]Name[]_cppflags="$INC[]NAME"
+    ac_cv_lib[]Name[]_prefix="$LIB[]NAME[]_PREFIX"
+  ])
+  LIB[]NAME="$ac_cv_lib[]Name[]_libs"
+  LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs"
+  INC[]NAME="$ac_cv_lib[]Name[]_cppflags"
+  LIB[]NAME[]_PREFIX="$ac_cv_lib[]Name[]_prefix"
+  AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME)
+  AC_SUBST([LIB]NAME)
+  AC_SUBST([LTLIB]NAME)
+  AC_SUBST([LIB]NAME[_PREFIX])
+  dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the
+  dnl results of this search when this library appears as a dependency.
+  HAVE_LIB[]NAME=yes
+  undefine([Name])
+  undefine([NAME])
+])
+
+dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode)
+dnl searches for libname and the libraries corresponding to explicit and
+dnl implicit dependencies, together with the specified include files and
+dnl the ability to compile and link the specified testcode. If found, it
+dnl sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} and
+dnl LTLIB${NAME} variables and augments the CPPFLAGS variable, and
+dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs
+dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty.
+dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname
+dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem.
+AC_DEFUN([AC_LIB_HAVE_LINKFLAGS],
+[
+  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+  AC_REQUIRE([AC_LIB_RPATH])
+  define([Name],[translit([$1],[./-], [___])])
+  define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+                               [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+
+  dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME
+  dnl accordingly.
+  AC_LIB_LINKFLAGS_BODY([$1], [$2])
+
+  dnl Add $INC[]NAME to CPPFLAGS before performing the following checks,
+  dnl because if the user has installed lib[]Name and not disabled its use
+  dnl via --without-lib[]Name-prefix, he wants to use it.
+  ac_save_CPPFLAGS="$CPPFLAGS"
+  AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME)
+
+  AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [
+    ac_save_LIBS="$LIBS"
+    LIBS="$LIBS $LIB[]NAME"
+    AC_TRY_LINK([$3], [$4], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name=no])
+    LIBS="$ac_save_LIBS"
+  ])
+  if test "$ac_cv_lib[]Name" = yes; then
+    HAVE_LIB[]NAME=yes
+    AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the $1 library.])
+    AC_MSG_CHECKING([how to link with lib[]$1])
+    AC_MSG_RESULT([$LIB[]NAME])
+  else
+    HAVE_LIB[]NAME=no
+    dnl If $LIB[]NAME didn't lead to a usable library, we don't need
+    dnl $INC[]NAME either.
+    CPPFLAGS="$ac_save_CPPFLAGS"
+    LIB[]NAME=
+    LTLIB[]NAME=
+    LIB[]NAME[]_PREFIX=
+  fi
+  AC_SUBST([HAVE_LIB]NAME)
+  AC_SUBST([LIB]NAME)
+  AC_SUBST([LTLIB]NAME)
+  AC_SUBST([LIB]NAME[_PREFIX])
+  undefine([Name])
+  undefine([NAME])
+])
+
+dnl Determine the platform dependent parameters needed to use rpath:
+dnl   acl_libext,
+dnl   acl_shlibext,
+dnl   acl_hardcode_libdir_flag_spec,
+dnl   acl_hardcode_libdir_separator,
+dnl   acl_hardcode_direct,
+dnl   acl_hardcode_minus_L.
+AC_DEFUN([AC_LIB_RPATH],
+[
+  dnl Tell automake >= 1.10 to complain if config.rpath is missing.
+  m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])])
+  AC_REQUIRE([AC_PROG_CC])                dnl we use $CC, $GCC, $LDFLAGS
+  AC_REQUIRE([AC_LIB_PROG_LD])            dnl we use $LD, $with_gnu_ld
+  AC_REQUIRE([AC_CANONICAL_HOST])         dnl we use $host
+  AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir
+  AC_CACHE_CHECK([for shared library run path origin], acl_cv_rpath, [
+    CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \
+    ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh
+    . ./conftest.sh
+    rm -f ./conftest.sh
+    acl_cv_rpath=done
+  ])
+  wl="$acl_cv_wl"
+  acl_libext="$acl_cv_libext"
+  acl_shlibext="$acl_cv_shlibext"
+  acl_libname_spec="$acl_cv_libname_spec"
+  acl_library_names_spec="$acl_cv_library_names_spec"
+  acl_hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec"
+  acl_hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator"
+  acl_hardcode_direct="$acl_cv_hardcode_direct"
+  acl_hardcode_minus_L="$acl_cv_hardcode_minus_L"
+  dnl Determine whether the user wants rpath handling at all.
+  AC_ARG_ENABLE(rpath,
+    [  --disable-rpath         do not hardcode runtime library paths],
+    :, enable_rpath=yes)
+])
+
+dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and
+dnl the libraries corresponding to explicit and implicit dependencies.
+dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables.
+dnl Also, sets the LIB${NAME}_PREFIX variable to nonempty if libname was found
+dnl in ${LIB${NAME}_PREFIX}/$acl_libdirstem.
+AC_DEFUN([AC_LIB_LINKFLAGS_BODY],
+[
+  AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+  define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+                               [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+  dnl Autoconf >= 2.61 supports dots in --with options.
+  define([N_A_M_E],[m4_if(m4_version_compare(m4_defn([m4_PACKAGE_VERSION]),[2.61]),[-1],[translit([$1],[.],[_])],[$1])])
+  dnl By default, look in $includedir and $libdir.
+  use_additional=yes
+  AC_LIB_WITH_FINAL_PREFIX([
+    eval additional_includedir=\"$includedir\"
+    eval additional_libdir=\"$libdir\"
+  ])
+  AC_LIB_ARG_WITH([lib]N_A_M_E[-prefix],
+[  --with-lib]N_A_M_E[-prefix[=DIR]  search for lib$1 in DIR/include and DIR/lib
+  --without-lib]N_A_M_E[-prefix     don't search for lib$1 in includedir and libdir],
+[
+    if test "X$withval" = "Xno"; then
+      use_additional=no
+    else
+      if test "X$withval" = "X"; then
+        AC_LIB_WITH_FINAL_PREFIX([
+          eval additional_includedir=\"$includedir\"
+          eval additional_libdir=\"$libdir\"
+        ])
+      else
+        additional_includedir="$withval/include"
+        additional_libdir="$withval/$acl_libdirstem"
+      fi
+    fi
+])
+  dnl Search the library and its dependencies in $additional_libdir and
+  dnl $LDFLAGS. Using breadth-first-seach.
+  LIB[]NAME=
+  LTLIB[]NAME=
+  INC[]NAME=
+  LIB[]NAME[]_PREFIX=
+  rpathdirs=
+  ltrpathdirs=
+  names_already_handled=
+  names_next_round='$1 $2'
+  while test -n "$names_next_round"; do
+    names_this_round="$names_next_round"
+    names_next_round=
+    for name in $names_this_round; do
+      already_handled=
+      for n in $names_already_handled; do
+        if test "$n" = "$name"; then
+          already_handled=yes
+          break
+        fi
+      done
+      if test -z "$already_handled"; then
+        names_already_handled="$names_already_handled $name"
+        dnl See if it was already located by an earlier AC_LIB_LINKFLAGS
+        dnl or AC_LIB_HAVE_LINKFLAGS call.
+        uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+        eval value=\"\$HAVE_LIB$uppername\"
+        if test -n "$value"; then
+          if test "$value" = yes; then
+            eval value=\"\$LIB$uppername\"
+            test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value"
+            eval value=\"\$LTLIB$uppername\"
+            test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value"
+          else
+            dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined
+            dnl that this library doesn't exist. So just drop it.
+            :
+          fi
+        else
+          dnl Search the library lib$name in $additional_libdir and $LDFLAGS
+          dnl and the already constructed $LIBNAME/$LTLIBNAME.
+          found_dir=
+          found_la=
+          found_so=
+          found_a=
+          eval libname=\"$acl_libname_spec\"    # typically: libname=lib$name
+          if test -n "$acl_shlibext"; then
+            shrext=".$acl_shlibext"             # typically: shrext=.so
+          else
+            shrext=
+          fi
+          if test $use_additional = yes; then
+            dir="$additional_libdir"
+            dnl The same code as in the loop below:
+            dnl First look for a shared library.
+            if test -n "$acl_shlibext"; then
+              if test -f "$dir/$libname$shrext"; then
+                found_dir="$dir"
+                found_so="$dir/$libname$shrext"
+              else
+                if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then
+                  ver=`(cd "$dir" && \
+                        for f in "$libname$shrext".*; do echo "$f"; done \
+                        | sed -e "s,^$libname$shrext\\\\.,," \
+                        | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \
+                        | sed 1q ) 2>/dev/null`
+                  if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then
+                    found_dir="$dir"
+                    found_so="$dir/$libname$shrext.$ver"
+                  fi
+                else
+                  eval library_names=\"$acl_library_names_spec\"
+                  for f in $library_names; do
+                    if test -f "$dir/$f"; then
+                      found_dir="$dir"
+                      found_so="$dir/$f"
+                      break
+                    fi
+                  done
+                fi
+              fi
+            fi
+            dnl Then look for a static library.
+            if test "X$found_dir" = "X"; then
+              if test -f "$dir/$libname.$acl_libext"; then
+                found_dir="$dir"
+                found_a="$dir/$libname.$acl_libext"
+              fi
+            fi
+            if test "X$found_dir" != "X"; then
+              if test -f "$dir/$libname.la"; then
+                found_la="$dir/$libname.la"
+              fi
+            fi
+          fi
+          if test "X$found_dir" = "X"; then
+            for x in $LDFLAGS $LTLIB[]NAME; do
+              AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+              case "$x" in
+                -L*)
+                  dir=`echo "X$x" | sed -e 's/^X-L//'`
+                  dnl First look for a shared library.
+                  if test -n "$acl_shlibext"; then
+                    if test -f "$dir/$libname$shrext"; then
+                      found_dir="$dir"
+                      found_so="$dir/$libname$shrext"
+                    else
+                      if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then
+                        ver=`(cd "$dir" && \
+                              for f in "$libname$shrext".*; do echo "$f"; done \
+                              | sed -e "s,^$libname$shrext\\\\.,," \
+                              | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \
+                              | sed 1q ) 2>/dev/null`
+                        if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then
+                          found_dir="$dir"
+                          found_so="$dir/$libname$shrext.$ver"
+                        fi
+                      else
+                        eval library_names=\"$acl_library_names_spec\"
+                        for f in $library_names; do
+                          if test -f "$dir/$f"; then
+                            found_dir="$dir"
+                            found_so="$dir/$f"
+                            break
+                          fi
+                        done
+                      fi
+                    fi
+                  fi
+                  dnl Then look for a static library.
+                  if test "X$found_dir" = "X"; then
+                    if test -f "$dir/$libname.$acl_libext"; then
+                      found_dir="$dir"
+                      found_a="$dir/$libname.$acl_libext"
+                    fi
+                  fi
+                  if test "X$found_dir" != "X"; then
+                    if test -f "$dir/$libname.la"; then
+                      found_la="$dir/$libname.la"
+                    fi
+                  fi
+                  ;;
+              esac
+              if test "X$found_dir" != "X"; then
+                break
+              fi
+            done
+          fi
+          if test "X$found_dir" != "X"; then
+            dnl Found the library.
+            LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name"
+            if test "X$found_so" != "X"; then
+              dnl Linking with a shared library. We attempt to hardcode its
+              dnl directory into the executable's runpath, unless it's the
+              dnl standard /usr/lib.
+              if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then
+                dnl No hardcoding is needed.
+                LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+              else
+                dnl Use an explicit option to hardcode DIR into the resulting
+                dnl binary.
+                dnl Potentially add DIR to ltrpathdirs.
+                dnl The ltrpathdirs will be appended to $LTLIBNAME at the end.
+                haveit=
+                for x in $ltrpathdirs; do
+                  if test "X$x" = "X$found_dir"; then
+                    haveit=yes
+                    break
+                  fi
+                done
+                if test -z "$haveit"; then
+                  ltrpathdirs="$ltrpathdirs $found_dir"
+                fi
+                dnl The hardcoding into $LIBNAME is system dependent.
+                if test "$acl_hardcode_direct" = yes; then
+                  dnl Using DIR/libNAME.so during linking hardcodes DIR into the
+                  dnl resulting binary.
+                  LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+                else
+                  if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then
+                    dnl Use an explicit option to hardcode DIR into the resulting
+                    dnl binary.
+                    LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+                    dnl Potentially add DIR to rpathdirs.
+                    dnl The rpathdirs will be appended to $LIBNAME at the end.
+                    haveit=
+                    for x in $rpathdirs; do
+                      if test "X$x" = "X$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      rpathdirs="$rpathdirs $found_dir"
+                    fi
+                  else
+                    dnl Rely on "-L$found_dir".
+                    dnl But don't add it if it's already contained in the LDFLAGS
+                    dnl or the already constructed $LIBNAME
+                    haveit=
+                    for x in $LDFLAGS $LIB[]NAME; do
+                      AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+                      if test "X$x" = "X-L$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir"
+                    fi
+                    if test "$acl_hardcode_minus_L" != no; then
+                      dnl FIXME: Not sure whether we should use
+                      dnl "-L$found_dir -l$name" or "-L$found_dir $found_so"
+                      dnl here.
+                      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+                    else
+                      dnl We cannot use $acl_hardcode_runpath_var and LD_RUN_PATH
+                      dnl here, because this doesn't fit in flags passed to the
+                      dnl compiler. So give up. No hardcoding. This affects only
+                      dnl very old systems.
+                      dnl FIXME: Not sure whether we should use
+                      dnl "-L$found_dir -l$name" or "-L$found_dir $found_so"
+                      dnl here.
+                      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name"
+                    fi
+                  fi
+                fi
+              fi
+            else
+              if test "X$found_a" != "X"; then
+                dnl Linking with a static library.
+                LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a"
+              else
+                dnl We shouldn't come here, but anyway it's good to have a
+                dnl fallback.
+                LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name"
+              fi
+            fi
+            dnl Assume the include files are nearby.
+            additional_includedir=
+            case "$found_dir" in
+              */$acl_libdirstem | */$acl_libdirstem/)
+                basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'`
+                LIB[]NAME[]_PREFIX="$basedir"
+                additional_includedir="$basedir/include"
+                ;;
+            esac
+            if test "X$additional_includedir" != "X"; then
+              dnl Potentially add $additional_includedir to $INCNAME.
+              dnl But don't add it
+              dnl   1. if it's the standard /usr/include,
+              dnl   2. if it's /usr/local/include and we are using GCC on Linux,
+              dnl   3. if it's already present in $CPPFLAGS or the already
+              dnl      constructed $INCNAME,
+              dnl   4. if it doesn't exist as a directory.
+              if test "X$additional_includedir" != "X/usr/include"; then
+                haveit=
+                if test "X$additional_includedir" = "X/usr/local/include"; then
+                  if test -n "$GCC"; then
+                    case $host_os in
+                      linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+                    esac
+                  fi
+                fi
+                if test -z "$haveit"; then
+                  for x in $CPPFLAGS $INC[]NAME; do
+                    AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+                    if test "X$x" = "X-I$additional_includedir"; then
+                      haveit=yes
+                      break
+                    fi
+                  done
+                  if test -z "$haveit"; then
+                    if test -d "$additional_includedir"; then
+                      dnl Really add $additional_includedir to $INCNAME.
+                      INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir"
+                    fi
+                  fi
+                fi
+              fi
+            fi
+            dnl Look for dependencies.
+            if test -n "$found_la"; then
+              dnl Read the .la file. It defines the variables
+              dnl dlname, library_names, old_library, dependency_libs, current,
+              dnl age, revision, installed, dlopen, dlpreopen, libdir.
+              save_libdir="$libdir"
+              case "$found_la" in
+                */* | *\\*) . "$found_la" ;;
+                *) . "./$found_la" ;;
+              esac
+              libdir="$save_libdir"
+              dnl We use only dependency_libs.
+              for dep in $dependency_libs; do
+                case "$dep" in
+                  -L*)
+                    additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+                    dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME.
+                    dnl But don't add it
+                    dnl   1. if it's the standard /usr/lib,
+                    dnl   2. if it's /usr/local/lib and we are using GCC on Linux,
+                    dnl   3. if it's already present in $LDFLAGS or the already
+                    dnl      constructed $LIBNAME,
+                    dnl   4. if it doesn't exist as a directory.
+                    if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+                      haveit=
+                      if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+                        if test -n "$GCC"; then
+                          case $host_os in
+                            linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+                          esac
+                        fi
+                      fi
+                      if test -z "$haveit"; then
+                        haveit=
+                        for x in $LDFLAGS $LIB[]NAME; do
+                          AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                            dnl Really add $additional_libdir to $LIBNAME.
+                            LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir"
+                          fi
+                        fi
+                        haveit=
+                        for x in $LDFLAGS $LTLIB[]NAME; do
+                          AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                            dnl Really add $additional_libdir to $LTLIBNAME.
+                            LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir"
+                          fi
+                        fi
+                      fi
+                    fi
+                    ;;
+                  -R*)
+                    dir=`echo "X$dep" | sed -e 's/^X-R//'`
+                    if test "$enable_rpath" != no; then
+                      dnl Potentially add DIR to rpathdirs.
+                      dnl The rpathdirs will be appended to $LIBNAME at the end.
+                      haveit=
+                      for x in $rpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        rpathdirs="$rpathdirs $dir"
+                      fi
+                      dnl Potentially add DIR to ltrpathdirs.
+                      dnl The ltrpathdirs will be appended to $LTLIBNAME at the end.
+                      haveit=
+                      for x in $ltrpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        ltrpathdirs="$ltrpathdirs $dir"
+                      fi
+                    fi
+                    ;;
+                  -l*)
+                    dnl Handle this in the next round.
+                    names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+                    ;;
+                  *.la)
+                    dnl Handle this in the next round. Throw away the .la's
+                    dnl directory; it is already contained in a preceding -L
+                    dnl option.
+                    names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+                    ;;
+                  *)
+                    dnl Most likely an immediate library name.
+                    LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep"
+                    LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep"
+                    ;;
+                esac
+              done
+            fi
+          else
+            dnl Didn't find the library; assume it is in the system directories
+            dnl known to the linker and runtime loader. (All the system
+            dnl directories known to the linker should also be known to the
+            dnl runtime loader, otherwise the system is severely misconfigured.)
+            LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name"
+            LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name"
+          fi
+        fi
+      fi
+    done
+  done
+  if test "X$rpathdirs" != "X"; then
+    if test -n "$acl_hardcode_libdir_separator"; then
+      dnl Weird platform: only the last -rpath option counts, the user must
+      dnl pass all path elements in one option. We can arrange that for a
+      dnl single library, but not when more than one $LIBNAMEs are used.
+      alldirs=
+      for found_dir in $rpathdirs; do
+        alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir"
+      done
+      dnl Note: acl_hardcode_libdir_flag_spec uses $libdir and $wl.
+      acl_save_libdir="$libdir"
+      libdir="$alldirs"
+      eval flag=\"$acl_hardcode_libdir_flag_spec\"
+      libdir="$acl_save_libdir"
+      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag"
+    else
+      dnl The -rpath options are cumulative.
+      for found_dir in $rpathdirs; do
+        acl_save_libdir="$libdir"
+        libdir="$found_dir"
+        eval flag=\"$acl_hardcode_libdir_flag_spec\"
+        libdir="$acl_save_libdir"
+        LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag"
+      done
+    fi
+  fi
+  if test "X$ltrpathdirs" != "X"; then
+    dnl When using libtool, the option that works for both libraries and
+    dnl executables is -R. The -R options are cumulative.
+    for found_dir in $ltrpathdirs; do
+      LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir"
+    done
+  fi
+])
+
+dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR,
+dnl unless already present in VAR.
+dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes
+dnl contains two or three consecutive elements that belong together.
+AC_DEFUN([AC_LIB_APPENDTOVAR],
+[
+  for element in [$2]; do
+    haveit=
+    for x in $[$1]; do
+      AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+      if test "X$x" = "X$element"; then
+        haveit=yes
+        break
+      fi
+    done
+    if test -z "$haveit"; then
+      [$1]="${[$1]}${[$1]:+ }$element"
+    fi
+  done
+])
+
+dnl For those cases where a variable contains several -L and -l options
+dnl referring to unknown libraries and directories, this macro determines the
+dnl necessary additional linker options for the runtime path.
+dnl AC_LIB_LINKFLAGS_FROM_LIBS([LDADDVAR], [LIBSVALUE], [USE-LIBTOOL])
+dnl sets LDADDVAR to linker options needed together with LIBSVALUE.
+dnl If USE-LIBTOOL evaluates to non-empty, linking with libtool is assumed,
+dnl otherwise linking without libtool is assumed.
+AC_DEFUN([AC_LIB_LINKFLAGS_FROM_LIBS],
+[
+  AC_REQUIRE([AC_LIB_RPATH])
+  AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+  $1=
+  if test "$enable_rpath" != no; then
+    if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then
+      dnl Use an explicit option to hardcode directories into the resulting
+      dnl binary.
+      rpathdirs=
+      next=
+      for opt in $2; do
+        if test -n "$next"; then
+          dir="$next"
+          dnl No need to hardcode the standard /usr/lib.
+          if test "X$dir" != "X/usr/$acl_libdirstem"; then
+            rpathdirs="$rpathdirs $dir"
+          fi
+          next=
+        else
+          case $opt in
+            -L) next=yes ;;
+            -L*) dir=`echo "X$opt" | sed -e 's,^X-L,,'`
+                 dnl No need to hardcode the standard /usr/lib.
+                 if test "X$dir" != "X/usr/$acl_libdirstem"; then
+                   rpathdirs="$rpathdirs $dir"
+                 fi
+                 next= ;;
+            *) next= ;;
+          esac
+        fi
+      done
+      if test "X$rpathdirs" != "X"; then
+        if test -n ""$3""; then
+          dnl libtool is used for linking. Use -R options.
+          for dir in $rpathdirs; do
+            $1="${$1}${$1:+ }-R$dir"
+          done
+        else
+          dnl The linker is used for linking directly.
+          if test -n "$acl_hardcode_libdir_separator"; then
+            dnl Weird platform: only the last -rpath option counts, the user
+            dnl must pass all path elements in one option.
+            alldirs=
+            for dir in $rpathdirs; do
+              alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$dir"
+            done
+            acl_save_libdir="$libdir"
+            libdir="$alldirs"
+            eval flag=\"$acl_hardcode_libdir_flag_spec\"
+            libdir="$acl_save_libdir"
+            $1="$flag"
+          else
+            dnl The -rpath options are cumulative.
+            for dir in $rpathdirs; do
+              acl_save_libdir="$libdir"
+              libdir="$dir"
+              eval flag=\"$acl_hardcode_libdir_flag_spec\"
+              libdir="$acl_save_libdir"
+              $1="${$1}${$1:+ }$flag"
+            done
+          fi
+        fi
+      fi
+    fi
+  fi
+  AC_SUBST([$1])
+])
diff --git a/m4/lib-prefix.m4 b/m4/lib-prefix.m4
new file mode 100644 (file)
index 0000000..a8684e1
--- /dev/null
@@ -0,0 +1,185 @@
+# lib-prefix.m4 serial 5 (gettext-0.15)
+dnl Copyright (C) 2001-2005 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and
+dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't
+dnl require excessive bracketing.
+ifdef([AC_HELP_STRING],
+[AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])],
+[AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])])
+
+dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed
+dnl to access previously installed libraries. The basic assumption is that
+dnl a user will want packages to use other packages he previously installed
+dnl with the same --prefix option.
+dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate
+dnl libraries, but is otherwise very convenient.
+AC_DEFUN([AC_LIB_PREFIX],
+[
+  AC_BEFORE([$0], [AC_LIB_LINKFLAGS])
+  AC_REQUIRE([AC_PROG_CC])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+  dnl By default, look in $includedir and $libdir.
+  use_additional=yes
+  AC_LIB_WITH_FINAL_PREFIX([
+    eval additional_includedir=\"$includedir\"
+    eval additional_libdir=\"$libdir\"
+  ])
+  AC_LIB_ARG_WITH([lib-prefix],
+[  --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib
+  --without-lib-prefix    don't search for libraries in includedir and libdir],
+[
+    if test "X$withval" = "Xno"; then
+      use_additional=no
+    else
+      if test "X$withval" = "X"; then
+        AC_LIB_WITH_FINAL_PREFIX([
+          eval additional_includedir=\"$includedir\"
+          eval additional_libdir=\"$libdir\"
+        ])
+      else
+        additional_includedir="$withval/include"
+        additional_libdir="$withval/$acl_libdirstem"
+      fi
+    fi
+])
+  if test $use_additional = yes; then
+    dnl Potentially add $additional_includedir to $CPPFLAGS.
+    dnl But don't add it
+    dnl   1. if it's the standard /usr/include,
+    dnl   2. if it's already present in $CPPFLAGS,
+    dnl   3. if it's /usr/local/include and we are using GCC on Linux,
+    dnl   4. if it doesn't exist as a directory.
+    if test "X$additional_includedir" != "X/usr/include"; then
+      haveit=
+      for x in $CPPFLAGS; do
+        AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+        if test "X$x" = "X-I$additional_includedir"; then
+          haveit=yes
+          break
+        fi
+      done
+      if test -z "$haveit"; then
+        if test "X$additional_includedir" = "X/usr/local/include"; then
+          if test -n "$GCC"; then
+            case $host_os in
+              linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+            esac
+          fi
+        fi
+        if test -z "$haveit"; then
+          if test -d "$additional_includedir"; then
+            dnl Really add $additional_includedir to $CPPFLAGS.
+            CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir"
+          fi
+        fi
+      fi
+    fi
+    dnl Potentially add $additional_libdir to $LDFLAGS.
+    dnl But don't add it
+    dnl   1. if it's the standard /usr/lib,
+    dnl   2. if it's already present in $LDFLAGS,
+    dnl   3. if it's /usr/local/lib and we are using GCC on Linux,
+    dnl   4. if it doesn't exist as a directory.
+    if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+      haveit=
+      for x in $LDFLAGS; do
+        AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+        if test "X$x" = "X-L$additional_libdir"; then
+          haveit=yes
+          break
+        fi
+      done
+      if test -z "$haveit"; then
+        if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+          if test -n "$GCC"; then
+            case $host_os in
+              linux*) haveit=yes;;
+            esac
+          fi
+        fi
+        if test -z "$haveit"; then
+          if test -d "$additional_libdir"; then
+            dnl Really add $additional_libdir to $LDFLAGS.
+            LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir"
+          fi
+        fi
+      fi
+    fi
+  fi
+])
+
+dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix,
+dnl acl_final_exec_prefix, containing the values to which $prefix and
+dnl $exec_prefix will expand at the end of the configure script.
+AC_DEFUN([AC_LIB_PREPARE_PREFIX],
+[
+  dnl Unfortunately, prefix and exec_prefix get only finally determined
+  dnl at the end of configure.
+  if test "X$prefix" = "XNONE"; then
+    acl_final_prefix="$ac_default_prefix"
+  else
+    acl_final_prefix="$prefix"
+  fi
+  if test "X$exec_prefix" = "XNONE"; then
+    acl_final_exec_prefix='${prefix}'
+  else
+    acl_final_exec_prefix="$exec_prefix"
+  fi
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  eval acl_final_exec_prefix=\"$acl_final_exec_prefix\"
+  prefix="$acl_save_prefix"
+])
+
+dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the
+dnl variables prefix and exec_prefix bound to the values they will have
+dnl at the end of the configure script.
+AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX],
+[
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  $1
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+])
+
+dnl AC_LIB_PREPARE_MULTILIB creates a variable acl_libdirstem, containing
+dnl the basename of the libdir, either "lib" or "lib64".
+AC_DEFUN([AC_LIB_PREPARE_MULTILIB],
+[
+  dnl There is no formal standard regarding lib and lib64. The current
+  dnl practice is that on a system supporting 32-bit and 64-bit instruction
+  dnl sets or ABIs, 64-bit libraries go under $prefix/lib64 and 32-bit
+  dnl libraries go under $prefix/lib. We determine the compiler's default
+  dnl mode by looking at the compiler's library search path. If at least
+  dnl of its elements ends in /lib64 or points to a directory whose absolute
+  dnl pathname ends in /lib64, we assume a 64-bit ABI. Otherwise we use the
+  dnl default, namely "lib".
+  acl_libdirstem=lib
+  searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'`
+  if test -n "$searchpath"; then
+    acl_save_IFS="${IFS=       }"; IFS=":"
+    for searchdir in $searchpath; do
+      if test -d "$searchdir"; then
+        case "$searchdir" in
+          */lib64/ | */lib64 ) acl_libdirstem=lib64 ;;
+          *) searchdir=`cd "$searchdir" && pwd`
+             case "$searchdir" in
+               */lib64 ) acl_libdirstem=lib64 ;;
+             esac ;;
+        esac
+      fi
+    done
+    IFS="$acl_save_IFS"
+  fi
+])
diff --git a/m4/progtest.m4 b/m4/progtest.m4
new file mode 100644 (file)
index 0000000..a56365c
--- /dev/null
@@ -0,0 +1,92 @@
+# progtest.m4 serial 4 (gettext-0.14.2)
+dnl Copyright (C) 1996-2003, 2005 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl
+dnl This file can can be used in projects which are not available under
+dnl the GNU General Public License or the GNU Library General Public
+dnl License but which still want to provide support for the GNU gettext
+dnl functionality.
+dnl Please note that the actual code of the GNU gettext library is covered
+dnl by the GNU Library General Public License, and the rest of the GNU
+dnl gettext package package is covered by the GNU General Public License.
+dnl They are *not* in the public domain.
+
+dnl Authors:
+dnl   Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+AC_PREREQ(2.50)
+
+# Search path for a program which passes the given test.
+
+dnl AM_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR,
+dnl   TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]])
+AC_DEFUN([AM_PATH_PROG_WITH_TEST],
+[
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+  ac_executable_p="test -x"
+else
+  ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "$2", so it can be a program name with args.
+set dummy $2; ac_word=[$]2
+AC_MSG_CHECKING([for $ac_word])
+AC_CACHE_VAL(ac_cv_path_$1,
+[case "[$]$1" in
+  [[\\/]]* | ?:[[\\/]]*)
+    ac_cv_path_$1="[$]$1" # Let the user override the test with a path.
+    ;;
+  *)
+    ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+    for ac_dir in ifelse([$5], , $PATH, [$5]); do
+      IFS="$ac_save_IFS"
+      test -z "$ac_dir" && ac_dir=.
+      for ac_exec_ext in '' $ac_executable_extensions; do
+        if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+          echo "$as_me: trying $ac_dir/$ac_word..." >&AS_MESSAGE_LOG_FD
+          if [$3]; then
+            ac_cv_path_$1="$ac_dir/$ac_word$ac_exec_ext"
+            break 2
+          fi
+        fi
+      done
+    done
+    IFS="$ac_save_IFS"
+dnl If no 4th arg is given, leave the cache variable unset,
+dnl so AC_PATH_PROGS will keep looking.
+ifelse([$4], , , [  test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4"
+])dnl
+    ;;
+esac])dnl
+$1="$ac_cv_path_$1"
+if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then
+  AC_MSG_RESULT([$]$1)
+else
+  AC_MSG_RESULT(no)
+fi
+AC_SUBST($1)dnl
+])
diff --git a/org.enlightenment.Ethumb.service.in b/org.enlightenment.Ethumb.service.in
new file mode 100644 (file)
index 0000000..ece2e03
--- /dev/null
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.enlightenment.Ethumb
+Exec=@prefix@/bin/ethumbd
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..31c7f28
--- /dev/null
@@ -0,0 +1,3 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = lib bin plugins tests
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
new file mode 100644 (file)
index 0000000..2558cff
--- /dev/null
@@ -0,0 +1,54 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = \
+-I$(top_srcdir)/src/lib \
+-I$(top_srcdir)/src/lib/client \
+@EINA_CFLAGS@ \
+@ECORE_CFLAGS@ \
+@EDBUS_CFLAGS@
+
+if USE_MODULE_ETHUMBD
+
+AM_CPPFLAGS += \
+@EDBUS_CFLAGS@ \
+-DETHUMB_LIBEXEC_DIR=\"$(libexecdir)\"
+
+endif
+
+bin_PROGRAMS = ethumb
+
+ethumb_SOURCES = ethumb.c
+ethumb_LDADD = \
+$(top_builddir)/src/lib/libethumb.la \
+@ECORE_LIBS@ \
+@EINA_LIBS@
+
+
+if USE_MODULE_ETHUMBD
+
+bin_PROGRAMS += ethumbd ethumbd_client
+
+libexec_PROGRAMS = ethumbd_slave
+
+ethumbd_SOURCES = ethumbd.c ethumbd_private.h
+ethumbd_LDADD = \
+$(top_builddir)/src/lib/libethumb.la \
+@EDBUS_LIBS@ \
+@ECORE_LIBS@ \
+@EINA_LIBS@
+
+ethumbd_slave_SOURCES = ethumbd_child.c ethumbd_private.h
+ethumbd_slave_LDADD = \
+$(top_builddir)/src/lib/libethumb.la \
+@EDBUS_LIBS@ \
+@ECORE_LIBS@ \
+@EINA_LIBS@
+
+ethumbd_client_SOURCES = ethumbd_client.c
+ethumbd_client_LDADD = \
+$(top_builddir)/src/lib/client/libethumb_client.la \
+$(top_builddir)/src/lib/libethumb.la \
+@ECORE_LIBS@ \
+@EINA_LIBS@
+
+endif
diff --git a/src/bin/ethumb.c b/src/bin/ethumb.c
new file mode 100644 (file)
index 0000000..bee2f52
--- /dev/null
@@ -0,0 +1,281 @@
+/**
+ * @file
+ *
+ * Copyright (C) 2009 by ProFUSION embedded systems
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Rafael Antognolli <antognolli@profusion.mobi>
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include <Eina.h>
+#include <Ecore.h>
+#include <Ecore_Getopt.h>
+#include <Ethumb.h>
+
+const char *aspect_opt[] = { "keep", "ignore", "crop", NULL };
+const char *format_opt[] = { "png", "jpg", "eet", NULL };
+struct frame
+{
+   const char *file;
+   const char *group;
+   const char *swallow;
+};
+
+static unsigned char
+_ethumb_getopt_callback_frame_parse(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc __UNUSED__, const char *str, void *data __UNUSED__, Ecore_Getopt_Value *storage)
+{
+   struct frame *f = (struct frame *)storage->ptrp;
+   const char *tfile, *tgroup, *tswallow, *base, *sep;
+
+   base = str;
+   sep = strchr(base, ':');
+   if (!sep)
+       goto error;
+   tfile = eina_stringshare_add_length(base, sep - base);
+   base = sep + 1;
+
+   sep = strchr(base, ':');
+   if (!sep)
+     {
+       eina_stringshare_del(tfile);
+       goto error;
+     }
+   tgroup = eina_stringshare_add_length(base, sep - base);
+   base = sep + 1;
+   if (base[0] == '\0')
+     {
+       eina_stringshare_del(tfile);
+       eina_stringshare_del(tgroup);
+       goto error;
+     }
+   tswallow = eina_stringshare_add(base);
+
+   f->file = tfile;
+   f->group = tgroup;
+   f->swallow = tswallow;
+   return 1;
+
+ error:
+   fprintf(stderr,
+          "ERROR: invalid theme, not in format "
+          "'file:group:swallow_part': '%s'\n",
+          str);
+   return 0;
+}
+
+const Ecore_Getopt optdesc = {
+  "ethumb",
+  NULL,
+  PACKAGE_VERSION,
+  "(C) 2009 - ProFUSION embedded systems",
+  "LGPL v3 - GNU Lesser General Public License",
+  "Thumbnails generator.\n"
+     "\n"
+     "This program uses ethumb to create thumbnails from pictures. "
+     "It's an example of use and a test for ethumb.\n",
+  0,
+  {
+     ECORE_GETOPT_CALLBACK_ARGS
+     ('s', "size", "thumbnail size expected.",
+      "WxH", ecore_getopt_callback_size_parse, NULL),
+     ECORE_GETOPT_CHOICE
+     ('f', "format", "file format to save.", format_opt),
+     ECORE_GETOPT_CHOICE
+     ('a', "aspect", "original image aspect ratio.", aspect_opt),
+     ECORE_GETOPT_STORE_STR
+     ('d', "directory", "directory to save thumbnails."),
+     ECORE_GETOPT_STORE_STR
+     ('c', "category", "thumbnails category."),
+     ECORE_GETOPT_CALLBACK_ARGS
+     ('t', "theme", "path to theme file, group and swallow part.",
+      "file:group:swallow_part", _ethumb_getopt_callback_frame_parse, NULL),
+     ECORE_GETOPT_STORE_STR
+     ('k', "key", "key inside eet file to read image from."),
+     ECORE_GETOPT_STORE_DOUBLE
+     ('v', "video_time", "time of video frame to use as thumbnail."),
+     ECORE_GETOPT_STORE_INT
+     ('p', "document_page", "document page to use as thumbnail."),
+     ECORE_GETOPT_LICENSE('L', "license"),
+     ECORE_GETOPT_COPYRIGHT('C', "copyright"),
+     ECORE_GETOPT_VERSION('V', "version"),
+     ECORE_GETOPT_HELP('h', "help"),
+     ECORE_GETOPT_SENTINEL
+  }
+};
+
+static void
+_thumb_report(const char *mode, Ethumb *e)
+{
+   const char *ap, *ak, *gp, *gk;
+   ethumb_file_get(e, &ap, &ak);
+   ethumb_thumb_path_get(e, &gp, &gk);
+   printf("%s '%s' '%s' => '%s' '%s'\n",
+         mode, ap, ak ? ak : "", gp, gk ? gk : "");
+}
+
+static void
+_finished_thumb( void *data __UNUSED__, Ethumb *e, Eina_Bool success)
+{
+   const char *mode = success ? "GENERATED" : "FAILED";
+   _thumb_report(mode, e);
+   ecore_main_loop_quit();
+}
+
+int
+main(int argc, char *argv[])
+{
+   Ethumb *e;
+   Eina_Bool quit_option = 0;
+   Eina_Rectangle geometry = {-1, -1, -1, -1};
+   unsigned int format = 0, aspect = 0;
+   char *format_str = NULL;
+   char *aspect_str = NULL;
+   char *directory = NULL;
+   char *category = NULL;
+   char *src_key = NULL;
+   struct frame frame = {NULL, NULL, NULL};
+   const char *thumb_path = NULL;
+   const char *thumb_key = NULL;
+   double video_time = 0;
+   int page = 0;
+   int arg_index;
+   int i;
+
+   int r = 1;
+
+   ethumb_init();
+   ecore_init();
+
+   Ecore_Getopt_Value values[] = {
+       ECORE_GETOPT_VALUE_PTR_CAST(geometry),
+       ECORE_GETOPT_VALUE_PTR_CAST(format_str),
+       ECORE_GETOPT_VALUE_PTR_CAST(aspect_str),
+       ECORE_GETOPT_VALUE_STR(directory),
+       ECORE_GETOPT_VALUE_STR(category),
+       ECORE_GETOPT_VALUE_PTR_CAST(frame),
+       ECORE_GETOPT_VALUE_STR(src_key),
+       ECORE_GETOPT_VALUE_DOUBLE(video_time),
+       ECORE_GETOPT_VALUE_INT(page),
+       ECORE_GETOPT_VALUE_BOOL(quit_option),
+       ECORE_GETOPT_VALUE_BOOL(quit_option),
+       ECORE_GETOPT_VALUE_BOOL(quit_option),
+       ECORE_GETOPT_VALUE_BOOL(quit_option),
+       ECORE_GETOPT_VALUE_NONE
+   };
+
+   arg_index = ecore_getopt_parse(&optdesc, values, argc, argv);
+   if (arg_index < 0)
+     {
+       fprintf(stderr, "Could not parse arguments.\n");
+       if (frame.file)
+         {
+            eina_stringshare_del(frame.file);
+            eina_stringshare_del(frame.group);
+            eina_stringshare_del(frame.swallow);
+         }
+       ecore_shutdown();
+       ethumb_shutdown();
+       return -1;
+     }
+
+   if (quit_option)
+     {
+       if (frame.file)
+         {
+            eina_stringshare_del(frame.file);
+            eina_stringshare_del(frame.group);
+            eina_stringshare_del(frame.swallow);
+         }
+       ecore_shutdown();
+       ethumb_shutdown();
+       return 0;
+     }
+
+   for (i = 0; i < 3; i++)
+     if (format_opt[i] == format_str)
+       {
+         format = i;
+         break;
+       }
+
+   for (i = 0; i < 3; i++)
+     if (aspect_opt[i] == aspect_str)
+       {
+         aspect = i;
+         break;
+       }
+
+   e = ethumb_new();
+
+   ethumb_thumb_format_set(e, format);
+   ethumb_thumb_aspect_set(e, aspect);
+   if (directory) ethumb_thumb_dir_path_set(e, directory);
+   if (category) ethumb_thumb_category_set(e, category);
+   if (geometry.w > 0 && geometry.h > 0)
+     ethumb_thumb_size_set(e, geometry.w, geometry.h);
+   if (frame.file)
+     {
+       ethumb_frame_set(e, frame.file, frame.group, frame.swallow);
+       eina_stringshare_del(frame.file);
+       eina_stringshare_del(frame.group);
+       eina_stringshare_del(frame.swallow);
+     }
+   if (video_time > 0)
+     ethumb_video_time_set(e, video_time);
+   if (page > 0)
+     ethumb_document_page_set(e, page);
+
+   if (r && arg_index < argc)
+     r = ethumb_file_set(e, argv[arg_index++], src_key);
+   else
+     r = 0;
+   if (r && arg_index < argc)
+     thumb_path = argv[arg_index++];
+   if (r && arg_index < argc)
+     thumb_key = argv[arg_index];
+
+   if (r)
+     {
+       ethumb_thumb_path_set(e, thumb_path, thumb_key);
+       if (ethumb_exists(e))
+         {
+            _thumb_report("EXISTS", e);
+            quit_option = 1;
+            r = 1;
+         }
+       else
+         r = ethumb_generate(e, _finished_thumb, NULL, NULL);
+     }
+
+   if (r && !quit_option)
+     ecore_main_loop_begin();
+
+   ethumb_file_free(e);
+   ethumb_free(e);
+
+   ecore_shutdown();
+   ethumb_shutdown();
+
+   return !r;
+}
diff --git a/src/bin/ethumbd.c b/src/bin/ethumbd.c
new file mode 100644 (file)
index 0000000..d3ca7c3
--- /dev/null
@@ -0,0 +1,1933 @@
+/**
+ * @file
+ *
+ * Copyright (C) 2009 by ProFUSION embedded systems
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,  but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the  GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * @author Rafael Antognolli <antognolli@profusion.mobi>
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <Eina.h>
+#include <Ecore_Getopt.h>
+#include <Ecore.h>
+#include <E_DBus.h>
+#include <Ethumb.h>
+
+#include "ethumbd_private.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#define MAX_ID 2000000
+
+#define DBG(...) EINA_LOG_DOM_DBG(_log_domain, __VA_ARGS__)
+#define INF(...) EINA_LOG_DOM_INFO(_log_domain, __VA_ARGS__)
+#define WRN(...) EINA_LOG_DOM_WARN(_log_domain, __VA_ARGS__)
+#define ERR(...) EINA_LOG_DOM_ERR(_log_domain, __VA_ARGS__)
+#define CRIT(...) EINA_LOG_DOM_CRIT(_log_domain, __VA_ARGS__)
+
+static const char _ethumb_dbus_bus_name[] = "org.enlightenment.Ethumb";
+static const char _ethumb_dbus_interface[] = "org.enlightenment.Ethumb";
+static const char _ethumb_dbus_objects_interface[] = "org.enlightenment.Ethumb.objects";
+static const char _ethumb_dbus_path[] = "/org/enlightenment/Ethumb";
+static const char fdo_interface[] = "org.freedesktop.DBus";
+static const char fdo_bus_name[] = "org.freedesktop.DBus";
+static const char fdo_path[] = "/org/freedesktop/DBus";
+
+static int _log_domain = -1;
+
+typedef struct _Ethumbd_Setup Ethumbd_Setup;
+typedef struct _Ethumbd_Slave Ethumbd_Slave;
+typedef struct _Ethumbd_Request Ethumbd_Request;
+typedef struct _Ethumbd_Queue Ethumbd_Queue;
+typedef struct _Ethumbd_Object Ethumbd_Object;
+typedef struct _Ethumbd_Object_Data Ethumbd_Object_Data;
+typedef struct _Ethumbd Ethumbd;
+
+struct _Ethumbd_Setup
+{
+   struct
+   {
+      Eina_Bool fdo : 1;
+      Eina_Bool size : 1;
+      Eina_Bool format : 1;
+      Eina_Bool aspect : 1;
+      Eina_Bool orientation: 1;
+      Eina_Bool crop : 1;
+      Eina_Bool quality : 1;
+      Eina_Bool compress : 1;
+      Eina_Bool directory : 1;
+      Eina_Bool category : 1;
+      Eina_Bool frame : 1;
+      Eina_Bool video_time : 1;
+      Eina_Bool video_start : 1;
+      Eina_Bool video_interval : 1;
+      Eina_Bool video_ntimes : 1;
+      Eina_Bool video_fps : 1;
+      Eina_Bool document_page : 1;
+   } flags;
+   int fdo;
+   int tw, th;
+   int format;
+   int aspect;
+   int orientation;
+   float cx, cy;
+   int quality;
+   int compress;
+   const char *directory;
+   const char *category;
+   const char *theme_file;
+   const char *group;
+   const char *swallow;
+   float video_time;
+   float video_start;
+   float video_interval;
+   unsigned int video_ntimes;
+   unsigned int video_fps;
+   unsigned int document_page;
+};
+
+struct _Ethumbd_Request
+{
+   int id;
+   const char *file, *key;
+   const char *thumb, *thumb_key;
+   Ethumbd_Setup setup;
+};
+
+struct _Ethumbd_Object
+{
+   int used;
+   const char *path;
+   const char *client;
+   Eina_List *queue;
+   int nqueue;
+   int id_count;
+   int max_id;
+   int min_id;
+   E_DBus_Object *dbus_obj;
+};
+
+struct _Ethumbd_Queue
+{
+   int count;
+   int max_count;
+   int nqueue;
+   int last;
+   int current;
+   Ethumbd_Object *table;
+   int *list;
+};
+
+struct _Ethumbd_Slave
+{
+   Ecore_Exe *exe;
+   char *bufcmd; // buffer to read commands from slave
+   int scmd; // size of command to read
+   int pcmd; // position in the command buffer
+};
+
+struct _Ethumbd
+{
+   E_DBus_Connection *conn;
+   E_DBus_Signal_Handler *name_owner_changed_handler;
+   E_DBus_Interface *eiface, *objects_iface;
+   E_DBus_Object *dbus_obj;
+   Ecore_Idler *idler;
+   Ethumbd_Request *processing;
+   Ethumbd_Queue queue;
+   double timeout;
+   Ecore_Timer *timeout_timer;
+   Ethumbd_Slave slave;
+
+   Ecore_Event_Handler *data_cb;
+   Ecore_Event_Handler *del_cb;
+};
+
+struct _Ethumbd_Object_Data
+{
+   int idx;
+   Ethumbd *ed;
+};
+
+struct _Ethumb_DBus_Method_Table
+{
+   const char *name;
+   const char *signature;
+   const char *reply;
+   E_DBus_Method_Cb function;
+};
+
+struct _Ethumb_DBus_Signal_Table
+{
+   const char *name;
+   const char *signature;
+};
+
+const Ecore_Getopt optdesc = {
+  "ethumbd",
+  NULL,
+  PACKAGE_VERSION,
+  "(C) 2009 - ProFUSION embedded systems",
+  "LGPL v3 - GNU Lesser General Public License",
+  "Ethumb daemon.\n"
+  "\n"
+  "ethumbd uses the Ethumb library to create thumbnails for any "
+  "program that requests it (now just by dbus).\n",
+  0,
+  {
+    ECORE_GETOPT_STORE_DOUBLE
+    ('t', "timeout", "finish ethumbd after <timeout> seconds of no activity."),
+    ECORE_GETOPT_LICENSE('L', "license"),
+    ECORE_GETOPT_COPYRIGHT('C', "copyright"),
+    ECORE_GETOPT_VERSION('V', "version"),
+    ECORE_GETOPT_HELP('h', "help"),
+    ECORE_GETOPT_SENTINEL
+  }
+};
+
+static void _ethumb_dbus_generated_signal(Ethumbd *ed, int *id, const char *thumb_path, const char *thumb_key, Eina_Bool success);
+static Eina_Bool _ethumbd_slave_spawn(Ethumbd_Slave *slave, Ethumbd *ed);
+
+static Eina_Bool
+_ethumbd_timeout_cb(void *data)
+{
+   Ethumbd *ed = data;
+
+   ecore_main_loop_quit();
+   ed->timeout_timer = NULL;
+
+   return 0;
+}
+
+static void
+_ethumbd_timeout_start(Ethumbd *ed)
+{
+   if (ed->timeout < 0)
+     return;
+
+   if (!ed->timeout_timer)
+     ed->timeout_timer = ecore_timer_add(ed->timeout, _ethumbd_timeout_cb, ed);
+}
+
+static void
+_ethumbd_timeout_stop(Ethumbd *ed)
+{
+   if (!ed->timeout_timer)
+     return;
+
+   ecore_timer_del(ed->timeout_timer);
+   ed->timeout_timer = NULL;
+}
+
+static int
+_ethumb_dbus_check_id(Ethumbd_Object *eobject, int id)
+{
+   if (id < 0 || id > MAX_ID)
+     return 0;
+
+   if (eobject->min_id < eobject->max_id)
+     return id < eobject->min_id || id > eobject->max_id;
+   else if (eobject->min_id > eobject->max_id)
+     return id < eobject->min_id && id > eobject->max_id;
+   else
+     return id != eobject->max_id;
+}
+
+static void
+_ethumb_dbus_inc_max_id(Ethumbd_Object *eobject, int id)
+{
+   if (eobject->min_id < 0 && eobject->max_id < 0)
+     eobject->min_id = id;
+
+   eobject->max_id = id;
+}
+
+static void
+_ethumb_dbus_inc_min_id(Ethumbd_Object *eobject)
+{
+   Eina_List *l;
+
+   l = eobject->queue;
+   while (l)
+     {
+       Ethumbd_Request *request = l->data;
+       if (request->id >= 0)
+         {
+            eobject->min_id = request->id;
+            break;
+         }
+
+       l = l->next;
+     }
+
+   if (!l)
+     {
+       eobject->min_id = -1;
+       eobject->max_id = -1;
+     }
+}
+
+int
+_ethumbd_write_safe(Ethumbd_Slave *slave, const void *buf, ssize_t size)
+{
+
+   if (!slave->exe)
+     {
+       ERR("slave process isn't running.");
+       return 0;
+     }
+
+   ecore_exe_send(slave->exe, buf, size);
+   return 1;
+}
+
+static void
+_ethumbd_child_write_op_new(Ethumbd_Slave *slave, int idx)
+{
+   int id = ETHUMBD_OP_NEW;
+   _ethumbd_write_safe(slave, &id, sizeof(id));
+   _ethumbd_write_safe(slave, &idx, sizeof(idx));
+}
+
+static void
+_ethumbd_child_write_op_del(Ethumbd_Slave *slave, int idx)
+{
+   int id = ETHUMBD_OP_DEL;
+   _ethumbd_write_safe(slave, &id, sizeof(id));
+   _ethumbd_write_safe(slave, &idx, sizeof(idx));
+}
+
+static void
+_ethumbd_pipe_str_write(Ethumbd_Slave *slave, const char *str)
+{
+   int len;
+
+   if (str)
+     len = strlen(str) + 1;
+   else
+     len = 0;
+
+   _ethumbd_write_safe(slave, &len, sizeof(len));
+   _ethumbd_write_safe(slave, str, len);
+}
+
+static void
+_ethumbd_child_write_op_generate(Ethumbd_Slave *slave,
+                                int idx, const char *path, const char *key,
+                                const char *thumb_path, const char *thumb_key)
+{
+   int id = ETHUMBD_OP_GENERATE;
+
+   _ethumbd_write_safe(slave, &id, sizeof(id));
+   _ethumbd_write_safe(slave, &idx, sizeof(idx));
+
+   _ethumbd_pipe_str_write(slave, path);
+   _ethumbd_pipe_str_write(slave, key);
+   _ethumbd_pipe_str_write(slave, thumb_path);
+   _ethumbd_pipe_str_write(slave, thumb_key);
+}
+
+static void
+_generated_cb(Ethumbd *ed, Eina_Bool success, const char *thumb_path, const char *thumb_key)
+{
+   int i = ed->queue.current;
+
+   DBG("thumbnail ready at: \"%s:%s\"", thumb_path, thumb_key);
+
+   if (ed->queue.table[i].used)
+     _ethumb_dbus_generated_signal
+       (ed, &ed->processing->id, thumb_path, thumb_key, success);
+   eina_stringshare_del(ed->processing->file);
+   eina_stringshare_del(ed->processing->key);
+   eina_stringshare_del(ed->processing->thumb);
+   eina_stringshare_del(ed->processing->thumb_key);
+   free(ed->processing);
+   ed->processing = NULL;
+}
+
+static void
+_ethumbd_slave_cmd_ready(Ethumbd *ed)
+{
+   const char *bufcmd = ed->slave.bufcmd;
+   Eina_Bool success;
+   const char *thumb_path = NULL;
+   const char *thumb_key = NULL;
+   int size_path, size_key;
+
+   /* NOTE: accessing values directly on bufcmd breaks alignment
+    * as the first item is an Eina_Bool (size 1) and second is
+    * an integer (size 4, alignment 4).
+    * Thus copy to stack values before using them, to have proper alignment.
+    */
+#define READVAL(dst)                           \
+   memcpy(&dst, bufcmd, sizeof(dst));          \
+   bufcmd += sizeof(dst);
+
+   READVAL(success);
+
+   READVAL(size_path);
+   if (size_path)
+     {
+       thumb_path = bufcmd;
+       bufcmd += size_path;
+     }
+
+   READVAL(size_key);
+   if (size_key) thumb_key = bufcmd;
+
+#undef READVAL
+
+   _generated_cb(ed, success, thumb_path, thumb_key);
+
+   free(ed->slave.bufcmd);
+   ed->slave.bufcmd = NULL;
+   ed->slave.scmd = 0;
+}
+
+static int
+_ethumbd_slave_alloc_cmd(Ethumbd *ed, int ssize, char *sdata)
+{
+   int *scmd;
+
+   if (ed->slave.bufcmd)
+     return 0;
+
+   scmd = (int *)sdata;
+   if (ssize < (int)sizeof(*scmd)) {
+       ERR("could not read size of command.");
+       return 0;
+   }
+   ed->slave.bufcmd = malloc(*scmd);
+   ed->slave.scmd = *scmd;
+   ed->slave.pcmd = 0;
+
+   return sizeof(*scmd);
+}
+
+static Eina_Bool
+_ethumbd_slave_data_read_cb(void *data, int type __UNUSED__, void *event)
+{
+   Ethumbd *ed = data;
+   Ecore_Exe_Event_Data *ev = event;
+   int ssize;
+   char *sdata;
+
+   if (ev->exe != ed->slave.exe)
+     {
+       ERR("PARENT ERROR: slave != ev->exe");
+       return 0;
+     }
+
+   ssize = ev->size;
+   sdata = ev->data;
+
+   while (ssize > 0)
+     {
+       if (!ed->slave.bufcmd)
+         {
+            int n;
+            n = _ethumbd_slave_alloc_cmd(ed, ssize, sdata);
+            ssize -= n;
+            sdata += n;
+         }
+       else
+         {
+            char *bdata;
+            int nbytes;
+            bdata = ed->slave.bufcmd + ed->slave.pcmd;
+            nbytes = ed->slave.scmd - ed->slave.pcmd;
+            nbytes = ssize < nbytes ? ssize : nbytes;
+            memcpy(bdata, sdata, nbytes);
+            sdata += nbytes;
+            ssize -= nbytes;
+            ed->slave.pcmd += nbytes;
+
+            if (ed->slave.pcmd == ed->slave.scmd)
+              _ethumbd_slave_cmd_ready(ed);
+         }
+     }
+
+   return 1;
+}
+
+static Eina_Bool
+_ethumbd_slave_del_cb(void *data, int type __UNUSED__, void *event)
+{
+   Ethumbd *ed = data;
+   Ecore_Exe_Event_Del *ev = event;
+   int i;
+
+   if (ev->exe != ed->slave.exe)
+     return 1;
+
+   if (ev->exited)
+     ERR("slave exited with code: %d", ev->exit_code);
+   else if (ev->signalled)
+     ERR("slave exited by signal: %d", ev->exit_signal);
+
+   if (!ed->processing)
+     goto end;
+
+   i = ed->queue.current;
+   ERR("failed to generate thumbnail for: \"%s:%s\"",
+       ed->processing->file, ed->processing->key);
+
+   if (ed->queue.table[i].used)
+     _ethumb_dbus_generated_signal
+       (ed, &ed->processing->id, NULL, NULL, EINA_FALSE);
+   eina_stringshare_del(ed->processing->file);
+   eina_stringshare_del(ed->processing->key);
+   eina_stringshare_del(ed->processing->thumb);
+   eina_stringshare_del(ed->processing->thumb_key);
+   free(ed->processing);
+   ed->processing = NULL;
+
+end:
+   ed->slave.exe = NULL;
+   if (ed->slave.bufcmd)
+     free(ed->slave.bufcmd);
+
+   if (!_ethumbd_slave_spawn(&ed->slave, ed))
+     return EINA_FALSE;
+
+   /* restart all queue */
+   for (i = 0; i < ed->queue.count; ++i)
+     _ethumbd_child_write_op_new(&ed->slave, ed->queue.list[i]);
+
+   return EINA_TRUE;
+}
+
+static void
+_ethumbd_pipe_write_setup(Ethumbd_Slave *slave, int type, const void *data)
+{
+   const int *i_value;
+   const float *f_value;
+
+   _ethumbd_write_safe(slave, &type, sizeof(type));
+
+   switch (type)
+     {
+      case ETHUMBD_FDO:
+      case ETHUMBD_FORMAT:
+      case ETHUMBD_ASPECT:
+      case ETHUMBD_ORIENTATION:
+      case ETHUMBD_QUALITY:
+      case ETHUMBD_COMPRESS:
+      case ETHUMBD_SIZE_W:
+      case ETHUMBD_SIZE_H:
+      case ETHUMBD_DOCUMENT_PAGE:
+      case ETHUMBD_VIDEO_NTIMES:
+      case ETHUMBD_VIDEO_FPS:
+        i_value = data;
+        _ethumbd_write_safe(slave, i_value, sizeof(*i_value));
+        break;
+      case ETHUMBD_CROP_X:
+      case ETHUMBD_CROP_Y:
+      case ETHUMBD_VIDEO_TIME:
+      case ETHUMBD_VIDEO_START:
+      case ETHUMBD_VIDEO_INTERVAL:
+        f_value = data;
+        _ethumbd_write_safe(slave, f_value, sizeof(*f_value));
+        break;
+      case ETHUMBD_DIRECTORY:
+      case ETHUMBD_CATEGORY:
+      case ETHUMBD_FRAME_FILE:
+      case ETHUMBD_FRAME_GROUP:
+      case ETHUMBD_FRAME_SWALLOW:
+        _ethumbd_pipe_str_write(slave, data);
+        break;
+      case ETHUMBD_SETUP_FINISHED:
+        break;
+      default:
+        ERR("wrong ethumb setup parameter.");
+     }
+}
+
+static void
+_process_setup(Ethumbd *ed)
+{
+   int op_id = ETHUMBD_OP_SETUP;
+   int idx = ed->queue.current;
+
+   Ethumbd_Setup *setup = &ed->processing->setup;
+   Ethumbd_Slave *slave = &ed->slave;
+
+   _ethumbd_write_safe(slave, &op_id, sizeof(op_id));
+   _ethumbd_write_safe(slave, &idx, sizeof(idx));
+
+   if (setup->flags.fdo)
+     _ethumbd_pipe_write_setup(slave, ETHUMBD_FDO, &setup->fdo);
+   if (setup->flags.size)
+     {
+       _ethumbd_pipe_write_setup(slave, ETHUMBD_SIZE_W, &setup->tw);
+       _ethumbd_pipe_write_setup(slave, ETHUMBD_SIZE_H, &setup->th);
+     }
+   if (setup->flags.format)
+     _ethumbd_pipe_write_setup(slave, ETHUMBD_FORMAT, &setup->format);
+   if (setup->flags.aspect)
+     _ethumbd_pipe_write_setup(slave, ETHUMBD_ASPECT, &setup->aspect);
+   if (setup->flags.orientation)
+     _ethumbd_pipe_write_setup(slave, ETHUMBD_ORIENTATION, &setup->orientation);
+   if (setup->flags.crop)
+     {
+       _ethumbd_pipe_write_setup(slave, ETHUMBD_CROP_X, &setup->cx);
+       _ethumbd_pipe_write_setup(slave, ETHUMBD_CROP_Y, &setup->cy);
+     }
+   if (setup->flags.quality)
+     _ethumbd_pipe_write_setup(slave, ETHUMBD_QUALITY, &setup->quality);
+   if (setup->flags.compress)
+     _ethumbd_pipe_write_setup(slave, ETHUMBD_COMPRESS, &setup->compress);
+   if (setup->flags.directory)
+     _ethumbd_pipe_write_setup(slave, ETHUMBD_DIRECTORY, setup->directory);
+   if (setup->flags.category)
+     _ethumbd_pipe_write_setup(slave, ETHUMBD_CATEGORY, setup->category);
+   if (setup->flags.frame)
+     {
+       _ethumbd_pipe_write_setup(slave, ETHUMBD_FRAME_FILE, setup->theme_file);
+       _ethumbd_pipe_write_setup(slave, ETHUMBD_FRAME_GROUP, setup->group);
+       _ethumbd_pipe_write_setup(slave, ETHUMBD_FRAME_SWALLOW, setup->swallow);
+     }
+   if (setup->flags.video_time)
+     _ethumbd_pipe_write_setup(slave, ETHUMBD_VIDEO_TIME, &setup->video_time);
+   if (setup->flags.video_start)
+     _ethumbd_pipe_write_setup(slave, ETHUMBD_VIDEO_START, &setup->video_start);
+   if (setup->flags.video_interval)
+     _ethumbd_pipe_write_setup(slave, ETHUMBD_VIDEO_INTERVAL,
+                              &setup->video_interval);
+   if (setup->flags.video_ntimes)
+     _ethumbd_pipe_write_setup(slave, ETHUMBD_VIDEO_NTIMES, &setup->video_ntimes);
+   if (setup->flags.video_fps)
+     _ethumbd_pipe_write_setup(slave, ETHUMBD_VIDEO_FPS, &setup->video_fps);
+   if (setup->flags.document_page)
+     _ethumbd_pipe_write_setup(slave, ETHUMBD_DOCUMENT_PAGE,
+                              &setup->document_page);
+   _ethumbd_pipe_write_setup(slave, ETHUMBD_SETUP_FINISHED, NULL);
+
+
+   if (setup->directory) eina_stringshare_del(setup->directory);
+   if (setup->category) eina_stringshare_del(setup->category);
+   if (setup->theme_file) eina_stringshare_del(setup->theme_file);
+   if (setup->group) eina_stringshare_del(setup->group);
+   if (setup->swallow) eina_stringshare_del(setup->swallow);
+
+   free(ed->processing);
+   ed->processing = NULL;
+}
+
+static void
+_process_file(Ethumbd *ed)
+{
+   _ethumbd_child_write_op_generate
+     (&ed->slave, ed->queue.current, ed->processing->file,
+      ed->processing->key, ed->processing->thumb, ed->processing->thumb_key);
+}
+
+static int
+_get_next_on_queue(Ethumbd_Queue *queue)
+{
+   int i, idx;
+   Ethumbd_Object *eobject;
+
+   i = queue->last;
+   i++;
+   if (i >= queue->count)
+     i = 0;
+
+   idx = queue->list[i];
+   eobject = &(queue->table[idx]);
+   while (!eobject->nqueue)
+     {
+       i = (i + 1) % queue->count;
+
+       idx = queue->list[i];
+       eobject = &(queue->table[idx]);
+     }
+
+   return queue->list[i];
+}
+
+static Eina_Bool
+_process_queue_cb(void *data)
+{
+   Ethumbd_Object *eobject;
+   int i;
+   Ethumbd *ed = data;
+   Ethumbd_Queue *queue = &ed->queue;
+   Ethumbd_Request *request;
+
+   if (ed->processing)
+     return 1;
+
+   if (!queue->nqueue)
+     {
+       ed->idler = NULL;
+       if (!queue->count)
+         _ethumbd_timeout_start(ed);
+       ed->idler = NULL;
+       return 0;
+     }
+
+   i = _get_next_on_queue(queue);
+   eobject = &(queue->table[i]);
+
+   request = eina_list_data_get(eobject->queue);
+   eobject->queue = eina_list_remove_list(eobject->queue, eobject->queue);
+   ed->queue.current = i;
+   DBG("processing file: \"%s:%s\"...", request->file,
+       request->key);
+   ed->processing = request;
+
+   if (request->id < 0)
+     _process_setup(ed);
+   else
+     {
+       _process_file(ed);
+       _ethumb_dbus_inc_min_id(eobject);
+     }
+   eobject->nqueue--;
+   queue->nqueue--;
+
+   queue->last = i;
+
+   return 1;
+}
+
+static void
+_process_queue_start(Ethumbd *ed)
+{
+   if (!ed->idler)
+     ed->idler = ecore_idler_add(_process_queue_cb, ed);
+}
+
+static void
+_process_queue_stop(Ethumbd *ed)
+{
+   if (ed->idler)
+     {
+       ecore_idler_del(ed->idler);
+       ed->idler = NULL;
+     }
+}
+
+static int
+_ethumb_table_append(Ethumbd *ed)
+{
+   int i;
+   char buf[1024];
+   Ethumbd_Queue *q = &ed->queue;
+
+   if (q->count == q->max_count)
+     {
+       int new_max = q->max_count + 5;
+       int start, size;
+       void *tmp;
+
+       start = q->max_count;
+       size = new_max - q->max_count;
+
+       tmp = realloc(q->table, new_max * sizeof(Ethumbd_Object));
+       if (!tmp)
+         {
+            CRIT("could not realloc q->table to %zd bytes: %s",
+                 new_max * sizeof(Ethumbd_Object), strerror(errno));
+            return -1;
+         }
+       q->table = tmp;
+       memset(&q->table[start], 0, size * sizeof(Ethumbd_Object));
+
+       tmp = realloc(q->list, new_max * sizeof(int));
+       if (!tmp)
+         {
+            CRIT("could not realloc q->list to %zd bytes: %s",
+                 new_max * sizeof(int), strerror(errno));
+            return -1;
+         }
+       q->list = tmp;
+
+       q->max_count = new_max;
+     }
+
+   for (i = 0; i < q->max_count; i++)
+     {
+       if (!q->table[i].used)
+         break;
+     }
+
+   snprintf(buf, sizeof(buf), "%s/%d", _ethumb_dbus_path, i);
+   q->table[i].used = 1;
+   q->table[i].path = eina_stringshare_add(buf);
+   q->table[i].max_id = -1;
+   q->table[i].min_id = -1;
+   q->list[q->count] = i;
+   q->count++;
+   DBG("new object: %s, idx = %d, count = %d", buf, i, q->count);
+
+   return i;
+}
+
+static inline int
+_get_idx_for_path(const char *path)
+{
+   int i;
+   int n;
+   n = sscanf(path, "/org/enlightenment/Ethumb/%d", &i);
+   if (!n)
+     return -1;
+   return i;
+}
+
+static void
+_ethumb_table_del(Ethumbd *ed, int i)
+{
+   int j;
+   Eina_List *l;
+   const Eina_List *il;
+   Ethumbd_Queue *q = &ed->queue;
+   Ethumbd_Object_Data *odata;
+
+   eina_stringshare_del(q->table[i].path);
+
+   l = q->table[i].queue;
+   while (l)
+     {
+       Ethumbd_Request *request = l->data;
+       eina_stringshare_del(request->file);
+       eina_stringshare_del(request->key);
+       eina_stringshare_del(request->thumb);
+       eina_stringshare_del(request->thumb_key);
+       free(request);
+       l = eina_list_remove_list(l, l);
+     }
+   q->nqueue -= q->table[i].nqueue;
+
+   il = e_dbus_object_interfaces_get(q->table[i].dbus_obj);
+   while (il)
+     {
+       e_dbus_object_interface_detach(q->table[i].dbus_obj, il->data);
+       il = e_dbus_object_interfaces_get(q->table[i].dbus_obj);
+     }
+   odata = e_dbus_object_data_get(q->table[i].dbus_obj);
+   free(odata);
+   e_dbus_object_free(q->table[i].dbus_obj);
+
+   memset(&(q->table[i]), 0, sizeof(Ethumbd_Object));
+   for (j = 0; j < q->count; j++)
+     {
+       if (q->list[j] == i)
+         q->list[j] = q->list[q->count - 1];
+     }
+
+   q->count--;
+   _ethumbd_child_write_op_del(&ed->slave, i);
+   if (!q->count && !ed->processing)
+     _ethumbd_timeout_start(ed);
+}
+
+static void
+_ethumb_table_clear(Ethumbd *ed)
+{
+   int i;
+
+   for (i = 0; i < ed->queue.max_count; i++)
+     if (ed->queue.table[i].used)
+       _ethumb_table_del(ed, i);
+}
+
+static void
+_name_owner_changed_cb(void *data, DBusMessage *msg)
+{
+   DBusError err;
+   Ethumbd *ed = data;
+   Ethumbd_Queue *q = &ed->queue;
+   const char *name, *from, *to;
+   int i;
+
+   dbus_error_init(&err);
+   if (!dbus_message_get_args(msg, &err,
+                             DBUS_TYPE_STRING, &name,
+                             DBUS_TYPE_STRING, &from,
+                             DBUS_TYPE_STRING, &to,
+                             DBUS_TYPE_INVALID))
+     {
+       ERR("could not get NameOwnerChanged arguments: %s: %s",
+           err.name, err.message);
+       dbus_error_free(&err);
+       return;
+     }
+
+   DBG("NameOwnerChanged: name = %s, from = %s, to = %s", name, from, to);
+
+   if (from[0] == '\0' || to[0] != '\0')
+     return;
+
+   from = eina_stringshare_add(from);
+   for (i = 0; i < q->max_count; i++)
+     {
+       if (q->table[i].used && q->table[i].client == from)
+         {
+            _ethumb_table_del(ed, i);
+            DBG("deleting [%d] from queue table.", i);
+         }
+     }
+}
+
+static void
+_ethumb_dbus_add_name_owner_changed_cb(Ethumbd *ed)
+{
+   ed->name_owner_changed_handler = e_dbus_signal_handler_add
+     (ed->conn, fdo_bus_name, fdo_path, fdo_interface, "NameOwnerChanged",
+      _name_owner_changed_cb, ed);
+}
+
+DBusMessage *
+_ethumb_dbus_ethumb_new_cb(E_DBus_Object *object, DBusMessage *msg)
+{
+   DBusMessage *reply;
+   DBusMessageIter iter;
+   E_DBus_Object *dbus_object;
+   Ethumbd_Object_Data *odata;
+   int i;
+   const char *return_path = "";
+   const char *client;
+   Ethumbd *ed;
+
+   ed = e_dbus_object_data_get(object);
+   client = dbus_message_get_sender(msg);
+   if (!client)
+     goto end_new;
+
+   i = _ethumb_table_append(ed);
+   if (i < 0)
+     goto end_new;
+
+   odata = calloc(1, sizeof(*odata));
+   odata->idx = i;
+   odata->ed = ed;
+
+   ed->queue.table[i].client = eina_stringshare_add(client);
+   return_path = ed->queue.table[i].path;
+
+   dbus_object = e_dbus_object_add(ed->conn, return_path, odata);
+   if (!dbus_object)
+     {
+       ERR("could not create dbus_object.");
+       free(odata);
+       return_path = "";
+       goto end_new;
+     }
+
+   e_dbus_object_interface_attach(dbus_object, ed->objects_iface);
+   ed->queue.table[i].dbus_obj = dbus_object;
+
+   _ethumbd_child_write_op_new(&ed->slave, i);
+   _ethumbd_timeout_stop(ed);
+
+ end_new:
+   reply = dbus_message_new_method_return(msg);
+   dbus_message_iter_init_append(reply, &iter);
+   dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+                                 &return_path);
+   return reply;
+}
+
+static struct _Ethumb_DBus_Method_Table _ethumb_dbus_methods[] =
+  {
+    {"new", "", "o", _ethumb_dbus_ethumb_new_cb},
+    {NULL, NULL, NULL, NULL}
+  };
+
+static const char *
+_ethumb_dbus_get_bytearray(DBusMessageIter *iter)
+{
+   int el_type;
+   int length;
+   DBusMessageIter riter;
+   const char *result;
+
+   el_type = dbus_message_iter_get_element_type(iter);
+   if (el_type != DBUS_TYPE_BYTE)
+     {
+       ERR("not an byte array element.");
+       return NULL;
+     }
+
+   dbus_message_iter_recurse(iter, &riter);
+   dbus_message_iter_get_fixed_array(&riter, &result, &length);
+
+   if ((length == 0) || (result[0] == '\0'))
+     return NULL;
+   else
+     return eina_stringshare_add_length(result, length);
+}
+
+static void
+_ethumb_dbus_append_bytearray(DBusMessageIter *iter, const char *string)
+{
+   DBusMessageIter viter;
+
+   if (!string)
+     string = "";
+
+   dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &viter);
+   dbus_message_iter_append_fixed_array
+     (&viter, DBUS_TYPE_BYTE, &string, strlen(string) + 1);
+   dbus_message_iter_close_container(iter, &viter);
+}
+
+DBusMessage *
+_ethumb_dbus_queue_add_cb(E_DBus_Object *object, DBusMessage *msg)
+{
+   DBusMessage *reply;
+   DBusMessageIter iter;
+   const char *file, *key;
+   const char *thumb, *thumb_key;
+   Ethumbd_Object_Data *odata;
+   Ethumbd_Object *eobject;
+   Ethumbd *ed;
+   Ethumbd_Request *request;
+   dbus_int32_t id = -1;
+
+   dbus_message_iter_init(msg, &iter);
+   dbus_message_iter_get_basic(&iter, &id);
+   dbus_message_iter_next(&iter);
+   file = _ethumb_dbus_get_bytearray(&iter);
+   dbus_message_iter_next(&iter);
+   key = _ethumb_dbus_get_bytearray(&iter);
+   dbus_message_iter_next(&iter);
+   thumb = _ethumb_dbus_get_bytearray(&iter);
+   dbus_message_iter_next(&iter);
+   thumb_key = _ethumb_dbus_get_bytearray(&iter);
+
+   if (!file)
+     {
+       ERR("no filename given.");
+       goto end;
+     }
+
+   odata = e_dbus_object_data_get(object);
+   if (!odata)
+     {
+       ERR("could not get dbus_object data.");
+       goto end;
+     }
+
+   ed = odata->ed;
+   eobject = &(ed->queue.table[odata->idx]);
+   if (!_ethumb_dbus_check_id(eobject, id))
+     goto end;
+   request = calloc(1, sizeof(*request));
+   request->id = id;
+   request->file = file;
+   request->key = key;
+   request->thumb = thumb;
+   request->thumb_key = thumb_key;
+   eobject->queue = eina_list_append(eobject->queue, request);
+   eobject->nqueue++;
+   ed->queue.nqueue++;
+   _ethumb_dbus_inc_max_id(eobject, id);
+
+   _process_queue_start(ed);
+
+ end:
+   reply = dbus_message_new_method_return(msg);
+   dbus_message_iter_init_append(reply, &iter);
+   dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &id);
+   return reply;
+}
+
+DBusMessage *
+_ethumb_dbus_queue_remove_cb(E_DBus_Object *object, DBusMessage *msg)
+{
+   DBusMessage *reply;
+   DBusMessageIter iter;
+   dbus_int32_t id;
+   Ethumbd_Object_Data *odata;
+   Ethumbd_Object *eobject;
+   Ethumbd_Request *request;
+   Ethumbd *ed;
+   dbus_bool_t r = 0;
+   Eina_List *l;
+
+   dbus_message_iter_init(msg, &iter);
+   dbus_message_iter_get_basic(&iter, &id);
+
+   odata = e_dbus_object_data_get(object);
+   if (!odata)
+     {
+       ERR("could not get dbus_object data.");
+       goto end;
+     }
+
+   ed = odata->ed;
+   eobject = &ed->queue.table[odata->idx];
+   l = eobject->queue;
+   while (l)
+     {
+       request = l->data;
+       if (id == request->id)
+         break;
+       l = l->next;
+     }
+
+   if (l)
+     {
+       r = 1;
+       eina_stringshare_del(request->file);
+       eina_stringshare_del(request->key);
+       eina_stringshare_del(request->thumb);
+       eina_stringshare_del(request->thumb_key);
+       free(request);
+       eobject->queue = eina_list_remove_list(eobject->queue, l);
+       eobject->nqueue--;
+       ed->queue.nqueue--;
+       _ethumb_dbus_inc_min_id(eobject);
+     }
+
+ end:
+   reply = dbus_message_new_method_return(msg);
+   dbus_message_iter_init_append(reply, &iter);
+   dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &r);
+   return reply;
+}
+
+DBusMessage *
+_ethumb_dbus_queue_clear_cb(E_DBus_Object *object, DBusMessage *msg)
+{
+   DBusMessage *reply;
+   Ethumbd_Object_Data *odata;
+   Ethumbd_Object *eobject;
+   Ethumbd *ed;
+   Eina_List *l;
+
+   odata = e_dbus_object_data_get(object);
+   if (!odata)
+     {
+       ERR("could not get dbus_object data.");
+       goto end;
+     }
+
+   ed = odata->ed;
+   eobject = &ed->queue.table[odata->idx];
+   l = eobject->queue;
+   while (l)
+     {
+       Ethumbd_Request *request = l->data;
+       eina_stringshare_del(request->file);
+       eina_stringshare_del(request->key);
+       eina_stringshare_del(request->thumb);
+       eina_stringshare_del(request->thumb_key);
+       free(request);
+       l = eina_list_remove_list(l, l);
+     }
+   ed->queue.nqueue -= eobject->nqueue;
+   eobject->nqueue = 0;
+
+ end:
+   reply = dbus_message_new_method_return(msg);
+   return reply;
+}
+
+DBusMessage *
+_ethumb_dbus_delete_cb(E_DBus_Object *object, DBusMessage *msg)
+{
+   DBusMessage *reply;
+   DBusMessageIter iter;
+   Ethumbd_Object_Data *odata;
+   Ethumbd *ed;
+
+   dbus_message_iter_init(msg, &iter);
+   reply = dbus_message_new_method_return(msg);
+
+   odata = e_dbus_object_data_get(object);
+   if (!odata)
+     {
+       ERR("could not get dbus_object data for del_cb.");
+       return reply;
+     }
+   ed = odata->ed;
+   _ethumb_table_del(ed, odata->idx);
+   free(odata);
+
+   return reply;
+}
+
+static int
+_ethumb_dbus_fdo_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   int type;
+   dbus_int32_t fdo;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_INT32)
+     {
+       ERR("invalid param for fdo_set.");
+       return 0;
+     }
+
+   dbus_message_iter_get_basic(iter, &fdo);
+   DBG("setting fdo to: %d", fdo);
+   request->setup.flags.fdo = 1;
+   request->setup.fdo = fdo;
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_size_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   DBusMessageIter oiter;
+   int type;
+   dbus_int32_t w, h;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_STRUCT)
+     {
+       ERR("invalid param for size_set.");
+       return 0;
+     }
+
+   dbus_message_iter_recurse(iter, &oiter);
+   dbus_message_iter_get_basic(&oiter, &w);
+   dbus_message_iter_next(&oiter);
+   dbus_message_iter_get_basic(&oiter, &h);
+   DBG("setting size to: %dx%d", w, h);
+   request->setup.flags.size = 1;
+   request->setup.tw = w;
+   request->setup.th = h;
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_format_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   int type;
+   dbus_int32_t format;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_INT32)
+     {
+       ERR("invalid param for format_set.");
+       return 0;
+     }
+
+   dbus_message_iter_get_basic(iter, &format);
+   DBG("setting format to: %d", format);
+   request->setup.flags.format = 1;
+   request->setup.format = format;
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_aspect_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   int type;
+   dbus_int32_t aspect;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_INT32)
+     {
+       ERR("invalid param for aspect_set.");
+       return 0;
+     }
+
+   dbus_message_iter_get_basic(iter, &aspect);
+   DBG("setting aspect to: %d", aspect);
+   request->setup.flags.aspect = 1;
+   request->setup.aspect = aspect;
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_orientation_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   int type;
+   dbus_int32_t orientation;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_INT32)
+     {
+       ERR("invalid param for orientation_set.");
+       return 0;
+     }
+
+   dbus_message_iter_get_basic(iter, &orientation);
+   DBG("setting orientation to: %d", orientation);
+   request->setup.flags.orientation = 1;
+   request->setup.orientation = orientation;
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_crop_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   DBusMessageIter oiter;
+   int type;
+   double x, y;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_STRUCT)
+     {
+       ERR("invalid param for crop_set.");
+       return 0;
+     }
+
+   dbus_message_iter_recurse(iter, &oiter);
+   dbus_message_iter_get_basic(&oiter, &x);
+   dbus_message_iter_next(&oiter);
+   dbus_message_iter_get_basic(&oiter, &y);
+   DBG("setting crop to: %3.2f,%3.2f", x, y);
+   request->setup.flags.crop = 1;
+   request->setup.cx = x;
+   request->setup.cy = y;
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_quality_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   int type;
+   dbus_int32_t quality;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_INT32)
+     {
+       ERR("invalid param for quality_set.");
+       return 0;
+     }
+
+   dbus_message_iter_get_basic(iter, &quality);
+   DBG("setting quality to: %d", quality);
+   request->setup.flags.quality = 1;
+   request->setup.quality = quality;
+
+   return 1;
+}
+
+
+static int
+_ethumb_dbus_compress_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   int type;
+   dbus_int32_t compress;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_INT32)
+     {
+       ERR("invalid param for compress_set.");
+       return 0;
+     }
+
+   dbus_message_iter_get_basic(iter, &compress);
+   DBG("setting compress to: %d", compress);
+   request->setup.flags.compress = 1;
+   request->setup.compress = compress;
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_frame_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   DBusMessageIter oiter;
+   int type;
+   const char *file, *group, *swallow;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_STRUCT)
+     {
+       ERR("invalid param for frame_set.");
+       return 0;
+     }
+
+   dbus_message_iter_recurse(iter, &oiter);
+   file = _ethumb_dbus_get_bytearray(&oiter);
+   dbus_message_iter_next(&oiter);
+   group = _ethumb_dbus_get_bytearray(&oiter);
+   dbus_message_iter_next(&oiter);
+   swallow = _ethumb_dbus_get_bytearray(&oiter);
+   DBG("setting frame to \"%s:%s:%s\"", file, group, swallow);
+   request->setup.flags.frame = 1;
+   request->setup.theme_file = eina_stringshare_add(file);
+   request->setup.group = eina_stringshare_add(group);
+   request->setup.swallow = eina_stringshare_add(swallow);
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_directory_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   int type;
+   const char *directory;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_ARRAY)
+     {
+       ERR("invalid param for dir_path_set.");
+       return 0;
+     }
+
+   directory = _ethumb_dbus_get_bytearray(iter);
+   DBG("setting directory to: %s", directory);
+   request->setup.flags.directory = 1;
+   request->setup.directory = eina_stringshare_add(directory);
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_category_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   int type;
+   const char *category;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_ARRAY)
+     {
+       ERR("invalid param for category.");
+       return 0;
+     }
+
+   category = _ethumb_dbus_get_bytearray(iter);
+   DBG("setting category to: %s", category);
+   request->setup.flags.category = 1;
+   request->setup.category = eina_stringshare_add(category);
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_video_time_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   int type;
+   double video_time;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_DOUBLE)
+     {
+       ERR("invalid param for video_time_set.");
+       return 0;
+     }
+
+   dbus_message_iter_get_basic(iter, &video_time);
+   DBG("setting video_time to: %3.2f", video_time);
+   request->setup.flags.video_time = 1;
+   request->setup.video_time = video_time;
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_video_start_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   int type;
+   double video_start;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_DOUBLE)
+     {
+       ERR("invalid param for video_start_set.");
+       return 0;
+     }
+
+   dbus_message_iter_get_basic(iter, &video_start);
+   DBG("setting video_start to: %3.2f", video_start);
+   request->setup.flags.video_start = 1;
+   request->setup.video_start = video_start;
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_video_interval_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   int type;
+   double video_interval;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_DOUBLE)
+     {
+       ERR("invalid param for video_interval_set.");
+       return 0;
+     }
+
+   dbus_message_iter_get_basic(iter, &video_interval);
+   DBG("setting video_interval to: %3.2f", video_interval);
+   request->setup.flags.video_interval = 1;
+   request->setup.video_interval = video_interval;
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_video_ntimes_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   int type;
+   unsigned int video_ntimes;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_UINT32)
+     {
+       ERR("invalid param for video_ntimes_set.");
+       return 0;
+     }
+
+   dbus_message_iter_get_basic(iter, &video_ntimes);
+   DBG("setting video_ntimes to: %3.2d", video_ntimes);
+   request->setup.flags.video_ntimes = 1;
+   request->setup.video_ntimes = video_ntimes;
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_video_fps_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   int type;
+   unsigned int video_fps;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_UINT32)
+     {
+       ERR("invalid param for video_fps_set.");
+       return 0;
+     }
+
+   dbus_message_iter_get_basic(iter, &video_fps);
+   DBG("setting video_fps to: %3.2d", video_fps);
+   request->setup.flags.video_fps = 1;
+   request->setup.video_fps = video_fps;
+
+   return 1;
+}
+
+static int
+_ethumb_dbus_document_page_set(Ethumbd_Object *eobject __UNUSED__, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   int type;
+   unsigned int document_page;
+
+   type = dbus_message_iter_get_arg_type(iter);
+   if (type != DBUS_TYPE_UINT32)
+     {
+       ERR("invalid param for document_page_set.");
+       return 0;
+     }
+
+   dbus_message_iter_get_basic(iter, &document_page);
+   DBG("setting document_page to: %d", document_page);
+   request->setup.flags.document_page = 1;
+   request->setup.document_page = document_page;
+
+   return 1;
+}
+
+static struct
+{
+   const char *option;
+   int (*setup_func)(Ethumbd_Object *eobject, DBusMessageIter *iter, Ethumbd_Request *request);
+} _option_cbs[] = {
+  {"fdo", _ethumb_dbus_fdo_set},
+  {"size", _ethumb_dbus_size_set},
+  {"format", _ethumb_dbus_format_set},
+  {"aspect", _ethumb_dbus_aspect_set},
+  {"orientation", _ethumb_dbus_orientation_set},
+  {"crop", _ethumb_dbus_crop_set},
+  {"quality", _ethumb_dbus_quality_set},
+  {"compress", _ethumb_dbus_compress_set},
+  {"frame", _ethumb_dbus_frame_set},
+  {"directory", _ethumb_dbus_directory_set},
+  {"category", _ethumb_dbus_category_set},
+  {"video_time", _ethumb_dbus_video_time_set},
+  {"video_start", _ethumb_dbus_video_start_set},
+  {"video_interval", _ethumb_dbus_video_interval_set},
+  {"video_ntimes", _ethumb_dbus_video_ntimes_set},
+  {"video_fps", _ethumb_dbus_video_fps_set},
+  {"document_page", _ethumb_dbus_document_page_set},
+  {NULL, NULL}
+};
+
+static int
+_ethumb_dbus_ethumb_setup_parse_element(Ethumbd_Object *eobject, DBusMessageIter *iter, Ethumbd_Request *request)
+{
+   DBusMessageIter viter, diter;
+   const char *option;
+   int i, r;
+
+   dbus_message_iter_recurse(iter, &diter);
+   dbus_message_iter_get_basic(&diter, &option);
+   dbus_message_iter_next(&diter);
+
+   r = 0;
+   for (i = 0; _option_cbs[i].option; i++)
+     if (!strcmp(_option_cbs[i].option, option))
+       {
+         r = 1;
+         break;
+       }
+
+   if (!r)
+     {
+       ERR("ethumb_setup invalid option: %s", option);
+       return 0;
+     }
+
+   dbus_message_iter_recurse(&diter, &viter);
+   return _option_cbs[i].setup_func(eobject, &viter, request);
+}
+
+DBusMessage *
+_ethumb_dbus_ethumb_setup_cb(E_DBus_Object *object, DBusMessage *msg)
+{
+   DBusMessage *reply;
+   DBusMessageIter iter, aiter;
+   Ethumbd_Object_Data *odata;
+   Ethumbd *ed;
+   Ethumbd_Object *eobject;
+   Ethumbd_Request *request;
+   dbus_bool_t r = 0;
+   int atype;
+
+   dbus_message_iter_init(msg, &iter);
+   if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+     {
+       ERR("wrong parameters.");
+       goto end;
+     }
+
+   odata = e_dbus_object_data_get(object);
+   if (!odata)
+     {
+       ERR("could not get dbus_object data for setup_cb.");
+       goto end;
+     }
+
+   ed = odata->ed;
+   eobject = &ed->queue.table[odata->idx];
+
+   request = calloc(1, sizeof(*request));
+   request->id = -1;
+   dbus_message_iter_recurse(&iter, &aiter);
+   atype = dbus_message_iter_get_arg_type(&aiter);
+
+   r = 1;
+   while (atype != DBUS_TYPE_INVALID)
+     {
+       if (!_ethumb_dbus_ethumb_setup_parse_element(eobject, &aiter, request))
+         r = 0;
+       dbus_message_iter_next(&aiter);
+       atype = dbus_message_iter_get_arg_type(&aiter);
+     }
+
+   eobject->queue = eina_list_append(eobject->queue, request);
+   eobject->nqueue++;
+   ed->queue.nqueue++;
+
+ end:
+   reply = dbus_message_new_method_return(msg);
+   dbus_message_iter_init_append(reply, &iter);
+   dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &r);
+
+   return reply;
+}
+
+static void
+_ethumb_dbus_generated_signal(Ethumbd *ed, int *id, const char *thumb_path, const char *thumb_key, Eina_Bool success)
+{
+   DBusMessage *sig;
+   int current;
+   const char *opath;
+   DBusMessageIter iter;
+   dbus_bool_t value;
+   dbus_int32_t id32;
+
+   value = success;
+   id32 = *id;
+
+   current = ed->queue.current;
+   opath = ed->queue.table[current].path;
+   sig = dbus_message_new_signal
+     (opath, _ethumb_dbus_objects_interface, "generated");
+
+   dbus_message_iter_init_append(sig, &iter);
+   dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &id32);
+   _ethumb_dbus_append_bytearray(&iter, thumb_path);
+   _ethumb_dbus_append_bytearray(&iter, thumb_key);
+   dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &value);
+
+   e_dbus_message_send(ed->conn, sig, NULL, -1, NULL);
+   dbus_message_unref(sig);
+}
+
+static struct _Ethumb_DBus_Method_Table _ethumb_dbus_objects_methods[] = {
+  {"queue_add", "iayayayay", "i", _ethumb_dbus_queue_add_cb},
+  {"queue_remove", "i", "b", _ethumb_dbus_queue_remove_cb},
+  {"clear_queue", "", "", _ethumb_dbus_queue_clear_cb},
+  {"ethumb_setup", "a{sv}", "b", _ethumb_dbus_ethumb_setup_cb},
+  {"delete", "", "", _ethumb_dbus_delete_cb},
+  {NULL, NULL, NULL, NULL}
+};
+
+static struct _Ethumb_DBus_Signal_Table _ethumb_dbus_objects_signals[] = {
+  {"generated", "iayayb"},
+  {NULL, NULL}
+};
+
+static int
+_ethumb_dbus_interface_elements_add(E_DBus_Interface *iface, struct _Ethumb_DBus_Method_Table *mtable, struct _Ethumb_DBus_Signal_Table *stable)
+{
+   int i = -1;
+   while (mtable && mtable[++i].name)
+     if (!e_dbus_interface_method_add(iface,
+                                     mtable[i].name,
+                                     mtable[i].signature,
+                                     mtable[i].reply,
+                                     mtable[i].function))
+       return 0;
+
+   i = -1;
+   while (stable && stable[++i].name)
+     if (!e_dbus_interface_signal_add(iface,
+                                     stable[i].name,
+                                     stable[i].signature))
+       return 0;
+   return 1;
+}
+
+static void
+_ethumb_dbus_request_name_cb(void *data, DBusMessage *msg __UNUSED__, DBusError *err)
+{
+   E_DBus_Object *dbus_object;
+   Ethumbd *ed = data;
+   int r;
+
+   if (dbus_error_is_set(err))
+     {
+       ERR("request name error: %s", err->message);
+       dbus_error_free(err);
+       e_dbus_connection_close(ed->conn);
+       return;
+     }
+
+   dbus_object = e_dbus_object_add(ed->conn, _ethumb_dbus_path, ed);
+   if (!dbus_object)
+     return;
+   ed->dbus_obj = dbus_object;
+   ed->eiface = e_dbus_interface_new(_ethumb_dbus_interface);
+   if (!ed->eiface)
+     {
+       ERR("could not create interface.");
+       return;
+     }
+   r = _ethumb_dbus_interface_elements_add(ed->eiface,
+                                          _ethumb_dbus_methods, NULL);
+   if (!r)
+     {
+       ERR("could not add methods to the interface.");
+       e_dbus_interface_unref(ed->eiface);
+       return;
+     }
+   e_dbus_object_interface_attach(dbus_object, ed->eiface);
+
+   ed->objects_iface = e_dbus_interface_new(_ethumb_dbus_objects_interface);
+   if (!ed->objects_iface)
+     {
+       ERR("could not create interface.");
+       return;
+     }
+
+   r = _ethumb_dbus_interface_elements_add(ed->objects_iface,
+                                          _ethumb_dbus_objects_methods,
+                                          _ethumb_dbus_objects_signals);
+   if (!r)
+     {
+       ERR("ERROR: could not setup objects interface methods.");
+       e_dbus_interface_unref(ed->objects_iface);
+       return;
+     }
+
+   _ethumb_dbus_add_name_owner_changed_cb(ed);
+
+   _ethumbd_timeout_start(ed);
+}
+
+static int
+_ethumb_dbus_setup(Ethumbd *ed)
+{
+   e_dbus_request_name
+     (ed->conn, _ethumb_dbus_bus_name, 0, _ethumb_dbus_request_name_cb, ed);
+
+   return 1;
+}
+
+static void
+_ethumb_dbus_finish(Ethumbd *ed)
+{
+   _process_queue_stop(ed);
+   _ethumb_table_clear(ed);
+   e_dbus_signal_handler_del(ed->conn, ed->name_owner_changed_handler);
+   e_dbus_interface_unref(ed->objects_iface);
+   e_dbus_interface_unref(ed->eiface);
+   e_dbus_object_free(ed->dbus_obj);
+   free(ed->queue.table);
+   free(ed->queue.list);
+}
+
+static Eina_Bool
+_ethumbd_slave_spawn(Ethumbd_Slave *slave, Ethumbd *ed)
+{
+   slave->bufcmd = NULL;
+   slave->scmd = 0;
+
+   slave->exe = ecore_exe_pipe_run(
+      ETHUMB_LIBEXEC_DIR"/ethumbd_slave",
+      ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_WRITE, ed);
+   if (!slave->exe)
+     {
+       ERR("could not create slave.");
+       return 0;
+     }
+
+   return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+   Eina_Bool quit_option = 0;
+   int exit_value = 0;
+   int arg_idx;
+   Ethumbd ed;
+   int child;
+   double timeout = -1;
+
+   memset(&ed, 0, sizeof(ed));
+   ecore_init();
+   eina_init();
+
+   ethumb_init();
+
+   if (_log_domain < 0)
+     {
+       _log_domain = eina_log_domain_register("ethumbd", NULL);
+       if (_log_domain < 0)
+         {
+            EINA_LOG_CRIT("could not register log domain 'ethumbd'");
+            exit_value = -7;
+            goto finish;
+         }
+     }
+
+   ed.data_cb = ecore_event_handler_add(ECORE_EXE_EVENT_DATA,
+                                       _ethumbd_slave_data_read_cb, &ed);
+   ed.del_cb = ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
+                                      _ethumbd_slave_del_cb, &ed);
+
+   child = _ethumbd_slave_spawn(&ed.slave, &ed);
+   if (!child)
+     {
+       exit_value = -6;
+       goto finish;
+     }
+
+   if (child == 2)
+     {
+       exit_value = 0;
+       goto finish;
+     }
+
+   if (!e_dbus_init())
+     {
+       ERR("could not init e_dbus.");
+       exit_value = -1;
+       goto finish;
+     }
+
+   Ecore_Getopt_Value values[] = {
+     ECORE_GETOPT_VALUE_DOUBLE(timeout),
+     ECORE_GETOPT_VALUE_BOOL(quit_option),
+     ECORE_GETOPT_VALUE_BOOL(quit_option),
+     ECORE_GETOPT_VALUE_BOOL(quit_option),
+     ECORE_GETOPT_VALUE_BOOL(quit_option),
+     ECORE_GETOPT_VALUE_NONE
+   };
+
+   arg_idx = ecore_getopt_parse(&optdesc, values, argc, argv);
+   if (arg_idx < 0)
+     {
+       ERR("Could not parse arguments.");
+       exit_value = -2;
+       goto finish;
+     }
+
+   if (quit_option)
+     goto finish;
+
+   ed.conn = e_dbus_bus_get(DBUS_BUS_SESSION);
+   if (!ed.conn)
+     {
+       ERR("could not connect to session bus.");
+       exit_value = -3;
+       goto finish_edbus;
+     }
+
+   ed.timeout = timeout;
+
+   if (!_ethumb_dbus_setup(&ed))
+     {
+       e_dbus_connection_close(ed.conn);
+       ERR("could not setup dbus connection.");
+       exit_value = -5;
+       goto finish_edbus;
+     }
+
+   ecore_main_loop_begin();
+   _ethumb_dbus_finish(&ed);
+
+ finish_edbus:
+   if (_log_domain >= 0)
+     {
+       eina_log_domain_unregister(_log_domain);
+       _log_domain = -1;
+     }
+
+   e_dbus_shutdown();
+ finish:
+   if (ed.slave.exe)
+     ecore_exe_quit(ed.slave.exe);
+   ethumb_shutdown();
+   eina_init();
+   ecore_shutdown();
+   return exit_value;
+}
diff --git a/src/bin/ethumbd_child.c b/src/bin/ethumbd_child.c
new file mode 100644 (file)
index 0000000..a11c90c
--- /dev/null
@@ -0,0 +1,770 @@
+/**
+ * @file
+ *
+ * Copyright (C) 2009 by ProFUSION embedded systems
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Rafael Antognolli <antognolli@profusion.mobi>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+
+#include <Eina.h>
+#include <Ecore.h>
+#include <Ethumb.h>
+
+#include "ethumbd_private.h"
+
+#define DBG(...) EINA_LOG_DOM_DBG(_log_domain, __VA_ARGS__)
+#define INF(...) EINA_LOG_DOM_INFO(_log_domain, __VA_ARGS__)
+#define WRN(...) EINA_LOG_DOM_WARN(_log_domain, __VA_ARGS__)
+#define ERR(...) EINA_LOG_DOM_ERR(_log_domain, __VA_ARGS__)
+
+#define NETHUMBS 100
+
+static int _log_domain = -1;
+
+struct _Ethumbd_Child
+{
+   Ecore_Fd_Handler *fd_handler;
+   Ethumb *ethumbt[NETHUMBS];
+};
+
+
+int
+_ec_read_safe(int fd, void *buf, ssize_t size)
+{
+   ssize_t todo;
+   char *p;
+
+   todo = size;
+   p = buf;
+
+   while (todo > 0)
+     {
+       ssize_t r;
+
+       r = read(fd, p, todo);
+       if (r > 0)
+         {
+            todo -= r;
+            p += r;
+         }
+       else if (r == 0)
+         return 0;
+       else
+         {
+            if (errno == EINTR || errno == EAGAIN)
+              continue;
+            else
+              {
+                 ERR("could not read from fd %d: %s",
+                     fd, strerror(errno));
+                 return 0;
+              }
+         }
+     }
+
+   return 1;
+}
+
+int
+_ec_write_safe(int fd, const void *buf, ssize_t size)
+{
+   ssize_t todo;
+   const char *p;
+
+   todo = size;
+   p = buf;
+
+   while (todo > 0)
+     {
+       ssize_t r;
+
+       r = write(fd, p, todo);
+       if (r > 0)
+         {
+            todo -= r;
+            p += r;
+         }
+       else if (r == 0)
+         return 0;
+       else
+         {
+            if (errno == EINTR || errno == EAGAIN)
+              continue;
+            else
+              {
+                 ERR("could not write to fd %d: %s", fd, strerror(errno));
+                 return 0;
+              }
+         }
+     }
+
+   return 1;
+}
+
+static int
+_ec_pipe_str_read(struct _Ethumbd_Child *ec __UNUSED__, char **str)
+{
+   int size;
+   int r;
+   char buf[PATH_MAX];
+
+   r = _ec_read_safe(STDIN_FILENO, &size, sizeof(size));
+   if (!r)
+     {
+       *str = NULL;
+       return 0;
+     }
+
+   if (!size)
+     {
+       *str = NULL;
+       return 1;
+     }
+
+   r = _ec_read_safe(STDIN_FILENO, buf, size);
+   if (!r)
+     {
+       *str = NULL;
+       return 0;
+     }
+
+   *str = strdup(buf);
+   return 1;
+}
+
+static struct _Ethumbd_Child *
+_ec_new(void)
+{
+   struct _Ethumbd_Child *ec = calloc(1, sizeof(*ec));
+
+   return ec;
+}
+
+static void
+_ec_free(struct _Ethumbd_Child *ec)
+{
+   int i;
+
+   if (ec->fd_handler)
+     ecore_main_fd_handler_del(ec->fd_handler);
+
+   for (i = 0; i < NETHUMBS; i++)
+     {
+       if (ec->ethumbt[i])
+         ethumb_free(ec->ethumbt[i]);
+     }
+
+   free(ec);
+}
+
+static int
+_ec_op_new(struct _Ethumbd_Child *ec)
+{
+   int r;
+   int idx;
+
+   r = _ec_read_safe(STDIN_FILENO, &idx, sizeof(idx));
+   if (!r)
+     return 0;
+
+   DBG("ethumbd new(). idx = %d", idx);
+
+   ec->ethumbt[idx] = ethumb_new();
+   return 1;
+}
+
+static int
+_ec_op_del(struct _Ethumbd_Child *ec)
+{
+   int r;
+   int idx;
+
+   r = _ec_read_safe(STDIN_FILENO, &idx, sizeof(idx));
+   if (!r)
+     return 0;
+
+   DBG("ethumbd del(). idx = %d", idx);
+
+   ethumb_free(ec->ethumbt[idx]);
+   ec->ethumbt[idx] = NULL;
+   return 1;
+}
+
+static void
+_ec_op_generated_cb(void *data __UNUSED__, Ethumb *e, Eina_Bool success)
+{
+   const char *thumb_path, *thumb_key;
+   int size_path, size_key, size_cmd;
+
+   DBG("thumb generated (%i)!", success);
+   ethumb_thumb_path_get(e, &thumb_path, &thumb_key);
+
+   if (!thumb_path)
+     size_path = 0;
+   else
+     size_path = strlen(thumb_path) + 1;
+
+   if (!thumb_key)
+     size_key = 0;
+   else
+     size_key = strlen(thumb_key) + 1;
+
+   size_cmd = sizeof(success) + sizeof(size_path) + size_path +
+      sizeof(size_key) + size_key;
+
+   _ec_write_safe(STDOUT_FILENO, &size_cmd, sizeof(size_cmd));
+   _ec_write_safe(STDOUT_FILENO, &success, sizeof(success));
+
+   _ec_write_safe(STDOUT_FILENO, &size_path, sizeof(size_path));
+   _ec_write_safe(STDOUT_FILENO, thumb_path, size_path);
+
+   _ec_write_safe(STDOUT_FILENO, &size_key, sizeof(size_key));
+   _ec_write_safe(STDOUT_FILENO, thumb_key, size_key);
+}
+
+static int
+_ec_op_generate(struct _Ethumbd_Child *ec)
+{
+   int idx;
+   char *path, *key, *thumb_path, *thumb_key;
+   int r;
+
+   r = _ec_read_safe(STDIN_FILENO, &idx, sizeof(idx));
+   if (!r)
+     return 0;
+
+   r = _ec_pipe_str_read(ec, &path);
+   if (!r)
+     return 0;
+   r = _ec_pipe_str_read(ec, &key);
+   if (!r)
+     return 0;
+   r = _ec_pipe_str_read(ec, &thumb_path);
+   if (!r)
+     return 0;
+   r = _ec_pipe_str_read(ec, &thumb_key);
+   if (!r)
+     return 0;
+
+   ethumb_file_set(ec->ethumbt[idx], path, key);
+   ethumb_thumb_path_set(ec->ethumbt[idx], thumb_path, thumb_key);
+
+   if (ethumb_exists(ec->ethumbt[idx]))
+     {
+        _ec_op_generated_cb(ec, ec->ethumbt[idx], EINA_TRUE);
+     }
+   else
+     {
+        ethumb_generate(ec->ethumbt[idx], _ec_op_generated_cb, ec, NULL);
+     }
+
+   free(path);
+   free(key);
+   free(thumb_path);
+   free(thumb_key);
+
+   return 1;
+}
+
+static int
+_ec_fdo_set(struct _Ethumbd_Child *ec __UNUSED__, Ethumb *e)
+{
+   int r;
+   int value;
+
+   r = _ec_read_safe(STDIN_FILENO, &value, sizeof(value));
+   if (!r)
+     return 0;
+   ethumb_thumb_fdo_set(e, value);
+   DBG("fdo = %d", value);
+
+   return 1;
+}
+
+static int
+_ec_size_set(struct _Ethumbd_Child *ec __UNUSED__, Ethumb *e)
+{
+   int r;
+   int w, h;
+   int type;
+
+   r = _ec_read_safe(STDIN_FILENO, &w, sizeof(w));
+   if (!r)
+     return 0;
+   r = _ec_read_safe(STDIN_FILENO, &type, sizeof(type));
+   if (!r)
+     return 0;
+   r = _ec_read_safe(STDIN_FILENO, &h, sizeof(h));
+   if (!r)
+     return 0;
+   ethumb_thumb_size_set(e, w, h);
+   DBG("size = %dx%d", w, h);
+
+   return 1;
+}
+
+static int
+_ec_format_set(struct _Ethumbd_Child *ec __UNUSED__, Ethumb *e)
+{
+   int r;
+   int value;
+
+   r = _ec_read_safe(STDIN_FILENO, &value, sizeof(value));
+   if (!r)
+     return 0;
+   ethumb_thumb_format_set(e, value);
+   DBG("format = %d", value);
+
+   return 1;
+}
+
+static int
+_ec_aspect_set(struct _Ethumbd_Child *ec __UNUSED__, Ethumb *e)
+{
+   int r;
+   int value;
+
+   r = _ec_read_safe(STDIN_FILENO, &value, sizeof(value));
+   if (!r)
+     return 0;
+   ethumb_thumb_aspect_set(e, value);
+   DBG("aspect = %d", value);
+
+   return 1;
+}
+
+static int
+_ec_orientation_set(struct _Ethumbd_Child *ec __UNUSED__, Ethumb *e)
+{
+   int r;
+   int value;
+
+   r = _ec_read_safe(STDIN_FILENO, &value, sizeof(value));
+   if (!r)
+     return 0;
+   ethumb_thumb_orientation_set(e, value);
+   DBG("orientation = %d", value);
+
+   return 1;
+}
+
+static int
+_ec_crop_set(struct _Ethumbd_Child *ec __UNUSED__, Ethumb *e)
+{
+   int r;
+   float x, y;
+   int type;
+
+   r = _ec_read_safe(STDIN_FILENO, &x, sizeof(x));
+   if (!r)
+     return 0;
+   r = _ec_read_safe(STDIN_FILENO, &type, sizeof(type));
+   if (!r)
+     return 0;
+   r = _ec_read_safe(STDIN_FILENO, &y, sizeof(y));
+   if (!r)
+     return 0;
+   ethumb_thumb_crop_align_set(e, x, y);
+   DBG("crop = %fx%f", x, y);
+
+   return 1;
+}
+
+static int
+_ec_quality_set(struct _Ethumbd_Child *ec __UNUSED__, Ethumb *e)
+{
+   int r;
+   int value;
+
+   r = _ec_read_safe(STDIN_FILENO, &value, sizeof(value));
+   if (!r)
+     return 0;
+   ethumb_thumb_quality_set(e, value);
+   DBG("quality = %d", value);
+
+   return 1;
+}
+
+static int
+_ec_compress_set(struct _Ethumbd_Child *ec __UNUSED__, Ethumb *e)
+{
+   int r;
+   int value;
+
+   r = _ec_read_safe(STDIN_FILENO, &value, sizeof(value));
+   if (!r)
+     return 0;
+   ethumb_thumb_compress_set(e, value);
+   DBG("compress = %d", value);
+
+   return 1;
+}
+
+static int
+_ec_frame_set(struct _Ethumbd_Child *ec, Ethumb *e)
+{
+   int r;
+   int type;
+   char *theme_file, *group, *swallow;
+
+   r = _ec_pipe_str_read(ec, &theme_file);
+   if (!r)
+     return 0;
+   r = _ec_read_safe(STDIN_FILENO, &type, sizeof(type));
+   if (!r)
+     return 0;
+   r = _ec_pipe_str_read(ec, &group);
+   if (!r)
+     return 0;
+   r = _ec_read_safe(STDIN_FILENO, &type, sizeof(type));
+   if (!r)
+     return 0;
+   r = _ec_pipe_str_read(ec, &swallow);
+   if (!r)
+     return 0;
+   DBG("frame = %s:%s:%s", theme_file, group, swallow);
+   ethumb_frame_set(e, theme_file, group, swallow);
+   free(theme_file);
+   free(group);
+   free(swallow);
+
+   return 1;
+}
+
+static int
+_ec_directory_set(struct _Ethumbd_Child *ec, Ethumb *e)
+{
+   int r;
+   char *directory;
+
+   r = _ec_pipe_str_read(ec, &directory);
+   if (!r)
+     return 0;
+   ethumb_thumb_dir_path_set(e, directory);
+   DBG("directory = %s", directory);
+   free(directory);
+
+   return 1;
+}
+
+static int
+_ec_category_set(struct _Ethumbd_Child *ec, Ethumb *e)
+{
+   int r;
+   char *category;
+
+   r = _ec_pipe_str_read(ec, &category);
+   if (!r)
+     return 0;
+   ethumb_thumb_category_set(e, category);
+   DBG("category = %s", category);
+   free(category);
+
+   return 1;
+}
+
+static int
+_ec_video_time_set(struct _Ethumbd_Child *ec __UNUSED__, Ethumb *e)
+{
+   int r;
+   float value;
+
+   r = _ec_read_safe(STDIN_FILENO, &value, sizeof(value));
+   if (!r)
+     return 0;
+   ethumb_video_time_set(e, value);
+   DBG("video_time = %f", value);
+
+   return 1;
+}
+
+static int
+_ec_video_start_set(struct _Ethumbd_Child *ec __UNUSED__, Ethumb *e)
+{
+   int r;
+   float value;
+
+   r = _ec_read_safe(STDIN_FILENO, &value, sizeof(value));
+   if (!r)
+     return 0;
+   ethumb_video_start_set(e, value);
+   DBG("video_start = %f", value);
+
+   return 1;
+}
+
+static int
+_ec_video_interval_set(struct _Ethumbd_Child *ec __UNUSED__, Ethumb *e)
+{
+   int r;
+   float value;
+
+   r = _ec_read_safe(STDIN_FILENO, &value, sizeof(value));
+   if (!r)
+     return 0;
+   ethumb_video_interval_set(e, value);
+   DBG("video_interval = %f", value);
+
+   return 1;
+}
+
+static int
+_ec_video_ntimes_set(struct _Ethumbd_Child *ec __UNUSED__, Ethumb *e)
+{
+   int r;
+   int value;
+
+   r = _ec_read_safe(STDIN_FILENO, &value, sizeof(value));
+   if (!r)
+     return 0;
+   ethumb_video_ntimes_set(e, value);
+   DBG("video_ntimes = %d", value);
+
+   return 1;
+}
+
+static int
+_ec_video_fps_set(struct _Ethumbd_Child *ec __UNUSED__, Ethumb *e)
+{
+   int r;
+   int value;
+
+   r = _ec_read_safe(STDIN_FILENO, &value, sizeof(value));
+   if (!r)
+     return 0;
+   ethumb_video_fps_set(e, value);
+   DBG("video_fps = %d", value);
+
+   return 1;
+}
+
+static int
+_ec_document_page_set(struct _Ethumbd_Child *ec __UNUSED__, Ethumb *e)
+{
+   int r;
+   int value;
+
+   r = _ec_read_safe(STDIN_FILENO, &value, sizeof(value));
+   if (!r)
+     return 0;
+   ethumb_document_page_set(e, value);
+   DBG("document_page = %d", value);
+
+   return 1;
+}
+
+static void
+_ec_setup_process(struct _Ethumbd_Child *ec, int idx, int type)
+{
+   Ethumb *e;
+
+   e = ec->ethumbt[idx];
+
+   switch (type)
+     {
+      case ETHUMBD_FDO:
+        _ec_fdo_set(ec, e);
+        break;
+      case ETHUMBD_SIZE_W:
+        _ec_size_set(ec, e);
+        break;
+      case ETHUMBD_FORMAT:
+        _ec_format_set(ec, e);
+        break;
+      case ETHUMBD_ASPECT:
+        _ec_aspect_set(ec, e);
+        break;
+      case ETHUMBD_ORIENTATION:
+        _ec_orientation_set(ec, e);
+        break;
+      case ETHUMBD_CROP_X:
+        _ec_crop_set(ec, e);
+        break;
+      case ETHUMBD_QUALITY:
+        _ec_quality_set(ec, e);
+        break;
+      case ETHUMBD_COMPRESS:
+        _ec_compress_set(ec, e);
+        break;
+      case ETHUMBD_FRAME_FILE:
+        _ec_frame_set(ec, e);
+        break;
+      case ETHUMBD_DIRECTORY:
+        _ec_directory_set(ec, e);
+        break;
+      case ETHUMBD_CATEGORY:
+        _ec_category_set(ec, e);
+        break;
+      case ETHUMBD_VIDEO_TIME:
+        _ec_video_time_set(ec, e);
+        break;
+      case ETHUMBD_VIDEO_START:
+        _ec_video_start_set(ec, e);
+        break;
+      case ETHUMBD_VIDEO_INTERVAL:
+        _ec_video_interval_set(ec, e);
+        break;
+      case ETHUMBD_VIDEO_NTIMES:
+        _ec_video_ntimes_set(ec, e);
+        break;
+      case ETHUMBD_VIDEO_FPS:
+        _ec_video_fps_set(ec, e);
+        break;
+      case ETHUMBD_DOCUMENT_PAGE:
+        _ec_document_page_set(ec, e);
+        break;
+      default:
+        ERR("wrong type!");
+     }
+}
+
+static int
+_ec_op_setup(struct _Ethumbd_Child *ec)
+{
+   int r;
+   int idx;
+   int type;
+
+   r = _ec_read_safe(STDIN_FILENO, &idx, sizeof(idx));
+   if (!r)
+     return 0;
+
+   r = _ec_read_safe(STDIN_FILENO, &type, sizeof(type));
+   if (!r)
+     return 0;
+   while (type != ETHUMBD_SETUP_FINISHED)
+     {
+       _ec_setup_process(ec, idx, type);
+       r = _ec_read_safe(STDIN_FILENO, &type, sizeof(type));
+       if (!r)
+         return 0;
+     }
+
+   return 1;
+}
+
+static Eina_Bool
+_ec_fd_handler(void *data, Ecore_Fd_Handler *fd_handler)
+{
+   struct _Ethumbd_Child *ec = data;
+   int op_id;
+   int r;
+
+   if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR))
+     {
+       ERR("error on pipein! child exiting...");
+       ec->fd_handler = NULL;
+       ecore_main_loop_quit();
+       return 0;
+     }
+
+   r = _ec_read_safe(STDIN_FILENO, &op_id, sizeof(op_id));
+   if (!r)
+     {
+       DBG("ethumbd exited! child exiting...");
+       ec->fd_handler = NULL;
+       ecore_main_loop_quit();
+       return 0;
+     }
+
+   DBG("received op: %d", op_id);
+
+   switch (op_id)
+     {
+      case ETHUMBD_OP_NEW:
+        r = _ec_op_new(ec);
+        break;
+      case ETHUMBD_OP_GENERATE:
+        r = _ec_op_generate(ec);
+        break;
+      case ETHUMBD_OP_SETUP:
+        r = _ec_op_setup(ec);
+        break;
+      case ETHUMBD_OP_DEL:
+        r = _ec_op_del(ec);
+        break;
+      default:
+        ERR("invalid operation: %d", op_id);
+        r = 0;
+     }
+
+   if (!r)
+     {
+       ERR("ethumbd exited! child exiting...");
+       ec->fd_handler = NULL;
+       ecore_main_loop_quit();
+     }
+
+   return r;
+}
+
+static void
+_ec_setup(struct _Ethumbd_Child *ec)
+{
+   ec->fd_handler = ecore_main_fd_handler_add(
+      STDIN_FILENO, ECORE_FD_READ | ECORE_FD_ERROR,
+      _ec_fd_handler, ec, NULL, NULL);
+}
+
+int
+main(int argc __UNUSED__, const char *argv[] __UNUSED__)
+{
+   struct _Ethumbd_Child *ec;
+
+   ethumb_init();
+
+   if (_log_domain < 0)
+     {
+       _log_domain = eina_log_domain_register("ethumbd_child", NULL);
+
+       if (_log_domain < 0)
+         {
+            EINA_LOG_CRIT("could not register log domain 'ethumbd_child'");
+            ethumb_shutdown();
+            return 1;
+         }
+     }
+
+   ec = _ec_new();
+
+   _ec_setup(ec);
+
+   DBG("child started!");
+   ecore_main_loop_begin();
+   DBG("child finishing.");
+
+   _ec_free(ec);
+
+   if (_log_domain >= 0)
+     {
+       eina_log_domain_unregister(_log_domain);
+       _log_domain = -1;
+     }
+   ethumb_shutdown();
+
+   return 0;
+}
diff --git a/src/bin/ethumbd_client.c b/src/bin/ethumbd_client.c
new file mode 100644 (file)
index 0000000..e236639
--- /dev/null
@@ -0,0 +1,330 @@
+/**
+ * @file
+ *
+ * Copyright (C) 2009 by ProFUSION embedded systems
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Rafael Antognolli <antognolli@profusion.mobi>
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include <Eina.h>
+#include <Ecore_Getopt.h>
+#include <Ecore.h>
+#include <Ethumb_Client.h>
+
+const char *aspect_opt[] = { "keep", "ignore", "crop", NULL };
+const char *format_opt[] = { "png", "jpg", "eet", NULL };
+struct frame
+{
+   const char *file;
+   const char *group;
+   const char *swallow;
+};
+
+struct options
+{
+   Eina_Rectangle geometry;
+   unsigned int format, aspect;
+   char *format_str;
+   char *aspect_str;
+   char *directory;
+   char *category;
+   struct frame frame;
+   char *src_path;
+   char *src_key;
+   const char *thumb_path;
+   const char *thumb_key;
+   double video_time;
+   int page;
+};
+
+static unsigned char
+_ethumb_getopt_callback_frame_parse(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc __UNUSED__, const char *str, void *data __UNUSED__, Ecore_Getopt_Value *storage)
+{
+   struct frame *f = (struct frame *)storage->ptrp;
+   const char *tfile, *tgroup, *tswallow, *base, *sep;
+
+   base = str;
+   sep = strchr(base, ':');
+   if (!sep)
+       goto error;
+   tfile = eina_stringshare_add_length(base, sep - base);
+   base = sep + 1;
+
+   sep = strchr(base, ':');
+   if (!sep)
+     {
+       eina_stringshare_del(tfile);
+       goto error;
+     }
+   tgroup = eina_stringshare_add_length(base, sep - base);
+   base = sep + 1;
+   if (base[0] == '\0')
+     {
+       eina_stringshare_del(tfile);
+       eina_stringshare_del(tgroup);
+       goto error;
+     }
+   tswallow = eina_stringshare_add(base);
+
+   f->file = tfile;
+   f->group = tgroup;
+   f->swallow = tswallow;
+   return 1;
+
+ error:
+   fprintf(stderr,
+          "ERROR: invalid theme, not in format "
+          "'file:group:swallow_part': '%s'\n",
+          str);
+   return 0;
+}
+
+const Ecore_Getopt optdesc = {
+  "ethumbd_client",
+  NULL,
+  PACKAGE_VERSION,
+  "(C) 2009 - ProFUSION embedded systems",
+  "LGPL v3 - GNU Lesser General Public License",
+  "Thumbnails generator client using DBus and ethumbd.\n"
+  "\n"
+  "This program uses ethumbd server to create thumbnails from pictures. "
+  "It's an example of use and a test for ethumbd.\n",
+  0,
+  {
+     ECORE_GETOPT_CALLBACK_ARGS
+     ('s', "size", "thumbnail size expected.",
+      "WxH", ecore_getopt_callback_size_parse, NULL),
+     ECORE_GETOPT_CHOICE
+     ('f', "format", "file format to save.", format_opt),
+     ECORE_GETOPT_CHOICE
+     ('a', "aspect", "original image aspect ratio.", aspect_opt),
+     ECORE_GETOPT_STORE_STR
+     ('d', "directory", "directory to save thumbnails."),
+     ECORE_GETOPT_STORE_STR
+     ('c', "category", "thumbnails category."),
+     ECORE_GETOPT_CALLBACK_ARGS
+     ('t', "theme", "path to theme file, group and swallow part.",
+      "file:group:swallow_part", _ethumb_getopt_callback_frame_parse, NULL),
+     ECORE_GETOPT_STORE_STR
+     ('k', "key", "key inside eet file to read image from."),
+     ECORE_GETOPT_STORE_DOUBLE
+     ('v', "video_time", "time of video frame to use as thumbnail."),
+     ECORE_GETOPT_STORE_INT
+     ('p', "document_page", "document page to use as thumbnail."),
+     ECORE_GETOPT_LICENSE('L', "license"),
+     ECORE_GETOPT_COPYRIGHT('C', "copyright"),
+     ECORE_GETOPT_VERSION('V', "version"),
+     ECORE_GETOPT_HELP('h', "help"),
+     ECORE_GETOPT_SENTINEL
+  }
+};
+
+static void
+_thumb_report(const char *mode, const char *src_path, const char *src_key, const char *thumb_path, const char *thumb_key)
+{
+   printf("%s '%s' '%s' => '%s' '%s'\n",
+         mode,
+         src_path, src_key ? src_key : "",
+         thumb_path, thumb_key ? thumb_key : "");
+}
+
+static void
+_finished_thumb(void *data __UNUSED__, Ethumb_Client *client __UNUSED__, int id __UNUSED__, const char *src_path, const char *src_key, const char *thumb_path, const char *thumb_key, Eina_Bool success)
+{
+   const char *mode = success ? "GENERATED" : "FAILED";
+   _thumb_report(mode, src_path, src_key, thumb_path, thumb_key);
+   ecore_main_loop_quit();
+}
+
+static void
+_exists(void *data, Ethumb_Client *c, __UNUSED__ Ethumb_Exists *thread, Eina_Bool exists)
+{
+   struct options *opts = data;
+   const char *thumb_path, *thumb_key;
+   long id;
+
+   if (exists)
+     {
+        ethumb_client_thumb_path_get(c, &thumb_path, &thumb_key);
+        _thumb_report
+          ("EXISTS", opts->src_path, opts->src_key, thumb_path, thumb_key);
+        ecore_main_loop_quit();
+        return;
+     }
+
+   id = ethumb_client_generate(c, _finished_thumb, NULL, NULL);
+   if (id < 0)
+     {
+       fputs("ERROR: could not request thumbnail to be generated.\n", stderr);
+       ecore_main_loop_quit();
+       return;
+     }
+   printf("request id=%ld, file='%s', key='%s'\n",
+         id, opts->src_path, opts->src_key ? opts->src_key : "");
+
+}
+
+static void
+_connected(void *data, Ethumb_Client *c, Eina_Bool success)
+{
+   struct options *opts = data;
+
+   if (!success)
+     {
+       fputs("ERROR: could not connect to DBus server.\n", stderr);
+       ecore_main_loop_quit();
+       return;
+     }
+
+   fputs("connected to DBus server, setup parameters...\n", stdout);
+
+   ethumb_client_format_set(c, opts->format);
+   ethumb_client_aspect_set(c, opts->aspect);
+
+   if (opts->directory) ethumb_client_dir_path_set(c, opts->directory);
+   if (opts->category) ethumb_client_category_set(c, opts->category);
+   if (opts->geometry.w > 0 && opts->geometry.h > 0)
+     ethumb_client_size_set(c, opts->geometry.w, opts->geometry.h);
+   if (opts->frame.file)
+     ethumb_client_frame_set
+       (c, opts->frame.file, opts->frame.group, opts->frame.swallow);
+   if (opts->video_time > 0)
+     ethumb_client_video_time_set(c, opts->video_time);
+   if (opts->page > 0)
+     ethumb_client_document_page_set(c, opts->page);
+
+   if (!ethumb_client_file_set(c, opts->src_path, opts->src_key))
+     {
+       fprintf(stderr, "ERROR: could not set file '%s', key '%s'\n",
+               opts->src_path, opts->src_key ? opts->src_key : "");
+       ecore_main_loop_quit();
+       return;
+     }
+
+   ethumb_client_thumb_path_set(c, opts->thumb_path, opts->thumb_key);
+   ethumb_client_thumb_exists(c, _exists, opts);
+}
+
+int
+main(int argc, char *argv[])
+{
+   Ethumb_Client *c;
+   Eina_Bool quit_option = 0;
+   const char *format_str = NULL, *aspect_str;
+   struct options opts = {
+     {-1, -1, -1, -1},
+     0, 0,
+     NULL, NULL, NULL, NULL,
+     {NULL, NULL, NULL},
+     NULL, NULL, NULL, NULL,
+     0.0,
+     0
+   };
+   int arg_index;
+   int i, ret = 0;
+
+   ethumb_client_init();
+   ecore_init();
+
+   Ecore_Getopt_Value values[] = {
+     ECORE_GETOPT_VALUE_PTR_CAST(opts.geometry),
+     ECORE_GETOPT_VALUE_PTR_CAST(format_str),
+     ECORE_GETOPT_VALUE_PTR_CAST(aspect_str),
+     ECORE_GETOPT_VALUE_STR(opts.directory),
+     ECORE_GETOPT_VALUE_STR(opts.category),
+     ECORE_GETOPT_VALUE_PTR_CAST(opts.frame),
+     ECORE_GETOPT_VALUE_STR(opts.src_key),
+     ECORE_GETOPT_VALUE_DOUBLE(opts.video_time),
+     ECORE_GETOPT_VALUE_INT(opts.page),
+     ECORE_GETOPT_VALUE_BOOL(quit_option),
+     ECORE_GETOPT_VALUE_BOOL(quit_option),
+     ECORE_GETOPT_VALUE_BOOL(quit_option),
+     ECORE_GETOPT_VALUE_BOOL(quit_option),
+     ECORE_GETOPT_VALUE_NONE
+   };
+
+   arg_index = ecore_getopt_parse(&optdesc, values, argc, argv);
+   if ((arg_index < 0) || (arg_index == argc))
+     {
+       if (arg_index < 0)
+         fprintf(stderr, "Could not parse arguments.\n");
+       else
+         fprintf(stderr, "Missing source file to thumbnail.\n");
+
+       ret = 1;
+       goto end;
+     }
+
+   if (quit_option)
+     {
+       ret = 0;
+       goto end;
+     }
+
+   for (i = 0; i < 3; i++)
+     if (format_opt[i] == format_str)
+       {
+         opts.format = i;
+         break;
+       }
+
+   for (i = 0; i < 3; i++)
+     if (aspect_opt[i] == aspect_str)
+       {
+         opts.aspect = i;
+         break;
+       }
+
+   opts.src_path = argv[arg_index++];
+   if (arg_index < argc)
+     {
+       opts.thumb_path = argv[arg_index++];
+       if (arg_index < argc)
+         opts.thumb_key = argv[arg_index];
+     }
+
+   c = ethumb_client_connect(_connected, &opts, NULL);
+   if (!c)
+     {
+       fputs("ERROR: could not connect to server.\n", stderr);
+       ret = 2;
+       goto end;
+     }
+
+   ecore_main_loop_begin();
+   ethumb_client_disconnect(c);
+
+ end:
+   if (opts.frame.file)
+     {
+       eina_stringshare_del(opts.frame.file);
+       eina_stringshare_del(opts.frame.group);
+       eina_stringshare_del(opts.frame.swallow);
+     }
+   ecore_shutdown();
+   ethumb_client_shutdown();
+
+   return ret;
+}
diff --git a/src/bin/ethumbd_private.h b/src/bin/ethumbd_private.h
new file mode 100644 (file)
index 0000000..688eadd
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef __ETHUMBD_PRIVATE_H__
+#define __ETHUMBD_PRIVATE_H__ 1
+
+
+enum Ethumbd_Operations
+{
+   ETHUMBD_OP_NEW,
+   ETHUMBD_OP_GENERATE,
+   ETHUMBD_OP_SETUP,
+   ETHUMBD_OP_DEL
+};
+
+enum Ethubmd_Setup_Option
+{
+   ETHUMBD_FDO,
+   ETHUMBD_SIZE_W,
+   ETHUMBD_SIZE_H,
+   ETHUMBD_FORMAT,
+   ETHUMBD_ASPECT,
+   ETHUMBD_ORIENTATION,
+   ETHUMBD_CROP_X,
+   ETHUMBD_CROP_Y,
+   ETHUMBD_QUALITY,
+   ETHUMBD_COMPRESS,
+   ETHUMBD_DIRECTORY,
+   ETHUMBD_CATEGORY,
+   ETHUMBD_FRAME_FILE,
+   ETHUMBD_FRAME_GROUP,
+   ETHUMBD_FRAME_SWALLOW,
+   ETHUMBD_VIDEO_TIME,
+   ETHUMBD_VIDEO_START,
+   ETHUMBD_VIDEO_INTERVAL,
+   ETHUMBD_VIDEO_NTIMES,
+   ETHUMBD_VIDEO_FPS,
+   ETHUMBD_DOCUMENT_PAGE,
+   ETHUMBD_SETUP_FINISHED
+};
+
+void ethumbd_child_start(int pipein, int pipeout);
+
+#endif
diff --git a/src/lib/Ethumb.h b/src/lib/Ethumb.h
new file mode 100644 (file)
index 0000000..0835433
--- /dev/null
@@ -0,0 +1,214 @@
+#ifndef __ETHUMB_H__
+#define __ETHUMB_H__ 1
+
+#include <Eina.h>
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_ETHUMB_BUILD
+#  ifdef DLL_EXPORT
+#   define EAPI __declspec(dllexport)
+#  else
+#   define EAPI
+#  endif /* ! DLL_EXPORT */
+# else
+#  define EAPI __declspec(dllimport)
+# endif /* ! EFL_ETHUMB_BUILD */
+#else
+# ifdef __GNUC__
+#  if __GNUC__ >= 4
+#   define EAPI __attribute__ ((visibility("default")))
+#  else
+#   define EAPI
+#  endif
+# else
+#  define EAPI
+# endif
+#endif /* ! _WIN32 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETHUMB_VERSION_MAJOR 1
+#define ETHUMB_VERSION_MINOR 7
+   
+   typedef struct _Ethumb_Version
+     {
+        int major;
+        int minor;
+        int micro;
+        int revision;
+     } Ethumb_Version;
+   
+   EAPI extern Ethumb_Version *ethumb_version;
+   
+/**
+ * @defgroup Ethumb Ethumb
+ *
+ * @{
+ */
+/**
+ * @defgroup Ethumb_Basics Ethumb Basics
+ *
+ * Functions that all users must know of to use Ethumb.
+ *
+ * @{
+ */
+
+/**
+ * @brief thumbnailer handle.
+ *
+ * The handle is returned by ethumb_new() and destroyed by ethumb_free().
+ */
+typedef struct _Ethumb Ethumb;
+
+/**
+ * @brief reports results of ethumb_generate().
+ *
+ * @param data extra context given to ethumb_generate().
+ * @param e handle of the current thumbnailer.
+ * @param success @c EINA_TRUE if generated or @c EINA_FALSE on errors.
+ */
+typedef void (*Ethumb_Generate_Cb)(void *data, Ethumb *e, Eina_Bool success);
+
+EAPI int ethumb_init(void);
+EAPI int ethumb_shutdown(void);
+
+EAPI Ethumb * ethumb_new(void) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+EAPI void ethumb_free(Ethumb *e);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ethumb_Setup Ethumb Fine Tune Setup
+ *
+ * How to fine tune thumbnail generation, setting size, aspect,
+ * frames, quality and so on.
+ *
+ * @{
+ */
+
+EAPI Eina_Bool    ethumb_frame_set(Ethumb *e, const char *theme_file, const char *group, const char *swallow) EINA_ARG_NONNULL(1);
+EAPI void         ethumb_frame_get(const Ethumb *e, const char **theme_file, const char **group, const char **swallow) EINA_ARG_NONNULL(1);
+
+EAPI void         ethumb_thumb_dir_path_set(Ethumb *e, const char *path) EINA_ARG_NONNULL(1);
+EAPI const char  *ethumb_thumb_dir_path_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+
+EAPI void         ethumb_thumb_category_set(Ethumb *e, const char *category) EINA_ARG_NONNULL(1);
+EAPI const char  *ethumb_thumb_category_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+
+EAPI void         ethumb_thumb_path_set(Ethumb *e, const char *path, const char *key) EINA_ARG_NONNULL(1);
+EAPI void         ethumb_thumb_path_get(Ethumb *e, const char **path, const char **key) EINA_ARG_NONNULL(1);
+EAPI void         ethumb_thumb_hash(Ethumb *e) EINA_ARG_NONNULL(1);
+EAPI void         ethumb_thumb_hash_copy(Ethumb *dst, const Ethumb *src) EINA_ARG_NONNULL(1, 2);
+
+typedef enum _Ethumb_Thumb_FDO_Size
+{
+  ETHUMB_THUMB_NORMAL, /**< 128x128 as defined by FreeDesktop.Org standard */
+  ETHUMB_THUMB_LARGE   /**< 256x256 as defined by FreeDesktop.Org standard */
+} Ethumb_Thumb_FDO_Size;
+
+typedef enum _Ethumb_Thumb_Format
+{
+   ETHUMB_THUMB_FDO,   /**< PNG as defined by FreeDesktop.Org standard */
+   ETHUMB_THUMB_JPEG,  /**< JPEGs are often smaller and faster to read/write */
+   ETHUMB_THUMB_EET    /**< EFL's own storage system, supports key parameter */
+} Ethumb_Thumb_Format;
+
+typedef enum _Ethumb_Thumb_Aspect
+{
+  ETHUMB_THUMB_KEEP_ASPECT, /**< keep original proportion between width and height */
+  ETHUMB_THUMB_IGNORE_ASPECT, /**< ignore aspect and foce it to match thumbnail's width and height */
+  ETHUMB_THUMB_CROP /**< keep aspect but crop (cut) the largest dimension */
+} Ethumb_Thumb_Aspect;
+
+typedef enum _Ethumb_Thumb_Orientation
+{
+  ETHUMB_THUMB_ORIENT_NONE,     /**< keep orientation as pixel data is */
+  ETHUMB_THUMB_ROTATE_90_CW,    /**< rotate 90° clockwise */
+  ETHUMB_THUMB_ROTATE_180,      /**< rotate 180° */
+  ETHUMB_THUMB_ROTATE_90_CCW,   /**< rotate 90° counter-clockwise */
+  ETHUMB_THUMB_FLIP_HORIZONTAL, /**< flip horizontally */
+  ETHUMB_THUMB_FLIP_VERTICAL,   /**< flip vertically */
+  ETHUMB_THUMB_FLIP_TRANSPOSE,  /**< transpose */
+  ETHUMB_THUMB_FLIP_TRANSVERSE, /**< transverse */
+  ETHUMB_THUMB_ORIENT_ORIGINAL  /**< use orientation from metadata (EXIF-only currently) */
+} Ethumb_Thumb_Orientation;
+
+EAPI void ethumb_thumb_fdo_set(Ethumb *e, Ethumb_Thumb_FDO_Size s) EINA_ARG_NONNULL(1);
+
+EAPI void ethumb_thumb_size_set(Ethumb *e, int tw, int th) EINA_ARG_NONNULL(1);
+EAPI void ethumb_thumb_size_get(const Ethumb *e, int *tw, int *th) EINA_ARG_NONNULL(1);
+
+EAPI void                     ethumb_thumb_format_set(Ethumb *e, Ethumb_Thumb_Format f) EINA_ARG_NONNULL(1);
+EAPI Ethumb_Thumb_Format      ethumb_thumb_format_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+
+EAPI void                     ethumb_thumb_aspect_set(Ethumb *e, Ethumb_Thumb_Aspect a) EINA_ARG_NONNULL(1);
+EAPI Ethumb_Thumb_Aspect      ethumb_thumb_aspect_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+
+EAPI void                     ethumb_thumb_orientation_set(Ethumb *e, Ethumb_Thumb_Orientation o) EINA_ARG_NONNULL(1);
+EAPI Ethumb_Thumb_Orientation ethumb_thumb_orientation_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+
+EAPI void         ethumb_thumb_crop_align_set(Ethumb *e, float x, float y) EINA_ARG_NONNULL(1);
+EAPI void         ethumb_thumb_crop_align_get(const Ethumb *e, float *x, float *y) EINA_ARG_NONNULL(1);
+
+EAPI void         ethumb_thumb_quality_set(Ethumb *e, int quality) EINA_ARG_NONNULL(1);
+EAPI int          ethumb_thumb_quality_get(const Ethumb *e) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_PURE;
+
+EAPI void         ethumb_thumb_compress_set(Ethumb *e, int compress) EINA_ARG_NONNULL(1);
+EAPI int          ethumb_thumb_compress_get(const Ethumb *e) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_PURE;
+
+EAPI void         ethumb_video_start_set(Ethumb *e, float start) EINA_ARG_NONNULL(1);
+EAPI float        ethumb_video_start_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+EAPI void         ethumb_video_time_set(Ethumb *e, float time) EINA_ARG_NONNULL(1);
+EAPI float        ethumb_video_time_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+EAPI void         ethumb_video_interval_set(Ethumb *e, float interval) EINA_ARG_NONNULL(1);
+EAPI float        ethumb_video_interval_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+EAPI void         ethumb_video_ntimes_set(Ethumb *e, unsigned int ntimes) EINA_ARG_NONNULL(1);
+EAPI unsigned int ethumb_video_ntimes_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+EAPI void         ethumb_video_fps_set(Ethumb *e, unsigned int fps) EINA_ARG_NONNULL(1);
+EAPI unsigned int ethumb_video_fps_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+
+
+EAPI void         ethumb_document_page_set(Ethumb *e, unsigned int page) EINA_ARG_NONNULL(1);
+EAPI unsigned int ethumb_document_page_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+/**
+ * @}
+ */
+
+/**
+ * @addtogroup Ethumb_Basics Ethumb Basics
+ * @{
+ */
+EAPI Eina_Bool ethumb_file_set(Ethumb *e, const char *path, const char *key) EINA_ARG_NONNULL(1, 2);
+EAPI void      ethumb_file_get(const Ethumb *e, const char **path, const char **key) EINA_ARG_NONNULL(1);
+EAPI void      ethumb_file_free(Ethumb *e) EINA_ARG_NONNULL(1);
+
+EAPI Eina_Bool ethumb_generate(Ethumb *e, Ethumb_Generate_Cb finished_cb, const void *data, Eina_Free_Cb free_data) EINA_ARG_NONNULL(1, 2);
+EAPI Eina_Bool ethumb_exists(Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+
+EAPI Ethumb *ethumb_dup(const Ethumb *e) EINA_ARG_NONNULL(1);
+EAPI Eina_Bool ethumb_cmp(const Ethumb *e1, const Ethumb *e2) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT EINA_PURE;
+EAPI int ethumb_hash(const void *key, int key_length) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_PURE;
+EAPI int ethumb_key_cmp(const void *key1, int key1_length,
+                       const void *key2, int key2_length) EINA_ARG_NONNULL(1, 3) EINA_WARN_UNUSED_RESULT EINA_PURE;
+EAPI unsigned int ethumb_length(const void *key) EINA_PURE EINA_WARN_UNUSED_RESULT;
+
+  /**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __ETHUMB_H__ */
diff --git a/src/lib/Ethumb_Plugin.h b/src/lib/Ethumb_Plugin.h
new file mode 100644 (file)
index 0000000..3eb6f02
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef _ETHUMB_PLUGIN_H_
+#define _ETHUMB_PLUGIN_H_
+
+#include <Evas.h>
+#include <Ecore_Evas.h>
+#include <Ethumb.h>
+
+typedef struct _Ethumb_Plugin Ethumb_Plugin;
+
+struct _Ethumb_Plugin
+{
+   const char **extensions;
+   void *(*thumb_generate)(Ethumb *);
+   void (*thumb_cancel)(Ethumb *, void *);
+};
+
+EAPI void ethumb_calculate_aspect_from_ratio(Ethumb *e, float ia, int *w, int *h);
+EAPI void ethumb_calculate_aspect(Ethumb *e, int iw, int ih, int *w, int *h);
+EAPI void ethumb_calculate_fill_from_ratio(Ethumb *e, float ia, int *fx, int *fy, int *fw, int *fh);
+EAPI void ethumb_calculate_fill(Ethumb *e, int iw, int ih, int *fx, int *fy, int *fw, int *fh);
+EAPI Eina_Bool ethumb_plugin_image_resize(Ethumb *e, int w, int h);
+EAPI Eina_Bool ethumb_image_save(Ethumb *e);
+EAPI void ethumb_finished_callback_call(Ethumb *e, int result);
+EAPI Evas * ethumb_evas_get(const Ethumb *e);
+EAPI Ecore_Evas * ethumb_ecore_evas_get(const Ethumb *e);
+
+#endif /* _ETHUMB_PLUGIN_H_ */
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
new file mode 100644 (file)
index 0000000..a9a99fa
--- /dev/null
@@ -0,0 +1,46 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = .
+
+if USE_MODULE_ETHUMBD
+SUBDIRS += client
+endif
+
+AM_CPPFLAGS = \
+-I$(top_srcdir) \
+-I$(top_builddir) \
+-DPLUGINSDIR=\"$(libdir)/ethumb/plugins\" \
+@EFL_ETHUMB_BUILD@ \
+@EINA_CFLAGS@ \
+@EVAS_CFLAGS@ \
+@ECORE_CFLAGS@ \
+@ECORE_EVAS_CFLAGS@ \
+@ECORE_FILE_CFLAGS@ \
+@EDJE_CFLAGS@
+
+if HAVE_LIBEXIF
+AM_CPPFLAGS += @LIBEXIF_CFLAGS@
+endif
+
+includes_HEADERS = Ethumb.h Ethumb_Plugin.h
+includesdir = $(includedir)/ethumb-@VMAJ@
+
+noinst_HEADERS = md5.h ethumb_private.h
+
+lib_LTLIBRARIES = libethumb.la
+
+
+libethumb_la_SOURCES = ethumb.c md5.c
+libethumb_la_LIBADD = \
+@EDJE_LIBS@ \
+@ECORE_FILE_LIBS@ \
+@ECORE_EVAS_LIBS@ \
+@ECORE_LIBS@ \
+@EVAS_LIBS@ \
+@EINA_LIBS@
+
+if HAVE_LIBEXIF
+libethumb_la_LIBADD += @LIBEXIF_LIBS@
+endif
+
+libethumb_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -version-info @version_info@ @release_info@
diff --git a/src/lib/client/Ethumb_Client.h b/src/lib/client/Ethumb_Client.h
new file mode 100644 (file)
index 0000000..81105aa
--- /dev/null
@@ -0,0 +1,219 @@
+#ifndef __ETHUMB_CLIENT_H__
+#define __ETHUMB_CLIENT_H__ 1
+
+#include <Ethumb.h>
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_ETHUMB_CLIENT_BUILD
+#  ifdef DLL_EXPORT
+#   define EAPI __declspec(dllexport)
+#  else
+#   define EAPI
+#  endif /* ! DLL_EXPORT */
+# else
+#  define EAPI __declspec(dllimport)
+# endif /* ! EFL_ETHUMB_CLIENT_BUILD */
+#else
+# ifdef __GNUC__
+#  if __GNUC__ >= 4
+#   define EAPI __attribute__ ((visibility("default")))
+#  else
+#   define EAPI
+#  endif
+# else
+#  define EAPI
+# endif
+#endif /* ! _WIN32 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup Ethumb_Client Ethumb Client
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Ethumb_Client_Basics Ethumb Client Basics
+ *
+ * Functions that all users must know of to use Ethumb_Client.
+ *
+ * @{
+ */
+
+/**
+ * @brief client handle.
+ *
+ * The client handle is created by ethumb_client_connect() and
+ * destroyed by ethumb_client_disconnect(). The connection and
+ * requests are asynchronous and callbacks should be used to report
+ * both success and failure of calls.
+ */
+typedef struct _Ethumb_Client Ethumb_Client;
+
+/**
+ * @brief client exists request handle.
+ *
+ * The exists request handle is created by ethumb_client_thumb_exists(),
+ * automatically destroyed when it end and cancelled when requested by
+ * ethumb_client_thumb_exists_cancel().
+ */
+typedef struct _Ethumb_Exists Ethumb_Exists;
+
+/**
+ * @brief reports results of ethumb_client_connect()
+ *
+ * @param data extra context given to ethumb_client_connect().
+ * @param client handle of the current connection to server.
+ * @param success @c EINA_TRUE if connected or @c EINA_FALSE if it was
+ *        not possible.
+ */
+typedef void (*Ethumb_Client_Connect_Cb)(void *data, Ethumb_Client *client, Eina_Bool success);
+
+/**
+ * @brief reports server connection ended.
+ *
+ * Functions of this type may be called if they are set with
+ * ethumb_client_on_server_die_callback_set().
+ *
+ * @param data extra context given to ethumb_client_on_server_die_callback_set().
+ * @param client handle of the current connection to server.
+ */
+typedef void (*Ethumb_Client_Die_Cb)(void *data, Ethumb_Client *client);
+
+/**
+ * @brief reports results of ethumb_client_generate().
+ *
+ * @param data extra context given to ethumb_client_generate().
+ * @param client handle of the current connection to server.
+ * @param id identifier returned by ethumb_client_generate().
+ * @param file path set with ethumb_client_file_set().
+ * @param key value set with ethumb_client_file_set() or @c NULL.
+ * @param thumb_path where thumbnail was stored, either set with
+ *        ethumb_client_thumb_path_set() or automatically calculated
+ *        using parameters.
+ * @param thumb_key key inside thumb_path where thumbnail was stored or @c NULL.
+ * @param success @c EINA_TRUE if generated or @c EINA_FALSE on errors.
+ */
+typedef void (*Ethumb_Client_Generate_Cb)(void *data, Ethumb_Client *client, int id, const char *file, const char *key, const char *thumb_path, const char *thumb_key, Eina_Bool success);
+
+/**
+ * @brief report results of ethumb_client_thumb_exists().
+ *
+ * @param client handle of the current connection to server.
+ * @param exists EINA_TRUE if the thumbnail exists.
+ * @param data extra context given to ethumb_client_thumb_exists().
+ *
+ * During the execution of the callback the state of the @p client is
+ * temporarily realy restored to what it was when the call to
+ * ethumb_client_thumb_exists() was done.
+ */
+typedef void (*Ethumb_Client_Thumb_Exists_Cb)(void *data, Ethumb_Client *client, Ethumb_Exists *thread, Eina_Bool exists);
+
+/**
+ * @brief reports results of ethumb_client_generate_cancel()
+ *
+ * @param data extra context given to ethumb_client_generate_cancel()
+ * @param client handle of the current connection to server.
+ */
+typedef void (*Ethumb_Client_Generate_Cancel_Cb)(void *data, Eina_Bool success);
+
+EAPI int ethumb_client_init(void);
+EAPI int ethumb_client_shutdown(void);
+
+EAPI Ethumb_Client * ethumb_client_connect(Ethumb_Client_Connect_Cb connect_cb, const void *data, Eina_Free_Cb free_data);
+EAPI void ethumb_client_disconnect(Ethumb_Client *client);
+EAPI void 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);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ethumb_Client_Setup Ethumb Client Fine Tune Setup
+ *
+ * How to fine tune thumbnail generation, setting size, aspect, orientation,
+ * frames, quality and so on.
+ *
+ * @{
+ */
+
+EAPI void ethumb_client_fdo_set(Ethumb_Client *client, Ethumb_Thumb_FDO_Size s);
+EAPI void ethumb_client_size_set(Ethumb_Client *client, int tw, int th);
+EAPI void ethumb_client_size_get(const Ethumb_Client *client, int *tw, int *th);
+EAPI void ethumb_client_format_set(Ethumb_Client *client, Ethumb_Thumb_Format f);
+EAPI Ethumb_Thumb_Format ethumb_client_format_get(const Ethumb_Client *client);
+EAPI void ethumb_client_aspect_set(Ethumb_Client *client, Ethumb_Thumb_Aspect a);
+EAPI Ethumb_Thumb_Aspect ethumb_client_aspect_get(const Ethumb_Client *client);
+EAPI void ethumb_client_orientation_set(Ethumb_Client *client, Ethumb_Thumb_Orientation o);
+EAPI Ethumb_Thumb_Orientation ethumb_client_orientation_get(const Ethumb_Client *client);
+EAPI void ethumb_client_crop_align_set(Ethumb_Client *client, float x, float y);
+EAPI void ethumb_client_crop_align_get(const Ethumb_Client *client, float *x, float *y);
+EAPI void ethumb_client_quality_set(Ethumb_Client *client, int quality);
+EAPI int ethumb_client_quality_get(const Ethumb_Client *client);
+EAPI void ethumb_client_compress_set(Ethumb_Client *client, int compress);
+EAPI int ethumb_client_compress_get(const Ethumb_Client *client);
+EAPI Eina_Bool ethumb_client_frame_set(Ethumb_Client *client, const char *file, const char *group, const char *swallow);
+EAPI void ethumb_client_dir_path_set(Ethumb_Client *client, const char *path);
+EAPI const char * ethumb_client_dir_path_get(const Ethumb_Client *client);
+EAPI void ethumb_client_category_set(Ethumb_Client *client, const char *category);
+EAPI const char * ethumb_client_category_get(const Ethumb_Client *client);
+EAPI void ethumb_client_video_time_set(Ethumb_Client *client, float time);
+EAPI void ethumb_client_video_start_set(Ethumb_Client *client, float start);
+EAPI void ethumb_client_video_interval_set(Ethumb_Client *client, float interval);
+EAPI void ethumb_client_video_ntimes_set(Ethumb_Client *client, unsigned int ntimes);
+EAPI void ethumb_client_video_fps_set(Ethumb_Client *client, unsigned int fps);
+EAPI void ethumb_client_document_page_set(Ethumb_Client *client, unsigned int page);
+
+EAPI void ethumb_client_ethumb_setup(Ethumb_Client *client);
+
+EAPI void ethumb_client_thumb_path_set(Ethumb_Client *client, const char *path, const char *key);
+EAPI void ethumb_client_thumb_path_get(Ethumb_Client *client, const char **path, const char **key);
+/**
+ * @}
+ */
+
+/**
+ * @addtogroup Ethumb_Client_Basics Ethumb Client Basics
+ * @{
+ */
+EAPI Eina_Bool ethumb_client_file_set(Ethumb_Client *client, const char *path, const char *key);
+EAPI void ethumb_client_file_get(Ethumb_Client *client, const char **path, const char **key);
+EAPI void ethumb_client_file_free(Ethumb_Client *client);
+
+EAPI Ethumb_Exists *ethumb_client_thumb_exists(Ethumb_Client *client, Ethumb_Client_Thumb_Exists_Cb exists_cb, const void *data);
+EAPI void ethumb_client_thumb_exists_cancel(Ethumb_Exists *exists);
+EAPI Eina_Bool ethumb_client_thumb_exists_check(Ethumb_Exists *exists);
+EAPI int  ethumb_client_generate(Ethumb_Client *client, Ethumb_Client_Generate_Cb generated_cb, const void *data, Eina_Free_Cb free_data);
+EAPI void ethumb_client_generate_cancel(Ethumb_Client *client, int id, Ethumb_Client_Generate_Cancel_Cb cancel_cb, const void *data, Eina_Free_Cb free_data);
+EAPI void ethumb_client_generate_cancel_all(Ethumb_Client *client);
+
+typedef void (*Ethumb_Client_Async_Done_Cb)(Ethumb_Client *ethumbd, const char *thumb_path, const char *thumb_key, void *data);
+typedef void (*Ethumb_Client_Async_Error_Cb)(Ethumb_Client *ethumbd, void *data);
+
+typedef struct _Ethumb_Client_Async Ethumb_Client_Async;
+
+EAPI Ethumb_Client_Async *ethumb_client_thumb_async_get(Ethumb_Client *client,
+                                                       Ethumb_Client_Async_Done_Cb done,
+                                                       Ethumb_Client_Async_Error_Cb error,
+                                                       const void *data);
+EAPI void ethumb_client_thumb_async_cancel(Ethumb_Client *client, Ethumb_Client_Async *request);
+  /**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __ETHUMB_CLIENT_H__ */
diff --git a/src/lib/client/Makefile.am b/src/lib/client/Makefile.am
new file mode 100644 (file)
index 0000000..e7faae2
--- /dev/null
@@ -0,0 +1,23 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = \
+-I$(top_srcdir) \
+-I$(top_builddir) \
+-I$(top_srcdir)/src/lib \
+@EFL_ETHUMB_CLIENT_BUILD@ \
+@ECORE_CFLAGS@ \
+@EDBUS_CFLAGS@ \
+@EINA_CFLAGS@
+
+includes_HEADERS = Ethumb_Client.h
+includesdir = $(includedir)/ethumb-@VMAJ@
+
+lib_LTLIBRARIES = libethumb_client.la
+
+libethumb_client_la_SOURCES = ethumb_client.c
+libethumb_client_la_LIBADD = \
+$(top_builddir)/src/lib/libethumb.la \
+@ECORE_LIBS@ \
+@EDBUS_LIBS@ \
+@EINA_LIBS@
+libethumb_client_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -version-info @version_info@
diff --git a/src/lib/client/ethumb_client.c b/src/lib/client/ethumb_client.c
new file mode 100644 (file)
index 0000000..ee17507
--- /dev/null
@@ -0,0 +1,2576 @@
+/**
+ * @file
+ *
+ * This is the client-server thumbnail library, see @ref
+ * tutorial_ethumb_client.
+ *
+ * Copyright (C) 2009 by ProFUSION embedded systems
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Rafael Antognolli <antognolli@profusion.mobi>
+ * @author Gustavo Sverzut Barbieri <barbieri@profusion.mobi>
+ */
+
+/**
+ * @page tutorial_ethumb_client Client-Server Thumbnailing Tutorial
+ *
+ * @section tutorial_ethumb_client_intro Introduction
+ *
+ * Ethumb provides both in process and client-server generation
+ * methods. The advantage of the client-server method is that current
+ * process will not do the heavy operations that may block, stopping
+ * animations and other user interactions. Instead the client library
+ * will configure a local #Ethumb instance and mirrors/controls a
+ * remote process using DBus. The simple operations like most setters
+ * and getters as well as checking for thumbnail existence
+ * (ethumb_client_thumb_exists()) is done locally, while expensive
+ * (ethumb_client_generate()) are done on server and then reported
+ * back to application when it is finished (both success or failure).
+ *
+ * @section tutorial_ethumb_client_connect Connecting to Server
+ *
+ * TODO
+ *
+ * @section tutorial_ethumb_client_generate Requesting Thumbnail Generation
+ *
+ * TODO
+ *
+ * @section tutorial_ethumb_client_setup Setup Extra Thumbnail Parameters
+ *
+ * TODO
+ *
+ * @section tutorial_ethumb_client_server_died Handle Server Disconnection
+ *
+ * TODO
+ */
+
+/**
+ * @cond LOCAL
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdbool.h>
+
+#include <Eina.h>
+#include <eina_safety_checks.h>
+#include <E_DBus.h>
+#include <Ethumb.h>
+#include <Ecore.h>
+
+#include "Ethumb_Client.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#define MAX_ID   2000000
+
+static int _log_dom = -1;
+#define DBG(...)      EINA_LOG_DOM_DBG(_log_dom, __VA_ARGS__)
+#define INF(...)      EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__)
+#define WRN(...)      EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
+#define ERR(...)      EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__)
+#define CRITICAL(...) EINA_LOG_DOM_CRIT(_log_dom, __VA_ARGS__)
+
+struct _Ethumb_Client
+{
+   Ethumb                *ethumb;
+   int                    id_count;
+
+   Ethumb                *old_ethumb_conf;
+   E_DBus_Connection     *conn;
+   E_DBus_Signal_Handler *name_owner_changed_handler;
+   E_DBus_Signal_Handler *generated_signal;
+   DBusPendingCall       *pending_get_name_owner;
+   DBusPendingCall       *pending_start_service_by_name;
+   const char            *unique_name;
+   DBusPendingCall       *pending_new;
+   struct
+   {
+      Ethumb_Client_Connect_Cb cb;
+      void                    *data;
+      Eina_Free_Cb             free_data;
+   } connect;
+   Eina_List             *pending_add;
+   Eina_List             *pending_remove;
+   Eina_List             *pending_gen;
+   DBusPendingCall       *pending_clear;
+   DBusPendingCall       *pending_setup;
+   struct
+   {
+      Ethumb_Client_Die_Cb cb;
+      void                *data;
+      Eina_Free_Cb         free_data;
+   } die;
+   const char            *object_path;
+
+   EINA_REFCOUNT;
+
+   Eina_Bool              connected : 1;
+   Eina_Bool              server_started : 1;
+};
+
+struct _ethumb_pending_add
+{
+   dbus_int32_t              id;
+   const char               *file;
+   const char               *key;
+   const char               *thumb;
+   const char               *thumb_key;
+   Ethumb_Client_Generate_Cb generated_cb;
+   void                     *data;
+   Eina_Free_Cb              free_data;
+   DBusPendingCall          *pending_call;
+   Ethumb_Client            *client;
+};
+
+struct _ethumb_pending_remove
+{
+   dbus_int32_t                     id;
+   Ethumb_Client_Generate_Cancel_Cb cancel_cb;
+   void                            *data;
+   Eina_Free_Cb                     free_data;
+   DBusPendingCall                 *pending_call;
+   Ethumb_Client                   *client;
+};
+
+struct _ethumb_pending_gen
+{
+   dbus_int32_t              id;
+   const char               *file;
+   const char               *key;
+   const char               *thumb;
+   const char               *thumb_key;
+   Ethumb_Client_Generate_Cb generated_cb;
+   void                     *data;
+   Eina_Free_Cb              free_data;
+};
+
+typedef struct _Ethumb_Async_Exists Ethumb_Async_Exists;
+
+struct _Ethumb_Async_Exists
+{
+   const char   *path;
+
+   Ethumb       *dup; /* We will work on that one to prevent race and lock */
+
+   Eina_List    *callbacks;
+   Ecore_Thread *thread;
+};
+
+struct _Ethumb_Exists
+{
+   Ethumb_Async_Exists          *parent;
+   Ethumb_Client                *client;
+   Ethumb                       *dup; /* We don't want to loose parameters so keep them around */
+
+   Ethumb_Client_Thumb_Exists_Cb exists_cb;
+   const void                   *data;
+};
+
+static const char _ethumb_dbus_bus_name[] = "org.enlightenment.Ethumb";
+static const char _ethumb_dbus_interface[] = "org.enlightenment.Ethumb";
+static const char _ethumb_dbus_objects_interface[] = "org.enlightenment.Ethumb.objects";
+static const char _ethumb_dbus_path[] = "/org/enlightenment/Ethumb";
+static const char fdo_interface[] = "org.freedesktop.DBus";
+static const char fdo_bus_name[] = "org.freedesktop.DBus";
+static const char fdo_path[] = "/org/freedesktop/DBus";
+
+static int _initcount = 0;
+static Eina_Hash *_exists_request = NULL;
+
+static void _ethumb_client_generated_cb(void *data, DBusMessage *msg);
+static void _ethumb_client_get_name_owner(void *data, DBusMessage *msg, DBusError *err);
+
+static inline bool
+__dbus_callback_check_and_init(const char *file, int line, const char *function, DBusMessage *msg, DBusMessageIter *itr, DBusError *err)
+{
+   if (!msg)
+     {
+        ERR("%s:%d:%s() callback without message arguments!",
+            file, line, function);
+
+        if (err)
+          ERR("%s:%d:%s() an error was reported by server: "
+              "name=\"%s\", message=\"%s\"",
+              file, line, function, err->name, err->message);
+
+        return 0;
+     }
+
+   if (!dbus_message_iter_init(msg, itr))
+     {
+        ERR("%s:%d:%s() could not init iterator.",
+            file, line, function);
+        return 0;
+     }
+
+   return 1;
+}
+
+#define _dbus_callback_check_and_init(msg, itr, err)               \
+  __dbus_callback_check_and_init(__FILE__, __LINE__, __FUNCTION__, \
+                                 msg, itr, err)
+
+static inline bool
+__dbus_iter_type_check(int type, int expected, const char *expected_name)
+{
+   if (type == expected)
+     return 1;
+
+   ERR("expected type %s (%c) but got %c instead!",
+       expected_name, expected, type);
+
+   return 0;
+}
+
+#define _dbus_iter_type_check(t, e) __dbus_iter_type_check(t, e, #e)
+
+#define CHECK_NULL_RETURN(ptr, ...)        \
+  do                                       \
+    {                                      \
+       if ((ptr) == NULL)                  \
+         {                                 \
+            CRITICAL("%s == NULL!", #ptr); \
+            return __VA_ARGS__;            \
+         }                                 \
+    }                                      \
+  while (0)
+
+static void
+_ethumb_client_free(Ethumb_Client *client)
+{
+   void *data;
+
+   if (!client->connected)
+     goto end_connection;
+
+   EINA_LIST_FREE(client->pending_add, data)
+     {
+        struct _ethumb_pending_add *pending = data;
+        eina_stringshare_del(pending->file);
+        eina_stringshare_del(pending->key);
+        eina_stringshare_del(pending->thumb);
+        eina_stringshare_del(pending->thumb_key);
+        dbus_pending_call_cancel(pending->pending_call);
+        dbus_pending_call_unref(pending->pending_call);
+        if (pending->free_data)
+          pending->free_data(pending->data);
+        free(pending);
+     }
+
+   EINA_LIST_FREE(client->pending_gen, data)
+     {
+        struct _ethumb_pending_gen *pending = data;
+        eina_stringshare_del(pending->file);
+        eina_stringshare_del(pending->key);
+        eina_stringshare_del(pending->thumb);
+        eina_stringshare_del(pending->thumb_key);
+        if (pending->free_data)
+          pending->free_data(pending->data);
+        free(pending);
+     }
+
+   EINA_LIST_FREE(client->pending_remove, data)
+     {
+        struct _ethumb_pending_remove *pending = data;
+        dbus_pending_call_cancel(pending->pending_call);
+        dbus_pending_call_unref(pending->pending_call);
+        if (pending->free_data)
+          pending->free_data(pending->data);
+        free(pending);
+     }
+
+   if (client->pending_clear)
+     {
+        dbus_pending_call_cancel(client->pending_clear);
+        dbus_pending_call_unref(client->pending_clear);
+     }
+
+end_connection:
+   if (client->object_path)
+     eina_stringshare_del(client->object_path);
+
+   if (client->pending_new)
+     dbus_pending_call_cancel(client->pending_new);
+
+   if (client->unique_name)
+     eina_stringshare_del(client->unique_name);
+
+   if (client->pending_get_name_owner)
+     dbus_pending_call_cancel(client->pending_get_name_owner);
+
+   if (client->pending_start_service_by_name)
+     dbus_pending_call_cancel(client->pending_start_service_by_name);
+
+   if (client->old_ethumb_conf)
+     ethumb_free(client->old_ethumb_conf);
+
+   ethumb_free(client->ethumb);
+
+   e_dbus_signal_handler_del(client->conn, client->name_owner_changed_handler);
+   if (client->connected)
+     e_dbus_signal_handler_del(client->conn, client->generated_signal);
+   e_dbus_connection_close(client->conn);
+
+   if (client->connect.free_data)
+     client->connect.free_data(client->connect.data);
+   if (client->die.free_data)
+     client->die.free_data(client->die.data);
+
+   free(client);
+}
+
+static void
+_ethumb_async_delete(void *data)
+{
+   Ethumb_Async_Exists *async = data;
+
+   assert(async->callbacks == NULL);
+   assert(async->thread == NULL);
+
+   ethumb_free(async->dup);
+   eina_stringshare_del(async->path);
+
+   free(async);
+}
+
+static void
+_ethumb_client_name_owner_changed(void *data, DBusMessage *msg)
+{
+   DBusError err;
+   const char *name, *from, *to;
+   Ethumb_Client *client = data;
+
+   dbus_error_init(&err);
+   if (!dbus_message_get_args(msg, &err,
+                              DBUS_TYPE_STRING, &name,
+                              DBUS_TYPE_STRING, &from,
+                              DBUS_TYPE_STRING, &to,
+                              DBUS_TYPE_INVALID))
+     {
+        ERR("could not get NameOwnerChanged arguments: %s: %s",
+            err.name, err.message);
+        dbus_error_free(&err);
+        return;
+     }
+
+   if (!from || !name)
+     return;
+   if (strcmp(name, _ethumb_dbus_bus_name) != 0)
+     return;
+
+   DBG("NameOwnerChanged from=[%s] to=[%s]", from, to);
+
+   if (from[0] != '\0' && to[0] == '\0')
+     {
+        DBG("exit ethumbd at %s", from);
+        if (client->unique_name && strcmp(client->unique_name, from) != 0)
+          WRN("%s was not the known name %s, ignored.",
+              from, client->unique_name);
+        else if (client->unique_name)
+          {
+             ERR("server exit!!!");
+             if (client->die.cb)
+               {
+                  client->die.cb(client->die.data, client);
+                  client->die.cb = NULL;
+               }
+             if (client->die.free_data)
+               {
+                  client->die.free_data(client->die.data);
+                  client->die.free_data = NULL;
+                  client->die.data = NULL;
+               }
+          }
+     }
+   else
+     DBG("unknown change from %s to %s", from, to);
+}
+
+static void
+_ethumb_client_report_connect(Ethumb_Client *client, Eina_Bool success)
+{
+   if (!client->connect.cb)
+     {
+        ERR("already called?!");
+        return;
+     }
+
+   client->connect.cb(client->connect.data, client, success);
+   if (client->connect.free_data)
+     {
+        client->connect.free_data(client->connect.data);
+        client->connect.free_data = NULL;
+     }
+   client->connect.cb = NULL;
+   client->connect.data = NULL;
+}
+
+static void
+_ethumb_client_new_cb(void *data, DBusMessage *msg, DBusError *error)
+{
+   DBusMessageIter iter;
+   const char *opath;
+   int t;
+   Ethumb_Client *client = data;
+
+   client->pending_new = NULL;
+
+   if (!_dbus_callback_check_and_init(msg, &iter, error))
+     goto end_error;
+   t = dbus_message_iter_get_arg_type(&iter);
+   if (!_dbus_iter_type_check(t, DBUS_TYPE_OBJECT_PATH))
+     goto end_error;
+
+   dbus_message_iter_get_basic(&iter, &opath);
+   if (opath[0] == '\0')
+     goto end_error;
+
+   client->object_path = eina_stringshare_add(opath);
+
+   client->generated_signal = e_dbus_signal_handler_add(
+       client->conn, _ethumb_dbus_bus_name, opath,
+       _ethumb_dbus_objects_interface, "generated",
+       _ethumb_client_generated_cb, client);
+
+   _ethumb_client_report_connect(client, 1);
+   return;
+
+end_error:
+   _ethumb_client_report_connect(client, 0);
+}
+
+static void
+_ethumb_client_call_new(Ethumb_Client *client)
+{
+   DBusMessage *msg;
+
+   msg = dbus_message_new_method_call(_ethumb_dbus_bus_name, _ethumb_dbus_path,
+                                      _ethumb_dbus_interface, "new");
+   client->pending_new = e_dbus_message_send(client->conn, msg,
+                                             _ethumb_client_new_cb, -1,
+                                             client);
+   dbus_message_unref(msg);
+}
+
+static void
+_ethumb_client_start_server_cb(void *data, DBusMessage *msg, DBusError *err)
+{
+   Ethumb_Client *client = data;
+   DBusMessageIter iter;
+   dbus_uint32_t ret;
+   int t;
+
+   client->pending_start_service_by_name = NULL;
+
+   if (!_dbus_callback_check_and_init(msg, &iter, err))
+     goto error;
+
+   t = dbus_message_iter_get_arg_type(&iter);
+   if (!_dbus_iter_type_check(t, DBUS_TYPE_UINT32))
+     goto error;
+
+   dbus_message_iter_get_basic(&iter, &ret);
+   if ((ret != 1) && (ret != 2))
+     {
+        ERR("Error starting Ethumbd DBus service by its name: retcode %u",
+            ret);
+        goto error;
+     }
+
+   client->server_started = 1;
+   DBG("Ethumbd DBus service started successfully (%d), now request its name",
+       ret);
+
+   if (client->pending_get_name_owner)
+     {
+        DBG("already requesting name owner, cancel and try again");
+        dbus_pending_call_cancel(client->pending_get_name_owner);
+     }
+
+   client->pending_get_name_owner = e_dbus_get_name_owner
+       (client->conn, _ethumb_dbus_bus_name, _ethumb_client_get_name_owner,
+       client);
+   if (!client->pending_get_name_owner)
+     {
+        ERR("could not create a get_name_owner request.");
+        goto error;
+     }
+
+   return;
+
+error:
+   ERR("failed to start Ethumbd DBus service by its name.");
+   _ethumb_client_report_connect(client, 0);
+}
+
+static void
+_ethumb_client_start_server(Ethumb_Client *client)
+{
+   if (client->pending_start_service_by_name)
+     {
+        DBG("already pending start service by name.");
+        return;
+     }
+
+   client->server_started = 0;
+   client->pending_start_service_by_name = e_dbus_start_service_by_name
+       (client->conn, _ethumb_dbus_bus_name, 0, _ethumb_client_start_server_cb,
+       client);
+   if (!client->pending_start_service_by_name)
+     {
+        ERR("could not start service by name!");
+        _ethumb_client_report_connect(client, 0);
+     }
+}
+
+static void
+_ethumb_client_get_name_owner(void *data, DBusMessage *msg, DBusError *err)
+{
+   DBusMessageIter iter;
+   const char *uid;
+   Ethumb_Client *client = data;
+   int t;
+
+   client->pending_get_name_owner = NULL;
+
+   if (dbus_error_is_set(err) && (!client->server_started))
+     {
+        DBG("could not find server (%s), try to start it...", err->message);
+        _ethumb_client_start_server(client);
+        return;
+     }
+
+   if (!_dbus_callback_check_and_init(msg, &iter, err))
+     goto error;
+
+   t = dbus_message_iter_get_arg_type(&iter);
+   if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING))
+     goto error;
+
+   dbus_message_iter_get_basic(&iter, &uid);
+   if (!uid)
+     {
+        ERR("no name owner!");
+        goto error;
+     }
+
+   DBG("unique name = %s", uid);
+   client->unique_name = eina_stringshare_add(uid);
+
+   _ethumb_client_call_new(client);
+   client->connected = 1;
+   return;
+
+error:
+   _ethumb_client_report_connect(client, 0);
+}
+
+static void
+_ethumb_client_exists_heavy(void *data, Ecore_Thread *thread __UNUSED__)
+{
+   Ethumb_Async_Exists *async = data;
+
+   ethumb_thumb_hash(async->dup);
+}
+
+static void
+_ethumb_client_exists_end(void *data, Ecore_Thread *thread __UNUSED__)
+{
+   Ethumb_Async_Exists *async = data;
+   Ethumb_Exists *cb;
+
+   EINA_LIST_FREE(async->callbacks, cb)
+     {
+        Ethumb *tmp;
+
+        ethumb_thumb_hash_copy(cb->dup, async->dup);
+        tmp = cb->client->ethumb;
+        cb->client->ethumb = cb->dup;
+
+        cb->exists_cb((void *)cb->data,
+                      cb->client, cb,
+                      ethumb_exists(cb->client->ethumb));
+
+        cb->client->ethumb = tmp;
+        EINA_REFCOUNT_UNREF(cb->client)
+        _ethumb_client_free(cb->client);
+        ethumb_free(cb->dup);
+        free(cb);
+     }
+
+   async->thread = NULL;
+
+   eina_hash_del(_exists_request, async->path, async);
+}
+
+/**
+ * @endcond
+ */
+
+/**
+ * @brief Initialize the Ethumb_Client library.
+ *
+ * @return 1 or greater on success, 0 on error.
+ *
+ * This function sets up all the Ethumb_Client module dependencies. It
+ * returns 0 on failure (that is, when one of the dependency fails to
+ * initialize), otherwise it returns the number of times it has
+ * already been called.
+ *
+ * When Ethumb_Client is not used anymore, call
+ * ethumb_client_shutdown() to shut down the Ethumb_Client library.
+ *
+ * @see ethumb_client_shutdown()
+ * @see ethumb_client_connect()
+ * @see @ref tutorial_ethumb_client
+ */
+EAPI int
+ethumb_client_init(void)
+{
+   if (_initcount)
+     return ++_initcount;
+
+   if (!eina_init())
+     {
+        fprintf(stderr, "ERROR: Could not initialize log module.\n");
+        return 0;
+     }
+   _log_dom = eina_log_domain_register("ethumb_client", EINA_COLOR_YELLOW);
+   if (_log_dom < 0)
+     {
+        EINA_LOG_ERR("Could not register log domain: ethumb_client");
+        eina_shutdown();
+        return 0;
+     }
+
+   ethumb_init();
+   e_dbus_init();
+
+   _exists_request = eina_hash_stringshared_new(_ethumb_async_delete);
+
+   return ++_initcount;
+}
+
+/**
+ * @brief Shut down the Ethumb_Client library.
+ *
+ * @return 0 when everything is shut down, 1 or greater if there are
+ *         other users of the Ethumb_Client library pending shutdown.
+ *
+ * This function shuts down the Ethumb_Client library. It returns 0
+ * when it has been called the same number of times than
+ * ethumb_client_init(). In that case it shut down all the
+ * Ethumb_Client modules dependencies.
+ *
+ * Once this function succeeds (that is, @c 0 is returned), you must
+ * not call any of the Eina function anymore. You must call
+ * ethumb_client_init() again to use the Ethumb_Client functions
+ * again.
+ */
+EAPI int
+ethumb_client_shutdown(void)
+{
+   _initcount--;
+   if (_initcount > 0)
+     return _initcount;
+
+   /* should find a non racy solution to closing all pending exists request */
+   eina_hash_free(_exists_request);
+   _exists_request = NULL;
+
+   e_dbus_shutdown();
+   ethumb_shutdown();
+   eina_log_domain_unregister(_log_dom);
+   _log_dom = -1;
+   eina_shutdown();
+   return _initcount;
+}
+
+/**
+ * Connects to Ethumb server and return the client instance.
+ *
+ * This is the "constructor" of Ethumb_Client, where everything
+ * starts.
+ *
+ * If server was down, it is tried to start it using DBus activation,
+ * then the connection is retried.
+ *
+ * This call is asynchronous and will not block, instead it will be in
+ * "not connected" state until @a connect_cb is called with either
+ * success or failure. On failure, then no methods should be
+ * called. On success you're now able to setup and then ask generation
+ * of thumbnails.
+ *
+ * Usually you should listen for server death/disconenction with
+ * ethumb_client_on_server_die_callback_set().
+ *
+ * @param connect_cb function to call to report connection success or
+ *        failure. Do not call any other ethumb_client method until
+ *        this function returns. The first received parameter is the
+ *        given argument @a data. Must @b not be @c NULL. This
+ *        function will not be called if user explicitly calls
+ *        ethumb_client_disconnect().
+ * @param data context to give back to @a connect_cb. May be @c NULL.
+ * @param free_data function used to release @a data resources, if
+ *        any. May be @c NULL. If this function exists, it will be
+ *        called immediately after @a connect_cb is called or if user
+ *        explicitly calls ethumb_client_disconnect() before such
+ *        (that is, don't rely on @a data after @a connect_cb was
+ *        called!)
+ *
+ * @return client instance or NULL if failed. If @a connect_cb is
+ *         missing it returns @c NULL. If it fail for other
+ *         conditions, @c NULL is also returned and @a connect_cb is
+ *         called with @c success=EINA_FALSE. The client instance is
+ *         not ready to be used until @a connect_cb is called.
+ */
+EAPI Ethumb_Client *
+ethumb_client_connect(Ethumb_Client_Connect_Cb connect_cb, const void *data, Eina_Free_Cb free_data)
+{
+   Ethumb_Client *eclient;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(connect_cb, NULL);
+
+   eclient = calloc(1, sizeof(*eclient));
+   if (!eclient)
+     {
+        ERR("could not allocate Ethumb_Client structure.");
+        goto err;
+     }
+
+   eclient->old_ethumb_conf = NULL;
+   eclient->connect.cb = connect_cb;
+   eclient->connect.data = (void *)data;
+   eclient->connect.free_data = free_data;
+
+   eclient->ethumb = ethumb_new();
+   if (!eclient->ethumb)
+     {
+        ERR("could not create ethumb handler.");
+        goto ethumb_new_err;
+     }
+
+   eclient->conn = e_dbus_bus_get(DBUS_BUS_SESSION);
+   if (!eclient->conn)
+     {
+        ERR("could not connect to session bus.");
+        goto connection_err;
+     }
+
+   eclient->name_owner_changed_handler = e_dbus_signal_handler_add(
+       eclient->conn, fdo_bus_name, fdo_path, fdo_interface,
+       "NameOwnerChanged", _ethumb_client_name_owner_changed, eclient);
+
+   eclient->pending_get_name_owner = e_dbus_get_name_owner(
+       eclient->conn, _ethumb_dbus_bus_name, _ethumb_client_get_name_owner,
+       eclient);
+   if (!eclient->pending_get_name_owner)
+     {
+        ERR("could not create a get_name_owner request.");
+        goto connection_err;
+     }
+
+   EINA_REFCOUNT_INIT(eclient);
+
+   return eclient;
+
+connection_err:
+   ethumb_free(eclient->ethumb);
+ethumb_new_err:
+   free(eclient);
+err:
+   connect_cb((void *)data, NULL, EINA_FALSE);
+   if (free_data)
+     free_data((void *)data);
+   return NULL;
+}
+
+/**
+ * Disconnect the client, releasing all client resources.
+ *
+ * This is the destructor of Ethumb_Client, after it's disconnected
+ * the client handle is now gone and should not be used.
+ *
+ * @param client client instance to be destroyed. Must @b not be @c
+ *        NULL.
+ */
+EAPI void
+ethumb_client_disconnect(Ethumb_Client *client)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   EINA_REFCOUNT_UNREF(client)
+   _ethumb_client_free(client);
+}
+
+/**
+ * Sets the callback to report server died.
+ *
+ * When server dies there is nothing you can do, just release
+ * resources with ethumb_client_disconnect() and probably try to
+ * connect again.
+ *
+ * Usually you should set this callback and handle this case, it does
+ * happen!
+ *
+ * @param client the client instance to monitor. Must @b not be @c
+ *        NULL.
+ * @param server_die_cb function to call back when server dies. The
+ *        first parameter will be the argument @a data. May be @c
+ *        NULL.
+ * @param data context to give back to @a server_die_cb. May be @c
+ *        NULL.
+ * @param free_data used to release @a data resources after @a
+ *        server_die_cb is called or user calls
+ *        ethumb_client_disconnect().
+ */
+EAPI void
+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)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   if (client->die.free_data)
+     client->die.free_data(client->die.data);
+
+   client->die.cb = server_die_cb;
+   client->die.data = (void *)data;
+   client->die.free_data = free_data;
+}
+
+/**
+ * @cond LOCAL
+ */
+
+static void
+_ethumb_client_ethumb_setup_cb(void *data, DBusMessage *msg, DBusError *error)
+{
+   DBusMessageIter iter;
+   int t;
+   dbus_bool_t result = 0;
+   Ethumb_Client *client = data;
+
+   client->pending_setup = NULL;
+
+   if (!_dbus_callback_check_and_init(msg, &iter, error))
+     return;
+
+   t = dbus_message_iter_get_arg_type(&iter);
+   if (!_dbus_iter_type_check(t, DBUS_TYPE_BOOLEAN))
+     return;
+
+   dbus_message_iter_get_basic(&iter, &result);
+}
+
+static const char *
+_ethumb_client_dbus_get_bytearray(DBusMessageIter *iter)
+{
+   int el_type;
+   int length;
+   DBusMessageIter riter;
+   const char *result;
+
+   el_type = dbus_message_iter_get_element_type(iter);
+   if (el_type != DBUS_TYPE_BYTE)
+     {
+        ERR("not an byte array element.");
+        return NULL;
+     }
+
+   dbus_message_iter_recurse(iter, &riter);
+   dbus_message_iter_get_fixed_array(&riter, &result, &length);
+
+   if (result[0] == '\0')
+     return NULL;
+   else
+     return eina_stringshare_add(result);
+}
+
+static void
+_ethumb_client_dbus_append_bytearray(DBusMessageIter *iter, const char *string)
+{
+   DBusMessageIter viter;
+
+   if (!string)
+     string = "";
+
+   dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &viter);
+   dbus_message_iter_append_fixed_array(&viter, DBUS_TYPE_BYTE, &string,
+                                        strlen(string) + 1);
+   dbus_message_iter_close_container(iter, &viter);
+}
+
+/**
+ * @endcond
+ */
+
+/**
+ * Send setup to server.
+ *
+ * This method is called automatically by ethumb_client_generate() if
+ * any property was changed. No need to call it manually.
+ *
+ * @param client client instance. Must @b not be @c NULL and client
+ *        must be connected (after connected_cb is called).
+ */
+EAPI void
+ethumb_client_ethumb_setup(Ethumb_Client *client)
+{
+   DBusMessage *msg;
+   DBusMessageIter iter, aiter, diter, viter, vaiter;
+   Ethumb *e = client->ethumb;
+   const char *entry;
+   dbus_int32_t tw, th, format, aspect, orientation, quality, compress;
+   float cx, cy;
+   double t;
+   const char *theme_file, *group, *swallow;
+   const char *directory, *category;
+   double video_time, video_start, video_interval;
+   dbus_int32_t video_ntimes, video_fps, document_page;
+
+   EINA_SAFETY_ON_NULL_RETURN(client);
+   EINA_SAFETY_ON_FALSE_RETURN(client->connected);
+
+   msg = dbus_message_new_method_call(_ethumb_dbus_bus_name,
+                                      client->object_path,
+                                      _ethumb_dbus_objects_interface,
+                                      "ethumb_setup");
+   dbus_message_iter_init_append(msg, &iter);
+   dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &aiter);
+
+/**
+ * @cond LOCAL
+ */
+#define _open_variant_iter(str_entry, str_type, end_iter)                       \
+  entry = str_entry;                                                            \
+  dbus_message_iter_open_container(&aiter, DBUS_TYPE_DICT_ENTRY, NULL, &diter); \
+  dbus_message_iter_append_basic(&diter, DBUS_TYPE_STRING, &entry);             \
+  dbus_message_iter_open_container(&diter, DBUS_TYPE_VARIANT, str_type,         \
+                                   &end_iter);
+
+#define _close_variant_iter(end_iter)                   \
+  dbus_message_iter_close_container(&diter, &end_iter); \
+  dbus_message_iter_close_container(&aiter, &diter);
+/**
+ * @endcond
+ */
+
+   /* starting array elements */
+
+   _open_variant_iter("size", "(ii)", viter);
+   dbus_message_iter_open_container(&viter, DBUS_TYPE_STRUCT, NULL, &vaiter);
+   ethumb_thumb_size_get(e, &tw, &th);
+   dbus_message_iter_append_basic(&vaiter, DBUS_TYPE_INT32, &tw);
+   dbus_message_iter_append_basic(&vaiter, DBUS_TYPE_INT32, &th);
+   dbus_message_iter_close_container(&viter, &vaiter);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("format", "i", viter);
+   format = ethumb_thumb_format_get(e);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &format);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("aspect", "i", viter);
+   aspect = ethumb_thumb_aspect_get(e);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &aspect);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("orientation", "i", viter);
+   orientation = ethumb_thumb_orientation_get(e);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &orientation);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("crop", "(dd)", viter);
+   dbus_message_iter_open_container(&viter, DBUS_TYPE_STRUCT, NULL, &vaiter);
+   ethumb_thumb_crop_align_get(e, &cx, &cy);
+   t = cx;
+   dbus_message_iter_append_basic(&vaiter, DBUS_TYPE_DOUBLE, &t);
+   t = cy;
+   dbus_message_iter_append_basic(&vaiter, DBUS_TYPE_DOUBLE, &t);
+   dbus_message_iter_close_container(&viter, &vaiter);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("quality", "i", viter);
+   quality = ethumb_thumb_quality_get(e);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &quality);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("compress", "i", viter);
+   compress = ethumb_thumb_compress_get(e);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &compress);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("frame", "(ayayay)", viter);
+   dbus_message_iter_open_container(&viter, DBUS_TYPE_STRUCT, NULL, &vaiter);
+   ethumb_frame_get(e, &theme_file, &group, &swallow);
+   _ethumb_client_dbus_append_bytearray(&vaiter, theme_file);
+   _ethumb_client_dbus_append_bytearray(&vaiter, group);
+   _ethumb_client_dbus_append_bytearray(&vaiter, swallow);
+   dbus_message_iter_close_container(&viter, &vaiter);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("directory", "ay", viter);
+   directory = ethumb_thumb_dir_path_get(e);
+   _ethumb_client_dbus_append_bytearray(&viter, directory);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("category", "ay", viter);
+   category = ethumb_thumb_category_get(e);
+   _ethumb_client_dbus_append_bytearray(&viter, category);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("video_time", "d", viter);
+   video_time = ethumb_video_time_get(e);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_DOUBLE, &video_time);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("video_start", "d", viter);
+   video_start = ethumb_video_start_get(e);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_DOUBLE, &video_start);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("video_interval", "d", viter);
+   video_interval = ethumb_video_interval_get(e);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_DOUBLE, &video_interval);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("video_ntimes", "u", viter);
+   video_ntimes = ethumb_video_ntimes_get(e);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_UINT32, &video_ntimes);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("video_fps", "u", viter);
+   video_fps = ethumb_video_fps_get(e);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_UINT32, &video_fps);
+   _close_variant_iter(viter);
+
+   _open_variant_iter("document_page", "u", viter);
+   document_page = ethumb_document_page_get(e);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_UINT32, &document_page);
+   _close_variant_iter(viter);
+
+#undef _open_variant_iter
+#undef _close_variant_iter
+
+   dbus_message_iter_close_container(&iter, &aiter);
+
+   client->pending_setup = e_dbus_message_send(client->conn, msg,
+                                               _ethumb_client_ethumb_setup_cb,
+                                               -1, client);
+   dbus_message_unref(msg);
+}
+
+/**
+ * @cond LOCAL
+ */
+
+static void
+_ethumb_client_generated_cb(void *data, DBusMessage *msg)
+{
+   DBusMessageIter iter;
+   dbus_int32_t id = -1;
+   const char *thumb = NULL;
+   const char *thumb_key = NULL;
+   Ethumb_Client *client = data;
+   int t;
+   dbus_bool_t success;
+   Eina_List *l;
+   int found;
+   struct _ethumb_pending_gen *pending;
+
+   dbus_message_iter_init(msg, &iter);
+
+   t = dbus_message_iter_get_arg_type(&iter);
+   if (!_dbus_iter_type_check(t, DBUS_TYPE_INT32))
+     goto end;
+   dbus_message_iter_get_basic(&iter, &id);
+   dbus_message_iter_next(&iter);
+
+   t = dbus_message_iter_get_arg_type(&iter);
+   if (!_dbus_iter_type_check(t, DBUS_TYPE_ARRAY))
+     goto end;
+   thumb = _ethumb_client_dbus_get_bytearray(&iter);
+   dbus_message_iter_next(&iter);
+
+   t = dbus_message_iter_get_arg_type(&iter);
+   if (!_dbus_iter_type_check(t, DBUS_TYPE_ARRAY))
+     goto end;
+   thumb_key = _ethumb_client_dbus_get_bytearray(&iter);
+   dbus_message_iter_next(&iter);
+
+   t = dbus_message_iter_get_arg_type(&iter);
+   if (!_dbus_iter_type_check(t, DBUS_TYPE_BOOLEAN))
+     goto end;
+   dbus_message_iter_get_basic(&iter, &success);
+
+   found = 0;
+   l = client->pending_gen;
+   while (l)
+     {
+        pending = l->data;
+        if (pending->id == id)
+          {
+             found = 1;
+             break;
+          }
+        l = l->next;
+     }
+
+   if (found)
+     {
+        client->pending_gen = eina_list_remove_list(client->pending_gen, l);
+        if (pending->generated_cb)
+          pending->generated_cb(pending->data, client, id,
+                                pending->file, pending->key,
+                                pending->thumb, pending->thumb_key,
+                                success);
+        if (pending->free_data)
+          pending->free_data(pending->data);
+        eina_stringshare_del(pending->file);
+        eina_stringshare_del(pending->key);
+        eina_stringshare_del(pending->thumb);
+        eina_stringshare_del(pending->thumb_key);
+        free(pending);
+     }
+
+end:
+   if (thumb) eina_stringshare_del(thumb);
+   if (thumb_key) eina_stringshare_del(thumb_key);
+}
+
+static void
+_ethumb_client_queue_add_cb(void *data, DBusMessage *msg, DBusError *error)
+{
+   DBusMessageIter iter;
+   int t;
+   dbus_int32_t id = -1;
+   struct _ethumb_pending_add *pending = data;
+   struct _ethumb_pending_gen *generating;
+   Ethumb_Client *client = pending->client;
+
+   client->pending_add = eina_list_remove(client->pending_add, pending);
+
+   if (!_dbus_callback_check_and_init(msg, &iter, error))
+     goto end;
+
+   t = dbus_message_iter_get_arg_type(&iter);
+   if (!_dbus_iter_type_check(t, DBUS_TYPE_INT32))
+     goto end;
+
+   dbus_message_iter_get_basic(&iter, &id);
+
+   generating = calloc(1, sizeof(*generating));
+   generating->id = id;
+   generating->file = pending->file;
+   generating->key = pending->key;
+   generating->thumb = pending->thumb;
+   generating->thumb_key = pending->thumb_key;
+   generating->generated_cb = pending->generated_cb;
+   generating->data = pending->data;
+   generating->free_data = pending->free_data;
+   client->pending_gen = eina_list_append(client->pending_gen, generating);
+
+end:
+   free(pending);
+}
+
+static int
+_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)
+{
+   DBusMessage *msg;
+   DBusMessageIter iter;
+   struct _ethumb_pending_add *pending;
+
+   pending = calloc(1, sizeof(*pending));
+   pending->id = client->id_count;
+   pending->file = eina_stringshare_add(file);
+   pending->key = eina_stringshare_add(key);
+   pending->thumb = eina_stringshare_add(thumb);
+   pending->thumb_key = eina_stringshare_add(thumb_key);
+   pending->generated_cb = generated_cb;
+   pending->data = (void *)data;
+   pending->free_data = free_data;
+   pending->client = client;
+
+   client->id_count = (client->id_count + 1) % MAX_ID;
+
+   msg = dbus_message_new_method_call(_ethumb_dbus_bus_name,
+                                      client->object_path,
+                                      _ethumb_dbus_objects_interface,
+                                      "queue_add");
+
+   dbus_message_iter_init_append(msg, &iter);
+   dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &pending->id);
+   _ethumb_client_dbus_append_bytearray(&iter, file);
+   _ethumb_client_dbus_append_bytearray(&iter, key);
+   _ethumb_client_dbus_append_bytearray(&iter, thumb);
+   _ethumb_client_dbus_append_bytearray(&iter, thumb_key);
+
+   pending->pending_call = e_dbus_message_send(client->conn, msg,
+                                               _ethumb_client_queue_add_cb,
+                                               -1, pending);
+   client->pending_add = eina_list_append(client->pending_add, pending);
+   dbus_message_unref(msg);
+
+   return pending->id;
+}
+
+static void
+_ethumb_client_queue_remove_cb(void *data, DBusMessage *msg, DBusError *error)
+{
+   DBusMessageIter iter;
+   int t;
+   dbus_bool_t success = 0;
+   struct _ethumb_pending_remove *pending = data;
+   Ethumb_Client *client = pending->client;
+
+   client->pending_remove = eina_list_remove(client->pending_remove, pending);
+
+   if (!_dbus_callback_check_and_init(msg, &iter, error))
+     goto end;
+
+   t = dbus_message_iter_get_arg_type(&iter);
+   if (!_dbus_iter_type_check(t, DBUS_TYPE_BOOLEAN))
+     goto end;
+
+   dbus_message_iter_get_basic(&iter, &success);
+
+end:
+   if (pending->cancel_cb)
+     pending->cancel_cb(pending->data, success);
+   if (pending->free_data)
+     pending->free_data(pending->data);
+   free(pending);
+}
+
+/**
+ * @endcond
+ */
+
+/**
+ * Ask server to cancel generation of thumbnail.
+ *
+ * @param client client instance. Must @b not be @c NULL and client
+ *        must be connected (after connected_cb is called).
+ * @param id valid id returned by ethumb_client_generate()
+ * @param cancel_cb function to report cancellation results.
+ * @param data context argument to give back to @a cancel_cb. May be
+ *        @c NULL.
+ * @param data context to give back to @a cancel_cb. May be @c
+ *        NULL.
+ * @param free_data used to release @a data resources after @a
+ *        cancel_cb is called or user calls
+ *        ethumb_client_disconnect().
+ */
+EAPI void
+ethumb_client_generate_cancel(Ethumb_Client *client, int id, Ethumb_Client_Generate_Cancel_Cb cancel_cb, const void *data, Eina_Free_Cb free_data)
+{
+   DBusMessage *msg;
+   struct _ethumb_pending_remove *pending;
+   Eina_List *l;
+   int found;
+   dbus_int32_t id32 = id;
+   EINA_SAFETY_ON_NULL_RETURN(client);
+   EINA_SAFETY_ON_FALSE_RETURN(id >= 0);
+
+   pending = calloc(1, sizeof(*pending));
+   pending->id = id;
+   pending->cancel_cb = cancel_cb;
+   pending->data = (void *)data;
+   pending->free_data = free_data;
+   pending->client = client;
+
+   msg = dbus_message_new_method_call(_ethumb_dbus_bus_name,
+                                      client->object_path,
+                                      _ethumb_dbus_objects_interface,
+                                      "queue_remove");
+
+   dbus_message_append_args(msg, DBUS_TYPE_INT32, &id32, DBUS_TYPE_INVALID);
+   pending->pending_call = e_dbus_message_send(client->conn, msg,
+                                               _ethumb_client_queue_remove_cb,
+                                               -1, pending);
+   client->pending_remove = eina_list_append(client->pending_remove, pending);
+
+   found = 0;
+   l = client->pending_add;
+   while (l)
+     {
+        struct _ethumb_pending_add *pending_add = l->data;
+        if (pending_add->id != id32)
+          {
+             l = l->next;
+             continue;
+          }
+        client->pending_add = eina_list_remove_list(client->pending_add, l);
+        eina_stringshare_del(pending_add->file);
+        eina_stringshare_del(pending_add->key);
+        eina_stringshare_del(pending_add->thumb);
+        eina_stringshare_del(pending_add->thumb_key);
+        dbus_pending_call_cancel(pending_add->pending_call);
+        dbus_pending_call_unref(pending_add->pending_call);
+        if (pending_add->free_data)
+          pending_add->free_data(pending_add->data);
+        free(pending_add);
+        found = 1;
+        break;
+     }
+
+   if (found)
+     goto end;
+
+   l = client->pending_gen;
+   while (l)
+     {
+        struct _ethumb_pending_gen *pending_gen = l->data;
+        if (pending_gen->id != id32)
+          {
+             l = l->next;
+             continue;
+          }
+        client->pending_gen = eina_list_remove_list(client->pending_gen, l);
+        eina_stringshare_del(pending_gen->file);
+        eina_stringshare_del(pending_gen->key);
+        eina_stringshare_del(pending_gen->thumb);
+        eina_stringshare_del(pending_gen->thumb_key);
+        if (pending_gen->free_data)
+          pending_gen->free_data(pending_gen->data);
+        free(pending_gen);
+        break;
+     }
+
+end:
+   dbus_message_unref(msg);
+}
+
+/**
+ * @cond LOCAL
+ */
+static void
+_ethumb_client_queue_clear_cb(void *data, DBusMessage *msg __UNUSED__, DBusError *error __UNUSED__)
+{
+   Ethumb_Client *client = data;
+
+   client->pending_clear = NULL;
+}
+
+/**
+ * @endcond
+ */
+
+/**
+ * Ask server to cancel generation of all thumbnails.
+ *
+ * @param client client instance. Must @b not be @c NULL and client
+ *        must be connected (after connected_cb is called).
+ *
+ * @see ethumb_client_generate_cancel()
+ */
+EAPI void
+ethumb_client_generate_cancel_all(Ethumb_Client *client)
+{
+   DBusMessage *msg;
+   void *data;
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   if (client->pending_clear)
+     return;
+
+   EINA_LIST_FREE(client->pending_add, data)
+     {
+        struct _ethumb_pending_add *pending = data;
+        eina_stringshare_del(pending->file);
+        eina_stringshare_del(pending->key);
+        eina_stringshare_del(pending->thumb);
+        eina_stringshare_del(pending->thumb_key);
+        dbus_pending_call_cancel(pending->pending_call);
+        dbus_pending_call_unref(pending->pending_call);
+        if (pending->free_data)
+          pending->free_data(pending->data);
+        free(pending);
+     }
+
+   EINA_LIST_FREE(client->pending_gen, data)
+     {
+        struct _ethumb_pending_gen *pending = data;
+        eina_stringshare_del(pending->file);
+        eina_stringshare_del(pending->key);
+        eina_stringshare_del(pending->thumb);
+        eina_stringshare_del(pending->thumb_key);
+        if (pending->free_data)
+          pending->free_data(pending->data);
+        free(pending);
+     }
+
+   msg = dbus_message_new_method_call(_ethumb_dbus_bus_name,
+                                      client->object_path,
+                                      _ethumb_dbus_objects_interface,
+                                      "queue_clear");
+
+   client->pending_clear = e_dbus_message_send(client->conn, msg,
+                                               _ethumb_client_queue_clear_cb,
+                                               -1, client);
+
+   dbus_message_unref(msg);
+}
+
+/**
+ * Configure future requests to use FreeDesktop.Org preset.
+ *
+ * This is a preset to provide freedesktop.org (fdo) standard
+ * compliant thumbnails. That is, files are stored as JPEG under
+ * ~/.thumbnails/SIZE, with size being either normal (128x128) or
+ * large (256x256).
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param s size identifier, either #ETHUMB_THUMB_NORMAL (0) or
+ *        #ETHUMB_THUMB_LARGE (1).
+ *
+ * @see ethumb_client_size_set()
+ * @see ethumb_client_aspect_set()
+ * @see ethumb_client_crop_align_set()
+ * @see ethumb_client_category_set()
+ * @see ethumb_client_dir_path_set()
+ */
+EAPI void
+ethumb_client_fdo_set(Ethumb_Client *client, Ethumb_Thumb_FDO_Size s)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   ethumb_thumb_fdo_set(client->ethumb, s);
+}
+
+/**
+ * Configure future request to use custom size.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param tw width, default is 128.
+ * @param th height, default is 128.
+ */
+EAPI void
+ethumb_client_size_set(Ethumb_Client *client, int tw, int th)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   ethumb_thumb_size_set(client->ethumb, tw, th);
+}
+
+/**
+ * Retrieve future request to use custom size.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param tw where to return width. May be @c NULL.
+ * @param th where to return height. May be @c NULL.
+ */
+EAPI void
+ethumb_client_size_get(const Ethumb_Client *client, int *tw, int *th)
+{
+   if (tw) *tw = 0;
+   if (th) *th = 0;
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   ethumb_thumb_size_get(client->ethumb, tw, th);
+}
+
+/**
+ * Configure format to use for future requests.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param f format identifier to use, either #ETHUMB_THUMB_FDO (0),
+ *        #ETHUMB_THUMB_JPEG (1) or #ETHUMB_THUMB_EET (2). Default is FDO.
+ */
+EAPI void
+ethumb_client_format_set(Ethumb_Client *client, Ethumb_Thumb_Format f)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   ethumb_thumb_format_set(client->ethumb, f);
+}
+
+/**
+ * Retrieve format to use for future requests.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
+ * @return format identifier to use, either #ETHUMB_THUMB_FDO (0),
+ *         #ETHUMB_THUMB_JPEG (1) or #ETHUMB_THUMB_EET (2).
+ */
+EAPI Ethumb_Thumb_Format
+ethumb_client_format_get(const Ethumb_Client *client)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
+
+   return ethumb_thumb_format_get(client->ethumb);
+}
+
+/**
+ * Configure aspect mode to use.
+ *
+ * If aspect is kept (#ETHUMB_THUMB_KEEP_ASPECT), then image will be
+ * rescaled so the largest dimension is not bigger than it's specified
+ * size (see ethumb_client_size_get()) and the other dimension is
+ * resized in the same proportion. Example: size is 256x256, image is
+ * 1000x500, resulting thumbnail is 256x128.
+ *
+ * If aspect is ignored (#ETHUMB_THUMB_IGNORE_ASPECT), then image will
+ * be distorted to match required thumbnail size. Example: size is
+ * 256x256, image is 1000x500, resulting thumbnail is 256x256.
+ *
+ * If crop is required (#ETHUMB_THUMB_CROP), then image will be
+ * cropped so the smallest dimension is not bigger than its specified
+ * size (see ethumb_client_size_get()) and the other dimension will
+ * overflow, not being visible in the final image. How it will
+ * overflow is speficied by ethumb_client_crop_align_set()
+ * alignment. Example: size is 256x256, image is 1000x500, crop
+ * alignment is 0.5, 0.5, resulting thumbnail is 256x256 with 250
+ * pixels from left and 250 pixels from right being lost, that is just
+ * the 500x500 central pixels of image will be considered for scaling.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param a aspect mode identifier, either #ETHUMB_THUMB_KEEP_ASPECT (0),
+ *        #ETHUMB_THUMB_IGNORE_ASPECT (1) or #ETHUMB_THUMB_CROP (2).
+ */
+EAPI void
+ethumb_client_aspect_set(Ethumb_Client *client, Ethumb_Thumb_Aspect a)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   ethumb_thumb_aspect_set(client->ethumb, a);
+}
+
+/**
+ * Get current aspect in use for requests.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
+ * @return aspect in use for future requests.
+ */
+EAPI Ethumb_Thumb_Aspect
+ethumb_client_aspect_get(const Ethumb_Client *client)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
+
+   return ethumb_thumb_aspect_get(client->ethumb);
+}
+
+/**
+ * Configure orientation to use for future requests.
+ *
+ * Default value is #ETHUMB_THUMB_ORIENT_ORIGINAL: metadata from the file
+ * will be used to orient pixel data.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param o format identifier to use, either #ETHUMB_THUMB_ORIENT_NONE (0),
+ *        #ETHUMB_THUMB_ROTATE_90_CW (1), #ETHUMB_THUMB_ROTATE_180 (2),
+ *        #ETHUMB_THUMB_ROTATE_90_CCW (3), #ETHUMB_THUMB_FLIP_HORIZONTAL (4),
+ *        #ETHUMB_THUMB_FLIP_VERTICAL (5), #ETHUMB_THUMB_FLIP_TRANSPOSE (6),
+ *        #ETHUMB_THUMB_FLIP_TRANSVERSE (7) or #ETHUMB_THUMB_ORIENT_ORIGINAL
+ *        (8). Default is ORIGINAL.
+ */
+EAPI void
+ethumb_client_orientation_set(Ethumb_Client *client, Ethumb_Thumb_Orientation o)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   ethumb_thumb_orientation_set(client->ethumb, o);
+}
+
+/**
+ * Get current orientation in use for requests.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
+ * @return orientation in use for future requests.
+ */
+EAPI Ethumb_Thumb_Orientation
+ethumb_client_orientation_get(const Ethumb_Client *client)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
+
+   return ethumb_thumb_orientation_get(client->ethumb);
+}
+
+/**
+ * Configure crop alignment in use for future requests.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param x horizontal alignment. 0.0 means left side will be visible
+ *        or right side is being lost. 1.0 means right side will be
+ *        visible or left side is being lost. 0.5 means just center is
+ *        visible, both sides will be lost.  Default is 0.5.
+ * @param y vertical alignment. 0.0 is top visible, 1.0 is bottom
+ *        visible, 0.5 is center visible. Default is 0.5
+ */
+EAPI void
+ethumb_client_crop_align_set(Ethumb_Client *client, float x, float y)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   ethumb_thumb_crop_align_set(client->ethumb, x, y);
+}
+
+/**
+ * Get current crop alignment in use for requests.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param x where to return horizontal alignment. May be @c NULL.
+ * @param y where to return vertical alignment. May be @c NULL.
+ */
+EAPI void
+ethumb_client_crop_align_get(const Ethumb_Client *client, float *x, float *y)
+{
+   if (x) *x = 0.0;
+   if (y) *y = 0.0;
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   ethumb_thumb_crop_align_get(client->ethumb, x, y);
+}
+
+/**
+ * Configure quality to be used in thumbnails.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param quality value from 0 to 100, default is 80. The effect
+ *        depends on the format being used, PNG will not use it.
+ */
+EAPI void
+ethumb_client_quality_set(Ethumb_Client *client, int quality)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   ethumb_thumb_quality_set(client->ethumb, quality);
+}
+
+/**
+ * Get quality to be used in thumbnails.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
+ * @return quality value from 0 to 100, default is 80. The effect
+ *         depends on the format being used, PNG will not use it.
+ */
+EAPI int
+ethumb_client_quality_get(const Ethumb_Client *client)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
+
+   return ethumb_thumb_quality_get(client->ethumb);
+}
+
+/**
+ * Configure compression level used in requests.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param compress value from 0 to 9, default is 9. The effect
+ *        depends on the format being used, JPEG will not use it.
+ */
+EAPI void
+ethumb_client_compress_set(Ethumb_Client *client, int compress)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   ethumb_thumb_compress_set(client->ethumb, compress);
+}
+
+/**
+ * Get compression level used in requests.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
+ * @return compress value from 0 to 9, default is 9. The effect
+ *         depends on the format being used, JPEG will not use it.
+ */
+EAPI int
+ethumb_client_compress_get(const Ethumb_Client *client)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
+
+   return ethumb_thumb_compress_get(client->ethumb);
+}
+
+/**
+ * Set frame to apply to future thumbnails.
+ *
+ * This will create an edje object that will have image swallowed
+ * in. This can be used to simulate Polaroid or wood frames in the
+ * generated image. Remeber it is bad to modify the original contents
+ * of thumbnails, but sometimes it's useful to have it composited and
+ * avoid runtime overhead.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param file file path to edje.
+ * @param group group inside edje to use.
+ * @param swallow name of swallow part.
+ *
+ * @return @c EINA_TRUE on success, @c EINA_FALSE on failure.
+ */
+EAPI Eina_Bool
+ethumb_client_frame_set(Ethumb_Client *client, const char *file, const char *group, const char *swallow)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   return ethumb_frame_set(client->ethumb, file, group, swallow);
+}
+
+/**
+ * Configure where to store thumbnails in future requests.
+ *
+ * This value will be used to generate thumbnail paths, that is, it
+ * will be used when ethumb_client_thumb_path_set() was not called
+ * after last ethumb_client_file_set().
+ *
+ * Note that this is the base, a category is added to this path as a
+ * sub directory. This is not the final directory where files are
+ * stored, the thumbnail system will account @b category as well, see
+ * ethumb_client_category_set().
+ *
+ * As other options, this value will only be applied to future
+ * requests.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param path base directory where to store thumbnails. Default is
+ *        ~/.thumbnails
+ *
+ * @see ethumb_client_category_set()
+ */
+EAPI void
+ethumb_client_dir_path_set(Ethumb_Client *client, const char *path)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   ethumb_thumb_dir_path_set(client->ethumb, path);
+}
+
+/**
+ * Get base directory path where to store thumbnails.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
+ * @return pointer to internal string with current path. This string
+ *         should not be modified or freed.
+ *
+ * @see ethumb_client_dir_path_set()
+ */
+EAPI const char *
+ethumb_client_dir_path_get(const Ethumb_Client *client)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
+
+   return ethumb_thumb_dir_path_get(client->ethumb);
+}
+
+/**
+ * Category directory to store thumbnails.
+ *
+ * This value will be used to generate thumbnail paths, that is, it
+ * will be used when ethumb_client_thumb_path_set() was not called
+ * after last ethumb_client_file_set().
+ *
+ * This is a sub-directory inside base directory
+ * (ethumb_client_dir_path_set()) that creates a namespace to avoid
+ * different options resulting in the same file.
+ *
+ * As other options, this value will only be applied to future
+ * requests.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param category category sub directory to store thumbnail. Default
+ *        is either "normal" or "large" for FDO compliant thumbnails
+ *        or WIDTHxHEIGHT-ASPECT[-FRAMED]-FORMAT. It can be a string
+ *        or @c NULL to use auto generated names.
+ *
+ * @see ethumb_client_dir_path_set()
+ */
+EAPI void
+ethumb_client_category_set(Ethumb_Client *client, const char *category)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   ethumb_thumb_category_set(client->ethumb, category);
+}
+
+/**
+ * Get category sub-directory  where to store thumbnails.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
+ * @return pointer to internal string with current path. This string
+ *         should not be modified or freed.
+ *
+ * @see ethumb_client_category_set()
+ */
+EAPI const char *
+ethumb_client_category_get(const Ethumb_Client *client)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
+
+   return ethumb_thumb_category_get(client->ethumb);
+}
+
+/**
+ * Set the video time (duration) in seconds.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param t duration (in seconds). Defaults to 3 seconds.
+ */
+EAPI void
+ethumb_client_video_time_set(Ethumb_Client *client, float t)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   ethumb_video_time_set(client->ethumb, t);
+}
+
+/**
+ * Set initial video position to start thumbnailing, in percentage.
+ *
+ * This is useful to avoid thumbnailing the company/producer logo or
+ * movie opening.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param start initial video positon to thumbnail, in percentage (0.0
+ *        to 1.0, inclusive). Defaults to 10% (0.1).
+ */
+EAPI void
+ethumb_client_video_start_set(Ethumb_Client *client, float start)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+   EINA_SAFETY_ON_FALSE_RETURN(start >= 0.0);
+   EINA_SAFETY_ON_FALSE_RETURN(start <= 1.0);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   ethumb_video_start_set(client->ethumb, start);
+}
+
+/**
+ * Set the video frame interval, in seconds.
+ *
+ * This is useful for animated thumbnail and will define skip time
+ * before going to the next frame. Note that video backends might not
+ * be able to precisely skip that amount as it will depend on various
+ * factors, including video encoding.
+ *
+ * Although this seems similar to ethumb_client_video_fps_set(), this
+ * one is the time that will be used to seek. The math is simple, for
+ * each new frame the video position will be set to:
+ * ((video_length * start_time) + (interval * current_frame_number)).
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param interval time between frames, in seconds. Defaults to 0.05
+ *        seconds.
+ */
+EAPI void
+ethumb_client_video_interval_set(Ethumb_Client *client, float interval)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   ethumb_video_interval_set(client->ethumb, interval);
+}
+
+/**
+ * Set the number of frames to thumbnail.
+ *
+ * This is useful for animated thumbnail and will define how many
+ * frames the generated file will have.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param ntimes number of times, must be greater than zero.
+ *        Defaults to 3.
+ */
+EAPI void
+ethumb_client_video_ntimes_set(Ethumb_Client *client, unsigned int ntimes)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+   EINA_SAFETY_ON_FALSE_RETURN(ntimes > 0);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   ethumb_video_ntimes_set(client->ethumb, ntimes);
+}
+
+/**
+ * Set the number of frames per second to thumbnail the video.
+ *
+ * This configures the number of times per seconds the thumbnail will
+ * use to create thumbnails.
+ *
+ * Although this is similar to ethumb_client_video_interval_set(), it
+ * is the delay used between calling functions thata generates frames,
+ * while the other is the time used to skip inside the video.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param fps number of frames per second to thumbnail. Must be greater
+ *        than zero. Defaults to 10.
+ */
+EAPI void
+ethumb_client_video_fps_set(Ethumb_Client *client, unsigned int fps)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+   EINA_SAFETY_ON_FALSE_RETURN(fps > 0);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   ethumb_video_fps_set(client->ethumb, fps);
+}
+
+/**
+ * Set the page number to thumbnail in paged documents.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param page page number, defaults to 0 (first).
+ */
+EAPI void
+ethumb_client_document_page_set(Ethumb_Client *client, unsigned int page)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   if (!client->old_ethumb_conf)
+     client->old_ethumb_conf = ethumb_dup(client->ethumb);
+   ethumb_document_page_set(client->ethumb, page);
+}
+
+/**
+ * Set source file to be thumbnailed.
+ *
+ * Calling this function has the side effect of resetting values set
+ * with ethumb_client_thumb_path_set() or auto-generated with
+ * ethumb_client_thumb_exists().
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param path the filesystem path to use. May be @c NULL.
+ * @param key the extra argument/key inside @a path to read image
+ *        from. This is only used for formats that allow multiple
+ *        resources in one file, like EET or Edje (group name).
+ *
+ * @return @c EINA_TRUE on success, @c EINA_FALSE on failure.
+ */
+EAPI Eina_Bool
+ethumb_client_file_set(Ethumb_Client *client, const char *path, const char *key)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
+
+   return ethumb_file_set(client->ethumb, path, key);
+}
+
+/**
+ * Get values set with ethumb_client_file_get()
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param path where to return configured path. May be @c NULL.  If
+ *        not @c NULL, then it will be a pointer to a stringshared
+ *        instance, but @b no references are added (do it with
+ *        eina_stringshare_ref())!
+ * @param key where to return configured key. May be @c NULL.If not @c
+ *        NULL, then it will be a pointer to a stringshared instance,
+ *        but @b no references are added (do it with
+ *        eina_stringshare_ref())!
+ */
+EAPI void
+ethumb_client_file_get(Ethumb_Client *client, const char **path, const char **key)
+{
+   if (path) *path = NULL;
+   if (key) *key = NULL;
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   ethumb_file_get(client->ethumb, path, key);
+}
+
+/**
+ * Reset previously set file to @c NULL.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ */
+EAPI void
+ethumb_client_file_free(Ethumb_Client *client)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   ethumb_file_free(client->ethumb);
+}
+
+/**
+ * Set a defined path and key to store the thumbnail.
+ *
+ * If not explicitly given, the thumbnail path will be auto-generated
+ * by ethumb_client_thumb_exists() or server using configured
+ * parameters like size, aspect and category.
+ *
+ * Set these to @c NULL to forget previously given values. After
+ * ethumb_client_file_set() these values will be reset to @c NULL.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param path force generated thumbnail to the exact given path. If
+ *        @c NULL, then reverts back to auto-generation.
+ * @param key force generated thumbnail to the exact given key. If
+ *        @c NULL, then reverts back to auto-generation.
+ */
+EAPI void
+ethumb_client_thumb_path_set(Ethumb_Client *client, const char *path, const char *key)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   ethumb_thumb_path_set(client->ethumb, path, key);
+}
+
+/**
+ * Get the configured thumbnail path.
+ *
+ * This returns the value set with ethumb_client_thumb_path_set() or
+ * auto-generated by ethumb_client_thumb_exists() if it was not set.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param path where to return configured path. May be @c NULL.  If
+ *        there was no path configured with
+ *        ethumb_client_thumb_path_set() and
+ *        ethumb_client_thumb_exists() was not called, then it will
+ *        probably return @c NULL. If not @c NULL, then it will be a
+ *        pointer to a stringshared instance, but @b no references are
+ *        added (do it with eina_stringshare_ref())!
+ * @param key where to return configured key. May be @c NULL.  If
+ *        there was no key configured with
+ *        ethumb_client_thumb_key_set() and
+ *        ethumb_client_thumb_exists() was not called, then it will
+ *        probably return @c NULL. If not @c NULL, then it will be a
+ *        pointer to a stringshared instance, but @b no references are
+ *        added (do it with eina_stringshare_ref())!
+ */
+EAPI void
+ethumb_client_thumb_path_get(Ethumb_Client *client, const char **path, const char **key)
+{
+   if (path) *path = NULL;
+   if (key) *key = NULL;
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   ethumb_thumb_path_get(client->ethumb, path, key);
+}
+
+/**
+ * Checks whenever file already exists (locally!)
+ *
+ * This will check locally (not calling server) if thumbnail already
+ * exists or not, also calculating the thumbnail path. See
+ * ethumb_client_thumb_path_get(). Path must be configured with
+ * ethumb_client_file_set() before using it and the last set file will
+ * be used!
+ *
+ * @param client client instance. Must @b not be @c NULL and client
+ *        must be configured with ethumb_client_file_set().
+ *
+ * @return @c EINA_TRUE if it exists, @c EINA_FALSE otherwise.
+ */
+EAPI Ethumb_Exists *
+ethumb_client_thumb_exists(Ethumb_Client *client, Ethumb_Client_Thumb_Exists_Cb exists_cb, const void *data)
+{
+   const char *path = NULL;
+   Ethumb_Async_Exists *async = NULL;
+   Ethumb_Exists *cb = NULL;
+   Ecore_Thread *t;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
+
+   ethumb_file_get(client->ethumb, &path, NULL);
+   if (!path) goto on_error;
+
+   async = eina_hash_find(_exists_request, path);
+   if (!async)
+     {
+        async = malloc(sizeof (Ethumb_Async_Exists));
+        if (!async) goto on_error;
+
+        async->path = eina_stringshare_ref(path);
+        async->callbacks = NULL;
+        async->dup = ethumb_dup(client->ethumb);
+
+        if (!async->dup) goto on_error;
+
+        cb = malloc(sizeof (Ethumb_Exists));
+        if (!cb) goto on_error;
+
+        EINA_REFCOUNT_REF(client);
+        cb->client = client;
+        cb->dup = ethumb_dup(client->ethumb);
+        cb->exists_cb = exists_cb;
+        cb->data = data;
+        cb->parent = async;
+
+        async->callbacks = eina_list_append(async->callbacks, cb);
+
+        /* spawn a thread here */
+        t = ecore_thread_run(_ethumb_client_exists_heavy,
+                             _ethumb_client_exists_end,
+                             _ethumb_client_exists_end,
+                             async);
+        if (!t) return NULL;
+        async->thread = t;
+
+        eina_hash_direct_add(_exists_request, async->path, async);
+
+        return cb;
+     }
+
+   cb = malloc(sizeof (Ethumb_Exists));
+   if (!cb)
+     {
+        async = NULL;
+        goto on_error;
+     }
+
+   EINA_REFCOUNT_REF(client);
+   cb->client = client;
+   cb->dup = ethumb_dup(client->ethumb);
+   cb->exists_cb = exists_cb;
+   cb->data = data;
+   cb->parent = async;
+
+   async->callbacks = eina_list_append(async->callbacks, cb);
+
+   return cb;
+
+on_error:
+   exists_cb((void *)data, client, NULL, EINA_FALSE);
+
+   if (async)
+     {
+        eina_stringshare_del(async->path);
+        if (async->dup) ethumb_free(async->dup);
+        free(async);
+     }
+   return NULL;
+}
+
+/**
+ * Cancel an ongoing exists request.
+ *
+ * @param exists the request to cancel.
+ */
+EAPI void
+ethumb_client_thumb_exists_cancel(Ethumb_Exists *exists)
+{
+   Ethumb_Async_Exists *async = exists->parent;
+
+   async->callbacks = eina_list_remove(async->callbacks, exists);
+   if (eina_list_count(async->callbacks) <= 0)
+     ecore_thread_cancel(async->thread);
+
+   ethumb_free(exists->dup);
+   EINA_REFCOUNT_UNREF(exists->client)
+   _ethumb_client_free(exists->client);
+   free(exists);
+}
+
+/**
+ * Check if an exists request was cancelled.
+ *
+ * @param exists the request to check.
+ * @result return EINA_TRUE if the request was cancelled.
+ */
+EAPI Eina_Bool
+ethumb_client_thumb_exists_check(Ethumb_Exists *exists)
+{
+   Ethumb_Async_Exists *async = exists->parent;
+
+   if (!async) return EINA_TRUE;
+
+   if (async->callbacks) return EINA_FALSE;
+
+   return ecore_thread_check(async->thread);
+}
+
+/**
+ * Ask server to generate thumbnail.
+ *
+ * This process is asynchronous and will report back from main loop
+ * using @a generated_cb. One can cancel this request by calling
+ * ethumb_client_generate_cancel() or
+ * ethumb_client_generate_cancel_all(), but not that request might be
+ * processed by server already and no generated files will be removed
+ * if that is the case.
+ *
+ * This will not check if file already exists, this should be done by
+ * explicitly calling ethumb_client_thumb_exists(). That is, this
+ * function will override any existing thumbnail.
+ *
+ * @param client client instance. Must @b not be @c NULL and client
+ *        must be connected (after connected_cb is called).
+ * @param generated_cb function to report generation results.
+ * @param data context argument to give back to @a generated_cb. May
+ *        be @c NULL.
+ * @param data context to give back to @a generate_cb. May be @c
+ *        NULL.
+ * @param free_data used to release @a data resources after @a
+ *        generated_cb is called or user calls
+ *        ethumb_client_disconnect().
+ *
+ * @return identifier or -1 on error. If -1 is returned (error) then
+ *         @a free_data is @b not called!
+ *
+ * @see ethumb_client_connect()
+ * @see ethumb_client_file_set()
+ * @see ethumb_client_thumb_exists()
+ * @see ethumb_client_generate_cancel()
+ * @see ethumb_client_generate_cancel_all()
+ */
+EAPI int
+ethumb_client_generate(Ethumb_Client *client, Ethumb_Client_Generate_Cb generated_cb, const void *data, Eina_Free_Cb free_data)
+{
+   const char *file, *key, *thumb, *thumb_key;
+   int id;
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, -1);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(client->connected, -1);
+
+   ethumb_file_get(client->ethumb, &file, &key);
+   if (!file)
+     {
+        ERR("no file set.");
+        return -1;
+     }
+
+   ethumb_thumb_path_get(client->ethumb, &thumb, &thumb_key);
+
+   if (client->old_ethumb_conf &&
+       ethumb_cmp(client->old_ethumb_conf, client->ethumb))
+     {
+        ethumb_client_ethumb_setup(client);
+        ethumb_free(client->old_ethumb_conf);
+        client->old_ethumb_conf = NULL;
+     }
+   id = _ethumb_client_queue_add(client, file, key, thumb, thumb_key,
+                                 generated_cb, data, free_data);
+
+   return id;
+}
+
+struct _Ethumb_Client_Async
+{
+   Ethumb_Exists               *exists;
+   Ethumb_Client               *client;
+   Ethumb                      *dup;
+
+   Ethumb_Client_Async_Done_Cb  done;
+   Ethumb_Client_Async_Error_Cb error;
+   const void                  *data;
+
+   int                          id;
+};
+
+static Ecore_Idler *idler[2] = { NULL, NULL };
+static Eina_List *pending = NULL;
+static Eina_List *idle_tasks[2] = { NULL, NULL };
+
+static void
+_ethumb_client_async_free(Ethumb_Client_Async *async)
+{
+   EINA_REFCOUNT_UNREF(async->client)
+   _ethumb_client_free(async->client);
+   ethumb_free(async->dup);
+   free(async);
+}
+
+static void
+_ethumb_client_thumb_finish(void *data,
+                            Ethumb_Client *client, int id,
+                            const char *file __UNUSED__, const char *key __UNUSED__,
+                            const char *thumb_path, const char *thumb_key,
+                            Eina_Bool success)
+{
+   Ethumb_Client_Async *async = data;
+
+   assert(async->id == id);
+
+   if (success)
+     {
+        async->done(client, thumb_path, thumb_key, (void *)async->data);
+     }
+   else
+     {
+        async->error(client, (void *)async->data);
+     }
+
+   pending = eina_list_remove(pending, async);
+   _ethumb_client_async_free(async);
+}
+
+static Eina_Bool
+_ethumb_client_thumb_generate_idler(void *data __UNUSED__)
+{
+   Ethumb_Client_Async *async;
+   Eina_List *l1, *l2;
+
+   EINA_LIST_FOREACH_SAFE (idle_tasks[1], l1, l2, async)
+     {
+        Ethumb *tmp;
+
+        idle_tasks[1] = eina_list_remove_list(idle_tasks[1], l1);
+
+        tmp = async->client->ethumb;
+        async->client->ethumb = async->dup;
+
+        async->id = ethumb_client_generate(async->client, _ethumb_client_thumb_finish, async, NULL);
+        if (async->id == -1)
+          {
+             async->error(async->client, (void *)async->data);
+             async->client->ethumb = tmp;
+             _ethumb_client_async_free(async);
+          }
+        else
+          {
+             async->client->ethumb = tmp;
+          }
+
+        pending = eina_list_append(pending, async);
+
+        if (ecore_time_get() - ecore_loop_time_get() > ecore_animator_frametime_get() * 0.5)
+          return EINA_TRUE;
+     }
+
+   idler[1] = NULL;
+   return EINA_FALSE;
+}
+
+static void
+_ethumb_client_thumb_exists(void *data, Ethumb_Client *client, Ethumb_Exists *request, Eina_Bool exists)
+{
+   Ethumb_Client_Async *async = data;
+
+   if (request == NULL)
+     return;
+
+   assert(async->exists == request);
+
+   async->exists = NULL;
+   pending = eina_list_remove(pending, async);
+
+   if (exists)
+     {
+        const char *thumb_path;
+        const char *thumb_key;
+
+        ethumb_client_thumb_path_get(client, &thumb_path, &thumb_key);
+        async->done(client, thumb_path, thumb_key, (void *)async->data);
+        _ethumb_client_async_free(async);
+     }
+   else
+     {
+        idle_tasks[1] = eina_list_append(idle_tasks[1], async);
+
+        if (!idler[1])
+          idler[1] = ecore_idler_add(_ethumb_client_thumb_generate_idler, NULL);
+     }
+}
+
+static Eina_Bool
+_ethumb_client_thumb_exists_idler(void *data __UNUSED__)
+{
+   Ethumb_Client_Async *async;
+   Eina_List *l1, *l2;
+
+   EINA_LIST_FOREACH_SAFE (idle_tasks[0], l1, l2, async)
+     {
+        Ethumb *tmp;
+
+        idle_tasks[0] = eina_list_remove_list(idle_tasks[0], l1);
+
+        tmp = async->client->ethumb;
+        async->client->ethumb = async->dup;
+
+        async->exists = ethumb_client_thumb_exists(async->client, _ethumb_client_thumb_exists, async);
+        if (!async->exists)
+          {
+             async->error(async->client, (void *)async->data);
+             async->client->ethumb = tmp;
+             _ethumb_client_async_free(async);
+             continue;
+          }
+
+        async->client->ethumb = tmp;
+
+        pending = eina_list_append(pending, async);
+
+        if (ecore_time_get() - ecore_loop_time_get() > ecore_animator_frametime_get() * 0.5)
+          return EINA_TRUE;
+     }
+
+   idler[0] = NULL;
+   return EINA_FALSE;
+}
+
+EAPI Ethumb_Client_Async *
+ethumb_client_thumb_async_get(Ethumb_Client *client,
+                              Ethumb_Client_Async_Done_Cb done,
+                              Ethumb_Client_Async_Error_Cb error,
+                              const void *data)
+{
+   Ethumb_Client_Async *async;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, NULL);
+
+   async = malloc(sizeof (Ethumb_Client_Async));
+   if (!async)
+     {
+        error(client, (void *)data);
+        return NULL;
+     }
+
+   EINA_REFCOUNT_REF(client);
+   async->client = client;
+   async->dup = ethumb_dup(client->ethumb);
+   async->done = done;
+   async->error = error;
+   async->data = data;
+   async->exists = NULL;
+   async->id = -1;
+
+   idle_tasks[0] = eina_list_append(idle_tasks[0], async);
+
+   if (!idler[0])
+     idler[0] = ecore_idler_add(_ethumb_client_thumb_exists_idler, NULL);
+
+   return async;
+}
+
+EAPI void
+ethumb_client_thumb_async_cancel(Ethumb_Client *client, Ethumb_Client_Async *request)
+{
+   const char *path;
+
+   EINA_SAFETY_ON_NULL_RETURN(client);
+   EINA_SAFETY_ON_NULL_RETURN(request);
+
+   ethumb_file_get(request->dup, &path, NULL);
+
+   if (request->exists)
+     {
+        ethumb_client_thumb_exists_cancel(request->exists);
+        request->exists = NULL;
+
+        pending = eina_list_remove(pending, request);
+     }
+   else if (request->id != -1)
+     {
+        Ethumb *tmp = request->client->ethumb;
+        request->client->ethumb = request->dup;
+
+        ethumb_client_generate_cancel(request->client, request->id, NULL, NULL, NULL);
+
+        request->client->ethumb = tmp;
+
+        pending = eina_list_remove(pending, request);
+     }
+   else
+     {
+        idle_tasks[0] = eina_list_remove(idle_tasks[0], request);
+        idle_tasks[1] = eina_list_remove(idle_tasks[1], request);
+     }
+
+   _ethumb_client_async_free(request);
+}
+
diff --git a/src/lib/ethumb.c b/src/lib/ethumb.c
new file mode 100644 (file)
index 0000000..ac1637a
--- /dev/null
@@ -0,0 +1,1827 @@
+/**
+ * @file
+ *
+ * Copyright (C) 2009 by ProFUSION embedded systems
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Rafael Antognolli <antognolli@profusion.mobi>
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <ctype.h>
+
+#ifdef HAVE_XATTR
+# include <sys/xattr.h>
+#endif
+
+#ifndef PATH_MAX
+# define PATH_MAX 4096
+#endif
+
+#include <Eina.h>
+#include <eina_safety_checks.h>
+#include <Evas.h>
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+#include <Ecore_File.h>
+#include <Edje.h>
+
+#include "Ethumb.h"
+#include "ethumb_private.h"
+#include "Ethumb_Plugin.h"
+#include "md5.h"
+
+#ifdef HAVE_LIBEXIF
+  #include <libexif/exif-data.h>
+#endif
+
+static Ethumb_Version _version = { VMAJ, VMIN, VMIC, VREV };
+EAPI Ethumb_Version *ethumb_version = &_version;
+
+static int _log_dom = -1;
+#define DBG(...) EINA_LOG_DOM_DBG(_log_dom, __VA_ARGS__)
+#define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__)
+#define WRN(...) EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
+#define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__)
+
+static int initcount = 0;
+static const char *_home_thumb_dir = NULL;
+static const char *_thumb_category_normal = NULL;
+static const char *_thumb_category_large = NULL;
+
+static const int THUMB_SIZE_NORMAL = 128;
+static const int THUMB_SIZE_LARGE = 256;
+
+static Eina_Hash *_plugins_ext = NULL;
+static Eina_Array *_plugins = NULL;
+
+static Eina_Bool
+_ethumb_plugin_list_cb(Eina_Module *m, void *data __UNUSED__)
+{
+   const char *file;
+   const char **ext;
+   Ethumb_Plugin *plugin;
+   Ethumb_Plugin *(*plugin_get)(void);
+
+   file = eina_module_file_get(m);
+   if (!eina_module_load(m))
+     {
+       ERR("could not load module \"%s\": %s",
+           file, eina_error_msg_get(eina_error_get()));
+       return EINA_FALSE;
+     }
+
+   plugin_get = eina_module_symbol_get(m, "ethumb_plugin_get");
+   if (!plugin_get)
+     {
+       ERR("could not find ethumb_plugin_get() in module \"%s\": %s",
+           file, eina_error_msg_get(eina_error_get()));
+       eina_module_unload(m);
+       return EINA_FALSE;
+     }
+
+   plugin = plugin_get();
+   if (!plugin)
+     {
+       ERR("plugin \"%s\" failed to init.", file);
+       eina_module_unload(m);
+       return EINA_FALSE;
+     }
+
+   DBG("loaded plugin \"%s\" (%p) with extensions:", file, plugin);
+   for (ext = plugin->extensions; *ext; ext++)
+     {
+       DBG("   extension \"%s\"", *ext);
+       eina_hash_add(_plugins_ext, *ext, plugin);
+     }
+
+   return EINA_TRUE;
+}
+
+static void
+_ethumb_plugins_load(void)
+{
+   _plugins_ext = eina_hash_string_small_new(NULL);
+   EINA_SAFETY_ON_NULL_RETURN(_plugins_ext);
+
+   _plugins = eina_module_list_get(_plugins, PLUGINSDIR, 1,
+                                  &_ethumb_plugin_list_cb, NULL);
+}
+
+static void
+_ethumb_plugins_unload(void)
+{
+   eina_hash_free(_plugins_ext);
+   _plugins_ext = NULL;
+   eina_module_list_unload(_plugins);
+   eina_module_list_free(_plugins);
+   eina_array_free(_plugins);
+   _plugins = NULL;
+}
+
+EAPI int
+ethumb_init(void)
+{
+   const char *home;
+   char buf[PATH_MAX];
+
+   if (initcount)
+     return ++initcount;
+
+   if (!eina_init())
+     {
+       fprintf(stderr, "ERROR: Could not initialize eina.\n");
+       return 0;
+     }
+   _log_dom = eina_log_domain_register("ethumb", EINA_COLOR_GREEN);
+   if (_log_dom < 0)
+     {
+       EINA_LOG_ERR("Could not register log domain: ethumb");
+       eina_shutdown();
+       return 0;
+     }
+
+   evas_init();
+   ecore_init();
+   ecore_evas_init();
+   edje_init();
+
+   home = getenv("HOME");
+   snprintf(buf, sizeof(buf), "%s/.thumbnails", home);
+
+   _home_thumb_dir = eina_stringshare_add(buf);
+   _thumb_category_normal = eina_stringshare_add("normal");
+   _thumb_category_large = eina_stringshare_add("large");
+
+   _ethumb_plugins_load();
+   return ++initcount;
+}
+
+EAPI int
+ethumb_shutdown(void)
+{
+   if (initcount <= 0)
+     {
+        EINA_LOG_ERR("Init count not greater than 0 in shutdown.");
+        return 0;
+     }
+   initcount--;
+   if (initcount == 0)
+     {
+       _ethumb_plugins_unload();
+       eina_stringshare_del(_home_thumb_dir);
+       eina_stringshare_del(_thumb_category_normal);
+       eina_stringshare_del(_thumb_category_large);
+       evas_shutdown();
+       ecore_shutdown();
+       ecore_evas_shutdown();
+       edje_shutdown();
+       eina_log_domain_unregister(_log_dom);
+       _log_dom = -1;
+       eina_shutdown();
+     }
+
+   return initcount;
+}
+
+EAPI Ethumb *
+ethumb_new(void)
+{
+   Ethumb *ethumb;
+   Ecore_Evas *ee, *sub_ee;
+   Evas *e, *sub_e;
+   Evas_Object *o, *img;
+
+   ethumb = calloc(1, sizeof(Ethumb));
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ethumb, NULL);
+
+   /* IF CHANGED, UPDATE DOCS in (Ethumb.c, Ethumb_Client.c, python...)!!! */
+   ethumb->tw = THUMB_SIZE_NORMAL;
+   ethumb->th = THUMB_SIZE_NORMAL;
+   ethumb->orientation = ETHUMB_THUMB_ORIENT_ORIGINAL;
+   ethumb->crop_x = 0.5;
+   ethumb->crop_y = 0.5;
+   ethumb->quality = 80;
+   ethumb->compress = 9;
+   ethumb->video.start = 0.1;
+   ethumb->video.time = 3;
+   ethumb->video.interval = 0.05;
+   ethumb->video.ntimes = 3;
+   ethumb->video.fps = 10;
+
+   ee = ecore_evas_buffer_new(1, 1);
+   e = ecore_evas_get(ee);
+   if (!e)
+     {
+       ERR("could not create ecore evas buffer");
+        free(ethumb);
+       return NULL;
+     }
+
+   evas_image_cache_set(e, 0);
+   evas_font_cache_set(e, 0);
+
+   o = ecore_evas_object_image_new(ee);
+   if (!o)
+     {
+       ERR("could not create sub ecore evas buffer");
+       ecore_evas_free(ee);
+       free(ethumb);
+       return NULL;
+     }
+
+   sub_ee = ecore_evas_object_ecore_evas_get(o);
+   sub_e = ecore_evas_object_evas_get(o);
+   ecore_evas_alpha_set(sub_ee, EINA_TRUE);
+
+   evas_image_cache_set(sub_e, 0);
+   evas_font_cache_set(sub_e, 0);
+
+   img = evas_object_image_add(sub_e);
+   if (!img)
+     {
+       ERR("could not create source objects.");
+       ecore_evas_free(ee);
+       free(ethumb);
+       return NULL;
+     }
+
+   ethumb->ee = ee;
+   ethumb->e = e;
+   ethumb->sub_ee = sub_ee;
+   ethumb->sub_e = sub_e;
+   ethumb->o = o;
+   ethumb->img = img;
+
+   DBG("ethumb=%p", ethumb);
+
+   return ethumb;
+}
+
+static void
+_ethumb_frame_free(Ethumb_Frame *frame)
+{
+   Evas_Object *o;
+
+   if (!frame)
+     return;
+
+   if (frame->swallow && frame->edje)
+     {
+     o = edje_object_part_swallow_get(frame->edje, frame->swallow);
+     if (o)
+       edje_object_part_unswallow(frame->edje, o);
+     }
+   eina_stringshare_del(frame->file);
+   eina_stringshare_del(frame->group);
+   eina_stringshare_del(frame->swallow);
+
+   if (frame->edje)
+     evas_object_del(frame->edje);
+
+   free(frame);
+}
+
+EAPI void
+ethumb_free(Ethumb *ethumb)
+{
+   EINA_SAFETY_ON_NULL_RETURN(ethumb);
+
+   DBG("ethumb=%p", ethumb);
+
+   if (ethumb->frame)
+     _ethumb_frame_free(ethumb->frame);
+   ethumb_file_free(ethumb);
+   evas_object_del(ethumb->o);
+   ecore_evas_free(ethumb->ee);
+   eina_stringshare_del(ethumb->thumb_dir);
+   eina_stringshare_del(ethumb->category);
+   if (ethumb->finished_idler)
+     ecore_idler_del(ethumb->finished_idler);
+   free(ethumb);
+}
+
+EAPI void
+ethumb_thumb_fdo_set(Ethumb *e, Ethumb_Thumb_FDO_Size s)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   EINA_SAFETY_ON_FALSE_RETURN(s == ETHUMB_THUMB_NORMAL ||
+                              s == ETHUMB_THUMB_LARGE);
+   DBG("ethumb=%p, size=%d", e, s);
+
+   if (s == ETHUMB_THUMB_NORMAL)
+     {
+       e->tw = THUMB_SIZE_NORMAL;
+       e->th = THUMB_SIZE_NORMAL;
+     }
+   else
+     {
+       e->tw = THUMB_SIZE_LARGE;
+       e->th = THUMB_SIZE_LARGE;
+     }
+
+   e->format = ETHUMB_THUMB_FDO;
+   e->aspect = ETHUMB_THUMB_KEEP_ASPECT;
+   e->orientation = ETHUMB_THUMB_ORIENT_ORIGINAL;
+   _ethumb_frame_free(e->frame);
+   e->frame = NULL;
+   eina_stringshare_del(e->thumb_dir);
+   eina_stringshare_del(e->category);
+   e->thumb_dir = NULL;
+   e->category = NULL;
+}
+
+EAPI void
+ethumb_thumb_size_set(Ethumb *e, int tw, int th)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   EINA_SAFETY_ON_FALSE_RETURN(tw > 0);
+   EINA_SAFETY_ON_FALSE_RETURN(th > 0);
+
+   DBG("ethumb=%p, w=%d, h=%d", e, tw, th);
+   e->tw = tw;
+   e->th = th;
+}
+
+EAPI void
+ethumb_thumb_size_get(const Ethumb *e, int *tw, int *th)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+
+   if (tw) *tw = e->tw;
+   if (th) *th = e->th;
+}
+
+EAPI void
+ethumb_thumb_format_set(Ethumb *e, Ethumb_Thumb_Format f)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   EINA_SAFETY_ON_FALSE_RETURN(f == ETHUMB_THUMB_FDO ||
+                              f == ETHUMB_THUMB_JPEG ||
+                              f == ETHUMB_THUMB_EET);
+
+   DBG("ethumb=%p, format=%d", e, f);
+   e->format = f;
+}
+
+EAPI Ethumb_Thumb_Format
+ethumb_thumb_format_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+   return e->format;
+}
+
+EAPI void
+ethumb_thumb_aspect_set(Ethumb *e, Ethumb_Thumb_Aspect a)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   EINA_SAFETY_ON_FALSE_RETURN(a == ETHUMB_THUMB_KEEP_ASPECT ||
+                              a == ETHUMB_THUMB_IGNORE_ASPECT ||
+                              a == ETHUMB_THUMB_CROP);
+
+   DBG("ethumb=%p, aspect=%d", e, a);
+   e->aspect = a;
+}
+
+EAPI Ethumb_Thumb_Aspect
+ethumb_thumb_aspect_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+   return e->aspect;
+}
+
+EAPI void
+ethumb_thumb_orientation_set(Ethumb *e, Ethumb_Thumb_Orientation o)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   EINA_SAFETY_ON_FALSE_RETURN(o == ETHUMB_THUMB_ORIENT_NONE ||
+                              o == ETHUMB_THUMB_ROTATE_90_CW ||
+                              o == ETHUMB_THUMB_ROTATE_180 ||
+                              o == ETHUMB_THUMB_ROTATE_90_CCW ||
+                              o == ETHUMB_THUMB_FLIP_HORIZONTAL ||
+                              o == ETHUMB_THUMB_FLIP_VERTICAL ||
+                              o == ETHUMB_THUMB_FLIP_TRANSPOSE ||
+                              o == ETHUMB_THUMB_FLIP_TRANSVERSE ||
+                              o == ETHUMB_THUMB_ORIENT_ORIGINAL);
+
+   DBG("ethumb=%p, orientation=%d", e, o);
+   e->orientation = o;
+}
+
+EAPI Ethumb_Thumb_Orientation
+ethumb_thumb_orientation_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+   return e->orientation;
+}
+
+EAPI void
+ethumb_thumb_crop_align_set(Ethumb *e, float x, float y)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+
+   DBG("ethumb=%p, x=%f, y=%f", e, x, y);
+   e->crop_x = x;
+   e->crop_y = y;
+}
+
+EAPI void
+ethumb_thumb_crop_align_get(const Ethumb *e, float *x, float *y)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+
+   if (x) *x = e->crop_x;
+   if (y) *y = e->crop_y;
+}
+
+EAPI void
+ethumb_thumb_quality_set(Ethumb *e, int quality)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+
+   DBG("ethumb=%p, quality=%d", e, quality);
+   e->quality = quality;
+}
+
+EAPI int
+ethumb_thumb_quality_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+   return e->quality;
+}
+
+EAPI void
+ethumb_thumb_compress_set(Ethumb *e, int compress)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+
+   DBG("ethumb=%p, compress=%d", e, compress);
+   e->compress = compress;
+}
+
+EAPI int
+ethumb_thumb_compress_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+   return e->compress;
+}
+
+EAPI Eina_Bool
+ethumb_frame_set(Ethumb *e, const char *theme_file, const char *group, const char *swallow)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+
+   Ethumb_Frame *frame;
+   frame = e->frame;
+
+   DBG("ethumb=%p, theme_file=%s, group=%s, swallow=%s",
+       e, theme_file ? theme_file : "", group ? group : "",
+       swallow ? swallow : "");
+
+   if (frame)
+     {
+       edje_object_part_unswallow(frame->edje, e->img);
+       if (!theme_file)
+         _ethumb_frame_free(frame);
+     }
+
+   if (!theme_file)
+     {
+       e->frame = NULL;
+       return EINA_TRUE;
+     }
+
+   if (!frame)
+     {
+       frame = calloc(1, sizeof(Ethumb_Frame));
+       if (!frame)
+         {
+            ERR("could not allocate Ethumb_Frame structure.");
+            return EINA_FALSE;
+         }
+
+       frame->edje = edje_object_add(e->sub_e);
+       if (!frame->edje)
+         {
+            ERR("could not create edje frame object.");
+            _ethumb_frame_free(frame);
+            e->frame = NULL;
+            return EINA_FALSE;
+         }
+     }
+
+   if (!edje_object_file_set(frame->edje, theme_file, group))
+     {
+       ERR("could not load frame theme.");
+       _ethumb_frame_free(frame);
+       e->frame = NULL;
+       return EINA_FALSE;
+     }
+
+   edje_object_part_swallow(frame->edje, swallow, e->img);
+   if (!edje_object_part_swallow_get(frame->edje, swallow))
+     {
+       ERR("could not swallow image to edje frame.");
+       _ethumb_frame_free(frame);
+       e->frame = NULL;
+       return EINA_FALSE;
+     }
+
+   eina_stringshare_replace(&frame->file, theme_file);
+   eina_stringshare_replace(&frame->group, group);
+   eina_stringshare_replace(&frame->swallow, swallow);
+
+   e->frame = frame;
+
+   return EINA_TRUE;
+}
+
+EAPI void
+ethumb_frame_get(const Ethumb *e, const char **theme_file, const char **group, const char **swallow)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+
+   if (e->frame)
+     {
+       if (theme_file) *theme_file = e->frame->file;
+       if (group) *group = e->frame->group;
+       if (swallow) *swallow = e->frame->swallow;
+     }
+   else
+     {
+       if (theme_file) *theme_file = NULL;
+       if (group) *group = NULL;
+       if (swallow) *swallow = NULL;
+     }
+}
+
+static const char *
+_ethumb_build_absolute_path(const char *path, char buf[PATH_MAX])
+{
+   char *p;
+   int len;
+
+   if (!path)
+     return NULL;
+
+   p = buf;
+
+   if (path[0] == '/')
+     strcpy(p, path);
+   else if (path[0] == '~')
+     {
+       const char *home = getenv("HOME");
+       if (!home)
+         return NULL;
+       strcpy(p, home);
+       len = strlen(p);
+       p += len;
+       p[0] = '/';
+       p++;
+       strcpy(p, path + 2);
+     }
+   else
+     {
+       if (!getcwd(p, PATH_MAX))
+         return NULL;
+       len = strlen(p);
+       p += len;
+       p[0] = '/';
+       p++;
+       strcpy(p, path);
+     }
+
+   return buf;
+}
+
+EAPI void
+ethumb_thumb_dir_path_set(Ethumb *e, const char *path)
+{
+   char buf[PATH_MAX];
+   EINA_SAFETY_ON_NULL_RETURN(e);
+
+   DBG("ethumb=%p, path=%s", e, path ? path : "");
+   path = _ethumb_build_absolute_path(path, buf);
+   eina_stringshare_replace(&e->thumb_dir, path);
+}
+
+EAPI const char *
+ethumb_thumb_dir_path_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
+
+   return e->thumb_dir;
+}
+
+EAPI void
+ethumb_thumb_category_set(Ethumb *e, const char *category)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+
+   DBG("ethumb=%p, category=%s", e, category ? category : "");
+   eina_stringshare_replace(&e->category, category);
+}
+
+EAPI const char *
+ethumb_thumb_category_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
+
+   return e->category;
+}
+
+EAPI void
+ethumb_video_start_set(Ethumb *e, float start)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   EINA_SAFETY_ON_FALSE_RETURN(start >= 0.0);
+   EINA_SAFETY_ON_FALSE_RETURN(start <= 1.0);
+
+   DBG("ethumb=%p, video_start=%f", e, start);
+   e->video.start = start;
+}
+
+EAPI float
+ethumb_video_start_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+
+   return e->video.start;
+}
+
+EAPI void
+ethumb_video_time_set(Ethumb *e, float t)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+
+   DBG("ethumb=%p, video_start=%f", e, t);
+   e->video.time = t;
+}
+
+EAPI float
+ethumb_video_time_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+
+   return e->video.time;
+}
+
+EAPI void
+ethumb_video_interval_set(Ethumb *e, float interval)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+
+   DBG("ethumb=%p, video_interval=%f", e, interval);
+   e->video.interval = interval;
+}
+
+EAPI float
+ethumb_video_interval_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+
+   return e->video.interval;
+}
+
+EAPI void
+ethumb_video_ntimes_set(Ethumb *e, unsigned int ntimes)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   EINA_SAFETY_ON_FALSE_RETURN(ntimes > 0);
+
+   DBG("ethumb=%p, video_ntimes=%d", e, ntimes);
+   e->video.ntimes = ntimes;
+}
+
+EAPI unsigned int
+ethumb_video_ntimes_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+
+   return e->video.ntimes;
+}
+
+EAPI void
+ethumb_video_fps_set(Ethumb *e, unsigned int fps)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   EINA_SAFETY_ON_FALSE_RETURN(fps > 0);
+
+   DBG("ethumb=%p, video_fps=%d", e, fps);
+   e->video.fps = fps;
+}
+
+EAPI unsigned int
+ethumb_video_fps_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+
+   return e->video.fps;
+}
+
+EAPI void
+ethumb_document_page_set(Ethumb *e, unsigned int page)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+
+   DBG("ethumb=%p, document_page=%d", e, page);
+   e->document.page = page;
+}
+
+EAPI unsigned int
+ethumb_document_page_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+
+   return e->document.page;
+}
+
+EAPI Eina_Bool
+ethumb_file_set(Ethumb *e, const char *path, const char *key)
+{
+   char buf[PATH_MAX];
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+
+   eina_stringshare_replace(&e->thumb_path, NULL);
+   eina_stringshare_replace(&e->thumb_key, NULL);
+
+   DBG("ethumb=%p, path=%s, key=%s", e, path ? path : "", key ? key : "");
+   if (path && access(path, R_OK))
+     {
+       ERR("couldn't access file \"%s\"", path);
+       return EINA_FALSE;
+     }
+
+   path = _ethumb_build_absolute_path(path, buf);
+   eina_stringshare_replace(&e->src_hash, NULL);
+   eina_stringshare_replace(&e->src_path, path);
+   eina_stringshare_replace(&e->src_key, key);
+
+   return EINA_TRUE;
+}
+
+EAPI void
+ethumb_file_get(const Ethumb *e, const char **path, const char **key)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+
+   if (path) *path = e->src_path;
+   if (key) *key = e->src_key;
+}
+
+static const char ACCEPTABLE_URI_CHARS[96] = {
+     /*      !    "    #    $    %    &    '    (    )    *    +    ,    -    .    / */ 
+     0x00,0x3F,0x20,0x20,0x28,0x00,0x2C,0x3F,0x3F,0x3F,0x3F,0x2A,0x28,0x3F,0x3F,0x1C,
+     /* 0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ? */
+     0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x38,0x20,0x20,0x2C,0x20,0x20,
+     /* @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    O */
+     0x38,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
+     /* P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _ */
+     0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x20,0x3F,
+     /* `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o */
+     0x20,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
+     /* p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~  DEL */
+     0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x3F,0x20
+};
+
+static const char *
+_ethumb_generate_hash(const char *file)
+{
+  int n;
+  MD5_CTX ctx;
+  char md5out[(2 * MD5_HASHBYTES) + 1];
+  unsigned char hash[MD5_HASHBYTES];
+  static const char hex[] = "0123456789abcdef";
+
+  char *uri;
+  char *t;
+  const unsigned char *c;
+
+#ifdef HAVE_XATTR
+  ssize_t length;
+
+  length = getxattr(file, "user.e.md5", NULL, 0);
+
+  if (length > 0)
+    {
+       char *tmp;
+
+       tmp = alloca(length);
+       length = getxattr(file, "user.e.md5", tmp, length);
+
+       /* check if we have at least something that look like a md5 hash */
+       if (length > 0 && (length == MD5_HASHBYTES * 2 + 1))
+         {
+            tmp[length] = '\0';
+            return eina_stringshare_add(tmp);
+         }
+    }
+#endif
+
+#define _check_uri_char(c) \
+  ((c) >= 32 && (c) < 128 && (ACCEPTABLE_URI_CHARS[(c) - 32] & 0x08))
+
+  EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+
+  uri = alloca(3 * strlen(file) + 9);
+  memcpy(uri, "file://", sizeof("file://") - 1);
+  t = uri + sizeof("file://") - 1;
+
+  for (c = (const unsigned char *)file; *c != '\0'; c++)
+    {
+       if (!_check_uri_char(*c))
+        {
+           *t++ = '%';
+           *t++ = hex[*c >> 4];
+           *t++ = hex[*c & 15];
+        }
+       else
+        *t++ = *c;
+    }
+  *t = '\0';
+
+#undef _check_uri_char
+
+  MD5Init (&ctx);
+  MD5Update (&ctx, (unsigned char const*)uri, (unsigned)strlen (uri));
+  MD5Final (hash, &ctx);
+
+  for (n = 0; n < MD5_HASHBYTES; n++)
+    {
+      md5out[2 * n] = hex[hash[n] >> 4];
+      md5out[2 * n + 1] = hex[hash[n] & 0x0f];
+    }
+  md5out[2 * n] = '\0';
+
+#ifdef HAVE_XATTR
+  setxattr(file, "user.e.md5", md5out, 2 * n + 1, 0);
+#endif
+
+  DBG("md5=%s, file=%s", md5out, file);
+  return eina_stringshare_add(md5out);
+}
+
+static int
+_ethumb_file_check_fdo(Ethumb *e)
+{
+   if (!((e->tw == THUMB_SIZE_NORMAL && e->th == THUMB_SIZE_NORMAL) ||
+       (e->tw == THUMB_SIZE_LARGE && e->th == THUMB_SIZE_LARGE)))
+     return 0;
+
+   if (e->format != ETHUMB_THUMB_FDO)
+     return 0;
+
+   if (e->aspect != ETHUMB_THUMB_KEEP_ASPECT)
+     return 0;
+
+   if (e->frame)
+     return 0;
+
+   return 1;
+}
+
+static const char *
+_ethumb_file_generate_custom_category(Ethumb *e)
+{
+   char buf[PATH_MAX];
+   const char *aspect, *format;
+   const char *frame;
+
+   if (e->aspect == ETHUMB_THUMB_KEEP_ASPECT)
+     aspect = "keep_aspect";
+   else if (e->aspect == ETHUMB_THUMB_IGNORE_ASPECT)
+     aspect = "ignore_aspect";
+   else
+     aspect = "crop";
+
+   if (e->format == ETHUMB_THUMB_FDO)
+     format = "png";
+   else if (e->format == ETHUMB_THUMB_JPEG)
+     format = "jpg";
+   else
+     format = "eet";
+
+   if (e->frame)
+     frame = "-framed";
+   else
+     frame = "";
+
+   snprintf(buf, sizeof(buf), "%dx%d-%s%s-%s",
+           e->tw, e->th, aspect, frame, format);
+
+   return eina_stringshare_add(buf);
+}
+
+static void
+_ethumb_file_generate_path(Ethumb *e)
+{
+   char buf[PATH_MAX];
+   const char *thumb_dir, *category;
+   const char *ext;
+   int fdo_format;
+
+   fdo_format = _ethumb_file_check_fdo(e);
+
+   if (e->thumb_dir)
+     thumb_dir = eina_stringshare_ref(e->thumb_dir);
+   else
+     thumb_dir = eina_stringshare_ref(_home_thumb_dir);
+
+   if (e->category)
+     category = eina_stringshare_ref(e->category);
+   else if (!fdo_format)
+     category = _ethumb_file_generate_custom_category(e);
+   else
+     {
+       if (e->tw == THUMB_SIZE_NORMAL)
+         category = eina_stringshare_ref(_thumb_category_normal);
+       else if (e->tw == THUMB_SIZE_LARGE)
+         category = eina_stringshare_ref(_thumb_category_large);
+       else
+         {
+            ERR("fdo_format but size %d is not NORMAL (%d) or LARGE (%d)?",
+                e->tw, THUMB_SIZE_NORMAL, THUMB_SIZE_LARGE);
+            category = "unknown";
+         }
+     }
+
+   if (e->format == ETHUMB_THUMB_FDO)
+     ext = "png";
+   else if (e->format == ETHUMB_THUMB_JPEG)
+     ext = "jpg";
+   else
+     ext = "eet";
+
+   if (!e->src_hash)
+     {
+       char *fullname;
+
+       fullname = ecore_file_realpath(e->src_path);
+       e->src_hash = _ethumb_generate_hash(fullname);
+       free(fullname);
+     }
+   snprintf(buf, sizeof(buf), "%s/%s/%s.%s", thumb_dir, category, e->src_hash, ext);
+   DBG("ethumb=%p, path=%s", e, buf);
+   eina_stringshare_replace(&e->thumb_path, buf);
+   if (e->format == ETHUMB_THUMB_EET)
+     eina_stringshare_replace(&e->thumb_key, "thumbnail");
+   else
+     {
+       eina_stringshare_del(e->thumb_key);
+       e->thumb_key = NULL;
+     }
+
+   eina_stringshare_del(thumb_dir);
+   eina_stringshare_del(category);
+}
+
+EAPI void
+ethumb_file_free(Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   DBG("ethumb=%p", e);
+
+   eina_stringshare_replace(&e->src_hash, NULL);
+   eina_stringshare_replace(&e->src_path, NULL);
+   eina_stringshare_replace(&e->src_key, NULL);
+   eina_stringshare_replace(&e->thumb_path, NULL);
+   eina_stringshare_replace(&e->thumb_key, NULL);
+}
+
+EAPI void
+ethumb_thumb_path_set(Ethumb *e, const char *path, const char *key)
+{
+   char buf[PATH_MAX];
+
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   DBG("ethumb=%p, path=%s, key=%s", e, path ? path : "", key ? key : "");
+
+   if (!path)
+     {
+       eina_stringshare_replace(&e->thumb_path, NULL);
+       eina_stringshare_replace(&e->thumb_key, NULL);
+     }
+   else
+     {
+       path = _ethumb_build_absolute_path(path, buf);
+       eina_stringshare_replace(&e->thumb_path, path);
+       eina_stringshare_replace(&e->thumb_key, key);
+     }
+}
+
+EAPI void
+ethumb_thumb_path_get(Ethumb *e, const char **path, const char **key)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   if (!e->thumb_path)
+     _ethumb_file_generate_path(e);
+
+   if (path) *path = e->thumb_path;
+   if (key) *key = e->thumb_key;
+}
+
+EAPI void
+ethumb_thumb_hash(Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   if (!e->src_hash)
+     {
+        char *fullname;
+
+        fullname = ecore_file_realpath(e->src_path);
+        e->src_hash = _ethumb_generate_hash(fullname);
+        free(fullname);
+     }
+}
+
+EAPI void
+ethumb_thumb_hash_copy(Ethumb *dst, const Ethumb *src)
+{
+   EINA_SAFETY_ON_NULL_RETURN(dst);
+   EINA_SAFETY_ON_NULL_RETURN(src);
+
+   if (src == dst) return ;
+
+   eina_stringshare_del(dst->src_hash);
+   dst->src_hash = eina_stringshare_ref(src->src_hash);
+}
+
+EAPI void
+ethumb_calculate_aspect_from_ratio(Ethumb *e, float ia, int *w, int *h)
+{
+   float a;
+
+   *w = e->tw;
+   *h = e->th;
+
+   if (ia == 0)
+     return;
+
+   a = e->tw / (float)e->th;
+
+   if (e->aspect == ETHUMB_THUMB_KEEP_ASPECT)
+     {
+       if ((ia > a && e->tw > 0) || e->th <= 0)
+         *h = e->tw / ia;
+       else
+         *w = e->th * ia;
+     }
+}
+
+EAPI void
+ethumb_calculate_aspect(Ethumb *e, int iw, int ih, int *w, int *h)
+{
+   float ia;
+
+   ia = iw / (float)ih;
+
+   ethumb_calculate_aspect_from_ratio(e, ia, w, h);
+}
+
+EAPI void
+ethumb_calculate_fill_from_ratio(Ethumb *e, float ia, int *fx, int *fy, int *fw, int *fh)
+{
+   float a;
+
+   *fw = e->tw;
+   *fh = e->th;
+   *fx = 0;
+   *fy = 0;
+
+   if (ia == 0)
+     return;
+
+   a = e->tw / (float)e->th;
+
+   if (e->aspect == ETHUMB_THUMB_CROP)
+     {
+       if ((ia > a && e->tw > 0) || e->th <= 0)
+         *fw = e->th * ia;
+       else
+         *fh = e->tw / ia;
+
+       *fx = - e->crop_x * (*fw - e->tw);
+       *fy = - e->crop_y * (*fh - e->th);
+     }
+   else if (e->aspect == ETHUMB_THUMB_KEEP_ASPECT)
+     {
+       if ((ia > a && e->tw > 0) || e->th <= 0)
+         *fh = e->tw / ia;
+       else
+         *fw = e->th * ia;
+     }
+}
+
+EAPI void
+ethumb_calculate_fill(Ethumb *e, int iw, int ih, int *fx, int *fy, int *fw, int *fh)
+{
+   float ia;
+   ia = iw / (float)ih;
+
+   ethumb_calculate_fill_from_ratio(e, ia, fx, fy, fw, fh);
+}
+
+static Eina_Bool
+_ethumb_plugin_generate(Ethumb *e)
+{
+   const char *extp;
+   char ext[PATH_MAX];
+   Ethumb_Plugin *plugin;
+   int i;
+
+   extp = strrchr(e->src_path, '.');
+   if (!extp)
+     {
+       ERR("could not get extension for file \"%s\"", e->src_path);
+       return EINA_FALSE;
+     }
+
+   for (i = 0; extp[i] != '\0'; i++)
+       ext[i] = tolower(extp[i + 1]);
+
+   plugin = eina_hash_find(_plugins_ext, ext);
+   if (!plugin)
+     {
+       DBG("no plugin for extension: \"%s\"", ext);
+       return EINA_FALSE;
+     }
+
+   if (e->frame)
+     evas_object_hide(e->frame->edje);
+   else
+     evas_object_hide(e->img);
+
+   e->plugin = plugin;
+   e->pdata = plugin->thumb_generate(e);
+
+   return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+ethumb_plugin_image_resize(Ethumb *e, int w, int h)
+{
+   Evas_Object *img;
+
+   img = e->img;
+
+   if (e->frame)
+     {
+       edje_extern_object_min_size_set(img, w, h);
+       edje_extern_object_max_size_set(img, w, h);
+       edje_object_calc_force(e->frame->edje);
+       evas_object_move(e->frame->edje, 0, 0);
+       evas_object_resize(e->frame->edje, w, h);
+     }
+   else
+     {
+       evas_object_move(img, 0, 0);
+       evas_object_resize(img, w, h);
+     }
+
+   evas_object_image_size_set(e->o, w, h);
+   ecore_evas_resize(e->sub_ee, w, h);
+
+   e->rw = w;
+   e->rh = h;
+
+   return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+ethumb_image_save(Ethumb *e)
+{
+   Eina_Bool r;
+   char *dname;
+   char flags[256];
+
+   evas_damage_rectangle_add(e->sub_e, 0, 0, e->rw, e->rh);
+   evas_render(e->sub_e);
+
+   if (!e->thumb_path)
+     _ethumb_file_generate_path(e);
+
+   if (!e->thumb_path)
+     {
+       ERR("could not create file path...");
+       return EINA_FALSE;
+     }
+
+   dname = ecore_file_dir_get(e->thumb_path);
+   r = ecore_file_mkpath(dname);
+   free(dname);
+   if (!r)
+     {
+       ERR("could not create directory '%s'", dname);
+       return EINA_FALSE;
+     }
+
+   snprintf(flags, sizeof(flags), "quality=%d compress=%d",
+           e->quality, e->compress);
+   r = evas_object_image_save(e->o, e->thumb_path, e->thumb_key, flags);
+
+   if (!r)
+     {
+       ERR("could not save image: path=%s, key=%s", e->thumb_path,
+           e->thumb_key);
+       return EINA_FALSE;
+     }
+
+   return EINA_TRUE;
+}
+
+static void
+_ethumb_image_orient(Ethumb *e, int orientation)
+{
+   Evas_Object *img = e->img, *tmp;
+   unsigned int *data, *data2, *to, *from, *p1, *p2, pt;
+   int x, y, w, hw, iw, ih, tw, th;
+   const char *file, *key;
+
+   evas_object_image_size_get(img, &iw, &ih);
+   evas_object_image_load_size_get(img, &tw, &th);
+   evas_object_image_file_get(img, &file, &key);
+   data = evas_object_image_data_get(img, 1);
+
+   switch (orientation)
+     {
+      case ETHUMB_THUMB_FLIP_HORIZONTAL:
+        for (y = 0; y < ih; y++)
+          {
+             p1 = data + (y * iw);
+             p2 = data + ((y + 1) * iw) - 1;
+             for (x = 0; x < (iw >> 1); x++)
+               {
+                  pt = *p1;
+                  *p1 = *p2;
+                  *p2 = pt;
+                  p1++;
+                  p2--;
+               }
+          }
+        evas_object_image_data_set(img, data);
+        evas_object_image_data_update_add(img, 0, 0, iw, ih);
+        return;
+      case ETHUMB_THUMB_FLIP_VERTICAL:
+        for (y = 0; y < (ih >> 1); y++)
+          {
+             p1 = data + (y * iw);
+             p2 = data + ((ih - 1 - y) * iw);
+             for (x = 0; x < iw; x++)
+               {
+                  pt = *p1;
+                  *p1 = *p2;
+                  *p2 = pt;
+                  p1++;
+                  p2++;
+               }
+          }
+        evas_object_image_data_set(img, data);
+        evas_object_image_data_update_add(img, 0, 0, iw, ih);
+        return;
+      case ETHUMB_THUMB_ROTATE_180:
+        hw = iw * ih;
+        x = (hw / 2);
+        p1 = data;
+        p2 = data + hw - 1;
+        for (; --x > 0;)
+          {
+             pt = *p1;
+             *p1 = *p2;
+             *p2 = pt;
+             p1++;
+             p2--;
+          }
+        evas_object_image_data_set(img, data);
+        evas_object_image_data_update_add(img, 0, 0, iw, ih);
+        return;
+     }
+
+   tmp = evas_object_image_add(evas_object_evas_get(img));
+   evas_object_image_load_size_set(tmp, tw, th);
+   evas_object_image_file_set(tmp, file, key);
+   data2 = evas_object_image_data_get(tmp, 0);
+
+   w = ih;
+   ih = iw;
+   iw = w;
+   hw = w * ih;
+
+   evas_object_image_size_set(img, iw, ih);
+   data = evas_object_image_data_get(img, 1);
+
+   switch (orientation)
+     {
+      case ETHUMB_THUMB_FLIP_TRANSPOSE:
+        to = data;
+        hw = -hw + 1;
+        break;
+      case ETHUMB_THUMB_FLIP_TRANSVERSE:
+        to = data + hw - 1;
+        w = -w;
+        hw = hw - 1;
+        break;
+      case ETHUMB_THUMB_ROTATE_90_CW:
+        to = data + w - 1;
+        hw = -hw - 1;
+        break;
+      case ETHUMB_THUMB_ROTATE_90_CCW:
+        to = data + hw - w;
+        w = -w;
+        hw = hw + 1;
+        break;
+      default:
+        ERR("unknown orient %d", orientation);
+        evas_object_del(tmp);
+        evas_object_image_data_set(img, data); // give it back
+        return;
+     }
+   from = data2;
+   for (x = iw; --x >= 0;)
+     {
+        for (y = ih; --y >= 0;)
+          {
+             *to = *from;
+             from++;
+             to += w;
+          }
+        to += hw;
+     }
+   evas_object_del(tmp);
+   evas_object_image_data_set(img, data);
+   evas_object_image_data_update_add(img, 0, 0, iw, ih);
+}
+
+static int
+_ethumb_image_load(Ethumb *e)
+{
+   int error;
+   Evas_Coord w, h, ww, hh, fx, fy, fw, fh;
+   Evas_Object *img;
+   int orientation = ETHUMB_THUMB_ORIENT_NONE;
+
+   img = e->img;
+
+   if (e->frame)
+     evas_object_hide(e->frame->edje);
+   else
+     evas_object_hide(img);
+   evas_object_image_file_set(img, NULL, NULL);
+   evas_object_image_load_size_set(img, e->tw, e->th);
+   evas_object_image_file_set(img, e->src_path, e->src_key);
+
+   if (e->frame)
+     evas_object_show(e->frame->edje);
+   else
+     evas_object_show(img);
+
+   error = evas_object_image_load_error_get(img);
+   if (error != EVAS_LOAD_ERROR_NONE)
+     {
+       ERR("could not load image '%s': %d", e->src_path, error);
+       return 0;
+     }
+
+   if (e->orientation == ETHUMB_THUMB_ORIENT_ORIGINAL)
+      {
+#ifdef HAVE_LIBEXIF
+        ExifData  *exif = exif_data_new_from_file(e->src_path);
+        ExifEntry *entry = NULL;
+        ExifByteOrder bo;
+        int o = 0;
+
+        if (exif)
+          {
+             entry = exif_data_get_entry(exif, EXIF_TAG_ORIENTATION);
+             if (entry)
+               {
+                  bo = exif_data_get_byte_order(exif);
+                  o = exif_get_short(entry->data, bo);
+               }
+             exif_data_free(exif);
+             switch (o)
+               {
+                case 2:
+                   orientation = ETHUMB_THUMB_FLIP_HORIZONTAL;
+                   break;
+                case 3:
+                   orientation = ETHUMB_THUMB_ROTATE_180;
+                   break;
+                case 4:
+                   orientation = ETHUMB_THUMB_FLIP_VERTICAL;
+                   break;
+                case 5:
+                   orientation = ETHUMB_THUMB_FLIP_TRANSPOSE;
+                   break;
+                case 6:
+                   orientation = ETHUMB_THUMB_ROTATE_90_CW;
+                   break;
+                case 7:
+                   orientation = ETHUMB_THUMB_FLIP_TRANSVERSE;
+                   break;
+                case 8:
+                   orientation = ETHUMB_THUMB_ROTATE_90_CCW;
+                   break;
+               }
+          }
+#endif
+   }
+
+   if (orientation != ETHUMB_THUMB_ORIENT_NONE)
+     _ethumb_image_orient(e, orientation);
+
+   evas_object_image_size_get(img, &w, &h);
+   if ((w <= 0) || (h <= 0))
+     return 0;
+
+   ethumb_calculate_aspect(e, w, h, &ww, &hh);
+
+   if (e->frame)
+     {
+       edje_extern_object_min_size_set(img, ww, hh);
+       edje_extern_object_max_size_set(img, ww, hh);
+       edje_object_calc_force(e->frame->edje);
+       evas_object_move(e->frame->edje, 0, 0);
+       evas_object_resize(e->frame->edje, ww, hh);
+     }
+   else
+     {
+       evas_object_move(img, 0, 0);
+       evas_object_resize(img, ww, hh);
+     }
+
+   ethumb_calculate_fill(e, w, h, &fx, &fy, &fw, &fh);
+   evas_object_image_fill_set(img, fx, fy, fw, fh);
+
+   evas_object_image_size_set(e->o, ww, hh);
+   ecore_evas_resize(e->sub_ee, ww, hh);
+
+   e->rw = ww;
+   e->rh = hh;
+
+   return 1;
+}
+
+static Eina_Bool
+_ethumb_finished_idler_cb(void *data)
+{
+   Ethumb *e = data;
+
+   e->finished_cb(e->cb_data, e, e->cb_result);
+   if (e->cb_data_free)
+     e->cb_data_free(e->cb_data);
+   e->finished_idler = NULL;
+   e->finished_cb = NULL;
+   e->cb_data = NULL;
+   e->cb_data_free = NULL;
+
+   return EINA_FALSE;
+}
+
+EAPI void
+ethumb_finished_callback_call(Ethumb *e, int result)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+
+   e->cb_result = result;
+   if (e->finished_idler)
+     ecore_idler_del(e->finished_idler);
+   e->finished_idler = ecore_idler_add(_ethumb_finished_idler_cb, e);
+   e->plugin = NULL;
+   e->pdata = NULL;
+}
+
+EAPI Eina_Bool
+ethumb_generate(Ethumb *e, Ethumb_Generate_Cb finished_cb, const void *data, Eina_Free_Cb free_data)
+{
+   int r;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(finished_cb, 0);
+   DBG("ethumb=%p, finished_cb=%p, data=%p, free_data=%p, path=%s, key=%s",
+       e, finished_cb, data, free_data,
+       e->src_path ? e->src_path : "", e->src_key ? e->src_key : "");
+
+   if (e->finished_idler)
+     {
+       ERR("thumbnail generation already in progress.");
+       return EINA_FALSE;
+     }
+   if (e->pdata)
+     {
+        e->plugin->thumb_cancel(e, e->pdata);
+        e->pdata = NULL;
+        e->plugin = NULL;
+     }
+
+   e->finished_cb = finished_cb;
+   e->cb_data = (void *)data;
+   e->cb_data_free = free_data;
+
+   if (!e->src_path)
+     {
+       ERR("no file set.");
+       ethumb_finished_callback_call(e, 0);
+       return EINA_FALSE;
+     }
+
+   r = _ethumb_plugin_generate(e);
+   fprintf(stderr, "ethumb generate: %i: %p\n", r, e->pdata);
+   if (r)
+     {
+        return EINA_TRUE;
+     }
+
+   if (!_ethumb_image_load(e))
+     {
+       ERR("could not load input image.");
+       ethumb_finished_callback_call(e, 0);
+       return EINA_FALSE;
+     }
+
+   r = ethumb_image_save(e);
+
+   ethumb_finished_callback_call(e, r);
+
+   return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+ethumb_exists(Ethumb *e)
+{
+   struct stat thumb, src;
+   int r_thumb, r_src;
+   Eina_Bool r = EINA_FALSE;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e->src_path, 0);
+   DBG("ethumb=%p, path=%s", e, e->src_path ? e->src_path : "");
+
+   if (!e->thumb_path)
+     _ethumb_file_generate_path(e);
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e->thumb_path, 0);
+
+   r_thumb = stat(e->thumb_path, &thumb);
+   r_src = stat(e->src_path, &src);
+
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(r_src, 0);
+
+   if (r_thumb && errno != ENOENT)
+     ERR("could not access file \"%s\": %s", e->thumb_path, strerror(errno));
+   else if (!r_thumb && thumb.st_mtime > src.st_mtime)
+     r = EINA_TRUE;
+
+   return r;
+}
+
+EAPI Evas *
+ethumb_evas_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
+
+   return e->sub_e;
+}
+
+EAPI Ecore_Evas *
+ethumb_ecore_evas_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
+
+   return e->sub_ee;
+}
+
+EAPI Ethumb *
+ethumb_dup(const Ethumb *e)
+{
+   Ecore_Evas *ee;
+   Ecore_Evas *sub_ee;
+   Evas *ev;
+   Evas *sub_ev;
+   Evas_Object *o;
+   Evas_Object *img;
+   Ethumb *r;
+
+   r = malloc(sizeof (Ethumb));
+   if (!r) return NULL;
+
+   memcpy(r, e, sizeof (Ethumb));
+
+   r->thumb_dir = eina_stringshare_ref(e->thumb_dir);
+   r->category = eina_stringshare_ref(e->category);
+   r->src_hash = eina_stringshare_ref(e->src_hash);
+   r->src_path = eina_stringshare_ref(e->src_path);
+   r->src_key = eina_stringshare_ref(e->src_key);
+   r->thumb_path = eina_stringshare_ref(e->thumb_path);
+   r->thumb_key = eina_stringshare_ref(e->thumb_key);
+
+   ee = ecore_evas_buffer_new(1, 1);
+   ev = ecore_evas_get(ee);
+   if (!ev)
+     {
+        ERR("could not create ecore evas buffer");
+        free(r);
+        return NULL;
+     }
+
+   evas_image_cache_set(ev, 0);
+   evas_font_cache_set(ev, 0);
+
+   o = ecore_evas_object_image_new(ee);
+   if (!o)
+     {
+        ERR("could not create sub ecore evas buffer");
+        ecore_evas_free(ee);
+        free(r);
+        return NULL;
+     }
+
+   sub_ee = ecore_evas_object_ecore_evas_get(o);
+   sub_ev = ecore_evas_object_evas_get(o);
+   ecore_evas_alpha_set(sub_ee, EINA_TRUE);
+
+   evas_image_cache_set(sub_ev, 0);
+   evas_font_cache_set(sub_ev, 0);
+
+   img = evas_object_image_add(sub_ev);
+   if (!img)
+     {
+        ERR("could not create source objects.");
+        ecore_evas_free(ee);
+        free(r);
+        return NULL;
+     }
+
+   r->ee = ee;
+   r->sub_ee = sub_ee;
+   r->e = ev;
+   r->sub_e = sub_ev;
+   r->o = o;
+   r->img = img;
+
+   r->frame = NULL;
+   r->finished_idler = NULL;
+   r->finished_cb = NULL;
+   r->cb_data = NULL;
+   r->cb_data_free = NULL;
+   r->cb_result = 0;
+   r->plugin = NULL;
+   r->pdata = NULL;
+
+   return r;
+}
+
+#define CHECK_DELTA(Param)                      \
+  if (e1->Param != e2->Param)                   \
+    return EINA_TRUE;
+
+EAPI Eina_Bool
+ethumb_cmp(const Ethumb *e1, const Ethumb *e2)
+{
+   CHECK_DELTA(thumb_dir);
+   CHECK_DELTA(category);
+   CHECK_DELTA(tw);
+   CHECK_DELTA(th);
+   CHECK_DELTA(format);
+   CHECK_DELTA(aspect);
+   CHECK_DELTA(orientation);
+   CHECK_DELTA(crop_x);
+   CHECK_DELTA(crop_y);
+   CHECK_DELTA(quality);
+   CHECK_DELTA(compress);
+   CHECK_DELTA(rw);
+   CHECK_DELTA(rh);
+   CHECK_DELTA(video.start);
+   CHECK_DELTA(video.time);
+   CHECK_DELTA(video.interval);
+   CHECK_DELTA(video.ntimes);
+   CHECK_DELTA(video.fps);
+   CHECK_DELTA(document.page);
+
+   return EINA_FALSE;
+}
+
+EAPI unsigned int
+ethumb_length(__UNUSED__ const void *key)
+{
+   return sizeof (Ethumb);
+}
+
+#define CMP_PARAM(Param)                       \
+  if (e1->Param != e2->Param)                  \
+    return e1->Param - e2->Param;
+
+EAPI int
+ethumb_key_cmp(const void *key1, __UNUSED__ int key1_length,
+               const void *key2, __UNUSED__ int key2_length)
+{
+   const Ethumb *e1 = key1;
+   const Ethumb *e2 = key2;
+
+   CMP_PARAM(thumb_dir);
+   CMP_PARAM(category);
+   CMP_PARAM(tw);
+   CMP_PARAM(th);
+   CMP_PARAM(format);
+   CMP_PARAM(aspect);
+   CMP_PARAM(orientation);
+   CMP_PARAM(crop_x);
+   CMP_PARAM(crop_y);
+   CMP_PARAM(quality);
+   CMP_PARAM(compress);
+   CMP_PARAM(rw);
+   CMP_PARAM(rh);
+   CMP_PARAM(video.start);
+   CMP_PARAM(video.time);
+   CMP_PARAM(video.interval);
+   CMP_PARAM(video.ntimes);
+   CMP_PARAM(video.fps);
+   CMP_PARAM(document.page);
+   CMP_PARAM(src_path);
+   CMP_PARAM(src_key);
+
+   return 0;
+}
+
+#undef CMP_PARAM
+
+#define HASH_PARAM_I(Param) r ^= eina_hash_int32((unsigned int*) &e->Param, 0);
+#ifdef __LP64__
+# define HASH_PARAM_P(Param) r ^= eina_hash_int64((unsigned long int*) &e->Param, 0);
+#else
+# define HASH_PARAM_P(Param) r ^= eina_hash_int32((unsigned int*) &e->Param, 0);
+#endif
+#define HASH_PARAM_D(Param) r ^= eina_hash_int64((unsigned long int*)&e->Param, 0);
+#define HASH_PARAM_F(Param) r ^= eina_hash_int32((unsigned int*) &e->Param, 0);
+
+EAPI int
+ethumb_hash(const void *key, int key_length __UNUSED__)
+{
+   const Ethumb *e = key;
+   int r = 0;
+
+   HASH_PARAM_P(thumb_dir);
+   HASH_PARAM_P(category);
+   HASH_PARAM_I(tw);
+   HASH_PARAM_I(th);
+   HASH_PARAM_I(format);
+   HASH_PARAM_I(aspect);
+   HASH_PARAM_I(orientation);
+   HASH_PARAM_F(crop_x);
+   HASH_PARAM_F(crop_y);
+   HASH_PARAM_I(quality);
+   HASH_PARAM_I(compress);
+   HASH_PARAM_P(src_path);
+   HASH_PARAM_P(src_key);
+   HASH_PARAM_I(rw);
+   HASH_PARAM_I(rh);
+   HASH_PARAM_D(video.start);
+   HASH_PARAM_D(video.time);
+   HASH_PARAM_D(video.interval);
+   HASH_PARAM_I(video.ntimes);
+   HASH_PARAM_I(video.fps);
+   HASH_PARAM_I(document.page);
+
+   return r;
+}
diff --git a/src/lib/ethumb_private.h b/src/lib/ethumb_private.h
new file mode 100644 (file)
index 0000000..49e4de9
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef __ETHUMB_PRIVATE_H__
+#define __ETHUMB_PRIVATE_H__ 1
+
+#include <Ethumb.h>
+#include <Ethumb_Plugin.h>
+
+typedef struct _Ethumb_Frame Ethumb_Frame;
+
+struct _Ethumb_Frame
+{
+   const char *file;
+   const char *group;
+   const char *swallow;
+   Evas_Object *edje;
+};
+
+struct _Ethumb
+{
+   const char *thumb_dir;
+   const char *category;
+   int tw, th;
+   int format;
+   int aspect;
+   int orientation;
+   float crop_x, crop_y;
+   int quality;
+   int compress;
+   const char *src_hash;
+   const char *src_path;
+   const char *src_key;
+   const char *thumb_path;
+   const char *thumb_key;
+   int rw, rh;
+   struct
+     {
+       double start, time, interval;
+       unsigned int ntimes, fps;
+     } video;
+   struct
+     {
+       unsigned int page;
+     } document;
+   Ethumb_Frame *frame;
+   Ecore_Evas *ee, *sub_ee;
+   Evas *e, *sub_e;
+   Evas_Object *o, *img;
+   Ecore_Idler *finished_idler;
+   Ethumb_Generate_Cb finished_cb;
+   void *cb_data;
+   Eina_Free_Cb cb_data_free;
+   int cb_result;
+
+   void *pdata;
+   Ethumb_Plugin *plugin;
+};
+
+#endif /* __ETHUMB_PRIVATE_H__ */
diff --git a/src/lib/md5.c b/src/lib/md5.c
new file mode 100644 (file)
index 0000000..b62a9ff
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>            /* for memcpy() */
+#include "md5.h"
+
+#if (__BYTE_ORDER == 1234)
+#define byteReverse(buf, len)  /* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+    uint32_t t;
+    do {
+       t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+           ((unsigned) buf[1] << 8 | buf[0]);
+       *(uint32_t *) buf = t;
+       buf += 4;
+    } while (--longs);
+}
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(MD5_CTX *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len)
+{
+    uint32_t t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+       ctx->bits[1]++;         /* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;       /* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+       unsigned char *p = (unsigned char *) ctx->in + t;
+
+       t = 64 - t;
+       if (len < t) {
+           memcpy(p, buf, len);
+           return;
+       }
+       memcpy(p, buf, t);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+       buf += t;
+       len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+       memcpy(ctx->in, buf, 64);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+       buf += 64;
+       len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], MD5_CTX *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+       /* Two lots of padding:  Pad the first block to 64 bytes */
+       memset(p, 0, count);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+       /* Now fill the next block with 56 bytes */
+       memset(ctx->in, 0, 56);
+    } else {
+       /* Pad block to 56 bytes */
+       memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((uint32_t *) ctx->in)[14] = ctx->bits[0];
+    ((uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memcpy(digest, ctx->buf, 16);
+    memset((char *) ctx, 0, sizeof(ctx));      /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+    register uint32_t a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
diff --git a/src/lib/md5.h b/src/lib/md5.h
new file mode 100644 (file)
index 0000000..348fcd6
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef _MD5_H_
+#define _MD5_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#define MD5_HASHBYTES 16
+
+typedef struct MD5Context {
+       uint32_t buf[4];
+       uint32_t bits[2];
+       unsigned char in[64];
+} MD5_CTX;
+
+extern void   MD5Init(MD5_CTX *context);
+extern void   MD5Update(MD5_CTX *context,unsigned char const *buf,unsigned len);
+extern void   MD5Final(unsigned char digest[MD5_HASHBYTES], MD5_CTX *context);
+
+extern void   MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+extern char  *MD5End(MD5_CTX *, char *);
+extern char  *MD5File(const char *, char *);
+extern char  *MD5Data (const unsigned char *, unsigned int, char *);
+
+#endif
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
new file mode 100644 (file)
index 0000000..2e5d034
--- /dev/null
@@ -0,0 +1,15 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS =
+
+if USE_MODULE_EMOTION
+SUBDIRS += emotion
+endif
+
+if USE_MODULE_EPDF
+SUBDIRS += epdf
+endif
+
+DIST_SUBDIRS = \
+       emotion \
+       epdf
diff --git a/src/plugins/emotion/Makefile.am b/src/plugins/emotion/Makefile.am
new file mode 100644 (file)
index 0000000..0c4565f
--- /dev/null
@@ -0,0 +1,49 @@
+
+MAINTAINERCLEANFILES = Makefile.in
+
+EDJE_CC = ${edje_cc}
+EDJE_FLAGS = -v
+
+pluginsdir = $(libdir)/ethumb/plugins
+filesdir = $(pluginsdir)/data
+files_DATA = emotion_template.edj
+
+EXTRA_DIST= \
+      template.edc
+
+AM_CPPFLAGS = \
+-I$(top_srcdir)/src/lib \
+-I$(top_srcdir)/src/plugins/emotion \
+-DPLUGINSDIR=\"$(pluginsdir)\" \
+@ECORE_EVAS_CFLAGS@ \
+@EET_CFLAGS@ \
+@EFL_ETHUMB_BUILD@ \
+@EVAS_CFLAGS@ \
+@EINA_CFLAGS@ \
+@EMOTION_CFLAGS@ \
+@ECORE_FILE_CFLAGS@ \
+@EDJE_CFLAGS@
+
+pkgdir = $(pluginsdir)
+pkg_LTLIBRARIES = emotion.la
+emotion_la_SOURCES = emotion.c
+emotion_la_LIBADD = \
+$(top_builddir)/src/lib/libethumb.la \
+@ECORE_EVAS_LIBS@ \
+@EET_LIBS@ \
+@EMOTION_LIBS@ \
+@EDJE_LIBS@ \
+@ECORE_FILE_LIBS@ \
+@EVAS_LIBS@ \
+@EINA_LIBS@
+emotion_la_LDFLAGS = -module -avoid-version
+emotion_la_LIBTOOLFLAGS = --tag=disable-static
+
+
+emotion_template.edj: Makefile $(EXTRADIST)
+       $(EDJE_CC) $(EDJE_FLAGS) \
+               $(top_srcdir)/src/plugins/emotion/template.edc \
+               $(top_builddir)/src/plugins/emotion/emotion_template.edj
+
+clean-local:
+       rm -f *.edj
diff --git a/src/plugins/emotion/emotion.c b/src/plugins/emotion/emotion.c
new file mode 100644 (file)
index 0000000..560e661
--- /dev/null
@@ -0,0 +1,409 @@
+#include "Ethumb.h"
+#include "Ethumb_Plugin.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define EDJE_EDIT_IS_UNSTABLE_AND_I_KNOW_ABOUT_IT 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <Eina.h>
+#include <Eet.h>
+#include <Ecore_File.h>
+#include <Evas.h>
+#include <Ecore.h>
+#include <Edje.h>
+#include <Edje_Edit.h>
+#include <Emotion.h>
+
+static int _log_dom = -1;
+#define DBG(...) EINA_LOG_DOM_DBG(_log_dom, __VA_ARGS__)
+#define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__)
+#define WRN(...) EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
+#define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__)
+
+struct _emotion_plugin
+{
+   unsigned int fps;
+   double ptotal, len, pi;
+   double total_time, tmp_time;
+   unsigned int pcount;
+   unsigned int frnum;
+   Eina_Bool first;
+   Eet_File *ef;
+   Evas_Object *video;
+   Ethumb *e;
+   int w, h;
+};
+
+static Eina_Bool _frame_grab(void *data);
+static Eina_Bool _frame_grab_single(void *data);
+
+static void
+_resize_movie(struct _emotion_plugin *_plugin)
+{
+   Ethumb *e = _plugin->e;
+   double ratio;
+   int w, h;
+   int fx, fy, fw, fh;
+
+   ratio = emotion_object_ratio_get(_plugin->video);
+   ethumb_calculate_aspect_from_ratio(e, ratio, &w, &h);
+   ethumb_calculate_fill_from_ratio(e, ratio, &fx, &fy, &fw, &fh);
+   DBG("size: w=%d, h=%d fill: x=%d, y=%d, w=%d, h=%d", w, h, fx, fy, fw, fh);
+
+   _plugin->w = w;
+   _plugin->h = h;
+
+   ethumb_plugin_image_resize(e, _plugin->w, _plugin->h);
+
+   evas_object_resize(_plugin->video, fw, fh);
+   evas_object_move(_plugin->video, fx, fy);
+   emotion_object_audio_mute_set(_plugin->video, 1);
+}
+
+static void
+_frame_decode_cb(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__)
+{
+   struct _emotion_plugin *_plugin = data;
+
+   if (_plugin->ef)
+     _frame_grab(data);
+   else
+     _frame_grab_single(data);
+}
+
+static void
+_frame_resized_cb(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__)
+{
+   _resize_movie(data);
+}
+
+static void
+_video_stopped_cb(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__)
+{
+   struct _emotion_plugin *_plugin = data;
+
+   _plugin->pi = 0;
+   _plugin->ptotal = 0;
+   _plugin->first = EINA_FALSE;
+   _plugin->total_time = _plugin->tmp_time;
+}
+
+static void
+_video_pos_set(struct _emotion_plugin *_plugin)
+{
+   double pos;
+   double interval;
+
+   pos = ethumb_video_start_get(_plugin->e);
+   interval = ethumb_video_interval_get(_plugin->e);
+   _plugin->len = emotion_object_play_length_get(_plugin->video);
+
+   if (_plugin->len > 0)
+     _plugin->first = EINA_TRUE;
+
+   if (pos <=0 || pos >= 1)
+     _plugin->pi = 0.1 * _plugin->len + _plugin->pcount *
+       _plugin->len * interval;
+   else
+     _plugin->pi = pos * _plugin->len + _plugin->pcount *
+       _plugin->len * interval;
+
+   emotion_object_position_set(_plugin->video, _plugin->pi);
+}
+
+static int
+_setup_thumbnail(struct _emotion_plugin *_plugin)
+{
+   char buf[4096];
+   Evas *evas;
+   Evas_Object *edje;
+   unsigned int i;
+   const char *thumb_path;
+
+   ethumb_thumb_path_get(_plugin->e, &thumb_path, NULL);
+   evas = ethumb_evas_get(_plugin->e);
+
+   if (!edje_file_group_exists(thumb_path, "movie/thumb"))
+     {
+       fprintf(stderr, "ERROR: no group 'movie/thumb' found.\n");
+       goto exit_error;
+     }
+
+   edje = edje_edit_object_add(evas);
+   edje_object_file_set(edje, thumb_path, "movie/thumb");
+   if (!edje_object_part_exists(edje, "image"))
+     {
+       fprintf(stderr, "ERROR: no 'image' part found.\n");
+       evas_object_del(edje);
+       goto exit_error;
+     }
+   if (!edje_edit_program_exist(edje, "animate"))
+     {
+       fprintf(stderr, "ERROR: no 'animate' program found.\n");
+       evas_object_del(edje);
+       goto exit_error;
+     }
+
+   for (i = 0; i < _plugin->frnum; i++)
+     {
+       snprintf(buf, sizeof(buf), "images/%d", i);
+       edje_edit_image_data_add(edje, buf, i);
+       if (i == 0)
+         edje_edit_state_image_set(edje, "image", "default", 0.00, buf);
+       else
+         edje_edit_state_tween_add(edje, "image", "default", 0.00, buf);
+     }
+
+   edje_edit_program_transition_time_set(edje, "animate",
+                                        _plugin->total_time);
+   edje_edit_program_transition_time_set(edje, "animate_loop",
+                                        _plugin->total_time);
+   edje_edit_group_min_w_set(edje, _plugin->w);
+   edje_edit_group_max_w_set(edje, _plugin->w);
+   edje_edit_group_min_h_set(edje, _plugin->h);
+   edje_edit_group_max_h_set(edje, _plugin->h);
+   edje_edit_save(edje);
+
+   evas_object_del(edje);
+
+   return 1;
+
+exit_error:
+   return 0;
+}
+
+static void
+_finish_thumb_generation(struct _emotion_plugin *_plugin, int success)
+{
+   int r = 0;
+   evas_object_smart_callback_del(_plugin->video, "frame_resize",
+                                 _frame_resized_cb);
+   evas_object_smart_callback_del(_plugin->video, "frame_decode",
+                                 _frame_decode_cb);
+   emotion_object_play_set(_plugin->video, 0);
+   evas_object_del(_plugin->video);
+   if (_plugin->ef)
+     eet_close(_plugin->ef);
+
+   if (success)
+     r = _setup_thumbnail(_plugin);
+
+   free(_plugin);
+   ethumb_finished_callback_call(_plugin->e, r);
+}
+
+static Eina_Bool
+_frame_grab_single(void *data)
+{
+   struct _emotion_plugin *_plugin = data;
+   Ethumb *e = _plugin->e;
+   double p;
+
+   if (_plugin->len <= 0)
+     {
+       _video_pos_set(_plugin);
+       return EINA_TRUE;
+     }
+
+   p = emotion_object_position_get(_plugin->video);
+//   if (p < _plugin->pi)
+   if (p <= 0.0)
+     return EINA_TRUE;
+
+   DBG("saving static thumbnail at position=%f (intended=%f)", p, _plugin->pi);
+
+   ethumb_image_save(e);
+
+   evas_object_smart_callback_del(_plugin->video, "frame_resize",
+                                 _frame_resized_cb);
+   emotion_object_play_set(_plugin->video, 0);
+   evas_object_del(_plugin->video);
+   free(_plugin);
+
+   ethumb_finished_callback_call(e, 1);
+
+   return EINA_FALSE;
+}
+
+static Eina_Bool
+_frame_grab(void *data)
+{
+   struct _emotion_plugin *_plugin = data;
+   Ethumb *e = _plugin->e;
+   char buf[4096];
+   const void *pixels;
+   double p;
+
+   if (_plugin->len <= 0)
+     {
+       _video_pos_set(_plugin);
+       return EINA_TRUE;
+     }
+
+   p = emotion_object_position_get(_plugin->video);
+   if (p < _plugin->pi)
+     return EINA_TRUE;
+
+   if (_plugin->first)
+     {
+       _plugin->pi = p;
+       _plugin->first = EINA_FALSE;
+     }
+
+   if (p > _plugin->pi + _plugin->ptotal)
+     {
+       _plugin->total_time += _plugin->tmp_time;
+       if (_plugin->pcount >= ethumb_video_ntimes_get(e))
+         {
+            _finish_thumb_generation(_plugin, EINA_TRUE);
+            return EINA_FALSE;
+         }
+       else
+         {
+            _plugin->pcount++;
+            _video_pos_set(_plugin);
+            return EINA_TRUE;
+         }
+     }
+
+   _plugin->tmp_time = p - _plugin->pi;
+
+   if (_plugin->ef)
+     {
+       Ecore_Evas *ee = ethumb_ecore_evas_get(e);
+       int quality, compress;
+
+       quality = ethumb_thumb_quality_get(e);
+       compress = ethumb_thumb_compress_get(e);
+
+       pixels = ecore_evas_buffer_pixels_get(ee);
+       snprintf(buf, sizeof(buf), "images/%d", _plugin->frnum);
+       eet_data_image_write(_plugin->ef, buf, pixels, _plugin->w, _plugin->h,
+                            0, compress, quality, quality);
+       _plugin->frnum++;
+     }
+
+   return EINA_TRUE;
+}
+
+static void
+_generate_animated_thumb(struct _emotion_plugin *_plugin)
+{
+   const char *thumb_path;
+   char *thumb_dir;
+   char buf[4096];
+   Ethumb *e = _plugin->e;
+
+   snprintf(buf, sizeof(buf), "%s/data/emotion_template.edj", PLUGINSDIR);
+   ethumb_thumb_path_get(e, &thumb_path, NULL);
+   thumb_dir = ecore_file_dir_get(thumb_path);
+   ecore_file_mkpath(thumb_dir);
+   free(thumb_dir);
+   ecore_file_cp(buf, thumb_path);
+   _plugin->ef = eet_open(thumb_path, EET_FILE_MODE_READ_WRITE);
+   if (!_plugin->ef)
+     {
+       fprintf(stderr, "ERROR: could not open '%s'\n", thumb_path);
+       _finish_thumb_generation(_plugin, 0);
+     }
+}
+
+static void *
+_thumb_generate(Ethumb *e)
+{
+   Evas_Object *o;
+   int r;
+   const char *file;
+   Ethumb_Thumb_Format f;
+   struct _emotion_plugin *_plugin = calloc(sizeof(struct _emotion_plugin), 1);
+
+   o = emotion_object_add(ethumb_evas_get(e));
+   r = emotion_object_init(o, NULL);
+   if (!r)
+     {
+       fprintf(stderr, "ERROR: could not start emotion using gstreamer"
+               " plugin.\n");
+       evas_object_del(o);
+       ethumb_finished_callback_call(e, 0);
+       free(_plugin);
+       return NULL;
+     }
+
+   _plugin->video = o;
+
+   ethumb_file_get(e, &file, NULL);
+   f = ethumb_thumb_format_get(e);
+
+   emotion_object_file_set(o, file);
+   emotion_object_audio_mute_set(o, EINA_TRUE);
+
+   _plugin->video = o;
+   _plugin->e = e;
+
+   _plugin->ptotal = ethumb_video_time_get(e) / ethumb_video_ntimes_get(e);
+   _plugin->pcount = 1;
+
+   _resize_movie(_plugin);
+   evas_object_smart_callback_add(o, "frame_decode",
+                                 _frame_decode_cb, _plugin);
+   evas_object_smart_callback_add(o, "frame_resize",
+                                 _frame_resized_cb, _plugin);
+   evas_object_smart_callback_add(o, "decode_stop",
+                                 _video_stopped_cb, _plugin);
+
+   if (f == ETHUMB_THUMB_EET)
+     {
+       _generate_animated_thumb(_plugin);
+     }
+
+   _video_pos_set(_plugin);
+   emotion_object_play_set(o, 1);
+   evas_object_show(o);
+
+   return _plugin;
+}
+
+static void
+_thumb_cancel(Ethumb *e __UNUSED__, void *data)
+{
+   struct _emotion_plugin *_plugin = data;
+
+   if (_plugin->ef) eet_close(_plugin->ef);
+   evas_object_del(_plugin->video);
+   free(_plugin);
+}
+
+EAPI Ethumb_Plugin *
+ethumb_plugin_get(void)
+{
+   static const char *extensions[] = { "avi", "mp4", "ogv", "mov", "mpg", "wmv",
+                                      NULL };
+   static Ethumb_Plugin plugin =
+     {
+       extensions,
+       _thumb_generate,
+       _thumb_cancel
+     };
+
+   _log_dom = eina_log_domain_register("ethumb_emotion", EINA_COLOR_GREEN);
+
+   return &plugin;
+}
+
+static Eina_Bool
+_module_init(void)
+{
+   return EINA_TRUE;
+}
+
+static void
+_module_shutdown(void)
+{
+}
+
+EINA_MODULE_INIT(_module_init);
+EINA_MODULE_SHUTDOWN(_module_shutdown);
diff --git a/src/plugins/emotion/template.edc b/src/plugins/emotion/template.edc
new file mode 100644 (file)
index 0000000..4a30c3d
--- /dev/null
@@ -0,0 +1,43 @@
+collections {
+
+   group {
+      name: "movie/thumb";
+
+      parts {
+         part {
+            name: "image";
+            type: IMAGE;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+              fill.smooth: 0;
+               image.scale_hint: DYNAMIC;
+            }
+         }
+      }
+
+      programs {
+         program {
+            name: "animate";
+            signal: "animate";
+            action: STATE_SET "default" 0.0;
+            transition: LINEAR 3.0;
+            target: "image";
+         }
+         program {
+            name: "animate_loop";
+            signal: "animate_loop";
+            action: STATE_SET "default" 0.0;
+            transition: LINEAR 3.0;
+            target: "image";
+            after: "animate_loop";
+         }
+         program {
+            name: "animate_stop";
+            signal: "animate_stop";
+            action: ACTION_STOP;
+            target: "animate_loop";
+         }
+      }
+   }
+}
diff --git a/src/plugins/epdf/Makefile.am b/src/plugins/epdf/Makefile.am
new file mode 100644 (file)
index 0000000..497c198
--- /dev/null
@@ -0,0 +1,21 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = \
+-I$(top_srcdir)/src/lib \
+-I$(top_srcdir)/src/plugins/epdf \
+@EFL_ETHUMB_BUILD@ \
+@EINA_CFLAGS@ \
+@EVAS_CFLAGS@ \
+@EPDF_CFLAGS@
+
+pluginsdir = $(libdir)/ethumb/plugins
+pkgdir = $(pluginsdir)
+pkg_LTLIBRARIES = epdf.la
+epdf_la_SOURCES = epdf.c
+epdf_la_LIBADD = \
+$(top_builddir)/src/lib/libethumb.la \
+@EPDF_LIBS@ \
+@EVAS_LIBS@ \
+@EINA_LIBS@
+epdf_la_LDFLAGS = -module -avoid-version
+epdf_la_LIBTOOLFLAGS = --tag=disable-static
diff --git a/src/plugins/epdf/epdf.c b/src/plugins/epdf/epdf.c
new file mode 100644 (file)
index 0000000..62234a9
--- /dev/null
@@ -0,0 +1,100 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <Eina.h>
+#include <Evas.h>
+#include <Epdf.h>
+
+#include "Ethumb.h"
+#include "Ethumb_Plugin.h"
+
+
+static void
+_generate_thumb(Ethumb *e)
+{
+   Epdf_Document *document;
+   Epdf_Page *page;
+   Evas_Object *o;
+   const char *src_path;
+   int w, h, ww, hh;
+   int fx, fy, fw, fh;
+   unsigned int npages, pagenum;
+
+   ethumb_file_get(e, &src_path, NULL);
+   document = epdf_document_new(src_path);
+   if (!document)
+     {
+       fprintf(stderr, "ERROR: could not read document: %s\n", src_path);
+       ethumb_finished_callback_call(e, 0);
+       return;
+     }
+
+   page = epdf_page_new(document);
+   if (!page)
+     {
+       fprintf(stderr, "ERROR: could not read document: %s\n", src_path);
+       epdf_document_delete(document);
+       ethumb_finished_callback_call(e, 0);
+       return;
+     }
+
+   npages = epdf_document_page_count_get(document);
+   pagenum = ethumb_document_page_get(e);
+   if (pagenum < npages)
+     epdf_page_page_set(page, pagenum);
+   epdf_page_size_get(page, &w, &h);
+   ethumb_calculate_aspect(e, w, h, &ww, &hh);
+   ethumb_plugin_image_resize(e, ww, hh);
+
+   o = evas_object_image_add(ethumb_evas_get(e));
+   epdf_page_render(page, o);
+   evas_object_resize(o, ww, hh);
+   evas_object_move(o, 0, 0);
+
+   ethumb_calculate_fill(e, w, h, &fx, &fy, &fw, &fh);
+   evas_object_image_fill_set(o, fx, fy, fw, fh);
+
+   evas_object_show(o);
+   ethumb_image_save(e);
+
+   evas_object_del(o);
+   epdf_page_delete(page);
+   epdf_document_delete(document);
+
+   ethumb_finished_callback_call(e, 1);
+}
+
+EAPI Ethumb_Plugin *
+ethumb_plugin_get(void)
+{
+   static const char *extensions[] = { "pdf", NULL };
+   static Ethumb_Plugin plugin =
+     {
+       extensions,
+       _generate_thumb,
+       NULL /* This plugin is not assynchronous so not possible to cancel it at all */
+     };
+
+   return &plugin;
+}
+
+static Eina_Bool
+_module_init(void)
+{
+   epdf_init();
+
+   return EINA_TRUE;
+}
+
+static void
+_module_shutdown(void)
+{
+   epdf_shutdown();
+}
+
+EINA_MODULE_INIT(_module_init);
+EINA_MODULE_SHUTDOWN(_module_shutdown);
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
new file mode 100644 (file)
index 0000000..fa6cfb6
--- /dev/null
@@ -0,0 +1,22 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/lib \
+       -I$(top_srcdir)/src/lib/client \
+       @EINA_CFLAGS@ @EVAS_CFLAGS@ @ECORE_CFLAGS@ @ECORE_EVAS_CFLAGS@ \
+       @EDJE_CFLAGS@ @ECORE_FILE_CFLAGS@
+
+check_PROGRAMS =
+
+if USE_MODULE_ETHUMBD
+
+AM_CPPFLAGS += @EDBUS_CFLAGS@
+check_PROGRAMS += ethumb_dbus
+ethumb_dbus_SOURCES = ethumb_dbus.c
+ethumb_dbus_LDADD = \
+       @EINA_LIBS@ @EVAS_LIBS@ @ECORE_LIBS@ @ECORE_EVAS_LIBS@ @EDJE_LIBS@ \
+       @ECORE_FILE_LIBS@ @EDBUS_LIBS@ \
+       $(top_builddir)/src/lib/libethumb.la \
+       $(top_builddir)/src/lib/client/libethumb_client.la
+
+endif
diff --git a/src/tests/ethumb_dbus.c b/src/tests/ethumb_dbus.c
new file mode 100644 (file)
index 0000000..d9e2f10
--- /dev/null
@@ -0,0 +1,124 @@
+/**
+ * @file
+ *
+ * Copyright (C) 2009 by ProFUSION embedded systems
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Rafael Antognolli <antognolli@profusion.mobi>
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <dirent.h>
+#include <Ethumb.h>
+#include <Ethumb_Client.h>
+#include <Eina.h>
+#include <Ecore_Getopt.h>
+#include <Ecore.h>
+
+static void
+_on_server_die_cb(void *data __UNUSED__, Ethumb_Client *client __UNUSED__)
+{
+   ecore_main_loop_quit();
+}
+
+static void
+_queue_add_cb(void *data __UNUSED__, Ethumb_Client *client __UNUSED__, int id, const char *file, const char *key __UNUSED__, const char *thumb_path, const char *thumb_key __UNUSED__, Eina_Bool success)
+{
+   fprintf(stderr, ">>> %hhu file ready: %s; thumb ready: %s; id = %d\n", success, file, thumb_path, id);
+}
+
+static void
+_request_thumbnails(Ethumb_Client *client, void *data)
+{
+   const char *path = data;
+   DIR *dir;
+   struct dirent *de;
+   char buf[PATH_MAX];
+
+   dir = opendir(path);
+   if (!dir)
+     {
+       fprintf(stderr, "ERROR: could not open directory: %s\n", path);
+       return;
+     }
+
+   ethumb_client_format_set(client, ETHUMB_THUMB_JPEG);
+   ethumb_client_aspect_set(client, ETHUMB_THUMB_CROP);
+   ethumb_client_crop_align_set(client, 0.2, 0.2);
+   ethumb_client_size_set(client, 192, 192);
+   ethumb_client_category_set(client, "custom");
+
+   while ((de = readdir(dir)))
+     {
+       if (de->d_type != DT_REG)
+         continue;
+       snprintf(buf, sizeof(buf), "%s/%s", path, de->d_name);
+       ethumb_client_file_set(client, buf, NULL);
+       ethumb_client_generate(client, _queue_add_cb, NULL, NULL);
+     }
+
+   closedir(dir);
+}
+
+static void
+_connect_cb(void *data, Ethumb_Client *client, Eina_Bool success)
+{
+   fprintf(stderr, "connected: %d\n", success);
+   if (!success)
+     {
+       ecore_main_loop_quit();
+       return;
+     }
+
+   _request_thumbnails(client, data);
+}
+
+int
+main(int argc, char *argv[])
+{
+   Ethumb_Client *client;
+
+   if (argc < 2)
+     {
+       fprintf(stderr, "ERROR: directory not specified.\n");
+       fprintf(stderr, "usage:\n\tethumb_dbus <images directory>\n");
+       return -2;
+     }
+
+   ethumb_client_init();
+   client = ethumb_client_connect(_connect_cb, argv[1], NULL);
+   if (!client)
+     {
+       fprintf(stderr, "ERROR: couldn't connect to server.\n");
+       ethumb_client_shutdown();
+       return -1;
+     }
+   ethumb_client_on_server_die_callback_set(client, _on_server_die_cb, NULL, NULL);
+
+   fprintf(stderr, "*** debug\n");
+   ecore_main_loop_begin();
+
+   ethumb_client_disconnect(client);
+
+   ethumb_client_shutdown();
+
+   return 0;
+}