Tizen Native API
5.0
|
In this example we illustrate how to interact with canvas' (and its objects') events, including the key input ones. We also demonstrate precise point collision on objects and canvas "obscured regions", here.
The example application consists of a window with a white background and an image -- the Enlightenment logo. The application begins with this image switching back and forth into two sizes: the exact canvas' size and one quarter of it (when it's placed on the top left quadrant). Thus, we'll have an animation going on, with image states set to change each 2 elapsed seconds.
There's a global variable to aid accessing our desired context variables from anywhere in the code:
struct test_data { Ecore_Evas *ee; Evas *canvas; Evas_Object *img, *bg; Ecore_Timer *resize_timer, *freeze_timer; Eina_Bool obscured, focus; }; static struct test_data d = {0};
What interests us there are the canvas
pointer, our image handle -- img
-- and the background one, bg
.
The first interesting thing on the example is the registration of a callback on each canvas resizing event, where we put our canvas' size and the background rectangle's one in synchrony, so that we don't get bogus content on rendering with canvas resizes:
ecore_evas_callback_resize_set(d.ee, _canvas_resize_cb);
/* here to keep our example's window size and background image's * size in synchrony */ static void _canvas_resize_cb(Ecore_Evas *ee) { int w, h; ecore_evas_geometry_get(ee, NULL, NULL, &w, &h); evas_object_resize(d.bg, w, h); }
Than, after grabbing our canvas pointer from the Ecore Evas helper infrastructure, we registrate an event callbacks on it:
evas_event_callback_add(d.canvas, EVAS_CALLBACK_RENDER_FLUSH_PRE, _render_flush_cb, NULL); if (evas_alloc_error() != EVAS_ALLOC_ERROR_NONE) { fprintf(stderr, "ERROR: Callback registering failed! Aborting.\n"); goto panic; } evas_event_callback_add(d.canvas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_IN, _object_focus_in_cb, NULL); if (evas_alloc_error() != EVAS_ALLOC_ERROR_NONE) { fprintf(stderr, "ERROR: Callback registering failed! Aborting.\n"); goto panic; } /* two canvas event callbacks */
/* render flush callback */ static void _render_flush_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, void *event_info EINA_UNUSED) { fprintf(stdout, "Canvas is about to flush its rendering pipeline!\n"); }
When one resizes the canvas, there's at least one operation it has to do which will require new calculation for rendering: the resizing of the background rectangle, in a callback we already shown you.
The creation of our background rectangle is so that we give it a name, via evas_object_name_set() and we give it the canvas focus:
d.bg = evas_object_rectangle_add(d.canvas); evas_object_name_set(d.bg, "our dear rectangle"); evas_object_color_set(d.bg, 255, 255, 255, 255); /* white bg */ evas_object_move(d.bg, 0, 0); /* at canvas' origin */ evas_object_resize(d.bg, WIDTH, HEIGHT); /* covers full canvas */ evas_object_show(d.bg); evas_object_focus_set(d.bg, EINA_TRUE); /* so we get input events */
Still exemplifying events and callbacks, we register a callback on the canvas event of an object being focused:
evas_event_callback_add(d.canvas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_IN, _object_focus_in_cb, NULL); if (evas_alloc_error() != EVAS_ALLOC_ERROR_NONE) { fprintf(stderr, "ERROR: Callback registering failed! Aborting.\n"); goto panic; } /* two canvas event callbacks */
/* called when our rectangle gets focus */ static void _object_focus_in_cb(void *data EINA_UNUSED, Evas *e, void *event_info) { fprintf(stdout, "An object got focused: %s\n", evas_object_name_get(event_info)); fprintf(stdout, "Let's recheck it: %s\n", evas_object_name_get(evas_focus_get(e))); fprintf(stdout, "And again: %s\n", evas_object_focus_get(event_info) ? "OK!" : "Oops, something is bad."); }
In that call, event_info
is going to be the focused object's handle, in this case our background rectangle. We print its name, so you can check it's the same. We check that pointer is the same reported by Evas' API with regard to the newest focused object. Finally, we check whether that object is really flagged as focused, now using an Evas object API function.
The animation we talked about comes from a timer we register just before we start the example's main loop. As we said, the resizing of the image will also force the canvas to repaint itself, thus flushing the rendering pipeline whenever the timer ticks:
d.resize_timer = ecore_timer_add(2, _resize_cb, NULL);
/* put some action in the canvas */ static Eina_Bool _resize_cb(void *data EINA_UNUSED) { int w, h, cw, ch; evas_object_geometry_get(d.img, NULL, NULL, &w, &h); ecore_evas_geometry_get(d.ee, NULL, NULL, &cw, &ch); if (w < cw) evas_object_resize(d.img, cw, ch); else evas_object_resize(d.img, cw / 2, ch / 2); return EINA_TRUE; /* re-issue the timer */ }
static const char *commands = \ "commands are:\n" "\ta - toggle animation timer\n" "\tc - cycle between focus and key grabs for key input\n" "\td - delete canvas callbacks\n" "\tf - freeze input for 3 seconds\n" "\tp - toggle precise point collision detection on image\n" "\tControl + o - add an obscured rectangle\n" "\th - print help\n";
if (strcmp(ev->key, "f") == 0) /* freeze input for 3 seconds */ { fprintf(stdout, "Freezing input for 3 seconds\n"); evas_event_freeze(evas); d.freeze_timer = ecore_timer_add(3, _thaw_cb, NULL); return; }
if (strcmp(ev->key, "d") == 0) /* delete canvas' callbacks */ { fprintf(stdout, "Deleting canvas event callbacks\n"); evas_event_callback_del_full(evas, EVAS_CALLBACK_RENDER_FLUSH_PRE, _render_flush_cb, NULL); evas_event_callback_del_full( evas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_IN, _object_focus_in_cb, NULL); return; }
evas_object_event_callback_add( d.bg, EVAS_CALLBACK_KEY_DOWN, _on_keydown, NULL); if (evas_alloc_error() != EVAS_ALLOC_ERROR_NONE) { fprintf(stderr, "ERROR: Callback registering failed! Aborting.\n"); goto panic; }
/* examine the keys pressed */ static void _on_keydown(void *data EINA_UNUSED, Evas *evas, Evas_Object *o EINA_UNUSED, void *einfo) { const Evas_Modifier *mods; Evas_Event_Key_Down *ev = einfo; fprintf(stdout, "We've got key input: %s\n", ev->key); fprintf(stdout, "It actually came from %s\n", d.focus ? "focus" : "key grab");
ev->key
string (remember the event information struct for key down events is the #Evas_Event_Key_Down one). There's one more trick for grabbing input events on this example -- evas_object_key_grab(). The 'c' command will, when firstly used, unfocus the background rectangle. Unfocused objects on an Evas canvas will never receive key events. We grab, then, the keys we're interested at to the object forcefully: if (d.focus) { evas_object_focus_set(d.bg, EINA_FALSE); fprintf(stdout, "Focused object is now %s\n", evas_focus_get(d.canvas) ? "still valid! Something went wrong." : "none."); ret = evas_object_key_grab(d.bg, "a", 0, 0, EINA_TRUE); if (!ret) { fprintf(stdout, "Something went wrong with key grabs.\n"); goto c_end; } ret = evas_object_key_grab(d.bg, "c", 0, 0, EINA_TRUE); if (!ret) { fprintf(stdout, "Something went wrong with key grabs.\n"); goto c_end; } ret = evas_object_key_grab(d.bg, "d", 0, 0, EINA_TRUE); if (!ret) { fprintf(stdout, "Something went wrong with key grabs.\n"); goto c_end; } ret = evas_object_key_grab(d.bg, "f", 0, 0, EINA_TRUE); if (!ret) { fprintf(stdout, "Something went wrong with key grabs.\n"); goto c_end; } ret = evas_object_key_grab(d.bg, "p", 0, 0, EINA_TRUE); if (!ret) { fprintf(stdout, "Something went wrong with key grabs.\n"); goto c_end; } ret = evas_object_key_grab(d.bg, "o", mask, 0, EINA_TRUE); if (!ret) { fprintf(stdout, "Something went wrong with key grabs.\n"); goto c_end; } ret = evas_object_key_grab(d.bg, "h", 0, 0, EINA_TRUE); if (!ret) { fprintf(stdout, "Something went wrong with key grabs.\n"); goto c_end; } } else /* got here by key grabs */
evas_object_show(d.img); evas_object_event_callback_add( d.img, EVAS_CALLBACK_MOUSE_IN, _on_mouse_in, NULL); evas_object_event_callback_add( d.img, EVAS_CALLBACK_MOUSE_OUT, _on_mouse_out, NULL);
/* mouse enters the object's area */ static void _on_mouse_in(void *data EINA_UNUSED, Evas *evas EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *einfo EINA_UNUSED) { fprintf(stdout, "Enlightenment logo has had the mouse in.\n"); } static void _on_mouse_out(void *data EINA_UNUSED, Evas *evas EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *einfo EINA_UNUSED) { fprintf(stdout, "Enlightenment logo has had the mouse out.\n"); } /* mouse exits the object's area */
if (strcmp(ev->key, "p") == 0) /* toggle precise point * collision detection */ { Eina_Bool precise = evas_object_precise_is_inside_get(d.img); fprintf(stdout, "Toggling precise point collision detection %s on" " Enlightenment logo\n", precise ? "off" : "on"); evas_object_precise_is_inside_set(d.img, !precise); return; }
mods = evas_key_modifier_get(evas); if (evas_key_modifier_is_set(mods, "Control") && (strcmp(ev->key, "o") == 0)) /* add an obscured * rectangle to the middle * of the canvas */ { fprintf(stdout, "Toggling obscured rectangle on canvas\n"); if (!d.obscured) { int w, h; evas_output_viewport_get(evas, NULL, NULL, &w, &h); evas_obscured_rectangle_add(evas, w / 4, h / 4, w / 2, h / 2); } else { int w, h; Eina_Rectangle *rect; Eina_List *updates, *l; evas_output_viewport_get(evas, NULL, NULL, &w, &h); evas_obscured_clear(evas); /* we have to flag a damage region here because * evas_obscured_clear() doesn't change the canvas' * state. we'd have to wait for an animation step, for * example, to get the result, without it */ evas_damage_rectangle_add(evas, 0, 0, w, h); updates = evas_render_updates(evas); EINA_LIST_FOREACH(updates, l, rect) { fprintf(stdout, "Rectangle (%d, %d, %d, %d) on canvas got a" " rendering update.\n", rect->x, rect->y, rect->w, rect->h); } evas_render_updates_free(updates); } d.obscured = !d.obscured; } /* end of obscured region command */
What follows is the complete code for this example.
#ifdef HAVE_CONFIG_H #include "config.h" #else #define PACKAGE_EXAMPLES_DIR "." #endif #include <Ecore.h> #include <Ecore_Evas.h> #include <stdio.h> #include <errno.h> #include "evas-common.h" #define WIDTH (320) #define HEIGHT (240) static const char *img_path = PACKAGE_EXAMPLES_DIR EVAS_IMAGE_FOLDER "/enlightenment.png"; static const char *commands = \ "commands are:\n" "\ta - toggle animation timer\n" "\tc - cycle between focus and key grabs for key input\n" "\td - delete canvas callbacks\n" "\tf - freeze input for 3 seconds\n" "\tp - toggle precise point collision detection on image\n" "\tControl + o - add an obscured rectangle\n" "\th - print help\n"; struct test_data { Ecore_Evas *ee; Evas *canvas; Evas_Object *img, *bg; Ecore_Timer *resize_timer, *freeze_timer; Eina_Bool obscured, focus; }; static struct test_data d = {0}; /* here to keep our example's window size and background image's * size in synchrony */ static void _canvas_resize_cb(Ecore_Evas *ee) { int w, h; ecore_evas_geometry_get(ee, NULL, NULL, &w, &h); evas_object_resize(d.bg, w, h); } /* called when our rectangle gets focus */ static void _object_focus_in_cb(void *data EINA_UNUSED, Evas *e, void *event_info) { fprintf(stdout, "An object got focused: %s\n", evas_object_name_get(event_info)); fprintf(stdout, "Let's recheck it: %s\n", evas_object_name_get(evas_focus_get(e))); fprintf(stdout, "And again: %s\n", evas_object_focus_get(event_info) ? "OK!" : "Oops, something is bad."); } /* render flush callback */ static void _render_flush_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, void *event_info EINA_UNUSED) { fprintf(stdout, "Canvas is about to flush its rendering pipeline!\n"); } /* put some action in the canvas */ static Eina_Bool _resize_cb(void *data EINA_UNUSED) { int w, h, cw, ch; evas_object_geometry_get(d.img, NULL, NULL, &w, &h); ecore_evas_geometry_get(d.ee, NULL, NULL, &cw, &ch); if (w < cw) evas_object_resize(d.img, cw, ch); else evas_object_resize(d.img, cw / 2, ch / 2); return EINA_TRUE; /* re-issue the timer */ } /* let's have our events back */ static Eina_Bool _thaw_cb(void *data EINA_UNUSED) { fprintf(stdout, "Canvas was frozen %d times, now thawing.\n", evas_event_freeze_get(d.canvas)); evas_event_thaw(d.canvas); return EINA_FALSE; /* do not re-issue the timer */ } /* mouse enters the object's area */ static void _on_mouse_in(void *data EINA_UNUSED, Evas *evas EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *einfo EINA_UNUSED) { fprintf(stdout, "Enlightenment logo has had the mouse in.\n"); } static void _on_mouse_out(void *data EINA_UNUSED, Evas *evas EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *einfo EINA_UNUSED) { fprintf(stdout, "Enlightenment logo has had the mouse out.\n"); } /* mouse exits the object's area */ /* examine the keys pressed */ static void _on_keydown(void *data EINA_UNUSED, Evas *evas, Evas_Object *o EINA_UNUSED, void *einfo) { const Evas_Modifier *mods; Evas_Event_Key_Down *ev = einfo; fprintf(stdout, "We've got key input: %s\n", ev->key); fprintf(stdout, "It actually came from %s\n", d.focus ? "focus" : "key grab"); if (strcmp(ev->key, "h") == 0) /* print help */ { puts(commands); return; } if (strcmp(ev->key, "a") == 0) /* toggle animation timer */ { if (d.resize_timer != NULL) { fprintf(stdout, "Stopping animation timer\n"); ecore_timer_del(d.resize_timer); d.resize_timer = NULL; } else { fprintf(stdout, "Re-issuing animation timer\n"); d.resize_timer = ecore_timer_add(2, _resize_cb, NULL); } return; } if (strcmp(ev->key, "c") == 0) /* cycle between focus and key * grabs for key input */ { Eina_Bool ret; Evas_Modifier_Mask mask = evas_key_modifier_mask_get(d.canvas, "Control"); fprintf(stdout, "Switching to %s for key input\n", d.focus ? "key grabs" : "focus"); if (d.focus) { evas_object_focus_set(d.bg, EINA_FALSE); fprintf(stdout, "Focused object is now %s\n", evas_focus_get(d.canvas) ? "still valid! Something went wrong." : "none."); ret = evas_object_key_grab(d.bg, "a", 0, 0, EINA_TRUE); if (!ret) { fprintf(stdout, "Something went wrong with key grabs.\n"); goto c_end; } ret = evas_object_key_grab(d.bg, "c", 0, 0, EINA_TRUE); if (!ret) { fprintf(stdout, "Something went wrong with key grabs.\n"); goto c_end; } ret = evas_object_key_grab(d.bg, "d", 0, 0, EINA_TRUE); if (!ret) { fprintf(stdout, "Something went wrong with key grabs.\n"); goto c_end; } ret = evas_object_key_grab(d.bg, "f", 0, 0, EINA_TRUE); if (!ret) { fprintf(stdout, "Something went wrong with key grabs.\n"); goto c_end; } ret = evas_object_key_grab(d.bg, "p", 0, 0, EINA_TRUE); if (!ret) { fprintf(stdout, "Something went wrong with key grabs.\n"); goto c_end; } ret = evas_object_key_grab(d.bg, "o", mask, 0, EINA_TRUE); if (!ret) { fprintf(stdout, "Something went wrong with key grabs.\n"); goto c_end; } ret = evas_object_key_grab(d.bg, "h", 0, 0, EINA_TRUE); if (!ret) { fprintf(stdout, "Something went wrong with key grabs.\n"); goto c_end; } } else /* got here by key grabs */ { evas_object_key_ungrab(d.bg, "a", 0, 0); evas_object_key_ungrab(d.bg, "c", 0, 0); evas_object_key_ungrab(d.bg, "d", 0, 0); evas_object_key_ungrab(d.bg, "f", 0, 0); evas_object_key_ungrab(d.bg, "p", 0, 0); evas_object_key_ungrab(d.bg, "o", mask, 0); evas_object_key_ungrab(d.bg, "h", 0, 0); evas_object_focus_set(d.bg, EINA_TRUE); } c_end: d.focus = !d.focus; return; } if (strcmp(ev->key, "d") == 0) /* delete canvas' callbacks */ { fprintf(stdout, "Deleting canvas event callbacks\n"); evas_event_callback_del_full(evas, EVAS_CALLBACK_RENDER_FLUSH_PRE, _render_flush_cb, NULL); evas_event_callback_del_full( evas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_IN, _object_focus_in_cb, NULL); return; } if (strcmp(ev->key, "f") == 0) /* freeze input for 3 seconds */ { fprintf(stdout, "Freezing input for 3 seconds\n"); evas_event_freeze(evas); d.freeze_timer = ecore_timer_add(3, _thaw_cb, NULL); return; } if (strcmp(ev->key, "p") == 0) /* toggle precise point * collision detection */ { Eina_Bool precise = evas_object_precise_is_inside_get(d.img); fprintf(stdout, "Toggling precise point collision detection %s on" " Enlightenment logo\n", precise ? "off" : "on"); evas_object_precise_is_inside_set(d.img, !precise); return; } mods = evas_key_modifier_get(evas); if (evas_key_modifier_is_set(mods, "Control") && (strcmp(ev->key, "o") == 0)) /* add an obscured * rectangle to the middle * of the canvas */ { fprintf(stdout, "Toggling obscured rectangle on canvas\n"); if (!d.obscured) { int w, h; evas_output_viewport_get(evas, NULL, NULL, &w, &h); evas_obscured_rectangle_add(evas, w / 4, h / 4, w / 2, h / 2); } else { int w, h; Eina_Rectangle *rect; Eina_List *updates, *l; evas_output_viewport_get(evas, NULL, NULL, &w, &h); evas_obscured_clear(evas); /* we have to flag a damage region here because * evas_obscured_clear() doesn't change the canvas' * state. we'd have to wait for an animation step, for * example, to get the result, without it */ evas_damage_rectangle_add(evas, 0, 0, w, h); updates = evas_render_updates(evas); EINA_LIST_FOREACH(updates, l, rect) { fprintf(stdout, "Rectangle (%d, %d, %d, %d) on canvas got a" " rendering update.\n", rect->x, rect->y, rect->w, rect->h); } evas_render_updates_free(updates); } d.obscured = !d.obscured; } /* end of obscured region command */ } int main(void) { int err; if (!ecore_evas_init()) return EXIT_FAILURE; /* this will give you a window with an Evas canvas under the first * engine available */ d.ee = ecore_evas_new(NULL, 10, 10, WIDTH, HEIGHT, NULL); if (!d.ee) goto error; ecore_evas_callback_resize_set(d.ee, _canvas_resize_cb); ecore_evas_show(d.ee); /* the canvas pointer, de facto */ d.canvas = ecore_evas_get(d.ee); evas_event_callback_add(d.canvas, EVAS_CALLBACK_RENDER_FLUSH_PRE, _render_flush_cb, NULL); if (evas_alloc_error() != EVAS_ALLOC_ERROR_NONE) { fprintf(stderr, "ERROR: Callback registering failed! Aborting.\n"); goto panic; } evas_event_callback_add(d.canvas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_IN, _object_focus_in_cb, NULL); if (evas_alloc_error() != EVAS_ALLOC_ERROR_NONE) { fprintf(stderr, "ERROR: Callback registering failed! Aborting.\n"); goto panic; } /* two canvas event callbacks */ d.bg = evas_object_rectangle_add(d.canvas); evas_object_name_set(d.bg, "our dear rectangle"); evas_object_color_set(d.bg, 255, 255, 255, 255); /* white bg */ evas_object_move(d.bg, 0, 0); /* at canvas' origin */ evas_object_resize(d.bg, WIDTH, HEIGHT); /* covers full canvas */ evas_object_show(d.bg); evas_object_focus_set(d.bg, EINA_TRUE); /* so we get input events */ d.focus = EINA_TRUE; evas_object_event_callback_add( d.bg, EVAS_CALLBACK_KEY_DOWN, _on_keydown, NULL); if (evas_alloc_error() != EVAS_ALLOC_ERROR_NONE) { fprintf(stderr, "ERROR: Callback registering failed! Aborting.\n"); goto panic; } d.img = evas_object_image_filled_add(d.canvas); evas_object_image_file_set(d.img, img_path, NULL); err = evas_object_image_load_error_get(d.img); if (err != EVAS_LOAD_ERROR_NONE) { fprintf(stderr, "ERROR: Image loading failed! Aborting.\n"); goto panic; } else { evas_object_move(d.img, 0, 0); evas_object_resize(d.img, WIDTH, HEIGHT); evas_object_show(d.img); evas_object_event_callback_add( d.img, EVAS_CALLBACK_MOUSE_IN, _on_mouse_in, NULL); evas_object_event_callback_add( d.img, EVAS_CALLBACK_MOUSE_OUT, _on_mouse_out, NULL); } d.resize_timer = ecore_timer_add(2, _resize_cb, NULL); puts(commands); ecore_main_loop_begin(); ecore_evas_free(d.ee); ecore_evas_shutdown(); return 0; error: fprintf(stderr, "you got to have at least one evas engine built and linked" " up to ecore-evas for this example to run properly.\n"); panic: ecore_evas_shutdown(); return -1; }