1 /**
2  * Ecore example illustrating how to use ecore imf.
3  *
4  * @verbatim
5  * gcc -o ecore_imf_example ecore_imf_example.c `pkg-config --cflags --libs ecore evas eina ecore-evas ecore-imf ecore-imf-evas`
6  * @endverbatim
7  */
8 
9 #include <Ecore.h>
10 #include <Ecore_Evas.h>
11 #include <Ecore_IMF.h>
12 #include <Ecore_IMF_Evas.h>
13 #include <Evas.h>
14 #include <stdio.h>
15 
16 #define WIDTH 480
17 #define HEIGHT 800
18 
19 typedef struct _Entry Entry;
20 
21 struct _Entry
22 {
23    Evas_Object           *rect;
24    Evas_Object           *txt_obj;
25    Evas_Textblock_Style  *txt_style;
26    Evas_Textblock_Cursor *cursor;
27    Evas_Textblock_Cursor *preedit_start;
28    Evas_Textblock_Cursor *preedit_end;
29    Ecore_IMF_Context     *imf_context;
30    Eina_Bool              have_preedit : 1;
31 };
32 
33 static void _imf_cursor_info_set(Entry *en);
34 
35 static void
_mouse_down_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * o EINA_UNUSED,void * event_info)36 _mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info)
37 {
38    Entry *en = data;
39    Evas_Event_Mouse_Down *ev = event_info;
40    if (!en) return;
41 
42    if (en->imf_context)
43      {
44         Ecore_IMF_Event_Mouse_Down ecore_ev;
45         ecore_imf_evas_event_mouse_down_wrap(ev, &ecore_ev);
46         if (ecore_imf_context_filter_event(en->imf_context,
47                                            ECORE_IMF_EVENT_MOUSE_DOWN,
48                                            (Ecore_IMF_Event *)&ecore_ev))
49           return;
50 
51         // ecore_imf_context_reset should be called before calculating new cursor position
52         ecore_imf_context_reset(en->imf_context);
53      }
54 
55    // calculate new cursor position
56 }
57 
58 static void
_mouse_up_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * o EINA_UNUSED,void * event_info)59 _mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info)
60 {
61    Entry *en = data;
62    Evas_Event_Mouse_Up *ev = event_info;
63    if (!en) return;
64 
65    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
66      {
67         _imf_cursor_info_set(en);
68         return;
69      }
70 
71    if (en->imf_context)
72      {
73         Ecore_IMF_Event_Mouse_Up ecore_ev;
74         ecore_imf_evas_event_mouse_up_wrap(ev, &ecore_ev);
75         if (ecore_imf_context_filter_event(en->imf_context,
76                                            ECORE_IMF_EVENT_MOUSE_UP,
77                                            (Ecore_IMF_Event *)&ecore_ev))
78           return;
79      }
80 
81    if (en->rect)
82      {
83         if (evas_object_focus_get(en->rect))
84           {
85              // notify cursor information
86              _imf_cursor_info_set(en);
87           }
88         else
89           evas_object_focus_set(en->rect, EINA_TRUE);
90      }
91 }
92 
93 static void
_entry_focus_in_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * o EINA_UNUSED,void * event_info EINA_UNUSED)94 _entry_focus_in_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info EINA_UNUSED)
95 {
96    Entry *en = data;
97    if (!en) return;
98 
99    if (en->imf_context)
100      ecore_imf_context_focus_in(en->imf_context);
101 
102    // notify the cursor information
103    _imf_cursor_info_set(en);
104 }
105 
106 static void
_entry_focus_out_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * o EINA_UNUSED,void * event_info EINA_UNUSED)107 _entry_focus_out_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info EINA_UNUSED)
108 {
109    Entry *en = data;
110    if (!en) return;
111 
112    if (en->imf_context)
113      {
114         // ecore_imf_context_reset should be called for flushing the preedit string in focus-out event handler
115         ecore_imf_context_reset(en->imf_context);
116         ecore_imf_context_focus_out(en->imf_context);
117      }
118 }
119 
120 static void
_canvas_focus_in_cb(void * data EINA_UNUSED,Evas * e,void * event_info EINA_UNUSED)121 _canvas_focus_in_cb(void *data EINA_UNUSED, Evas *e, void *event_info EINA_UNUSED)
122 {
123    Entry *en;
124    Evas_Object *obj = evas_focus_get(e);
125    if (!obj) return;
126 
127    en = evas_object_data_get(obj, "Entry");
128    if (en)
129      _entry_focus_in_cb(en, NULL, NULL, NULL);
130 }
131 
132 static void
_canvas_focus_out_cb(void * data EINA_UNUSED,Evas * e,void * event_info EINA_UNUSED)133 _canvas_focus_out_cb(void *data EINA_UNUSED, Evas *e, void *event_info EINA_UNUSED)
134 {
135    Entry *en;
136    Evas_Object *obj = evas_focus_get(e);
137    if (!obj) return;
138 
139    en = evas_object_data_get(obj, "Entry");
140    if (en)
141      _entry_focus_out_cb(en, NULL, NULL, NULL);
142 }
143 
144 static void
_imf_cursor_info_set(Entry * en)145 _imf_cursor_info_set(Entry *en)
146 {
147    Evas_Coord x, y, w, h;
148    Evas_Coord cx, cy, cw, ch; // cursor geometry
149    int cursor_pos; // cursor position in chars (Not bytes)
150    Evas_BiDi_Direction dir;
151 
152    if (!en) return;
153 
154    // get cursor geometry
155    if (en->txt_obj)
156      evas_object_geometry_get(en->txt_obj, &x, &y, &w, &h);
157 
158    if (en->cursor && en->imf_context)
159      {
160         evas_textblock_cursor_geometry_get(en->cursor, &cx, &cy, &cw, &ch, &dir, EVAS_TEXTBLOCK_CURSOR_BEFORE);
161 
162         // get cursor position
163         cursor_pos = evas_textblock_cursor_pos_get(en->cursor);
164 
165         ecore_imf_context_cursor_position_set(en->imf_context, cursor_pos);
166         ecore_imf_context_cursor_location_set(en->imf_context, x + cx, y + cy, cw, ch);
167         ecore_imf_context_bidi_direction_set(en->imf_context, (Ecore_IMF_BiDi_Direction)dir);
168      }
169 }
170 
171 static void
_preedit_del(Entry * en)172 _preedit_del(Entry *en)
173 {
174    if (!en || !en->have_preedit) return;
175    if (!en->preedit_start || !en->preedit_end) return;
176    if (!evas_textblock_cursor_compare(en->preedit_start, en->preedit_end)) return;
177 
178    // delete the preedit characters
179    evas_textblock_cursor_range_delete(en->preedit_start, en->preedit_end);
180 }
181 
182 static void
_preedit_clear(Entry * en)183 _preedit_clear(Entry *en)
184 {
185    if (en->preedit_start)
186      {
187         evas_textblock_cursor_free(en->preedit_start);
188         en->preedit_start = NULL;
189      }
190 
191    if (en->preedit_end)
192      {
193         evas_textblock_cursor_free(en->preedit_end);
194         en->preedit_end = NULL;
195      }
196 
197    en->have_preedit = EINA_FALSE;
198 }
199 
200 static Eina_Bool
_ecore_imf_retrieve_surrounding_cb(void * data,Ecore_IMF_Context * ctx EINA_UNUSED,char ** text,int * cursor_pos)201 _ecore_imf_retrieve_surrounding_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, char **text, int *cursor_pos)
202 {
203    // This callback will be called when the Input Method Context module requests the surrounding context.
204    Entry *en = data;
205    const char *str;
206 
207    if (!en) return EINA_FALSE;
208 
209    str = evas_object_textblock_text_markup_get(en->txt_obj);
210 
211    if (text)
212      *text = str ? strdup(str) : strdup("");
213 
214    // get the current position of cursor
215    if (cursor_pos && en->cursor)
216      *cursor_pos = evas_textblock_cursor_pos_get(en->cursor);
217 
218    return EINA_TRUE;
219 }
220 
221 static void
_ecore_imf_event_delete_surrounding_cb(void * data,Ecore_IMF_Context * ctx EINA_UNUSED,void * event_info)222 _ecore_imf_event_delete_surrounding_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, void *event_info)
223 {
224    // called when the input method needs to delete all or part of the context surrounding the cursor
225    Entry *en = data;
226    Ecore_IMF_Event_Delete_Surrounding *ev = event_info;
227    Evas_Textblock_Cursor *del_start, *del_end;
228    int cursor_pos;
229 
230    if ((!en) || (!ev) || (!en->cursor)) return;
231 
232    // get the current cursor position
233    cursor_pos = evas_textblock_cursor_pos_get(en->cursor);
234 
235    // start cursor position to be deleted
236    del_start = evas_object_textblock_cursor_new(en->txt_obj);
237    evas_textblock_cursor_pos_set(del_start, cursor_pos + ev->offset);
238 
239    // end cursor position to be deleted
240    del_end = evas_object_textblock_cursor_new(en->txt_obj);
241    evas_textblock_cursor_pos_set(del_end, cursor_pos + ev->offset + ev->n_chars);
242 
243    // implement function to delete character(s) from 'cursor_pos+ev->offset' cursor position to 'cursor_pos + ev->offset + ev->n_chars'
244    evas_textblock_cursor_range_delete(del_start, del_end);
245 
246    evas_textblock_cursor_free(del_start);
247    evas_textblock_cursor_free(del_end);
248 }
249 
250 static void
_ecore_imf_event_commit_cb(void * data,Ecore_IMF_Context * ctx EINA_UNUSED,void * event_info)251 _ecore_imf_event_commit_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, void *event_info)
252 {
253    Entry *en = data;
254    char *commit_str = (char *)event_info;
255    if (!en) return;
256 
257    // delete preedit string
258    _preedit_del(en);
259    _preedit_clear(en);
260 
261    printf("commit string : %s\n", commit_str);
262 
263    // insert the commit string in the editor
264    if (en->cursor && commit_str)
265      evas_object_textblock_text_markup_prepend(en->cursor, commit_str);
266 
267    // notify the cursor information
268    _imf_cursor_info_set(en);
269 
270    return;
271 }
272 
273 static void
_ecore_imf_event_preedit_changed_cb(void * data,Ecore_IMF_Context * ctx,void * event_info EINA_UNUSED)274 _ecore_imf_event_preedit_changed_cb(void *data, Ecore_IMF_Context *ctx, void *event_info EINA_UNUSED)
275 {
276    // example how to get preedit string
277    Entry *en = data;
278    char *preedit_string;
279    int cursor_pos;
280    Eina_List *attrs = NULL;
281    Eina_List *l;
282    Ecore_IMF_Preedit_Attr *attr;
283    Ecore_IMF_Context *imf_context = ctx;
284    int preedit_start_pos, preedit_end_pos;
285    int i;
286    Eina_Bool preedit_end_state = EINA_FALSE;
287 
288    if (!en || !en->cursor) return;
289 
290    // get preedit string and attributes
291    ecore_imf_context_preedit_string_with_attributes_get(imf_context, &preedit_string, &attrs, &cursor_pos);
292    printf("preedit string : %s\n", preedit_string);
293 
294    if (!strcmp(preedit_string, ""))
295      preedit_end_state = EINA_TRUE;
296 
297    // delete preedit
298    _preedit_del(en);
299 
300    preedit_start_pos = evas_textblock_cursor_pos_get(en->cursor);
301 
302    // insert preedit character(s)
303    if (strlen(preedit_string) > 0)
304      {
305         if (attrs)
306           {
307              EINA_LIST_FOREACH(attrs, l, attr)
308                {
309                   if (attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB1) // style type
310                     {
311                        // apply appropriate style such as underline
312                     }
313                   else if (attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB2 || attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB3)
314                     {
315                        // apply appropriate style such as underline
316                     }
317                }
318 
319              // insert code to display preedit string in your editor
320              evas_object_textblock_text_markup_prepend(en->cursor, preedit_string);
321           }
322      }
323 
324    if (!preedit_end_state)
325      {
326         // set preedit start cursor
327         if (!en->preedit_start)
328           en->preedit_start = evas_object_textblock_cursor_new(en->txt_obj);
329         evas_textblock_cursor_copy(en->cursor, en->preedit_start);
330 
331         // set preedit end cursor
332         if (!en->preedit_end)
333           en->preedit_end = evas_object_textblock_cursor_new(en->txt_obj);
334         evas_textblock_cursor_copy(en->cursor, en->preedit_end);
335 
336         preedit_end_pos = evas_textblock_cursor_pos_get(en->cursor);
337 
338         for (i = 0; i < (preedit_end_pos - preedit_start_pos); i++)
339           {
340              evas_textblock_cursor_char_prev(en->preedit_start);
341           }
342 
343         en->have_preedit = EINA_TRUE;
344 
345         // set cursor position
346         evas_textblock_cursor_pos_set(en->cursor, preedit_start_pos + cursor_pos);
347      }
348 
349    // notify the cursor information
350    _imf_cursor_info_set(en);
351 
352    EINA_LIST_FREE(attrs, attr)
353      free(attr);
354 
355    free(preedit_string);
356 }
357 
358 static void
_key_down_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * o EINA_UNUSED,void * event_info)359 _key_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info)
360 {
361    Entry *en = data;
362    Evas_Event_Key_Down *ev = event_info;
363    Eina_Bool control, alt, shift;
364    if ((!en) || (!ev->key) || (!en->cursor)) return;
365 
366    if (en->imf_context)
367      {
368         Ecore_IMF_Event_Key_Down ecore_ev;
369         ecore_imf_evas_event_key_down_wrap(ev, &ecore_ev);
370         if (ecore_imf_context_filter_event(en->imf_context,
371                                            ECORE_IMF_EVENT_KEY_DOWN,
372                                            (Ecore_IMF_Event *)&ecore_ev))
373           return;
374      }
375 
376    control = evas_key_modifier_is_set(ev->modifiers, "Control");
377    alt = evas_key_modifier_is_set(ev->modifiers, "Alt");
378    shift = evas_key_modifier_is_set(ev->modifiers, "Shift");
379    (void)alt;
380    (void)shift;
381 
382    if (!strcmp(ev->key, "BackSpace"))
383      {
384         if (evas_textblock_cursor_char_prev(en->cursor))
385           {
386              evas_textblock_cursor_char_delete(en->cursor);
387              // notify the cursor information
388              _imf_cursor_info_set(en);
389           }
390          return;
391      }
392    else if (!strcmp(ev->key, "Delete") ||
393             (!strcmp(ev->key, "KP_Delete") && !ev->string))
394      {
395         // FILLME
396      }
397    else if ((control) && (!strcmp(ev->key, "v")))
398      {
399         // ctrl + v
400         // FILLME
401      }
402    else if ((control) && (!strcmp(ev->key, "a")))
403      {
404         // ctrl + a
405         // FILLME
406      }
407    else if ((control) && (!strcmp(ev->key, "A")))
408      {
409         // ctrl + A
410         // FILLME
411      }
412    else if ((control) && ((!strcmp(ev->key, "c") || (!strcmp(ev->key, "Insert")))))
413      {
414         // ctrl + c
415         // FILLME
416      }
417    else if ((control) && ((!strcmp(ev->key, "x") || (!strcmp(ev->key, "m")))))
418      {
419         // ctrl + x
420         // FILLME
421      }
422    else if ((control) && (!strcmp(ev->key, "z")))
423      {
424         // ctrl + z (undo)
425         // FILLME
426      }
427    else if ((control) && (!strcmp(ev->key, "y")))
428      {
429         // ctrl + y (redo)
430         // FILLME
431      }
432    else if ((!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter")))
433      {
434         // FILLME
435      }
436    else
437      {
438         if (ev->string)
439           {
440              printf("key down string : %s\n", ev->string);
441              evas_object_textblock_text_markup_prepend(en->cursor, ev->string);
442           }
443      }
444 
445    // notify the cursor information
446    _imf_cursor_info_set(en);
447 }
448 
449 static void
_key_up_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * o EINA_UNUSED,void * event_info)450 _key_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info)
451 {
452    Entry *en = data;
453    Evas_Event_Key_Up *ev = event_info;
454 
455    if (!en) return;
456 
457    if (en->imf_context)
458      {
459         Ecore_IMF_Event_Key_Up ecore_ev;
460 
461         ecore_imf_evas_event_key_up_wrap(ev, &ecore_ev);
462         if (ecore_imf_context_filter_event(en->imf_context,
463                                            ECORE_IMF_EVENT_KEY_UP,
464                                            (Ecore_IMF_Event *)&ecore_ev))
465           return;
466      }
467 }
468 
469 static void
create_input_field(Evas * evas,Entry * en,Evas_Coord x,Evas_Coord y,Evas_Coord w,Evas_Coord h)470 create_input_field(Evas *evas, Entry *en, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
471 {
472    if (!en) return;
473 
474    en->have_preedit = EINA_FALSE;
475    en->preedit_start = NULL;
476    en->preedit_end = NULL;
477 
478    // create the background for text input field
479    en->rect = evas_object_rectangle_add(evas);
480    evas_object_color_set(en->rect, 150, 150, 150, 255); // gray color
481    evas_object_move(en->rect, x, y);
482    evas_object_resize(en->rect, w, h);
483    evas_object_show(en->rect);
484    evas_object_data_set(en->rect, "Entry", en);
485 
486    // create text object for displaying text
487    en->txt_obj = evas_object_textblock_add(evas);
488    evas_object_color_set(en->txt_obj, 0, 0, 0, 255);
489    evas_object_pass_events_set(en->txt_obj, EINA_TRUE);
490    evas_object_move(en->txt_obj, x, y);
491    evas_object_resize(en->txt_obj, w, h);
492    evas_object_show(en->txt_obj);
493 
494    // set style on textblock
495    static const char *style_buf =
496       "DEFAULT='font=Sans font_size=30 color=#000 text_class=entry'"
497       "newline='br'"
498       "b='+ font=Sans:style=bold'";
499    en->txt_style = evas_textblock_style_new();
500    evas_textblock_style_set(en->txt_style, style_buf);
501    evas_object_textblock_style_set(en->txt_obj, en->txt_style);
502 
503    // create cursor
504    en->cursor = evas_object_textblock_cursor_new(en->txt_obj);
505 
506    // create input context
507    const char *default_id = ecore_imf_context_default_id_get();
508    if (!default_id)
509      {
510         fprintf(stderr, "Can't create ecore_imf_context\n");
511         return;
512      }
513 
514    en->imf_context = ecore_imf_context_add(default_id);
515    ecore_imf_context_client_canvas_set(en->imf_context, evas);
516 
517    // register key event handler
518    evas_object_event_callback_add(en->rect, EVAS_CALLBACK_KEY_DOWN, _key_down_cb, en);
519    evas_object_event_callback_add(en->rect, EVAS_CALLBACK_KEY_UP, _key_up_cb, en);
520 
521    // register mouse event handler
522    evas_object_event_callback_add(en->rect, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, en);
523    evas_object_event_callback_add(en->rect, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, en);
524 
525    // register focus event handler
526    evas_object_event_callback_add(en->rect, EVAS_CALLBACK_FOCUS_IN, _entry_focus_in_cb, en);
527    evas_object_event_callback_add(en->rect, EVAS_CALLBACK_FOCUS_OUT, _entry_focus_out_cb, en);
528 
529    // register retrieve surrounding callback
530    ecore_imf_context_retrieve_surrounding_callback_set(en->imf_context, _ecore_imf_retrieve_surrounding_cb, en);
531 
532    // register commit event callback
533    ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_COMMIT, _ecore_imf_event_commit_cb, en);
534 
535    // register preedit changed event handler
536    ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, _ecore_imf_event_preedit_changed_cb, en);
537 
538    // register surrounding delete event callback
539    ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, _ecore_imf_event_delete_surrounding_cb, en);
540 }
541 
542 static void
delete_input_field(Entry * en)543 delete_input_field(Entry *en)
544 {
545    if (!en) return;
546 
547    if (en->rect)
548      {
549         evas_object_del(en->rect);
550         en->rect = NULL;
551      }
552 
553    if (en->cursor)
554      {
555         evas_textblock_cursor_free(en->cursor);
556         en->cursor = NULL;
557      }
558 
559    if (en->preedit_start)
560      {
561         evas_textblock_cursor_free(en->preedit_start);
562         en->preedit_start = NULL;
563      }
564 
565    if (en->preedit_end)
566      {
567         evas_textblock_cursor_free(en->preedit_end);
568         en->preedit_end = NULL;
569      }
570 
571    if (en->txt_obj)
572      {
573         evas_object_del(en->txt_obj);
574         en->txt_obj = NULL;
575      }
576 
577    if (en->txt_style)
578      {
579         evas_textblock_style_free(en->txt_style);
580         en->txt_style = NULL;
581      }
582 
583    if (en->imf_context)
584      {
585         ecore_imf_context_del(en->imf_context);
586         en->imf_context = NULL;
587      }
588 }
589 
590 int
main(void)591 main(void)
592 {
593    Ecore_Evas *ee;
594    Evas *evas;
595    Entry en1, en2;
596 
597    if (!ecore_evas_init())
598      {
599         fprintf(stderr, "failed to call ecore_evas_init()\n");
600         return EXIT_FAILURE;
601      }
602 
603    ecore_imf_init();
604 
605    // create a new window, with size=WIDTHxHEIGHT and default engine
606    ee = ecore_evas_new(NULL, 0, 0, WIDTH, HEIGHT, NULL);
607 
608    if (!ee)
609      {
610         fprintf(stderr, "failed to call ecore_evas_new\n");
611         return EXIT_FAILURE;
612      }
613 
614    ecore_evas_show(ee);
615 
616    // get the canvas off just-created window
617    evas = ecore_evas_get(ee);
618    if (!evas)
619      {
620         fprintf(stderr, "failed to call ecore_evas_get\n");
621         return EXIT_FAILURE;
622      }
623 
624    // create input field rectangle
625    Evas_Object *bg = evas_object_rectangle_add(evas);
626    evas_object_move(bg, 0, 0);
627    evas_object_resize(bg, WIDTH, HEIGHT);
628    evas_object_color_set(bg, 255, 255, 255, 255);
629    evas_object_show(bg);
630 
631    // register canvas focus in/out event handler
632    evas_event_callback_add(evas, EVAS_CALLBACK_CANVAS_FOCUS_IN, _canvas_focus_in_cb, NULL);
633    evas_event_callback_add(evas, EVAS_CALLBACK_CANVAS_FOCUS_OUT, _canvas_focus_out_cb, NULL);
634 
635    memset(&en1, 0, sizeof(en1));
636    memset(&en2, 0, sizeof(en2));
637 
638    // create input field 1
639    create_input_field(evas, &en1, 40, 60, 400, 80);
640 
641    // create input field 2
642    create_input_field(evas, &en2, 40, 180, 400, 80);
643 
644    // give focus to input field 1
645    evas_object_focus_set(en1.rect, EINA_TRUE);
646 
647    ecore_main_loop_begin(); // begin mainloop
648 
649    delete_input_field(&en1); // delete input field 1
650    delete_input_field(&en2); // delete input field 2
651 
652    evas_event_callback_del_full(evas, EVAS_CALLBACK_CANVAS_FOCUS_IN, _canvas_focus_in_cb, NULL);
653    evas_event_callback_del_full(evas, EVAS_CALLBACK_CANVAS_FOCUS_OUT, _canvas_focus_out_cb, NULL);
654 
655    ecore_evas_free(ee);
656 
657    ecore_imf_shutdown();
658    ecore_evas_shutdown();
659 
660    return 0;
661 }
662 
663