Canvas widgets for pre-rendered graphics are not anything new of course. Evas is a well-thought solution heavily optimized for images and is also field tested in the Enlightenment window manager and the rest of EFL applications which are under development. If you were reading the previous chapters, shaking your head and thinking that there is nothing extraordinary about Evas, then you might want to reconsider after reading this chapter.
Evas does its own bookkeeping. It knows the whole state of the Canvas at any given moment. While most other Canvas widgets are one layer above the X abstractions (exposing windows and stuff), Evas is rather smart and sophisticated. It frees the programmer from keeping his/her own data-structures for objects shown on screen, and allows one to concentrate on the actual interaction of these objects rather than their memory management.
We could spend a lot of paragraphs talking about this subject, but you could easily understand if we show some pseudocode. We noted on the beginning that this is not a technical document, so don't expect to see details of Evas API. The code presented here is only describing concepts and not showing how Evas works.
Joe Programmer is attempting to create his first canvas application. Joe is a seasoned
C programmer but also knows C++ and maybe Java. He knows that a C API can be
centered around "objects" (think GTK+) and he has some minimal exposure to X11 toolkits.
Before starting actual development, Joe thinks how the API of the Canvas should look. Since
he does not believe that a Canvas is something extraordinary he concludes that adding some
more statements to the program main
should do the trick. Here is what
a Canvas should look like in his mind.
Example 3.1. A simple Canvas in object-oriented style (even for C)
int main() { Canvas *a_canvas; Rectangle *rect; Image *image; a_canvas=create_new_canvas(800,600); rect=create_new_rectangle(); draw_rect_on_canvas(a_canvas,rect); move_rect(rect,10,50); resize_rect(rect, 100,100); image=create_new_from_file("button.png"); draw_image_on_canvas(a_canvas,image); move_image(image, 150,200); show_rect(rect); show_image(image); show_canvas(canvas); //Continue with rest of the program [...] }
Looks rather logical. With that in mind Joe starts to look at the API of the Canvas widget he uses
only to find that things are a bit more complex. He learns that the canvas has a paint
function
which is run when the canvas is redrawn (either at short time periods or after an on-screen event). In order
to make the Canvas draw something he must collect all objects that need to be drawn and pass them to the canvas.
He also has to override/extend/call/redefine (pick your favourite) the paint function of the canvas.
After some coding he finally gets the Canvas to show stuff with the following code listing:
Example 3.2. What happens in reality
//Include files which contain implementation //of linked lists or other data structures. [...] int main() { Canvas *a_canvas; List *objects_to_be_drawn; a_canvas=create_new_canvas(800,600); //Setup a callback. VERY IMPORTANT set_paint_function_of_canvas(a_canvas,my_repaint); rect=create_new_rectangle(); set_coords_rect(rect,10,50); set_size_rect(rect, 100,100); //Append the rectangle to objects drawn by Canvas add_rect(objects_to_be_drawn,rect); image=create_new_from_file("button.png"); set_coord_image(150,200); //Append the image to objects drawn by Canvas add_image(objects_to_be_drawn,image); show_canvas(canvas); repaint(canvas); //Here the my_repaint function is called. //Continue with rest of the program [...] } //Function which smells X11 internals (what happens after an expose event) void my_repaint(canvas *where) { canvas_object *current; while(objects_to_be_drawn !=EMPTY) { current=get_next_object(objects_to_be_drawn); draw_object(where,current); //Finally each object is drawn. } }
While the actual code is not a lot longer than what he had in mind, Joe makes
a couple of observations. First, he is forced to keep by himself all canvas objects in
a separate data structure. The List objects_to_be_drawn
holds
everything that should appear on screen. Joe never actually draws directly on
the Canvas. He just adds objects to this List and informs the Canvas (via a
separate callback function) that it should process it for the
actual drawing. The second important observation is the fact that whichever
function would like to access the canvas cannot directly access the
Canvas
object. Instead it needs both the
Canvas
object and the
objects_to_be_drawn
List which represents its state. To
actually change anything, this List should be changed first and then the Canvas
should be ordered to redraw again.
Later on, Joe needs to do something simple. He wants the user to be able to click an object on the canvas and have that object move on the top left of the screen (at x=10 and y=10). He searches again and again the Canvas API only to discover that he has to write a lot of code himself. The Canvas API is full of functions that draw new objects on screen but almost no functions that deal with existing objects on screen. After some effort and not sure about himself anymore he reaches the following code:
Example 3.3. Moving an object
void user_clicked_on_canvas(canvas *where) { canvas_object *clicked_by_the_user; int x; int y; x=get_pointer_x(where); y=get_pointer_y(where); clicked_by_the_user=search_what_object_is_there(x,y,objects_to_be_drawn); //Let's say it is a rectangle for simplicity set_coords_rect(clicked_by_the_user,10,10); repaint(canvas); //Here the canvas is repainted; } canvas_object *search_what_object_is_there(x,y,objects_to_be_drawn) { canvas_object *current; while(objects_to_be_drawn!=NULL) { current=get_next_object(objects_to_be_drawn); if(is_x_in_object(x,current)==1); { if(is_y_in_object(y,current)==1); return current; //Found it } } }
Joe is a bit disappointed now. He sees that he has to keep the state of canvas in custom data structures, and manually control when the canvas is updated. He also sees that once objects are drawn it is hard to find them again. Complex functions which look into the custom structures must be implemented if the Canvas is to do anything useful rather than act as a simple scenery view. Joe starts to think that he is spending more code on Canvas management instead of the application code. He finally realizes the sad truth. The canvas is just a 2D array of pixels. Once an object is drawn on the Canvas it loses all character. Its pixels are the same as the rest of the Canvas which are not drawn. This Canvas is essentially dumb and "state unaware".
Joe starts to ponder why the situation is like this. If he had a "state-aware" canvas, coding would be much easier. Such a Canvas would know all of its objects. It would be able to access and move them on demand. It would not bother the programmer with Canvas management. The programmer would concentrate on the functionality and not on the Canvas widget. With such a canvas the previous example becomes trivial.
Example 3.4. Moving an object with a state-aware Canvas
void user_clicked_on_canvas(canvas *where) { canvas_object *clicked_by_the_user; int x; int y; x=get_pointer_x(where); y=get_pointer_y(where); //The next function is already implemented by the canvas itself. clicked_by_the_user=check_which_object_is_in(x,y,canvas); //Let's say it is a rectangle for simplicity move_rect(clicked_by_the_user,10,10); }
You can see where this is heading. This ideal "state-aware" Canvas is actually Evas. If you find yourself often in Joe's shoes, include Evas in your programs and never look back again. A whole lot of infrastructure code is already written for you. The Canvas is a self-contained object which is smart enough to know what is going on under the surface.
Evas also is the same Canvas Joe was thinking as ideal (see the first code-listing example). Evas implements a Canvas "as it should be done" in the first place. Everything about Evas is logical. The author of the document is clearly biased, but he believes that if you invest time in Evas, the reward will be great. For big programs where Canvas management overruns the actual application, Evas will show the difference and allow you to build your application as you were imagining it from the beginning, rather than searching all the time the Canvas API for things you need.
Evas is here now. Power your applications with it!