ecore-drm: Add ecore_drm_dbus file for dbus functions

Signed-off-by: Chris Michael <cp.michael@samsung.com>
This commit is contained in:
Chris Michael 2014-07-15 09:28:57 -04:00
parent 61070f779f
commit be325af372
1 changed files with 819 additions and 0 deletions

View File

@ -0,0 +1,819 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "ecore_drm_private.h"
#include <sys/eventfd.h>
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);
}