elput: define and implement an async device opening interface for libinput
authorMike Blumenkrantz <zmike@osg.samsung.com>
Tue, 24 May 2016 20:11:20 +0000 (16:11 -0400)
committerMike Blumenkrantz <zmike@osg.samsung.com>
Wed, 25 May 2016 16:57:27 +0000 (12:57 -0400)
this adds an overly-complex method of removing blocking dbus calls from libinput's
synchronous device initialization architecture. libinput was clearly never meant
to be used in this way, but we're doing it anyway because we're efl.

 #SamsungFeatures

src/lib/elput/Elput.h
src/lib/elput/elput_input.c
src/lib/elput/elput_logind.c
src/lib/elput/elput_manager.c
src/lib/elput/elput_private.h

index 07ebfa2..2f31160 100644 (file)
@@ -240,14 +240,13 @@ EAPI const Eina_List *elput_manager_seats_get(Elput_Manager *manager);
  * Initialize input
  *
  * @param manager
- * @param seat
  *
  * @return EINA_TRUE on success, EINA_FALSE on failure
  *
  * @ingroup Elput_Input_Group
  * @since 1.18
  */
-EAPI Eina_Bool elput_input_init(Elput_Manager *manager, const char *seat);
+EAPI Eina_Bool elput_input_init(Elput_Manager *manager);
 
 /**
  * Shutdown input
index d49a2d0..8431c29 100644 (file)
@@ -1,12 +1,82 @@
 #include "elput_private.h"
+#include <libudev.h>
+
+void
+_elput_input_window_update(Elput_Manager *manager)
+{
+   Eina_List *l, *ll;
+   Elput_Seat *seat;
+   Elput_Device *device;
+
+   if (manager->input.thread) return;
+   EINA_LIST_FOREACH(manager->input.seats, l, seat)
+     EINA_LIST_FOREACH(seat->devices, ll, device)
+       device->window = manager->window;
+}
+
+void
+_elput_input_pointer_max_update(Elput_Manager *manager)
+{
+   Eina_List *l;
+   Elput_Seat *eseat;
+
+   if (manager->input.thread) return;
+   EINA_LIST_FOREACH(manager->input.seats, l, eseat)
+     {
+        if (!eseat->ptr) continue;
+
+        eseat->ptr->maxw = manager->input.pointer_w;
+        eseat->ptr->maxh = manager->input.pointer_h;
+     }
+}
 
 static int
 _cb_open_restricted(const char *path, int flags, void *data)
 {
-   Elput_Manager *em;
-
-   em = data;
-   return elput_manager_open(em, path, flags);
+   Elput_Manager *em = data;
+   int ret = -1;
+   Elput_Async_Open *ao;
+   int p[2];
+
+   if (!em->input.thread)
+     return em->interface->open(em, path, flags);
+   if (!em->interface->open_async) return ret;
+   ao = calloc(1, sizeof(Elput_Async_Open));
+   if (!ao) return ret;
+   if (pipe2(p, O_CLOEXEC) < 0)
+     {
+        free(ao);
+        return ret;
+     }
+   ao->manager = em;
+   ao->path = strdup(path);
+   ao->flags = flags;
+   em->input.pipe = p[1];
+   ecore_thread_feedback(em->input.thread, ao);
+   while (!ecore_thread_check(em->input.thread))
+     {
+        int avail, fd;
+        fd_set rfds, wfds, exfds;
+        struct timeval tv, *t;
+
+        FD_ZERO(&rfds);
+        FD_ZERO(&wfds);
+        FD_ZERO(&exfds);
+        FD_SET(p[0], &rfds);
+        tv.tv_sec = 0;
+        tv.tv_usec = 300;
+        t = &tv;
+        avail = select(p[0] + 1, &rfds, &wfds, &exfds, t);
+        if (avail > 0)
+          {
+             read(p[0], &fd, sizeof(int));
+             ret = fd;
+             break;
+          }
+        if (avail < 0) break;
+     }
+   close(p[0]);
+   return ret;
 }
 
 static void
@@ -209,52 +279,91 @@ _cb_input_dispatch(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED)
    return EINA_TRUE;
 }
 
-EAPI Eina_Bool
-elput_input_init(Elput_Manager *manager, const char *seat)
+static void
+_elput_input_init_cancel(void *data, Ecore_Thread *eth EINA_UNUSED)
 {
-   int fd;
-
-   EINA_SAFETY_ON_NULL_RETURN_VAL(manager, EINA_FALSE);
+   Elput_Manager *manager = data;
 
-   memset(&manager->input, 0, sizeof(Elput_Input));
-
-   manager->input.lib =
-     libinput_udev_create_context(&_input_interface, manager, eeze_udev_get());
-   if (!manager->input.lib)
+   manager->input.thread = NULL;
+   if (manager->input.current_pending)
      {
-        ERR("libinput could not create udev context");
-        goto udev_err;
+        eldbus_pending_cancel(manager->input.current_pending);
+        if (manager->input.pipe >= 0)
+          close(manager->input.pipe);
      }
+   if (manager->del)
+     elput_manager_disconnect(manager);
+}
 
-   /* if not seat name is passed in, just use default seat name */
-   if (!seat) seat = "seat0";
+static void
+_elput_input_init_end(void *data, Ecore_Thread *eth EINA_UNUSED)
+{
+   Elput_Manager *manager = data;
 
-   if (libinput_udev_assign_seat(manager->input.lib, seat) != 0)
+   manager->input.thread = NULL;
+   if (!manager->input.lib) return;
+   manager->input.hdlr =
+     ecore_main_fd_handler_add(libinput_get_fd(manager->input.lib), ECORE_FD_READ,
+       _cb_input_dispatch, &manager->input, NULL, NULL);
+
+   if (manager->input.hdlr)
      {
-        ERR("libinput could not assign udev seat");
-        goto seat_err;
+        _process_events(&manager->input);
+        _elput_input_window_update(manager);
+        _elput_input_pointer_max_update(manager);
+     }
+   else
+     {
+        ERR("Could not create input fd handler");
+        libinput_unref(manager->input.lib);
+        manager->input.lib = NULL;
      }
+}
 
-   _process_events(&manager->input);
+static void
+_elput_input_init_notify(void *data EINA_UNUSED, Ecore_Thread *eth EINA_UNUSED, void *msg_data)
+{
+   Elput_Async_Open *ao = msg_data;
 
-   fd = libinput_get_fd(manager->input.lib);
+   ao->manager->interface->open_async(ao->manager, ao->path, ao->flags);
+   free(ao->path);
+   free(ao);
+}
 
-   manager->input.hdlr =
-     ecore_main_fd_handler_add(fd, ECORE_FD_READ, _cb_input_dispatch,
-                               &manager->input, NULL, NULL);
-   if (!manager->input.hdlr)
+static void
+_elput_input_init_thread(void *data, Ecore_Thread *eth EINA_UNUSED)
+{
+   Elput_Manager *manager = data;
+   struct udev *udev = udev_new();
+
+   manager->input.lib =
+     libinput_udev_create_context(&_input_interface, manager, udev);
+   if (!manager->input.lib)
      {
-        ERR("Could not create input fd handler");
-        goto hdlr_err;
+        ERR("libinput could not create udev context");
+        return;
      }
+   udev_unref(udev);
 
-   return EINA_TRUE;
+   if (libinput_udev_assign_seat(manager->input.lib, manager->seat))
+     {
+        ERR("libinput could not assign udev seat");
+        libinput_unref(manager->input.lib);
+        manager->input.lib = NULL;
+     }
+}
+
+EAPI Eina_Bool
+elput_input_init(Elput_Manager *manager)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(manager, EINA_FALSE);
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(!!manager->input.hdlr, EINA_TRUE);
 
-hdlr_err:
-seat_err:
-   libinput_unref(manager->input.lib);
-udev_err:
-   return EINA_FALSE;
+   memset(&manager->input, 0, sizeof(Elput_Input));
+   manager->input.thread =
+     ecore_thread_feedback_run(_elput_input_init_thread, _elput_input_init_notify,
+     _elput_input_init_end, _elput_input_init_cancel, manager, 1);
+   return !!manager->input.thread;
 }
 
 EAPI void
@@ -263,14 +372,18 @@ elput_input_shutdown(Elput_Manager *manager)
    Elput_Seat *seat;
 
    EINA_SAFETY_ON_NULL_RETURN(manager);
-   EINA_SAFETY_ON_NULL_RETURN(&manager->input);
 
-   if (manager->input.hdlr) ecore_main_fd_handler_del(manager->input.hdlr);
+   ecore_main_fd_handler_del(manager->input.hdlr);
 
    EINA_LIST_FREE(manager->input.seats, seat)
      _udev_seat_destroy(seat);
-
-   libinput_unref(manager->input.lib);
+   if (manager->input.thread)
+     ecore_thread_cancel(manager->input.thread);
+   else
+     {
+        libinput_unref(manager->input.lib);
+        manager->input.lib = NULL;
+     }
 }
 
 EAPI void
index 3beb8b4..8db4a11 100644 (file)
@@ -30,31 +30,19 @@ _logind_session_active_send(Elput_Manager *em, Eina_Bool active)
 static void
 _logind_device_pause_complete(Elput_Manager *em, uint32_t major, uint32_t minor)
 {
-   Eldbus_Proxy *proxy;
    Eldbus_Message *msg;
 
-   proxy =
-     eldbus_proxy_get(em->dbus.obj, "org.freedesktop.login1.Session");
-   if (!proxy)
-     {
-        ERR("Could not get proxy for session");
-        return;
-     }
-
-   msg = eldbus_proxy_method_call_new(proxy, "PauseDeviceComplete");
+   msg = eldbus_proxy_method_call_new(em->dbus.session, "PauseDeviceComplete");
    if (!msg)
      {
         ERR("Could not create method call for proxy");
-        goto end;
+        eldbus_message_unref(msg);
+        return;
      }
 
    eldbus_message_arguments_append(msg, "uu", major, minor);
 
-   eldbus_proxy_send(proxy, msg, NULL, NULL, -1);
-
-end:
-   eldbus_message_unref(msg);
-   eldbus_proxy_unref(proxy);
+   eldbus_proxy_send(em->dbus.session, msg, NULL, NULL, -1);
 }
 
 static void
@@ -194,10 +182,10 @@ _logind_dbus_setup(Elput_Manager *em)
         ERR("Could not get dbus proxy");
         goto proxy_err;
      }
+   em->dbus.manager = proxy;
 
    eldbus_proxy_signal_handler_add(proxy, "SessionRemoved",
                                    _cb_session_removed, em);
-   eldbus_proxy_unref(proxy);
 
    proxy =
      eldbus_proxy_get(em->dbus.obj, "org.freedesktop.login1.Session");
@@ -206,13 +194,12 @@ _logind_dbus_setup(Elput_Manager *em)
         ERR("Could not get dbus proxy");
         goto proxy_err;
      }
+   em->dbus.session = proxy;
 
    eldbus_proxy_signal_handler_add(proxy, "PauseDevice",
                                    _cb_device_paused, em);
    eldbus_proxy_signal_handler_add(proxy, "ResumeDevice",
                                    _cb_device_resumed, em);
-   eldbus_proxy_unref(proxy);
-
    proxy =
      eldbus_proxy_get(em->dbus.obj, "org.freedesktop.DBus.Properties");
    if (!proxy)
@@ -235,173 +222,189 @@ obj_err:
 static Eina_Bool
 _logind_control_take(Elput_Manager *em)
 {
-   Eldbus_Proxy *proxy;
    Eldbus_Message *msg, *reply;
    const char *errname, *errmsg;
 
-   proxy =
-     eldbus_proxy_get(em->dbus.obj, "org.freedesktop.login1.Session");
-   if (!proxy)
-     {
-        ERR("Could not get proxy for session");
-        return EINA_FALSE;
-     }
-
-   msg = eldbus_proxy_method_call_new(proxy, "TakeControl");
+   msg = eldbus_proxy_method_call_new(em->dbus.session, "TakeControl");
    if (!msg)
      {
         ERR("Could not create method call for proxy");
-        goto msg_err;
+        return EINA_FALSE;
      }
 
    eldbus_message_arguments_append(msg, "b", EINA_FALSE);
 
-   reply = eldbus_proxy_send_and_block(proxy, msg, -1);
+   reply = eldbus_proxy_send_and_block(em->dbus.session, msg, -1);
    if (eldbus_message_error_get(reply, &errname, &errmsg))
      {
         ERR("Eldbus Message Error: %s %s", errname, errmsg);
-        goto msg_err;
+        return EINA_FALSE;
      }
 
    eldbus_message_unref(reply);
-   eldbus_proxy_unref(proxy);
 
    return EINA_TRUE;
-
-msg_err:
-   eldbus_proxy_unref(proxy);
-   return EINA_FALSE;
 }
 
 static void
 _logind_control_release(Elput_Manager *em)
 {
-   Eldbus_Proxy *proxy;
    Eldbus_Message *msg;
 
-   proxy =
-     eldbus_proxy_get(em->dbus.obj, "org.freedesktop.login1.Session");
-   if (!proxy)
+   msg = eldbus_proxy_method_call_new(em->dbus.session, "ReleaseControl");
+   if (!msg)
      {
-        ERR("Could not get proxy for session");
+        ERR("Could not create method call for proxy");
         return;
      }
 
-   msg = eldbus_proxy_method_call_new(proxy, "ReleaseControl");
+   eldbus_proxy_send(em->dbus.session, msg, NULL, NULL, -1);
+}
+
+static void
+_logind_device_release(Elput_Manager *em, uint32_t major, uint32_t minor)
+{
+   Eldbus_Message *msg;
+
+   msg = eldbus_proxy_method_call_new(em->dbus.session, "ReleaseDevice");
    if (!msg)
      {
         ERR("Could not create method call for proxy");
-        goto end;
+        return;
      }
 
-   eldbus_proxy_send(proxy, msg, NULL, NULL, -1);
+   eldbus_message_arguments_append(msg, "uu", major, minor);
 
-end:
-   eldbus_proxy_unref(proxy);
+   eldbus_proxy_send(em->dbus.session, msg, NULL, NULL, -1);
 }
 
-static int
-_logind_device_take(Elput_Manager *em, uint32_t major, uint32_t minor)
+static void
+_logind_pipe_write_fd(Elput_Manager *em, int fd)
+{
+   write(em->input.pipe, &fd, sizeof(int));
+   close(em->input.pipe);
+   em->input.pipe = -1;
+}
+
+static void
+_logind_device_take_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
 {
-   Eldbus_Proxy *proxy;
-   Eldbus_Message *msg, *reply;
    Eina_Bool p = EINA_FALSE;
    const char *errname, *errmsg;
-   int fd = -1;
-
-   proxy =
-     eldbus_proxy_get(em->dbus.obj, "org.freedesktop.login1.Session");
-   if (!proxy)
-     {
-        ERR("Could not get dbus proxy");
-        return -1;
-     }
-
-   msg = eldbus_proxy_method_call_new(proxy, "TakeDevice");
-   if (!msg)
-     {
-        ERR("Could not create method call for proxy");
-        goto err;
-     }
+   int ret, fd = -1;
+   int fl, flags;
+   Elput_Manager *em = data;
 
-   eldbus_message_arguments_append(msg, "uu", major, minor);
+   if (em->input.current_pending == pending)
+     em->input.current_pending = NULL;
 
-   reply = eldbus_proxy_send_and_block(proxy, msg, -1);
-   if (eldbus_message_error_get(reply, &errname, &errmsg))
+   if (eldbus_message_error_get(msg, &errname, &errmsg))
      {
         ERR("Eldbus Message Error: %s %s", errname, errmsg);
         goto err;
      }
 
-   if (!eldbus_message_arguments_get(reply, "hb", &fd, &p))
+   if (!eldbus_message_arguments_get(msg, "hb", &fd, &p))
      ERR("Could not get UNIX_FD from dbus message");
 
-   eldbus_message_unref(reply);
+   if (fd < 0) goto err;
+
+   fl = fcntl(fd, F_GETFL);
+   if (fl < 0) goto err;
+
+   flags = (intptr_t)eldbus_pending_data_get(pending, "flags");
+
+   if (flags & O_NONBLOCK)
+     fl |= O_NONBLOCK;
+
+   ret = fcntl(fd, F_SETFL, fl);
+   if (ret < 0) goto err;
+
+   _logind_pipe_write_fd(em, fd);
+   return;
 
 err:
-   eldbus_proxy_unref(proxy);
-   return fd;
+   if (fd >= 0)
+     {
+        uintptr_t majo, mino;
+
+        close(fd);
+        majo = (uintptr_t)eldbus_pending_data_get(pending, "major");
+        mino = (uintptr_t)eldbus_pending_data_get(pending, "minor");
+        _logind_device_release(em, majo, mino);
+     }
+   fd = -1;
+   _logind_pipe_write_fd(em, fd);
 }
 
 static void
-_logind_device_release(Elput_Manager *em, uint32_t major, uint32_t minor)
+_logind_device_take_async(Elput_Manager *em, int flags, uint32_t major, uint32_t minor)
 {
-   Eldbus_Proxy *proxy;
    Eldbus_Message *msg;
+   intptr_t fd = -1;
 
-   proxy =
-     eldbus_proxy_get(em->dbus.obj, "org.freedesktop.login1.Session");
-   if (!proxy)
+   msg = eldbus_proxy_method_call_new(em->dbus.session, "TakeDevice");
+   if (!msg)
      {
-        ERR("Could not get proxy for session");
+        ERR("Could not create method call for proxy");
+        _logind_pipe_write_fd(em, fd);
         return;
      }
 
-   msg = eldbus_proxy_method_call_new(proxy, "ReleaseDevice");
+   eldbus_message_arguments_append(msg, "uu", major, minor);
+
+   em->input.current_pending = eldbus_proxy_send(em->dbus.session, msg, _logind_device_take_cb, em, -1);
+   if (!em->input.current_pending) CRIT("FAIL!");
+   eldbus_pending_data_set(em->input.current_pending, "major", (uintptr_t*)(uintptr_t)major);
+   eldbus_pending_data_set(em->input.current_pending, "minor", (uintptr_t*)(uintptr_t)minor);
+   eldbus_pending_data_set(em->input.current_pending, "flags", (intptr_t*)(intptr_t)flags);
+}
+
+static int
+_logind_device_take(Elput_Manager *em, uint32_t major, uint32_t minor)
+{
+   Eldbus_Message *msg, *reply;
+   Eina_Bool p = EINA_FALSE;
+   const char *errname, *errmsg;
+   int fd = -1;
+
+   msg = eldbus_proxy_method_call_new(em->dbus.session, "TakeDevice");
    if (!msg)
      {
         ERR("Could not create method call for proxy");
-        goto end;
+        return -1;
      }
 
    eldbus_message_arguments_append(msg, "uu", major, minor);
 
-   eldbus_proxy_send(proxy, msg, NULL, NULL, -1);
+   reply = eldbus_proxy_send_and_block(em->dbus.session, msg, -1);
+   if (eldbus_message_error_get(reply, &errname, &errmsg))
+     {
+        ERR("Eldbus Message Error: %s %s", errname, errmsg);
+        return -1;
+     }
 
-end:
-   eldbus_proxy_unref(proxy);
+   if (!eldbus_message_arguments_get(reply, "hb", &fd, &p))
+     ERR("Could not get UNIX_FD from dbus message");
+
+   eldbus_message_unref(reply);
+   return fd;
 }
 
 static Eina_Bool
 _logind_activate(Elput_Manager *em)
 {
-   Eldbus_Proxy *proxy;
    Eldbus_Message *msg;
 
-   proxy =
-     eldbus_proxy_get(em->dbus.obj, "org.freedesktop.login1.Session");
-   if (!proxy)
-     {
-        ERR("Could not get proxy for session");
-        return EINA_FALSE;
-     }
-
-   msg = eldbus_proxy_method_call_new(proxy, "Activate");
+   msg = eldbus_proxy_method_call_new(em->dbus.session, "Activate");
    if (!msg)
      {
         ERR("Could not create method call for proxy");
-        goto msg_err;
+        return EINA_FALSE;
      }
 
-   eldbus_proxy_send(proxy, msg, NULL, NULL, -1);
-
-   eldbus_proxy_unref(proxy);
-
+   eldbus_proxy_send(em->dbus.session, msg, NULL, NULL, -1);
    return EINA_TRUE;
-
-msg_err:
-   eldbus_proxy_unref(proxy);
-   return EINA_FALSE;
 }
 
 static Eina_Bool
@@ -498,6 +501,8 @@ static void
 _logind_disconnect(Elput_Manager *em)
 {
    _logind_control_release(em);
+   eldbus_proxy_unref(em->dbus.manager);
+   eldbus_proxy_unref(em->dbus.session);
    eldbus_object_unref(em->dbus.obj);
    free(em->dbus.path);
    _logind_dbus_close(em->dbus.conn);
@@ -506,6 +511,18 @@ _logind_disconnect(Elput_Manager *em)
    free(em);
 }
 
+static void
+_logind_open_async(Elput_Manager *em, const char *path, int flags)
+{
+   struct stat st;
+   intptr_t fd = -1;
+
+   if ((stat(path, &st) < 0) || (!S_ISCHR(st.st_mode)))
+        _logind_pipe_write_fd(em, fd);
+   else
+     _logind_device_take_async(em, flags, major(st.st_rdev), minor(st.st_rdev));
+}
+
 static int
 _logind_open(Elput_Manager *em, const char *path, int flags)
 {
@@ -606,6 +623,7 @@ Elput_Interface _logind_interface =
    _logind_connect,
    _logind_disconnect,
    _logind_open,
+   _logind_open_async,
    _logind_close,
    _logind_vt_set,
 };
index b1378f5..3b127af 100644 (file)
@@ -58,6 +58,14 @@ elput_manager_disconnect(Elput_Manager *manager)
    EINA_SAFETY_ON_NULL_RETURN(manager);
    EINA_SAFETY_ON_NULL_RETURN(manager->interface);
 
+
+   if (manager->input.thread)
+     {
+        ecore_thread_cancel(manager->input.thread);
+        manager->del = 1;
+        return;
+     }
+
    if (manager->interface->disconnect)
      manager->interface->disconnect(manager);
 }
index c2f4b00..a934bf1 100644 (file)
@@ -71,6 +71,7 @@ typedef struct _Elput_Interface
    Eina_Bool (*connect)(Elput_Manager **manager, const char *seat, unsigned int tty);
    void (*disconnect)(Elput_Manager *manager);
    int (*open)(Elput_Manager *manager, const char *path, int flags);
+   void (*open_async)(Elput_Manager *manager, const char *path, int flags);
    void (*close)(Elput_Manager *manager, int fd);
    Eina_Bool (*vt_set)(Elput_Manager *manager, int vt);
 } Elput_Interface;
@@ -82,6 +83,9 @@ typedef struct _Elput_Input
    Ecore_Fd_Handler *hdlr;
 
    Eina_List *seats;
+   Ecore_Thread *thread;
+   Eldbus_Pending *current_pending;
+   int pipe;
 
    Eina_Bool suspended : 1;
 } Elput_Input;
@@ -224,18 +228,30 @@ struct _Elput_Manager
    char *sid;
    const char *seat;
    unsigned int vt_num;
+   int vt_fd;
    Ecore_Event_Handler *vt_hdlr;
+   uint32_t window;
 
    struct
      {
         char *path;
         Eldbus_Object *obj;
         Eldbus_Connection *conn;
+        Eldbus_Proxy *session;
+        Eldbus_Proxy *manager;
      } dbus;
 
    Elput_Input input;
+   Eina_Bool del : 1;
 };
 
+typedef struct _Elput_Async_Open
+{
+   Elput_Manager *manager;
+   char *path;
+   int flags;
+} Elput_Async_Open;
+
 int _evdev_event_process(struct libinput_event *event);
 Elput_Device *_evdev_device_create(Elput_Seat *seat, struct libinput_device *device);
 void _evdev_device_destroy(Elput_Device *edev);