1 #include "e_mod_notifier_host_private.h"
2
3 #define WATCHER_BUS "org.kde.StatusNotifierWatcher"
4 #define WATCHER_PATH "/StatusNotifierWatcher"
5 #define WATCHER_IFACE "org.kde.StatusNotifierWatcher"
6
7 #define ITEM_IFACE "org.kde.StatusNotifierItem"
8
9 const char *Category_Names[] = {
10 "unknown", "SystemServices", NULL
11 };
12
13 const char *Status_Names[] = {
14 "unknown", "Active", "Passive", "NeedsAttention", NULL
15 };
16
17 static Context_Notifier_Host *ctx = NULL;
18
19 void
systray_notifier_item_free(Notifier_Item * item)20 systray_notifier_item_free(Notifier_Item *item)
21 {
22 Eldbus_Object *obj;
23 Eldbus_Signal_Handler *sig;
24 Instance_Notifier_Host *host_inst;
25 EINA_INLIST_FOREACH(ctx->instances, host_inst)
26 {
27 Notifier_Item_Icon *ii;
28 EINA_INLIST_FOREACH(host_inst->ii_list, ii)
29 {
30 if (ii->item == item)
31 break;
32 }
33 if (!ii)
34 continue;
35 host_inst->ii_list = eina_inlist_remove(host_inst->ii_list,
36 EINA_INLIST_GET(ii));
37 evas_object_del(ii->icon);
38 free(ii);
39 systray_size_updated(host_inst->inst);
40 }
41 if (item->menu_path)
42 e_dbusmenu_unload(item->menu_data);
43 eina_stringshare_del(item->bus_id);
44 eina_stringshare_del(item->path);
45 free(item->imgdata);
46 free(item->attnimgdata);
47 if (item->attention_icon_name)
48 eina_stringshare_del(item->attention_icon_name);
49 if (item->icon_name)
50 eina_stringshare_del(item->icon_name);
51 if (item->icon_path)
52 eina_stringshare_del(item->icon_path);
53 if (item->id)
54 eina_stringshare_del(item->id);
55 if (item->menu_path)
56 eina_stringshare_del(item->menu_path);
57 if (item->title)
58 eina_stringshare_del(item->title);
59 EINA_LIST_FREE(item->signals, sig)
60 eldbus_signal_handler_del(sig);
61 obj = eldbus_proxy_object_get(item->proxy);
62 eldbus_proxy_unref(item->proxy);
63 eldbus_object_unref(obj);
64 ctx->item_list = eina_inlist_remove(ctx->item_list, EINA_INLIST_GET(item));
65 free(item);
66 }
67
68 static void
image_load(const char * name,const char * path,uint32_t * imgdata,int w,int h,Evas_Object * image)69 image_load(const char *name, const char *path, uint32_t *imgdata, int w, int h, Evas_Object *image)
70 {
71 const char **ext, *exts[] =
72 {
73 ".png",
74 ".jpg",
75 NULL
76 };
77 printf("SYSTRAY: load image name=[%s] path=[%s] imgdata=[%p] size=[%ix%i]\n", name, path, imgdata, w, h);
78 if (path && path[0] && name)
79 {
80 char buf[PATH_MAX];
81 const char **theme, *themes[] =
82 {
83 e_config->icon_theme,
84 "hicolor",
85 // hmm sometimes this is there
86 // "emblems",
87 NULL
88 };
89
90 for (theme = themes; *theme; theme++)
91 {
92 unsigned int *i, sizes[] =
93 {
94 512, 256, 192, 128, 96, 72, 64, 48, 40, 36, 32, 24, 22, 16, 0
95 };
96
97 snprintf(buf, sizeof(buf), "%s/%s", path, *theme);
98 if (!ecore_file_is_dir(buf)) continue;
99 for (i = sizes; *i; i++)
100 {
101 snprintf(buf, sizeof(buf), "%s/%s/%ux%u", path, *theme, *i, *i);
102 if (!ecore_file_is_dir(buf)) continue;
103 for (ext = exts; *ext; ext++)
104 {
105 snprintf(buf, sizeof(buf), "%s/%s/%ux%u/status/%s%s", path, *theme, *i, *i, name, *ext);
106 if (ecore_file_exists(buf))
107 {
108 e_icon_file_set(image, buf);
109 return;
110 }
111 snprintf(buf, sizeof(buf), "%s/%s/%ux%u/apps/%s%s", path, *theme, *i, *i, name, *ext);
112 if (ecore_file_exists(buf))
113 {
114 e_icon_file_set(image, buf);
115 return;
116 }
117 }
118 }
119 }
120 for (ext = exts; *ext; ext++)
121 {
122 snprintf(buf, sizeof(buf), "%s/%s%s", path, name, *ext);
123 if (ecore_file_exists(buf))
124 {
125 e_icon_file_set(image, buf);
126 return;
127 }
128 }
129 }
130 if (name && name[0] && e_util_icon_theme_set(image, name)) return;
131 if (imgdata)
132 {
133 Evas_Object *o;
134
135 o = evas_object_image_filled_add(evas_object_evas_get(image));
136 evas_object_image_alpha_set(o, 1);
137 evas_object_image_size_set(o, w, h);
138 evas_object_image_data_set(o, imgdata);
139 e_icon_image_object_set(image, o);
140 }
141 else
142 e_util_icon_theme_set(image, "dialog-error");
143 }
144
145 static void
_sub_item_clicked_cb(void * data,E_Menu * m EINA_UNUSED,E_Menu_Item * mi EINA_UNUSED)146 _sub_item_clicked_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
147 {
148 E_DBusMenu_Item *item = data;
149 e_dbusmenu_event_send(item, E_DBUSMENU_ITEM_EVENT_CLICKED);
150 }
151
152 static void
_menu_post_deactivate(void * data,E_Menu * m)153 _menu_post_deactivate(void *data, E_Menu *m)
154 {
155 Eina_List *iter;
156 E_Menu_Item *mi;
157 E_Gadcon *gadcon = data;
158 E_DBusMenu_Item *item;
159
160 item = e_object_data_get(E_OBJECT(m));
161 if (item)
162 {
163 e_object_data_set(E_OBJECT(m), NULL);
164 e_dbusmenu_item_unref(item);
165 }
166
167 if (gadcon) e_gadcon_locked_set(gadcon, 0);
168 EINA_LIST_FOREACH(m->items, iter, mi)
169 {
170 item = e_object_data_get(E_OBJECT(mi));
171 if (item)
172 {
173 e_object_data_set(E_OBJECT(m), NULL);
174 e_dbusmenu_item_unref(item);
175 }
176 if (mi->submenu) e_menu_deactivate(mi->submenu);
177 }
178 e_object_del(E_OBJECT(m));
179 }
180
181 static E_Menu *
_item_submenu_new(E_DBusMenu_Item * item,E_Menu_Item * mi)182 _item_submenu_new(E_DBusMenu_Item *item, E_Menu_Item *mi)
183 {
184 E_DBusMenu_Item *child;
185 E_Menu *m;
186 E_Menu_Item *submi;
187
188 m = e_menu_new();
189 e_dbusmenu_item_ref(item);
190 e_object_data_set(E_OBJECT(m), item);
191 e_menu_post_deactivate_callback_set(m, _menu_post_deactivate, NULL);
192 if (mi) e_menu_item_submenu_set(mi, m);
193
194 EINA_INLIST_FOREACH(item->sub_items, child)
195 {
196 if (!child->visible) continue;
197 submi = e_menu_item_new(m);
198 e_dbusmenu_item_ref(child);
199 e_object_data_set(E_OBJECT(submi), child);
200 if (child->type == E_DBUSMENU_ITEM_TYPE_SEPARATOR)
201 e_menu_item_separator_set(submi, 1);
202 else
203 {
204 e_menu_item_label_set(submi, child->label);
205 e_menu_item_callback_set(submi, _sub_item_clicked_cb, child);
206 if (!child->enabled) e_menu_item_disabled_set(submi, 1);
207 if (child->toggle_type == E_DBUSMENU_ITEM_TOGGLE_TYPE_CHECKMARK)
208 e_menu_item_check_set(submi, 1);
209 else if (child->toggle_type == E_DBUSMENU_ITEM_TOGGLE_TYPE_RADIO)
210 e_menu_item_radio_set(submi, 1);
211 if (child->toggle_type)
212 e_menu_item_toggle_set(submi, child->toggle_state);
213 if (child->sub_items)
214 _item_submenu_new(child, submi);
215 e_util_menu_item_theme_icon_set(submi, child->icon_name);
216 }
217 }
218 return m;
219 }
220
221 void
_clicked_item_cb(void * data,Evas * evas,Evas_Object * obj EINA_UNUSED,void * event)222 _clicked_item_cb(void *data, Evas *evas, Evas_Object *obj EINA_UNUSED, void *event)
223 {
224 Notifier_Item_Icon *ii = data;
225 Evas_Event_Mouse_Down *ev = event;
226 E_DBusMenu_Item *root_item;
227 E_Menu *m;
228 E_Zone *zone;
229 int x, y;
230 E_Gadcon *gadcon = evas_object_data_get(ii->icon, "gadcon");
231
232 if (ev->button != 1) return;
233
234 EINA_SAFETY_ON_NULL_RETURN(gadcon);
235 if (!ii->item->dbus_item) return;
236 root_item = ii->item->dbus_item;
237 EINA_SAFETY_ON_FALSE_RETURN(root_item->is_submenu);
238
239 m = _item_submenu_new(root_item, NULL);
240 e_gadcon_locked_set(gadcon, 1);
241 e_menu_post_deactivate_callback_set(m, _menu_post_deactivate, gadcon);
242
243 zone = e_gadcon_zone_get(gadcon);
244 evas_pointer_canvas_xy_get(e_comp->evas, &x, &y);
245 e_menu_activate_mouse(m, zone, x, y, 1, 1, E_MENU_POP_DIRECTION_DOWN, ev->timestamp);
246 evas_event_feed_mouse_up(evas, ev->button,
247 EVAS_BUTTON_NONE, ev->timestamp, NULL);
248 }
249
250 void
systray_notifier_update_menu(void * data,E_DBusMenu_Item * new_root_item)251 systray_notifier_update_menu(void *data, E_DBusMenu_Item *new_root_item)
252 {
253 Notifier_Item *item = data;
254 item->dbus_item = new_root_item;
255 }
256
257 static void
image_scale(Instance_Notifier_Host * notifier_inst,Notifier_Item_Icon * ii)258 image_scale(Instance_Notifier_Host *notifier_inst, Notifier_Item_Icon *ii)
259 {
260 Evas_Coord sz;
261 switch (systray_gadcon_get(notifier_inst->inst)->orient)
262 {
263 case E_GADCON_ORIENT_FLOAT:
264 case E_GADCON_ORIENT_HORIZ:
265 case E_GADCON_ORIENT_TOP:
266 case E_GADCON_ORIENT_BOTTOM:
267 case E_GADCON_ORIENT_CORNER_TL:
268 case E_GADCON_ORIENT_CORNER_TR:
269 case E_GADCON_ORIENT_CORNER_BL:
270 case E_GADCON_ORIENT_CORNER_BR:
271 if (systray_gadcon_get(notifier_inst->inst)->shelf)
272 sz = systray_gadcon_get(notifier_inst->inst)->shelf->h;
273 else
274 evas_object_geometry_get(notifier_inst->inst->gcc->o_frame ?:
275 notifier_inst->inst->gcc->o_base, NULL, NULL, NULL, &sz);
276 break;
277
278 case E_GADCON_ORIENT_VERT:
279 case E_GADCON_ORIENT_LEFT:
280 case E_GADCON_ORIENT_RIGHT:
281 case E_GADCON_ORIENT_CORNER_LT:
282 case E_GADCON_ORIENT_CORNER_RT:
283 case E_GADCON_ORIENT_CORNER_LB:
284 case E_GADCON_ORIENT_CORNER_RB:
285 default:
286 if (systray_gadcon_get(notifier_inst->inst)->shelf)
287 sz = systray_gadcon_get(notifier_inst->inst)->shelf->w;
288 else
289 evas_object_geometry_get(notifier_inst->inst->gcc->o_frame ?:
290 notifier_inst->inst->gcc->o_base, NULL, NULL, &sz, NULL);
291 break;
292 }
293 sz = sz - 5;
294 evas_object_resize(ii->icon, sz, sz);
295 }
296
297 static void
_systray_notifier_inst_item_update(Instance_Notifier_Host * host_inst,Notifier_Item * item,Eina_Bool search)298 _systray_notifier_inst_item_update(Instance_Notifier_Host *host_inst, Notifier_Item *item, Eina_Bool search)
299 {
300 Notifier_Item_Icon *ii = NULL;
301 if (!search)
302 goto jump_search;
303 EINA_INLIST_FOREACH(host_inst->ii_list, ii)
304 {
305 if (ii->item == item)
306 break;
307 }
308 jump_search:
309 if (!ii)
310 {
311 ii = calloc(1, sizeof(Notifier_Item_Icon));
312 ii->item = item;
313 host_inst->ii_list = eina_inlist_append(host_inst->ii_list,
314 EINA_INLIST_GET(ii));
315 }
316
317 if (!ii->icon)
318 {
319 ii->icon = e_icon_add(evas_object_evas_get(host_inst->edje));
320 EINA_SAFETY_ON_NULL_RETURN(ii->icon);
321 image_scale(host_inst, ii);
322 evas_object_data_set(ii->icon, "gadcon", host_inst->gadcon);
323 evas_object_event_callback_add(ii->icon, EVAS_CALLBACK_MOUSE_DOWN,
324 _clicked_item_cb, ii);
325 }
326 switch (item->status)
327 {
328 case STATUS_ACTIVE:
329 {
330 image_load(item->icon_name, item->icon_path, item->imgdata, item->imgw, item->imgh, ii->icon);
331 if (!evas_object_visible_get(ii->icon))
332 {
333 systray_edje_box_append(host_inst->inst, ii->icon);
334 evas_object_show(ii->icon);
335 }
336 break;
337 }
338 case STATUS_PASSIVE:
339 {
340 if (evas_object_visible_get(ii->icon))
341 {
342 systray_edje_box_remove(host_inst->inst, ii->icon);
343 evas_object_hide(ii->icon);
344 }
345 break;
346 }
347 case STATUS_ATTENTION:
348 {
349 image_load(item->attention_icon_name, item->icon_path, item->attnimgdata, item->attnimgw, item->attnimgh, ii->icon);
350 if (!evas_object_visible_get(ii->icon))
351 {
352 systray_edje_box_append(host_inst->inst, ii->icon);
353 evas_object_show(ii->icon);
354 }
355 break;
356 }
357 default:
358 {
359 WRN("unhandled status");
360 break;
361 }
362 }
363 systray_size_updated(host_inst->inst);
364 }
365
366 void
systray_notifier_item_update(Notifier_Item * item)367 systray_notifier_item_update(Notifier_Item *item)
368 {
369 Instance_Notifier_Host *inst;
370 EINA_INLIST_FOREACH(ctx->instances, inst)
371 _systray_notifier_inst_item_update(inst, item, EINA_TRUE);
372 }
373
374 Instance_Notifier_Host *
systray_notifier_host_new(Instance * inst,E_Gadcon * gadcon)375 systray_notifier_host_new(Instance *inst, E_Gadcon *gadcon)
376 {
377 Instance_Notifier_Host *host_inst = NULL;
378 Notifier_Item *item;
379 host_inst = calloc(1, sizeof(Instance_Notifier_Host));
380 EINA_SAFETY_ON_NULL_RETURN_VAL(host_inst, NULL);
381 host_inst->inst = inst;
382 host_inst->edje = systray_edje_get(inst);
383 host_inst->gadcon = gadcon;
384 ctx->instances = eina_inlist_append(ctx->instances, EINA_INLIST_GET(host_inst));
385
386 EINA_INLIST_FOREACH(ctx->item_list, item)
387 _systray_notifier_inst_item_update(host_inst, item, EINA_FALSE);
388
389 return host_inst;
390 }
391
392 void
systray_notifier_host_free(Instance_Notifier_Host * notifier)393 systray_notifier_host_free(Instance_Notifier_Host *notifier)
394 {
395 while (notifier->ii_list)
396 {
397 Notifier_Item_Icon *ii = EINA_INLIST_CONTAINER_GET(notifier->ii_list, Notifier_Item_Icon);
398 notifier->ii_list = eina_inlist_remove(notifier->ii_list,
399 notifier->ii_list);
400 free(ii);
401 }
402 ctx->instances = eina_inlist_remove(ctx->instances, EINA_INLIST_GET(notifier));
403 free(notifier);
404 }
405
406 void
systray_notifier_host_init(void)407 systray_notifier_host_init(void)
408 {
409 ctx = calloc(1, sizeof(Context_Notifier_Host));
410 EINA_SAFETY_ON_NULL_RETURN(ctx);
411 systray_notifier_dbus_init(ctx);
412 }
413
414 void
systray_notifier_host_shutdown(void)415 systray_notifier_host_shutdown(void)
416 {
417 Eldbus_Pending *p;
418
419 EINA_LIST_FREE(ctx->pending, p) eldbus_pending_cancel(p);
420 systray_notifier_dbus_shutdown(ctx);
421 free(ctx);
422 ctx = NULL;
423 }
424