On Fri, 17 Apr 2009, Enlightenment SVN wrote:

Log:
 Added support to plugins.

 Now it's possible to implement new plugins that generate thumbnails
 from file formats that evas doesn't.

why don't you use eina_module ?

Vincent


Author:       antognolli
Date:         2009-04-17 16:33:45 -0700 (Fri, 17 Apr 2009)
New Revision: 40156

Modified:
 trunk/PROTO/ethumb/configure.ac trunk/PROTO/ethumb/m4/ac-modules.m4 
trunk/PROTO/ethumb/src/Makefile.am trunk/PROTO/ethumb/src/bin/ethumb.c 
trunk/PROTO/ethumb/src/lib/Ethumb.c trunk/PROTO/ethumb/src/lib/Ethumb.h

Modified: trunk/PROTO/ethumb/configure.ac
===================================================================
--- trunk/PROTO/ethumb/configure.ac     2009-04-17 23:31:41 UTC (rev 40155)
+++ trunk/PROTO/ethumb/configure.ac     2009-04-17 23:33:45 UTC (rev 40156)
@@ -34,6 +34,11 @@
AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)
AC_DEFINE_UNQUOTED(SYSCONFDIR, ["$SYSCONFDIR"], [Where system configuration is 
stored])

+pluginsdir="${libdir}/ethumb/plugins"
+AC_SUBST(pluginsdir)
+AS_AC_EXPAND(PLUGINSDIR, $pluginsdir)
+AC_DEFINE_UNQUOTED(PLUGINSDIR, ["$PLUGINSDIR"], [Where plugins are installed.])
+
PKG_CHECK_MODULES(EINA, [eina-0])
PKG_CHECK_MODULES(EVAS, [evas])
PKG_CHECK_MODULES(ECORE, [ecore])
@@ -43,6 +48,18 @@

requirement_ethumb="eina-0 evas ecore ecore-evas ecore-file edje"

+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
+
AC_SUBST(requirement_ethumb)

AC_OUTPUT([
@@ -51,6 +68,8 @@
src/Makefile
src/bin/Makefile
src/lib/Makefile
+src/plugins/Makefile
+src/plugins/emotion/Makefile
data/Makefile
data/frames/Makefile
m4/Makefile

Modified: trunk/PROTO/ethumb/m4/ac-modules.m4
===================================================================
--- trunk/PROTO/ethumb/m4/ac-modules.m4 2009-04-17 23:31:41 UTC (rev 40155)
+++ trunk/PROTO/ethumb/m4/ac-modules.m4 2009-04-17 23:33:45 UTC (rev 40156)
@@ -22,7 +22,7 @@
        fi
])

-dnl AC_TCS_CHECK_PKG(name, lib [>= version], [action-if, [action-not]])
+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.
@@ -38,7 +38,7 @@
dnl       - [name]_LIBS: if HAVE_[name]=yes
dnl       - [name]_VERSION: if HAVE_[name]=yes
dnl
-AC_DEFUN([AC_TCS_CHECK_PKG],
+AC_DEFUN([AC_ETH_CHECK_PKG],
[
# ----------------------------------------------------------------------
# BEGIN: Check library with pkg-config: $1 (pkg-config=$2)
@@ -69,7 +69,7 @@
# ----------------------------------------------------------------------
])

-dnl AC_TCS_OPTIONAL_MODULE(name, [initial-status, [check-if-enabled]])
+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
@@ -94,7 +94,7 @@
dnl     - USE_MODULE_[name]=true|false [make, shell]
dnl     - USE_MODULE_[name]=1 if enabled [config.h]
dnl
-AC_DEFUN([AC_TCS_OPTIONAL_MODULE],
+AC_DEFUN([AC_ETH_OPTIONAL_MODULE],
[
# ----------------------------------------------------------------------
# BEGIN: Check for optional module: $1 (default: $2)

Modified: trunk/PROTO/ethumb/src/Makefile.am
===================================================================
--- trunk/PROTO/ethumb/src/Makefile.am  2009-04-17 23:31:41 UTC (rev 40155)
+++ trunk/PROTO/ethumb/src/Makefile.am  2009-04-17 23:33:45 UTC (rev 40156)
@@ -1,3 +1,3 @@
MAINTAINERCLEANFILES = Makefile.in

-SUBDIRS = lib bin
+SUBDIRS = lib bin plugins

Modified: trunk/PROTO/ethumb/src/bin/ethumb.c
===================================================================
--- trunk/PROTO/ethumb/src/bin/ethumb.c 2009-04-17 23:31:41 UTC (rev 40155)
+++ trunk/PROTO/ethumb/src/bin/ethumb.c 2009-04-17 23:33:45 UTC (rev 40156)
@@ -28,6 +28,7 @@
#include <Ethumb.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", NULL };
@@ -115,6 +116,8 @@
      "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_LICENSE('L', "license"),
     ECORE_GETOPT_COPYRIGHT('C', "copyright"),
     ECORE_GETOPT_VERSION('V', "version"),
@@ -123,6 +126,12 @@
  }
};

+static void
+_finished_thumb(Ethumb_File *ef, void *data)
+{
+   ecore_main_loop_quit();
+}
+
int
main(int argc, char *argv[])
{
@@ -139,11 +148,13 @@
   struct frame frame = {NULL};
   const char *thumb_path = NULL;
   const char *thumb_key = NULL;
+   double video_time = 0;
   int arg_index;
   int i;

   int r = 1;
   ethumb_init();
+   ecore_init();

   Ecore_Getopt_Value values[] = {
        ECORE_GETOPT_VALUE_PTR_CAST(geometry),
@@ -153,6 +164,7 @@
        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_BOOL(quit_option),
        ECORE_GETOPT_VALUE_BOOL(quit_option),
        ECORE_GETOPT_VALUE_BOOL(quit_option),
@@ -198,6 +210,8 @@
        eina_stringshare_del(frame.group);
        eina_stringshare_del(frame.swallow);
     }
+   if (video_time > 0)
+     ethumb_video_time_set(e, video_time);

   if (r && arg_index < argc)
     ef = ethumb_file_new(e, argv[arg_index++], src_key);
@@ -209,12 +223,16 @@
   if (ef)
     {
        ethumb_file_thumb_path_set(ef, thumb_path, thumb_key);
-       ethumb_file_generate(ef);
+       r = ethumb_file_generate(ef, _finished_thumb, NULL);
     }

+   if (r)
+     ecore_main_loop_begin();
+
   ethumb_file_free(ef);
   ethumb_free(e);

+   ecore_shutdown();
   ethumb_shutdown();

   return !r;

Modified: trunk/PROTO/ethumb/src/lib/Ethumb.c
===================================================================
--- trunk/PROTO/ethumb/src/lib/Ethumb.c 2009-04-17 23:31:41 UTC (rev 40155)
+++ trunk/PROTO/ethumb/src/lib/Ethumb.c 2009-04-17 23:33:45 UTC (rev 40156)
@@ -25,12 +25,16 @@
#endif
#include <eina_safety_checks.h>
#include "Ethumb.h"
+#include "Ethumb_Plugin.h"
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <dlfcn.h>
#include "md5.h"

#ifndef PATH_MAX
@@ -49,6 +53,12 @@
#define WRN(...) EINA_ERROR_PWARN(__VA_ARGS__)
#define ERR(...) EINA_ERROR_PERR(__VA_ARGS__)

+struct _Ethumb_Plugin_Object
+{
+   Ethumb_Plugin *plugin;
+   void *dl_handle;
+};
+
static int initcount = 0;
static const char *_home_thumb_dir = NULL;
static const char *_thumb_category_normal = NULL;
@@ -57,6 +67,95 @@
static const int THUMB_SIZE_NORMAL = 128;
static const int THUMB_SIZE_LARGE = 256;

+static Eina_Hash *_plugins_ext = NULL;
+static Eina_List *_plugins = NULL;
+
+static struct _Ethumb_Plugin_Object *
+_ethumb_plugin_load(const char *path)
+{
+   char *errmsg;
+   struct _Ethumb_Plugin_Object *p;
+   Ethumb_Plugin *(*init)(void);
+
+   p = calloc(1, sizeof(struct _Ethumb_Plugin_Object));
+
+   p->dl_handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
+   errmsg = dlerror();
+   if (errmsg)
+     {
+       ERR("could not dlopen() %s\n", errmsg);
+       return NULL;
+     }
+
+   init = dlsym(p->dl_handle, "ethumb_plugin_init");
+   errmsg = dlerror();
+   if (errmsg)
+     {
+       ERR("could not find plugin entry point %s\n", errmsg);
+       return NULL;
+     }
+
+   p->plugin = init();
+   if (!p->plugin)
+     {
+       ERR("plugin \"%s\" failed to init.\n", path);
+       return NULL;
+     }
+
+   return p;
+}
+
+static void
+_ethumb_plugins_load(void)
+{
+   DIR *dir;
+   struct dirent *de;
+   char plugin_path[PATH_MAX];
+   struct _Ethumb_Plugin_Object *p;
+
+   _plugins_ext = eina_hash_string_small_new(NULL);
+   EINA_SAFETY_ON_NULL_RETURN(_plugins_ext);
+
+   dir = opendir(PLUGINSDIR);
+   EINA_SAFETY_ON_NULL_RETURN(dir);
+
+   while ((de = readdir(dir)))
+     {
+       const char **ext;
+       if (strncmp(de->d_name + strlen(de->d_name) - 3, ".so", 3))
+         continue;
+       snprintf(plugin_path, 1024, "%s/%s", PLUGINSDIR, de->d_name);
+       p = _ethumb_plugin_load(plugin_path);
+       if (!p)
+         {
+            ERR("couldn't load plugin '%s'\n", plugin_path);
+            continue;
+         }
+       for (ext = p->plugin->extensions; *ext; ext++)
+         eina_hash_add(_plugins_ext, *ext, p->plugin);
+
+       _plugins = eina_list_append(_plugins, p);
+     }
+}
+
+static void
+_ethumb_plugins_unload(void)
+{
+   Eina_List *l;
+
+   eina_hash_free(_plugins_ext);
+   _plugins_ext = NULL;
+
+   l = _plugins;
+   for (l = _plugins; l; l = l->next)
+     {
+       struct _Ethumb_Plugin_Object *p = l->data;
+       p->plugin->shutdown(p->plugin);
+       dlclose(p->dl_handle);
+       free(p);
+     }
+}
+
EAPI int
ethumb_init(void)
{
@@ -67,6 +166,8 @@
     return ++initcount;

   eina_stringshare_init();
+   eina_list_init();
+   eina_hash_init();
   evas_init();
   ecore_init();
   ecore_evas_init();
@@ -79,6 +180,7 @@
   _thumb_category_normal = eina_stringshare_add("normal");
   _thumb_category_large = eina_stringshare_add("large");

+   _ethumb_plugins_load();
   return ++initcount;
}

@@ -88,10 +190,13 @@
   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);
        eina_stringshare_shutdown();
+       eina_list_shutdown();
+       eina_hash_shutdown();
        evas_shutdown();
        ecore_shutdown();
        ecore_evas_shutdown();
@@ -194,6 +299,8 @@
   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);
}

@@ -385,6 +492,14 @@
   return e->category;
}

+EAPI void
+ethumb_video_time_set(Ethumb *e, float time)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+
+   e->video.time = time;
+}
+
EAPI Ethumb_File *
ethumb_file_new(Ethumb *e, const char *path, const char *key)
{
@@ -581,8 +696,8 @@
   return ef->thumb_path;
}

-static void
-_ethumb_calculate_aspect(Ethumb *e, int iw, int ih, int *w, int *h)
+void
+ethumb_calculate_aspect(Ethumb *e, int iw, int ih, int *w, int *h)
{
   *w = e->tw;
   *h = e->th;
@@ -596,8 +711,8 @@
     }
}

-static void
-_ethumb_calculate_fill(Ethumb *e, int iw, int ih, int *fx, int *fy, int *fw, 
int *fh)
+void
+ethumb_calculate_fill(Ethumb *e, int iw, int ih, int *fx, int *fy, int *fw, 
int *fh)
{
   *fw = e->tw;
   *fh = e->th;
@@ -624,6 +739,111 @@
}

static int
+_ethumb_plugin_generate(Ethumb_File *ef)
+{
+   const char *ext;
+   Ethumb_Plugin *plugin;
+   Ethumb *e;
+   int r;
+
+   ext = strrchr(ef->src_path, '.');
+   if (!ext)
+     {
+       ERR("could not get extension for file \"%s\"\n", ef->src_path);
+       return 0;
+     }
+
+   plugin = eina_hash_find(_plugins_ext, ext + 1);
+   if (!plugin)
+     {
+       DBG("no plugin for extension: \"%s\"\n", ext + 1);
+       return 0;
+     }
+
+   e = ef->ethumb;
+   if (e->frame)
+     evas_object_hide(e->frame->edje);
+   else
+     evas_object_hide(e->img);
+
+   r = plugin->generate_thumb(ef);
+
+   return r;
+}
+
+int
+ethumb_plugin_image_resize(Ethumb_File *ef, int w, int h)
+{
+   Ethumb *eth;
+   Evas_Object *img;
+
+   eth = ef->ethumb;
+   img = eth->img;
+
+   if (eth->frame)
+     {
+       edje_extern_object_min_size_set(img, w, h);
+       edje_extern_object_max_size_set(img, w, h);
+       edje_object_calc_force(eth->frame->edje);
+       evas_object_move(eth->frame->edje, 0, 0);
+       evas_object_resize(eth->frame->edje, w, h);
+     }
+   else
+     {
+       evas_object_move(img, 0, 0);
+       evas_object_resize(img, w, h);
+     }
+
+   evas_object_image_size_set(eth->o, w, h);
+   ecore_evas_resize(eth->sub_ee, w, h);
+
+   ef->w = w;
+   ef->h = h;
+
+   return 1;
+}
+
+int
+ethumb_image_save(Ethumb_File *ef)
+{
+   int r;
+   char *dname;
+   Ethumb *eth = ef->ethumb;
+
+   evas_damage_rectangle_add(eth->sub_e, 0, 0, ef->w, ef->h);
+   evas_render(eth->sub_e);
+
+   if (!ef->thumb_path)
+     _ethumb_file_generate_path(ef);
+
+   if (!ef->thumb_path)
+     {
+       ERR("could not create file path...\n");
+       return 0;
+     }
+
+   dname = ecore_file_dir_get(ef->thumb_path);
+   r = ecore_file_mkpath(dname);
+   free(dname);
+   if (!r)
+     {
+       ERR("could not create directory '%s'\n", dname);
+       return 0;
+     }
+
+   r = evas_object_image_save(eth->o, ef->thumb_path, ef->thumb_key,
+                             "quality=85");
+
+   if (!r)
+     {
+       ERR("could not save image.\n");
+       return 0;
+     }
+
+   return 1;
+}
+
+static int
_ethumb_image_load(Ethumb_File *ef)
{
   Ethumb *eth;
@@ -658,7 +878,7 @@
   if ((w <= 0) || (h <= 0))
     return 0;

-   _ethumb_calculate_aspect(eth, w, h, &ww, &hh);
+   ethumb_calculate_aspect(eth, w, h, &ww, &hh);

   if (eth->frame)
     {
@@ -674,64 +894,74 @@
        evas_object_resize(img, ww, hh);
     }

-   _ethumb_calculate_fill(eth, w, h, &fx, &fy, &fw, &fh);
+   ethumb_calculate_fill(eth, w, h, &fx, &fy, &fw, &fh);
   evas_object_image_fill_set(img, fx, fy, fw, fh);

   evas_object_image_size_set(eth->o, ww, hh);
   ecore_evas_resize(eth->sub_ee, ww, hh);

-   evas_damage_rectangle_add(eth->sub_e, 0, 0, ww, hh);
-
   ef->w = ww;
   ef->h = hh;

   return 1;
}

+static int
+_ethumb_finished_idler_cb(void *data)
+{
+   Ethumb_File *ef = data;
+   Ethumb *e = ef->ethumb;
+
+   e->finished_cb(ef, e->cb_data);
+   e->finished_idler = NULL;
+   e->finished_cb = NULL;
+   e->cb_data = NULL;
+
+   return 0;
+}
+
+void
+ethumb_finished_callback_call(Ethumb_File *ef)
+{
+   Ethumb *e = ef->ethumb;
+
+   if (e->finished_idler)
+     ecore_idler_del(e->finished_idler);
+   e->finished_idler = ecore_idler_add(_ethumb_finished_idler_cb, ef);
+}
+
EAPI int
-ethumb_file_generate(Ethumb_File *ef)
+ethumb_file_generate(Ethumb_File *ef, ethumb_generate_callback_t finished_cb, 
void *data)
{
-   Ethumb *eth;
   int r;
-   char *dname;
+   Ethumb *e;

   EINA_SAFETY_ON_NULL_RETURN_VAL(ef, 0);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(finished_cb, 0);

-   if (!_ethumb_image_load(ef))
+   e = ef->ethumb;
+
+   if (e->finished_idler)
     {
-       ERR("could not load input image.\n");
+       ERR("thumbnail generation already in progress.\n");
        return 0;
     }
+   e->finished_cb = finished_cb;
+   e->cb_data = data;

-   eth = ef->ethumb;
-   evas_render(eth->sub_e);
+   r = _ethumb_plugin_generate(ef);
+   if (r)
+     return r;

-   if (!ef->thumb_path)
-     _ethumb_file_generate_path(ef);
-
-   if (!ef->thumb_path)
+   if (!_ethumb_image_load(ef))
     {
-       ERR("could not create file path...\n");
+       ERR("could not load input image.\n");
        return 0;
     }

-   dname = ecore_file_dir_get(ef->thumb_path);
-   r = ecore_file_mkpath(dname);
-   free(dname);
-   if (!r)
-     {
-       ERR("could not create directory '%s'\n", dname);
-       return 0;
-     }
+   r = ethumb_image_save(ef);
+   if (r && finished_cb)
+     ethumb_finished_callback_call(ef);

-   r = evas_object_image_save(eth->o, ef->thumb_path, ef->thumb_key,
-                             "quality=85");
-
-   if (!r)
-     {
-       ERR("could not save image.\n");
-       return 0;
-     }
-
-   return 1;
+   return r;
}

Modified: trunk/PROTO/ethumb/src/lib/Ethumb.h
===================================================================
--- trunk/PROTO/ethumb/src/lib/Ethumb.h 2009-04-17 23:31:41 UTC (rev 40155)
+++ trunk/PROTO/ethumb/src/lib/Ethumb.h 2009-04-17 23:33:45 UTC (rev 40156)
@@ -31,6 +31,7 @@
#endif /* ! _WIN32 */
#endif /* EAPI */

+#include <Ecore.h>
#include <Ecore_Evas.h>
#include <Evas.h>

@@ -63,6 +64,11 @@

typedef enum _Ethumb_Thumb_Aspect Ethumb_Thumb_Aspect;

+typedef struct _Ethumb_Frame Ethumb_Frame;
+typedef struct _Ethumb Ethumb;
+typedef struct _Ethumb_File Ethumb_File;
+typedef void (*ethumb_generate_callback_t)(Ethumb_File *ef, void *data);
+
struct _Ethumb_Frame
{
   const char *file;
@@ -71,8 +77,6 @@
   Evas_Object *edje;
};

-typedef struct _Ethumb_Frame Ethumb_Frame;
-
struct _Ethumb
{
   const char *thumb_dir;
@@ -81,14 +85,20 @@
   int format;
   int aspect;
   float crop_x, crop_y;
+   struct
+     {
+       double time;
+     } video;
   Ethumb_Frame *frame;
   Ecore_Evas *ee, *sub_ee;
   Evas *e, *sub_e;
   Evas_Object *o, *img;
+   Evas_Object *plugin_img;
+   Ecore_Idler *finished_idler;
+   ethumb_generate_callback_t finished_cb;
+   void *cb_data;
};

-typedef struct _Ethumb Ethumb;
-
struct _Ethumb_File
{
   Ethumb *ethumb;
@@ -99,9 +109,7 @@
   int w, h;
};

-typedef struct _Ethumb_File Ethumb_File;

-
EAPI int ethumb_init(void);
EAPI int ethumb_shutdown(void);

@@ -130,11 +138,13 @@
EAPI void ethumb_thumb_category_set(Ethumb *e, const char *category) 
EINA_ARG_NONNULL(1);
EAPI const char * ethumb_thumb_category_get(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 Ethumb_File * ethumb_file_new(Ethumb *e, const char *path, const char 
*key) EINA_MALLOC EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1, 2);
EAPI void ethumb_file_free(Ethumb_File *ef);
EAPI void ethumb_file_thumb_path_set(Ethumb_File *ef, const char *path, const 
char *key) EINA_ARG_NONNULL(1);
EAPI const char * ethumb_file_thumb_path_get(Ethumb_File *ef) 
EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
-EAPI int ethumb_file_generate(Ethumb_File *ef) EINA_ARG_NONNULL(1);
+EAPI int ethumb_file_generate(Ethumb_File *ef, ethumb_generate_callback_t 
finished_cb, void *data) EINA_ARG_NONNULL(1, 2);

#ifdef __cplusplus
}


------------------------------------------------------------------------------
Stay on top of everything new and different, both inside and
around Java (TM) technology - register by April 22, and save
$200 on the JavaOne (SM) conference, June 2-5, 2009, San Francisco.
300 plus technical and hands-on sessions. Register today.
Use priority code J9JMT32. http://p.sf.net/sfu/p
_______________________________________________
enlightenment-svn mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/enlightenment-svn

--
Ce message a été vérifié par MailScanner
pour des virus ou des polluriels et rien de
suspect n'a été trouvé.
Message délivré par le serveur de messagerie de l'Université d'Evry.

------------------------------------------------------------------------------
Stay on top of everything new and different, both inside and 
around Java (TM) technology - register by April 22, and save
$200 on the JavaOne (SM) conference, June 2-5, 2009, San Francisco.
300 plus technical and hands-on sessions. Register today. 
Use priority code J9JMT32. http://p.sf.net/sfu/p
_______________________________________________
enlightenment-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/enlightenment-devel

Reply via email to