Friday, August 26, 2005

Exploring E17 Modules - Part I

DISCLAIMER: This is not your traditional tutorial where someone that knows somthing tells you about it. No. In this case, this is someone learning about something and writing down thoughts. That it happens to feel somewhat like a tutorial is a side effect. That said. Njoi :-)

After much procrastination, yesternite, i was plain restless, I looked at the numerous projects I have at different stages of completion on my box, and decided to instead look at something different: E17 Modules.

After my fair success with pysystray(http://datavibe.net/~essiene/pysystray), I've been thinking of doing something similar in E17. The only problem is having the time to sit down to understand what is really going on. So yesterday, I took the first plunge.

Its amazing what barriers knowledge can pose, and consequently, what barriers you can surmount, when you have the knowledge. My main problem earlier had been setting up a build environment for a module. I hadn't really understood the working of Autotools (from the angle of the developer), so a few weeks back, I did a Google for AutoTools tutorials and came up with a free book :). After reading upto chapter 12, I really was *enlightened* :-), and finally, looking around the e_modules source tree, I suddenly understood how to insert my own module into the whole e_modules build process. (I'll expantiate on this latter, just incase you are like me, and didn't quite grab it at first).

Anyways, my approach is a very bare bones approach. I took the in-tree weather module, stripped it to its barest components. And created a module i called 'hello' :). And from there, I'll start building up as I keep striving to understand the whole architecture.

My first findings (which are not really news anyways):

1. There are some functions that MUST be supplied by the module (this is akin to implementing an Interface if you're thinking OOP). e_modapi_init(), e_modapi_shutdown, e_modapi_save(), e_modapi_info(), e_modapi_about().

2. Each of these functions takes a single argument. A pointer to an E_Module.

3. The E_Module structure, contains all the information you need to know/tweak for your module.

4. The order goes like this:
a. When you load your module, nothing in your code is called (same when you unload your module). The work starts when you Enable your module or when you Start/Restart E while your module is marked as Enabled
b. When your module gets started some funky stuff happens to load the module into memory, and the first function from your module to be called in e_modapi_info(). In e_modapi_info(), you're supposed to set distinctive characteristics of your module. Like the icon file, the edje file, and some other things like that. In my bare module. I didn't do anything there... just to get stuff running.

c. Next, E's config system queues your e_modapi_save() to be called at a latter time when the system is idle. (Thnx to raster for this explanation, I had it wrong before). e_modapi_save() is actually supposed to be used to persist data used by your module. (more on this latter). In this bare module. I left this blank as well.

d. Finally, you e_modapi_init() is called. Remember this function takes an E_Module *, but returns a void*. Some important things need to happen here. Firstly, You _SHOULD_ return a valid pointer to a data structure that will be used by your module. If you return a NULL the init will fail. You can be sure that if you return anything that points to memory you can't account for you'll have funky results ;) (Be my guest... try this, and lemme know how long it took for you CPU to melt :-) ).
The strategy here, is to create a simple struct that will contain all the info that you need to do you stuff, and return a pointer to that struct. What E does, is take your return value and store it in the E_Module->data attribute, where it will always be available from that point onwards.
Imagine you have this.

typedef struct _hello {
char* message;
} hello;

typedef hello* Hello;

Let that struct be your module Hello struct. for e_modapi_init(), we'll have this:

void*
e_modapi_init(E_Module *m)
{
Hello h = NULL;

/*normally you check for api version here and bail out if version is wrong. lets just ignore that for now*/

h = (Hello) malloc(sizeof(hello)); /*you should usually ecapsulate this in a function like hello_new() that does this and some other stuff for you.*/

if(!h) {
e_error_dialog_show("Hello Module", "Initialize Failed!"); /* I'm adding this so we can see what's going on.*/
return NULL;
}

e_error_dialog_show("Hello Module", "Initialized Successfully!");
e_error_dialog_show("Hello Module", "Hello World!"); /* Finally our own e_module hello world... now the world is at peace ;) */
return h;
}

That's a very basic e_modapi_init() to get you going. When you being thinking of saving and restoring data, a bit more will need to be done here, and when you throw in menus, GUI, config stuff... that baby can begin to do realy much more... for now tho.. this is all you need.

e. After init returns. You module should be fully running. There are two more important hooks that you need to take care of. e_modapi_save(), and e_modapi_shutdown(). Like I mentioned earlier, e_modapi_save() is used to persist data. And we don't really want to do any persisting now, so we ignore it. On the other hand e_modapi_shutdown(), will be called when we Disable our Module, or when E is going down (for good, or for a restart). What we need to do in shutdown() is to release all memory we claimed in init(). But how do we get that data? The E_Module struct has a number of attributes we should get very friendly with, but right now, what we're iterested in, is the data struct we initialized in init(), That data struct is stored in the 'data' attribute of our E_Module struct. In our case this should suffice.

int
e_modapi_shutdown(E_Module *m)
{
Hello h = NULL;

h = m->data; /* this is how we get back our Hello struct from the module subsystem. There are other attributes that hold other things, face, menu, etc. ignore for now */

free(h);

e_error_dialog_show("Hello Module", "Shutdown Completed");

return 1;
}

In our case, that simple function will do our shutdown for us.

f. Lets quickly run over the info, save, and about. Remember, e_modapi_info() is the first function that is called in your module. Its supposed to help setup information on your module, like label, icon_file (which shows up in the menu), etc. about is called when the about menu item is clicked. Typically should just give info on your module and exit, but hey.... you can write a webserver inside there if you're so inclined :), just return 1 when you're done. For save/info, we'll do nothing. Here we go:

int
e_modapi_info(E_Module *m)
{
return 1;
}

int
e_modapi_save(E_Module *m)
{
return 1;
}

int
e_modapi_about(E_Module *m)
{
e_error_dialog_show("Hello Module",
"A hello wold module for E17. To appease the gods of tutorials.\n"
"If we don't have this, we may be banished to debugging the great "
"Aiii Eee for the rest of our days! \n\n"
" Visit: http://essiene.blogspot.com/2005/07/travellers-log-1.html "
"for more information :)"
);

return 1;
}


5. Finally comes the part that kept me from doing this for a very long time. I didn't understand the compilation process. Well, now I understand it a bit more, thanks to reading up autotools (btw, FREE BOOKS ROXS :-) )

The _easiest_ way *THAT I* found to compile my module was to add it to the e_module tree from CVS. If you already have that proceed. Else, check http://www.get-e.org for instructions on how to grab E from CVS. You only need the e_module tree really. So this could be a quick start here. The password for anonymous is blank. Remember that.

$ cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/enlightenment login
$ cvs -z3 -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/enlightenment co e17/apps/e_modules

These two commands will check out e_modules for you:

The e_modules directory will contain this directory src/modules, where all the other modules exist.
a. Create a new directory called hello in the src/modules directory.
b. Type your source files in here. Traditionally, the files are named e_mod_main.c and e_mod_main.h (for any custom headers you want to define and function prototypes). Remember to include the correct header files, e.h for all the e stuff, and any other functions you use. (stdlib.h for malloc and free)
c. You'll need a file Makefile.am, that is used by autotools to generate Makefile.in which is used by configure to generate Makefile. (pretty twisted huh? Yup... if you don't understand it that is :) )
d. For that, you can copy the Makefile.am from the flame module directory, into your directory, and rename all instances of 'flame' to 'hello'
e. Some files are mentioned in that Makefile.am that you don't have currently, so i suggest that you copy module_icon.png from the flame module over as well. Don't wory, it won't break anything.
f. Now move into the parent directory (modules), and edit the Makefile.am there. add 'hello' to the end of the line that says SUBDIRS = . (this enables your module folder to be processed)
g. Step upto the e_modules root folder and edit configure.in. At the bottom of the file, there will be a set of lines that look like: src/modules/flame/Makefile. Add your line after the rest, but before the square brackets. Something like this should suffice: src/modules/hello/Makefile
h. Save and exit configure.in
i. If you fetched from CVS, this is the time to type ./autogen.sh in the e_modules root (if you downloaded a tarball, then just go ahead and type ./configure, specifying the neccessary options to configure/autogen.sh)
j. go ahead and make with make install
k. load the module like you would load other modules
l. enable it. You should see your message boxen now :)


Well, that's how far I got last night. Before I started delving into the E sources trying to understand more stuff, also asking questions on IRC. In the next log, i'll attempt to tackle persisting information, and maybe adding menus and or faces. I hope this helps someone out there :)

7 Comments:

At Friday, August 26, 2005 5:00:00 PM, Anonymous Ralph said...

Wow that's great. I was looking into writing a module for e17, but couldn't figure it out. Thx !

 
At Friday, August 26, 2005 5:10:00 PM, Blogger Essien Ita Essien said...

I'm glad it's usefull :)

I'm still learning more about it, and i'll write more as I learn.

thnx for the feedback. Encouraging :)

 
At Saturday, August 27, 2005 12:20:00 AM, Anonymous sharon said...

I agree entirely with your profile comment about beauty. Funny, that's exactly the impression that I get when I open enlightenment. It's this "Aaaaaaahhhh, yes. That's what I wanted." feeling.

 
At Saturday, August 27, 2005 11:46:00 AM, Anonymous ngc said...

Great work!
I hope to read more about adding libs etc. to the compilation process.

 
At Monday, August 29, 2005 10:51:00 AM, Blogger Essien Ita Essien said...

Yup sharon... simple is well... simply beautiful :)

ngc: I'll get around to it :)

thnx for the great feedback folks.

 
At Monday, August 29, 2005 1:38:00 PM, Blogger Essien Ita Essien said...

This article is now a proper tutorial (I had to remove that lovely disclaimer :) ),

And its currently hosted on edevelop. http://www.edevelop.org

 
At Wednesday, March 29, 2006 11:05:00 AM, Anonymous Zukero said...

Just to update this great tutorial, cvs checkout of e_modules is now done by using
$ cvs -z3 -d:pserver:anonymous@anoncvs.enlightenment.org:/cvsroot/enlightenment checkout e17/apps/e_modules

No need to use the first command (login) with this new anon cvs.

 

Post a Comment

<< Home