e_devicemgr: make an internal header
[platform/upstream/enlightenment.git] / src / bin / e_input_inputs.c
index 4e513ff..8f96e1d 100644 (file)
 #include "e.h"
+#include "e_devicemgr_intern.h"
+#include "e_input_intern.h"
 #include "e_input_private.h"
+#include "e_input_event_intern.h"
+#include "e_keyrouter_intern.h"
+#include "e_comp_wl_intern.h"
+
+#include <glib.h>
+
+static gboolean input_dispatch(GSource *source, GSourceFunc callback, gpointer user_data);
+static gboolean input_thread_prepare(GSource *source, gint *time);
+
+static E_Input_Event_Source *g_input_event_source = NULL;
+static GList *_key_event_list = NULL;
+
+GSourceFuncs input_event_funcs = {
+   .prepare = input_thread_prepare,
+   .check = NULL,
+   .dispatch = input_dispatch,
+   .finalize = NULL
+};
+
+typedef struct
+{
+   GSource gsource;
+   gpointer tag;
+} InputEventSource;
+
+static char *
+_e_input_ecore_device_class_to_string(Ecore_Device_Class clas)
+{
+   switch (clas)
+     {
+        case ECORE_DEVICE_CLASS_NONE:
+          return "None";
+          break;
+        case ECORE_DEVICE_CLASS_SEAT:
+          return "Seat";
+          break;
+        case ECORE_DEVICE_CLASS_KEYBOARD:
+          return "Keyboard";
+          break;
+        case ECORE_DEVICE_CLASS_MOUSE:
+          return "Mouse";
+          break;
+        case ECORE_DEVICE_CLASS_TOUCH:
+          return "Touch";
+          break;
+        case ECORE_DEVICE_CLASS_PEN:
+          return "Pen";
+          break;
+        case ECORE_DEVICE_CLASS_WAND:
+          return "Wand";
+          break;
+        case ECORE_DEVICE_CLASS_GAMEPAD:
+          return "Gamepad";
+          break;
+        default:
+          return "Unknown";
+     }
+}
+
+static Ecore_Device_Class
+_e_input_seat_cap_to_ecore_device_class(unsigned int cap)
+{
+   switch(cap)
+     {
+      case E_INPUT_SEAT_POINTER:
+         return ECORE_DEVICE_CLASS_MOUSE;
+      case E_INPUT_SEAT_KEYBOARD:
+         return ECORE_DEVICE_CLASS_KEYBOARD;
+      case E_INPUT_SEAT_TOUCH:
+         return ECORE_DEVICE_CLASS_TOUCH;
+      default:
+         return ECORE_DEVICE_CLASS_NONE;
+     }
+   return ECORE_DEVICE_CLASS_NONE;
+}
+
+static void
+_e_input_ecore_device_info_free(void *data EINA_UNUSED, void *ev)
+{
+   Ecore_Event_Device_Info *e;
+
+   e = ev;
+   eina_stringshare_del(e->name);
+   eina_stringshare_del(e->identifier);
+   eina_stringshare_del(e->seatname);
+
+   free(e);
+}
+
+void
+_e_input_ecore_device_event(Ecore_Device *dev, const char* seat_name, Eina_Bool flag)
+{
+   Ecore_Event_Device_Info *e;
+   E_Input *e_input;
+   const char *name, *identifier;
+
+   ecore_thread_main_loop_begin();
+
+   if (!(name = ecore_device_name_get(dev))) goto end;
+   if (!(identifier = ecore_device_identifier_get(dev))) goto end;
+
+   if (!(e = calloc(1, sizeof(Ecore_Event_Device_Info)))) goto end;
+
+   e_input = e_input_get();
+
+   e->window = e_input?e_input->window:(Ecore_Window)0;
+   e->name = eina_stringshare_add(name);
+   e->identifier = eina_stringshare_add(identifier);
+   if (seat_name && strlen(seat_name))
+     e->seatname = eina_stringshare_add(seat_name);
+   else
+     e->seatname = eina_stringshare_add(name);
+   e->clas = ecore_device_class_get(dev);
+   e->subclas = ecore_device_subclass_get(dev);
+
+   if (flag)
+     ecore_event_add(ECORE_EVENT_DEVICE_ADD, e, _e_input_ecore_device_info_free, NULL);
+   else
+     ecore_event_add(ECORE_EVENT_DEVICE_DEL, e, _e_input_ecore_device_info_free, NULL);
+
+end:
+   ecore_thread_main_loop_end();
+}
 
 static E_Input_Seat *
 _seat_create(E_Input_Backend *input, const char *seat)
 {
    E_Input_Seat *s;
-   Evas *evs = NULL;
-   E_Input *ei = NULL;
-   Ecore_Evas *ee = NULL;
-   Evas_Device *evas_dev = NULL;
+   Ecore_Device *ecore_dev = NULL;
+   E_Device *e_dev = NULL;
 
-   ei = e_input_get();
-   if (!ei) return NULL;
+   ecore_thread_main_loop_begin();
 
-   ee = e_input_ecore_evas_get(ei);
-   if (!ee) return NULL;
+   /* create an evas device of a seat */
+   ecore_dev = ecore_device_add();
+   if (!ecore_dev)
+     {
+        ERR("Failed to create an ecore device for a seat !\n");
+        ecore_thread_main_loop_end();
+               return NULL;
+     }
 
-   evs = ecore_evas_get(ee);
-   if (!evs) return NULL;
+   ecore_device_name_set(ecore_dev, seat);
+   ecore_device_identifier_set(ecore_dev, "Enlightenment seat");
+   ecore_device_class_set(ecore_dev, ECORE_DEVICE_CLASS_SEAT);
+   ecore_device_subclass_set(ecore_dev, ECORE_DEVICE_SUBCLASS_NONE);
 
-   /* create an evas device of a seat */
-   evas_dev = evas_device_add_full(evs, seat, "Enlightenment seat", NULL, NULL,
-                                   EVAS_DEVICE_CLASS_SEAT, EVAS_DEVICE_SUBCLASS_NONE);
-   if (!evas_dev)
+   /* create an e device of a seat */
+   e_dev = e_device_new();
+   if (!e_dev)
      {
-        ERR("Failed to create an evas device for a seat !\n");
+        ERR("Failed to create an ecore device for a seat !\n");
+        ecore_thread_main_loop_end();
                return NULL;
      }
 
+   e_device_name_set(e_dev, seat);
+   e_device_identifier_set(e_dev, "Enlightenment seat");
+   e_device_class_set(e_dev, ECORE_DEVICE_CLASS_SEAT);
+   e_device_subclass_set(e_dev, ECORE_DEVICE_SUBCLASS_NONE);
+
    /* try to allocate space for new seat */
    if (!(s = calloc(1, sizeof(E_Input_Seat))))
      {
-        evas_device_del(evas_dev);
+        ecore_device_del(ecore_dev);
+        ecore_thread_main_loop_end();
         return NULL;
      }
 
    s->input = input;
    s->name = eina_stringshare_add(seat);
-   s->evas_dev = evas_dev;
+   s->ecore_dev = ecore_dev;
+   s->e_dev = e_dev;
 
    /* add this new seat to list */
    input->dev->seats = eina_list_append(input->dev->seats, s);
+   s->dev = input->dev;
 
    ecore_event_add(E_INPUT_EVENT_SEAT_ADD, NULL, NULL, NULL);
+   ecore_thread_main_loop_end();
 
-   return s;
-}
-
-static void
-_e_input_event_input_device_add_free(void *data EINA_UNUSED, void *ev)
-{
-   E_Input_Event_Input_Device_Add *e;
-
-   e = ev;
-   eina_stringshare_del(e->name);
-   eina_stringshare_del(e->sysname);
-   eina_stringshare_del(e->seatname);
-   eina_stringshare_del(e->identifier);
+   _e_input_ecore_device_event(ecore_dev, seat, EINA_TRUE);
 
-   free(e);
-}
-
-static void
-_e_input_event_input_device_del_free(void *data EINA_UNUSED, void *ev)
-{
-   E_Input_Event_Input_Device_Del *e;
-
-   e = ev;
-   eina_stringshare_del(e->name);
-   eina_stringshare_del(e->sysname);
-   eina_stringshare_del(e->seatname);
-   eina_stringshare_del(e->identifier);
-
-   free(e);
+   return s;
 }
 
 static E_Input_Seat *
@@ -89,119 +202,245 @@ _seat_get(E_Input_Backend *input, const char *seat)
    return _seat_create(input, seat);
 }
 
-static Evas_Device_Class
-_e_input_seat_cap_to_evas_device_class(unsigned int cap)
-{
-   switch(cap)
-     {
-      case E_INPUT_SEAT_POINTER:
-         return EVAS_DEVICE_CLASS_MOUSE;
-      case E_INPUT_SEAT_KEYBOARD:
-         return EVAS_DEVICE_CLASS_KEYBOARD;
-      case E_INPUT_SEAT_TOUCH:
-         return EVAS_DEVICE_CLASS_TOUCH;
-      default:
-         return EVAS_DEVICE_CLASS_NONE;
-     }
-   return EVAS_DEVICE_CLASS_NONE;
-}
-
 static Eina_Bool
-_e_input_add_evas_device(E_Input_Evdev *edev, Evas_Device_Class clas)
+_e_input_add_ecore_device(E_Input_Evdev *edev, Ecore_Device_Class clas)
 {
    const Eina_List *dev_list = NULL;
    const Eina_List *l;
-   Evas_Device *dev = NULL;
+   Ecore_Device *dev = NULL;
+   E_Device *e_dev = NULL;
    const char *identifier;
 
-   Ecore_Evas *ee = NULL;
-   E_Input *ei = NULL;
-   Evas *evs = NULL;
-
    if (!edev || !edev->path) return EINA_FALSE;
 
-   dev_list = evas_device_list(e_comp->evas, NULL);
+   dev_list = ecore_device_list();
    if (dev_list)
      {
         EINA_LIST_FOREACH(dev_list, l, dev)
           {
              if (!dev) continue;
-             identifier = evas_device_description_get(dev);
+             identifier = ecore_device_identifier_get(dev);
              if (!identifier) continue;
-             if ((evas_device_class_get(dev) == clas) && (!strcmp(identifier, edev->path)))
-                return EINA_FALSE;
+             if ((ecore_device_class_get(dev) == clas) && (!strcmp(identifier, edev->path)))
+               {
+                  ERR("Found same device in device list");
+                  return EINA_FALSE;
+               }
           }
      }
 
-   ei = e_input_get();
-   if (!ei) return EINA_FALSE;
+   // create ecore device info
+   dev = ecore_device_add();
+   if (!dev)
+     {
+        ERR("Failed to create ecore device");
+        edev->ecore_dev = NULL;
+        return EINA_FALSE;
+     }
+
+   ecore_device_name_set(dev, libinput_device_get_name(edev->device));
+   ecore_device_identifier_set(dev, edev->path);
+   ecore_device_class_set(dev, clas);
+   ecore_device_subclass_set(dev, ECORE_DEVICE_SUBCLASS_NONE);
 
-   ee = e_input_ecore_evas_get(ei);
-   if (!ee) return EINA_FALSE;
+   if (!edev->ecore_dev)
+     {
+        if (!edev->ecore_dev_list || (eina_list_count(edev->ecore_dev_list) == 0))
+          {
+             /* 1st Ecore_Device is added */
+             edev->ecore_dev = ecore_device_ref(dev);
+          }
+        else
+          {
+             /* 3rd or more Ecore_Device is added */
+             edev->ecore_dev_list = eina_list_append(edev->ecore_dev_list, ecore_device_ref(dev));
+          }
+     }
+   else
+     {
+        /* 2nd Ecore_Device is added */
+        edev->ecore_dev_list = eina_list_append(edev->ecore_dev_list, edev->ecore_dev);
+        edev->ecore_dev = NULL;
 
-   evs = ecore_evas_get(ee);
-   if (!evs) return EINA_FALSE;
+        edev->ecore_dev_list = eina_list_append(edev->ecore_dev_list, ecore_device_ref(dev));
+     }
 
-   dev = evas_device_add_full(evs,libinput_device_get_name(edev->device),
-                              edev->path, edev->seat->evas_dev , NULL, clas, EVAS_DEVICE_SUBCLASS_NONE);
-   if (!dev)
+   const GList *device_list = e_device_list_get();
+   const gchar *device_identifier;
+   for (GList *list = g_list_first((GList *)device_list); list; list = list->next)
      {
-        edev->evas_dev = NULL;
-        return EINA_FALSE;
+        E_Device *device = (E_Device *)list->data;
+        if (!device) continue;
+        device_identifier = e_device_identifier_get(device);
+        if (!device_identifier) continue;
+
+        if ((e_device_class_get(device) == clas) && (!strcmp(device_identifier, edev->path)))
+          {
+             e_dev = device;
+             break;
+          }
+     }
+
+   if (!e_dev)
+     {
+        // create E_Device info
+        e_dev = e_device_new();
+        if (!e_dev)
+          {
+             ERR("Failed to add e device");
+             edev->e_dev = NULL;
+             return EINA_FALSE;
+          }
+     }
+
+   e_device_name_set(e_dev, libinput_device_get_name(edev->device));
+   e_device_identifier_set(e_dev, edev->path);
+   e_device_class_set(e_dev, clas);
+   e_device_subclass_set(e_dev, ECORE_DEVICE_SUBCLASS_NONE);
+
+   if (!edev->e_dev)
+     {
+        if (!edev->e_dev_list || (g_list_length(edev->e_dev_list) == 0))
+          {
+             /* 1st Ecore_Device is added */
+             edev->e_dev = g_object_ref(e_dev);
+          }
+        else
+          {
+             /* 3rd or more Ecore_Device is added */
+             edev->e_dev_list = g_list_append(edev->e_dev_list, g_object_ref(e_dev));
+          }
+     }
+   else
+     {
+        /* 2nd Ecore_Device is added */
+        edev->e_dev_list = g_list_append(edev->e_dev_list, edev->e_dev);
+        edev->e_dev = NULL;
+
+        edev->e_dev_list = g_list_append(edev->e_dev_list, g_object_ref(e_dev));
      }
 
-   edev->evas_dev = dev;
+   _e_input_ecore_device_event(dev, edev->seat ? edev->seat->name : NULL, EINA_TRUE);
+
+   INF("[Add Device] device name(%s), identifier(%s), class(%s)", e_device_name_get(e_dev), edev->path, _e_input_ecore_device_class_to_string(clas));
 
    return EINA_TRUE;
 }
 
 static Eina_Bool
-_e_input_remove_evas_device(E_Input_Evdev *edev, Evas_Device_Class clas)
+_e_input_remove_ecore_device(E_Input_Evdev *edev, Ecore_Device_Class clas)
 {
-   const Eina_List *dev_list = NULL;
-   const Eina_List *l;
-   Evas_Device *dev = NULL;
+   Eina_Bool ret = EINA_FALSE;
+   const Eina_List *dev_list = NULL, *l;
+   const GList *e_dev_list = NULL;
+   Eina_List *ll, *ll_next;
+   Ecore_Device *dev = NULL, *data;
    const char *identifier;
+   const gchar *device_identifier;
+   const char *device_remove_log = NULL;
 
    if (!edev->path) return EINA_FALSE;
 
-   dev_list = evas_device_list(e_comp->evas, NULL);
+   dev_list = ecore_device_list();
    if (!dev_list) return EINA_FALSE;
+
    EINA_LIST_FOREACH(dev_list, l, dev)
       {
          if (!dev) continue;
-         identifier = evas_device_description_get(dev);
+         identifier = ecore_device_identifier_get(dev);
          if (!identifier) continue;
-         if ((evas_device_class_get(dev) == clas) && (!strcmp(identifier, edev->path)))
+         if ((ecore_device_class_get(dev) == clas) && (!strcmp(identifier, edev->path)))
            {
-              evas_device_del(dev);
-                         edev->evas_dev = NULL;
-              return EINA_TRUE;
+              if (edev->ecore_dev)
+                {
+                   ecore_device_unref(dev);
+                   edev->ecore_dev = NULL;
+                }
+              else if (edev->ecore_dev_list)
+                {
+                   EINA_LIST_FOREACH_SAFE(edev->ecore_dev_list, ll, ll_next, data)
+                     {
+                        if (data == dev)
+                          {
+                             ecore_device_unref(dev);
+                             edev->ecore_dev_list = eina_list_remove_list(edev->ecore_dev_list, ll);
+                          }
+                     }
+                }
+              _e_input_ecore_device_event(dev, edev->seat ? edev->seat->name : NULL, EINA_FALSE);
+              ecore_device_del(dev);
+              ret = EINA_TRUE;
            }
       }
-   return EINA_FALSE;
+
+   e_dev_list = e_device_list_get();
+   if (!e_dev_list)
+     {
+        ERR("Failed to get e device list");
+        return EINA_FALSE;
+     }
+
+   for (GList *list = g_list_first((GList *)e_dev_list); list; list = list->next)
+     {
+        E_Device *device = (E_Device *)list->data;
+        if (!device) continue;
+        device_identifier = e_device_identifier_get(device);
+        if (!device_identifier) continue;
+
+        if ((e_device_class_get(device) == clas) && (!strcmp(device_identifier, edev->path)))
+          {
+             device_remove_log = eina_stringshare_printf("[Remove Device] device name(%s), identifier(%s), class(%s)",
+                                                         e_device_name_get(device),
+                                                         device_identifier,
+                                                         _e_input_ecore_device_class_to_string(clas));
+
+             if (edev->e_dev)
+               {
+                  g_object_unref(device);
+                  edev->e_dev = NULL;
+               }
+             else if (edev->e_dev_list)
+               {
+                  GList *del_list = g_list_find(edev->e_dev_list, device);
+                  if (del_list)
+                    {
+                       edev->e_dev_list = g_list_delete_link(edev->e_dev_list, del_list);
+                    }
+               }
+             ret = EINA_TRUE;
+          }
+     }
+
+   if (device_remove_log)
+     {
+        INF("%s", device_remove_log);
+        eina_stringshare_del(device_remove_log);
+     }
+
+   return ret;
 }
 
 Eina_Bool
 _e_input_device_add(E_Input_Evdev *edev)
 {
    Eina_Bool ret = EINA_FALSE;
-   Evas_Device_Class clas;
+   Ecore_Device_Class clas = ECORE_DEVICE_CLASS_NONE;
 
    if (edev->caps & E_INPUT_SEAT_POINTER)
      {
-        clas = _e_input_seat_cap_to_evas_device_class(E_INPUT_SEAT_POINTER);
-        ret = _e_input_add_evas_device(edev, clas);
+        if (!e_devicemgr_detent_is_detent(libinput_device_get_name(edev->device)))
+          clas = _e_input_seat_cap_to_ecore_device_class(E_INPUT_SEAT_POINTER);
+        ret = _e_input_add_ecore_device(edev, clas);
      }
    if (edev->caps & E_INPUT_SEAT_KEYBOARD)
      {
-        clas = _e_input_seat_cap_to_evas_device_class(E_INPUT_SEAT_KEYBOARD);
-        ret = _e_input_add_evas_device(edev, clas);
+        clas = _e_input_seat_cap_to_ecore_device_class(E_INPUT_SEAT_KEYBOARD);
+        ret = _e_input_add_ecore_device(edev, clas);
      }
    if (edev->caps & E_INPUT_SEAT_TOUCH)
      {
-        clas = _e_input_seat_cap_to_evas_device_class(E_INPUT_SEAT_TOUCH);
-        ret = _e_input_add_evas_device(edev, clas);
+        clas = _e_input_seat_cap_to_ecore_device_class(E_INPUT_SEAT_TOUCH);
+        ret = _e_input_add_ecore_device(edev, clas);
      }
 
    return ret;
@@ -210,22 +449,41 @@ _e_input_device_add(E_Input_Evdev *edev)
 void
 _e_input_device_remove(E_Input_Evdev *edev)
 {
-   Evas_Device_Class clas;
+   Ecore_Device_Class clas = ECORE_DEVICE_CLASS_NONE;
+   Ecore_Device *data;
 
    if (edev->caps & E_INPUT_SEAT_POINTER)
      {
-        clas = _e_input_seat_cap_to_evas_device_class(E_INPUT_SEAT_POINTER);
-        _e_input_remove_evas_device(edev, clas);
+        if (!e_devicemgr_detent_is_detent(libinput_device_get_name(edev->device)))
+          clas = _e_input_seat_cap_to_ecore_device_class(E_INPUT_SEAT_POINTER);
+        _e_input_remove_ecore_device(edev, clas);
      }
    if (edev->caps & E_INPUT_SEAT_KEYBOARD)
      {
-        clas = _e_input_seat_cap_to_evas_device_class(E_INPUT_SEAT_KEYBOARD);
-        _e_input_remove_evas_device(edev, clas);
+        clas = _e_input_seat_cap_to_ecore_device_class(E_INPUT_SEAT_KEYBOARD);
+        _e_input_remove_ecore_device(edev, clas);
      }
    if (edev->caps & E_INPUT_SEAT_TOUCH)
      {
-        clas = _e_input_seat_cap_to_evas_device_class(E_INPUT_SEAT_TOUCH);
-        _e_input_remove_evas_device(edev, clas);
+        clas = _e_input_seat_cap_to_ecore_device_class(E_INPUT_SEAT_TOUCH);
+        _e_input_remove_ecore_device(edev, clas);
+     }
+
+   if (edev->ecore_dev_list)
+     {
+        if (eina_list_count(edev->ecore_dev_list) > 0)
+          {
+             EINA_LIST_FREE(edev->ecore_dev_list, data)
+               {
+                  WRN("Invalid device is left. name: %s, identifier: %s, clas: %s\n",
+                      ecore_device_name_get(data), ecore_device_identifier_get(data),
+                      _e_input_ecore_device_class_to_string(ecore_device_class_get(data)));
+
+                  ecore_device_unref(data);
+                  ecore_device_del(data);
+               }
+          }
+        edev->ecore_dev_list = NULL;
      }
 }
 
@@ -236,8 +494,8 @@ _device_added(E_Input_Backend *input, struct libinput_device *device)
    const char *seat_name;
    E_Input_Seat *seat;
    E_Input_Evdev *edev;
-   E_Input_Event_Input_Device_Add *ev;
 
+   ecore_thread_main_loop_begin();
    libinput_seat = libinput_device_get_seat(device);
    seat_name = libinput_seat_get_logical_name(libinput_seat);
 
@@ -245,6 +503,7 @@ _device_added(E_Input_Backend *input, struct libinput_device *device)
    if (!(seat = _seat_get(input, seat_name)))
      {
         ERR("Could not get matching seat: %s", seat_name);
+        ecore_thread_main_loop_end();
         return;
      }
 
@@ -252,88 +511,46 @@ _device_added(E_Input_Backend *input, struct libinput_device *device)
    if (!(edev = _e_input_evdev_device_create(seat, device)))
      {
         ERR("Failed to create new evdev device");
+        ecore_thread_main_loop_end();
         return;
      }
 
-   edev->fd = (int)(intptr_t)eina_hash_find(input->dev->fd_hash, edev->path);
-
    /* append this device to the seat */
    seat->devices = eina_list_append(seat->devices, edev);
 
-   ev = calloc(1, sizeof(E_Input_Event_Input_Device_Add));
-   if (!ev)
-     {
-        return;
-     }
-
    if (EINA_FALSE == _e_input_device_add(edev))
      {
         ERR("Failed to create evas device !\n");
-               return;
+        ecore_thread_main_loop_end();
+        return;
      }
 
-   ev->name = eina_stringshare_add(libinput_device_get_name(device));
-   ev->sysname = eina_stringshare_add(edev->path);
-   ev->seatname = eina_stringshare_add(edev->seat->name);
-   ev->caps = edev->caps;
-   ev->clas = evas_device_class_get(edev->evas_dev);
-   ev->identifier = eina_stringshare_add(edev->path);
-   ev->subclas = evas_device_subclass_get(edev->evas_dev);
-
-   ecore_event_add(E_INPUT_EVENT_INPUT_DEVICE_ADD,
-                   ev,
-                   _e_input_event_input_device_add_free,
-                   NULL);
+   ecore_thread_main_loop_end();
 }
 
 static void
 _device_removed(E_Input_Backend *input, struct libinput_device *device)
 {
    E_Input_Evdev *edev;
-   E_Input_Event_Input_Device_Del *ev;
+
+   ecore_thread_main_loop_begin();
 
    /* try to get the evdev structure */
    if (!(edev = libinput_device_get_user_data(device)))
      {
+        ecore_thread_main_loop_end();
         return;
      }
 
-   ev = calloc(1, sizeof(E_Input_Event_Input_Device_Del));
-   if (!ev)
-     {
-        return;
-     }
-
-   ev->name = eina_stringshare_add(libinput_device_get_name(device));
-   ev->sysname = eina_stringshare_add(edev->path);
-   ev->seatname = eina_stringshare_add(edev->seat->name);
-   ev->caps = edev->caps;
-   ev->clas = evas_device_class_get(edev->evas_dev);
-   ev->identifier = eina_stringshare_add(edev->path);
-   ev->subclas = evas_device_subclass_get(edev->evas_dev);
-
-   ecore_event_add(E_INPUT_EVENT_INPUT_DEVICE_DEL,
-                   ev,
-                   _e_input_event_input_device_del_free,
-                   NULL);
-
    _e_input_device_remove(edev);
 
    /* remove this evdev from the seat's list of devices */
    edev->seat->devices = eina_list_remove(edev->seat->devices, edev);
 
-   if (input->dev->fd_hash)
-     eina_hash_del_by_key(input->dev->fd_hash, edev->path);
-
-   /* tell launcher to release device */
-   if (edev->fd >= 0)
-     {
-        close(edev->fd);
-        edev->fd = -1;
-     }
-
    /* destroy this evdev */
    _e_input_evdev_device_destroy(edev);
+
+   ecore_thread_main_loop_end();
 }
 
 static int
@@ -351,10 +568,34 @@ _udev_event_process(struct libinput_event *event)
    switch (libinput_event_get_type(event))
      {
       case LIBINPUT_EVENT_DEVICE_ADDED:
+        if (e_config->key_input_ttrace_enable)
+          {
+             TRACE_INPUT_BEGIN(_device_added);
+             ELOGF("INPUT", "_device_added|B|", NULL);
+          }
+
         _device_added(input, device);
+
+        if (e_config->key_input_ttrace_enable)
+          {
+             TRACE_INPUT_END();
+             ELOGF("INPUT", "_device_added|E|", NULL);
+          }
         break;
       case LIBINPUT_EVENT_DEVICE_REMOVED:
+        if (e_config->key_input_ttrace_enable)
+          {
+             TRACE_INPUT_BEGIN(_device_removed);
+             ELOGF("INPUT", "_device_removed|B|", NULL);
+          }
+
         _device_removed(input, device);
+
+        if (e_config->key_input_ttrace_enable)
+          {
+             TRACE_INPUT_END();
+             ELOGF("INPUT", "_device_removed|E|", NULL);
+          }
         break;
       default:
         ret = EINA_FALSE;
@@ -374,7 +615,6 @@ void
 _input_events_process(E_Input_Backend *input)
 {
    struct libinput_event *event;
-
    while ((event = libinput_get_event(input->libinput)))
      {
         _input_event_process(event);
@@ -389,15 +629,265 @@ _cb_input_dispatch(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED)
 
    if (!(input = data)) return EINA_TRUE;
 
+   if (e_config->key_input_ttrace_enable)
+     {
+        TRACE_INPUT_BEGIN(_cb_input_dispatch);
+        ELOGF("INPUT", "_cb_input_dispatch|B|", NULL);
+     }
+
    if (libinput_dispatch(input->libinput) != 0)
      ERR("Failed to dispatch libinput events: %m");
 
    /* process pending events */
    _input_events_process(input);
 
+   if (e_config->key_input_ttrace_enable)
+     {
+        TRACE_INPUT_END();
+        ELOGF("INPUT", "_cb_input_dispatch|E|", NULL);
+     }
+
    return EINA_TRUE;
 }
 
+static void
+_e_input_delayed_key_events_print()
+{
+   struct timespec tp;
+   unsigned int time;
+
+   clock_gettime(CLOCK_MONOTONIC, &tp);
+   time = (tp.tv_sec * 1000) + (tp.tv_nsec / 1000000L);
+
+   GList *key_list = g_list_first(_key_event_list);
+   while (key_list)
+     {
+        Ecore_Event_Key *key = (Ecore_Event_Key *)key_list->data;
+        if (!key) continue;
+
+        if (e_config->key_input_time_limit <= (time - key->timestamp))
+          ERR("Delayed key event : keyname(%s), keycode(%u), timestamp(%u), elapsed_time(%u ms)", key->keyname, key->keycode, key->timestamp, time - key->timestamp);
+
+        if (key->keyname)
+          eina_stringshare_del(key->keyname);
+        E_FREE(key);
+
+        GList *next = key_list->next;
+        _key_event_list = g_list_delete_link(_key_event_list, key_list);
+        key_list = next;
+     }
+
+   _key_event_list = NULL;
+}
+
+static gboolean
+input_thread_prepare(GSource *source, gint *time)
+{
+   if (e_config->key_input_ttrace_enable)
+     {
+        TRACE_INPUT_BEGIN(e_comp_wl_focused_client_flush);
+        ELOGF("INPUT", "e_comp_wl_focused_client_flush|B|", NULL);
+     }
+
+   /* flush only focused client events */
+   e_comp_wl_focused_client_flush();
+
+   if (_key_event_list)
+     _e_input_delayed_key_events_print();
+
+   if (e_config->key_input_ttrace_enable)
+     {
+        TRACE_INPUT_END();
+        ELOGF("INPUT", "e_comp_wl_focused_client_flush|E|", NULL);
+     }
+
+   if (time)
+     *time = -1;
+
+   return FALSE;
+}
+
+static gboolean
+input_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
+{
+   InputEventSource *src = (InputEventSource *)source;
+   E_Input_Backend *input = (E_Input_Backend *)user_data;
+   if (!src) return G_SOURCE_REMOVE;
+   if (!input) return G_SOURCE_REMOVE;
+
+   if (e_config->key_input_ttrace_enable)
+     {
+        TRACE_INPUT_BEGIN(input_dispatch);
+        ELOGF("INPUT", "input_dispatch|B|", NULL);
+     }
+
+   GIOCondition cond;
+   cond = g_source_query_unix_fd(source, src->tag);
+
+   if (cond & G_IO_ERR || cond & G_IO_HUP || cond & G_IO_NVAL)
+     {
+        INF("error cond(%d)\n", cond);
+        return G_SOURCE_CONTINUE;
+     }
+
+   if (!input->libinput)
+     return G_SOURCE_REMOVE;
+
+   if (e_config->key_input_ttrace_enable)
+     {
+        TRACE_INPUT_BEGIN(libinput_dispatch);
+        ELOGF("INPUT", "libinput_dispatch|B|", NULL);
+     }
+
+   if (libinput_dispatch(input->libinput) != 0)
+     ERR("Failed to dispatch libinput events: %m");
+
+   if (e_config->key_input_ttrace_enable)
+     {
+        TRACE_INPUT_END();
+        ELOGF("INPUT", "libinput_dispatch|E|", NULL);
+     }
+
+   if (e_config->key_input_ttrace_enable)
+     {
+        TRACE_INPUT_BEGIN(_input_events_process);
+        ELOGF("INPUT", "_input_events_process|B|", NULL);
+     }
+
+   /* process pending events */
+   _input_events_process(input);
+
+   if (e_config->key_input_ttrace_enable)
+     {
+        TRACE_INPUT_END();
+        ELOGF("INPUT", "_input_events_process|E|", NULL);
+
+        TRACE_INPUT_END();
+        ELOGF("INPUT", "input_dispatch|E|", NULL);
+     }
+
+   e_input_event_process((GSource *)input->event_source);
+
+   return G_SOURCE_CONTINUE;
+}
+
+static void
+input_thread_start(void *data, Ecore_Thread *th)
+{
+   E_Input_Device *dev;
+   E_Input_Backend *input;
+   GMainContext *context = NULL;
+   InputEventSource *input_event_source = NULL;
+
+   if (!(input = data)) return;
+
+   eina_thread_name_set(eina_thread_self(), "input-thread");
+
+   e_input_libinput_context_create(input);
+
+   /* enable this input */
+   if (!e_input_enable_input(input))
+     {
+        ERR("Failed to enable input");
+        return;
+     }
+
+   /* append this input */
+   dev = input->dev;
+   dev->inputs = eina_list_append(dev->inputs, input);
+
+   //create a context
+   context = g_main_context_new();
+   g_main_context_push_thread_default(context);
+
+   input_event_source = (InputEventSource *)g_source_new(&input_event_funcs, sizeof(InputEventSource));
+   input_event_source->tag = g_source_add_unix_fd(&input_event_source->gsource, input->fd, G_IO_IN);
+
+   if (!input->event_source)
+     e_input_create_event_source(input);
+
+   //create main loop
+   input->input_thread_loop = g_main_loop_new(context, FALSE);
+
+   g_source_attach((GSource *)input->event_source, context);
+
+   //set the callback for this source
+   g_source_set_callback(&input_event_source->gsource, NULL, input, NULL);
+   g_source_attach(&input_event_source->gsource, context);
+
+   e_keyrouter_input_handler_add();
+
+   e_input_thread_id_set(gettid());
+   e_input_main_thread_id_set(getpid());
+
+   _e_input_hook_call(E_INPUT_HOOK_INPUT_THREAD_START, NULL);
+
+   INF("input thread start tid(%d) pid(%d)", e_input_thread_id_get(), e_input_main_thread_id_get());
+
+   g_main_loop_run(input->input_thread_loop);
+}
+
+static void
+input_thread_feedback(void *data, Ecore_Thread *th, void *msgdata)
+{
+   E_Input_Backend *input;
+
+   INF("input thread start");
+
+   if (!(input = data)) return;
+}
+
+static void
+input_thread_end(void *data, Ecore_Thread *th)
+{
+   E_Input_Backend *input;
+
+   if (!(input = data)) return;
+   INF("input thread complete");
+
+   e_input_event_source_destroy(input->event_source);
+   input->event_source = NULL;
+
+   g_main_loop_quit(input->input_thread_loop);
+   g_main_loop_unref(input->input_thread_loop);
+   input->input_thread_loop = NULL;
+
+   if (th == input->input_thread)
+     input->input_thread = NULL;
+}
+
+static void
+input_thread_cancel(void *data, Ecore_Thread *th)
+{
+   E_Input_Backend *input;
+
+   if (!(input = data)) return;
+
+   INF("input thread cancel");
+
+   if (th == input->input_thread)
+     input->input_thread = NULL;
+}
+
+void
+_e_input_key_event_list_add(Ecore_Event_Key *key)
+{
+   Ecore_Event_Key *clone = NULL;
+
+   if (!key) return;
+
+   clone = E_NEW(Ecore_Event_Key, 1);
+   if (!clone) return;
+
+   if (key->keyname)
+     clone->keyname = (char *)eina_stringshare_add(key->keyname);
+
+   clone->keycode = key->keycode;
+   clone->timestamp = key->timestamp;
+
+   _key_event_list = g_list_append(_key_event_list, clone);
+}
+
 EINTERN Eina_Bool
 e_input_enable_input(E_Input_Backend *input)
 {
@@ -406,11 +896,14 @@ e_input_enable_input(E_Input_Backend *input)
 
    input->fd = libinput_get_fd(input->libinput);
 
-   if (!input->hdlr)
+   if (!e_input_thread_mode_get())
      {
-        input->hdlr =
-          ecore_main_fd_handler_add(input->fd, ECORE_FD_READ,
-                                    _cb_input_dispatch, input, NULL, NULL);
+        if (!input->hdlr)
+          {
+             input->hdlr =
+                ecore_main_fd_handler_add(input->fd, ECORE_FD_READ,
+                                          _cb_input_dispatch, input, NULL, NULL);
+          }
      }
 
    if (input->suspended)
@@ -431,9 +924,12 @@ e_input_enable_input(E_Input_Backend *input)
 
 err:
    input->enabled = EINA_FALSE;
-   if (input->hdlr)
-     ecore_main_fd_handler_del(input->hdlr);
-   input->hdlr = NULL;
+   if (!e_input_thread_mode_get())
+     {
+        if (input->hdlr)
+          ecore_main_fd_handler_del(input->hdlr);
+        input->hdlr = NULL;
+     }
 
    return EINA_FALSE;
 }
@@ -451,11 +947,45 @@ e_input_disable_input(E_Input_Backend *input)
    _input_events_process(input);
 
    input->suspended = EINA_TRUE;
+
+   if (e_input_thread_mode_get())
+     {
+        if (input->input_thread && !ecore_thread_check(input->input_thread))
+          ecore_thread_cancel(input->input_thread);
+     }
 }
 
-E_API Eina_List *
+EINTERN Eina_List *
 e_input_seat_evdev_list_get(E_Input_Seat *seat)
 {
    EINA_SAFETY_ON_NULL_RETURN_VAL(seat, NULL);
    return seat->devices;
 }
+
+E_API E_Input_Event_Source *
+e_input_event_source_get()
+{
+   return g_input_event_source;
+}
+
+E_API Eina_Bool
+e_input_thread_mode_get()
+{
+   if (e_config)
+     return e_config->input_thread_mode;
+   else
+     return EINA_TRUE;
+}
+
+EINTERN void
+e_input_create_event_source(E_Input_Backend *input)
+{
+   input->event_source = e_input_event_source_create();
+
+   g_input_event_source = input->event_source;
+}
+
+EINTERN void e_input_thread_run(E_Input_Backend *input)
+{
+   input->input_thread = ecore_thread_feedback_run(input_thread_start, input_thread_feedback, input_thread_end, input_thread_cancel, input, EINA_FALSE);
+}