#include "e_mod_notifier_host_private.h" #define WATCHER_BUS "org.kde.StatusNotifierWatcher" #define WATCHER_PATH "/StatusNotifierWatcher" #define WATCHER_IFACE "org.kde.StatusNotifierWatcher" #define ITEM_IFACE "org.kde.StatusNotifierItem" #define HOST_REGISTRER "/bla" //TODO check what watcher expect we send to him #undef ERR #define ERR(...) fprintf(stderr, __VA_ARGS__) extern const char *Category_Names[]; extern const char *Status_Names[]; static Eina_Stringshare *DBUS_PATH; typedef struct _Notifier_Host_Data { Instance_Notifier_Host *host_inst; void *data; } Notifier_Host_Data; static Eina_Bool service_string_parse(const char *item, const char **path, const char **bus_id) { const char *p; p = strchr(item, '/'); if (!p) return EINA_FALSE; *path = eina_stringshare_add(p); *bus_id = eina_stringshare_add_length(item, p - item); return EINA_TRUE; } static Notifier_Item * notifier_item_find(const char *path, const char *bus_id, Context_Notifier_Host *ctx) { Notifier_Item *item; EINA_INLIST_FOREACH(ctx->item_list, item) { if (item->bus_id == bus_id && item->path == path) return item; } return NULL; } static int id_find(const char *s, const char *names[]) { unsigned i; for (i = 0; names[i]; i++) { if (!strcmp(s, names[i])) return i; } return 0; } static void icon_pixmap_deserialize(Eldbus_Message_Iter *variant, uint32_t **data, int *w, int *h) { Eldbus_Message_Iter *iter, *struc; *data = NULL; *w = *h = 0; eldbus_message_iter_arguments_get(variant, "a(iiay)", &iter); while (eldbus_message_iter_get_and_next(iter, 'r', &struc)) { Eldbus_Message_Iter *imgdata; if (eldbus_message_iter_arguments_get(struc, "iiay", w, h, &imgdata)) { uint32_t *img; int len; if (eldbus_message_iter_fixed_array_get(imgdata, 'y', &img, &len)) { unsigned int pos; *data = malloc(len * sizeof(int)); for (pos = 0; pos < (unsigned int)len; pos++) (*data)[pos] = eina_swap32(img[pos]); return; } } } } static void item_prop_get(void *data, const void *key, Eldbus_Message_Iter *var) { Notifier_Item *item = data; if (!strcmp(key, "Category")) { const char *category; eldbus_message_iter_arguments_get(var, "s", &category); item->category = id_find(category, Category_Names); } else if (!strcmp(key, "IconName")) { const char *name; eldbus_message_iter_arguments_get(var, "s", &name); eina_stringshare_replace(&item->icon_name, name); } else if (!strcmp(key, "IconPixmap")) { free(item->imgdata); icon_pixmap_deserialize(var, &item->imgdata, &item->imgw, &item->imgh); } else if (!strcmp(key, "AttentionIconPixmap")) { free(item->attnimgdata); icon_pixmap_deserialize(var, &item->attnimgdata, &item->attnimgw, &item->attnimgh); } else if (!strcmp(key, "AttentionIconName")) { const char *name; eldbus_message_iter_arguments_get(var, "s", &name); eina_stringshare_replace(&item->attention_icon_name, name); } else if (!strcmp(key, "IconThemePath")) { const char *path; eldbus_message_iter_arguments_get(var, "s", &path); eina_stringshare_replace(&item->icon_path, path); } else if (!strcmp(key, "Menu")) { const char *path; eldbus_message_iter_arguments_get(var, "o", &path); eina_stringshare_replace(&item->menu_path, path); } else if (!strcmp(key, "Status")) { const char *status; eldbus_message_iter_arguments_get(var, "s", &status); item->status = id_find(status, Status_Names); } else if (!strcmp(key, "Id")) { const char *id; eldbus_message_iter_arguments_get(var, "s", &id); eina_stringshare_replace(&item->id, id); } else if (!strcmp(key, "Title")) { const char *title; eldbus_message_iter_arguments_get(var, "s", &title); eina_stringshare_replace(&item->title, title); } } static void props_changed(void *data, const Eldbus_Message *msg) { Notifier_Item *item = data; const char *interface, *menu = item->menu_path; Eldbus_Message_Iter *changed, *invalidate; if (!eldbus_message_arguments_get(msg, "sa{sv}as", &interface, &changed, &invalidate)) { ERR("Error reading message"); return; } eldbus_message_iter_dict_iterate(changed, "sv", item_prop_get, item); if (menu != item->menu_path) { Eldbus_Connection *conn = eldbus_object_connection_get(eldbus_proxy_object_get(item->proxy)); item->dbus_item = NULL; e_dbusmenu_unload(item->menu_data); item->menu_data = e_dbusmenu_load(conn, item->bus_id, item->menu_path, item); e_dbusmenu_update_cb_set(item->menu_data, systray_notifier_update_menu); } } static void props_get_all_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) { const char *error, *error_name; Eldbus_Message_Iter *dict; Notifier_Item *item = data; Eldbus_Connection *conn; if (eldbus_message_error_get(msg, &error, &error_name)) { ERR("%s %s", error, error_name); return; } if (!eldbus_message_arguments_get(msg, "a{sv}", &dict)) { ERR("Error getting arguments."); return; } eldbus_message_iter_dict_iterate(dict, "sv", item_prop_get, item); if (!item->menu_path) ERR("Notifier item %s dont have menu path.", item->menu_path); conn = eldbus_object_connection_get(eldbus_proxy_object_get(item->proxy)); item->menu_data = e_dbusmenu_load(conn, item->bus_id, item->menu_path, item); e_dbusmenu_update_cb_set(item->menu_data, systray_notifier_update_menu); systray_notifier_item_update(item); } static Eina_Bool basic_prop_get(const char *propname, void *data, const Eldbus_Message *msg) { Eldbus_Message_Iter *var; const char *error, *error_msg; if (eldbus_message_error_get(msg, &error, &error_msg)) { ERR("%s %s", error, error_msg); return EINA_FALSE; } if (!eldbus_message_arguments_get(msg, "v", &var)) { ERR("Error reading message."); return EINA_FALSE; } item_prop_get(data, propname, var); return EINA_TRUE; } static void attention_icon_pixmap_get_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) { Notifier_Item *item = data; Eldbus_Message_Iter *variant; if (!eldbus_message_arguments_get(msg, "v", &variant)) return; free(item->attnimgdata); icon_pixmap_deserialize(variant, &item->attnimgdata, &item->attnimgw, &item->attnimgh); systray_notifier_item_update(item); } static void attention_icon_get_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) { Notifier_Item *item = data; const char *propname = "AttentionIconName"; basic_prop_get(propname, item, msg); if ((!item->attention_icon_name) || (!item->attention_icon_name[0])) eldbus_proxy_property_get(item->proxy, "AttentionIconPixmap", attention_icon_pixmap_get_cb, item); else systray_notifier_item_update(item); } static void new_attention_icon_cb(void *data, const Eldbus_Message *msg EINA_UNUSED) { Notifier_Item *item = data; eldbus_proxy_property_get(item->proxy, "AttentionIconName", attention_icon_get_cb, item); } static void icon_pixmap_get_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) { Notifier_Item *item = data; Eldbus_Message_Iter *variant; if (!eldbus_message_arguments_get(msg, "v", &variant)) return; free(item->imgdata); icon_pixmap_deserialize(variant, &item->imgdata, &item->imgw, &item->imgh); systray_notifier_item_update(item); } static void icon_get_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) { Notifier_Item *item = data; const char *propname = "IconName"; basic_prop_get(propname, item, msg); if ((!item->icon_name) || (!item->icon_name[0])) eldbus_proxy_property_get(item->proxy, "IconPixmap", icon_pixmap_get_cb, item); else systray_notifier_item_update(item); } static void new_icon_cb(void *data, const Eldbus_Message *msg EINA_UNUSED) { Notifier_Item *item = data; eldbus_proxy_property_get(item->proxy, "IconName", icon_get_cb, item); } static void title_get_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) { Notifier_Item *item = data; const char *propname = "Title"; basic_prop_get(propname, item, msg); systray_notifier_item_update(item); } static void new_title_cb(void *data, const Eldbus_Message *msg EINA_UNUSED) { Notifier_Item *item = data; eldbus_proxy_property_get(item->proxy, "Title", title_get_cb, item); } static void new_icon_theme_path_cb(void *data, const Eldbus_Message *msg) { Notifier_Item *item = data; const char *path; if (!eldbus_message_arguments_get(msg, "s", &path)) { ERR("Error reading message."); return; } eina_stringshare_replace(&item->icon_path, path); systray_notifier_item_update(item); } static void new_status_cb(void *data, const Eldbus_Message *msg) { Notifier_Item *item = data; const char *status; if (!eldbus_message_arguments_get(msg, "s", &status)) { ERR("Error reading message."); return; } item->status = id_find(status, Status_Names); systray_notifier_item_update(item); } static void notifier_item_add(const char *path, const char *bus_id, Context_Notifier_Host *ctx) { Eldbus_Proxy *proxy; Notifier_Item *item = calloc(1, sizeof(Notifier_Item)); Eldbus_Signal_Handler *s; EINA_SAFETY_ON_NULL_RETURN(item); item->path = path; item->bus_id = bus_id; ctx->item_list = eina_inlist_append(ctx->item_list, EINA_INLIST_GET(item)); proxy = eldbus_proxy_get(eldbus_object_get(ctx->conn, bus_id, path), ITEM_IFACE); item->proxy = proxy; eldbus_proxy_property_get_all(proxy, props_get_all_cb, item); s = eldbus_proxy_properties_changed_callback_add(proxy, props_changed, item); item->signals = eina_list_append(item->signals, s); s = eldbus_proxy_signal_handler_add(proxy, "NewAttentionIcon", new_attention_icon_cb, item); item->signals = eina_list_append(item->signals, s); s = eldbus_proxy_signal_handler_add(proxy, "NewIcon", new_icon_cb, item); item->signals = eina_list_append(item->signals, s); s = eldbus_proxy_signal_handler_add(proxy, "NewIconThemePath", new_icon_theme_path_cb, item); item->signals = eina_list_append(item->signals, s); s = eldbus_proxy_signal_handler_add(proxy, "NewStatus", new_status_cb, item); item->signals = eina_list_append(item->signals, s); s = eldbus_proxy_signal_handler_add(proxy, "NewTitle", new_title_cb, item); item->signals = eina_list_append(item->signals, s); } static void notifier_item_add_cb(void *data, const Eldbus_Message *msg) { const char *item, *bus, *path; Context_Notifier_Host *ctx = data; if (!eldbus_message_arguments_get(msg, "s", &item)) { ERR("Error getting arguments from msg."); return; } DBG("add %s", item); if (service_string_parse(item, &path, &bus)) notifier_item_add(path, bus, ctx); } static void notifier_item_del_cb(void *data, const Eldbus_Message *msg) { const char *service, *bus, *path; Notifier_Item *item; Context_Notifier_Host *ctx = data; if (!eldbus_message_arguments_get(msg, "s", &service)) { ERR("Error getting arguments from msg."); return; } DBG("service %s", service); if (!service_string_parse(service, &path, &bus)) return; item = notifier_item_find(path, bus, ctx); if (item) systray_notifier_item_free(item); eina_stringshare_del(path); eina_stringshare_del(bus); } static void notifier_items_get_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) { const char *item; const char *error, *error_msg; Eldbus_Message_Iter *array, *variant; Context_Notifier_Host *ctx = data; if (eldbus_message_error_get(msg, &error, &error_msg)) { ERR("%s %s", error, error_msg); return; } if (!eldbus_message_arguments_get(msg, "v", &variant)) { ERR("Error getting arguments from msg."); return; } if (!eldbus_message_iter_arguments_get(variant, "as", &array)) { ERR("Error getting arguments from msg."); return; } while (eldbus_message_iter_get_and_next(array, 's', &item)) { const char *bus, *path; if (service_string_parse(item, &path, &bus)) notifier_item_add(path, bus, ctx); } } static void item_registered_local_cb(void *data, const char *bus) { Context_Notifier_Host *ctx = data; notifier_item_add(eina_stringshare_ref(DBUS_PATH), eina_stringshare_add(bus), ctx); } static void item_unregistered_local_cb(void *data, const char *bus) { Context_Notifier_Host *ctx = data; Notifier_Item *item; Eina_Stringshare *s; s = eina_stringshare_add(bus); item = notifier_item_find(DBUS_PATH, s, ctx); if (item) systray_notifier_item_free(item); eina_stringshare_del(s); } static void name_request_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) { const char *error, *error_msg; unsigned flag; Eldbus_Object *obj; Context_Notifier_Host *ctx = data; ctx->pending = eina_list_remove(ctx->pending, pending); if (eldbus_message_error_get(msg, &error, &error_msg)) { ERR("%s %s", error, error_msg); goto end; } if (!eldbus_message_arguments_get(msg, "u", &flag)) { ERR("Error reading message."); goto end; } if (flag == ELDBUS_NAME_REQUEST_REPLY_PRIMARY_OWNER) { systray_notifier_dbus_watcher_start(ctx->conn, item_registered_local_cb, item_unregistered_local_cb, ctx); return; } end: WRN("Bus name: %s already in use, getting data via dbus.\n", WATCHER_BUS); obj = eldbus_object_get(ctx->conn, WATCHER_BUS, WATCHER_PATH); ctx->watcher = eldbus_proxy_get(obj, WATCHER_IFACE); eldbus_proxy_call(ctx->watcher, "RegisterStatusNotifierHost", NULL, NULL, -1, "s", HOST_REGISTRER); eldbus_proxy_property_get(ctx->watcher, "RegisteredStatusNotifierItems", notifier_items_get_cb, ctx); eldbus_proxy_signal_handler_add(ctx->watcher, "StatusNotifierItemRegistered", notifier_item_add_cb, ctx); eldbus_proxy_signal_handler_add(ctx->watcher, "StatusNotifierItemUnregistered", notifier_item_del_cb, ctx); } void systray_notifier_dbus_init(Context_Notifier_Host *ctx) { Eldbus_Pending *p; eldbus_init(); ctx->conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION); if (!ctx->conn) return; DBUS_PATH = eina_stringshare_add("/StatusNotifierItem"); p = eldbus_name_request(ctx->conn, WATCHER_BUS, ELDBUS_NAME_REQUEST_FLAG_REPLACE_EXISTING, name_request_cb, ctx); if (p) ctx->pending = eina_list_append(ctx->pending, p); } void systray_notifier_dbus_shutdown(Context_Notifier_Host *ctx) { Eina_Inlist *safe_list; Notifier_Item *item; ERR("systray_notifier_dbus_shutdown"); EINA_INLIST_FOREACH_SAFE(ctx->item_list, safe_list, item) systray_notifier_item_free(item); if (!ctx->watcher) systray_notifier_dbus_watcher_stop(); else { Eldbus_Object *obj; obj = eldbus_proxy_object_get(ctx->watcher); eldbus_proxy_unref(ctx->watcher); eldbus_object_unref(obj); ctx->watcher = NULL; } eina_stringshare_replace(&DBUS_PATH, NULL); eldbus_connection_unref(ctx->conn); eldbus_shutdown(); }