#ifdef HAVE_CONFIG_H # include #endif #include #include #include "Ecore.h" #include "Ecore_Input.h" #include "Ecore_Input_Evas.h" #include "ecore_input_evas_private.h" int _ecore_input_evas_log_dom = -1; typedef struct _Ecore_Input_Window Ecore_Input_Window; struct _Ecore_Input_Window { Evas *evas; void *window; Ecore_Event_Mouse_Move_Cb move_mouse; Ecore_Event_Multi_Move_Cb move_multi; Ecore_Event_Multi_Down_Cb down_multi; Ecore_Event_Multi_Up_Cb up_multi; int ignore_event; }; typedef enum _Ecore_Input_State { ECORE_INPUT_NONE = 0, ECORE_INPUT_DOWN, ECORE_INPUT_MOVE, ECORE_INPUT_UP, ECORE_INPUT_CANCEL } Ecore_Input_State; typedef enum _Ecore_Input_Action { ECORE_INPUT_CONTINUE = 0, ECORE_INPUT_IGNORE, ECORE_INPUT_FAKE_UP } Ecore_Input_Action; typedef struct _Ecore_Input_Last Ecore_Event_Last; struct _Ecore_Input_Last { Ecore_Event_Mouse_Button *ev; Ecore_Timer *timer; unsigned int device; unsigned int buttons; Ecore_Input_State state; Ecore_Window win; Eina_Bool faked : 1; }; static int _ecore_event_evas_init_count = 0; static Ecore_Event_Handler *ecore_event_evas_handlers[10]; static Eina_Hash *_window_hash = NULL; static Eina_List *_last_events = NULL; static double _last_events_timeout = 0.5; static Eina_Bool _last_events_enable = EINA_FALSE; static Eina_Bool _cancel_events_enable = EINA_FALSE; static Eina_Bool _ecore_event_evas_mouse_button(Ecore_Event_Mouse_Button *e, Ecore_Event_Press press, Eina_Bool faked); static Ecore_Input_Action _ecore_event_last_check(Ecore_Event_Last *eel, Ecore_Event_Press press) { switch (eel->state) { case ECORE_INPUT_NONE: /* 1. ECORE_INPUT_NONE => ECORE_UP : impossible * 2. ECORE_INPUT_NONE => ECORE_CANCEL : impossible * 3. ECORE_INPUT_NONE => ECORE_DOWN : ok */ return ECORE_INPUT_CONTINUE; case ECORE_INPUT_DOWN: /* 1. ECORE_INPUT_DOWN => ECORE_UP : ok * 2. ECORE_INPUT_DOWN => ECORE_CANCEL : ok */ if ((press == ECORE_UP) || (press == ECORE_CANCEL)) return ECORE_INPUT_CONTINUE; /* 3. ECORE_INPUT_DOWN => ECORE_DOWN : emit a faked UP then */ INF("Down event occurs twice. device(%d), button(%d)", eel->device, eel->buttons); return ECORE_INPUT_FAKE_UP; case ECORE_INPUT_MOVE: /* 1. ECORE_INPUT_MOVE => ECORE_UP : ok * 2. ECORE_INPUT_MOVE => ECORE_CANCEL : ok */ if ((press == ECORE_UP) || (press == ECORE_CANCEL)) return ECORE_INPUT_CONTINUE; /* 3. ECORE_INPUT_MOVE => ECORE_DOWN : ok * FIXME: handle fake button up and push for more delay here */ //TODO: How to deal with down event after move event? INF("Down event occurs after move event. device(%d), button(%d)", eel->device, eel->buttons); return ECORE_INPUT_FAKE_UP; case ECORE_INPUT_UP: case ECORE_INPUT_CANCEL: /* 1. ECORE_INPUT_UP => ECORE_DOWN : ok */ /* 2. ECORE_INPUT_CANCEL => ECORE_DOWN : ok */ if (press == ECORE_DOWN) return ECORE_INPUT_CONTINUE; /* 3. ECORE_INPUT_UP => ECORE_UP : ignore */ /* 4. ECORE_INPUT_UP => ECORE_CANCEL : ignore */ /* 5. ECORE_INPUT_CANCEL => ECORE_UP : ignore */ /* 6. ECORE_INPUT_CANCEL => ECORE_CANCEL : ignore */ INF("Up/cancel event occurs after up/cancel event. device(%d), button(%d)", eel->device, eel->buttons); return ECORE_INPUT_IGNORE; } return ECORE_INPUT_IGNORE; } static Ecore_Event_Last * _ecore_event_evas_lookup(unsigned int device, unsigned int buttons, Ecore_Window win, Eina_Bool create_new) { Ecore_Event_Last *eel; Eina_List *l; //the number of last event is small, simple check is ok. EINA_LIST_FOREACH(_last_events, l, eel) if ((eel->device == device) && (eel->buttons == buttons)) return eel; if (!create_new) return NULL; eel = malloc(sizeof (Ecore_Event_Last)); if (!eel) return NULL; eel->timer = NULL; eel->ev = NULL; eel->device = device; eel->buttons = buttons; eel->state = ECORE_INPUT_NONE; eel->faked = EINA_FALSE; eel->win = win; _last_events = eina_list_append(_last_events, eel); return eel; } static Eina_Bool _ecore_event_evas_push_fake(void *data) { Ecore_Event_Last *eel = data; switch (eel->state) { case ECORE_INPUT_NONE: case ECORE_INPUT_UP: case ECORE_INPUT_CANCEL: /* should not happen */ break; case ECORE_INPUT_DOWN: /* use the saved Ecore_Event */ /* No up event since timeout started ... */ case ECORE_INPUT_MOVE: /* No up event since timeout started ... */ _ecore_event_evas_mouse_button(eel->ev, ECORE_UP, EINA_TRUE); eel->faked = EINA_TRUE; break; } free(eel->ev); eel->ev = NULL; eel->timer = NULL; return EINA_FALSE; } static Eina_Bool _ecore_event_evas_push_mouse_button(Ecore_Event_Mouse_Button *e, Ecore_Event_Press press) { Ecore_Event_Last *eel; Ecore_Input_Action action = ECORE_INPUT_CONTINUE; //_ecore_event_evas_mouse_button already check press or cancel without history eel = _ecore_event_evas_lookup(e->multi.device, e->buttons, e->window, EINA_TRUE); if (!eel) return EINA_FALSE; INF("dev(%d), button(%d), last_press(%d), press(%d)", e->multi.device, e->buttons, eel->state, press); if (e->window == eel->win) action = _ecore_event_last_check(eel, press); INF("action(%d)", action); switch (action) { case ECORE_INPUT_FAKE_UP: _ecore_event_evas_mouse_button(e, ECORE_UP, EINA_TRUE); case ECORE_INPUT_CONTINUE: break; case ECORE_INPUT_IGNORE: default: eel->win = e->window; return EINA_FALSE; } switch (press) { case ECORE_DOWN: eel->state = ECORE_INPUT_DOWN; break; case ECORE_UP: eel->state = ECORE_INPUT_UP; break; default: break; } eel->win = e->window; //if up event not occurs from under layers of ecore //up event is generated by ecore if (_last_events_enable && _last_events_timeout) { if (eel->timer) ecore_timer_del(eel->timer); eel->timer = NULL; if (press == ECORE_DOWN) { /* Save the Ecore_Event somehow */ if (!eel->ev) eel->ev = malloc(sizeof (Ecore_Event_Mouse_Button)); if (!eel->ev) return EINA_FALSE; memcpy(eel->ev, e, sizeof (Ecore_Event_Mouse_Button)); eel->timer = ecore_timer_add(_last_events_timeout, _ecore_event_evas_push_fake, eel); } else { free(eel->ev); eel->ev = NULL; } } return EINA_TRUE; } static void _ecore_event_evas_push_mouse_move(Ecore_Event_Mouse_Move *e) { Ecore_Event_Last *eel; Eina_List *l; if (!_last_events_enable) return; EINA_LIST_FOREACH(_last_events, l, eel) switch (eel->state) { case ECORE_INPUT_NONE: case ECORE_INPUT_UP: case ECORE_INPUT_CANCEL: /* (none, up, or cancel) => move, sounds fine to me */ break; case ECORE_INPUT_DOWN: case ECORE_INPUT_MOVE: /* Down and moving, let's see */ if (eel->ev) { /* Add some delay to the timer */ ecore_timer_reset(eel->timer); /* Update position */ eel->ev->x = e->x; eel->ev->y = e->y; eel->ev->root.x = e->root.x; eel->ev->root.y = e->root.y; eel->state = ECORE_INPUT_MOVE; break; } /* FIXME: Timer did expire, do something maybe */ break; } } EAPI void ecore_event_evas_modifier_lock_update(Evas *e, unsigned int modifiers) { if (modifiers & ECORE_EVENT_MODIFIER_SHIFT) evas_key_modifier_on(e, "Shift"); else evas_key_modifier_off(e, "Shift"); if (modifiers & ECORE_EVENT_MODIFIER_CTRL) evas_key_modifier_on(e, "Control"); else evas_key_modifier_off(e, "Control"); if (modifiers & ECORE_EVENT_MODIFIER_ALT) evas_key_modifier_on(e, "Alt"); else evas_key_modifier_off(e, "Alt"); if (modifiers & ECORE_EVENT_MODIFIER_WIN) { evas_key_modifier_on(e, "Super"); evas_key_modifier_on(e, "Hyper"); } else { evas_key_modifier_off(e, "Super"); evas_key_modifier_off(e, "Hyper"); } if (modifiers & ECORE_EVENT_MODIFIER_ALTGR) evas_key_modifier_on(e, "AltGr"); else evas_key_modifier_off(e, "AltGr"); if (modifiers & ECORE_EVENT_LOCK_SCROLL) evas_key_lock_on(e, "Scroll_Lock"); else evas_key_lock_off(e, "Scroll_Lock"); if (modifiers & ECORE_EVENT_LOCK_NUM) evas_key_lock_on(e, "Num_Lock"); else evas_key_lock_off(e, "Num_Lock"); if (modifiers & ECORE_EVENT_LOCK_CAPS) evas_key_lock_on(e, "Caps_Lock"); else evas_key_lock_off(e, "Caps_Lock"); if (modifiers & ECORE_EVENT_LOCK_SHIFT) evas_key_lock_on(e, "Shift_Lock"); else evas_key_lock_off(e, "Shift_Lock"); } EAPI void ecore_event_window_register(Ecore_Window id, void *window, Evas *evas, Ecore_Event_Mouse_Move_Cb move_mouse, Ecore_Event_Multi_Move_Cb move_multi, Ecore_Event_Multi_Down_Cb down_multi, Ecore_Event_Multi_Up_Cb up_multi) { Ecore_Input_Window *w; w = calloc(1, sizeof(Ecore_Input_Window)); if (!w) return; w->evas = evas; w->window = window; w->move_mouse = move_mouse; w->move_multi = move_multi; w->down_multi = down_multi; w->up_multi = up_multi; w->ignore_event = 0; eina_hash_add(_window_hash, &id, w); evas_key_modifier_add(evas, "Shift"); evas_key_modifier_add(evas, "Control"); evas_key_modifier_add(evas, "Alt"); evas_key_modifier_add(evas, "Meta"); evas_key_modifier_add(evas, "Hyper"); evas_key_modifier_add(evas, "Super"); evas_key_modifier_add(evas, "AltGr"); evas_key_lock_add(evas, "Caps_Lock"); evas_key_lock_add(evas, "Num_Lock"); evas_key_lock_add(evas, "Scroll_Lock"); } EAPI void ecore_event_window_unregister(Ecore_Window id) { eina_hash_del(_window_hash, &id, NULL); } EAPI void * ecore_event_window_match(Ecore_Window id) { Ecore_Input_Window *lookup; lookup = eina_hash_find(_window_hash, &id); if (lookup) return lookup->window; return NULL; } EAPI void ecore_event_window_ignore_events(Ecore_Window id, int ignore_event) { Ecore_Input_Window *lookup; lookup = eina_hash_find(_window_hash, &id); if (!lookup) return; lookup->ignore_event = ignore_event; } static Ecore_Input_Window* _ecore_event_window_match(Ecore_Window id) { Ecore_Input_Window *lookup; lookup = eina_hash_find(_window_hash, &id); if (!lookup) return NULL; if (lookup->ignore_event) return NULL; /* Pass on event. */ return lookup; } static Eina_Bool _ecore_event_evas_key(Ecore_Event_Key *e, Ecore_Event_Press press) { Ecore_Input_Window *lookup; lookup = _ecore_event_window_match(e->event_window); if (!lookup) return ECORE_CALLBACK_PASS_ON; ecore_event_evas_modifier_lock_update(lookup->evas, e->modifiers); if (press == ECORE_DOWN) evas_event_feed_key_down_with_keycode(lookup->evas, e->keyname, e->key, e->string, e->compose, e->timestamp, e->data, e->keycode); else evas_event_feed_key_up_with_keycode(lookup->evas, e->keyname, e->key, e->string, e->compose, e->timestamp, e->data, e->keycode); return ECORE_CALLBACK_PASS_ON; } static Eina_Bool _ecore_event_evas_mouse_button_cancel(Ecore_Event_Mouse_Button *e) { Ecore_Input_Window *lookup; Ecore_Event_Last *eel; Eina_List *l; lookup = _ecore_event_window_match(e->event_window); if (!lookup) return ECORE_CALLBACK_PASS_ON; INF("ButtonEvent cancel, device(%d), button(%d)", e->multi.device, e->buttons); evas_event_feed_mouse_cancel(lookup->evas, e->timestamp, NULL); //the number of last event is small, simple check is ok. EINA_LIST_FOREACH(_last_events, l, eel) { Ecore_Input_Action act = _ecore_event_last_check(eel, ECORE_CANCEL); INF("ButtonEvent cancel, dev(%d), button(%d), last_press(%d), action(%d)", eel->device, eel->buttons, eel->state, act); if (act == ECORE_INPUT_CONTINUE) eel->state = ECORE_INPUT_CANCEL; } return ECORE_CALLBACK_PASS_ON; } static Eina_Bool _ecore_event_evas_mouse_button(Ecore_Event_Mouse_Button *e, Ecore_Event_Press press, Eina_Bool faked) { Ecore_Event_Last *eel; Ecore_Input_Window *lookup; Evas_Button_Flags flags = EVAS_BUTTON_NONE; lookup = _ecore_event_window_match(e->event_window); if (!lookup) return ECORE_CALLBACK_PASS_ON; if (e->double_click) flags |= EVAS_BUTTON_DOUBLE_CLICK; if (e->triple_click) flags |= EVAS_BUTTON_TRIPLE_CLICK; INF("\tButtonEvent:ecore_event_evas press(%d), device(%d), button(%d), fake(%d)", press, e->multi.device, e->buttons, faked); //handle all mouse error from under layers of ecore //error handle // 1. ecore up without ecore down // 2. ecore cancel without ecore down if (_cancel_events_enable) { if (press != ECORE_DOWN) { //ECORE_UP or ECORE_CANCEL eel = _ecore_event_evas_lookup(e->multi.device, e->buttons, e->window, EINA_FALSE); if (!eel) { WRN("ButtonEvent has no history."); return ECORE_CALLBACK_PASS_ON; } if ((e->window == eel->win) && ((eel->state == ECORE_INPUT_UP) || (eel->state == ECORE_INPUT_CANCEL))) { WRN("ButtonEvent has wrong history. Last state=%d", eel->state); return ECORE_CALLBACK_PASS_ON; } } if (!faked) { Eina_Bool ret = EINA_FALSE; ret = _ecore_event_evas_push_mouse_button(e, press); /* This ButtonEvent is worng */ if (!ret) return ECORE_CALLBACK_PASS_ON; } } if (e->multi.device == 0) { ecore_event_evas_modifier_lock_update(lookup->evas, e->modifiers); if (press == ECORE_DOWN) evas_event_feed_mouse_down(lookup->evas, e->buttons, flags, e->timestamp, NULL); else evas_event_feed_mouse_up(lookup->evas, e->buttons, flags, e->timestamp, NULL); } else { if (press == ECORE_DOWN) { if (lookup->down_multi) lookup->down_multi(lookup->window, e->multi.device, e->x, e->y, e->multi.radius, e->multi.radius_x, e->multi.radius_y, e->multi.pressure, e->multi.angle, e->multi.x, e->multi.y, flags, e->timestamp); else evas_event_input_multi_down(lookup->evas, e->multi.device, e->x, e->y, e->multi.radius, e->multi.radius_x, e->multi.radius_y, e->multi.pressure, e->multi.angle, e->multi.x, e->multi.y, flags, e->timestamp, NULL); } else { if (lookup->up_multi) lookup->up_multi(lookup->window, e->multi.device, e->x, e->y, e->multi.radius, e->multi.radius_x, e->multi.radius_y, e->multi.pressure, e->multi.angle, e->multi.x, e->multi.y, flags, e->timestamp); else evas_event_input_multi_up(lookup->evas, e->multi.device, e->x, e->y, e->multi.radius, e->multi.radius_x, e->multi.radius_y, e->multi.pressure, e->multi.angle, e->multi.x, e->multi.y, flags, e->timestamp, NULL); } } return ECORE_CALLBACK_PASS_ON; } EAPI Eina_Bool ecore_event_evas_mouse_move(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { Ecore_Event_Mouse_Move *e; Ecore_Input_Window *lookup; e = event; lookup = _ecore_event_window_match(e->event_window); if (!lookup) return ECORE_CALLBACK_PASS_ON; if (e->multi.device == 0) { _ecore_event_evas_push_mouse_move(e); ecore_event_evas_modifier_lock_update(lookup->evas, e->modifiers); if (lookup->move_mouse) lookup->move_mouse(lookup->window, e->x, e->y, e->timestamp); else evas_event_input_mouse_move(lookup->evas, e->x, e->y, e->timestamp, NULL); } else { if (lookup->move_multi) lookup->move_multi(lookup->window, e->multi.device, e->x, e->y, e->multi.radius, e->multi.radius_x, e->multi.radius_y, e->multi.pressure, e->multi.angle, e->multi.x, e->multi.y, e->timestamp); else evas_event_input_multi_move(lookup->evas, e->multi.device, e->x, e->y, e->multi.radius, e->multi.radius_x, e->multi.radius_y, e->multi.pressure, e->multi.angle, e->multi.x, e->multi.y, e->timestamp, NULL); } return ECORE_CALLBACK_PASS_ON; } EAPI Eina_Bool ecore_event_evas_mouse_button_down(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { return _ecore_event_evas_mouse_button((Ecore_Event_Mouse_Button *)event, ECORE_DOWN, EINA_FALSE); } EAPI Eina_Bool ecore_event_evas_mouse_button_up(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { return _ecore_event_evas_mouse_button((Ecore_Event_Mouse_Button *)event, ECORE_UP, EINA_FALSE); } EAPI Eina_Bool ecore_event_evas_mouse_button_cancel(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { if (!_cancel_events_enable) return EINA_FALSE; return _ecore_event_evas_mouse_button_cancel((Ecore_Event_Mouse_Button *)event); } static Eina_Bool _ecore_event_evas_mouse_io(Ecore_Event_Mouse_IO *e, Ecore_Event_IO io) { Ecore_Input_Window *lookup; lookup = _ecore_event_window_match(e->event_window); if (!lookup) return ECORE_CALLBACK_PASS_ON; ecore_event_evas_modifier_lock_update(lookup->evas, e->modifiers); switch (io) { case ECORE_IN: evas_event_feed_mouse_in(lookup->evas, e->timestamp, NULL); break; case ECORE_OUT: evas_event_feed_mouse_out(lookup->evas, e->timestamp, NULL); break; default: break; } lookup->move_mouse(lookup->window, e->x, e->y, e->timestamp); return ECORE_CALLBACK_PASS_ON; } EAPI Eina_Bool ecore_event_evas_key_down(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { return _ecore_event_evas_key((Ecore_Event_Key *)event, ECORE_DOWN); } EAPI Eina_Bool ecore_event_evas_key_up(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { return _ecore_event_evas_key((Ecore_Event_Key *)event, ECORE_UP); } EAPI Eina_Bool ecore_event_evas_mouse_wheel(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { Ecore_Event_Mouse_Wheel *e; Ecore_Input_Window *lookup; e = event; lookup = _ecore_event_window_match(e->event_window); if (!lookup) return ECORE_CALLBACK_PASS_ON; ecore_event_evas_modifier_lock_update(lookup->evas, e->modifiers); evas_event_feed_mouse_wheel(lookup->evas, e->direction, e->z, e->timestamp, NULL); return ECORE_CALLBACK_PASS_ON; } EAPI Eina_Bool ecore_event_evas_mouse_in(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { return _ecore_event_evas_mouse_io((Ecore_Event_Mouse_IO *)event, ECORE_IN); } EAPI Eina_Bool ecore_event_evas_mouse_out(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { return _ecore_event_evas_mouse_io((Ecore_Event_Mouse_IO *)event, ECORE_OUT); } EAPI Eina_Bool ecore_event_evas_axis_update(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { Ecore_Event_Axis_Update *e; Ecore_Input_Window *lookup; e = event; lookup = _ecore_event_window_match(e->event_window); if (!lookup) return ECORE_CALLBACK_PASS_ON; evas_event_feed_axis_update(lookup->evas, e->timestamp, e->device, e->toolid, e->naxis, (Evas_Axis *)e->axis, NULL); return ECORE_CALLBACK_PASS_ON; } EAPI int ecore_event_evas_init(void) { if (++_ecore_event_evas_init_count != 1) return _ecore_event_evas_init_count; _ecore_input_evas_log_dom = eina_log_domain_register ("ecore_input_evas", ECORE_INPUT_EVAS_DEFAULT_LOG_COLOR); if (_ecore_input_evas_log_dom < 0) { EINA_LOG_ERR("Impossible to create a log domain for the ecore input evas_module."); return --_ecore_event_evas_init_count; } if (!ecore_init()) { return --_ecore_event_evas_init_count; } if (!ecore_event_init()) { goto shutdown_ecore; } ecore_event_evas_handlers[0] = ecore_event_handler_add(ECORE_EVENT_KEY_DOWN, ecore_event_evas_key_down, NULL); ecore_event_evas_handlers[1] = ecore_event_handler_add(ECORE_EVENT_KEY_UP, ecore_event_evas_key_up, NULL); ecore_event_evas_handlers[2] = ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_DOWN, ecore_event_evas_mouse_button_down, NULL); ecore_event_evas_handlers[3] = ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_UP, ecore_event_evas_mouse_button_up, NULL); ecore_event_evas_handlers[4] = ecore_event_handler_add(ECORE_EVENT_MOUSE_MOVE, ecore_event_evas_mouse_move, NULL); ecore_event_evas_handlers[5] = ecore_event_handler_add(ECORE_EVENT_MOUSE_WHEEL, ecore_event_evas_mouse_wheel, NULL); ecore_event_evas_handlers[6] = ecore_event_handler_add(ECORE_EVENT_MOUSE_IN, ecore_event_evas_mouse_in, NULL); ecore_event_evas_handlers[7] = ecore_event_handler_add(ECORE_EVENT_MOUSE_OUT, ecore_event_evas_mouse_out, NULL); ecore_event_evas_handlers[8] = ecore_event_handler_add(ECORE_EVENT_AXIS_UPDATE, ecore_event_evas_axis_update, NULL); ecore_event_evas_handlers[9] = ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_CANCEL, ecore_event_evas_mouse_button_cancel, NULL); _window_hash = eina_hash_pointer_new(free); if (getenv("ECORE_INPUT_FIX")) { const char *tmp; _last_events_enable = EINA_TRUE; tmp = getenv("ECORE_INPUT_TIMEOUT_FIX"); if (tmp) _last_events_timeout = ((double) atoi(tmp)) / 60; } if (getenv("ECORE_INPUT_CANCEL")) { _cancel_events_enable = EINA_TRUE; } return _ecore_event_evas_init_count; shutdown_ecore: ecore_shutdown(); return --_ecore_event_evas_init_count; } EAPI int ecore_event_evas_shutdown(void) { size_t i; if (--_ecore_event_evas_init_count != 0) return _ecore_event_evas_init_count; eina_hash_free(_window_hash); _window_hash = NULL; for (i = 0; i < sizeof(ecore_event_evas_handlers) / sizeof(Ecore_Event_Handler *); i++) { ecore_event_handler_del(ecore_event_evas_handlers[i]); ecore_event_evas_handlers[i] = NULL; } ecore_event_shutdown(); ecore_shutdown(); eina_log_domain_unregister(_ecore_input_evas_log_dom); _ecore_input_evas_log_dom = -1; return _ecore_event_evas_init_count; }