Logo Search packages:      
Sourcecode: e16 version File versions  Download package

systray.c

/*
 * Copyright (C) 2004-2007 Kim Woelders
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies of the Software, its documentation and marketing & publicity
 * materials, and acknowledgment shall be given in the documentation, materials
 * and software packages that this Software was used.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "E.h"
#include "container.h"
#include "e16-ecore_hints.h"
#include "events.h"
#include "ewins.h"
#include "hints.h"
#include "xwin.h"

#define DEBUG_SYSTRAY 0

/* Systray object info */
typedef struct
{
   Win                 win;
   char                mapped;
} SWin;

#define StObjGetWin(o) (((SWin*)(o))->win)
#define StObjIsMapped(o) (((SWin*)(o))->mapped)

/* XEmbed atoms */
static Atom         E_XA__XEMBED = 0;
static Atom         E_XA__XEMBED_INFO = 0;

/* Systray atoms */
static Atom         _NET_SYSTEM_TRAY_OPCODE = 0;
static Atom         _NET_SYSTEM_TRAY_MESSAGE_DATA = 0;

/* Systray selection */
static ESelection  *systray_sel = NULL;

static void         SystrayItemEvent(Win win, XEvent * ev, void *prm);

#define SYSTEM_TRAY_REQUEST_DOCK    0
#define SYSTEM_TRAY_BEGIN_MESSAGE   1
#define SYSTEM_TRAY_CANCEL_MESSAGE  2

/* _XEMBED client message */
#define XEMBED_EMBEDDED_NOTIFY      0

/* _XEMBED_INFO property */
#define XEMBED_MAPPED               (1 << 0)

static int
SystrayGetXembedInfo(Window win, int *info)
{
   unsigned char      *prop_ret;
   Atom                type_ret;
   unsigned long       bytes_after, num_ret;
   int                 format_ret;

   prop_ret = NULL;
   if (XGetWindowProperty(disp, win, E_XA__XEMBED_INFO, 0, 0x7fffffff,
                    False, E_XA__XEMBED_INFO, &type_ret, &format_ret,
                    &num_ret, &bytes_after, &prop_ret) != Success)
      return -1;

   if (prop_ret && type_ret == E_XA__XEMBED_INFO && format_ret == 32
       && num_ret >= 2)
     {
      info[0] = ((unsigned long *)prop_ret)[0];
      info[1] = ((unsigned long *)prop_ret)[1];
     }
   else
     {
      /* Property invalid or not there. I doubt we ever get here */
      info[0] = 0;            /* Set protocol version 0 */
      info[1] = 1;            /* Set mapped */
      num_ret = 0;
     }
   if (prop_ret)
      XFree(prop_ret);

   return num_ret;
}

/*
 * Return index, -1 if not found.
 */
static int
SystrayObjFind(Container * ct, Window win)
{
   int                 i;

   for (i = 0; i < ct->num_objs; i++)
      if (win == WinGetXwin(StObjGetWin(ct->objs[i].obj)))
       return i;

   return -1;
}

static              Win
SystrayObjManage(Container * ct, Window xwin)
{
   Win                 win;

#if DEBUG_SYSTRAY
   Eprintf("SystrayObjManage %#lx\n", xwin);
#endif
   win = ERegisterWindow(xwin, NULL);
   if (win == NoWin)
      return win;

   ESelectInput(win, StructureNotifyMask | PropertyChangeMask);
   EventCallbackRegister(win, 0, SystrayItemEvent, ct);
   EReparentWindow(win, ct->icon_win, 0, 0);
   XAddToSaveSet(disp, xwin);

   return win;
}

static void
SystrayObjUnmanage(Container * ct __UNUSED__, Win win, int gone)
{
#if DEBUG_SYSTRAY
   Eprintf("SystrayObjUnmanage %#lx gone=%d\n", WinGetXwin(win), gone);
#endif

   if (!gone)
     {
      ESelectInput(win, NoEventMask);
      EUnmapWindow(win);
      EReparentWindow(win, VRoot.win, 0, 0);
      XRemoveFromSaveSet(disp, WinGetXwin(win));
     }
   EventCallbackUnregister(win, 0, SystrayItemEvent, ct);
   EUnregisterWindow(win);
}

static void
SystrayObjAdd(Container * ct, Window xwin)
{
   SWin               *swin = NULL;
   Win                 win;
   int                 xembed_info[2];

   /* Not if already there */
   if (SystrayObjFind(ct, xwin) >= 0)
      return;

   EGrabServer();

   switch (SystrayGetXembedInfo(xwin, xembed_info))
     {
     case -1:                 /* Error - assume invalid window */
      Eprintf("SystrayObjAdd: Hmm.. Invalid window? Ignoring %#lx\n", xwin);
      goto bail_out;
     case 0:                  /* Assume broken - proceed anyway */
      Eprintf("SystrayObjAdd: Hmm.. No _XEMBED_INFO?\n");
      break;
     default:
      if (EDebug(EDBUG_TYPE_ICONBOX))
         Eprintf("SystrayObjAdd: _XEMBED_INFO: %#lx: %d %d\n", xwin,
               xembed_info[0], xembed_info[1]);
      break;
     }

   swin = EMALLOC(SWin, 1);
   if (!swin)
      goto bail_out;

   if (ContainerObjectAdd(ct, swin) < 0)
      goto bail_out;

   win = SystrayObjManage(ct, xwin);
   if (win == NoWin)
      goto bail_out;

   swin->win = win;
   swin->mapped = (xembed_info[1] & XEMBED_MAPPED) != 0;
   if (swin->mapped)
      EMapWindow(win);

   /* TBD - Always set protocol version as reported by client */
   ecore_x_client_message32_send(xwin, E_XA__XEMBED, NoEventMask,
                         CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0,
                         xwin, xembed_info[0]);

   EUngrabServer();

   return;              /* Success */

 bail_out:
   EUngrabServer();
   if (!swin)
      return;
   ContainerObjectDel(ct, swin);
   Efree(swin);
}

static void
SystrayObjDel(Container * ct, Win win, int gone)
{
   int                 i;
   SWin               *swin;

   i = SystrayObjFind(ct, WinGetXwin(win));
   if (i < 0)
      return;

   if (EDebug(EDBUG_TYPE_ICONBOX))
      Eprintf("SystrayObjDel %#lx\n", WinGetXwin(win));

   swin = (SWin *) ct->objs[i].obj;

   ContainerObjectDel(ct, swin);

   if (disp)
      SystrayObjUnmanage(ct, swin->win, gone);

   Efree(swin);
}

static void
SystrayObjMapUnmap(Container * ct, Window win)
{
   int                 i, map;
   SWin               *swin;
   int                 xembed_info[2];

   i = SystrayObjFind(ct, win);
   if (i < 0)
      return;

   swin = (SWin *) ct->objs[i].obj;

   if (SystrayGetXembedInfo(win, xembed_info) >= 0)
     {
      if (EDebug(EDBUG_TYPE_ICONBOX))
         Eprintf("SystrayObjMapUnmap: _XEMBED_INFO: %#lx: %d %d\n", win,
               xembed_info[0], xembed_info[1]);

      map = (xembed_info[1] & XEMBED_MAPPED) != 0;
      if (map == swin->mapped)
         return;

      if (map)
         EMapWindow(swin->win);
      else
         EUnmapWindow(swin->win);
     }
   else
     {
      if (EDebug(EDBUG_TYPE_ICONBOX))
         Eprintf("SystrayObjMapUnmap: _XEMBED_INFO: %#lx: gone?\n", win);

      map = 0;
      if (map == swin->mapped)
         return;
     }

   swin->mapped = map;
   ContainerRedraw(ct);
}

static void
SystrayEventClientMessage(Container * ct, XClientMessageEvent * ev)
{
   Window              win;

   if (EDebug(EDBUG_TYPE_ICONBOX))
      Eprintf
       ("SystrayEventClientMessage: ev->type=%ld ev->data.l: %#lx %#lx %#lx %#lx\n",
        ev->message_type, ev->data.l[0], ev->data.l[1], ev->data.l[2],
        ev->data.l[3]);

   if (ev->message_type == _NET_SYSTEM_TRAY_OPCODE)
     {
      win = ev->data.l[2];
      if (win == None)
         goto done;

      SystrayObjAdd(ct, win);
     }
   else if (ev->message_type == _NET_SYSTEM_TRAY_MESSAGE_DATA)
     {
      if (EDebug(EDBUG_TYPE_ICONBOX))
         Eprintf("SystrayEventClientMessage: Got data message\n");
     }
 done:
   ;
}

static void
SystrayEventClientProperty(Container * ct, XPropertyEvent * ev)
{
   if (EDebug(EDBUG_TYPE_ICONBOX))
      Eprintf("SystrayEventClientProperty %#lx %ld\n", ev->window, ev->atom);

   if (ev->atom == E_XA__XEMBED_INFO)
     {
      SystrayObjMapUnmap(ct, ev->window);
     }
}

static void
SystraySelectionEvent(Win win __UNUSED__, XEvent * ev, void *prm)
{
   if (EDebug(EDBUG_TYPE_ICONBOX))
      Eprintf("SystraySelectionEvent %2d %#lx\n", ev->type, ev->xany.window);

   switch (ev->type)
     {
     default:
      Eprintf(" ??? SystraySelectionEvent %2d %#lx\n", ev->type,
            ev->xany.window);
      break;

     case SelectionClear:
      DialogOK(_("Systray Error!"), _("Systray went elsewhere?!?"));
      SelectionRelease(systray_sel);
      systray_sel = None;
      EwinHide(((Container *) prm)->ewin);
      break;

     case ClientMessage:
      SystrayEventClientMessage((Container *) prm, &(ev->xclient));
      break;
     }
}

static void
SystrayEvent(Win _win __UNUSED__, XEvent * ev, void *prm __UNUSED__)
{
   if (EDebug(EDBUG_TYPE_ICONBOX))
      Eprintf("SystrayEvent %2d %#lx\n", ev->type, ev->xany.window);

#if 0                   /* FIXME - Need this one at all? ConfigureRequest? */
   Window              win;

   switch (ev->type)
     {
     case MapNotify:
      EWindowSync(ELookupXwin(ev->xmap.window));
      ContainerRedraw(prm);
      break;

     case DestroyNotify:
      win = ev->xdestroywindow.window;
      goto do_terminate;

     case ReparentNotify:
     case EX_EVENT_REPARENT_GONE:
      /* Terminate if reparenting away from systray */
      if (ev->xreparent.parent == ev->xreparent.event)
         break;
      win = ev->xreparent.window;
      goto do_terminate;

      do_terminate:
      SystrayObjDel(prm, win);
      break;
     }
#endif
}

static void
SystrayItemEvent(Win win, XEvent * ev, void *prm)
{
   Container          *ct = (Container *) prm;

   if (EDebug(EDBUG_TYPE_ICONBOX))
      Eprintf("SystrayItemEvent %2d %#lx\n", ev->type, ev->xany.window);

   switch (ev->type)
     {
     case MapNotify:
      EWindowSync(win);
      ContainerRedraw(ct);
      break;

     case DestroyNotify:
      goto do_terminate;

     case ReparentNotify:
     case EX_EVENT_REPARENT_GONE:
      /* Terminate if reparenting away from systray */
      if (ev->xreparent.parent == WinGetXwin(ct->icon_win))
         break;
      goto do_terminate;

     case ClientMessage:
      SystrayEventClientMessage(ct, &(ev->xclient));
      break;

     case PropertyNotify:
      SystrayEventClientProperty(ct, &(ev->xproperty));
      break;

      do_terminate:
      SystrayObjDel(ct, win, ev->type != ReparentNotify);
      ContainerRedraw(ct);
      break;
     }
}

static void
SystrayInit(Container * ct)
{
   char                buf[32];
   Win                 win;

   Esnprintf(buf, sizeof(buf), "_NET_SYSTEM_TRAY_S%d", VRoot.scr);

   E_XA__XEMBED = XInternAtom(disp, "_XEMBED", False);
   E_XA__XEMBED_INFO = XInternAtom(disp, "_XEMBED_INFO", False);
   _NET_SYSTEM_TRAY_OPCODE =
      XInternAtom(disp, "_NET_SYSTEM_TRAY_OPCODE", False);
   _NET_SYSTEM_TRAY_MESSAGE_DATA =
      XInternAtom(disp, "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);

   /* Acquire selection */
   if (systray_sel)
     {
      DialogOK(_("Systray Error!"), _("Only one systray is allowed"));
      return;
     }

   systray_sel =
      SelectionAcquire("_NET_SYSTEM_TRAY_S", SystraySelectionEvent, ct);
   if (!systray_sel)
     {
      DialogOK(_("Systray Error!"), _("Could not activate systray"));
      return;
     }

   win = ct->icon_win;
   ESelectInputChange(win, SubstructureRedirectMask, 0);
   EventCallbackRegister(win, 0, SystrayEvent, ct);

   /* Container parameter setup */
   ct->wm_name = "Systray";
   ct->menu_title = _("Systray Options");
   ct->dlg_title = _("Systray Settings");
   ct->iconsize = 24;
}

static void
SystrayExit(Container * ct, int wm_exit __UNUSED__)
{
   SelectionRelease(systray_sel);
   systray_sel = None;

   EventCallbackUnregister(ct->win, 0, SystrayEvent, ct);

   while (ct->num_objs)
     {
      SystrayObjDel(ct, StObjGetWin(ct->objs[0].obj), 0);
     }
}

static void
SystrayObjSizeCalc(Container * ct, ContainerObject * cto)
{
   /* Inner size */
   if (StObjIsMapped(cto->obj))
      cto->wi = cto->hi = ct->iconsize;
   else
      cto->wi = cto->hi = 0;
}

static void
SystrayObjPlace(Container * ct __UNUSED__, ContainerObject * cto,
            EImage * im __UNUSED__)
{
   if (StObjIsMapped(cto->obj))
     {
      EMoveResizeWindow(StObjGetWin(cto->obj), cto->xi, cto->yi, cto->wi,
                    cto->hi);
     }
}

extern const ContainerOps SystrayOps;
const ContainerOps  SystrayOps = {
   SystrayInit,
   SystrayExit,
   NULL,
   NULL,
   SystrayObjSizeCalc,
   SystrayObjPlace,
};

Generated by  Doxygen 1.6.0   Back to index