update for beta release
[framework/uifw/e17.git] / src / modules / systray / e_mod_main.c
1 /**
2  * systray implementation following freedesktop.org specification.
3  *
4  * @see: http://standards.freedesktop.org/systemtray-spec/latest/
5  *
6  * @todo: implement xembed, mostly done, at least relevant parts are done.
7  *        http://standards.freedesktop.org/xembed-spec/latest/
8  *
9  * @todo: implement messages/popup part of the spec (anyone using this at all?)
10  */
11
12 #include "e.h"
13 #include "e_mod_main.h"
14
15 #define RETRY_TIMEOUT 2.0
16
17 #define SYSTEM_TRAY_REQUEST_DOCK 0
18 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
19 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
20 #define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
21 #define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1
22
23 /* XEMBED messages */
24 #define XEMBED_EMBEDDED_NOTIFY          0
25 #define XEMBED_WINDOW_ACTIVATE          1
26 #define XEMBED_WINDOW_DEACTIVATE        2
27 #define XEMBED_REQUEST_FOCUS            3
28 #define XEMBED_FOCUS_IN                 4
29 #define XEMBED_FOCUS_OUT                5
30 #define XEMBED_FOCUS_NEXT               6
31 #define XEMBED_FOCUS_PREV               7
32 /* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */
33 #define XEMBED_MODALITY_ON              10
34 #define XEMBED_MODALITY_OFF             11
35 #define XEMBED_REGISTER_ACCELERATOR     12
36 #define XEMBED_UNREGISTER_ACCELERATOR   13
37 #define XEMBED_ACTIVATE_ACCELERATOR     14
38
39 /* Details for  XEMBED_FOCUS_IN: */
40 #define XEMBED_FOCUS_CURRENT            0
41 #define XEMBED_FOCUS_FIRST              1
42 #define XEMBED_FOCUS_LAST               2
43
44 typedef struct _Instance Instance;
45 typedef struct _Icon Icon;
46
47 struct _Icon
48 {
49    Ecore_X_Window win;
50    Evas_Object *o;
51    Instance *inst;
52 };
53
54 struct _Instance
55 {
56    E_Gadcon_Client *gcc;
57    E_Container *con;
58    Evas *evas;
59    struct
60    {
61       Ecore_X_Window parent;
62       Ecore_X_Window base;
63       Ecore_X_Window selection;
64    } win;
65    struct
66    {
67       Evas_Object *gadget;
68    } ui;
69    struct
70    {
71       Ecore_Event_Handler *message;
72       Ecore_Event_Handler *destroy;
73       Ecore_Event_Handler *show;
74       Ecore_Event_Handler *reparent;
75       Ecore_Event_Handler *sel_clear;
76       Ecore_Event_Handler *configure;
77    } handler;
78    struct
79    {
80       Ecore_Timer *retry;
81    } timer;
82    struct
83    {
84       Ecore_Job *size_apply;
85    } job;
86    Eina_List *icons;
87    E_Menu *menu;
88 };
89
90 static const char _Name[] = "Systray";
91 static const char _name[] = "systray";
92 static const char _group_gadget[] = "e/modules/systray/main";
93 static const char _part_box[] = "e.box";
94 static const char _part_size[] = "e.size";
95 static const char _sig_source[] = "e";
96 static const char _sig_enable[] = "e,action,enable";
97 static const char _sig_disable[] = "e,action,disable";
98
99 static Ecore_X_Atom _atom_manager = 0;
100 static Ecore_X_Atom _atom_st_orient = 0;
101 static Ecore_X_Atom _atom_st_visual = 0;
102 static Ecore_X_Atom _atom_st_op_code = 0;
103 static Ecore_X_Atom _atom_st_msg_data = 0;
104 static Ecore_X_Atom _atom_xembed = 0;
105 static Ecore_X_Atom _atom_xembed_info = 0;
106 static Ecore_X_Atom _atom_st_num = 0;
107 static int _last_st_num = -1;
108
109 static E_Module *systray_mod = NULL;
110 static Instance *instance = NULL; /* only one systray ever possible */
111 static char tmpbuf[PATH_MAX]; /* general purpose buffer, just use immediately */
112
113 static Eina_Bool 
114 _systray_site_is_safe(E_Gadcon_Site site) 
115 {
116    /* NB: filter out sites we know are not safe for a systray to sit.
117     * This was done so that systray could be put into illume indicator 
118     * (or anywhere else really) that is 'safe' for systray to be. 
119     * Pretty much, this is anywhere but Desktop and toolbars at the moment */
120    if (e_gadcon_site_is_desktop(site)) 
121      return EINA_FALSE;
122    else if (e_gadcon_site_is_any_toolbar(site)) 
123      return EINA_FALSE;
124    return EINA_TRUE;
125 }
126
127 static const char *
128 _systray_theme_path(void)
129 {
130 #define TF "/e-module-systray.edj"
131    unsigned int dirlen;
132    const char *moddir = e_module_dir_get(systray_mod);
133
134    dirlen = strlen(moddir);
135    if (dirlen >= sizeof(tmpbuf) - sizeof(TF))
136      return NULL;
137
138    memcpy(tmpbuf, moddir, dirlen);
139    memcpy(tmpbuf + dirlen, TF, sizeof(TF));
140
141    return tmpbuf;
142 #undef TF
143 }
144
145 static void
146 _systray_menu_cb_post(void *data, E_Menu *menu __UNUSED__)
147 {
148    Instance *inst = data;
149    if (!inst->menu) return;
150    e_object_del(E_OBJECT(inst->menu));
151    inst->menu = NULL;
152 }
153
154 static void
155 _systray_menu_new(Instance *inst, Evas_Event_Mouse_Down *ev)
156 {
157    E_Zone *zone;
158    E_Menu *m;
159    int x, y;
160
161    zone = e_util_zone_current_get(e_manager_current_get());
162
163    m = e_menu_new();
164    m = e_gadcon_client_util_menu_items_append(inst->gcc, m, 0);
165    e_menu_post_deactivate_callback_set(m, _systray_menu_cb_post, inst);
166    inst->menu = m;
167    e_gadcon_canvas_zone_geometry_get(inst->gcc->gadcon, &x, &y, NULL, NULL);
168    e_menu_activate_mouse(m, zone, x + ev->output.x, y + ev->output.y,
169                          1, 1, E_MENU_POP_DIRECTION_AUTO, ev->timestamp);
170 }
171
172 static void
173 _systray_cb_mouse_down(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event __UNUSED__)
174 {
175    Instance *inst = data;
176    Evas_Event_Mouse_Down *ev = event;
177
178    if ((ev->button == 3) && (!inst->menu))
179      _systray_menu_new(inst, ev);
180 }
181
182 static void
183 _systray_size_apply_do(Instance *inst)
184 {
185    const Evas_Object *o;
186    Evas_Coord x, y, w, h;
187
188    edje_object_message_signal_process(inst->ui.gadget);
189    o = edje_object_part_object_get(inst->ui.gadget, _part_box);
190    if (!o) return;
191    evas_object_size_hint_min_get(o, &w, &h);
192
193    if (w < 1) w = 1;
194    if (h < 1) h = 1;
195
196    if (eina_list_count(inst->icons) == 0)
197       ecore_x_window_hide(inst->win.base);
198    else
199       ecore_x_window_show(inst->win.base);
200
201    e_gadcon_client_aspect_set(inst->gcc, w, h);
202    e_gadcon_client_min_size_set(inst->gcc, w, h);
203
204    evas_object_geometry_get(o, &x, &y, &w, &h);
205    ecore_x_window_move_resize(inst->win.base, x, y, w, h);
206 }
207
208 static void
209 _systray_size_apply_delayed(void *data)
210 {
211    Instance *inst = data;
212    _systray_size_apply_do(inst);
213    inst->job.size_apply = NULL;
214 }
215
216 static void
217 _systray_size_apply(Instance *inst)
218 {
219    if (inst->job.size_apply) return;
220    inst->job.size_apply = ecore_job_add(_systray_size_apply_delayed, inst);
221 }
222
223 static void
224 _systray_cb_move(void *data, Evas *evas __UNUSED__, Evas_Object *o __UNUSED__, void *event __UNUSED__)
225 {
226    Instance *inst = data;
227    _systray_size_apply(inst);
228 }
229
230 static void
231 _systray_cb_resize(void *data, Evas *evas __UNUSED__, Evas_Object *o __UNUSED__, void *event __UNUSED__)
232 {
233    Instance *inst = data;
234    _systray_size_apply(inst);
235 }
236
237 static void
238 _systray_icon_geometry_apply(Icon *icon)
239 {
240    const Evas_Object *o;
241    Evas_Coord x, y, w, h, wx, wy;
242
243    o = edje_object_part_object_get(icon->inst->ui.gadget, _part_size);
244    if (!o) return;
245
246    evas_object_geometry_get(icon->o, &x, &y, &w, &h);
247    evas_object_geometry_get(o, &wx, &wy, NULL, NULL);
248    ecore_x_window_move_resize(icon->win, x - wx, y - wy, w, h);
249 }
250
251 static void
252 _systray_icon_cb_move(void *data, Evas *evas __UNUSED__, Evas_Object *o __UNUSED__, void *event __UNUSED__)
253 {
254    Icon *icon = data;
255    _systray_icon_geometry_apply(icon);
256 }
257
258 static void
259 _systray_icon_cb_resize(void *data, Evas *evas __UNUSED__, Evas_Object *o __UNUSED__, void *event __UNUSED__)
260 {
261    Icon *icon = data;
262    _systray_icon_geometry_apply(icon);
263 }
264
265 static Ecore_X_Gravity
266 _systray_gravity(const Instance *inst)
267 {
268    switch (inst->gcc->gadcon->orient)
269      {
270       case E_GADCON_ORIENT_FLOAT:
271          return ECORE_X_GRAVITY_STATIC;
272       case E_GADCON_ORIENT_HORIZ:
273          return ECORE_X_GRAVITY_CENTER;
274       case E_GADCON_ORIENT_VERT:
275          return ECORE_X_GRAVITY_CENTER;
276       case E_GADCON_ORIENT_LEFT:
277          return ECORE_X_GRAVITY_CENTER;
278       case E_GADCON_ORIENT_RIGHT:
279          return ECORE_X_GRAVITY_CENTER;
280       case E_GADCON_ORIENT_TOP:
281          return ECORE_X_GRAVITY_CENTER;
282       case E_GADCON_ORIENT_BOTTOM:
283          return ECORE_X_GRAVITY_CENTER;
284       case E_GADCON_ORIENT_CORNER_TL:
285          return ECORE_X_GRAVITY_S;
286       case E_GADCON_ORIENT_CORNER_TR:
287          return ECORE_X_GRAVITY_S;
288       case E_GADCON_ORIENT_CORNER_BL:
289          return ECORE_X_GRAVITY_N;
290       case E_GADCON_ORIENT_CORNER_BR:
291          return ECORE_X_GRAVITY_N;
292       case E_GADCON_ORIENT_CORNER_LT:
293          return ECORE_X_GRAVITY_E;
294       case E_GADCON_ORIENT_CORNER_RT:
295          return ECORE_X_GRAVITY_W;
296       case E_GADCON_ORIENT_CORNER_LB:
297          return ECORE_X_GRAVITY_E;
298       case E_GADCON_ORIENT_CORNER_RB:
299          return ECORE_X_GRAVITY_W;
300       default:
301          return ECORE_X_GRAVITY_STATIC;
302      }
303 }
304
305 static Evas_Coord
306 _systray_icon_size_normalize(Evas_Coord size)
307 {
308    const Evas_Coord *itr, sizes[] = {
309      16, 22, 24, 32, 36, 48, 64, 72, 96, 128, 192, 256, -1
310    };
311    for (itr = sizes; *itr > 0; itr++)
312      if (*itr == size)
313        return size;
314      else if (*itr > size)
315        {
316           if (itr > sizes)
317             return itr[-1];
318           else
319             return sizes[0];
320        }
321    return sizes[0];
322 }
323
324 static Icon *
325 _systray_icon_add(Instance *inst, const Ecore_X_Window win)
326 {
327    Ecore_X_Gravity gravity;
328    Evas_Object *o;
329    Evas_Coord w, h;
330    Icon *icon;
331
332    edje_object_part_geometry_get(inst->ui.gadget, _part_size,
333                                  NULL, NULL, &w, &h);
334    if (w > h)
335      w = h;
336    else
337      h = w;
338
339    w = h = _systray_icon_size_normalize(w);
340
341    o = evas_object_rectangle_add(inst->evas);
342    if (!o)
343      return NULL;
344    evas_object_color_set(o, 0, 0, 0, 0);
345    evas_object_resize(o, w, h);
346    evas_object_show(o);
347
348    icon = malloc(sizeof(*icon));
349    if (!icon)
350      {
351         evas_object_del(o);
352         return NULL;
353      }
354    icon->win = win;
355    icon->inst = inst;
356    icon->o = o;
357
358    gravity = _systray_gravity(inst);
359    ecore_x_icccm_size_pos_hints_set(win, 1, gravity,
360                                     w, h, w, h, w, h, 0, 0,
361                                     1.0, (double)w / (double)h);
362
363    ecore_x_window_reparent(win, inst->win.base, 0, 0);
364    ecore_x_window_resize(win, w, h);
365    ecore_x_window_raise(win);
366    ecore_x_window_client_manage(win);
367    ecore_x_window_save_set_add(win);
368    ecore_x_window_shape_events_select(win, 1);
369
370    //ecore_x_window_geometry_get(win, NULL, NULL, &w, &h);
371
372    evas_object_event_callback_add
373      (o, EVAS_CALLBACK_MOVE, _systray_icon_cb_move, icon);
374    evas_object_event_callback_add
375      (o, EVAS_CALLBACK_RESIZE, _systray_icon_cb_resize, icon);
376
377    inst->icons = eina_list_append(inst->icons, icon);
378    edje_object_part_box_append(inst->ui.gadget, _part_box, o);
379    _systray_size_apply_do(inst);
380    _systray_icon_geometry_apply(icon);
381
382    ecore_x_window_show(win);
383
384    return icon;
385 }
386
387 static void
388 _systray_icon_del_list(Instance *inst, Eina_List *l, Icon *icon)
389 {
390    inst->icons = eina_list_remove_list(inst->icons, l);
391
392    ecore_x_window_save_set_del(icon->win);
393    ecore_x_window_reparent(icon->win, 0, 0, 0);
394    evas_object_del(icon->o);
395    free(icon);
396
397    _systray_size_apply(inst);
398 }
399
400 static Ecore_X_Atom
401 _systray_atom_st_get(int screen_num)
402 {
403    if ((_last_st_num == -1) || (_last_st_num != screen_num))
404      {
405         char buf[32];
406         snprintf(buf, sizeof(buf), "_NET_SYSTEM_TRAY_S%d", screen_num);
407         _atom_st_num = ecore_x_atom_get(buf);
408         _last_st_num = screen_num;
409      }
410
411    return _atom_st_num;
412 }
413
414 static Eina_Bool
415 _systray_selection_owner_set(int screen_num, Ecore_X_Window win)
416 {
417    Ecore_X_Atom atom;
418    Ecore_X_Window cur_selection;
419    Eina_Bool ret;
420
421    atom = _systray_atom_st_get(screen_num);
422    ecore_x_selection_owner_set(win, atom, ecore_x_current_time_get());
423    ecore_x_sync();
424    cur_selection = ecore_x_selection_owner_get(atom);
425
426    ret = (cur_selection == win);
427    if (!ret)
428      fprintf(stderr, "SYSTRAY: tried to set selection to %#x, but got %#x\n",
429              win, cur_selection);
430
431    return ret;
432 }
433
434 static Eina_Bool
435 _systray_selection_owner_set_current(Instance *inst)
436 {
437    return _systray_selection_owner_set
438      (inst->con->manager->num, inst->win.selection);
439 }
440
441 static void
442 _systray_deactivate(Instance *inst)
443 {
444    Ecore_X_Window old;
445
446    if (inst->win.selection == 0) return;
447
448    edje_object_signal_emit(inst->ui.gadget, _sig_disable, _sig_source);
449
450    while (inst->icons)
451      _systray_icon_del_list(inst, inst->icons, inst->icons->data);
452
453    old = inst->win.selection;
454    inst->win.selection = 0;
455    _systray_selection_owner_set_current(inst);
456    ecore_x_sync();
457    ecore_x_window_free(old);
458    ecore_x_window_free(inst->win.base);
459    inst->win.base = 0;
460 }
461
462 static Eina_Bool
463 _systray_base_create(Instance *inst)
464 {
465    const Evas_Object *o;
466    Evas_Coord x, y, w, h;
467    unsigned short r, g, b;
468    const char *color;
469
470    color = edje_object_data_get(inst->ui.gadget, inst->gcc->style);
471    if (!color)
472      color = edje_object_data_get(inst->ui.gadget, "default");
473
474    if (color && (sscanf(color, "%hu %hu %hu", &r, &g, &b) == 3))
475      {
476         r = (65535 * (unsigned int)r) / 255;
477         g = (65535 * (unsigned int)g) / 255;
478         b = (65535 * (unsigned int)b) / 255;
479      }
480    else
481      r = g = b = (unsigned short)65535;
482
483    o = edje_object_part_object_get(inst->ui.gadget, _part_size);
484    if (!o)
485      return 0;
486
487    evas_object_geometry_get(o, &x, &y, &w, &h);
488    if (w < 1) w = 1;
489    if (h < 1) h = 1;
490    inst->win.base = ecore_x_window_new(0, 0, 0, w, h);
491    ecore_x_window_reparent(inst->win.base, inst->win.parent, x, y); 
492    ecore_x_window_background_color_set(inst->win.base, r, g, b);
493    ecore_x_window_show(inst->win.base);
494    return 1;
495 }
496
497 static Eina_Bool
498 _systray_activate(Instance *inst)
499 {
500    unsigned int visual;
501    Ecore_X_Atom atom;
502    Ecore_X_Window old_win;
503    Ecore_X_Window_Attributes attr;
504
505    if (inst->win.selection != 0) return 1;
506
507    atom = _systray_atom_st_get(inst->con->manager->num);
508    old_win = ecore_x_selection_owner_get(atom);
509    if (old_win != 0) return 0;
510
511    if (inst->win.base == 0)
512      {
513         if (!_systray_base_create(inst))
514           return 0;
515      }
516
517    inst->win.selection = ecore_x_window_input_new(inst->win.base, 0, 0, 1, 1);
518    if (inst->win.selection == 0)
519      {
520         ecore_x_window_free(inst->win.base);
521         inst->win.base = 0;
522         return 0;
523      }
524
525    if (!_systray_selection_owner_set_current(inst))
526      {
527         ecore_x_window_free(inst->win.selection);
528         inst->win.selection = 0;
529         ecore_x_window_free(inst->win.base);
530         inst->win.base = 0;
531         return 0;
532      }
533
534    ecore_x_window_attributes_get(inst->win.base, &attr);
535
536    visual = ecore_x_visual_id_get(attr.visual);
537    ecore_x_window_prop_card32_set(inst->win.selection, _atom_st_visual,
538                                   (void *)&visual, 1);
539
540    ecore_x_client_message32_send(inst->con->manager->root, _atom_manager,
541                                  ECORE_X_EVENT_MASK_WINDOW_CONFIGURE,
542                                  ecore_x_current_time_get(), atom,
543                                  inst->win.selection, 0, 0);
544
545    edje_object_signal_emit(inst->ui.gadget, _sig_enable, _sig_source);
546
547    return 1;
548 }
549
550 static Eina_Bool
551 _systray_activate_retry(void *data)
552 {
553    Instance *inst = data;
554    Eina_Bool ret;
555
556    fputs("SYSTRAY: reactivate...\n", stderr);
557    ret = _systray_activate(inst);
558    if (ret)
559      fputs("SYSTRAY: activate success!\n", stderr);
560    else
561      fprintf(stderr, "SYSTRAY: activate failure! retrying in %0.1f seconds\n",
562              RETRY_TIMEOUT);
563
564    if (!ret)
565      return ECORE_CALLBACK_RENEW;
566
567    inst->timer.retry = NULL;
568    return ECORE_CALLBACK_CANCEL;
569 }
570
571 static void
572 _systray_retry(Instance *inst)
573 {
574    if (inst->timer.retry) return;
575    inst->timer.retry = ecore_timer_add
576      (RETRY_TIMEOUT, _systray_activate_retry, inst);
577 }
578
579 static Eina_Bool
580 _systray_activate_retry_first(void *data)
581 {
582    Instance *inst = data;
583    Eina_Bool ret;
584
585    fputs("SYSTRAY: reactivate (first time)...\n", stderr);
586    ret = _systray_activate(inst);
587    if (ret)
588      {
589         fputs("SYSTRAY: activate success!\n", stderr);
590         inst->timer.retry = NULL;
591         return ECORE_CALLBACK_CANCEL;
592      }
593
594    edje_object_signal_emit(inst->ui.gadget, _sig_disable, _sig_source);
595
596    fprintf(stderr, "SYSTRAY: activate failure! retrying in %0.1f seconds\n",
597            RETRY_TIMEOUT);
598
599    inst->timer.retry = NULL;
600    _systray_retry(inst);
601    return ECORE_CALLBACK_CANCEL;
602 }
603
604 static void
605 _systray_handle_request_dock(Instance *inst, Ecore_X_Event_Client_Message *ev)
606 {
607    Ecore_X_Window win = (Ecore_X_Window)ev->data.l[2];
608    Ecore_X_Time time;
609    Ecore_X_Window_Attributes attr;
610    const Eina_List *l;
611    Icon *icon;
612    unsigned int val[2];
613    int r;
614
615    EINA_LIST_FOREACH(inst->icons, l, icon)
616      if (icon->win == win)
617        return;
618
619    if (!ecore_x_window_attributes_get(win, &attr))
620      {
621         fprintf(stderr, "SYSTRAY: could not get attributes of win %#x\n", win);
622         return;
623      }
624
625    icon = _systray_icon_add(inst, win);
626    if (!icon)
627      return;
628
629    r  = ecore_x_window_prop_card32_get(win, _atom_xembed_info, val, 2);
630    if (r < 2)
631      {
632         /*
633         fprintf(stderr, "SYSTRAY: win %#x does not support _XEMBED_INFO (%d)\n",
634                 win, r);
635         */
636         return;
637      }
638
639    time = ecore_x_current_time_get();
640    ecore_x_client_message32_send(win, _atom_xembed,
641                                  ECORE_X_EVENT_MASK_NONE,
642                                  time, XEMBED_EMBEDDED_NOTIFY, 0,
643                                  inst->win.selection, 0);
644 }
645
646 static void
647 _systray_handle_op_code(Instance *inst, Ecore_X_Event_Client_Message *ev)
648 {
649    unsigned long message = ev->data.l[1];
650
651    switch (message)
652      {
653       case SYSTEM_TRAY_REQUEST_DOCK:
654          _systray_handle_request_dock(inst, ev);
655          break;
656       case SYSTEM_TRAY_BEGIN_MESSAGE:
657       case SYSTEM_TRAY_CANCEL_MESSAGE:
658          fputs("SYSTRAY TODO: handle messages (anyone uses this?)\n", stderr);
659          break;
660       default:
661          fprintf(stderr,
662                  "SYSTRAY: error, unknown message op code: %ld, win: %#lx\n",
663                  message, ev->data.l[2]);
664      }
665 }
666
667 static void
668 _systray_handle_message(Instance *inst __UNUSED__, Ecore_X_Event_Client_Message *ev)
669 {
670    fprintf(stderr, "SYSTRAY TODO: message op: %ld, data: %ld, %ld, %ld\n",
671            ev->data.l[1], ev->data.l[2], ev->data.l[3], ev->data.l[4]);
672 }
673
674 static void
675 _systray_handle_xembed(Instance *inst __UNUSED__, Ecore_X_Event_Client_Message *ev __UNUSED__)
676 {
677    unsigned long message = ev->data.l[1];
678
679    switch (message)
680      {
681       case XEMBED_EMBEDDED_NOTIFY:
682       case XEMBED_WINDOW_ACTIVATE:
683       case XEMBED_WINDOW_DEACTIVATE:
684       case XEMBED_REQUEST_FOCUS:
685       case XEMBED_FOCUS_IN:
686       case XEMBED_FOCUS_OUT:
687       case XEMBED_FOCUS_NEXT:
688       case XEMBED_FOCUS_PREV:
689       case XEMBED_MODALITY_ON:
690       case XEMBED_MODALITY_OFF:
691       case XEMBED_REGISTER_ACCELERATOR:
692       case XEMBED_UNREGISTER_ACCELERATOR:
693       case XEMBED_ACTIVATE_ACCELERATOR:
694       default:
695          fprintf(stderr,
696                  "SYSTRAY: unsupported xembed: %#lx, %#lx, %#lx, %#lx\n",
697                  ev->data.l[1], ev->data.l[2], ev->data.l[3], ev->data.l[4]);
698      }
699 }
700
701 static Eina_Bool
702 _systray_cb_client_message(void *data, int type __UNUSED__, void *event)
703 {
704    Ecore_X_Event_Client_Message *ev = event;
705    Instance *inst = data;
706
707    if (ev->message_type == _atom_st_op_code)
708      _systray_handle_op_code(inst, ev);
709    else if (ev->message_type == _atom_st_msg_data)
710      _systray_handle_message(inst, ev);
711    else if (ev->message_type == _atom_xembed)
712      _systray_handle_xembed(inst, ev);
713
714    return ECORE_CALLBACK_PASS_ON;
715 }
716
717 static Eina_Bool
718 _systray_cb_window_destroy(void *data, int type __UNUSED__, void *event)
719 {
720    Ecore_X_Event_Window_Destroy *ev = event;
721    Instance *inst = data;
722    Icon *icon;
723    Eina_List *l;
724
725    EINA_LIST_FOREACH(inst->icons, l, icon)
726      if (icon->win == ev->win)
727        {
728           _systray_icon_del_list(inst, l, icon);
729           break;
730        }
731
732    return ECORE_CALLBACK_PASS_ON;
733 }
734
735 static Eina_Bool
736 _systray_cb_window_show(void *data, int type __UNUSED__, void *event)
737 {
738    Ecore_X_Event_Window_Show *ev = event;
739    Instance *inst = data;
740    Icon *icon;
741    Eina_List *l;
742
743    EINA_LIST_FOREACH(inst->icons, l, icon)
744      if (icon->win == ev->win)
745        {
746           _systray_icon_geometry_apply(icon);
747           break;
748        }
749
750    return ECORE_CALLBACK_PASS_ON;
751 }
752
753 static Eina_Bool
754 _systray_cb_window_configure(void *data, int type __UNUSED__, void *event)
755 {
756    Ecore_X_Event_Window_Configure *ev = event;
757    Instance *inst = data;
758    Icon *icon;
759    const Eina_List *l;
760
761    EINA_LIST_FOREACH(inst->icons, l, icon)
762      if (icon->win == ev->win)
763        {
764           _systray_icon_geometry_apply(icon);
765           break;
766        }
767
768    return ECORE_CALLBACK_PASS_ON;
769 }
770
771 static Eina_Bool
772 _systray_cb_reparent_notify(void *data, int type __UNUSED__, void *event)
773 {
774    Ecore_X_Event_Window_Reparent *ev = event;
775    Instance *inst = data;
776    Icon *icon;
777    Eina_List *l;
778
779    EINA_LIST_FOREACH(inst->icons, l, icon)
780      if ((icon->win == ev->win) && (ev->parent != inst->win.base))
781        {
782           _systray_icon_del_list(inst, l, icon);
783           break;
784        }
785
786    return ECORE_CALLBACK_PASS_ON;
787 }
788
789 static Eina_Bool
790 _systray_cb_selection_clear(void *data, int type __UNUSED__, void *event)
791 {
792    Ecore_X_Event_Selection_Clear *ev = event;
793    Instance *inst = data;
794
795    if ((ev->win == inst->win.selection) && (inst->win.selection != 0) &&
796        (ev->atom == _systray_atom_st_get(inst->con->manager->num)))
797      {
798         edje_object_signal_emit(inst->ui.gadget, _sig_disable, _sig_source);
799
800         while (inst->icons)
801           _systray_icon_del_list(inst, inst->icons, inst->icons->data);
802
803         ecore_x_window_free(inst->win.selection);
804         inst->win.selection = 0;
805         ecore_x_window_free(inst->win.base);
806         inst->win.base = 0;
807         _systray_retry(inst);
808      }
809    return ECORE_CALLBACK_PASS_ON;
810 }
811
812 static void
813 _systray_theme(Evas_Object *o, const char *shelf_style, const char *gc_style)
814 {
815    const char base_theme[] = "base/theme/modules/systray";
816    const char *path = _systray_theme_path();
817    char buf[128], *p;
818    size_t len, avail;
819
820    len = eina_strlcpy(buf, _group_gadget, sizeof(buf));
821    if (len >= sizeof(buf))
822      goto fallback;
823    p = buf + len;
824    *p = '/';
825    p++;
826    avail = sizeof(buf) - len - 1;
827
828    if (shelf_style && gc_style)
829      {
830         size_t r;
831         r = snprintf(p, avail, "%s/%s", shelf_style, gc_style);
832         if (r < avail && e_theme_edje_object_set(o, base_theme, buf))
833           return;
834      }
835
836    if (shelf_style)
837      {
838         size_t r;
839         r = eina_strlcpy(p, shelf_style, avail);
840         if (r < avail && e_theme_edje_object_set(o, base_theme, buf))
841           return;
842      }
843
844    if (gc_style)
845      {
846         size_t r;
847         r = eina_strlcpy(p, gc_style, avail);
848         if (r < avail && e_theme_edje_object_set(o, base_theme, buf))
849           return;
850      }
851
852    if (e_theme_edje_object_set(o, base_theme, _group_gadget))
853      return;
854
855    if (shelf_style && gc_style)
856      {
857         size_t r;
858         r = snprintf(p, avail, "%s/%s", shelf_style, gc_style);
859         if (r < avail && edje_object_file_set(o, path, buf))
860           return;
861      }
862
863    if (shelf_style)
864      {
865         size_t r;
866         r = eina_strlcpy(p, shelf_style, avail);
867         if (r < avail && edje_object_file_set(o, path, buf))
868           return;
869      }
870
871    if (gc_style)
872      {
873         size_t r;
874         r = eina_strlcpy(p, gc_style, avail);
875         if (r < avail && edje_object_file_set(o, path, buf))
876           return;
877      }
878
879  fallback:
880    edje_object_file_set(o, path, _group_gadget);
881 }
882
883
884 static E_Gadcon_Client *
885 _gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style)
886 {
887    Instance *inst;
888
889    // fprintf(stderr, "SYSTRAY: init name=%s, id=%s, style=%s\n", name, id, style);
890
891    if (!systray_mod)
892      return NULL;
893    if ((!id) || (instance))
894      {
895         e_util_dialog_internal
896           (_("Another systray exists"),
897            _("There can be only one systray gadget and "
898              "another one already exists."));
899         return NULL;
900      }
901
902    if ((gc->shelf) && (!gc->shelf->popup))
903      {
904         e_util_dialog_internal
905           (_("Systray Error"),
906            _("Systray cannot work in a shelf that is set to below everything."));
907         return NULL;
908      }
909
910    inst = E_NEW(Instance, 1);
911    if (!inst)
912      return NULL;
913    inst->evas = gc->evas;
914    inst->con = e_container_current_get(e_manager_current_get());
915    if (!inst->con)
916      {
917         E_FREE(inst);
918         return NULL;
919      }
920
921    if ((gc->shelf) && (gc->shelf->popup))
922      inst->win.parent = gc->shelf->popup->evas_win;
923    else
924      inst->win.parent = (Ecore_X_Window) ecore_evas_window_get(gc->ecore_evas);
925
926    inst->win.base = 0;
927    inst->win.selection = 0;
928
929    inst->ui.gadget = edje_object_add(inst->evas);
930
931    _systray_theme(inst->ui.gadget, gc->shelf ? gc->shelf->style : NULL, style);
932
933    inst->gcc = e_gadcon_client_new(gc, name, id, style, inst->ui.gadget);
934    if (!inst->gcc)
935      {
936         evas_object_del(inst->ui.gadget);
937         E_FREE(inst);
938         return NULL;
939      }
940
941    inst->gcc->data = inst;
942
943    if (!_systray_activate(inst))
944      {
945         if (!inst->timer.retry)
946           inst->timer.retry = ecore_timer_add
947             (0.1, _systray_activate_retry_first, inst);
948         else
949           edje_object_signal_emit(inst->ui.gadget, _sig_disable, _sig_source);
950      }
951
952    evas_object_event_callback_add(inst->ui.gadget, EVAS_CALLBACK_MOUSE_DOWN,
953                                   _systray_cb_mouse_down, inst);
954    evas_object_event_callback_add(inst->ui.gadget, EVAS_CALLBACK_MOVE,
955                                   _systray_cb_move, inst);
956    evas_object_event_callback_add(inst->ui.gadget, EVAS_CALLBACK_RESIZE,
957                                   _systray_cb_resize, inst);
958
959    inst->handler.message = ecore_event_handler_add
960      (ECORE_X_EVENT_CLIENT_MESSAGE, _systray_cb_client_message, inst);
961    inst->handler.destroy = ecore_event_handler_add
962      (ECORE_X_EVENT_WINDOW_DESTROY, _systray_cb_window_destroy, inst);
963    inst->handler.show = ecore_event_handler_add
964      (ECORE_X_EVENT_WINDOW_SHOW, _systray_cb_window_show, inst);
965    inst->handler.reparent = ecore_event_handler_add
966      (ECORE_X_EVENT_WINDOW_REPARENT, _systray_cb_reparent_notify, inst);
967    inst->handler.sel_clear = ecore_event_handler_add
968      (ECORE_X_EVENT_SELECTION_CLEAR, _systray_cb_selection_clear, inst);
969    inst->handler.configure = ecore_event_handler_add
970      (ECORE_X_EVENT_WINDOW_CONFIGURE, _systray_cb_window_configure, inst);
971
972    instance = inst;
973    return inst->gcc;
974 }
975
976 /* Called when Gadget_Container says stop */
977 static void
978 _gc_shutdown(E_Gadcon_Client *gcc)
979 {
980    Instance *inst = gcc->data;
981
982    // fprintf(stderr, "SYSTRAY: shutdown %p, inst=%p\n", gcc, inst);
983
984    if (!inst)
985      return;
986
987    if (inst->menu)
988      {
989         e_menu_post_deactivate_callback_set(inst->menu, NULL, NULL);
990         e_object_del(E_OBJECT(inst->menu));
991      }
992
993    _systray_deactivate(inst);
994    evas_object_del(inst->ui.gadget);
995
996    if (inst->handler.message)
997      ecore_event_handler_del(inst->handler.message);
998    if (inst->handler.destroy)
999      ecore_event_handler_del(inst->handler.destroy);
1000    if (inst->handler.show)
1001      ecore_event_handler_del(inst->handler.show);
1002    if (inst->handler.reparent)
1003      ecore_event_handler_del(inst->handler.reparent);
1004    if (inst->handler.sel_clear)
1005      ecore_event_handler_del(inst->handler.sel_clear);
1006    if (inst->handler.configure)
1007      ecore_event_handler_del(inst->handler.configure);
1008    if (inst->timer.retry)
1009      ecore_timer_del(inst->timer.retry);
1010    if (inst->job.size_apply)
1011      ecore_job_del(inst->job.size_apply);
1012
1013    if (instance == inst)
1014      instance = NULL;
1015
1016    E_FREE(inst);
1017    gcc->data = NULL;
1018 }
1019
1020 static void
1021 _gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient)
1022 {
1023    Instance *inst = gcc->data;
1024    const char *signal;
1025    unsigned int systray_orient;
1026
1027    if (!inst)
1028      return;
1029
1030    switch (orient)
1031      {
1032       case E_GADCON_ORIENT_FLOAT:
1033          signal = "e,action,orient,float";
1034          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
1035          break;
1036       case E_GADCON_ORIENT_HORIZ:
1037          signal = "e,action,orient,horiz";
1038          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
1039          break;
1040       case E_GADCON_ORIENT_VERT:
1041          signal = "e,action,orient,vert";
1042          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_VERT;
1043          break;
1044       case E_GADCON_ORIENT_LEFT:
1045          signal = "e,action,orient,left";
1046          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_VERT;
1047          break;
1048       case E_GADCON_ORIENT_RIGHT:
1049          signal = "e,action,orient,right";
1050          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_VERT;
1051          break;
1052       case E_GADCON_ORIENT_TOP:
1053          signal = "e,action,orient,top";
1054          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
1055          break;
1056       case E_GADCON_ORIENT_BOTTOM:
1057          signal = "e,action,orient,bottom";
1058          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
1059          break;
1060       case E_GADCON_ORIENT_CORNER_TL:
1061          signal = "e,action,orient,corner_tl";
1062          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
1063          break;
1064       case E_GADCON_ORIENT_CORNER_TR:
1065          signal = "e,action,orient,corner_tr";
1066          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
1067          break;
1068       case E_GADCON_ORIENT_CORNER_BL:
1069          signal = "e,action,orient,corner_bl";
1070          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
1071          break;
1072       case E_GADCON_ORIENT_CORNER_BR:
1073          signal = "e,action,orient,corner_br";
1074          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
1075          break;
1076       case E_GADCON_ORIENT_CORNER_LT:
1077          signal = "e,action,orient,corner_lt";
1078          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_VERT;
1079          break;
1080       case E_GADCON_ORIENT_CORNER_RT:
1081          signal = "e,action,orient,corner_rt";
1082          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_VERT;
1083          break;
1084       case E_GADCON_ORIENT_CORNER_LB:
1085          signal = "e,action,orient,corner_lb";
1086          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_VERT;
1087          break;
1088       case E_GADCON_ORIENT_CORNER_RB:
1089          signal = "e,action,orient,corner_rb";
1090          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_VERT;
1091          break;
1092       default:
1093          signal = "e,action,orient,horiz";
1094          systray_orient = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
1095      }
1096
1097    ecore_x_window_prop_card32_set
1098      (inst->win.selection, _atom_st_orient, &systray_orient, 1);
1099
1100    edje_object_signal_emit(inst->ui.gadget, signal, _sig_source);
1101    edje_object_message_signal_process(inst->ui.gadget);
1102    _systray_size_apply(inst);
1103 }
1104
1105 static const char *
1106 _gc_label(E_Gadcon_Client_Class *client_class __UNUSED__)
1107 {
1108    return _("Systray");
1109 }
1110
1111 static Evas_Object *
1112 _gc_icon(E_Gadcon_Client_Class *client_class __UNUSED__, Evas *evas)
1113 {
1114    Evas_Object *o;
1115
1116    o = edje_object_add(evas);
1117    edje_object_file_set(o, _systray_theme_path(), "icon");
1118    return o;
1119 }
1120
1121 static const char *
1122 _gc_id_new(E_Gadcon_Client_Class *client_class __UNUSED__)
1123 {
1124    if (!instance)
1125      return _name;
1126    else
1127      return NULL;
1128 }
1129
1130 static const E_Gadcon_Client_Class _gc_class =
1131 {
1132   GADCON_CLIENT_CLASS_VERSION, _name,
1133   {
1134      _gc_init, _gc_shutdown, _gc_orient, _gc_label, _gc_icon, _gc_id_new, NULL,
1135      _systray_site_is_safe
1136   },
1137   E_GADCON_CLIENT_STYLE_INSET
1138 };
1139
1140 EAPI E_Module_Api e_modapi = {E_MODULE_API_VERSION, _Name};
1141
1142 EAPI void *
1143 e_modapi_init(E_Module *m)
1144 {
1145    systray_mod = m;
1146
1147    e_gadcon_provider_register(&_gc_class);
1148
1149    if (!_atom_manager)
1150      _atom_manager = ecore_x_atom_get("MANAGER");
1151    if (!_atom_st_orient)
1152      _atom_st_orient = ecore_x_atom_get("_NET_SYSTEM_TRAY_ORIENTATION");
1153    if (!_atom_st_visual)
1154      _atom_st_visual = ecore_x_atom_get("_NET_SYSTEM_TRAY_VISUAL");
1155    if (!_atom_st_op_code)
1156      _atom_st_op_code = ecore_x_atom_get("_NET_SYSTEM_TRAY_OPCODE");
1157    if (!_atom_st_msg_data)
1158      _atom_st_msg_data = ecore_x_atom_get("_NET_SYSTEM_TRAY_MESSAGE_DATA");
1159    if (!_atom_xembed)
1160      _atom_xembed = ecore_x_atom_get("_XEMBED");
1161    if (!_atom_xembed_info)
1162      _atom_xembed_info = ecore_x_atom_get("_XEMBED_INFO");
1163
1164    return m;
1165 }
1166
1167 EAPI int
1168 e_modapi_shutdown(E_Module *m __UNUSED__)
1169 {
1170    e_gadcon_provider_unregister(&_gc_class);
1171    systray_mod = NULL;
1172    return 1;
1173 }
1174
1175 EAPI int
1176 e_modapi_save(E_Module *m __UNUSED__)
1177 {
1178    return 1;
1179 }