#include "e_mod_notifier_host_private.h" #define PATH "/StatusNotifierWatcher" #define IFACE "org.kde.StatusNotifierWatcher" #define PROTOCOL_VERSION 1 #define ERROR_HOST_ALREADY_REGISTERED "org.kde.StatusNotifierWatcher.Host.AlreadyRegistered" #define ERROR_ITEM_ALREADY_REGISTERED "org.kde.StatusNotifierWatcher.Item.AlreadyRegistered" static Eldbus_Connection *conn = NULL; static Eldbus_Service_Interface *iface = NULL; static Eina_List *items; static const char *host_service = NULL; static E_Notifier_Watcher_Item_Registered_Cb registered_cb; static E_Notifier_Watcher_Item_Unregistered_Cb unregistered_cb; static void *user_data; enum { ITEM_REGISTERED = 0, ITEM_UNREGISTERED, HOST_REGISTERED, HOST_UNREGISTERED }; static void item_name_monitor_cb(void *data, const char *bus, const char *old_id EINA_UNUSED, const char *new_id) { const char *svc, *service = data; Eina_List *l; l = eina_list_data_find_list(items, service); if (strcmp(new_id, "")) { if (l) return; items = eina_list_append(items, service); svc = strchr(service, '/') + 1; registered_cb(user_data, bus, svc); return; } svc = strchr(service, '/') + 1; eldbus_service_signal_emit(iface, ITEM_UNREGISTERED, svc); if (l) { items = eina_list_remove_list(items, l); if (unregistered_cb) unregistered_cb(user_data, bus, svc); } bus = eina_stringshare_add(bus); if (eina_hash_del_by_key(systray_ctx_get()->config->items, bus)) e_config_save_queue(); eina_stringshare_del(bus); eldbus_name_owner_changed_callback_del(conn, bus, item_name_monitor_cb, service); eina_stringshare_del(service); } static Eldbus_Message * register_item_cb(const Eldbus_Service_Interface *s_iface, const Eldbus_Message *msg) { const char *service, *svc; char buf[1024]; Eina_Bool stupid; if (!eldbus_message_arguments_get(msg, "s", &service)) return NULL; svc = service; /* if stupid, this app does not conform to http://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/ * and is expecting to have its send id watched as it is not providing a real bus name here */ stupid = !!strncmp(svc, "org.", 4); snprintf(buf, sizeof(buf), "%s/%s", stupid ? eldbus_message_sender_get(msg) : svc, stupid ? svc : "/StatusNotifierItem"); service = eina_stringshare_add(buf); if (eina_list_data_find(items, service)) { eina_stringshare_del(service); return eldbus_message_error_new(msg, ERROR_ITEM_ALREADY_REGISTERED, ""); } items = eina_list_append(items, service); eldbus_service_signal_emit(s_iface, ITEM_REGISTERED, svc); eldbus_name_owner_changed_callback_add(conn, stupid ? eldbus_message_sender_get(msg) : svc, item_name_monitor_cb, service, EINA_FALSE); if (registered_cb) registered_cb(user_data, stupid ? eldbus_message_sender_get(msg) : svc, stupid ? svc : "/StatusNotifierItem"); return eldbus_message_method_return_new(msg); } static void host_name_monitor_cb(void *data EINA_UNUSED, const char *bus, const char *old_id EINA_UNUSED, const char *new_id) { if (strcmp(new_id, "")) return; eldbus_service_signal_emit(iface, HOST_UNREGISTERED); eina_stringshare_del(host_service); host_service = NULL; eldbus_name_owner_changed_callback_del(conn, bus, host_name_monitor_cb, NULL); } static Eldbus_Message * register_host_cb(const Eldbus_Service_Interface *s_iface, const Eldbus_Message *msg) { if (host_service) return eldbus_message_error_new(msg, ERROR_HOST_ALREADY_REGISTERED, ""); if (!eldbus_message_arguments_get(msg, "s", &host_service)) return NULL; host_service = eina_stringshare_add(host_service); eldbus_service_signal_emit(s_iface, HOST_REGISTERED); eldbus_name_owner_changed_callback_add(conn, eldbus_message_sender_get(msg), host_name_monitor_cb, NULL, EINA_FALSE); return eldbus_message_method_return_new(msg); } static Eina_Bool properties_get(const Eldbus_Service_Interface *s_iface EINA_UNUSED, const char *propname, Eldbus_Message_Iter *iter, const Eldbus_Message *request_msg EINA_UNUSED, Eldbus_Message **error EINA_UNUSED) { if (!strcmp(propname, "ProtocolVersion")) eldbus_message_iter_basic_append(iter, 'i', PROTOCOL_VERSION); else if (!strcmp(propname, "RegisteredStatusNotifierItems")) { Eldbus_Message_Iter *array; Eina_List *l; const char *service; eldbus_message_iter_arguments_append(iter, "as", &array); EINA_LIST_FOREACH(items, l, service) eldbus_message_iter_arguments_append(array, "s", service); eldbus_message_iter_container_close(iter, array); } else if (!strcmp(propname, "IsStatusNotifierHostRegistered")) eldbus_message_iter_arguments_append(iter, "b", host_service ? EINA_TRUE : EINA_FALSE); return EINA_TRUE; } static const Eldbus_Property properties[] = { { "RegisteredStatusNotifierItems", "as", NULL, NULL, 0 }, { "IsStatusNotifierHostRegistered", "b", NULL, NULL, 0 }, { "ProtocolVersion", "i", NULL, NULL, 0 }, { NULL, NULL, NULL, NULL, 0 } }; static const Eldbus_Signal signals[] = { { "StatusNotifierItemRegistered", ELDBUS_ARGS({"s", "service"}), 0 }, { "StatusNotifierItemUnregistered", ELDBUS_ARGS({"s", "service"}), 0 }, { "StatusNotifierHostRegistered", NULL, 0 }, { "StatusNotifierHostUnregistered", NULL, 0 }, { NULL, NULL, 0 } }; static const Eldbus_Method methods[] = { { "RegisterStatusNotifierItem", ELDBUS_ARGS({"s", "service"}), NULL, register_item_cb, 0 }, { "RegisterStatusNotifierHost", ELDBUS_ARGS({"s", "service"}), NULL, register_host_cb, 0 }, { NULL, NULL, NULL, NULL, 0 } }; static const Eldbus_Service_Interface_Desc iface_desc = { IFACE, methods, signals, properties, properties_get, NULL }; static void systray_notifier_item_hash_del(Notifier_Item_Cache *item) { eina_stringshare_del(item->path); free(item); } void systray_notifier_dbus_watcher_start(Eldbus_Connection *connection, E_Notifier_Watcher_Item_Registered_Cb registered, E_Notifier_Watcher_Item_Unregistered_Cb unregistered, const void *data) { const char *dbus; EINA_SAFETY_ON_TRUE_RETURN(!!conn); conn = connection; iface = eldbus_service_interface_register(conn, PATH, &iface_desc); registered_cb = registered; unregistered_cb = unregistered; user_data = (void *)data; host_service = eina_stringshare_add("internal"); dbus = eldbus_connection_unique_name_get(conn); if (systray_ctx_get()->config->items) eina_hash_free_cb_set(systray_ctx_get()->config->items, (Eina_Free_Cb)systray_notifier_item_hash_del); if (dbus && systray_ctx_get()->config->dbus && systray_ctx_get()->config->items) { if (!strcmp(systray_ctx_get()->config->dbus, dbus)) { Eina_Iterator *it; Eina_Hash_Tuple *t; it = eina_hash_iterator_tuple_new(systray_ctx_get()->config->items); EINA_ITERATOR_FOREACH(it, t) { char buf[1024]; Notifier_Item_Cache *nic = t->data; snprintf(buf, sizeof(buf), "%s/%s", (char*)t->key, nic->path); eldbus_name_owner_changed_callback_add(conn, t->key, item_name_monitor_cb, eina_stringshare_add(buf), EINA_TRUE); } eina_iterator_free(it); return; } } eina_stringshare_replace(&systray_ctx_get()->config->dbus, dbus); if (systray_ctx_get()->config->items) eina_hash_free_buckets(systray_ctx_get()->config->items); else systray_ctx_get()->config->items = eina_hash_stringshared_new((Eina_Free_Cb)systray_notifier_item_hash_del); e_config_save_queue(); } void systray_notifier_dbus_watcher_stop(void) { const char *txt; eldbus_service_interface_unregister(iface); EINA_LIST_FREE(items, txt) { char *bus; int i; for (i = 0; txt[i] != '/'; i++); i++; bus = malloc(sizeof(char) * i); snprintf(bus, i, "%s", txt); eldbus_name_owner_changed_callback_del(conn, bus, item_name_monitor_cb, txt); free(bus); eina_stringshare_del(txt); } if (host_service) eina_stringshare_del(host_service); conn = NULL; E_FREE_FUNC(systray_ctx_get()->config->items, eina_hash_free); eina_stringshare_replace(&systray_ctx_get()->config->dbus, NULL); }