aboutsummaryrefslogblamecommitdiffstats
path: root/src/lib/elput/elput_evdev.c
blob: 29c695dcecd9739478a1a0458df41969fd9e7a76 (plain) (tree)
1
2
3
4
5
6
7
8
9
10


                          






                                             

                                   










                                                          

 











                                                           




























                                                                 


                            
              
              

                      
                         
 


                                           
                                                              
 
                                          
                         



















                                                      

                             































































                                                                               

                                      

                 





































                                                                            
 











                                                           

                                  






















































                                                                       

















                                                                                                                                                                          




                                        


                                                 
 


                                         
                                           
                                                                              
       
                                                                            











































































                                                                          













                                                        







































































                                                                                                


                                                    


































                                                                       
                                                                        





















                                                                          








                                          
                              












                                 

                                  













































                                          

                                  























                                 



















                                                  

                                                                 


                     

                                                                 
 


              


                                                  


                                  



                       


















                                                        



                                                                              

 
















                                                                       




                                                                                   






                                              

                                                  


                                                           
                                   






















                                                                                       
                                   

















                                                                          


                                                  



















                                             


                                          










                                                 

                                                       
       

                                                     
















































































                                                                                   


                                                 















                                                       



                                                                               











                                                                                          
                                                                          




























































                                                                                    
                                              










                                                    


                                                 
                                    




















                                        





                                                                             

                     



                                                            


           


































                                                  



                                                                              


           



                                                                                






                                                

                                                           








                                                                       
                                     




                                 

                   
                           
                                                         


                          
                                     

                                 
                                                 
















                                                                              
                                                           
 
                           
                                                       


           



















                                                                                  
                                                           
 
                           

 
    














                                                                       
                                                                                      

























                                                                                










                                                  
                                                                      

                                         

                                                                         

                                                  

                                                                             

                                         

                                                                         

                                       

                                                                       

                                     
                                                                 

                                       
                                                                   

                                   
                                                               








                                      
                           
 
                                                   
                                               








                                                                      
                     





                                          
                  
 


                                                       

                                                                                
      
                                                           
                                          

      

                                                                              

                            
                                         




                                                                         
                                       

      


                                              

              
















                                                                              



               






                                         


                                  
                                       
                                  
                                        
                                   
                                     



                                           


                                                                  

































                                                                       
 





                                              






















                                             
#include "elput_private.h"

static void
_ecore_event_cb_free(void *data, void *event)
{
   if (data) efl_unref(data);
   free(event);
}

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)
{
   int fd = 0;
   char *path;
   char tmp[PATH_MAX];
   long flags;
   Eina_Tmpstr *fullname;

   if (!(path = getenv("XDG_RUNTIME_DIR")))
     return -1;

   snprintf(tmp, sizeof(tmp), "%s/elput-keymap-XXXXXX", path);

   fd = eina_file_mkstemp(tmp, &fullname);
   if (fd < 0) return -1;

   flags = fcntl(fd, F_GETFD);
   if (flags < 0)
     {
        close(fd);
        return -1;
     }

   if (fcntl(fd, F_SETFD, (flags | FD_CLOEXEC)) == -1)
     {
        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 (dev->evas_device)
     ev->dev = efl_ref(dev->evas_device);

   if (state == LIBINPUT_KEY_STATE_PRESSED)
     ecore_event_add(ECORE_EVENT_KEY_DOWN, ev, _ecore_event_cb_free, ev->dev);
   else
     ecore_event_add(ECORE_EVENT_KEY_UP, ev, _ecore_event_cb_free, ev->dev);
}

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;

   _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;

   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 = 1.0;
   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;

   if (edev->evas_device)
     ev->dev = efl_ref(edev->evas_device);

   ecore_event_add(ECORE_EVENT_MOUSE_MOVE, ev, _ecore_event_cb_free, ev->dev);
}

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 = 1.0;
   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;

   if (edev->evas_device)
     ev->dev = efl_ref(edev->evas_device);

   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,
                     _ecore_event_cb_free, ev->dev);
   else
     ecore_event_add(ECORE_EVENT_MOUSE_BUTTON_UP, ev,
                     _ecore_event_cb_free, ev->dev);
}

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)
     {
        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;
     }

   _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;

   if (dev->evas_device)
     ev->dev = efl_ref(dev->evas_device);

   ecore_event_add(ECORE_EVENT_MOUSE_WHEEL, ev, _ecore_event_cb_free, ev->dev);
}

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;
     }

   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 = 1.0;
   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;

   if (dev->evas_device)
     ev->dev = efl_ref(dev->evas_device);

   ecore_event_add(type, ev, _ecore_event_cb_free, ev->dev);
}

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 = 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 = 1.0;
   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;

   if (dev->evas_device)
     ev->dev = efl_ref(dev->evas_device);

   ecore_event_add(ECORE_EVENT_MOUSE_MOVE, ev, _ecore_event_cb_free, ev->dev);
}

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);
     }
}

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:
      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 |= ELPUT_SEAT_KEYBOARD;
     }
 
   if ((libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER) &&
       (libinput_device_pointer_has_button(device, BTN_LEFT))))
     {
        _pointer_init(seat);
        edev->caps |= ELPUT_SEAT_POINTER;
     }

   if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TOUCH))
     {
        _touch_init(seat);
        edev->caps |= ELPUT_SEAT_TOUCH;
     }

   if (!((edev->caps & ELPUT_SEAT_KEYBOARD) ||
         (edev->caps & ELPUT_SEAT_POINTER) ||
         (edev->caps & ELPUT_SEAT_TOUCH)))
     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);
     }

   /* FIXME: Normally we would do a device calibration set here however
    * that requires Output support. Since this is just an input library, we
    * may need to add external facing APIs to do calibration. Then a user of
    * elput would handle outputs, and make calls to calibrate */

   return edev;

err:
   free(edev);
   return NULL;
}

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

   if (edev->evas_device)
     efl_unref(edev->evas_device);

   if (edev->caps & ELPUT_SEAT_POINTER)
     _pointer_release(edev->seat);
   if (edev->caps & ELPUT_SEAT_KEYBOARD)
     _keyboard_release(edev->seat);
   if (edev->caps & ELPUT_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;
}