elput: define and implement an async device opening interface for libinput

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
This commit is contained in:
Mike Blumenkrantz 2016-05-24 16:11:20 -04:00
parent 33a5d44dcf
commit 5f088b026d
5 changed files with 309 additions and 155 deletions

View 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

View 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;
Elput_Manager *em = data;
int ret = -1;
Elput_Async_Open *ao;
int p[2];
em = data;
return elput_manager_open(em, path, flags);
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;
Elput_Manager *manager = data;
EINA_SAFETY_ON_NULL_RETURN_VAL(manager, EINA_FALSE);
manager->input.thread = NULL;
if (manager->input.current_pending)
{
eldbus_pending_cancel(manager->input.current_pending);
if (manager->input.pipe >= 0)
close(manager->input.pipe);
}
if (manager->del)
elput_manager_disconnect(manager);
}
memset(&manager->input, 0, sizeof(Elput_Input));
static void
_elput_input_init_end(void *data, Ecore_Thread *eth EINA_UNUSED)
{
Elput_Manager *manager = data;
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)
{
_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;
}
}
static void
_elput_input_init_notify(void *data EINA_UNUSED, Ecore_Thread *eth EINA_UNUSED, void *msg_data)
{
Elput_Async_Open *ao = msg_data;
ao->manager->interface->open_async(ao->manager, ao->path, ao->flags);
free(ao->path);
free(ao);
}
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, eeze_udev_get());
libinput_udev_create_context(&_input_interface, manager, udev);
if (!manager->input.lib)
{
ERR("libinput could not create udev context");
goto udev_err;
return;
}
udev_unref(udev);
/* if not seat name is passed in, just use default seat name */
if (!seat) seat = "seat0";
if (libinput_udev_assign_seat(manager->input.lib, seat) != 0)
if (libinput_udev_assign_seat(manager->input.lib, manager->seat))
{
ERR("libinput could not assign udev seat");
goto seat_err;
libinput_unref(manager->input.lib);
manager->input.lib = NULL;
}
}
_process_events(&manager->input);
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);
fd = libinput_get_fd(manager->input.lib);
manager->input.hdlr =
ecore_main_fd_handler_add(fd, ECORE_FD_READ, _cb_input_dispatch,
&manager->input, NULL, NULL);
if (!manager->input.hdlr)
{
ERR("Could not create input fd handler");
goto hdlr_err;
}
return 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

View 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)
{
ERR("Could not get proxy for session");
return;
}
msg = eldbus_proxy_method_call_new(proxy, "ReleaseControl");
msg = eldbus_proxy_method_call_new(em->dbus.session, "ReleaseControl");
if (!msg)
{
ERR("Could not create method call for proxy");
goto end;
return;
}
eldbus_proxy_send(proxy, msg, NULL, NULL, -1);
eldbus_proxy_send(em->dbus.session, msg, NULL, NULL, -1);
}
end:
eldbus_proxy_unref(proxy);
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");
return;
}
eldbus_message_arguments_append(msg, "uu", major, minor);
eldbus_proxy_send(em->dbus.session, msg, NULL, NULL, -1);
}
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)
{
Eina_Bool p = EINA_FALSE;
const char *errname, *errmsg;
int ret, fd = -1;
int fl, flags;
Elput_Manager *em = data;
if (em->input.current_pending == pending)
em->input.current_pending = NULL;
if (eldbus_message_error_get(msg, &errname, &errmsg))
{
ERR("Eldbus Message Error: %s %s", errname, errmsg);
goto err;
}
if (!eldbus_message_arguments_get(msg, "hb", &fd, &p))
ERR("Could not get UNIX_FD from dbus message");
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:
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_take_async(Elput_Manager *em, int flags, uint32_t major, uint32_t minor)
{
Eldbus_Message *msg;
intptr_t fd = -1;
msg = eldbus_proxy_method_call_new(em->dbus.session, "TakeDevice");
if (!msg)
{
ERR("Could not create method call for proxy");
_logind_pipe_write_fd(em, fd);
return;
}
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_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");
msg = eldbus_proxy_method_call_new(em->dbus.session, "TakeDevice");
if (!msg)
{
ERR("Could not create method call for proxy");
goto err;
return -1;
}
eldbus_message_arguments_append(msg, "uu", major, minor);
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 err;
return -1;
}
if (!eldbus_message_arguments_get(reply, "hb", &fd, &p))
ERR("Could not get UNIX_FD from dbus message");
eldbus_message_unref(reply);
err:
eldbus_proxy_unref(proxy);
return fd;
}
static void
_logind_device_release(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, "ReleaseDevice");
if (!msg)
{
ERR("Could not create method call for proxy");
goto end;
}
eldbus_message_arguments_append(msg, "uu", major, minor);
eldbus_proxy_send(proxy, msg, NULL, NULL, -1);
end:
eldbus_proxy_unref(proxy);
}
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,
};

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

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