chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / systray.c
1 /*
2  * Copyright (C) 2004-2009 Kim Woelders
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies of the Software, its documentation and marketing & publicity
13  * materials, and acknowledgment shall be given in the documentation, materials
14  * and software packages that this Software was used.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 #include "E.h"
24 #include "container.h"
25 #include "e16-ecore_hints.h"
26 #include "events.h"
27 #include "ewins.h"
28 #include "hints.h"
29 #include "xwin.h"
30
31 #define DEBUG_SYSTRAY 0
32
33 /* Systray object info */
34 typedef struct {
35    Win                 win;
36    char                mapped;
37 } SWin;
38
39 #define StObjGetWin(o) (((SWin*)(o))->win)
40 #define StObjIsMapped(o) (((SWin*)(o))->mapped)
41
42 /* XEmbed atoms */
43 static Atom         E_XA__XEMBED = 0;
44 static Atom         E_XA__XEMBED_INFO = 0;
45
46 /* Systray atoms */
47 static Atom         _NET_SYSTEM_TRAY_OPCODE = 0;
48 static Atom         _NET_SYSTEM_TRAY_MESSAGE_DATA = 0;
49
50 /* Systray selection */
51 static ESelection  *systray_sel = NULL;
52
53 static void         SystrayItemEvent(Win win, XEvent * ev, void *prm);
54
55 #define SYSTEM_TRAY_REQUEST_DOCK    0
56 #define SYSTEM_TRAY_BEGIN_MESSAGE   1
57 #define SYSTEM_TRAY_CANCEL_MESSAGE  2
58
59 /* _XEMBED client message */
60 #define XEMBED_EMBEDDED_NOTIFY      0
61
62 /* _XEMBED_INFO property */
63 #define XEMBED_MAPPED               (1 << 0)
64
65 static int
66 SystrayGetXembedInfo(Window win, int *info)
67 {
68    unsigned char      *prop_ret;
69    Atom                type_ret;
70    unsigned long       bytes_after, num_ret;
71    int                 format_ret;
72
73    prop_ret = NULL;
74    if (XGetWindowProperty(disp, win, E_XA__XEMBED_INFO, 0, 0x7fffffff,
75                           False, E_XA__XEMBED_INFO, &type_ret, &format_ret,
76                           &num_ret, &bytes_after, &prop_ret) != Success)
77       return -1;
78
79    if (prop_ret && type_ret == E_XA__XEMBED_INFO && format_ret == 32
80        && num_ret >= 2)
81      {
82         info[0] = ((unsigned long *)prop_ret)[0];
83         info[1] = ((unsigned long *)prop_ret)[1];
84      }
85    else
86      {
87         /* Property invalid or not there. I doubt we ever get here */
88         info[0] = 0;            /* Set protocol version 0 */
89         info[1] = 1;            /* Set mapped */
90         num_ret = 0;
91      }
92    if (prop_ret)
93       XFree(prop_ret);
94
95    return num_ret;
96 }
97
98 /*
99  * Return index, -1 if not found.
100  */
101 static int
102 SystrayObjFind(Container * ct, Window win)
103 {
104    int                 i;
105
106    for (i = 0; i < ct->num_objs; i++)
107       if (win == WinGetXwin(StObjGetWin(ct->objs[i].obj)))
108          return i;
109
110    return -1;
111 }
112
113 static              Win
114 SystrayObjManage(Container * ct, Window xwin)
115 {
116    Win                 win;
117
118 #if DEBUG_SYSTRAY
119    Eprintf("SystrayObjManage %#lx\n", xwin);
120 #endif
121    win = ERegisterWindow(xwin, NULL);
122    if (win == NULL)
123       return win;
124
125    ESelectInput(win, StructureNotifyMask | PropertyChangeMask);
126    EventCallbackRegister(win, 0, SystrayItemEvent, ct);
127    EReparentWindow(win, ct->icon_win, 0, 0);
128    XAddToSaveSet(disp, xwin);
129
130    return win;
131 }
132
133 static void
134 SystrayObjUnmanage(Container * ct __UNUSED__, Win win, int gone)
135 {
136 #if DEBUG_SYSTRAY
137    Eprintf("SystrayObjUnmanage %#lx gone=%d\n", WinGetXwin(win), gone);
138 #endif
139
140    if (!gone)
141      {
142         ESelectInput(win, NoEventMask);
143         EUnmapWindow(win);
144         EReparentWindow(win, VROOT, 0, 0);
145         XRemoveFromSaveSet(disp, WinGetXwin(win));
146      }
147    EventCallbackUnregister(win, 0, SystrayItemEvent, ct);
148    EUnregisterWindow(win);
149 }
150
151 static void
152 SystrayObjAdd(Container * ct, Window xwin)
153 {
154    SWin               *swin = NULL;
155    Win                 win;
156    int                 xembed_info[2];
157
158    /* Not if already there */
159    if (SystrayObjFind(ct, xwin) >= 0)
160       return;
161
162    EGrabServer();
163
164    switch (SystrayGetXembedInfo(xwin, xembed_info))
165      {
166      case -1:                   /* Error - assume invalid window */
167         Eprintf("SystrayObjAdd: Hmm.. Invalid window? Ignoring %#lx\n", xwin);
168         goto bail_out;
169      case 0:                    /* Assume broken - proceed anyway */
170         Eprintf("SystrayObjAdd: Hmm.. No _XEMBED_INFO?\n");
171         break;
172      default:
173         if (EDebug(EDBUG_TYPE_ICONBOX))
174            Eprintf("SystrayObjAdd: _XEMBED_INFO: %#lx: %d %d\n", xwin,
175                    xembed_info[0], xembed_info[1]);
176         break;
177      }
178
179    swin = EMALLOC(SWin, 1);
180    if (!swin)
181       goto bail_out;
182
183    if (ContainerObjectAdd(ct, swin) < 0)
184       goto bail_out;
185
186    win = SystrayObjManage(ct, xwin);
187    if (win == NULL)
188       goto bail_out;
189
190    swin->win = win;
191    swin->mapped = (xembed_info[1] & XEMBED_MAPPED) != 0;
192    if (swin->mapped)
193       EMapWindow(win);
194
195    /* TBD - Always set protocol version as reported by client */
196    ecore_x_client_message32_send(xwin, E_XA__XEMBED, NoEventMask,
197                                  CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0,
198                                  xwin, xembed_info[0]);
199
200    EUngrabServer();
201
202    return;                      /* Success */
203
204  bail_out:
205    EUngrabServer();
206    if (!swin)
207       return;
208    ContainerObjectDel(ct, swin);
209    Efree(swin);
210 }
211
212 static void
213 SystrayObjDel(Container * ct, Win win, int gone)
214 {
215    int                 i;
216    SWin               *swin;
217
218    i = SystrayObjFind(ct, WinGetXwin(win));
219    if (i < 0)
220       return;
221
222    if (EDebug(EDBUG_TYPE_ICONBOX))
223       Eprintf("SystrayObjDel %#lx\n", WinGetXwin(win));
224
225    swin = (SWin *) ct->objs[i].obj;
226
227    ContainerObjectDel(ct, swin);
228
229    if (disp)
230       SystrayObjUnmanage(ct, swin->win, gone);
231
232    Efree(swin);
233 }
234
235 static void
236 SystrayObjMapUnmap(Container * ct, Window win)
237 {
238    int                 i, map;
239    SWin               *swin;
240    int                 xembed_info[2];
241
242    i = SystrayObjFind(ct, win);
243    if (i < 0)
244       return;
245
246    swin = (SWin *) ct->objs[i].obj;
247
248    if (SystrayGetXembedInfo(win, xembed_info) >= 0)
249      {
250         if (EDebug(EDBUG_TYPE_ICONBOX))
251            Eprintf("SystrayObjMapUnmap: _XEMBED_INFO: %#lx: %d %d\n", win,
252                    xembed_info[0], xembed_info[1]);
253
254         map = (xembed_info[1] & XEMBED_MAPPED) != 0;
255         if (map == swin->mapped)
256            return;
257
258         if (map)
259            EMapWindow(swin->win);
260         else
261            EUnmapWindow(swin->win);
262      }
263    else
264      {
265         if (EDebug(EDBUG_TYPE_ICONBOX))
266            Eprintf("SystrayObjMapUnmap: _XEMBED_INFO: %#lx: gone?\n", win);
267
268         map = 0;
269         if (map == swin->mapped)
270            return;
271      }
272
273    swin->mapped = map;
274    ContainerRedraw(ct);
275 }
276
277 static void
278 SystrayEventClientMessage(Container * ct, XClientMessageEvent * ev)
279 {
280    Window              win;
281
282    if (EDebug(EDBUG_TYPE_ICONBOX))
283       Eprintf
284          ("SystrayEventClientMessage: ev->type=%ld ev->data.l: %#lx %#lx %#lx %#lx\n",
285           ev->message_type, ev->data.l[0], ev->data.l[1], ev->data.l[2],
286           ev->data.l[3]);
287
288    if (ev->message_type == _NET_SYSTEM_TRAY_OPCODE)
289      {
290         win = ev->data.l[2];
291         if (win == None)
292            goto done;
293
294         SystrayObjAdd(ct, win);
295      }
296    else if (ev->message_type == _NET_SYSTEM_TRAY_MESSAGE_DATA)
297      {
298         if (EDebug(EDBUG_TYPE_ICONBOX))
299            Eprintf("SystrayEventClientMessage: Got data message\n");
300      }
301  done:
302    ;
303 }
304
305 static void
306 SystrayEventClientProperty(Container * ct, XPropertyEvent * ev)
307 {
308    if (EDebug(EDBUG_TYPE_ICONBOX))
309       Eprintf("SystrayEventClientProperty %#lx %ld\n", ev->window, ev->atom);
310
311    if (ev->atom == E_XA__XEMBED_INFO)
312      {
313         SystrayObjMapUnmap(ct, ev->window);
314      }
315 }
316
317 static void
318 SystraySelectionEvent(Win win __UNUSED__, XEvent * ev, void *prm)
319 {
320    if (EDebug(EDBUG_TYPE_ICONBOX))
321       Eprintf("SystraySelectionEvent %2d %#lx\n", ev->type, ev->xany.window);
322
323    switch (ev->type)
324      {
325      default:
326         Eprintf(" ??? SystraySelectionEvent %2d %#lx\n", ev->type,
327                 ev->xany.window);
328         break;
329
330      case SelectionClear:
331         DialogOK(_("Systray Error!"), _("Systray went elsewhere?!?"));
332         SelectionRelease(systray_sel);
333         systray_sel = None;
334         EwinHide(((Container *) prm)->ewin);
335         break;
336
337      case ClientMessage:
338         SystrayEventClientMessage((Container *) prm, &(ev->xclient));
339         break;
340      }
341 }
342
343 static void
344 SystrayEvent(Win _win __UNUSED__, XEvent * ev, void *prm __UNUSED__)
345 {
346    if (EDebug(EDBUG_TYPE_ICONBOX))
347       Eprintf("SystrayEvent %2d %#lx\n", ev->type, ev->xany.window);
348
349 #if 0                           /* FIXME - Need this one at all? ConfigureRequest? */
350    Window              win;
351
352    switch (ev->type)
353      {
354      case MapNotify:
355         EWindowSync(ELookupXwin(ev->xmap.window));
356         ContainerRedraw(prm);
357         break;
358
359      case DestroyNotify:
360         win = ev->xdestroywindow.window;
361         goto do_terminate;
362
363      case ReparentNotify:
364      case EX_EVENT_REPARENT_GONE:
365         /* Terminate if reparenting away from systray */
366         if (ev->xreparent.parent == ev->xreparent.event)
367            break;
368         win = ev->xreparent.window;
369         goto do_terminate;
370
371       do_terminate:
372         SystrayObjDel(prm, win);
373         break;
374      }
375 #endif
376 }
377
378 static void
379 SystrayItemEvent(Win win, XEvent * ev, void *prm)
380 {
381    Container          *ct = (Container *) prm;
382
383    if (EDebug(EDBUG_TYPE_ICONBOX))
384       Eprintf("SystrayItemEvent %2d %#lx\n", ev->type, ev->xany.window);
385
386    switch (ev->type)
387      {
388      case MapNotify:
389         EWindowSync(win);
390         ContainerRedraw(ct);
391         break;
392
393      case DestroyNotify:
394         goto do_terminate;
395
396      case ReparentNotify:
397      case EX_EVENT_REPARENT_GONE:
398         /* Terminate if reparenting away from systray */
399         if (ev->xreparent.parent == WinGetXwin(ct->icon_win))
400            break;
401         goto do_terminate;
402
403      case ClientMessage:
404         SystrayEventClientMessage(ct, &(ev->xclient));
405         break;
406
407      case PropertyNotify:
408         SystrayEventClientProperty(ct, &(ev->xproperty));
409         break;
410
411       do_terminate:
412         SystrayObjDel(ct, win, ev->type != ReparentNotify);
413         ContainerRedraw(ct);
414         break;
415      }
416 }
417
418 static void
419 SystrayInit(Container * ct)
420 {
421    Win                 win;
422
423    E_XA__XEMBED = EInternAtom("_XEMBED");
424    E_XA__XEMBED_INFO = EInternAtom("_XEMBED_INFO");
425    _NET_SYSTEM_TRAY_OPCODE = EInternAtom("_NET_SYSTEM_TRAY_OPCODE");
426    _NET_SYSTEM_TRAY_MESSAGE_DATA = EInternAtom("_NET_SYSTEM_TRAY_MESSAGE_DATA");
427
428    /* Acquire selection */
429    if (systray_sel)
430      {
431         DialogOK(_("Systray Error!"), _("Only one systray is allowed"));
432         return;
433      }
434
435    systray_sel =
436       SelectionAcquire("_NET_SYSTEM_TRAY_S", SystraySelectionEvent, ct);
437    if (!systray_sel)
438      {
439         DialogOK(_("Systray Error!"), _("Could not activate systray"));
440         return;
441      }
442
443    win = ct->icon_win;
444    ESelectInputChange(win, SubstructureRedirectMask, 0);
445    EventCallbackRegister(win, 0, SystrayEvent, ct);
446
447    /* Container parameter setup */
448    ct->wm_name = "Systray";
449    ct->menu_title = _("Systray Options");
450    ct->dlg_title = _("Systray Settings");
451    ct->iconsize = 24;
452 }
453
454 static void
455 SystrayExit(Container * ct, int wm_exit __UNUSED__)
456 {
457    SelectionRelease(systray_sel);
458    systray_sel = None;
459
460    EventCallbackUnregister(ct->win, 0, SystrayEvent, ct);
461
462    while (ct->num_objs)
463      {
464         SystrayObjDel(ct, StObjGetWin(ct->objs[0].obj), 0);
465      }
466 }
467
468 static void
469 SystrayObjSizeCalc(Container * ct, ContainerObject * cto)
470 {
471    /* Inner size */
472    if (StObjIsMapped(cto->obj))
473       cto->wi = cto->hi = ct->iconsize;
474    else
475       cto->wi = cto->hi = 0;
476 }
477
478 static void
479 SystrayObjPlace(Container * ct __UNUSED__, ContainerObject * cto,
480                 EImage * im __UNUSED__)
481 {
482    if (StObjIsMapped(cto->obj))
483      {
484         EMoveResizeWindow(StObjGetWin(cto->obj), cto->xi, cto->yi, cto->wi,
485                           cto->hi);
486         /* This seems to fix rendering for ceratin apps which seem to expect
487          * expose events after resize (e.g. opera) */
488         ESync(0);
489         XClearArea(disp, WinGetXwin(StObjGetWin(cto->obj)), 0, 0, 0, 0, True);
490      }
491 }
492
493 extern const ContainerOps SystrayOps;
494 const ContainerOps  SystrayOps = {
495    SystrayInit,
496    SystrayExit,
497    NULL,
498    NULL,
499    SystrayObjSizeCalc,
500    SystrayObjPlace,
501 };