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