summaryrefslogblamecommitdiff
path: root/src/lib/elput/elput_evdev.c
blob: 56aa78135b32161b6482935af2983a3fda3b378c (plain) (tree)
1
2
3
4
5




                                   










                                                          

 











                                                           




























                                                                 


                            
                         
              
                            
 


                                                                          
                     
 
                         
 
                                               










                               

                             































































                                                                               

                                      

                 





































                                                                            
 











                                                           

                                  






















































                                                                       

















                                                                                                                                                                          




                                        


                                                 
















































































                                                                          













                                                        







































































                                                                                                


                                                    


































                                                                       
                                                                        





















                                                                          








                                          
                              












                                 

                                  






                               
                       

























                                          
                         












                                   

                                  























                                 



















                                                  

                                                                 


                     

                                                                 
 


              


                                                  


                                  



                       











                                                        
                                      
                         



                             



                                                           
















                                                                       




                                                                                   






                                              

                                                  


                                                           
                                   






















                                                                                       
                                   

















                                                                          


                                                  












                                             
                                      
                         



                             
















                                                                    





























                                                                    































                                                                                   
                                              


















                                                               


                                                 




























                                                                                          
                                                                          




                                                                 

              























































                                                                                    
                                              










                                                    


                                                 
                                    












                                        
                                        






                             





                                                                             





                                         
















                                                  



                            






                                        
                                        
                         



                               




                                                           



                                                                                






                                                

                                                           








                                                                       
                                     




                                 

                   
                           
                                                         


                          
                                     

                                 
                                                 
















                                                                              
                                                           
 
                           
                                                       


           



















                                                                                  
                                                           
 
                           

 
    














                                                                       
                                                                                      

























                                                                                
           








                                                  




                                                                                         


                                        







                                                                         











                                                       
                                               












































                                                                                            

                                                               
























                                                                        




















                                                                                        

                                                   


                                           










                                                  
                                                                      

                                         

                                                                         

                                                  

                                                                             

                                         

                                                                         

                                       

                                                                       

                                     
                                                                 

                                       
                                                                   

                                   
                                                               
              








                                                                             






                
                           
 
                                                   
                                               








                                                                      
                     





                                          
                  
 


                                                       

                                                                                
      
                                                           


                                          

                                                                              





                                                                               





                                                                         




                                                                           
 
                             
 










                                                                              
               

    
                                           

               















                                           


                                                                  

































                                                                       
 





                                              






















                                             
#include "elput_private.h"

static void
_seat_caps_update(Elput_Seat *seat)
{
   Elput_Event_Seat_Caps *ev;

   ev = calloc(1, sizeof(Elput_Event_Seat_Caps));
   if (!ev) return;

   ev->pointer_count = seat->count.ptr;
   ev->keyboard_count = seat->count.kbd;
   ev->touch_count = seat->count.touch;
   ev->seat = seat;

   ecore_event_add(ELPUT_EVENT_SEAT_CAPS, ev, NULL, NULL);
}

static void
_seat_frame_send(Elput_Seat *seat)
{
   Elput_Event_Seat_Frame *ev;

   ev = calloc(1, sizeof(Elput_Event_Seat_Frame));
   if (!ev) return;

   ev->seat = seat;
   ecore_event_add(ELPUT_EVENT_SEAT_FRAME, ev, NULL, NULL);
}

static void
_keyboard_modifiers_update(Elput_Keyboard *kbd, Elput_Seat *seat)
{
   xkb_mod_mask_t mask;

   kbd->mods.depressed =
     xkb_state_serialize_mods(kbd->state, XKB_STATE_DEPRESSED);
   kbd->mods.latched =
     xkb_state_serialize_mods(kbd->state, XKB_STATE_LATCHED);
   kbd->mods.locked =
     xkb_state_serialize_mods(kbd->state, XKB_STATE_LOCKED);
   kbd->mods.group =
     xkb_state_serialize_mods(kbd->state, XKB_STATE_EFFECTIVE);

   mask = (kbd->mods.depressed | kbd->mods.latched);

   seat->modifiers = 0;
   if (mask & kbd->info->mods.ctrl)
     seat->modifiers |= ECORE_EVENT_MODIFIER_CTRL;
   if (mask & kbd->info->mods.alt)
     seat->modifiers |= ECORE_EVENT_MODIFIER_ALT;
   if (mask & kbd->info->mods.shift)
     seat->modifiers |= ECORE_EVENT_MODIFIER_SHIFT;
   if (mask & kbd->info->mods.super)
     seat->modifiers |= ECORE_EVENT_MODIFIER_WIN;
   if (mask & kbd->info->mods.altgr)
     seat->modifiers |= ECORE_EVENT_MODIFIER_ALTGR;
}

static int
_keyboard_fd_get(off_t size)
{
   Eina_Tmpstr *fullname;
   int fd = 0;
   Efl_Vpath_File *file_obj;

   file_obj = efl_vpath_manager_fetch(EFL_VPATH_MANAGER_CLASS,
                                      "(:run:)/elput-keymap-XXXXXX");
   fd = eina_file_mkstemp(efl_vpath_file_result_get(file_obj), &fullname);
   efl_del(file_obj);

   if (fd < 0) return -1;

   if (!eina_file_close_on_exec(fd, EINA_TRUE))
     {
        close(fd);
        return -1;
     }

   if (ftruncate(fd, size) < 0)
     {
        close(fd);
        return -1;
     }

   unlink(fullname);
   eina_tmpstr_del(fullname);
   return fd;
}

static Elput_Keyboard_Info *
_keyboard_info_create(struct xkb_keymap *keymap, Eina_Bool external)
{
   Elput_Keyboard_Info *info;
   char *str;

   info = calloc(1, sizeof(Elput_Keyboard_Info));
   if (!info) return NULL;

   info->keymap.map = xkb_keymap_ref(keymap);
   info->refs = 1;

   info->mods.super =
     1 << xkb_keymap_mod_get_index(info->keymap.map, XKB_MOD_NAME_LOGO);
   info->mods.shift =
     1 << xkb_keymap_mod_get_index(info->keymap.map, XKB_MOD_NAME_SHIFT);
   info->mods.caps =
     1 << xkb_keymap_mod_get_index(info->keymap.map, XKB_MOD_NAME_CAPS);
   info->mods.ctrl =
     1 << xkb_keymap_mod_get_index(info->keymap.map, XKB_MOD_NAME_CTRL);
   info->mods.alt =
     1 << xkb_keymap_mod_get_index(info->keymap.map, XKB_MOD_NAME_ALT);
   info->mods.altgr =
     1 << xkb_keymap_mod_get_index(info->keymap.map, "ISO_Level3_Shift");

   /* if we are using an external keymap then we do not need go further */
   if (external) return info;

   str = xkb_keymap_get_as_string(info->keymap.map, XKB_KEYMAP_FORMAT_TEXT_V1);
   if (!str) goto err;

   info->keymap.size = strlen(str) + 1;

   info->keymap.fd = _keyboard_fd_get(info->keymap.size);
   if (info->keymap.fd < 0) goto err_fd;

   info->keymap.area =
     mmap(NULL, info->keymap.size, PROT_READ | PROT_WRITE,
          MAP_SHARED, info->keymap.fd, 0);
   if (info->keymap.area == MAP_FAILED) goto err_map;

   strcpy(info->keymap.area, str);
   free(str);

   return info;

err_map:
   close(info->keymap.fd);
err_fd:
   free(str);
err:
   xkb_keymap_unref(info->keymap.map);
   free(info);
   return NULL;
}

static void
_keyboard_info_destroy(Elput_Keyboard_Info *info, Eina_Bool external)
{
   if (--info->refs > 0) return;

   xkb_keymap_unref(info->keymap.map);

   if (!external)
     {
        if (info->keymap.area) munmap(info->keymap.area, info->keymap.size);
        if (info->keymap.fd >= 0) close(info->keymap.fd);
     }

   free(info);
}

static Eina_Bool
_keyboard_global_build(Elput_Keyboard *kbd)
{
   struct xkb_keymap *keymap;

   kbd->context = xkb_context_new(0);
   if (!kbd->context) return EINA_FALSE;

   if (!kbd->names.rules) kbd->names.rules = strdup("evdev");
   if (!kbd->names.model) kbd->names.model = strdup("pc105");
   if (!kbd->names.layout) kbd->names.layout = strdup("us");

   keymap = xkb_keymap_new_from_names(kbd->context, &kbd->names, 0);
   if (!keymap) return EINA_FALSE;

   kbd->info = _keyboard_info_create(keymap, EINA_FALSE);
   xkb_keymap_unref(keymap);

   if (!kbd->info) return EINA_FALSE;
   return EINA_TRUE;
}

static Elput_Keyboard *
_keyboard_create(Elput_Seat *seat)
{
   Elput_Keyboard *kbd;

   kbd = calloc(1, sizeof(Elput_Keyboard));
   if (!kbd) return NULL;

   kbd->seat = seat;

   return kbd;
}

static Eina_Bool
_keyboard_init(Elput_Seat *seat, struct xkb_keymap *keymap)
{
   Elput_Keyboard *kbd;

   if (seat->kbd)
     {
        seat->count.kbd += 1;
        if (seat->count.kbd == 1)
          _seat_caps_update(seat);
        return EINA_TRUE;
     }

   kbd = _keyboard_create(seat);
   if (!kbd) return EINA_FALSE;

   if (keymap)
     {
        kbd->info = _keyboard_info_create(keymap, EINA_TRUE);
        if (!kbd->info) goto err;
     }
   else
     {
        if (!_keyboard_global_build(kbd)) goto err;
        kbd->info->refs++;
     }

   kbd->state = xkb_state_new(kbd->info->keymap.map);
   if (!kbd->state) goto err;

   seat->kbd = kbd;
   seat->count.kbd = 1;

   _seat_caps_update(seat);

   return EINA_TRUE;

err:
   if (kbd->info) _keyboard_info_destroy(kbd->info, kbd->external_map);
   free(kbd);
   return EINA_FALSE;
}

static void
_keyboard_state_reset(Elput_Keyboard *kbd)
{
   struct xkb_state *state;

   state = xkb_state_new(kbd->info->keymap.map);
   if (!state) return;

   xkb_state_unref(kbd->state);
   kbd->state = state;
}

static void
_keyboard_release(Elput_Seat *seat)
{
   seat->count.kbd--;
   if (seat->count.kbd == 0)
     {
        _keyboard_state_reset(seat->kbd);
        _seat_caps_update(seat);
     }
}

static void
_keyboard_key_send(Elput_Device *dev, enum libinput_key_state state, const char *keyname, const char *key, const char *compose, unsigned int code, unsigned int timestamp)
{
   Ecore_Event_Key *ev;

   ev = calloc(1, sizeof(Ecore_Event_Key) + strlen(key) + strlen(keyname) + 
               ((compose[0] != '\0') ? strlen(compose) : 0) + 3);
   if (!ev) return;

   ev->keyname = (char *)(ev + 1);
   ev->key = ev->keyname + strlen(keyname) + 1;
   ev->compose = strlen(compose) ? ev->key + strlen(key) + 1 : NULL;
   ev->string = ev->compose;

   strcpy((char *)ev->keyname, keyname);
   strcpy((char *)ev->key, key);
   if (strlen(compose)) strcpy((char *)ev->compose, compose);

   ev->keycode = code;
   ev->modifiers = dev->seat->modifiers;
   ev->timestamp = timestamp;
   ev->same_screen = 1;

   ev->window = dev->seat->manager->window;
   ev->event_window = dev->seat->manager->window;
   ev->root_window = dev->seat->manager->window;

   if (state == LIBINPUT_KEY_STATE_PRESSED)
     ecore_event_add(ECORE_EVENT_KEY_DOWN, ev, NULL, NULL);
   else
     ecore_event_add(ECORE_EVENT_KEY_UP, ev, NULL, NULL);
}

static void
_keyboard_keymap_send(Elput_Keyboard_Info *info)
{
   Elput_Event_Keymap_Send *ev;

   ev = calloc(1, sizeof(Elput_Event_Keymap_Send));
   if (!ev) return;

   ev->fd = info->keymap.fd;
   ev->size = info->keymap.size;
   ev->format = XKB_KEYMAP_FORMAT_TEXT_V1;

   ecore_event_add(ELPUT_EVENT_KEYMAP_SEND, ev, NULL, NULL);
}

static void
_keyboard_modifiers_send(Elput_Keyboard *kbd)
{
   Elput_Event_Modifiers_Send *ev;

   ev = calloc(1, sizeof(Elput_Event_Modifiers_Send));
   if (!ev) return;

   ev->depressed = kbd->mods.depressed;
   ev->latched = kbd->mods.latched;
   ev->locked = kbd->mods.locked;
   ev->group = kbd->mods.group;

   ecore_event_add(ELPUT_EVENT_MODIFIERS_SEND, ev, NULL, NULL);
}

static void
_keyboard_keymap_update(Elput_Seat *seat)
{
   Elput_Keyboard *kbd;
   Elput_Keyboard_Info *info;
   struct xkb_state *state;
   xkb_mod_mask_t latched, locked;

   kbd = _evdev_keyboard_get(seat);
   if (!kbd) return;

   info = _keyboard_info_create(kbd->pending_map, kbd->external_map);
   xkb_keymap_unref(kbd->pending_map);
   kbd->pending_map = NULL;

   if (!info) return;

   state = xkb_state_new(info->keymap.map);
   if (!state)
     {
        _keyboard_info_destroy(info, kbd->external_map);
        return;
     }

   latched = xkb_state_serialize_mods(kbd->state, XKB_STATE_MODS_LATCHED);
   locked = xkb_state_serialize_mods(kbd->state, XKB_STATE_MODS_LOCKED);
   xkb_state_update_mask(state, 0, latched, locked, 0, 0, 0);

   _keyboard_info_destroy(kbd->info, kbd->external_map);
   kbd->info = info;

   xkb_state_unref(kbd->state);
   kbd->state = state;

   _keyboard_modifiers_update(kbd, seat);
   _keyboard_keymap_send(kbd->info);

   if ((!latched) && (!locked)) return;

   _keyboard_modifiers_send(kbd);
}

static int
_keyboard_remapped_key_get(Elput_Device *edev, int code)
{
   void *ret = NULL;

   if (!edev) return code;
   if (!edev->key_remap) return code;
   if (!edev->key_remap_hash) return code;

   ret = eina_hash_find(edev->key_remap_hash, &code);
   if (ret) code = (int)(intptr_t)ret;
   return code;
}

static int
_keyboard_keysym_translate(xkb_keysym_t keysym, unsigned int modifiers, char *buffer, int bytes)
{
   unsigned long hbytes = 0;
   unsigned char c;

   if (!keysym) return 0;
   hbytes = (keysym >> 8);

   if (!(bytes &&
         ((hbytes == 0) ||
          ((hbytes == 0xFF) &&
           (((keysym >= XKB_KEY_BackSpace) && (keysym <= XKB_KEY_Clear)) ||
            (keysym == XKB_KEY_Return) || (keysym == XKB_KEY_Escape) ||
            (keysym == XKB_KEY_KP_Space) || (keysym == XKB_KEY_KP_Tab) ||
            (keysym == XKB_KEY_KP_Enter) ||
            ((keysym >= XKB_KEY_KP_Multiply) && (keysym <= XKB_KEY_KP_9)) ||
            (keysym == XKB_KEY_KP_Equal) || (keysym == XKB_KEY_Delete))))))
     return 0;

   if (keysym == XKB_KEY_KP_Space)
     c = (XKB_KEY_space & 0x7F);
   else if (hbytes == 0xFF)
     c = (keysym & 0x7F);
   else
     c = (keysym & 0xFF);

   if (modifiers & ECORE_EVENT_MODIFIER_CTRL)
     {
        if (((c >= '@') && (c < '\177')) || c == ' ')
          c &= 0x1F;
        else if (c == '2')
          c = '\000';
        else if ((c >= '3') && (c <= '7'))
          c -= ('3' - '\033');
        else if (c == '8')
          c = '\177';
        else if (c == '/')
          c = '_' & 0x1F;
     }
   buffer[0] = c;
   return 1;
}

static void
_keyboard_key(struct libinput_device *idevice, struct libinput_event_keyboard *event)
{
   Elput_Device *dev;
   Elput_Keyboard *kbd;
   enum libinput_key_state state;
   xkb_keysym_t sym = XKB_KEY_NoSymbol;
   const xkb_keysym_t *syms;
   unsigned int code = 0;
   unsigned int nsyms;
   unsigned int timestamp;
   char key[256], keyname[256], buffer[256];
   char *tmp = NULL, *compose = NULL;
   int count;

   dev = libinput_device_get_user_data(idevice);
   if (!dev) return;

   kbd = _evdev_keyboard_get(dev->seat);
   if (!kbd) return;

   state = libinput_event_keyboard_get_key_state(event);
   count = libinput_event_keyboard_get_seat_key_count(event);

   /* Ignore key events that are not seat wide state changes. */
   if (((state == LIBINPUT_KEY_STATE_PRESSED) && (count != 1)) ||
       ((state == LIBINPUT_KEY_STATE_RELEASED) && (count != 0)))
     return;

   code = libinput_event_keyboard_get_key(event);
   code = _keyboard_remapped_key_get(dev, code) + 8;

   timestamp = libinput_event_keyboard_get_time(event);

   if (state == LIBINPUT_KEY_STATE_PRESSED)
     xkb_state_update_key(kbd->state, code, XKB_KEY_DOWN);
   else
     xkb_state_update_key(kbd->state, code, XKB_KEY_UP);

   nsyms = xkb_key_get_syms(kbd->state, code, &syms);
   if (nsyms == 1) sym = syms[0];

   memset(key, 0, sizeof(key));
   xkb_keysym_get_name(sym, key, sizeof(key));

   memset(keyname, 0, sizeof(keyname));
   memcpy(keyname, key, sizeof(keyname));

   if (keyname[0] == '\0')
     snprintf(keyname, sizeof(keyname), "Keycode-%u", code);

   if (xkb_state_mod_index_is_active(kbd->state, kbd->info->mods.shift,
                                     XKB_STATE_MODS_EFFECTIVE))
     {
        if (keyname[0] != '\0')
          keyname[0] = tolower(keyname[0]);
     }

   _keyboard_modifiers_update(kbd, dev->seat);

   memset(buffer, 0, sizeof(buffer));
   if (_keyboard_keysym_translate(sym, dev->seat->modifiers, 
                                  buffer, sizeof(buffer)))
     {
        compose = eina_str_convert("ISO8859-1", "UTF-8", buffer);
        if (!compose)
          {
             ERR("Elput cannot convert input key string '%s' to UTF-8. "
                 "Is Eina built with iconv support?", buffer);
          }
        else
          tmp = compose;
     }

   if (!compose) compose = buffer;

   _keyboard_key_send(dev, state, keyname, key, compose, code, timestamp);

   if (tmp) free(tmp);

   if ((kbd->pending_map) && (count == 0))
     _keyboard_keymap_update(dev->seat);

   if (state == LIBINPUT_KEY_STATE_PRESSED)
     {
        kbd->grab.key = code;
        kbd->grab.timestamp = timestamp;
     }
}

static Elput_Pointer *
_pointer_create(Elput_Seat *seat)
{
   Elput_Pointer *ptr;

   ptr = calloc(1, sizeof(Elput_Pointer));
   if (!ptr) return NULL;

   ptr->seat = seat;
   ptr->mouse.threshold = 250;

   return ptr;
}

static Eina_Bool
_pointer_init(Elput_Seat *seat)
{
   Elput_Pointer *ptr;

   if (seat->ptr)
     {
        seat->count.ptr += 1;
        if (seat->count.ptr == 1)
          _seat_caps_update(seat);
        return EINA_TRUE;
     }

   ptr = _pointer_create(seat);
   if (!ptr) return EINA_FALSE;

   seat->ptr = ptr;
   seat->count.ptr = 1;
   ptr->pressure = 1.0;

   _seat_caps_update(seat);

   return EINA_TRUE;
}

static void
_pointer_release(Elput_Seat *seat)
{
   seat->count.ptr--;
   if (seat->count.ptr == 0)
     {
        seat->ptr->buttons = 0;
        _seat_caps_update(seat);
     }
}

static Elput_Touch *
_touch_create(Elput_Seat *seat)
{
   Elput_Touch *touch;

   touch = calloc(1, sizeof(Elput_Touch));
   if (!touch) return NULL;

   touch->seat = seat;
   touch->pressure = 1.0;

   return touch;
}

static Eina_Bool
_touch_init(Elput_Seat *seat)
{
   Elput_Touch *touch;

   if (seat->touch)
     {
        seat->count.touch += 1;
        if (seat->count.touch == 1)
          _seat_caps_update(seat);
        return EINA_TRUE;
     }

   touch = _touch_create(seat);
   if (!touch) return EINA_FALSE;

   seat->touch = touch;
   seat->count.touch = 1;

   _seat_caps_update(seat);

   return EINA_TRUE;
}

static void
_touch_release(Elput_Seat *seat)
{
   seat->count.touch--;
   if (seat->count.touch == 0)
     {
        seat->touch->points = 0;
        _seat_caps_update(seat);
     }
}

static void
_pointer_motion_send(Elput_Device *edev)
{
   Elput_Pointer *ptr;
   Elput_Keyboard *kbd;
   Elput_Touch *touch;
   Ecore_Event_Mouse_Move *ev;
   double x, y;

   ptr = _evdev_pointer_get(edev->seat);
   if (!ptr) return;

   ev = calloc(1, sizeof(Ecore_Event_Mouse_Move));
   if (!ev) return;

   x = ptr->x;
   y = ptr->y;

   if (x < ptr->minx)
     x = ptr->minx;
   else if (x >= ptr->minx + ptr->seat->manager->input.pointer_w)
     x = ptr->minx + ptr->seat->manager->input.pointer_w - 1;

   if (y < ptr->miny)
     y = ptr->miny;
   else if (y >= ptr->miny + ptr->seat->manager->input.pointer_h)
     y = ptr->miny + ptr->seat->manager->input.pointer_h - 1;

   ptr->x = x;
   ptr->y = y;

   ev->window = edev->seat->manager->window;
   ev->event_window = edev->seat->manager->window;
   ev->root_window = edev->seat->manager->window;
   ev->timestamp = ptr->timestamp;
   ev->same_screen = 1;

   ev->x = ptr->x;
   ev->y = ptr->y;
   ev->root.x = ptr->x;
   ev->root.y = ptr->y;

   kbd = _evdev_keyboard_get(edev->seat);
   if (kbd) _keyboard_modifiers_update(kbd, edev->seat);

   ev->modifiers = edev->seat->modifiers;

   touch = _evdev_touch_get(edev->seat);
   if (touch) ev->multi.device = touch->slot;

   ev->multi.radius = 1;
   ev->multi.radius_x = 1;
   ev->multi.radius_y = 1;
   ev->multi.pressure = ptr->pressure;
   ev->multi.angle = 0.0;
   ev->multi.x = ptr->x;
   ev->multi.y = ptr->y;
   ev->multi.root.x = ptr->x;
   ev->multi.root.y = ptr->y;

   ecore_event_add(ECORE_EVENT_MOUSE_MOVE, ev, NULL, NULL);
}

static void
_pointer_motion_relative(struct libinput_event_pointer *event)
{
   Elput_Event_Pointer_Motion *ev;

   ev = calloc(1, sizeof(Elput_Event_Pointer_Motion));
   EINA_SAFETY_ON_NULL_RETURN(ev);

   ev->time_usec = libinput_event_pointer_get_time_usec(event);
   ev->dx = libinput_event_pointer_get_dx(event);
   ev->dy = libinput_event_pointer_get_dy(event);
   ev->dx_unaccel = libinput_event_pointer_get_dx_unaccelerated(event);
   ev->dy_unaccel = libinput_event_pointer_get_dy_unaccelerated(event);

   ecore_event_add(ELPUT_EVENT_POINTER_MOTION, ev, NULL, NULL);
}

static Eina_Bool
_pointer_motion(struct libinput_device *idev, struct libinput_event_pointer *event)
{
   Elput_Device *edev;
   Elput_Pointer *ptr;

   edev = libinput_device_get_user_data(idev);
   if (!edev) return EINA_FALSE;

   ptr = _evdev_pointer_get(edev->seat);
   if (!ptr) return EINA_FALSE;

   ptr->x += libinput_event_pointer_get_dx(event);
   ptr->y += libinput_event_pointer_get_dy(event);
   ptr->timestamp = libinput_event_pointer_get_time(event);

   _pointer_motion_send(edev);
   _pointer_motion_relative(event);

   return EINA_TRUE;
}

static Eina_Bool
_pointer_motion_abs(struct libinput_device *idev, struct libinput_event_pointer *event)
{
   Elput_Device *edev;
   Elput_Pointer *ptr;

   edev = libinput_device_get_user_data(idev);
   if (!edev) return EINA_FALSE;

   ptr = _evdev_pointer_get(edev->seat);
   if (!ptr) return EINA_FALSE;

   ptr->x = libinput_event_pointer_get_absolute_x_transformed(event, edev->ow);
   ptr->y = libinput_event_pointer_get_absolute_y_transformed(event, edev->oh);
   ptr->timestamp = libinput_event_pointer_get_time(event);

   /* TODO: these needs to run a matrix transform based on output */

   _pointer_motion_send(edev);
   _pointer_motion_relative(event);

   return EINA_TRUE;
}

static void
_pointer_button_send(Elput_Device *edev, enum libinput_button_state state)
{
   Elput_Pointer *ptr;
   Elput_Keyboard *kbd;
   Elput_Touch *touch;
   Ecore_Event_Mouse_Button *ev;

   ptr = _evdev_pointer_get(edev->seat);
   if (!ptr) return;

   ev = calloc(1, sizeof(Ecore_Event_Mouse_Button));
   if (!ev) return;

   ev->window = edev->seat->manager->window;
   ev->event_window = edev->seat->manager->window;
   ev->root_window = edev->seat->manager->window;
   ev->timestamp = ptr->timestamp;
   ev->same_screen = 1;

   ev->x = ptr->x;
   ev->y = ptr->y;
   ev->root.x = ptr->x;
   ev->root.y = ptr->y;

   touch = _evdev_touch_get(edev->seat);
   if (touch) ev->multi.device = touch->slot;
   ev->multi.radius = 1;
   ev->multi.radius_x = 1;
   ev->multi.radius_y = 1;
   ev->multi.pressure = ptr->pressure;
   ev->multi.angle = 0.0;
   ev->multi.x = ptr->x;
   ev->multi.y = ptr->y;
   ev->multi.root.x = ptr->x;
   ev->multi.root.y = ptr->y;

   ev->buttons = ptr->buttons;

   ev->double_click = ptr->mouse.double_click;
   ev->triple_click = ptr->mouse.triple_click;

   kbd = _evdev_keyboard_get(edev->seat);
   if (kbd)
     _keyboard_modifiers_update(kbd, edev->seat);
   ev->modifiers = edev->seat->modifiers;

   if (state)
     ecore_event_add(ECORE_EVENT_MOUSE_BUTTON_DOWN, ev, NULL, NULL);
   else
     ecore_event_add(ECORE_EVENT_MOUSE_BUTTON_UP, ev, NULL, NULL);
}

static void
_pointer_click_update(Elput_Pointer *ptr, unsigned int btn)
{
   unsigned int current;

   current = ptr->timestamp;
   ptr->mouse.double_click = EINA_FALSE;
   ptr->mouse.triple_click = EINA_FALSE;

   if (((current - ptr->mouse.prev_time) <= ptr->mouse.threshold) &&
       (btn == ptr->mouse.prev_button))
     {
        ptr->mouse.double_click = EINA_TRUE;
        if (((current - ptr->mouse.last_time) <=
             (2 * ptr->mouse.threshold)) &&
            (btn == ptr->mouse.last_button))
          {
             ptr->mouse.triple_click = EINA_TRUE;
             ptr->mouse.prev_time = 0;
             ptr->mouse.last_time = 0;
             current = 0;
          }
     }

   ptr->mouse.last_time = ptr->mouse.prev_time;
   ptr->mouse.prev_time = current;
   ptr->mouse.last_button = ptr->mouse.prev_button;
   ptr->mouse.prev_button = ptr->buttons;
}

static Eina_Bool
_pointer_button(struct libinput_device *idev, struct libinput_event_pointer *event)
{
   Elput_Device *edev;
   Elput_Pointer *ptr;
   int count;
   enum libinput_button_state state;
   unsigned int btn;

   edev = libinput_device_get_user_data(idev);
   if (!edev) return EINA_FALSE;

   ptr = _evdev_pointer_get(edev->seat);
   if (!ptr) return EINA_FALSE;

   state = libinput_event_pointer_get_button_state(event);
   count = libinput_event_pointer_get_seat_button_count(event);

   /* Ignore button events that are not seat wide state changes. */
   if (((state == LIBINPUT_BUTTON_STATE_PRESSED) && (count != 1)) ||
       ((state == LIBINPUT_BUTTON_STATE_RELEASED) && (count != 0)))
     return EINA_FALSE;

   btn = libinput_event_pointer_get_button(event);

   btn = ((btn & 0x00F) + 1);
   if (btn == 3) btn = 2;
   else if (btn == 2) btn = 3;

   ptr->buttons = btn;
   ptr->timestamp = libinput_event_pointer_get_time(event);

   if (state) _pointer_click_update(ptr, btn);

   _pointer_button_send(edev, state);

   return EINA_TRUE;
}

static void
_pointer_axis_send(Elput_Device *dev, int direction, int value)
{
   Elput_Pointer *ptr;
   Elput_Keyboard *kbd;
   Ecore_Event_Mouse_Wheel *ev;

   ptr = _evdev_pointer_get(dev->seat);
   if (!ptr) return;

   ev = calloc(1, sizeof(Ecore_Event_Mouse_Wheel));
   if (!ev) return;

   ev->window = dev->seat->manager->window;
   ev->event_window = dev->seat->manager->window;
   ev->root_window = dev->seat->manager->window;
   ev->timestamp = ptr->timestamp;
   ev->same_screen = 1;

   ev->x = ptr->x;
   ev->y = ptr->y;
   ev->root.x = ptr->x;
   ev->root.y = ptr->y;

   ev->z = value;
   ev->direction = direction;

   kbd = _evdev_keyboard_get(dev->seat);
   if (kbd) _keyboard_modifiers_update(kbd, dev->seat);

   ev->modifiers = dev->seat->modifiers;

   ecore_event_add(ECORE_EVENT_MOUSE_WHEEL, ev, NULL, NULL);
}

static double
_pointer_axis_value(struct libinput_event_pointer *event, enum libinput_pointer_axis axis)
{
   enum libinput_pointer_axis_source source;
   double val = 0.0;

   source = libinput_event_pointer_get_axis_source(event);
   switch (source)
     {
      case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
        val = libinput_event_pointer_get_axis_value_discrete(event, axis);
        break;
      case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
      case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
        val = libinput_event_pointer_get_axis_value(event, axis);
        break;
      default:
        break;
     }

   return val;
}

static Eina_Bool
_pointer_axis(struct libinput_device *idevice, struct libinput_event_pointer *event)
{
   Elput_Device *dev;
   Elput_Pointer *ptr;
   enum libinput_pointer_axis axis;
   Eina_Bool vert = EINA_FALSE, horiz = EINA_FALSE;
   int dir = 0, val = 0;

   dev = libinput_device_get_user_data(idevice);
   if (!dev) return EINA_FALSE;

   ptr = _evdev_pointer_get(dev->seat);
   if (!ptr) return EINA_FALSE;

#ifdef LIBINPUT_HIGHER_08
   vert =
     libinput_event_pointer_has_axis(event,
                                     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
   horiz =
     libinput_event_pointer_has_axis(event,
                                     LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
   if ((!vert) && (!horiz)) return EINA_FALSE;

   if (vert)
     {
        axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
        val = _pointer_axis_value(event, axis);
     }

   if (horiz)
     {
        axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
        val = _pointer_axis_value(event, axis);
        dir = 1;
     }

#else
   axis = libinput_event_pointer_get_axis(event);
   if (axis == LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL) dir = 1;
   val = libinput_event_pointer_get_axis_value(event);
#endif

   ptr->timestamp = libinput_event_pointer_get_time(event);

   _pointer_axis_send(dev, dir, val);

   return EINA_TRUE;
}

static void
_touch_event_send(Elput_Device *dev, int type)
{
   Elput_Touch *touch;
   Ecore_Event_Mouse_Button *ev;
   unsigned int btn = 0;

   touch = _evdev_touch_get(dev->seat);
   if (!touch) return;

   ev = calloc(1, sizeof(Ecore_Event_Mouse_Button));
   if (!ev) return;

   ev->window = dev->seat->manager->window;
   ev->event_window = dev->seat->manager->window;
   ev->root_window = dev->seat->manager->window;
   ev->timestamp = touch->timestamp;
   ev->same_screen = 1;

   ev->x = touch->x;
   ev->y = touch->y;
   ev->root.x = touch->x;
   ev->root.y = touch->y;

   ev->modifiers = dev->seat->modifiers;

   ev->multi.device = touch->slot;
   ev->multi.radius = 1;
   ev->multi.radius_x = 1;
   ev->multi.radius_y = 1;
   ev->multi.pressure = touch->pressure;
   ev->multi.angle = 0.0;
   ev->multi.x = ev->x;
   ev->multi.y = ev->y;
   ev->multi.root.x = ev->x;
   ev->multi.root.y = ev->y;

   btn = ((btn & 0x00F) + 1);
// XXX: this code is useless. above btn is set to 0 at declaration time, then
// no code changes it until the above like effectively makes it 1. it can
// only ever be 1 so the below lines are pointless. this is probably a bug
// lurking...
//   if (btn == 3) btn = 2;
//   else if (btn == 2) btn = 3;
   ev->buttons = btn;

   ecore_event_add(type, ev, NULL, NULL);
}

static void
_touch_motion_send(Elput_Device *dev)
{
   Elput_Touch *touch;
   Ecore_Event_Mouse_Move *ev;

   touch = _evdev_touch_get(dev->seat);
   if (!touch) return;

   ev = calloc(1, sizeof(Ecore_Event_Mouse_Move));
   if (!ev) return;

   ev->window = dev->seat->manager->window;
   ev->event_window = dev->seat->manager->window;
   ev->root_window = dev->seat->manager->window;
   ev->timestamp = touch->timestamp;
   ev->same_screen = 1;

   ev->x = lround(touch->x);
   ev->y = lround(touch->y);
   ev->root.x = ev->x;
   ev->root.y = ev->y;

   ev->modifiers = dev->seat->modifiers;

   ev->multi.device = touch->slot;
   ev->multi.radius = 1;
   ev->multi.radius_x = 1;
   ev->multi.radius_y = 1;
   ev->multi.pressure = touch->pressure;
   ev->multi.angle = 0.0;
   ev->multi.x = touch->x;
   ev->multi.y = touch->y;
   ev->multi.root.x = touch->x;
   ev->multi.root.y = touch->y;

   ecore_event_add(ECORE_EVENT_MOUSE_MOVE, ev, NULL, NULL);
}

static void
_touch_down(struct libinput_device *idevice, struct libinput_event_touch *event)
{
   Elput_Device *dev;
   Elput_Touch *touch;

   dev = libinput_device_get_user_data(idevice);
   if (!dev) return;

   touch = _evdev_touch_get(dev->seat);
   if (!touch) return;

   touch->slot = libinput_event_touch_get_seat_slot(event);
   touch->timestamp = libinput_event_touch_get_time(event);

   touch->x = libinput_event_touch_get_x_transformed(event, dev->ow);
   touch->y = libinput_event_touch_get_y_transformed(event, dev->oh);

   /* TODO: these needs to run a matrix transform based on output */
   /* _ecore_drm2_output_coordinate_transform(dev->output, */
   /*                                         touch->x, touch->y, */
   /*                                         &touch->x, &touch->y); */

   if (touch->slot == touch->grab.id)
     {
        touch->grab.x = touch->x;
        touch->grab.y = touch->y;
     }

   touch->points++;

   _touch_motion_send(dev);
   _touch_event_send(dev, ECORE_EVENT_MOUSE_BUTTON_DOWN);

   if (touch->points == 1)
     {
        touch->grab.id = touch->slot;
        touch->grab.x = touch->x;
        touch->grab.y = touch->y;
        touch->grab.timestamp = touch->timestamp;
     }
}

static void
_touch_up(struct libinput_device *idevice, struct libinput_event_touch *event)
{
   Elput_Device *dev;
   Elput_Touch *touch;

   dev = libinput_device_get_user_data(idevice);
   if (!dev) return;

   touch = _evdev_touch_get(dev->seat);
   if (!touch) return;

   touch->points--;
   touch->slot = libinput_event_touch_get_seat_slot(event);
   touch->timestamp = libinput_event_touch_get_time(event);

   _touch_motion_send(dev);
   _touch_event_send(dev, ECORE_EVENT_MOUSE_BUTTON_UP);
}

static void
_touch_motion(struct libinput_device *idevice, struct libinput_event_touch *event)
{
   Elput_Device *dev;
   Elput_Touch *touch;

   dev = libinput_device_get_user_data(idevice);
   if (!dev) return;

   touch = _evdev_touch_get(dev->seat);
   if (!touch) return;

   touch->x = libinput_event_touch_get_x_transformed(event, dev->ow);
   touch->y = libinput_event_touch_get_y_transformed(event, dev->oh);

   /* TODO: these needs to run a matrix transform based on output */
   /* _ecore_drm2_output_coordinate_transform(dev->output, */
   /*                                         touch->x, touch->y, */
   /*                                         &touch->x, &touch->y); */

   touch->slot = libinput_event_touch_get_seat_slot(event);
   touch->timestamp = libinput_event_touch_get_time(event);

   _touch_motion_send(dev);
}

void
_evdev_device_calibrate(Elput_Device *dev)
{
   float cal[6];
   const char *vals;
   const char *sysname;
   const char *device;
   Eina_List *devices;
   int w = 0, h = 0;
   enum libinput_config_status status;

   w = dev->ow;
   h = dev->oh;
   if ((w == 0) || (h == 0)) return;

   if ((!libinput_device_config_calibration_has_matrix(dev->device)) ||
       (libinput_device_config_calibration_get_default_matrix(dev->device, cal) != 0))
     return;

   sysname = libinput_device_get_sysname(dev->device);

   devices = eeze_udev_find_by_subsystem_sysname("input", sysname);
   EINA_LIST_FREE(devices, device)
     {
        vals = eeze_udev_syspath_get_property(device, "WL_CALIBRATION");
        if ((!vals) ||
            (sscanf(vals, "%f %f %f %f %f %f",
                    &cal[0], &cal[1], &cal[2], &cal[3], &cal[4], &cal[5]) != 6))
          goto cont;

        cal[2] /= w;
        cal[5] /= h;

        status =
          libinput_device_config_calibration_set_matrix(dev->device, cal);
        if (status != LIBINPUT_CONFIG_STATUS_SUCCESS)
          WRN("Failed to apply device calibration");

cont:
        eina_stringshare_del(device);
     }
}

static void
_axis_event_free(void *d EINA_UNUSED, void *event)
{
   Ecore_Event_Axis_Update *ev = event;

   free(ev->axis);
   free(ev);
}

static void
_tablet_tool_axis(struct libinput_device *idev, struct libinput_event_tablet_tool *event)
{
   Elput_Pointer *ptr;
   struct libinput_tablet_tool *tool;
   Elput_Device *dev = libinput_device_get_user_data(idev);
   Ecore_Event_Axis_Update *ev;
   Ecore_Axis ax[8] = {0}, *axis = NULL;
   int i, num = 0;

   ptr = _evdev_pointer_get(dev->seat);
   EINA_SAFETY_ON_NULL_RETURN(ptr);
   tool = libinput_event_tablet_tool_get_tool(event);

   ptr->x = libinput_event_tablet_tool_get_x_transformed(event, dev->ow);
   ptr->y = libinput_event_tablet_tool_get_y_transformed(event, dev->oh);

   if (libinput_event_tablet_tool_x_has_changed(event))
     {
        ax[num].label = ECORE_AXIS_LABEL_X;
        ax[num].value = ptr->x;
        num++;
     }
   if (libinput_event_tablet_tool_y_has_changed(event))
     {
        ax[num].label = ECORE_AXIS_LABEL_Y;
        ax[num].value = ptr->y;
        num++;
     }
   if (libinput_tablet_tool_has_pressure(tool))
     {
        if (libinput_event_tablet_tool_pressure_has_changed(event))
          {
             ax[num].label = ECORE_AXIS_LABEL_PRESSURE;
             ax[num].value = ptr->pressure = libinput_event_tablet_tool_get_pressure(event);
             num++;
          }
     }
   if (libinput_tablet_tool_has_distance(tool))
     {
        if (libinput_event_tablet_tool_distance_has_changed(event))
          {
             ax[num].label = ECORE_AXIS_LABEL_DISTANCE;
             ax[num].value = libinput_event_tablet_tool_get_distance(event);
             num++;
          }
     }
   if (libinput_tablet_tool_has_tilt(tool))
     {
        if (libinput_event_tablet_tool_tilt_x_has_changed(event) ||
            libinput_event_tablet_tool_tilt_y_has_changed(event))
          {
             double x = sin(libinput_event_tablet_tool_get_tilt_x(event));
             double y = sin(-libinput_event_tablet_tool_get_tilt_y(event));

             ax[num].label = ECORE_AXIS_LABEL_TILT;
             ax[num].value = asin(sqrt((x * x) + (y * y)));
             num++;

             /* note: the value of atan2(0,0) is implementation-defined */
             ax[num].label = ECORE_AXIS_LABEL_AZIMUTH;
             ax[num].value = atan2(y, x);
             num++;
          }
     }
   if (libinput_tablet_tool_has_rotation(tool))
     {
        if (libinput_event_tablet_tool_rotation_has_changed(event))
          {
             ax[num].label = ECORE_AXIS_LABEL_TWIST;
             ax[num].value = libinput_event_tablet_tool_get_rotation(event);
             ax[num].value *= M_PI / 180;
             num++;
          }
     }

   ptr->timestamp = libinput_event_tablet_tool_get_time(event);
   /* FIXME: other properties which efl event structs don't support:
    * slider_position
    * wheel_delta
    */


   if (libinput_event_tablet_tool_x_has_changed(event) ||
       libinput_event_tablet_tool_y_has_changed(event))
     _pointer_motion_send(dev);

   if (!num) return;
   ev = calloc(1, sizeof(Ecore_Event_Axis_Update));

   ev->window = dev->seat->manager->window;
   ev->event_window = dev->seat->manager->window;
   ev->root_window = dev->seat->manager->window;
   ev->timestamp = ptr->timestamp;
   ev->naxis = num;
   ev->axis = axis = calloc(num, sizeof(Ecore_Axis));
   for (i = 0; i < num; i++)
     {
        axis[i].label = ax[i].label;
        axis[i].value = ax[i].value;
     }
   ecore_event_add(ECORE_EVENT_AXIS_UPDATE, ev, _axis_event_free, NULL);
}

static void
_tablet_tool_tip(struct libinput_device *idev, struct libinput_event_tablet_tool *event)
{
   Elput_Pointer *ptr;
   Elput_Device *dev = libinput_device_get_user_data(idev);
   int state;
   int press[] =
   {
    [LIBINPUT_TABLET_TOOL_TIP_DOWN] = LIBINPUT_BUTTON_STATE_PRESSED,
    [LIBINPUT_TABLET_TOOL_TIP_UP] = LIBINPUT_BUTTON_STATE_RELEASED,
   };

   ptr = _evdev_pointer_get(dev->seat);
   EINA_SAFETY_ON_NULL_RETURN(ptr);

   state = libinput_event_tablet_tool_get_tip_state(event);
   ptr->buttons = 1;
   ptr->timestamp = libinput_event_tablet_tool_get_time(event);

   if (press[state]) _pointer_click_update(ptr, 1);

   _pointer_button_send(dev, press[state]);
}

int
_evdev_event_process(struct libinput_event *event)
{
   struct libinput_device *idev;
   int ret = 1;
   Eina_Bool frame = EINA_FALSE;

   idev = libinput_event_get_device(event);
   switch (libinput_event_get_type(event))
     {
      case LIBINPUT_EVENT_KEYBOARD_KEY:
        _keyboard_key(idev, libinput_event_get_keyboard_event(event));
        break;
      case LIBINPUT_EVENT_POINTER_MOTION:
        frame =
          _pointer_motion(idev, libinput_event_get_pointer_event(event));
        break;
      case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
        frame =
          _pointer_motion_abs(idev, libinput_event_get_pointer_event(event));
        break;
      case LIBINPUT_EVENT_POINTER_BUTTON:
        frame =
          _pointer_button(idev, libinput_event_get_pointer_event(event));
        break;
      case LIBINPUT_EVENT_POINTER_AXIS:
        frame =
          _pointer_axis(idev, libinput_event_get_pointer_event(event));
        break;
      case LIBINPUT_EVENT_TOUCH_DOWN:
        _touch_down(idev, libinput_event_get_touch_event(event));
        break;
      case LIBINPUT_EVENT_TOUCH_MOTION:
        _touch_motion(idev, libinput_event_get_touch_event(event));
        break;
      case LIBINPUT_EVENT_TOUCH_UP:
        _touch_up(idev, libinput_event_get_touch_event(event));
        break;
      case LIBINPUT_EVENT_TOUCH_FRAME: break;
      case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
        _tablet_tool_axis(idev, libinput_event_get_tablet_tool_event(event));
        break;
      case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: /* is this useful? */
        break;
      case LIBINPUT_EVENT_TABLET_TOOL_TIP: /* is this useful? */
        _tablet_tool_tip(idev, libinput_event_get_tablet_tool_event(event));
        break;
      default:
        ret = 0;
        break;
     }

   if (frame)
     {
        Elput_Device *edev;

        edev = libinput_device_get_user_data(idev);
        if (edev) _seat_frame_send(edev->seat);
     }

   return ret;
}

Elput_Device *
_evdev_device_create(Elput_Seat *seat, struct libinput_device *device)
{
   Elput_Device *edev;
   const char *oname;

   edev = calloc(1, sizeof(Elput_Device));
   if (!edev) return NULL;

   edev->seat = seat;
   edev->device = device;
   edev->caps = 0;

   oname = libinput_device_get_output_name(device);
   eina_stringshare_replace(&edev->output_name, oname);

   if ((libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD)) &&
       (libinput_device_keyboard_has_key(device, KEY_ENTER)))
     {
        _keyboard_init(seat, seat->manager->cached.keymap);
        edev->caps |= EVDEV_SEAT_KEYBOARD;
     }
 
   if ((libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER) &&
       (libinput_device_pointer_has_button(device, BTN_LEFT))))
     edev->caps |= EVDEV_SEAT_POINTER;
   if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_TOOL))
     edev->caps |= EVDEV_SEAT_POINTER | EVDEV_SEAT_TABLET_TOOL;
   if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_PAD))
     edev->caps |= EVDEV_SEAT_POINTER | EVDEV_SEAT_TABLET_PAD;
   if (edev->caps & EVDEV_SEAT_POINTER)
     {
        _pointer_init(seat);
        edev->caps |= EVDEV_SEAT_POINTER;
     }

   if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TOUCH))
     edev->caps |= EVDEV_SEAT_TOUCH;
   if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_GESTURE))
     edev->caps |= EVDEV_SEAT_TOUCH | EVDEV_SEAT_GESTURE;
   if (edev->caps & EVDEV_SEAT_TOUCH)
     _touch_init(seat);

   if (!edev->caps) goto err;

   libinput_device_set_user_data(device, edev);
   libinput_device_ref(edev->device);

   if (libinput_device_config_tap_get_finger_count(edev->device) > 0)
     {
        Eina_Bool enable = EINA_FALSE;

        enable = libinput_device_config_tap_get_default_enabled(edev->device);
        libinput_device_config_tap_set_enabled(edev->device, enable);
     }

   return edev;

err:
   eina_stringshare_del(edev->output_name);
   free(edev);
   return NULL;
}

void
_evdev_device_destroy(Elput_Device *edev)
{
   if (!edev) return;

   if (edev->caps & EVDEV_SEAT_POINTER)
     _pointer_release(edev->seat);
   if (edev->caps & EVDEV_SEAT_KEYBOARD)
     _keyboard_release(edev->seat);
   if (edev->caps & EVDEV_SEAT_TOUCH)
     _touch_release(edev->seat);

   libinput_device_unref(edev->device);
   eina_stringshare_del(edev->output_name);

   if (edev->key_remap_hash) eina_hash_free(edev->key_remap_hash);

   free(edev);
}

void
_evdev_keyboard_destroy(Elput_Keyboard *kbd)
{
   free((char *)kbd->names.rules);
   free((char *)kbd->names.model);
   free((char *)kbd->names.layout);
   free((char *)kbd->names.variant);
   free((char *)kbd->names.options);

   if (kbd->state) xkb_state_unref(kbd->state);
   if (kbd->info) _keyboard_info_destroy(kbd->info, kbd->external_map);

   xkb_context_unref(kbd->context);
   xkb_keymap_unref(kbd->pending_map);

   free(kbd);
}

void
_evdev_pointer_destroy(Elput_Pointer *ptr)
{
   /* FIXME: destroy any resources inside pointer structure */
   free(ptr);
}

void
_evdev_touch_destroy(Elput_Touch *touch)
{
   /* FIXME: destroy any resources inside touch structure */
   free(touch);
}

void
_evdev_pointer_motion_send(Elput_Device *edev)
{
   _pointer_motion_send(edev);
}

Elput_Pointer *
_evdev_pointer_get(Elput_Seat *seat)
{
   if (!seat) return NULL;
   if (seat->count.ptr) return seat->ptr;
   return NULL;
}

Elput_Keyboard *
_evdev_keyboard_get(Elput_Seat *seat)
{
   if (!seat) return NULL;
   if (seat->count.kbd) return seat->kbd;
   return NULL;
}

Elput_Touch *
_evdev_touch_get(Elput_Seat *seat)
{
   if (!seat) return NULL;
   if (seat->count.touch) return seat->touch;
   return NULL;
}