543 lines
16 KiB
C
543 lines
16 KiB
C
#include "edbus_private.h"
|
|
#include "edbus_private_types.h"
|
|
|
|
/* TODO: mempool of EDBus_Proxy, Edbus_Proxy_Context_Event_Cb and
|
|
* EDBus_Proxy_Context_Event
|
|
*/
|
|
|
|
typedef struct _EDBus_Proxy_Context_Event_Cb
|
|
{
|
|
EINA_INLIST;
|
|
EDBus_Proxy_Event_Cb cb;
|
|
const void *cb_data;
|
|
Eina_Bool deleted : 1;
|
|
} EDBus_Proxy_Context_Event_Cb;
|
|
|
|
typedef struct _EDBus_Proxy_Context_Event
|
|
{
|
|
Eina_Inlist *list;
|
|
int walking;
|
|
Eina_List *to_delete;
|
|
} EDBus_Proxy_Context_Event;
|
|
|
|
struct _EDBus_Proxy
|
|
{
|
|
EINA_MAGIC;
|
|
int refcount;
|
|
EDBus_Object *obj;
|
|
const char *interface;
|
|
Eina_Inlist *pendings;
|
|
Eina_List *handlers;
|
|
Eina_Inlist *cbs_free;
|
|
EDBus_Proxy_Context_Event event_handlers[EDBUS_PROXY_EVENT_LAST];
|
|
};
|
|
|
|
#define EDBUS_PROXY_CHECK(proxy) \
|
|
do \
|
|
{ \
|
|
EINA_SAFETY_ON_NULL_RETURN(proxy); \
|
|
if (!EINA_MAGIC_CHECK(proxy, EDBUS_PROXY_MAGIC)) \
|
|
{ \
|
|
EINA_MAGIC_FAIL(proxy, EDBUS_PROXY_MAGIC); \
|
|
return; \
|
|
} \
|
|
EINA_SAFETY_ON_TRUE_RETURN(proxy->refcount <= 0); \
|
|
} \
|
|
while (0)
|
|
|
|
#define EDBUS_PROXY_CHECK_RETVAL(proxy, retval) \
|
|
do \
|
|
{ \
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(proxy, retval); \
|
|
if (!EINA_MAGIC_CHECK(proxy, EDBUS_PROXY_MAGIC)) \
|
|
{ \
|
|
EINA_MAGIC_FAIL(proxy, EDBUS_PROXY_MAGIC); \
|
|
return retval; \
|
|
} \
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(proxy->refcount <= 0, retval); \
|
|
} \
|
|
while (0)
|
|
|
|
#define EDBUS_PROXY_CHECK_GOTO(proxy, label) \
|
|
do \
|
|
{ \
|
|
EINA_SAFETY_ON_NULL_GOTO(proxy, label); \
|
|
if (!EINA_MAGIC_CHECK(proxy, EDBUS_PROXY_MAGIC)) \
|
|
{ \
|
|
EINA_MAGIC_FAIL(proxy, EDBUS_PROXY_MAGIC); \
|
|
goto label; \
|
|
} \
|
|
EINA_SAFETY_ON_TRUE_GOTO(proxy->refcount <= 0, label); \
|
|
} \
|
|
while (0)
|
|
|
|
Eina_Bool
|
|
edbus_proxy_init(void)
|
|
{
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
void
|
|
edbus_proxy_shutdown(void)
|
|
{
|
|
}
|
|
|
|
static void _edbus_proxy_event_callback_call(EDBus_Proxy *proxy, EDBus_Proxy_Event_Type type, const void *event_info);
|
|
static void _edbus_proxy_context_event_cb_del(EDBus_Proxy_Context_Event *ce, EDBus_Proxy_Context_Event_Cb *ctx);
|
|
|
|
static void
|
|
_edbus_proxy_call_del(EDBus_Proxy *proxy)
|
|
{
|
|
EDBus_Proxy_Context_Event *ce;
|
|
|
|
_edbus_proxy_event_callback_call(proxy, EDBUS_PROXY_EVENT_DEL, NULL);
|
|
|
|
/* clear all del callbacks so we don't call them twice at
|
|
* _edbus_proxy_clear()
|
|
*/
|
|
ce = proxy->event_handlers + EDBUS_PROXY_EVENT_DEL;
|
|
while (ce->list)
|
|
{
|
|
EDBus_Proxy_Context_Event_Cb *ctx;
|
|
|
|
ctx = EINA_INLIST_CONTAINER_GET(ce->list,
|
|
EDBus_Proxy_Context_Event_Cb);
|
|
_edbus_proxy_context_event_cb_del(ce, ctx);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_edbus_proxy_clear(EDBus_Proxy *proxy)
|
|
{
|
|
EDBus_Signal_Handler *h;
|
|
EDBus_Pending *p;
|
|
Eina_List *iter, *iter_next;
|
|
Eina_Inlist *in_l;
|
|
DBG("proxy=%p, refcount=%d, interface=%s, obj=%p",
|
|
proxy, proxy->refcount, proxy->interface, proxy->obj);
|
|
proxy->refcount = 1;
|
|
edbus_object_proxy_del(proxy->obj, proxy, proxy->interface);
|
|
_edbus_proxy_call_del(proxy);
|
|
|
|
EINA_LIST_FOREACH_SAFE(proxy->handlers, iter, iter_next, h)
|
|
{
|
|
DBG("proxy=%p delete owned signal handler %p %s",
|
|
proxy, h, edbus_signal_handler_match_get(h));
|
|
edbus_signal_handler_del(h);
|
|
}
|
|
|
|
EINA_INLIST_FOREACH_SAFE(proxy->pendings, in_l, p)
|
|
{
|
|
DBG("proxy=%p delete owned pending call=%p dest=%s path=%s %s.%s()",
|
|
proxy, p,
|
|
edbus_pending_destination_get(p),
|
|
edbus_pending_path_get(p),
|
|
edbus_pending_interface_get(p),
|
|
edbus_pending_method_get(p));
|
|
edbus_pending_cancel(p);
|
|
}
|
|
|
|
edbus_cbs_free_dispatch(&(proxy->cbs_free), proxy);
|
|
proxy->refcount = 0;
|
|
}
|
|
|
|
static void
|
|
_edbus_proxy_free(EDBus_Proxy *proxy)
|
|
{
|
|
unsigned int i;
|
|
if (proxy->handlers)
|
|
{
|
|
EDBus_Signal_Handler *h;
|
|
CRITICAL("Proxy %p released with live signal handlers!", proxy);
|
|
EINA_LIST_FREE (proxy->handlers, h)
|
|
ERR("proxy=%p alive handler=%p %s", proxy, h,
|
|
edbus_signal_handler_match_get(h));
|
|
}
|
|
|
|
if (proxy->pendings)
|
|
CRITICAL("Proxy %p released with live pending calls!", proxy);
|
|
|
|
for (i = 0; i < EDBUS_PROXY_EVENT_LAST; i++)
|
|
{
|
|
EDBus_Proxy_Context_Event *ce = proxy->event_handlers + i;
|
|
while (ce->list)
|
|
{
|
|
EDBus_Proxy_Context_Event_Cb *ctx;
|
|
ctx = EINA_INLIST_CONTAINER_GET(ce->list,
|
|
EDBus_Proxy_Context_Event_Cb);
|
|
_edbus_proxy_context_event_cb_del(ce, ctx);
|
|
}
|
|
eina_list_free(ce->to_delete);
|
|
}
|
|
|
|
eina_stringshare_del(proxy->interface);
|
|
EINA_MAGIC_SET(proxy, EINA_MAGIC_NONE);
|
|
free(proxy);
|
|
}
|
|
|
|
static void
|
|
_on_object_free(void *data, const void *dead_pointer)
|
|
{
|
|
EDBus_Proxy *proxy = data;
|
|
EDBUS_PROXY_CHECK(proxy);
|
|
DBG("proxy=%p, refcount=%d, interface=%s, obj=%p",
|
|
proxy, proxy->refcount, proxy->interface, proxy->obj);
|
|
_edbus_proxy_clear(proxy);
|
|
_edbus_proxy_free(proxy);
|
|
}
|
|
|
|
EAPI EDBus_Proxy *
|
|
edbus_proxy_get(EDBus_Object *obj, const char *interface)
|
|
{
|
|
EDBus_Proxy *proxy;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(interface, NULL);
|
|
|
|
proxy = edbus_object_proxy_get(obj, interface);
|
|
if (proxy) return proxy;
|
|
|
|
proxy = calloc(1, sizeof(EDBus_Proxy));
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(proxy, NULL);
|
|
|
|
proxy->refcount = 1;
|
|
proxy->obj = obj;
|
|
proxy->interface = eina_stringshare_add(interface);
|
|
EINA_MAGIC_SET(proxy, EDBUS_PROXY_MAGIC);
|
|
if (!edbus_object_proxy_add(obj, proxy))
|
|
goto cleanup;
|
|
edbus_object_cb_free_add(obj, _on_object_free, proxy);
|
|
|
|
return proxy;
|
|
|
|
cleanup:
|
|
eina_stringshare_del(proxy->interface);
|
|
free(proxy);
|
|
return NULL;
|
|
}
|
|
|
|
static void _on_signal_handler_free(void *data, const void *dead_pointer);
|
|
|
|
static void
|
|
_edbus_proxy_unref(EDBus_Proxy *proxy)
|
|
{
|
|
proxy->refcount--;
|
|
if (proxy->refcount > 0) return;
|
|
|
|
edbus_object_cb_free_del(proxy->obj, _on_object_free, proxy);
|
|
_edbus_proxy_clear(proxy);
|
|
_edbus_proxy_free(proxy);
|
|
}
|
|
|
|
EAPI EDBus_Proxy *
|
|
edbus_proxy_ref(EDBus_Proxy *proxy)
|
|
{
|
|
EDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
|
|
DBG("proxy=%p, pre-refcount=%d, interface=%s, obj=%p",
|
|
proxy, proxy->refcount, proxy->interface, proxy->obj);
|
|
proxy->refcount++;
|
|
return proxy;
|
|
}
|
|
|
|
EAPI void
|
|
edbus_proxy_unref(EDBus_Proxy *proxy)
|
|
{
|
|
EDBUS_PROXY_CHECK(proxy);
|
|
DBG("proxy=%p, pre-refcount=%d, interface=%s, obj=%p",
|
|
proxy, proxy->refcount, proxy->interface, proxy->obj);
|
|
_edbus_proxy_unref(proxy);
|
|
}
|
|
|
|
EAPI void
|
|
edbus_proxy_cb_free_add(EDBus_Proxy *proxy, EDBus_Free_Cb cb, const void *data)
|
|
{
|
|
EDBUS_PROXY_CHECK(proxy);
|
|
EINA_SAFETY_ON_NULL_RETURN(cb);
|
|
proxy->cbs_free = edbus_cbs_free_add(proxy->cbs_free, cb, data);
|
|
}
|
|
|
|
EAPI void
|
|
edbus_proxy_cb_free_del(EDBus_Proxy *proxy, EDBus_Free_Cb cb, const void *data)
|
|
{
|
|
EDBUS_PROXY_CHECK(proxy);
|
|
EINA_SAFETY_ON_NULL_RETURN(cb);
|
|
proxy->cbs_free = edbus_cbs_free_del(proxy->cbs_free, cb, data);
|
|
}
|
|
|
|
EAPI void
|
|
edbus_proxy_event_callback_add(EDBus_Proxy *proxy, EDBus_Proxy_Event_Type type, EDBus_Proxy_Event_Cb cb, const void *cb_data)
|
|
{
|
|
EDBus_Proxy_Context_Event *ce;
|
|
EDBus_Proxy_Context_Event_Cb *ctx;
|
|
|
|
EDBUS_PROXY_CHECK(proxy);
|
|
EINA_SAFETY_ON_NULL_RETURN(cb);
|
|
EINA_SAFETY_ON_TRUE_RETURN(type >= EDBUS_PROXY_EVENT_LAST);
|
|
|
|
ce = proxy->event_handlers + type;
|
|
|
|
ctx = calloc(1, sizeof(EDBus_Proxy_Context_Event_Cb));
|
|
EINA_SAFETY_ON_NULL_RETURN(ctx);
|
|
ctx->cb = cb;
|
|
ctx->cb_data = cb_data;
|
|
|
|
ce->list = eina_inlist_append(ce->list, EINA_INLIST_GET(ctx));
|
|
}
|
|
|
|
static void
|
|
_edbus_proxy_context_event_cb_del(EDBus_Proxy_Context_Event *ce, EDBus_Proxy_Context_Event_Cb *ctx)
|
|
{
|
|
ce->list = eina_inlist_remove(ce->list, EINA_INLIST_GET(ctx));
|
|
free(ctx);
|
|
}
|
|
|
|
EAPI void
|
|
edbus_proxy_event_callback_del(EDBus_Proxy *proxy, EDBus_Proxy_Event_Type type, EDBus_Proxy_Event_Cb cb, const void *cb_data)
|
|
{
|
|
EDBus_Proxy_Context_Event *ce;
|
|
EDBus_Proxy_Context_Event_Cb *iter, *found = NULL;
|
|
|
|
EDBUS_PROXY_CHECK(proxy);
|
|
EINA_SAFETY_ON_NULL_RETURN(cb);
|
|
EINA_SAFETY_ON_TRUE_RETURN(type >= EDBUS_PROXY_EVENT_LAST);
|
|
|
|
ce = proxy->event_handlers + type;
|
|
|
|
EINA_INLIST_FOREACH (ce->list, iter)
|
|
{
|
|
if (cb != iter->cb) continue;
|
|
if ((cb_data) && (cb_data != iter->cb_data)) continue;
|
|
|
|
found = iter;
|
|
break;
|
|
}
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(found);
|
|
EINA_SAFETY_ON_TRUE_RETURN(found->deleted);
|
|
|
|
if (ce->walking)
|
|
{
|
|
found->deleted = EINA_TRUE;
|
|
ce->to_delete = eina_list_append(ce->to_delete, found);
|
|
return;
|
|
}
|
|
|
|
_edbus_proxy_context_event_cb_del(ce, found);
|
|
}
|
|
|
|
static void
|
|
_edbus_proxy_event_callback_call(EDBus_Proxy *proxy, EDBus_Proxy_Event_Type type, const void *event_info)
|
|
{
|
|
EDBus_Proxy_Context_Event *ce;
|
|
EDBus_Proxy_Context_Event_Cb *iter;
|
|
|
|
ce = proxy->event_handlers + type;
|
|
|
|
ce->walking++;
|
|
EINA_INLIST_FOREACH (ce->list, iter)
|
|
{
|
|
if (iter->deleted) continue;
|
|
iter->cb((void *)iter->cb_data, proxy, (void *)event_info);
|
|
}
|
|
ce->walking--;
|
|
if (ce->walking > 0) return;
|
|
|
|
EINA_LIST_FREE (ce->to_delete, iter)
|
|
_edbus_proxy_context_event_cb_del(ce, iter);
|
|
}
|
|
|
|
EAPI EDBus_Object *
|
|
edbus_proxy_object_get(const EDBus_Proxy *proxy)
|
|
{
|
|
EDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
|
|
return proxy->obj;
|
|
}
|
|
|
|
EAPI const char *
|
|
edbus_proxy_interface_get(const EDBus_Proxy *proxy)
|
|
{
|
|
EDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
|
|
return proxy->interface;
|
|
}
|
|
|
|
static void
|
|
_on_pending_free(void *data, const void *dead_pointer)
|
|
{
|
|
EDBus_Proxy *proxy = data;
|
|
EDBus_Pending *pending = (EDBus_Pending *)dead_pointer;
|
|
EDBUS_PROXY_CHECK(proxy);
|
|
proxy->pendings = eina_inlist_remove(proxy->pendings,
|
|
EINA_INLIST_GET(pending));
|
|
}
|
|
|
|
static EDBus_Pending *
|
|
_edbus_proxy_send(EDBus_Proxy *proxy, EDBus_Message *msg, EDBus_Message_Cb cb, const void *cb_data, double timeout)
|
|
{
|
|
EDBus_Pending *pending;
|
|
|
|
pending = _edbus_connection_send(proxy->obj->conn, msg, cb, cb_data, timeout);
|
|
if (!cb) return NULL;
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(pending, NULL);
|
|
|
|
edbus_pending_cb_free_add(pending, _on_pending_free, proxy);
|
|
proxy->pendings = eina_inlist_append(proxy->pendings,
|
|
EINA_INLIST_GET(pending));
|
|
|
|
return pending;
|
|
}
|
|
|
|
EAPI EDBus_Pending *
|
|
edbus_proxy_send(EDBus_Proxy *proxy, EDBus_Message *msg, EDBus_Message_Cb cb, const void *cb_data, double timeout)
|
|
{
|
|
EDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(msg, NULL);
|
|
|
|
return _edbus_proxy_send(proxy, msg, cb, cb_data, timeout);
|
|
}
|
|
|
|
EAPI EDBus_Message *
|
|
edbus_proxy_method_call_new(EDBus_Proxy *proxy, const char *member)
|
|
{
|
|
EDBus_Message *msg;
|
|
EDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
|
|
|
|
msg = edbus_message_method_call_new(
|
|
edbus_object_bus_name_get(proxy->obj),
|
|
edbus_object_bus_path_get(proxy->obj),
|
|
proxy->interface, member);
|
|
return msg;
|
|
}
|
|
|
|
static EDBus_Pending *
|
|
_edbus_proxy_vcall(EDBus_Proxy *proxy, const char *member, EDBus_Message_Cb cb, const void *cb_data, double timeout, const char *signature, va_list ap)
|
|
{
|
|
EDBus_Pending *pending;
|
|
EDBus_Message *msg = edbus_proxy_method_call_new(proxy, member);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(msg, NULL);
|
|
|
|
if (!edbus_message_arguments_vset(msg, signature, ap))
|
|
{
|
|
edbus_message_unref(msg);
|
|
ERR("Error setting arguments");
|
|
return NULL;
|
|
}
|
|
|
|
pending = _edbus_proxy_send(proxy, msg, cb, cb_data, timeout);
|
|
edbus_message_unref(msg);
|
|
return pending;
|
|
}
|
|
|
|
EAPI EDBus_Pending *
|
|
edbus_proxy_call(EDBus_Proxy *proxy, const char *member, EDBus_Message_Cb cb, const void *cb_data, double timeout, const char *signature, ...)
|
|
{
|
|
EDBus_Pending *pending;
|
|
va_list ap;
|
|
|
|
EDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(member, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(signature, NULL);
|
|
|
|
va_start(ap, signature);
|
|
pending = _edbus_proxy_vcall(proxy, member, cb, cb_data, timeout,
|
|
signature, ap);
|
|
va_end(ap);
|
|
|
|
return pending;
|
|
}
|
|
|
|
EAPI EDBus_Pending *
|
|
edbus_proxy_vcall(EDBus_Proxy *proxy, const char *member, EDBus_Message_Cb cb, const void *cb_data, double timeout, const char *signature, va_list ap)
|
|
{
|
|
EDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(member, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(signature, NULL);
|
|
|
|
return _edbus_proxy_vcall(proxy, member, cb, cb_data, timeout,
|
|
signature, ap);
|
|
}
|
|
|
|
static void
|
|
_on_signal_handler_free(void *data, const void *dead_pointer)
|
|
{
|
|
EDBus_Proxy *proxy = data;
|
|
EDBUS_PROXY_CHECK(proxy);
|
|
proxy->handlers = eina_list_remove(proxy->handlers, dead_pointer);
|
|
}
|
|
|
|
EAPI EDBus_Signal_Handler *
|
|
edbus_proxy_signal_handler_add(EDBus_Proxy *proxy, const char *member, EDBus_Signal_Cb cb, const void *cb_data)
|
|
{
|
|
EDBus_Signal_Handler *handler;
|
|
const char *name, *path;
|
|
|
|
EDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(cb, NULL);
|
|
|
|
name = edbus_object_bus_name_get(proxy->obj);
|
|
path = edbus_object_bus_path_get(proxy->obj);
|
|
|
|
handler = edbus_signal_handler_add(proxy->obj->conn, name, path,
|
|
proxy->interface, member, cb, cb_data);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(handler, NULL);
|
|
|
|
edbus_signal_handler_cb_free_add(handler, _on_signal_handler_free, proxy);
|
|
proxy->handlers = eina_list_append(proxy->handlers, handler);
|
|
|
|
return handler;
|
|
}
|
|
|
|
static EDBus_Proxy *
|
|
get_properties_proxy(EDBus_Proxy *proxy)
|
|
{
|
|
return edbus_proxy_get(proxy->obj, EDBUS_FDO_INTERFACE_PROPERTIES);
|
|
}
|
|
|
|
EAPI EDBus_Pending *
|
|
edbus_proxy_property_get(EDBus_Proxy *proxy, const char *name, EDBus_Message_Cb cb, const void *data)
|
|
{
|
|
EDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
|
|
return edbus_proxy_call(get_properties_proxy(proxy), "Get", cb, data, -1,
|
|
"ss", proxy->interface, name);
|
|
}
|
|
|
|
EAPI EDBus_Pending *
|
|
edbus_proxy_property_set(EDBus_Proxy *proxy, const char *name, char type, const void *value, EDBus_Message_Cb cb, const void *data)
|
|
{
|
|
EDBus_Message *msg;
|
|
EDBus_Message_Iter *iter, *variant;
|
|
EDBus_Pending *pending;
|
|
char sig[2];
|
|
EDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
|
|
|
|
if (!dbus_type_is_basic(type))
|
|
{
|
|
ERR("Only basic types may be set using edbus_proxy_property_set()");
|
|
return NULL;
|
|
}
|
|
|
|
sig[0] = type;
|
|
sig[1] = 0;
|
|
msg = edbus_proxy_method_call_new(get_properties_proxy(proxy), "Set");
|
|
iter = edbus_message_iter_get(msg);
|
|
edbus_message_iter_append_basic(iter, 's', proxy->interface);
|
|
edbus_message_iter_append_basic(iter, 's', name);
|
|
variant = edbus_message_iter_container_new(iter, 'v', sig);
|
|
edbus_message_iter_append_basic(variant, type, value);
|
|
edbus_message_iter_container_close(iter, variant);
|
|
|
|
pending = edbus_proxy_send(get_properties_proxy(proxy), msg, cb, data, -1);
|
|
edbus_message_unref(msg);
|
|
|
|
return pending;
|
|
}
|
|
|
|
EAPI EDBus_Pending *
|
|
edbus_proxy_property_get_all(EDBus_Proxy *proxy, EDBus_Message_Cb cb, const void *data)
|
|
{
|
|
EDBUS_PROXY_CHECK_RETVAL(proxy, NULL);
|
|
return edbus_proxy_call(get_properties_proxy(proxy), "GetAll", cb, data, -1,
|
|
"s", proxy->interface);
|
|
}
|