From be325af372360df0489f371895ceb03afd47f078 Mon Sep 17 00:00:00 2001 From: Chris Michael Date: Tue, 15 Jul 2014 09:28:57 -0400 Subject: [PATCH] ecore-drm: Add ecore_drm_dbus file for dbus functions Signed-off-by: Chris Michael --- src/lib/ecore_drm/ecore_drm_dbus.c | 819 +++++++++++++++++++++++++++++ 1 file changed, 819 insertions(+) create mode 100644 src/lib/ecore_drm/ecore_drm_dbus.c diff --git a/src/lib/ecore_drm/ecore_drm_dbus.c b/src/lib/ecore_drm/ecore_drm_dbus.c new file mode 100644 index 0000000000..19986025fb --- /dev/null +++ b/src/lib/ecore_drm/ecore_drm_dbus.c @@ -0,0 +1,819 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "ecore_drm_private.h" +#include + +static DBusConnection *conn; +static DBusPendingCall *dpending; +static Ecore_Fd_Handler *_dbus_hdlr; +static Ecore_Fd_Handler *_watch_hdlr; +static char *dpath; +static const char *sid; + +static void +_dbus_session_removed(DBusMessage *msg) +{ + const char *n, *o; + dbus_bool_t ret; + + ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &n, + DBUS_TYPE_OBJECT_PATH, &o, DBUS_TYPE_INVALID); + if (!ret) return; + + if (!strcmp(n, sid)) + { + ERR("DBus Session Closed"); +// ecore_main_loop_quit(); + } +} + +static void +_dbus_cb_notify(DBusPendingCall *pending, void *data EINA_UNUSED) +{ + DBusMessage *msg; + DBusMessageIter iter, s; + dbus_bool_t ret; + int type = 0; + + dbus_pending_call_unref(dpending); + dpending = NULL; + + msg = dbus_pending_call_steal_reply(pending); + if (!msg) return; + + type = dbus_message_get_type(msg); + if (type != DBUS_MESSAGE_TYPE_METHOD_RETURN) goto err; + + if ((!dbus_message_iter_init(msg, &iter)) || + (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)) + goto err; + + dbus_message_iter_recurse(&iter, &s); + + if (dbus_message_iter_get_arg_type(&s) != DBUS_TYPE_BOOLEAN) + goto err; + + dbus_message_iter_get_basic(&s, &ret); + if (!ret) + { + /* TODO: emit active signal to compositor ? */ + } + +err: + dbus_message_unref(msg); +} + +static void +_dbus_active_get(void) +{ + DBusPendingCall *pend; + DBusMessage *msg; + dbus_bool_t ret; + const char *iface, *n; + + msg = + dbus_message_new_method_call("org.freedesktop.login1", dpath, + "org.freedesktop.DBus.Properties", "Get"); + if (!msg) return; + + iface = "org.freedesktop.login1.Session"; + n = "Active"; + + ret = dbus_message_append_args(msg, DBUS_TYPE_STRING, &iface, + DBUS_TYPE_STRING, &n, DBUS_TYPE_INVALID); + if (!ret) goto err; + + ret = dbus_connection_send_with_reply(conn, msg, &pend, -1); + if (!ret) goto err; + + ret = dbus_pending_call_set_notify(pend, _dbus_cb_notify, NULL, NULL); + if (!ret) + { + dbus_pending_call_cancel(pend); + dbus_pending_call_unref(pend); + goto err; + } + + if (dpending) + { + dbus_pending_call_cancel(dpending); + dbus_pending_call_unref(dpending); + } + + dpending = pend; + + return; + +err: + dbus_message_unref(msg); +} + +static void +_dbus_property_changed(DBusMessage *msg) +{ + DBusMessageIter iter, s, ent; + const char *iface, *n; + dbus_bool_t ret; + + if ((!dbus_message_iter_init(msg, &iter)) || + (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)) + return; + + dbus_message_iter_get_basic(&iter, &iface); + + if ((!dbus_message_iter_next(&iter)) || + (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)) + return; + + dbus_message_iter_recurse(&iter, &s); + while (dbus_message_iter_get_arg_type(&s) == DBUS_TYPE_DICT_ENTRY) + { + dbus_message_iter_recurse(&s, &ent); + if (dbus_message_iter_get_arg_type(&ent) != DBUS_TYPE_STRING) + return; + + dbus_message_iter_get_basic(&ent, &n); + if (!dbus_message_iter_next(&ent)) return; + + if (!strcmp(n, "Active")) + { + if (dbus_message_iter_get_arg_type(&ent) == DBUS_TYPE_BOOLEAN) + { + dbus_message_iter_get_basic(&ent, &ret); + if (!ret) + { + /* TODO: emit active signal to compositor ? */ + } + return; + } + } + + dbus_message_iter_next(&s); + } + + if ((!dbus_message_iter_next(&iter)) || + (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)) + return; + + dbus_message_iter_recurse(&iter, &s); + while (dbus_message_iter_get_arg_type(&s) == DBUS_TYPE_STRING) + { + dbus_message_iter_get_basic(&s, &n); + if (!strcmp(n, "Active")) + { + _dbus_active_get(); + return; + } + + dbus_message_iter_next(&s); + } +} + +static void +_dbus_device_pause_done(uint32_t major, uint32_t minor) +{ + DBusMessage *msg; + dbus_bool_t ret; + + msg = dbus_message_new_method_call("org.freedesktop.login1", dpath, + "org.freedesktop.login1.Session", + "PauseDeviceComplete"); + if (msg) + { + ret = dbus_message_append_args(msg, DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_INVALID); + if (ret) + dbus_connection_send(conn, msg, NULL); + + dbus_message_unref(msg); + } +} + +static void +_dbus_device_paused(DBusMessage *msg) +{ + dbus_bool_t ret; + const char *type; + uint32_t maj, min; + + ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &maj, + DBUS_TYPE_UINT32, &min, + DBUS_TYPE_STRING, &type, DBUS_TYPE_INVALID); + if (!ret) return; + + if (!strcmp(type, "pause")) + _dbus_device_pause_done(maj, min); + + if (maj == DRM_MAJOR) + { + /* TODO: emit active signal to compositor ? */ + } +} + +static void +_dbus_device_resumed(DBusMessage *msg) +{ + dbus_bool_t ret; + uint32_t maj; + + ret = dbus_message_get_args(msg, NULL, + DBUS_TYPE_UINT32, &maj, DBUS_TYPE_INVALID); + if (!ret) return; + + if (maj == DRM_MAJOR) + { + /* TODO: emit active signal to compositor ? */ + } +} + +static void +_dbus_device_release(uint32_t major, uint32_t minor) +{ + DBusMessage *msg; + + msg = dbus_message_new_method_call("org.freedesktop.login1", dpath, + "org.freedesktop.login1.Session", + "ReleaseDevice"); + if (msg) + { + dbus_bool_t ret; + + ret = dbus_message_append_args(msg, DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_INVALID); + if (ret) dbus_connection_send(conn, msg, NULL); + dbus_message_unref(msg); + } +} + +static int +_dbus_device_take(uint32_t major, uint32_t minor) +{ + DBusMessage *msg, *rep; + DBusError err; + dbus_bool_t p, ret; + int fd = -1; + + msg = dbus_message_new_method_call("org.freedesktop.login1", dpath, + "org.freedesktop.login1.Session", + "TakeDevice"); + if (!msg) return -1; + + ret = dbus_message_append_args(msg, DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, DBUS_TYPE_INVALID); + if (!ret) goto err; + + dbus_error_init(&err); + + rep = + dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); + if (!rep) + { + if (dbus_error_has_name(&err, DBUS_ERROR_UNKNOWN_METHOD)) + ERR("Old Systemd Version detected"); + else if (dbus_error_is_set(&err)) + ERR("DBusError: %s %s", err.name, err.message); + dbus_error_free(&err); + goto err; + } + + ret = dbus_message_get_args(rep, &err, DBUS_TYPE_UNIX_FD, &fd, + DBUS_TYPE_BOOLEAN, &p, DBUS_TYPE_INVALID); + if (!ret) + { + if (dbus_error_is_set(&err)) + ERR("DBusError: %s %s", err.name, err.message); + + dbus_error_free(&err); + goto err_rep; + } + + return fd; + +err_rep: + dbus_message_unref(rep); +err: + dbus_message_unref(msg); + return -1; +} + +static int +_dbus_device_open(const char *path) +{ + struct stat st; + int ret, fl, fd = -1; + /* char name[256] = "unknown"; */ + + if ((ret = stat(path, &st)) < 0) return -1; + if (!S_ISCHR(st.st_mode)) return -1; + + fd = _dbus_device_take(major(st.st_rdev), minor(st.st_rdev)); + if (fd < 0) + { + ERR("Failed to take device: %s", path); + return -1; + } + + if ((fl = fcntl(fd, F_GETFL)) < 0) + { + ERR("Failed to get file flags: %m"); + goto flag_err; + } + + fl = (O_RDWR | O_NONBLOCK); + + if ((ret = fcntl(fd, F_SETFL, fl)) < 0) + { + ERR("Failed to set file flags: %m"); + goto flag_err; + } + + if ((fl = fcntl(fd, F_GETFD)) < 0) + { + ERR("Failed to get file fd: %m"); + goto flag_err; + } + + fl &= ~FD_CLOEXEC; + + if ((ret = fcntl(fd, F_SETFD, fl)) < 0) + { + ERR("Failed to set file fds: %m"); + goto flag_err; + } + + /* if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) */ + /* { */ + /* ERR("Could not get device name: %m"); */ + /* goto flag_err; */ + /* } */ + /* else */ + /* { */ + /* name[sizeof(name) - 1] = '\0'; */ + /* DBG("%s Opened", name); */ + /* } */ + + return fd; + +flag_err: + _dbus_device_release(major(st.st_rdev), minor(st.st_rdev)); + return -1; +} + +static DBusHandlerResult +_dbus_cb_filter(DBusConnection *conn EINA_UNUSED, DBusMessage *msg, void *data EINA_UNUSED) +{ + if (dbus_message_is_signal(msg, DBUS_INTERFACE_LOCAL, "Disconnected")) + { + ERR("DBus Disconnected"); + } + else if (dbus_message_is_signal(msg, "org.freedesktop.login1.Manager", + "SessionRemoved")) + _dbus_session_removed(msg); + else if (dbus_message_is_signal(msg, "org.freedesktop.DBus.Properties", + "PropertiesChanged")) + _dbus_property_changed(msg); + else if (dbus_message_is_signal(msg, "org.freedesktop.login1.Session", + "PauseDevice")) + _dbus_device_paused(msg); + else if (dbus_message_is_signal(msg, "org.freedesktop.login1.Sesion", + "ResumeDevice")) + _dbus_device_resumed(msg); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static Eina_Bool +_dbus_cb_dispatch(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED) +{ + DBusConnection *conn; + int ret = 0; + + if (!(conn = data)) return ECORE_CALLBACK_CANCEL; + + do + { + ret = dbus_connection_dispatch(conn); + switch (ret) + { + case DBUS_DISPATCH_COMPLETE: + ret = 0; + break; + case DBUS_DISPATCH_DATA_REMAINS: + ret = -EAGAIN; + break; + case DBUS_DISPATCH_NEED_MEMORY: + ret = -ENOMEM; + break; + default: + ret = -EIO; + break; + } + } while (ret == -EAGAIN); + + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_dbus_cb_watch(void *data, Ecore_Fd_Handler *hdlr) +{ + DBusWatch *watch; + uint32_t flags = 0; + + if (!(watch = data)) return ECORE_CALLBACK_RENEW; + + if (dbus_watch_get_enabled(watch)) + { + if (ecore_main_fd_handler_active_get(hdlr, ECORE_FD_READ)) + flags |= DBUS_WATCH_READABLE; + if (ecore_main_fd_handler_active_get(hdlr, ECORE_FD_WRITE)) + flags |= DBUS_WATCH_WRITABLE; + if (ecore_main_fd_handler_active_get(hdlr, ECORE_FD_ERROR)) + flags |= DBUS_WATCH_ERROR; + + dbus_watch_handle(watch, flags); + } + + return ECORE_CALLBACK_RENEW; +} + +static dbus_bool_t +_dbus_watch_add(DBusWatch *watch, void *data EINA_UNUSED) +{ + uint32_t msk = 0, flags = 0; + int fd = -1; + + msk |= ECORE_FD_ERROR; + if (dbus_watch_get_enabled(watch)) + { + flags = dbus_watch_get_flags(watch); + if (flags & DBUS_WATCH_READABLE) + msk |= ECORE_FD_READ; + if (flags & DBUS_WATCH_WRITABLE) + msk |= ECORE_FD_WRITE; + } + + fd = dbus_watch_get_unix_fd(watch); + + _watch_hdlr = + ecore_main_fd_handler_add(fd, msk, _dbus_cb_watch, watch, NULL, NULL); + + dbus_watch_set_data(watch, _watch_hdlr, NULL); + + return TRUE; +} + +static void +_dbus_watch_del(DBusWatch *watch, void *data EINA_UNUSED) +{ + Ecore_Fd_Handler *hdlr; + + if (!(hdlr = dbus_watch_get_data(watch))) return; + ecore_main_fd_handler_del(hdlr); + _watch_hdlr = NULL; +} + +static void +_dbus_watch_toggle(DBusWatch *watch, void *data EINA_UNUSED) +{ + uint32_t flags = 0, mask = 0; + Ecore_Fd_Handler *hdlr; + + if (!(hdlr = dbus_watch_get_data(watch))) return; + + if (dbus_watch_get_enabled(watch)) + { + flags = dbus_watch_get_flags(watch); + if (flags & DBUS_WATCH_READABLE) + mask |= ECORE_FD_READ; + if (flags & DBUS_WATCH_WRITABLE) + mask |= ECORE_FD_WRITE; + } + + ecore_main_fd_handler_active_set(hdlr, mask); +} + +static Eina_Bool +_dbus_cb_timeout(void *data) +{ + DBusTimeout *timeout; + + if (!(timeout = data)) return ECORE_CALLBACK_RENEW; + + if (dbus_timeout_get_enabled(timeout)) + dbus_timeout_handle(timeout); + + return ECORE_CALLBACK_RENEW; +} + +static dbus_bool_t +_dbus_timeout_add(DBusTimeout *timeout, void *data EINA_UNUSED) +{ + if (dbus_timeout_get_enabled(timeout)) + { + Ecore_Timer *tmr = NULL; + int tme; + + tme = dbus_timeout_get_interval(timeout); + if (!(tmr = ecore_timer_loop_add(tme, _dbus_cb_timeout, timeout))) + return EINA_FALSE; + + dbus_timeout_set_data(timeout, tmr, NULL); + } + + return EINA_TRUE; +} + +static void +_dbus_timeout_del(DBusTimeout *timeout, void *data EINA_UNUSED) +{ + Ecore_Timer *tmr = NULL; + + if (!(tmr = dbus_timeout_get_data(timeout))) return; + ecore_timer_del(tmr); +} + +static void +_dbus_timeout_toggle(DBusTimeout *timeout, void *data EINA_UNUSED) +{ + Ecore_Timer *tmr = NULL; + + if (!(tmr = dbus_timeout_get_data(timeout))) return; + + if (dbus_timeout_get_enabled(timeout)) + ecore_timer_thaw(tmr); + else + ecore_timer_freeze(tmr); +} + +static Eina_Bool +_dbus_match_add(DBusConnection *conn, const char *format, ...) +{ + DBusError err; + va_list lst; + char *tmp; + int ret; + + va_start(lst, format); + ret = vasprintf(&tmp, format, lst); + va_end(lst); + + if (ret < 0) return EINA_FALSE; + + dbus_error_init(&err); + dbus_bus_add_match(conn, tmp, &err); + free(tmp); + + if (dbus_error_is_set(&err)) + { + dbus_error_free(&err); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static Eina_Bool +_dbus_signal_add(DBusConnection *conn, const char *sender, const char *iface, const char *mem, const char *path) +{ + return _dbus_match_add(conn, "type='signal',sender='%s'," + "interface='%s',member='%s',path='%s'", + sender, iface, mem, path); +} + +static Eina_Bool +_dbus_setup(void) +{ + int ret = 0; + dbus_bool_t res; + + ret = asprintf(&dpath, "/org/freedesktop/login1/session/%s", sid); + if (ret < 0) return EINA_FALSE; + + res = dbus_connection_add_filter(conn, _dbus_cb_filter, NULL, NULL); + if (!res) + { + ERR("Could not setup dbus filter: %m\n"); + goto err; + } + + res = _dbus_signal_add(conn, "org.freedesktop.login1", + "org.freedesktop.login1.Manager", + "SessionRemoved", "/org/freedesktop/login1"); + if (!res) goto err; + + res = _dbus_signal_add(conn, "org.freedesktop.login1", + "org.freedesktop.login1.Session", + "PauseDevice", dpath); + if (!res) goto err; + + res = _dbus_signal_add(conn, "org.freedesktop.login1", + "org.freedesktop.login1.Session", + "ResumeDevice", dpath); + if (!res) goto err; + + res = _dbus_signal_add(conn, "org.freedesktop.login1", + "org.freedesktop.DBus.Properties", + "PropertiesChanged", dpath); + if (!res) goto err; + + return EINA_TRUE; + +err: + free(dpath); + return EINA_FALSE; +} + +static Eina_Bool +_dbus_control_take(void) +{ + DBusError err; + DBusMessage *msg, *rep; + dbus_bool_t f = EINA_FALSE; + + dbus_error_init(&err); + + msg = + dbus_message_new_method_call("org.freedesktop.login1", dpath, + "org.freedesktop.login1.Session", + "TakeControl"); + if (!msg) goto err; + + if (!dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &f, DBUS_TYPE_INVALID)) + goto msg_err; + + rep = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); + if (!rep) + { + if (dbus_error_has_name(&err, DBUS_ERROR_UNKNOWN_METHOD)) + ERR("Old Systemd Version detected\n"); + goto msg_err; + } + + dbus_message_unref(rep); + dbus_message_unref(msg); + dbus_error_free(&err); + + return EINA_TRUE; + +msg_err: + dbus_message_unref(msg); +err: + if (dbus_error_is_set(&err)) + ERR("DBusError: %s %s", err.name, err.message); + dbus_error_free(&err); + return EINA_FALSE; +} + +static void +_dbus_control_release(void) +{ + DBusMessage *msg; + + msg = + dbus_message_new_method_call("org.freedesktop.login1", dpath, + "org.freedesktop.login1.Session", + "ReleaseControl"); + if (msg) + { + dbus_connection_send(conn, msg, NULL); + dbus_message_unref(msg); + } +} + +static Eina_Bool +_dbus_bind(DBusConnection *conn) +{ + int fd = -1; + + if ((fd = eventfd(0, EFD_CLOEXEC)) < 0) + { + ERR("Could not create eventfd: %m"); + return EINA_FALSE; + } + + _dbus_hdlr = + ecore_main_fd_handler_add(fd, (ECORE_FD_READ | ECORE_FD_WRITE), + _dbus_cb_dispatch, conn, NULL, NULL); + if (!_dbus_hdlr) + { + ERR("Failed to create ecore fd handler"); + goto hdlr_err; + } + + if (!dbus_connection_set_watch_functions(conn, _dbus_watch_add, + _dbus_watch_del, + _dbus_watch_toggle, NULL, NULL)) + { + ERR("Failed to set dbus watch functions: %m"); + goto watch_err; + } + + if (!dbus_connection_set_timeout_functions(conn, _dbus_timeout_add, + _dbus_timeout_del, + _dbus_timeout_toggle, + NULL, NULL)) + { + ERR("Failed to set dbus timeout functions: %m"); + goto timeout_err; + } + + dbus_connection_ref(conn); + + return EINA_TRUE; + +timeout_err: + dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL); +watch_err: + ecore_main_fd_handler_del(_dbus_hdlr); + _dbus_hdlr = NULL; +hdlr_err: + close(fd); + return EINA_FALSE; +} + +static DBusConnection * +_dbus_open(void) +{ + DBusConnection *conn; + + dbus_connection_set_change_sigpipe(EINA_FALSE); + + conn = dbus_bus_get_private(DBUS_BUS_SYSTEM, NULL); + if (!conn) + { + ERR("Failed to get dbus connection: %m"); + goto conn_err; + } + + dbus_connection_set_exit_on_disconnect(conn, EINA_FALSE); + + if (!_dbus_bind(conn)) + { + ERR("Failed to bind dbus: %m"); + goto bind_err; + } + + return conn; + +bind_err: + dbus_connection_close(conn); + dbus_connection_unref(conn); +conn_err: + return NULL; +} + +static void +_dbus_close(void) +{ + dbus_connection_set_timeout_functions(conn, NULL, NULL, NULL, NULL, NULL); + dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL); + + if (_dbus_hdlr) ecore_main_fd_handler_del(_dbus_hdlr); + _dbus_hdlr = NULL; + + dbus_connection_close(conn); + dbus_connection_unref(conn); +} + +Eina_Bool +_ecore_drm_dbus_init(const char *session) +{ + if (conn) return EINA_TRUE; + + /* try to init dbus */ + if (!(conn = _dbus_open())) return EINA_FALSE; + + sid = eina_stringshare_add(session); + + /* try to setup signal handlers */ + if (!_dbus_setup()) goto setup_err; + + /* try to take control of the session */ + if (!_dbus_control_take()) goto setup_err; + + return EINA_TRUE; + +setup_err: + _dbus_close(); + eina_stringshare_del(sid); + return EINA_FALSE; +} + +void +_ecore_drm_dbus_shutdown(void) +{ + _dbus_control_release(); + _dbus_close(); + eina_stringshare_del(sid); + free(dpath); +} + +int +_ecore_drm_dbus_device_open(const char *device) +{ + return _dbus_device_open(device); +}