--- /dev/null
+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.
+
--- /dev/null
+Rafael Antognolli <antognolli@profusion.mobi>
--- /dev/null
+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
--- /dev/null
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ 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 that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU 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 as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
--- /dev/null
+MAINTAINERCLEANFILES = \
+ Makefile.in \
+ aclocal.m4 \
+ compile \
+ config.guess \
+ config.h.in \
+ config.sub \
+ configure \
+ depcomp \
+ install-sh \
+ ltmain.sh \
+ missing
+
+EXTRA_DIST = m4/Makefile.in \
+ README \
+ AUTHORS \
+ COPYING
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = ethumb.pc
+
+
+if USE_MODULE_ETHUMBD
+
+pkgconfig_DATA += ethumb_client.pc
+## FIXME: this breaks "make distcheck" as this installs somethnig outside of
+## the prefix. try make distcheck (as a user)
+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
--- /dev/null
+ 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
--- /dev/null
+ * 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.
--- /dev/null
+#!/bin/sh
+
+rm -rf autom4te.cache
+rm -f aclocal.m4 ltmain.sh
+
+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
+
+if [ -z "$NOCONFIGURE" ]; then
+ ./configure "$@"
+fi
--- /dev/null
+AC_INIT([ethumb], [0.1], [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
+
+AM_INIT_AUTOMAKE(1.6 dist-bzip2)
+AM_CONFIG_HEADER(config.h)
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+_XTERM_COLORS
+
+AC_PROG_CC
+AM_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
+
+VMAJ=`echo $PACKAGE_VERSION | awk -F. '{printf("%s", $1);}'`
+VMIN=`echo $PACKAGE_VERSION | awk -F. '{printf("%s", $2);}'`
+version_info=`expr $VMAJ + $VMIN`
+AC_SUBST(version_info)
+
+#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-0])
+PKG_CHECK_MODULES(EVAS, [evas])
+PKG_CHECK_MODULES(ECORE, [ecore])
+PKG_CHECK_MODULES(ECORE_EVAS, [ecore-evas])
+PKG_CHECK_MODULES(ECORE_FILE, [ecore-file])
+PKG_CHECK_MODULES(EDJE, [edje])
+
+requirement_ethumb="eina-0 evas ecore ecore-evas ecore-file edje"
+requirement_ethumb_client="ethumb edbus"
+
+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=`$PKG_CONFIG --variable=session_bus_services_dir dbus-1`
+ AC_ARG_WITH([dbus-services],
+ AC_HELP_STRING([--with-dbus-services=DBUS_SERVICES], [specify a directory to store dbus service files.]),
+ [
+ dbusservicedir=$withval
+ ]
+ )
+fi
+
+AC_SUBST(requirement_ethumb)
+AC_SUBST(requirement_ethumb_client)
+AC_SUBST(dbusservicedir)
+
+AC_OUTPUT([
+ethumb.pc
+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
+])
+
+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:
+
+ * 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
--- /dev/null
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = frames
--- /dev/null
+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
--- /dev/null
+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
+
+ }
+ }
+}
--- /dev/null
+Revision 48959
+Last Changed Rev 48347
--- /dev/null
+ethumb (0.1-1) unstable; urgency=low
+
+ * a SVN release.
+
+ -- quaker <quaker66@gmail.com> Sat, 10 Apr 2009 10:50:46 +0100
--- /dev/null
+Source: ethumb
+Section: x11
+Priority: optional
+Maintainer: quaker <quaker66@gmail.com>
+Build-Depends: cdbs, debhelper (>= 6), libevas-dev, libedje-dev, libeina-dev, libecore-dev, pkg-config, libtool
+Standards-Version: 3.8.1
+Homepage: http://www.enlightenment.org
+
+Package: ethumb
+Architecture: any
+Depends: ${shlibs:Depends}
+Description: Enlightenment thumbnailing library
+ Thumbnailing library meant to replace epsilon.
+
--- /dev/null
+This package was debianized by quaker <quaker66@gmail.com> on
+Sat, 10 Apr 2009 10:50:46 +0100.
+
+Author(s):
+ Rafael Antognolli <antognolli@profusion.mobi>
+
+License:
+ See the COPYING file
+
--- /dev/null
+#!/usr/bin/make -f
+
+include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/cdbs/1/class/autotools.mk
+
+DEB_CONFIGURE_SCRIPT := ./autogen.sh
+DEB_MAKE_CLEAN_TARGET := distclean
+DEB_CONFIGURE_EXTRA_FLAGS := --disable-rpath
+
+clean::
+ [ ! -f Makefile ] || make distclean
--- /dev/null
+# 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 =
+
+# 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 DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = 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 = ../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 = 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 = 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 = 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
--- /dev/null
+
+MAINTAINERCLEANFILES = Makefile.in
+
+.PHONY: doc
+
+if EFL_BUILD_DOC
+
+doc: all
+ rm -rf html/ latex/ man/
+ $(efl_doxygen)
+ cp img/* html/
+
+else
+
+doc:
+ @echo "Documentation not built. Run ./configure --help"
+
+endif
+
+clean-local:
+ @rm -rf html/ latex/ man/
+
+EXTRA_DIST = Doxyfile $(wildcard img/*.*) e.css head.html foot.html
--- /dev/null
+/*
+ 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%;
+}
+
--- /dev/null
+
+ <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 ©$year Enlightenment</td>
+ <td class="generated">Docs generated $datetime</td>
+ </tr></table>
+ </div>
+
+
+</body>
+</html>
--- /dev/null
+<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">
--- /dev/null
+/*
+ * 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%;
+}
+
--- /dev/null
+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}
--- /dev/null
+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}
--- /dev/null
+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
--- /dev/null
+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)
+# ----------------------------------------------------------------------
+])
--- /dev/null
+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
--- /dev/null
+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
+])
--- /dev/null
+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
--- /dev/null
+# 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
+])
--- /dev/null
+# 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
+])
--- /dev/null
+# 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])
+])
--- /dev/null
+# 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
+])
--- /dev/null
+# 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
+])
--- /dev/null
+[D-BUS Service]
+Name=org.enlightenment.Ethumb
+Exec=@prefix@/bin/ethumbd
--- /dev/null
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = lib bin plugins tests
--- /dev/null
+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@
+
+bin_PROGRAMS = ethumb
+noinst_HEADERS =
+
+ethumb_SOURCES = ethumb.c
+ethumb_LDADD = \
+ @EINA_LIBS@ @ECORE_LIBS@ \
+ $(top_builddir)/src/lib/libethumb.la
+ethumb_DEPENDENCIES = $(top_builddir)/config.h
+
+
+
+if USE_MODULE_ETHUMBD
+
+AM_CPPFLAGS += @EDBUS_CFLAGS@ \
+ -DETHUMB_LIBEXEC_DIR=\"$(libexecdir)\"
+bin_PROGRAMS += ethumbd ethumbd_client
+noinst_HEADERS += ethumbd_private.h
+
+libexec_PROGRAMS = ethumbd_slave
+
+ethumbd_SOURCES = ethumbd.c
+ethumbd_LDADD = \
+ @EINA_LIBS@ @ECORE_LIBS@ @EDBUS_LIBS@ \
+ $(top_builddir)/src/lib/libethumb.la
+ethumbd_DEPENDENCIES = $(top_builddir)/config.h
+
+ethumbd_slave_SOURCES = ethumbd_child.c
+ethumbd_slave_LDADD = \
+ @EINA_LIBS@ @ECORE_LIBS@ @EDBUS_LIBS@ \
+ $(top_builddir)/src/lib/libethumb.la
+
+ethumbd_client_SOURCES = ethumbd_client.c
+ethumbd_client_LDADD = \
+ @EINA_LIBS@ @ECORE_LIBS@ \
+ $(top_builddir)/src/lib/libethumb.la \
+ $(top_builddir)/src/lib/client/libethumb_client.la
+ethumbd_client_DEPENDENCIES = $(top_builddir)/config.h
+
+endif
--- /dev/null
+/**
+ * @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>
+ * @author Gustavo Sverzut Barbieri <barbieri@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;
+}
--- /dev/null
+/**
+ * @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 <Ethumb.h>
+#include <Eina.h>
+#include <Ecore_Getopt.h>
+#include <Ecore.h>
+#include <E_DBus.h>
+
+#include "ethumbd_private.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#define MAX_ID 2000000
+
+#define DBG(...) EINA_LOG_DBG(__VA_ARGS__)
+#define INF(...) EINA_LOG_INFO(__VA_ARGS__)
+#define WRN(...) EINA_LOG_WARN(__VA_ARGS__)
+#define ERR(...) EINA_LOG_ERR(__VA_ARGS__)
+#define CRIT(...) EINA_LOG_CRIT(__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";
+
+struct _Ethumb_Setup
+{
+ struct
+ {
+ Eina_Bool fdo : 1;
+ Eina_Bool size : 1;
+ Eina_Bool format : 1;
+ Eina_Bool aspect : 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;
+ 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 _Ethumb_Request
+{
+ int id;
+ const char *file, *key;
+ const char *thumb, *thumb_key;
+ struct _Ethumb_Setup setup;
+};
+
+struct _Ethumb_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 _Ethumb_Queue
+{
+ int count;
+ int max_count;
+ int nqueue;
+ int last;
+ int current;
+ struct _Ethumb_Object *table;
+ int *list;
+};
+
+struct _Ethumb_Slave
+{
+ Ecore_Exe *exe;
+ Ecore_Event_Handler *data_cb;
+ Ecore_Event_Handler *del_cb;
+ 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;
+ struct _Ethumb_Request *processing;
+ struct _Ethumb_Queue queue;
+ double timeout;
+ Ecore_Timer *timeout_timer;
+ struct _Ethumb_Slave slave;
+};
+
+struct _Ethumb_Object_Data
+{
+ int index;
+ struct _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(struct _Ethumbd *ed, int *id, const char *thumb_path, const char *thumb_key, Eina_Bool success);
+static int _ethumbd_slave_spawn(struct _Ethumbd *ed);
+
+static int
+_ethumbd_timeout_cb(void *data)
+{
+ struct _Ethumbd *ed = data;
+
+ ecore_main_loop_quit();
+ ed->timeout_timer = NULL;
+
+ return 0;
+}
+
+static void
+_ethumbd_timeout_start(struct _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(struct _Ethumbd *ed)
+{
+ if (!ed->timeout_timer)
+ return;
+
+ ecore_timer_del(ed->timeout_timer);
+ ed->timeout_timer = NULL;
+}
+
+static int
+_ethumb_dbus_check_id(struct _Ethumb_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(struct _Ethumb_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(struct _Ethumb_Object *eobject)
+{
+ Eina_List *l;
+
+ l = eobject->queue;
+ while (l)
+ {
+ struct _Ethumb_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(struct _Ethumbd *ed, const void *buf, ssize_t size)
+{
+
+ if (!ed->slave.exe)
+ {
+ ERR("slave process isn't running.");
+ return 0;
+ }
+
+ ecore_exe_send(ed->slave.exe, buf, size);
+ return 1;
+}
+
+static void
+_ethumbd_child_write_op_new(struct _Ethumbd *ed, int index)
+{
+ int id = ETHUMBD_OP_NEW;
+ _ethumbd_write_safe(ed, &id, sizeof(id));
+ _ethumbd_write_safe(ed, &index, sizeof(index));
+}
+
+static void
+_ethumbd_child_write_op_del(struct _Ethumbd *ed, int index)
+{
+ int id = ETHUMBD_OP_DEL;
+ _ethumbd_write_safe(ed, &id, sizeof(id));
+ _ethumbd_write_safe(ed, &index, sizeof(index));
+}
+
+static void
+_ethumbd_pipe_str_write(struct _Ethumbd *ed, const char *str)
+{
+ int len;
+
+ if (str)
+ len = strlen(str) + 1;
+ else
+ len = 0;
+
+ _ethumbd_write_safe(ed, &len, sizeof(len));
+ _ethumbd_write_safe(ed, str, len);
+}
+
+static void
+_ethumbd_child_write_op_generate(struct _Ethumbd *ed, int index, const char *path, const char *key, const char *thumb_path, const char *thumb_key)
+{
+ int id = ETHUMBD_OP_GENERATE;
+
+ _ethumbd_write_safe(ed, &id, sizeof(id));
+ _ethumbd_write_safe(ed, &index, sizeof(index));
+
+ _ethumbd_pipe_str_write(ed, path);
+ _ethumbd_pipe_str_write(ed, key);
+ _ethumbd_pipe_str_write(ed, thumb_path);
+ _ethumbd_pipe_str_write(ed, thumb_key);
+}
+
+static void
+_generated_cb(struct _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 Eina_Bool
+_write_safe(int fd, void *data, size_t size)
+{
+ unsigned char *buf = data;
+ size_t todo = size;
+ while (todo > 0)
+ {
+ size_t r = write(fd, buf, todo);
+ if (r > 0)
+ {
+ todo -= r;
+ buf += r;
+ }
+ else if ((r < 0) && (errno != EINTR))
+ {
+ ERR("could not write to fd=%d: %s", fd, strerror(errno));
+ return EINA_FALSE;
+ }
+ }
+ return EINA_TRUE;
+}
+
+static void
+_ethumbd_slave_cmd_ready(struct _Ethumbd *ed)
+{
+ char *bufcmd = ed->slave.bufcmd;
+ Eina_Bool *success;
+ char *thumb_path, *thumb_key;
+ int *size_path, *size_key;
+
+
+ success = (Eina_Bool *)bufcmd;
+ bufcmd += sizeof(*success);
+
+ size_path = (int *)bufcmd;
+ bufcmd += sizeof(*size_path);
+
+ _write_safe(STDERR_FILENO, bufcmd, ed->slave.scmd);
+
+ thumb_path = bufcmd;
+ bufcmd += *size_path;
+
+ size_key = (int *)bufcmd;
+ bufcmd += sizeof(*size_key);
+
+ thumb_key = bufcmd;
+
+ _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(struct _Ethumbd *ed, int ssize, char *sdata)
+{
+ int *scmd;
+
+ if (ed->slave.bufcmd)
+ return 0;
+
+ scmd = (int *)sdata;
+ if (ssize < 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 int
+_ethumbd_slave_data_read_cb(void *data, int type, void *event)
+{
+ struct _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;
+
+ if (!_write_safe(STDERR_FILENO, sdata, ssize))
+ return 0;
+
+ 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 int
+_ethumbd_slave_del_cb(void *data, int type, void *event)
+{
+ struct _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);
+
+ return _ethumbd_slave_spawn(ed);
+}
+
+static void
+_ethumbd_pipe_write_setup(struct _Ethumbd *ed, int type, const void *data)
+{
+ const int *i_value;
+ const float *f_value;
+
+ _ethumbd_write_safe(ed, &type, sizeof(type));
+
+ switch (type)
+ {
+ case ETHUMBD_FDO:
+ case ETHUMBD_FORMAT:
+ case ETHUMBD_ASPECT:
+ 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(ed, 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(ed, 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(ed, data);
+ break;
+ case ETHUMBD_SETUP_FINISHED:
+ break;
+ default:
+ ERR("wrong ethumb setup parameter.");
+ }
+}
+
+static void
+_process_setup(struct _Ethumbd *ed)
+{
+ int op_id = ETHUMBD_OP_SETUP;
+ int index = ed->queue.current;
+
+ struct _Ethumb_Setup *setup = &ed->processing->setup;
+
+ _ethumbd_write_safe(ed, &op_id, sizeof(op_id));
+ _ethumbd_write_safe(ed, &index, sizeof(index));
+
+ if (setup->flags.fdo)
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_FDO, &setup->fdo);
+ if (setup->flags.size)
+ {
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_SIZE_W, &setup->tw);
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_SIZE_H, &setup->th);
+ }
+ if (setup->flags.format)
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_FORMAT, &setup->format);
+ if (setup->flags.aspect)
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_ASPECT, &setup->aspect);
+ if (setup->flags.crop)
+ {
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_CROP_X, &setup->cx);
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_CROP_Y, &setup->cy);
+ }
+ if (setup->flags.quality)
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_QUALITY, &setup->quality);
+ if (setup->flags.compress)
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_COMPRESS, &setup->compress);
+ if (setup->flags.directory)
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_DIRECTORY, setup->directory);
+ if (setup->flags.category)
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_CATEGORY, setup->category);
+ if (setup->flags.frame)
+ {
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_FRAME_FILE, setup->theme_file);
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_FRAME_GROUP, setup->group);
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_FRAME_SWALLOW, setup->swallow);
+ }
+ if (setup->flags.video_time)
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_VIDEO_TIME, &setup->video_time);
+ if (setup->flags.video_start)
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_VIDEO_START, &setup->video_start);
+ if (setup->flags.video_interval)
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_VIDEO_INTERVAL,
+ &setup->video_interval);
+ if (setup->flags.video_ntimes)
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_VIDEO_NTIMES, &setup->video_ntimes);
+ if (setup->flags.video_fps)
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_VIDEO_FPS, &setup->video_fps);
+ if (setup->flags.document_page)
+ _ethumbd_pipe_write_setup(ed, ETHUMBD_DOCUMENT_PAGE,
+ &setup->document_page);
+ _ethumbd_pipe_write_setup(ed, 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(struct _Ethumbd *ed)
+{
+ _ethumbd_child_write_op_generate
+ (ed, ed->queue.current, ed->processing->file,
+ ed->processing->key, ed->processing->thumb, ed->processing->thumb_key);
+}
+
+static int
+_get_next_on_queue(struct _Ethumb_Queue *queue)
+{
+ int i, index;
+ struct _Ethumb_Object *eobject;
+
+ i = queue->last;
+ i++;
+ if (i >= queue->count)
+ i = 0;
+
+ index = queue->list[i];
+ eobject = &(queue->table[index]);
+ while (!eobject->nqueue)
+ {
+ i = (i + 1) % queue->count;
+
+ index = queue->list[i];
+ eobject = &(queue->table[index]);
+ }
+
+ return queue->list[i];
+}
+
+static int
+_process_queue_cb(void *data)
+{
+ struct _Ethumb_Object *eobject;
+ int i;
+ struct _Ethumbd *ed = data;
+ struct _Ethumb_Queue *queue = &ed->queue;
+ struct _Ethumb_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(struct _Ethumbd *ed)
+{
+ if (!ed->idler)
+ ed->idler = ecore_idler_add(_process_queue_cb, ed);
+}
+
+static void
+_process_queue_stop(struct _Ethumbd *ed)
+{
+ if (ed->idler)
+ {
+ ecore_idler_del(ed->idler);
+ ed->idler = NULL;
+ }
+}
+
+static int
+_ethumb_table_append(struct _Ethumbd *ed)
+{
+ int i;
+ char buf[1024];
+ struct _Ethumb_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(struct _Ethumb_Object));
+ if (!tmp)
+ {
+ CRIT("could not realloc q->table to %zd bytes: %s",
+ new_max * sizeof(struct _Ethumb_Object), strerror(errno));
+ return -1;
+ }
+ q->table = tmp;
+ memset(&q->table[start], 0, size * sizeof(struct _Ethumb_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, index = %d, count = %d", buf, i, q->count);
+
+ return i;
+}
+
+static inline int
+_get_index_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(struct _Ethumbd *ed, int i)
+{
+ int j;
+ Eina_List *l;
+ const Eina_List *il;
+ struct _Ethumb_Queue *q = &ed->queue;
+ struct _Ethumb_Object_Data *odata;
+
+ eina_stringshare_del(q->table[i].path);
+
+ l = q->table[i].queue;
+ while (l)
+ {
+ struct _Ethumb_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(struct _Ethumb_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, i);
+ if (!q->count && !ed->processing)
+ _ethumbd_timeout_start(ed);
+}
+
+static void
+_ethumb_table_clear(struct _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;
+ struct _Ethumbd *ed = data;
+ struct _Ethumb_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(struct _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;
+ struct _Ethumb_Object_Data *odata;
+ int i;
+ const char *return_path = "";
+ const char *client;
+ struct _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->index = 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, 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;
+ struct _Ethumb_Object_Data *odata;
+ struct _Ethumb_Object *eobject;
+ struct _Ethumbd *ed;
+ struct _Ethumb_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->index]);
+ 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;
+ struct _Ethumb_Object_Data *odata;
+ struct _Ethumb_Object *eobject;
+ struct _Ethumb_Request *request;
+ struct _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->index];
+ 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;
+ struct _Ethumb_Object_Data *odata;
+ struct _Ethumb_Object *eobject;
+ struct _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->index];
+ l = eobject->queue;
+ while (l)
+ {
+ struct _Ethumb_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;
+ struct _Ethumb_Object_Data *odata;
+ struct _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->index);
+ free(odata);
+
+ return reply;
+}
+
+static int
+_ethumb_dbus_fdo_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request __UNUSED__)
+{
+ 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);
+
+ return 1;
+}
+
+static int
+_ethumb_dbus_size_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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_crop_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_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)(struct _Ethumb_Object *eobject, DBusMessageIter *iter, struct _Ethumb_Request *request);
+} _option_cbs[] = {
+ {"fdo", _ethumb_dbus_fdo_set},
+ {"size", _ethumb_dbus_size_set},
+ {"format", _ethumb_dbus_format_set},
+ {"aspect", _ethumb_dbus_aspect_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(struct _Ethumb_Object *eobject, DBusMessageIter *iter, struct _Ethumb_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;
+ struct _Ethumb_Object_Data *odata;
+ struct _Ethumbd *ed;
+ struct _Ethumb_Object *eobject;
+ struct _Ethumb_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->index];
+
+ 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(struct _Ethumbd *ed, int *id, const char *thumb_path, const char *thumb_key, Eina_Bool success)
+{
+ DBusMessage *signal;
+ 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;
+ signal = dbus_message_new_signal
+ (opath, _ethumb_dbus_objects_interface, "generated");
+
+ dbus_message_iter_init_append(signal, &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, signal, NULL, -1, NULL);
+ dbus_message_unref(signal);
+}
+
+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 != NULL)
+ 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 != NULL)
+ 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;
+ struct _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(struct _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(struct _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 int
+_ethumbd_slave_spawn(struct _Ethumbd *ed)
+{
+ ed->slave.data_cb = ecore_event_handler_add(
+ ECORE_EXE_EVENT_DATA, _ethumbd_slave_data_read_cb, ed);
+ ed->slave.del_cb = ecore_event_handler_add(
+ ECORE_EXE_EVENT_DEL, _ethumbd_slave_del_cb, ed);
+
+ ed->slave.bufcmd = NULL;
+ ed->slave.scmd = 0;
+
+ ed->slave.exe = ecore_exe_pipe_run(
+ ETHUMB_LIBEXEC_DIR"/ethumbd_slave",
+ ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_WRITE, ed);
+ if (!ed->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_index;
+ struct _Ethumbd ed;
+ int child;
+ double timeout = -1;
+
+ memset(&ed, 0, sizeof(ed));
+ ecore_init();
+ eina_init();
+
+ ethumb_init();
+
+ child = _ethumbd_slave_spawn(&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_index = ecore_getopt_parse(&optdesc, values, argc, argv);
+ if (arg_index < 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:
+ e_dbus_shutdown();
+ finish:
+ if (ed.slave.exe)
+ ecore_exe_quit(ed.slave.exe);
+ ethumb_shutdown();
+ eina_init();
+ ecore_shutdown();
+ return exit_value;
+}
--- /dev/null
+/**
+ * @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 <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+
+#include <Ecore.h>
+#include <Ethumb.h>
+#include <Eina.h>
+
+#include "ethumbd_private.h"
+
+#define DBG(...) EINA_LOG_DBG(__VA_ARGS__)
+#define INF(...) EINA_LOG_INFO(__VA_ARGS__)
+#define WRN(...) EINA_LOG_WARN(__VA_ARGS__)
+#define ERR(...) EINA_LOG_ERR(__VA_ARGS__)
+
+#define NETHUMBS 100
+
+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, 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 index;
+
+ r = _ec_read_safe(STDIN_FILENO, &index, sizeof(index));
+ if (!r)
+ return 0;
+
+ DBG("ethumbd new(). index = %d", index);
+
+ ec->ethumbt[index] = ethumb_new();
+ return 1;
+}
+
+static int
+_ec_op_del(struct _Ethumbd_Child *ec)
+{
+ int r;
+ int index;
+
+ r = _ec_read_safe(STDIN_FILENO, &index, sizeof(index));
+ if (!r)
+ return 0;
+
+ DBG("ethumbd del(). index = %d", index);
+
+ ethumb_free(ec->ethumbt[index]);
+ ec->ethumbt[index] = NULL;
+ return 1;
+}
+
+static void
+_ec_op_generated_cb(void *data, Ethumb *e, Eina_Bool success)
+{
+ const char *thumb_path, *thumb_key;
+ int size_path, size_key, size_cmd;
+
+ fprintf(stderr, "thumbnail generated!");
+ DBG("thumb generated!");
+ 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 index;
+ char *path, *key, *thumb_path, *thumb_key;
+ int r;
+
+ r = _ec_read_safe(STDIN_FILENO, &index, sizeof(index));
+ 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[index], path, key);
+ ethumb_thumb_path_set(ec->ethumbt[index], thumb_path, thumb_key);
+ ethumb_generate(ec->ethumbt[index], _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, 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, 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, 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, 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_crop_set(struct _Ethumbd_Child *ec, 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, 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, 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, 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, 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, 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, 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, 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, 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 index, int type)
+{
+ Ethumb *e;
+
+ e = ec->ethumbt[index];
+
+ 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_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 index;
+ int type;
+
+ r = _ec_read_safe(STDIN_FILENO, &index, sizeof(index));
+ 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, index, type);
+ r = _ec_read_safe(STDIN_FILENO, &type, sizeof(type));
+ if (!r)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+_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, const char *argv[])
+{
+ struct _Ethumbd_Child *ec;
+
+ ethumb_init();
+
+ ec = _ec_new();
+
+ _ec_setup(ec);
+
+ DBG("child started!");
+ ecore_main_loop_begin();
+ DBG("child finishing.");
+
+ _ec_free(ec);
+
+ ethumb_shutdown();
+
+ return 0;
+}
--- /dev/null
+/**
+ * @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>
+ * @author Gustavo Sverzut Barbieri <barbieri@profusion.mobi>
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <Ethumb_Client.h>
+#include <Eina.h>
+#include <Ecore_Getopt.h>
+#include <Ecore.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
+_connected(void *data, Ethumb_Client *c, Eina_Bool success)
+{
+ struct options *opts = data;
+ const char *thumb_path, *thumb_key;
+ long id;
+
+ 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_path_get(c, &thumb_path, &thumb_key);
+ if (ethumb_client_thumb_exists(c))
+ {
+ _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 : "");
+}
+
+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;
+}
--- /dev/null
+#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_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
--- /dev/null
+/**
+ * @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
+
+#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>
+
+#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"
+
+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)
+{
+ 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->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");
+ 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 = evas_object_data_get(o, "Ecore_Evas");
+ sub_e = ecore_evas_get(sub_ee);
+
+ 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);
+ 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;
+ _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_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 time)
+{
+ EINA_SAFETY_ON_NULL_RETURN(e);
+
+ DBG("ethumb=%p, video_start=%f", e, time);
+ e->video.time = time;
+}
+
+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);
+
+ 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_path, path);
+ eina_stringshare_replace(&e->src_key, key);
+ eina_stringshare_replace(&e->thumb_path, NULL);
+ eina_stringshare_replace(&e->thumb_key, NULL);
+
+ 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;
+
+#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';
+
+ 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];
+ char *fullname;
+ const char *hash;
+ 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";
+
+
+ fullname = ecore_file_realpath(e->src_path);
+ hash = _ethumb_generate_hash(fullname);
+ snprintf(buf, sizeof(buf), "%s/%s/%s.%s", thumb_dir, category, hash, ext);
+ free(fullname);
+ 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);
+ eina_stringshare_del(hash);
+}
+
+EAPI void
+ethumb_file_free(Ethumb *e)
+{
+ EINA_SAFETY_ON_NULL_RETURN(e);
+ DBG("ethumb=%p", e);
+
+ 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;
+}
+
+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;
+ }
+}
+
+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);
+}
+
+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;
+ }
+}
+
+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);
+
+ plugin->generate_thumb(e);
+
+ return EINA_TRUE;
+}
+
+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;
+}
+
+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 int
+_ethumb_image_load(Ethumb *e)
+{
+ int error;
+ Evas_Coord w, h, ww, hh, fx, fy, fw, fh;
+ Evas_Object *img;
+
+ 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;
+ }
+
+ 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 int
+_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 0;
+}
+
+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);
+}
+
+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;
+ }
+ 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_TRUE;
+ }
+
+ r = _ethumb_plugin_generate(e);
+ if (r)
+ return EINA_TRUE;
+
+ if (!_ethumb_image_load(e))
+ {
+ ERR("could not load input image.");
+ ethumb_finished_callback_call(e, 0);
+ return EINA_TRUE;
+ }
+
+ 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;
+}
+
+Evas *
+ethumb_evas_get(const Ethumb *e)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
+
+ return e->sub_e;
+}
+
+Ecore_Evas *
+ethumb_ecore_evas_get(const Ethumb *e)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
+
+ return e->sub_ee;
+}
--- /dev/null
+#ifndef __ETHUMB_H__
+#define __ETHUMB_H__ 1
+
+#include <Eina.h>
+#include <Evas.h>
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_ETHUMB_BUILD
+# ifdef DLL_EXPORT
+# define EAPI __declspec(dllexport)
+# define GNUC_NULL_TERMINATED
+# else
+# define EAPI
+# define GNUC_NULL_TERMINATED
+# endif /* ! DLL_EXPORT */
+# else
+# define EAPI __declspec(dllimport)
+# define GNUC_NULL_TERMINATED
+# endif /* ! EFL_ETHUMB_BUILD */
+#else
+# ifdef __GNUC__
+# if __GNUC__ >= 4
+# define EAPI __attribute__ ((visibility("default")))
+# define GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+# else
+# define EAPI
+# define GNUC_NULL_TERMINATED
+# endif
+# else
+# define EAPI
+# define GNUC_NULL_TERMINATED
+# endif
+#endif /* ! _WIN32 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @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);
+
+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;
+
+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_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;
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __ETHUMB_H__ */
--- /dev/null
+#ifndef _ETHUMB_PLUGIN_H_
+#define _ETHUMB_PLUGIN_H_
+
+#include <Ethumb.h>
+
+typedef struct _Ethumb_Plugin Ethumb_Plugin;
+
+struct _Ethumb_Plugin
+{
+ const char **extensions;
+ void (*generate_thumb)(Ethumb *);
+};
+
+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_ */
--- /dev/null
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = .
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -DPLUGINSDIR=\"$(libdir)/ethumb/plugins\" \
+ @EINA_CFLAGS@ @EVAS_CFLAGS@ @ECORE_EVAS_CFLAGS@ @ECORE_FILE_CFLAGS@ @EDJE_CFLAGS@ \
+ @EFL_ETHUMB_BUILD@
+
+include_HEADERS = Ethumb.h Ethumb_Plugin.h
+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@ @EVAS_LIBS@
+libethumb_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -version-info @version_info@
+
+
+if USE_MODULE_ETHUMBD
+SUBDIRS += client
+endif
--- /dev/null
+/**
+ * @file
+ *
+ * This is the client-server thumbnail library, see @ref
+ * tutorial_ethumb_client.
+ *
+ * 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>
+ * @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 <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 "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;
+
+ 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_Bool ethumb_dirty : 1;
+ 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;
+};
+
+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 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_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);
+}
+
+/**
+ * @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();
+
+ 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;
+
+ 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->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;
+ }
+
+ 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)
+{
+ void *data;
+
+ EINA_SAFETY_ON_NULL_RETURN(client);
+
+ 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);
+
+ 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);
+}
+
+/**
+ * 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, 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);
+ client->ethumb_dirty = 0;
+
+ 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("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);
+ 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 = l->data;
+ if (pending->id != id32)
+ {
+ l = l->next;
+ continue;
+ }
+ client->pending_add = eina_list_remove_list(client->pending_add, l);
+ 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);
+ found = 1;
+ break;
+ }
+
+ if (found)
+ goto end;
+
+ l = client->pending_gen;
+ while (l)
+ {
+ struct _ethumb_pending_gen *pending = l->data;
+ if (pending->id != id32)
+ {
+ l = l->next;
+ continue;
+ }
+ client->pending_gen = eina_list_remove_list(client->pending_gen, l);
+ 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);
+ 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);
+
+ client->ethumb_dirty = 1;
+ 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);
+
+ client->ethumb_dirty = 1;
+ 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);
+
+ client->ethumb_dirty = 1;
+ 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);
+
+ client->ethumb_dirty = 1;
+ 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 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);
+
+ client->ethumb_dirty = 1;
+ 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.
+ */
+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);
+
+ client->ethumb_dirty = 1;
+ 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);
+
+ client->ethumb_dirty = 1;
+ 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);
+
+ client->ethumb_dirty = 1;
+ 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 time duration (in seconds). Defaults to 3 seconds.
+ */
+EAPI void
+ethumb_client_video_time_set(Ethumb_Client *client, float time)
+{
+ EINA_SAFETY_ON_NULL_RETURN(client);
+
+ client->ethumb_dirty = 1;
+ ethumb_video_time_set(client->ethumb, time);
+}
+
+/**
+ * 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);
+
+ client->ethumb_dirty = 1;
+ 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);
+
+ client->ethumb_dirty = 1;
+ 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);
+
+ client->ethumb_dirty = 1;
+ 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);
+
+ client->ethumb_dirty = 1;
+ 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);
+
+ client->ethumb_dirty = 1;
+ 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 Eina_Bool
+ethumb_client_thumb_exists(Ethumb_Client *client)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
+
+ return ethumb_exists(client->ethumb);
+}
+
+/**
+ * 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->ethumb_dirty)
+ ethumb_client_ethumb_setup(client);
+ id = _ethumb_client_queue_add(client, file, key, thumb, thumb_key,
+ generated_cb, data, free_data);
+
+ return id;
+}
--- /dev/null
+#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)
+# define GNUC_NULL_TERMINATED
+# else
+# define EAPI
+# define GNUC_NULL_TERMINATED
+# endif /* ! DLL_EXPORT */
+# else
+# define EAPI __declspec(dllimport)
+# define GNUC_NULL_TERMINATED
+# endif /* ! EFL_ETHUMB_CLIENT_BUILD */
+#else
+# ifdef __GNUC__
+# if __GNUC__ >= 4
+# define EAPI __attribute__ ((visibility("default")))
+# define GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+# else
+# define EAPI
+# define GNUC_NULL_TERMINATED
+# endif
+# else
+# define EAPI
+# define GNUC_NULL_TERMINATED
+# 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 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 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,
+ * 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_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 Eina_Bool ethumb_client_thumb_exists(Ethumb_Client *client);
+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);
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __ETHUMB_CLIENT_H__ */
--- /dev/null
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -I$(top_srcdir)/src/lib \
+ @EDBUS_CFLAGS@ \
+ @EINA_CFLAGS@ \
+ @EFL_ETHUMB_CLIENT_BUILD@
+
+include_HEADERS = Ethumb_Client.h
+
+lib_LTLIBRARIES = libethumb_client.la
+
+libethumb_client_la_SOURCES = \
+ Ethumb_Client.c
+libethumb_client_la_DEPENDENCIES = $(top_builddir)/config.h
+libethumb_client_la_LIBADD = \
+ $(top_builddir)/src/lib/libethumb.la \
+ @EDBUS_LIBS@
+libethumb_client_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -version-info @version_info@
+
+# @EVAS_LIBS@ @ECORE_EVAS_LIBS@ @ECORE_FILE_LIBS@ @EDJE_LIBS@
--- /dev/null
+#ifndef __ETHUMB_PRIVATE_H__
+#define __ETHUMB_PRIVATE_H__ 1
+
+#include <Ethumb.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;
+ float crop_x, crop_y;
+ int quality;
+ int compress;
+ 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;
+};
+
+#endif /* __ETHUMB_PRIVATE_H__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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
--- /dev/null
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS =
+
+if USE_MODULE_EMOTION
+SUBDIRS += emotion
+endif
+
+if USE_MODULE_EPDF
+SUBDIRS += epdf
+endif
+
+DIST_SUBDIRS = \
+ emotion \
+ epdf
--- /dev/null
+
+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)\" \
+ @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 \
+ @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
--- /dev/null
+#include "Ethumb.h"
+#include "Ethumb_Plugin.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <Eina.h>
+#include <Eet.h>
+#include <Ecore_File.h>
+#include <Evas.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 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_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);
+ 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 int
+_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)
+ 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 int
+_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);
+ }
+
+ ecore_timer_add(1.0 / ethumb_video_fps_get(e), _frame_grab, _plugin);
+}
+
+static void
+_generate_thumb(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, "xine");
+ 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;
+ }
+
+ _plugin->video = o;
+
+ ethumb_file_get(e, &file, NULL);
+ f = ethumb_thumb_format_get(e);
+
+ emotion_object_file_set(o, file);
+
+ _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_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);
+ }
+ else
+ {
+ ecore_timer_add(0.1, _frame_grab_single, _plugin);
+ }
+
+ _video_pos_set(_plugin);
+ emotion_object_play_set(o, 1);
+ evas_object_show(o);
+}
+
+EAPI Ethumb_Plugin *
+ethumb_plugin_get(void)
+{
+ static const char *extensions[] = { "avi", "mp4", "ogv", "mov", "mpg", "wmv",
+ NULL };
+ static Ethumb_Plugin plugin =
+ {
+ extensions,
+ _generate_thumb,
+ };
+
+ _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);
--- /dev/null
+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";
+ }
+ }
+ }
+}
--- /dev/null
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_srcdir)/src/plugins/epdf \
+ @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
--- /dev/null
+#include "Ethumb.h"
+#include "Ethumb_Plugin.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <Eina.h>
+#include <Evas.h>
+#include <Epdf.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,
+ };
+
+ 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);
--- /dev/null
+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
--- /dev/null
+/**
+ * @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 <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;
+}