Recent posts (max 5) - Browse or Archive for more

Elementary + WebKit-EFL = Elm_Web

The long overdue introduction to Elm_Web is finally here, thanks in part to this little blog being back, and in part to me being reminded for the sixth time that I had still to put it up. But those are unimportant details, what matters is what follows.

What

Elm_Web is a new Elementary widget that provides a way to display Web content in your Elementary applications. It uses WebKit's EFL port, wrapped around to integrate properly within Elementary's infrastructure and to provide a simpler API, so it can be used without having to worry about needing to write the boilerplate code required to get things up and running.

Why

WebKit-EFL provides two main classes that developers can interact with to control how their documents will be shown. Ewk_View and Ewk_Frame.

Ewk_Frame is a lower level object and there's one for each frame as the loaded document contains. As such, they are created by WebKit as needed, but things like key or mouse events and scrolling the frame contents need all be fed by someone else to be processed.

Ewk_View is the higher level object the developer will actually interact with. It always contains at least one Ewk_Frame in it and it abstracts almost every part of the frame API, so that developers need only work with specific Ewk_Frame objects when it's absolutely required.

Still, the Ewk_View class demands that a lot of boilerplate code be provided to get something more than the bare basics. It's implemented as a Smart Object with its class being public, so applications have to define most of it to handle things like JavaScript dialogs, mouse events for touchscreen style scrolling and requests to open a new window.

Elm_Web simplifies this. It provides default implementations for the items mentioned above, while allowing the user to override them by giving their own callback functions to handle each event. It takes care of finding and setting the theme path for WebKit to use when rendering its internal widgets and attempts to provide an API similar to those of other Elementary widgets.

However, Elm_Web is meant to be a simple layer on top of WebKit-EFL, intended to provide a simple way for applications to have a Web widget where to display simple pages, online help or simple markup content. It is not meant to write a full featured browser, at least in its current state.

The API provided is the most common subset of functions that the Ewk_View object supports, but if something more advanced is needed, it's always possible to get the internal Ewk_View object and operate on it directly. Note must be taken though, that not everything can be changed, and some things may enter in direct conflict with how Elementary handles the object internally.

How

So how do we get this new toy you ask? Assuming you already know how to build the EFL (here's some help), you would now need to get and build WebKit before Elementary.

Detailed instructions on how to get WebKit can be found in its own wiki, right  here.

Once WebKit is built (it can take a long time, depending on the computer), it's time to build Elementary. If everything's right, it should auto-detect the WebKit installation and inform that Web support will be built in its configure output. If it doesn't find it, make sure that the PKG_CONFIG_PATH environment variable contains the path where ewebkit.pc can be found, usually something like "/usr/local/lib/pkgconfig".

When the build finishes, check out the Web test in elementary_test and the example in src/examples/web_example.c to see it in action.

To start using it in your own programs, refer to the documentation that can be found  here.

And if you want to help improve the widget, refer to the next point.

Pending

There's always work to do and there are always ways to improve things, so here are a few pending items, in no particular order.

  • Each Ewk_View object requires a theme to be set to it so it can render the internal objects like buttons, textarea, scrollbars, etc. Elm_Web will do this for you by looking in the list of themes set for the program (in order, going from overlays to main theme and then extensions) looking for one that provides the "webkit/base" group. Once it finds one, it will set it as the theme for its internal view object. If no theme contains this group, it will fall back to WebKits default theme, using the path to its data directory found at built time. The default theme for Elementary currently has no theme for WebKit, so it will be always using the default, which doesn't exactly match the looks of the rest of the toolkit. It would be nice to have a theme using the same graphics as the rest of the toolkit, to make things a bit more integrated.
  • Panning, scrolling, thumb dragging... all of these are implemented by handling the events coming from the Ewk_View object, and not integrating with the scroller widget in Elementary. The reason being that the view object is not just one simple object with a defined size that can be freely moved around, but a container for an unknown number of elements that can be scrollable themselves.
  • JavaScript dialogs have their default implementation and users can override them by providing function callbacks to handle the creation and managing of said dialogs. However, to completely disable them, an implementation of them returning NULL for the object is needed. Maybe it would be a good thing to have a flag to have the widget ignore their requests entirely.
  • Your idea here.

Eina_Simple_XML Tutorial

Eina_Simple_XML is a library that should make it easy to handle XML files while consuming very little (or no) memory. Before we start I've got to mention the caveat: to preserve simplicity Eina_Simple_XML does not implement a few XML features such as namespaces, encoding and entities, it's however capable of parsing simple XML files.

Eina_Simple_XML provides a  SAX like API, you give it a string and a callback function to be called every time it finds a relevant token. This is all a lot easier to understand by looking at an example, so let's do that. We are first going to parse and then add to this XML file:

<phonebook>
        <contact>
                <name>John Doe</name>
                <phone>+55(19)5555-7777</phone>
                <email>john.doe@email.com</email>
                <dob>1968-09-19</dob>
        </contact>
        <contact>
                <name>Jane Doe</name>
                <phone>+55(19)6666-7777</phone>
                <email>jane.doe@email.com</email>
                <dob>1968-09-21</dob>
        </contact>
        <contact>
                <name>Bill</name>
                <phone>+55(19)7777-7777</phone>
                <email>bill@cool_emails.com</email>
                <dob>1968-09-23</dob>
        </contact>
</phonebook>

In the parsing stage we're going to not just look at the data but actually populate a list of contacts:

typedef struct _contact
{
    char *name;
    char *phone;
    char *email;
    char *dob;
} contact;

Now that we've defined the data structure to be used we need to create the function that we'll use as a callback to the parser. This function treats 4 situations, the first is the contact open tag, the second is the open tag of name, phone, email or dob, the third is for data elements and finally the contact close tag. In the first case we create a new contact, in the second we set a state variable so that we know where to put the data we receive next, and the final situation is when the contact is fully created and we add it to the list.

typedef enum _State
{
    NAME,
    PHONE,
    EMAIL,
    DOB
} State;

static Eina_List *contacts;

static Eina_Bool _parse(void *data, Eina_Simple_XML_Type type, const char *content, unsigned offset, unsigned length)
{
    static contact *cur = NULL;
    static State s;

    if(type == EINA_SIMPLE_XML_OPEN && !strncmp("contact", content, length))
        cur = malloc(sizeof(contact));
    else if(cur && type == EINA_SIMPLE_XML_OPEN) {
        if(!strncmp("name", content, length))
            s = NAME;
        else if(!strncmp("phone", content, length))
            s = PHONE;
        else if(!strncmp("email", content, length))
            s = EMAIL;
        else if(!strncmp("dob", content, length))
            s = DOB;
    } else if(cur && type == EINA_SIMPLE_XML_DATA) {
        char *ptr = strndup(content, length);
        switch(s) {
            case NAME:
                cur->name = ptr;
                break;
            case PHONE:
                cur->phone = ptr;
                break;
            case EMAIL:
                cur->email = ptr;
                break;
            case DOB:
                cur->dob = ptr;
                break;
        }
    } else if(cur && type == EINA_SIMPLE_XML_CLOSE && !strncmp("contact", content, length)) {
        contacts = eina_list_append(contacts, cur);
        cur = NULL;
    }
    return EINA_TRUE;
}

Creating a new contact is even simpler, as you can see the function creates a contact tag, and a name, phone, email and dob tag. When creating each tag we specify the parent and after the tag is created we add a data node as it's child:

void create_contact(Eina_Simple_XML_Node_Tag *phonebook, const char *n, const char *p, const char *e, const char *d) {
    Eina_Simple_XML_Node_Tag *c, *n_tag, *p_tag, *e_tag, *d_tag;
    c = eina_simple_xml_node_tag_new(phonebook, "contact");
    n_tag = eina_simple_xml_node_tag_new(c, "name");
    eina_simple_xml_node_data_new(n_tag, n, strlen(n));
    p_tag = eina_simple_xml_node_tag_new(c, "phone");
    eina_simple_xml_node_data_new(p_tag, p, strlen(p));
    e_tag = eina_simple_xml_node_tag_new(c, "email");
    eina_simple_xml_node_data_new(e_tag, e, strlen(e));
    d_tag = eina_simple_xml_node_tag_new(c, "dob");
    eina_simple_xml_node_data_new(d_tag, d, strlen(d));
}

So we've seen a function to use as a callback in parsing the phonebook and a function to create contacts in the phonebook, now we look at how we tie all of this together. Our main function does a quite a bit of boiler plate stuff that I'll leave as an exercise to the reader to figure out.

int main(int argc, char **argv)
{
    //Just declaring and initializing some variables
    Eina_List *l;
    char *buf, ch;
    int i;
    FILE *f = fopen("phonebook.xml", "r");
    Eina_Simple_XML_Node_Root *root;
    Eina_Simple_XML_Node *phonebook;
    contact *cur;

    eina_init(); //Always initialize eina before using it

    //Here we read the contents of the file into a variable and then close the file
    fseek(f, 0, SEEK_END);
    buf = malloc(sizeof(char) * ftell(f)); //We don't care about the EOF char
    fseek(f, 0, SEEK_SET);

    for(i = 0; (ch = fgetc(f)) != EOF; i++)
        buf[i] = ch;
    fclose(f); //Free resources as soon as we're done with done

    eina_simple_xml_parse(buf, i, EINA_TRUE, _parse, NULL);

    EINA_LIST_FOREACH(contacts, l, cur)
        printf("%s: %s\n%s - %s\n\n", cur->name, cur->phone, cur->email, cur->dob);
    EINA_LIST_FREE(contacts, cur) {
        free(cur->name);
        free(cur->phone);
        free(cur->email);
        free(cur->dob);
        free(cur);
    }

    root = eina_simple_xml_node_load(buf, i, EINA_TRUE);
    free(buf); //We won't need this anymore so free

    phonebook = EINA_INLIST_CONTAINER_GET(root->children, Eina_Simple_XML_Node);
    create_contact((Eina_Simple_XML_Node_Tag*)phonebook, "greg", "0000-1111", "greg@good-guy.com", "1988-02-30");
    buf = eina_simple_xml_node_dump((Eina_Simple_XML_Node*)root, "\t");

    //Now that we have the new XML in a variable let's put it back in the file
    f = fopen("phonebook.xml", "w");
    fputs(buf, f);
    fclose(f);
    free(buf);
    eina_simple_xml_node_root_free(root);

    eina_shutdown();

    return 0;
}

The first bit here that we should look at is the eina_simple_xml_parse() call, this is what is doing all the work and calling our _parse() function, the first parameter we give it is the content of the phonebook.xml file, the second the size(in chars/bytes), the third is an EINA_BOOL telling it to trim whitespace, finally we give it the callback function and a NULL since we don't need to receive anything on it. Just to make sure the parsing worked we then print the list of contacts and free it.

That's all for the parsing, let's now talk about the creation of another contact. The first thing to do is to have Eina_Simple_XML create a tree of the document for us and get the root element, which is a single call to eina_simple_xml_node_load(). Once we have the root node we get it's first(and only) child, which, since we know the structure of the document, we know is the "phonebook" node, from then on it's a simple matter of calling the previously discussed(and shown) function and eina_simple_xml_node_dump() to get the resulting XML. The rest of the code is just file and memory handling, which should be very straitghforward and easy to understand.

Now that I've thrown all this code at you I need to give another warning, DON'T use this as is. If you're wandering why I showed you all of this if you shouldn't use, it's a simple matter, to avoid polluting the code with a lot of stuff that isn't directly relevant I removed all error checking. If you want to parse your own XML files feel free to use this code as a base and modify as needed(e.g.: add error checking =) ).

Windows Installer

As I think that the release is not that far, I built another Windows installer:

 http://www.maths.univ-evry.fr/pages_perso/vtorri/files/Efl-1.1.0.exe

It can install the EFL up to Edje, plus Elementary and Expedite. It's not an "official" 1.1.0 release contrary to the name of the file. I just took the code in the svn the 18th September 2011. But I don't think that there will be a lot more features for 1.1.0 release.

If you have some time, feel free to try it.

There are some bugs, and elm tests that are not all working (factory test, images which path is hardcoded, ...)

I tested it on Win XP 32 bits. So on Win > XP, there could be some problems. Especially, try to enable the XP compatibility mode, it shoud help a bit.

Main improvements compared to 1.0 :

  • ecore_con, which should work (hence ecore_ipc too)
  • resize of the windows, which is faaaaar smoother than in the 1.0 release.
  • Posted: 2011-09-18 23:27
  • Author: vtorri
  • Categories: (none)
  • Comments (0)

Got docs?

Two months passed, and with them the documentation project sponsored by Samsung came to an end. During this time, ProFUSION guys Jonas Gastal, Rafael Antognolli, Gustavo Lima, Bruno Dilly, Flávio Ceolin and yours truly, covered several aspects of the EFL adding documentation where it was missing, expanding when it was not enough and writing examples of each component to help make sense out of them.

A summary of what was done:

  •  Eina
    • List and Inlist
    • Hash
    • Array
    • Stringshare, strbuf and str
    • Log, Magic and Error
    • Iterator and Accessor
    • File
    • Tiler
  •  Eet
    • Basic file operations
    • Serialization of user structures
    • Image saving and loading
  •  Evas
    • Canvas functions, creation and basic handling
    • General manipulation of Evas_Object's
    • Functions specific to each type of object: Image, Text, Box, Table
    • Smart Objects
    • Map
    • Size hints
  •  Ecore
    • The main loop
    • Timers, animators and pollers
    • Threads pool
    • Pipe
    • File handlers
    • Events, jobs and idlers
    • Ecore_Con and Ecore_Con_Url
    • Ecore_Evas
  •  Edje
    • The entire C API (sorry, no Edc tutorials this time around)
  •  Emotion (Yes! It is documented now!)
    • Creation, play and seek controls
    • Audio controls
    • Other media info
  •  Elementary
    • Errr... a lot really.
    • There's a widget list now in the docs with a preview screenshot of each.

A lot of good work has been done, but in no way this means it's perfect. It's up to users and developers now to use these weird new things we were not used to before (the docs, get it?). Read them when you know what you want but are not sure of how to use it. Browse them when you don't know what to use and discover a world of possibilities (I'm getting a bit cheesy here).

And report back any problems. Things are not entirely clear? Something documented doesn't match the program's result? Is that a bug in the documentation or in the code? Just like any other piece of code written, it will improve as people uses it and the rough edges get polished. The project may have ended, but the work itself goes on.

MacOSX installation

I have documented the installation of e17 and several EFL apps on MacOSX, using XQuartz (Apple's X11 implementation) and a standard OS environment without Fink or Mac Ports installed. It is built with svn source code of March 2011 with no OS-specific patches needed. The instructions also describe minor patches needed to compile the very latest svn source code. Requires MacOSX Leopard (10.5) or Snow Leopard (10.6), the latest XQuartz distribution, and the latest XCode development package from Apple.

The page is here:  http://trac.enlightenment.org/e/wiki/MACOSX
Please test and report any problems.

Click on the Comments link to see a screenshot.

The installed EFLs are: eina, eet, evas, ecore, embryo, edje, efreet, e_dbus, elementary, ethumb, epdf, eio, enlil, libeweather, emotion
Working EFL apps include: e17, ephoto, enki, enjoy, envision, eyelight, e_phys_demo, econcentration
Known issues:
I am investigating problems with the compilation of enna
I am investigating a MacOS-compatible hardware abstraction layer to use with eeze (hal, udev don't work with Darwin)
I am investigating very minor runtime issues in e17
Of course any help is welcome.

If there is sufficient interest I will create a binary installation packages for MacOSX.