Index: /trunk/src/3rdparty/os2/xsystray/apilib/apilib.pro
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/apilib/apilib.pro	(revision 835)
+++ /trunk/src/3rdparty/os2/xsystray/apilib/apilib.pro	(revision 835)
@@ -0,0 +1,14 @@
+TEMPLATE = lib
+
+CONFIG += dll
+QT =
+
+TARGET = xsystray
+
+SOURCES = xsystray.c
+
+DEF_FILE = xsystray.def
+
+INCLUDEPATH += .. ../plugin ../apilib
+
+DESTDIR = $$[QT_INSTALL_PLUGINS]/xcenter/apilib
Index: /trunk/src/3rdparty/os2/xsystray/apilib/xsystray.c
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/apilib/xsystray.c	(revision 835)
+++ /trunk/src/3rdparty/os2/xsystray/apilib/xsystray.c	(revision 835)
@@ -0,0 +1,676 @@
+
+/*
+ *@@sourcefile xsystray.c:
+ *      Extended system tray widget for XCenter/eCenter.
+ *
+ *      Implementation of the public API.
+ *
+ *      Copyright (C) 2009 Dmitry A. Kuminov
+ *
+ *      This file is part of the Extended system tray widget source package.
+ *      Extended system tray widget is free software; you can redistribute it
+ *      and/or modify it under the terms of the GNU General Public License as
+ *      published by the Free Software Foundation, in version 2 as it comes in
+ *      the "COPYING" file of the Extended system tray widget distribution. This
+ *      program is distributed in the hope that it will be useful, but WITHOUT
+ *      ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *      FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ *      more details.
+ */
+
+#define OS2EMX_PLAIN_CHAR
+#define INCL_DOSERRORS
+#define INCL_DOSPROCESS
+#define INCL_WINWINDOWMGR
+#define INCL_WINERRORS
+#define INCL_WINATOM
+#define INCL_WINPOINTERS
+#define INCL_WINHOOKS
+#define INCL_WINWINDOWMGR
+#include <os2.h>
+
+#define XSTAPI_IMPL
+#include "xsystray.h"
+
+#include "w_xsystray.h"
+
+#include <string.h>
+#include <sys/builtin.h>        // atomics
+#include <sys/fmutex.h>         // fast mutex
+#include <sys/smutex.h>         // simple mutex
+
+static ULONG WM_XST_CREATED = 0;
+             // identity of the WM_XST_CREATED message taken from the atom table
+static ULONG WM_XST_NOTIFY = 0;
+             // identity of the WM_XST_NOTIFY message taken from the atom table
+
+static
+volatile HWND G_hwndSysTray = NULLHANDLE;
+              // window handle of the system tray server
+
+static
+volatile PVOID G_pvMemoryPool = NULL;
+               // shared memory pool for SYSTRAYCTLDATA structs used by
+               // WM_XST_CONTROL messages. Note that once allocated, this memory
+               // is never freed: it is intentional since the memory is assumed
+               // to be always in need and that the system will free it when the
+               // application terminates
+
+#define CLIENT_MEMORYPOOL_SIZE 65536
+        // taking SYSTRAYCTLDATA size into account (<=1024 B), this is enough
+        // for at least 64 threads sending WM_XST_CONTROL simultaneously, which
+        // sounds sane
+
+typedef struct
+{
+    HMQ         hmq;
+    unsigned    cRefs;
+} HMQREFS, *PHMQREFS;
+
+static PHMQREFS G_pHmqRefs = NULL;
+                // array of references to each HMQ that we make
+static size_t   G_cHmqRefs = 0;
+                // number of elements in G_pHmqRefs
+static size_t   G_cHmqRefsMax = 0;
+                // maximum number of elements in G_pHmqRefs
+static _fmutex  G_fmtx;
+                // fast mutex to protect
+static _smutex  G_smtx = 0;
+                // simple mutex for xstAddSysTrayIcon()
+#define         HMQREFS_GROW 4
+                // how fast G_pHmqRefs grows when more space is necessary
+
+// @todo to be on the safe side with casting in __atomic_cmpxchg32() we need
+// compile-time assertions like this:
+// AssertCompile(sizeof(uint32_t) == sizeof(HWND));
+// AssertCompile(sizeof(uint32_t) == sizeof(PVOID));
+
+static HWND FindSysTrayServerWindow()
+{
+    char buf[sizeof(WNDCLASS_WIDGET_XSYSTRAY_SERVER) + 1];
+    HWND hwnd;
+    HENUM henum = WinBeginEnumWindows(HWND_DESKTOP);
+    while ((hwnd = WinGetNextWindow(henum)) != NULLHANDLE)
+    {
+        LONG len = WinQueryClassName(hwnd, sizeof(buf), buf);
+        buf[len] = '\0';
+        if (strcmp(WNDCLASS_WIDGET_XSYSTRAY_SERVER, buf) == 0)
+            break;
+    }
+    WinEndEnumWindows(henum);
+
+    return hwnd;
+}
+
+static ULONG SendSysTrayCtlMsg(PSYSTRAYCTLDATA pData)
+{
+    APIRET arc;
+    PID pid;
+    TID tid;
+    MRESULT mrc;
+
+    BOOL bTriedFind = FALSE;
+
+    do
+    {
+        if (G_hwndSysTray == NULLHANDLE)
+        {
+            bTriedFind = TRUE;
+            HWND hwnd = FindSysTrayServerWindow();
+            __atomic_cmpxchg32((volatile uint32_t *)&G_hwndSysTray,
+                               hwnd, NULLHANDLE);
+            if (G_hwndSysTray == NULLHANDLE)
+                break;
+        }
+
+        if (bTriedFind)
+        {
+            arc = ERROR_INVALID_HANDLE;
+            if (WinQueryWindowProcess(G_hwndSysTray, &pid, &tid))
+                arc = DosGiveSharedMem(G_pvMemoryPool,
+                                       pid, PAG_READ | PAG_WRITE);
+            if (arc != NO_ERROR)
+                break;
+        }
+
+        pData->bAcknowledged = FALSE;
+
+        mrc = WinSendMsg(G_hwndSysTray, WM_XST_CONTROL, pData, NULL);
+        if (pData->bAcknowledged)
+            return (ULONG)mrc;
+
+        // if we failed to send the message, it may mean that XCenter was restarted
+        // or the system tray was re-enabled. Try to get a new handle (only if we
+        // didn't already do it in this call)
+        if (!bTriedFind)
+        {
+            G_hwndSysTray = NULLHANDLE;
+            continue;
+        }
+
+        break;
+    }
+    while (1);
+
+    return XST_FAIL;
+}
+
+/*
+ *@@ AllocSysTrayCtlDataPtr:
+ *      Allocates a SYSTRAYCTLDATA struct in the pool of shared memory.
+ *
+ *      If there is no free space in the pool, it returns NULL. The allocated
+ *      memory must be freed by FreeSysTrayCtlDataPtr() when not needed.
+ */
+
+static PSYSTRAYCTLDATA AllocSysTrayCtlDataPtr()
+{
+    APIRET arc;
+    PVOID pvPool;
+    PSYSTRAYCTLDATA pData;
+
+    if (!G_pvMemoryPool)
+    {
+        // Note: we don't PAG_COMMIT, DosSubAllocMem will do so when needed
+        arc = DosAllocSharedMem((PVOID)&pvPool, NULL, CLIENT_MEMORYPOOL_SIZE,
+                                PAG_READ | PAG_WRITE | OBJ_GIVEABLE);
+        if (arc == NO_ERROR)
+            arc = DosSubSetMem(pvPool,
+                               DOSSUB_INIT | DOSSUB_SPARSE_OBJ,
+                               CLIENT_MEMORYPOOL_SIZE);
+        if (!__atomic_cmpxchg32((volatile uint32_t *)&G_pvMemoryPool,
+                                (uint32_t)pvPool, (uint32_t)NULL))
+        {
+            // another thread has already got an entry, discard our try
+            if (pvPool)
+                DosFreeMem(pvPool);
+        }
+        else
+        {
+            // we could fail to allocate while being the first... give up
+            if (arc != NO_ERROR)
+                return NULL;
+        }
+    }
+
+    arc = DosSubAllocMem(G_pvMemoryPool, (PVOID)&pData, sizeof(*pData));
+    if (arc != NO_ERROR)
+        return NULL;
+
+    return pData;
+}
+
+static VOID FreeSysTrayCtlDataPtr(PSYSTRAYCTLDATA pData)
+{
+    DosSubFreeMem(G_pvMemoryPool, pData, sizeof(*pData));
+}
+
+/*
+ *@@ InputHook:
+ *      This is used to intercept posted WM_XST_NOTIFY messages and apply
+ *      special processing to them (compose a client window-specific
+ *      notification message, free the NOTIFYDATA structure and post the
+ *      composed message to the target window).
+ */
+
+static BOOL EXPENTRY InputHook(HAB hab, PQMSG pQmsg, ULONG fs)
+{
+    if (pQmsg->msg == WM_XST_NOTIFY)
+    {
+        PNOTIFYDATA pNotifyData = (PNOTIFYDATA)pQmsg->mp1;
+        PVOID pvMemoryPool = (PVOID)pQmsg->mp2;
+
+        // copy NOTIFYDATA and free it
+        NOTIFYDATA NotifyData = *pNotifyData;
+        FreeNotifyDataPtr(pvMemoryPool, pQmsg->hwnd, pNotifyData);
+
+        // start with a copy of the message and change the fields we need
+        QMSG newMsg = *pQmsg;
+        newMsg.msg = NotifyData.msg;
+        newMsg.mp1 = NotifyData.mp1;
+        newMsg.mp2 = NotifyData.mp2;
+
+        // deliver the message
+        WinDispatchMsg(hab, &newMsg);
+
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/*
+ *@@ xstQuerySysTrayVersion:
+ *
+ *      Returns the version of the Extended system tray in the variables pointed
+ *      to by arguments. Any argument may be NULL in which case the
+ *      corresponding component of the version is not returned.
+ *
+ *      Returns TRUE on success and FALSE if the Extended system tray is not
+ *      installed or not operational.
+ *
+ *      NOTE: When the Extended system tray is started up or gets enabled after
+ *      being temporarily disabled, it sends a message with the ID returned by
+ *      xstGetSysTrayCreatedMsgId() to all top-level WC_FRAME windows on the
+ *      desktop to let them add tray icons if they need.
+ */
+
+BOOL xstQuerySysTrayVersion(PULONG pulMajor,    // out: major version number
+                            PULONG pulMinor,    // out: minor version number
+                            PULONG pulRevision) // out: revision number
+{
+    BOOL brc;
+    PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
+    if (!pData)
+        return FALSE;
+
+    pData->ulCommand = SYSTRAYCMD_GETVERSION;
+    pData->hwndSender = NULLHANDLE;
+
+    brc = SendSysTrayCtlMsg(pData) == XST_OK;
+    if (brc)
+    {
+        if (pulMajor)
+            *pulMajor = pData->u.version.ulMajor;
+        if (pulMinor)
+            *pulMinor = pData->u.version.ulMinor;
+        if (pulRevision)
+            *pulRevision = pData->u.version.ulRevision;
+    }
+
+    FreeSysTrayCtlDataPtr(pData);
+
+    return brc;
+}
+
+/*
+ *@@ xstAddSysTrayIcon:
+ *
+ *      Adds an icon for the given window handle to the system tray. The icon ID
+ *      is used to distinguish between several icons for the same window handle.
+ *      If the icon with the specified ID already exists in the system tray, it
+ *      will be replaced.
+ *
+ *      Returns TRUE on success and FALSE otherwise.
+ *
+ *      The specified window handle receives notification messages about icon
+ *      events using the message ID specified by the ulMsgId parameter. The
+ *      layout of the message parameters is as follows:
+ *
+ *          param1
+ *              USHORT  usIconID        icon ID
+ *              USHORT  usNotifyCode    notify code, one of XST_IN_ constants
+ *
+ *          param2
+ *              PVOID   pData           notify code specific data (see below)
+ *
+ *      The following notify codes are currently recognized:
+ *
+ *          XST_IN_MOUSE:
+ *              Mouse event in the icon area. Currently, only mouse click
+ *              messages are recognized. param2 is a pointer to the XSTMOUSEMSG
+ *              structure containing full mouse message details.
+ *
+ *          XST_IN_CONTEXT:
+ *              Context menu event in the icon area. param2 is a pointer to the
+ *              XSTCONTEXTMSG structure containing full message details.
+ *
+ *          XST_IN_WHEEL:
+ *              Mouse wheel event in the icon area. param2 is a pointer to the
+ *              XSTWHEELTMSG structure containing full message details.
+ *
+ *      NOTE: The maximum tooltip text length (including terminating null) is
+ *      limited to a value returned by xstGetSysTrayMaxTextLen(). If the
+ *      supplied string is longer, it will be truncated.
+ */
+
+BOOL xstAddSysTrayIcon(HWND hwnd,       // in: window handle associated with the icon
+                       USHORT usId,     // in: icon ID to add
+                       HPOINTER hIcon,  // in: icon handle
+                       PCSZ pcszToolTip,// in: tooltip text
+                       ULONG ulMsgId,   // in: message ID for notifications
+                       ULONG ulFlags)   // in: flags (not currently used, must be 0)
+{
+    BOOL    brc;
+    ULONG   xrc = XST_FAIL;
+    PPIB    ppib;
+    HAB     hab;
+    HMQ     hmq;
+    size_t  i;
+
+    PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
+    if (!pData)
+        return FALSE;
+
+    if (WM_XST_NOTIFY == 0)
+        WM_XST_NOTIFY = WinAddAtom(WinQuerySystemAtomTable(),
+                                   WM_XST_NOTIFY_ATOM);
+
+    hab = WinQueryAnchorBlock(hwnd);
+    hmq = WinQueryWindowULong(hwnd, QWL_HMQ);
+    if (hmq == NULLHANDLE)
+        return FALSE;
+
+    // initialize the HMQ refs array
+    // @todo remove _smutex usage when we get into the DLL and initialize
+    // _fmutex + array in the DLL init routine
+    _smutex_request(&G_smtx);
+    if (!G_pHmqRefs)
+    {
+        if (_fmutex_create(&G_fmtx, 0))
+            return FALSE;
+        G_pHmqRefs = malloc(sizeof(*G_pHmqRefs) * HMQREFS_GROW);
+        if (!G_pHmqRefs)
+            return FALSE;
+        G_cHmqRefs = 0;
+        G_cHmqRefsMax = HMQREFS_GROW;
+    }
+    _smutex_release(&G_smtx);
+
+    // give all processes temporary access to hIcon
+    brc = WinSetPointerOwner(hIcon, 0, FALSE);
+    if (brc)
+    {
+        pData->ulCommand = SYSTRAYCMD_ADDICON;
+        pData->hwndSender = hwnd;
+
+        pData->u.icon.usId = usId;
+        pData->u.icon.hIcon = hIcon;
+        pData->u.icon.ulMsgId = ulMsgId;
+
+        if (!pcszToolTip)
+            pData->u.icon.szToolTip[0] = '\0';
+        else
+        {
+            strncpy(pData->u.icon.szToolTip, pcszToolTip,
+                    sizeof(pData->u.icon.szToolTip) - 1);
+            // be on the safe side
+            pData->u.icon.szToolTip[sizeof(pData->u.icon.szToolTip) - 1] = '\0';
+        }
+
+        xrc = SendSysTrayCtlMsg(pData);
+        brc = xrc == XST_OK || xrc == XST_REPLACED;
+
+        // revoke temporary access to hIcon
+        DosGetInfoBlocks(NULL, &ppib);
+        WinSetPointerOwner(hIcon, ppib->pib_ulpid, TRUE);
+    }
+
+    FreeSysTrayCtlDataPtr(pData);
+
+    if (xrc == XST_OK)
+    {
+        // install the message hook for the new icon to intercept WM_XST_NOTIFY
+        // messages or increase the reference count if already done so
+        brc = FALSE;
+        _fmutex_request(&G_fmtx, _FMR_IGNINT);
+        do
+        {
+            for (i = 0; i < G_cHmqRefs; ++i)
+                if (G_pHmqRefs[i].hmq == hmq)
+                    break;
+            if (i < G_cHmqRefs)
+                ++G_pHmqRefs[i].cRefs;
+            else
+            {
+                if (i == G_cHmqRefsMax)
+                {
+                    PHMQREFS pNewRefs = realloc(G_pHmqRefs,
+                                                sizeof(*G_pHmqRefs) *
+                                                (G_cHmqRefsMax + HMQREFS_GROW));
+                    if (!pNewRefs)
+                        break;
+                    G_pHmqRefs = pNewRefs;
+                    G_cHmqRefsMax += HMQREFS_GROW;
+                }
+                brc = WinSetHook(hab, hmq, HK_INPUT, (PFN)InputHook, NULLHANDLE);
+                if (!brc)
+                    break;
+                ++G_cHmqRefs;
+                G_pHmqRefs[i].hmq = hmq;
+                G_pHmqRefs[i].cRefs = 1;
+            }
+            brc = TRUE;
+        }
+        while (0);
+        _fmutex_release(&G_fmtx);
+
+        if (!brc)
+            xstRemoveSysTrayIcon(hwnd, usId);
+    }
+
+    return brc;
+}
+
+/*
+ *@@ xstReplaceSysTrayIcon:
+ *
+ *      Replaces the existing icon previously added by xstAddSysTrayIcon() with
+ *      a new icon.
+ *
+ *      Returns TRUE on success and FALSE otherwise.
+ */
+
+BOOL xstReplaceSysTrayIcon(HWND hwnd,        // in: window handle associated with the icon
+                           USHORT usId,      // in: icon ID to change
+                           HPOINTER hIcon)   // in: new icon handle
+{
+    BOOL    brc;
+    PPIB    ppib;
+
+    PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
+    if (!pData)
+        return FALSE;
+
+    // give all processes temporary access to hIcon
+    brc = WinSetPointerOwner(hIcon, 0, FALSE);
+    if (brc)
+    {
+        pData->ulCommand = SYSTRAYCMD_REPLACEICON;
+        pData->hwndSender = hwnd;
+
+        pData->u.icon.usId = usId;
+        pData->u.icon.hIcon = hIcon;
+
+        brc = SendSysTrayCtlMsg(pData) == XST_OK;
+
+        // revoke temporary access to hIcon
+        DosGetInfoBlocks(NULL, &ppib);
+        WinSetPointerOwner(hIcon, ppib->pib_ulpid, TRUE);
+    }
+
+    FreeSysTrayCtlDataPtr(pData);
+
+    return brc;
+}
+
+/*
+ *@@ xstRemoveSysTrayIcon:
+ *
+ *      Removes the icon previously added by xstAddSysTrayIcon() from the system
+ *      tray.
+ *
+ *      Returns TRUE on success and FALSE otherwise.
+ */
+
+BOOL xstRemoveSysTrayIcon(HWND hwnd,    // in: window handle associated with the icon
+                          USHORT usId)  // in: icon ID to remove
+{
+    BOOL brc;
+    HAB     hab;
+    HMQ     hmq;
+    size_t  i;
+
+    PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
+    if (!pData)
+        return FALSE;
+
+    hab = WinQueryAnchorBlock(hwnd);
+    hmq = WinQueryWindowULong(hwnd, QWL_HMQ);
+    if (hmq == NULLHANDLE)
+        return FALSE;
+
+    pData->ulCommand = SYSTRAYCMD_REMOVEICON;
+    pData->hwndSender = hwnd;
+    pData->u.icon.usId = usId;
+
+    brc = SendSysTrayCtlMsg(pData) == XST_OK;
+
+    FreeSysTrayCtlDataPtr(pData);
+
+    if (brc)
+    {
+        // remove the message hook if it's the last reference to the HMQ
+        _fmutex_request(&G_fmtx, _FMR_IGNINT);
+        do
+        {
+            for (i = 0; i < G_cHmqRefs; ++i)
+                if (G_pHmqRefs[i].hmq == hmq)
+                    break;
+            if (i == G_cHmqRefs)
+                // unknown HMQ??
+                break;
+
+            if (--G_pHmqRefs[i].cRefs == 0)
+                WinReleaseHook(hab, hmq, HK_INPUT, (PFN)InputHook, NULLHANDLE);
+        }
+        while (0);
+        _fmutex_release(&G_fmtx);
+    }
+
+    return brc;
+}
+
+/*
+ *@@ xstSetSysTrayIconToolTip:
+ *
+ *      Sets the tooltip text for the given icon in the system tray. This text
+ *      is shown when the mouse pointer is held still over the icon area for
+ *      some time.
+ *
+ *      If pszText is NULL, the tooltip text is reset and will not be shown next
+ *      time. The old tooltip is hidden if it is being currently shown.
+ *
+ *      Returns TRUE on success and FALSE otherwise.
+ *
+ *      NOTE: The maximum tooltip text length (including terminating null) is
+ *      limited to a value returned by xstGetSysTrayMaxTextLen(). If the
+ *      supplied string is longer, it will be truncated.
+ */
+
+BOOL xstSetSysTrayIconToolTip(HWND hwnd,        // in: window handle associated with the icon
+                              USHORT usId,      // in: icon ID to set the tooltip for
+                              PCSZ pcszToolTip) // in: tooltip text
+{
+    BOOL brc;
+    PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
+    if (!pData)
+        return FALSE;
+
+    pData->ulCommand = SYSTRAYCMD_SETTOOLTIP;
+    pData->hwndSender = hwnd;
+    pData->u.icon.usId = usId;
+
+    if (!pcszToolTip)
+        pData->u.icon.szToolTip[0] = '\0';
+    else
+    {
+        strncpy(pData->u.icon.szToolTip, pcszToolTip,
+                sizeof(pData->u.icon.szToolTip) - 1);
+        // be on the safe side
+        pData->u.icon.szToolTip[sizeof(pData->u.icon.szToolTip) - 1] = '\0';
+    }
+
+    brc = SendSysTrayCtlMsg(pData) == XST_OK;
+
+    FreeSysTrayCtlDataPtr(pData);
+
+    return brc;
+}
+
+BOOL xstShowSysTrayIconBalloon(HWND hwnd, USHORT usId, PCSZ pcszTitle,
+                               PCSZ pcszText, ULONG ulFlags, ULONG ulTimeout)
+{
+    // @todo implement
+    return FALSE;
+}
+
+BOOL xstHideSysTrayIconBalloon(HWND hwnd, USHORT usId)
+{
+    // @todo implement
+    return FALSE;
+}
+
+/*
+ *@@ xstQuerySysTrayIconRect:
+ *
+ *      Obtains a rectangle occupied by the given icon (in screen coordinates,
+ *      top right corner exclusive).
+ *
+ *      Returns TRUE on success and FALSE otherwise.
+ */
+BOOL xstQuerySysTrayIconRect(HWND hwnd, USHORT usId, PRECTL prclRect)
+{
+    BOOL brc;
+    PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
+    if (!pData)
+        return FALSE;
+
+    pData->ulCommand = SYSTRAYCMD_QUERYRECT;
+    pData->hwndSender = hwnd;
+    pData->u.icon.usId = usId;
+
+    brc = SendSysTrayCtlMsg(pData) == XST_OK;
+    if (brc)
+    {
+        *prclRect = pData->u.rect.rclIcon;
+    }
+
+    FreeSysTrayCtlDataPtr(pData);
+
+    return brc;
+}
+
+/*
+ *@@ xstGetSysTrayCreatedMsgId:
+ *
+ *      Returns the ID of the message that is sent by the Extended system tray
+ *      to all top-level WC_FRAME windows on the desktop to let them add tray
+ *      icons if they need.
+ *
+ *      NOTE: The returned value never changes until reboot so it is a good
+ *      practice to cache it instead of calling this function each time from the
+ *      window procedure of every involved window.
+ */
+
+ULONG xstGetSysTrayCreatedMsgId()
+{
+    if (WM_XST_CREATED == 0)
+        WM_XST_CREATED = WinAddAtom(WinQuerySystemAtomTable(),
+                                    WM_XST_CREATED_ATOM);
+    return WM_XST_CREATED;
+}
+
+/*
+ *@@ xstGetSysTrayMaxTextLen:
+ *
+ *      Returns the maximum length of the text (in bytes, including the
+ *      terminating null) that can be shown in the tooltop of the icon in the
+ *      system tray. You can use the returned value to determine the maximum
+ *      length of the string passed as pszText to xstSetSysTrayIconToolTip().
+ *
+ *      The returned value also defines the maximum length of both the title and
+ *      the text (including terminating nulls) of the icon's balloon for the
+ *      xstShowSysTrayIconBalloon() call.
+ *
+ *      Returns TRUE on success and FALSE otherwise.
+ *
+ *      NOTE: The returned value never changes until reboot so it is a good
+ *      practice to cache it instead of calling this function each time.
+ */
+
+ULONG xstGetSysTrayMaxTextLen()
+{
+    return sizeof(((PSYSTRAYCTLDATA)0)->u.icon.szToolTip);
+}
+
Index: /trunk/src/3rdparty/os2/xsystray/apilib/xsystray.def
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/apilib/xsystray.def	(revision 835)
+++ /trunk/src/3rdparty/os2/xsystray/apilib/xsystray.def	(revision 835)
@@ -0,0 +1,7 @@
+;
+; module definition file for XSYSTRAY.DLL which
+; provides Extended system tray public API
+;
+
+LIBRARY XSYSTRAY INITINSTANCE TERMINSTANCE
+DESCRIPTION '@#netlabs.org:0.1.0#@ Extended system tray API'
Index: /trunk/src/3rdparty/os2/xsystray/apilib/xsystray.h
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/apilib/xsystray.h	(revision 835)
+++ /trunk/src/3rdparty/os2/xsystray/apilib/xsystray.h	(revision 835)
@@ -0,0 +1,112 @@
+
+/*
+ *@@sourcefile xsystray.h:
+ *      Extended system tray widget for XCenter/eCenter.
+ *
+ *      Public API.
+ *
+ *      This file contains the public API to the Extended system tray widget.
+ *      This API is used by applications to add and remove icons from the
+ *      special notification area on the desktop provided by the Extended
+ *      system tray widget.
+ *
+ *      Refer to the "API" file for more details about the public API.
+ *
+ *      Copyright (C) 2009 Dmitry A. Kuminov
+ *
+ *      This file is part of the Extended system tray widget source package.
+ *      Extended system tray widget is free software; you can redistribute it
+ *      and/or modify it under the terms of the GNU General Public License as
+ *      published by the Free Software Foundation, in version 2 as it comes in
+ *      the "COPYING" file of the Extended system tray widget distribution. This
+ *      program is distributed in the hope that it will be useful, but WITHOUT
+ *      ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *      FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ *      more details.
+ */
+
+#ifndef XSYSTRAY_API_HEADER_INCLUDED
+#define XSYSTRAY_API_HEADER_INCLUDED
+
+#if __cplusplus
+extern "C" {
+#endif
+
+#ifdef XSTAPI_IMPL
+#define XSTAPI __declspec(dllexport) EXPENTRY
+#else
+#define XSTAPI EXPENTRY
+#endif
+
+// notification code constants for the notification messages sent by the system
+// tray (refer to xstAddSysTrayIcon() for details)
+#define XST_IN_MOUSE    0x0001
+#define XST_IN_CONTEXT  0x0002
+#define XST_IN_WHEEL    0x0003
+
+// structure for XST_IN_MOUSE
+typedef struct
+{
+    ULONG  ulMouseMsg;
+           // mouse message (one of WM_BUTTONxyyy)
+    POINTS ptsPointerPos;
+           // global pointer position at the time of the mouse event
+    USHORT fsHitTestRes;
+           // hit-test result (see WM_BUTTONxyyy description in PM)
+    USHORT fsFlags;
+           // keyboard control codes (see WM_BUTTONxyyy description in PM)
+
+} XSTMOUSEMSG, *PXSTMOUSEMSG;
+
+// structure for XST_IN_CONTEXT
+typedef struct
+{
+    POINTS ptsPointerPos;
+           // global pointer position at the time of the mouse event
+    USHORT fPointer;
+           // input device flag (see WM_CONTEXTMENU description in PM)
+
+} XSTCONTEXTMSG, *PXSTCONTEXTMSG;
+
+// structure for XST_IN_WHEEL
+typedef struct
+{
+    ULONG  ulWheelMsg;
+           // mouse message (one of WM_HSCROLL or WM_VSCROLL)
+    POINTS ptsPointerPos;
+           // global pointer position at the time of the mouse event
+    USHORT usCmd;
+           // command (see WM_HSCROLL/WM_VSCROLL description in PM)
+
+} XSTWHEELMSG, *PXSTWHEELMSG;
+
+BOOL XSTAPI xstQuerySysTrayVersion(PULONG pulMajor, PULONG pulMinor,
+                                   PULONG pulRevision);
+                                    
+BOOL XSTAPI xstAddSysTrayIcon(HWND hwnd, USHORT usId, HPOINTER hIcon,
+                              PCSZ pcszToolTip, ULONG ulMsgId, ULONG ulFlags);
+                               
+BOOL XSTAPI xstReplaceSysTrayIcon(HWND hwnd, USHORT usId, HPOINTER hIcon);
+
+BOOL XSTAPI xstRemoveSysTrayIcon(HWND hwnd, USHORT usId);
+
+BOOL XSTAPI xstSetSysTrayIconToolTip(HWND hwnd, USHORT usId, PCSZ pcszToolTip);
+
+BOOL XSTAPI xstShowSysTrayIconBalloon(HWND hwnd, USHORT usId, PCSZ pcszTitle,
+                                      PCSZ pcszText, ULONG ulFlags,
+                                      ULONG ulTimeout);
+
+BOOL XSTAPI xstHideSysTrayIconBalloon(HWND hwnd, USHORT usId);
+
+BOOL XSTAPI xstQuerySysTrayIconRect(HWND hwnd, USHORT usId, PRECTL prclRect);
+
+ULONG XSTAPI xstGetSysTrayCreatedMsgId();
+
+ULONG XSTAPI xstGetSysTrayMaxTextLen();
+
+#if __cplusplus
+}
+#endif
+
+#endif // XSYSTRAY_API_HEADER_INCLUDED
+
Index: /trunk/src/3rdparty/os2/xsystray/plugin/plugin.pro
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/plugin/plugin.pro	(revision 835)
+++ /trunk/src/3rdparty/os2/xsystray/plugin/plugin.pro	(revision 835)
@@ -0,0 +1,14 @@
+TEMPLATE = lib
+
+CONFIG += dll
+QT =
+
+TARGET = xsystray
+
+SOURCES = w_xsystray.c
+
+DEF_FILE = w_xsystray.def
+
+INCLUDEPATH += .. ../apilib
+
+DESTDIR = $$[QT_INSTALL_PLUGINS]/xcenter
Index: /trunk/src/3rdparty/os2/xsystray/plugin/w_xsystray.c
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/plugin/w_xsystray.c	(revision 835)
+++ /trunk/src/3rdparty/os2/xsystray/plugin/w_xsystray.c	(revision 835)
@@ -0,0 +1,1859 @@
+
+/*
+ *@@sourcefile w_xsystray.c:
+ *      Extended system tray widget for XCenter/eCenter.
+ *
+ *      Implementation.
+ *
+ *      Copyright (C) 2009 Dmitry A. Kuminov
+ *
+ *      This file is part of the Extended system tray widget source package.
+ *      Extended system tray widget is free software; you can redistribute it
+ *      and/or modify it under the terms of the GNU General Public License as
+ *      published by the Free Software Foundation, in version 2 as it comes in
+ *      the "COPYING" file of the Extended system tray widget distribution. This
+ *      program is distributed in the hope that it will be useful, but WITHOUT
+ *      ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *      FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ *      more details.
+ */
+
+#if defined(__IBMC__) || defined(__IBMCPP__)
+#pragma strings(readonly)
+#endif
+
+/*
+ *  Suggested #include order:
+ *  1)  os2.h
+ *  2)  C library headers
+ *  3)  setup.h (code generation and debugging options)
+ *  4)  headers in helpers\
+ *  5)  at least one SOM implementation header (*.ih)
+ *  6)  dlgids.h, headers in shared\ (as needed)
+ *  7)  headers in implementation dirs (e.g. filesys\, as needed)
+ *  8)  #pragma hdrstop and then more SOM headers which crash with precompiled headers
+ */
+
+#if defined(__GNUC__) && defined(__EMX__)
+#define OS2EMX_PLAIN_CHAR
+#endif
+
+#define INCL_BASE
+#define INCL_PM
+#include <os2.h>
+
+// C library headers
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+// generic headers
+// If this file were part of the XWorkplace sources, we'd now include
+// the generic "setup.h" file, which has common set up code for every
+// single XWorkplace code file. But it's not, so we won't include that.
+// #include "setup.h"                      // code generation and debugging options
+
+// headers in /helpers
+// This would be the place to include headers from the "XWorkplace helpers".
+// But since we do a minimal sample here, we can't include those helpful
+// routines... again, see the src\widgets in the XWorkplace source code
+// for how these functions can be imported from XFLDR.DLL to avoid duplicate
+// code.
+// #include "helpers\dosh.h"               // Control Program helper routines
+// #include "helpers\gpih.h"               // GPI helper routines
+// #include "helpers\prfh.h"               // INI file helper routines;
+                                        // this include is required for some
+                                        // of the structures in shared\center.h
+// #include "helpers\winh.h"               // PM helper routines
+// #include "helpers\xstring.h"            // extended string helpers
+
+// XWorkplace implementation headers
+// If this file were part of the XCenter sources, we'd now include
+// "center.h" from the "include\shared" directory. Since we're not
+// part of the XCenter sources here, we include that file from the
+// "toolkit" directory in the binary release. That file is identical
+// to "include\shared\center.h" in the XWorkplace sources.
+#include "shared\center.h"              // public XCenter interfaces
+
+#include "w_xsystray.h"
+
+#if defined(__IBMC__) || defined(__IBMCPP__)
+#pragma hdrstop                     // VAC++ keeps crashing otherwise
+#endif
+
+// copy paste from helpers\comctl.h
+// @todo not necessary when we become part of XWorkplace
+
+#define TTN_NEEDTEXT        1000
+#define TTN_SHOW            1001
+#define TTN_POP             1002
+
+#define TTFMT_PSZ           0x01
+#define TTFMT_STRINGRES     0x02
+
+typedef struct _TOOLTIPTEXT
+{
+    HWND    hwndTooltip;
+    HWND    hwndTool;
+    ULONG   ulFormat;
+    PSZ     pszText;
+    HMODULE hmod;
+    ULONG   idResource;
+} TOOLTIPTEXT, *PTOOLTIPTEXT;
+
+#define TTM_FIRST                   (WM_USER + 1000)
+#define TTM_UPDATETIPTEXT           (TTM_FIRST + 9)
+#define TTM_SHOWTOOLTIPNOW          (TTM_FIRST + 17)
+
+// primitive debug logging to a file
+#if 0
+static void __LOG_WORKER(const char *fmt, ...)
+{
+    static FILE *f = NULL;
+    if (f == NULL)
+    {
+        f = fopen("c:\\xsystray.dbg", "a");
+        setbuf(f, NULL);
+    }
+    if (f != NULL)
+    {
+        va_list vl;
+        va_start(vl, fmt);
+        vfprintf(f, fmt, vl);
+        va_end(vl);
+    }
+}
+#define LOG(m)  do { __LOG_WORKER m; } while(0)
+#define LOGF(m) do { __LOG_WORKER("%s: ", __FUNCTION__); __LOG_WORKER m; } while(0)
+#else
+#define LOG(m)  do {} while (0)
+#define LOGF(m) do {} while (0)
+#endif
+
+/* ******************************************************************
+ *
+ *   Private definitions
+ *
+ ********************************************************************/
+
+/*
+ *@@ ICONDATA:
+ *      Per-icon data.
+ */
+
+typedef struct
+{
+    HWND        hwnd;
+                // associated window
+    USHORT      usId;
+                // icon ID
+    HPOINTER    hIcon;
+                // icon handle
+    ULONG       ulMsgId;
+                // message ID for notifications
+    PSZ         pszToolTip;
+                // icon tooltip (NULL if none)
+    BOOL        bIsToolTipShowing;
+                // whether the tooltip is currently shown
+    BOOL        bMemoryPoolGiven;
+                // TRUE if SYSTRAYDATA::pvMemoryPool is already given to
+                // the process hwnd belongs to
+
+} ICONDATA, *PICONDATA;
+
+/*
+ *@@ SYSTRAYDATA:
+ *      Global system tray data.
+ */
+
+typedef struct
+{
+    HWND        hwndServer;
+                // systtem tray server window handle
+    LONG        lIconWidth;
+                // system icon width in px
+    LONG        lIconHeight;
+                // system icon height in px
+    LONG        lIconPad;
+                // padding around each icon in px
+    PICONDATA   pIcons;
+                // array of icons currently shown in the system tray
+    size_t      cIcons;
+                // number of icons in the pIcons array
+    size_t      cIconsMax;
+                // maximum number of icons pIcons can fit
+    CHAR        szToolTip[sizeof(((PSYSTRAYCTLDATA)0)->u.icon.szToolTip)];
+                // "static" buffer for the tooltip (XCenter requirement)
+    PVOID       pvMemoryPool;
+                // memory pool for NOTIFYDATA structures
+
+} SYSTRAYDATA, *PSYSTRAYDATA;
+
+#define ICONARRAY_GROW  4
+        // number of element the icon array is increased by when there is no
+        // space for the newly added icon
+
+#define SERVER_MEMORYPOOL_SIZE 65536
+        // taking NOTIFYDATA size into account (<=32 B), this is enough for at
+        // least 2048 simultaneous notification messages, which (even taking
+        // slowly responsing clients into account) sounds sane since in most
+        // cases the structure is freed once it reaches the target event queue
+        // and before a message created as a copy of it is sent to the target
+        // window procedure
+
+#define TID_CHECKALIVE          1
+         // timer that checks if windows associated with icons are still alive
+#define TID_CHECKALIVE_TIMEOUT  2000 // ms
+         // how often to perform alive checks
+
+static ULONG WM_XST_CREATED = 0;
+             // identity of the WM_XST_CREATED message taken from the atom table
+static ULONG WM_XST_NOTIFY = 0;
+             // identity of the WM_XST_NOTIFY message taken from the atom table
+
+static ULONG QWL_USER_SERVER_DATA = 0;
+             // offset to the PXCENTERWIDGET pointer in the widget data array
+
+static
+VOID WgtXSysTrayUpdateAfterIconAddRemove(PXCENTERWIDGET pWidget);
+
+/* ******************************************************************
+ *
+ *   XCenter widget class definition
+ *
+ ********************************************************************/
+
+/*
+ *      This contains the name of the PM window class and
+ *      the XCENTERWIDGETCLASS definition(s) for the widget
+ *      class(es) in this DLL.
+ *
+ *      The address of this structure (or an array of these
+ *      structures, if there were several widget classes in
+ *      this plugin) is returned by the "init" export
+ *      (WgtInitModule).
+ */
+
+static const XCENTERWIDGETCLASS G_WidgetClasses[] =
+{
+    {
+        WNDCLASS_WIDGET_XSYSTRAY,   // PM window class name
+        0,                          // additional flag, not used here
+        INTCLASS_WIDGET_XSYSTRAY,   // internal widget class name
+        HUMANSTR_WIDGET_XSYSTRAY,   // widget class name displayed to user
+        WGTF_UNIQUEGLOBAL |         // widget class flags
+        WGTF_TOOLTIP,
+        NULL                        // no settings dialog
+    }
+};
+
+/* ******************************************************************
+ *
+ *   Function imports from XFLDR.DLL
+ *
+ ********************************************************************/
+
+/*
+ *      To reduce the size of the widget DLL, it can
+ *      be compiled with the VAC subsystem libraries.
+ *      In addition, instead of linking frequently
+ *      used helpers against the DLL again, you can
+ *      import them from XFLDR.DLL, whose module handle
+ *      is given to you in the INITMODULE export.
+ *
+ *      Note that importing functions from XFLDR.DLL
+ *      is _not_ a requirement. We can't do this in
+ *      this minimal sample anyway without having access
+ *      to the full XWorkplace source code.
+ *
+ *      If you want to know how you can import the useful
+ *      functions from XFLDR.DLL to use them in your widget
+ *      plugin, again, see src\widgets in the XWorkplace sources.
+ *      The actual imports would then be made by WgtInitModule.
+ */
+
+// @todo the function declarations should become not necessary when we move into
+// XWorkplace. Let's hope the prototypes will not change until then. Let's also
+// pray that XFLDRxxx.DLL has the _Optlink calling convention for exports and
+// not the default __cdecl (which happens if it builds with GCC according to the
+// XWPENTRY definition in the current xwphelpers sources)
+
+#define XWPENTRY _Optlink
+
+typedef VOID XWPENTRY GPIHDRAW3DFRAME(HPS hps,
+                                      PRECTL prcl,
+                                      USHORT usWidth,
+                                      LONG lColorLeft,
+                                      LONG lColorRight);
+typedef GPIHDRAW3DFRAME *PGPIHDRAW3DFRAME;
+PGPIHDRAW3DFRAME pgpihDraw3DFrame = NULL;
+
+typedef struct _RESOLVEFUNCTION
+{
+    const char  *pcszFunctionName;
+    PFN         *ppFuncAddress;
+} RESOLVEFUNCTION, *PRESOLVEFUNCTION;
+RESOLVEFUNCTION G_aImports[] =
+{
+    { "gpihDraw3DFrame", (PFN*)&pgpihDraw3DFrame },
+};
+
+/* ******************************************************************
+ *
+ *   Private widget instance data
+ *
+ ********************************************************************/
+
+// None presently. The samples in src\widgets in the XWorkplace
+// sources cleanly separate setup string data from other widget
+// instance data to allow for easier manipulation with settings
+// dialogs. We have skipped this for the minimal sample.
+
+/* ******************************************************************
+ *
+ *   Widget setup management
+ *
+ ********************************************************************/
+
+// None presently. See above.
+
+/* ******************************************************************
+ *
+ *   Widget settings dialog
+ *
+ ********************************************************************/
+
+// None currently. To see how a setup dialog can be done,
+// see the "window list" widget in the XWorkplace sources
+// (src\widgets\w_winlist.c).
+
+/* ******************************************************************
+ *
+ *   Callbacks stored in XCENTERWIDGETCLASS
+ *
+ ********************************************************************/
+
+// If you implement a settings dialog, you must write a
+// "show settings dlg" function and store its function pointer
+// in XCENTERWIDGETCLASS.
+
+/* ******************************************************************
+ *
+ *   Helper methods
+ *
+ ********************************************************************/
+
+/*
+ *@@ FreeIconData:
+ *      Frees all members of the ICONDATA structure and resets them to 0.
+ */
+
+static
+VOID FreeIconData(PICONDATA pData)
+{
+    pData->hwnd = NULLHANDLE;
+    pData->usId = 0;
+    if (pData->hIcon != NULLHANDLE)
+    {
+        WinDestroyPointer(pData->hIcon);
+        pData->hIcon = NULLHANDLE;
+
+    }
+    pData->ulMsgId = 0;
+    if (pData->pszToolTip)
+    {
+        free(pData->pszToolTip);
+        pData->pszToolTip = NULL;
+    }
+}
+
+/*
+ *@@ FindIconData:
+ *      Searches for the icon with the given identity. Returns NULL if not
+ *      found.
+ */
+
+static
+PICONDATA FindIconData(PSYSTRAYDATA pSysTrayData,
+                       HWND hwnd,       // in: associated window handle
+                       USHORT usId,     // in: icon ID
+                       size_t *pIdx)    // out: index of the icon in the icon array
+                                        // (optional, may be NULL)
+{
+    size_t i;
+    for (i = 0; i < pSysTrayData->cIcons; ++i)
+    {
+        if (pSysTrayData->pIcons[i].hwnd == hwnd &&
+            pSysTrayData->pIcons[i].usId == usId)
+        {
+            if (pIdx)
+                *pIdx = i;
+            return &pSysTrayData->pIcons[i];
+        }
+    }
+
+    if (pIdx)
+        *pIdx = i;
+    return NULL;
+}
+
+/*
+ *@@ FindIconDataAtPt:
+ *      Searches for the icon under the given point.
+ *      Returns NULL if no icon found (e.g. the pad space).
+ *
+ *      Refer to WgtPaint() for system tray geometry description.
+ */
+
+static
+PICONDATA FindIconDataAtPt(PXCENTERWIDGET pWidget,
+                           PPOINTL pptl,    // point coordinates (relative to systray)
+                           size_t *pIdx)    // out: index of the icon in the icon array
+                                            // (optional, may be NULL)
+{
+    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
+
+    SWP     swp;
+    RECTL   rcl;
+    BOOL    bLeftToRight;
+    LONG    y, lIconStep;
+    size_t  i;
+
+    // start with invalid index index
+    if (pIdx)
+        *pIdx = pSysTrayData->cIcons;
+
+    WinQueryWindowPos(pWidget->hwndWidget, &swp);
+    WinQueryWindowRect(pWidget->pGlobals->hwndClient, &rcl);
+
+    y = (swp.cy - pSysTrayData->lIconHeight) / 2;
+    if (pptl->y < y || pptl->y >= y + pSysTrayData->lIconHeight)
+        return NULL; // hit pad space
+
+    // detect the direction
+    bLeftToRight = swp.x + swp.cx / 2 < (rcl.xRight / 2);
+
+    lIconStep = pSysTrayData->lIconWidth + pSysTrayData->lIconPad;
+
+    // which icon is that?
+    if (bLeftToRight)
+    {
+        i = pptl->x / lIconStep;
+        if (pptl->x % lIconStep < pSysTrayData->lIconPad)
+            return NULL; // hit pad space
+    }
+    else
+    {
+        i = (swp.cx - pptl->x - 1) / lIconStep;
+        if ((swp.cx - pptl->x - 1) % lIconStep < pSysTrayData->lIconPad)
+            return NULL; // hit pad space
+    }
+    if (i >= pSysTrayData->cIcons)
+        return NULL; // hit pad space
+
+    if (pIdx)
+        *pIdx = i;
+    return &pSysTrayData->pIcons[i];
+}
+
+static
+VOID FreeSysTrayData(PSYSTRAYDATA pSysTrayData)
+{
+    // destroy the server
+    if (pSysTrayData->hwndServer != NULLHANDLE)
+    {
+        WinDestroyWindow(pSysTrayData->hwndServer);
+        pSysTrayData->hwndServer = NULLHANDLE;
+    }
+
+    // free all system tray data
+    if (pSysTrayData->pvMemoryPool)
+    {
+        DosFreeMem(pSysTrayData->pvMemoryPool);
+    }
+    if (pSysTrayData->pIcons)
+    {
+        size_t i;
+        for (i = 0; i < pSysTrayData->cIcons; ++i)
+            FreeIconData(&pSysTrayData->pIcons[i]);
+        pSysTrayData->cIcons = 0;
+        free(pSysTrayData->pIcons);
+        pSysTrayData->pIcons = NULL;
+    }
+
+    free(pSysTrayData);
+}
+
+/*
+ *@@ AllocNotifyDataPtr:
+ *      Allocates a SYSTRAYCTLDATA struct in the pool of shared memory on belalf
+ *      of the client window identified by pIconData->hwnd.
+ *
+ *      If there is no free space in the pool, it returns NULL. On success,
+ *      fills in msg and mp1 fields of the returned structure using the
+ *      information provided in pIconData.
+ *
+ *      Note that the allocated structure is supposed to be freed using
+ *      FreeNotifyDataPtr by the client-side API implementation in another
+ *      process.
+ */
+static
+PNOTIFYDATA AllocNotifyDataPtr(PVOID pvMemoryPool,  // in: memory pool base address
+                               PICONDATA pIconData) // in: icon data
+{
+    // NOTE: we cannot use DosSubAllocMem() and friends since we want to be able
+    // to free blocks allocated to clients which death we detect but we cannot
+    // be sure about the layout of memory DosSub API uses. Therefore, we provide
+    // our own sub-allocation scheme which is rather simple because we need to
+    // allocate equally sized blocks (each is NOTIFYDATA struct). The memory
+    // pool is laid out as follows: MEMPOOLHDR followed by a number of
+    // MEMPOOLBLK.
+
+    APIRET arc;
+    PID pid;
+    TID tid;
+    PMEMPOOLHDR pHdr;
+    ULONG ulMax, ulNeedsCommit, ulNext, ulCurr;
+    PNOTIFYDATA pData;
+
+    LOGF(("pvMemoryPool  %p\n", pvMemoryPool));
+    LOGF(("hwnd          %lx\n", pIconData->hwnd));
+
+    if (!pIconData->bMemoryPoolGiven)
+    {
+        LOGF(("Giving memory pool to %lx\n", pIconData->hwnd));
+
+        arc = ERROR_INVALID_HANDLE;
+        if (WinQueryWindowProcess(pIconData->hwnd, &pid, &tid))
+            arc = DosGiveSharedMem(pvMemoryPool, pid, PAG_READ | PAG_WRITE);
+        if (arc != NO_ERROR)
+            return NULL;
+
+        pIconData->bMemoryPoolGiven = TRUE;
+    }
+
+    pHdr = (PMEMPOOLHDR)pvMemoryPool;
+
+    // maximum address that is still enough for a block
+    ulMax = pHdr->ulBeyond - sizeof(MEMPOOLBLK);
+
+    ulNeedsCommit = pHdr->ulNeedsCommit;
+    ulNext = pHdr->ulNext;
+    ulCurr = ulNext;
+
+    LOGF(("ulNeedsCommit %p\n", ulNeedsCommit));
+    LOGF(("ulNext        %p\n", ulNext));
+
+    do
+    {
+        if (ulCurr >= ulNeedsCommit)
+        {
+            // commit more memory; it's OK two or more threads will do the same
+            DosSetMem((PVOID)ulNeedsCommit, 4096,
+                      PAG_COMMIT | PAG_READ | PAG_WRITE);
+            // advance the address (only if nobody has already done so -- they
+            // could already commit more than we did)
+            __atomic_cmpxchg32((volatile uint32_t *)&pHdr->ulNeedsCommit,
+                               ulNeedsCommit + 4096, ulNeedsCommit);
+        }
+
+        if (__atomic_cmpxchg32((volatile uint32_t *)ulCurr,
+                               pIconData->hwnd, NULLHANDLE))
+            break;
+
+        ulCurr += sizeof(MEMPOOLBLK);
+        if (ulCurr > ulMax)
+            // start over
+            ulCurr = ((ULONG)pvMemoryPool) + sizeof(MEMPOOLHDR);
+
+        if (ulCurr == ulNext)
+            return NULL; // no free blocks!
+    }
+    while (1);
+
+    LOGF(("ulCurr        %p\n", ulCurr));
+
+    // memorize a pointer to the new struct
+    pData = &((PMEMPOOLBLK)ulCurr)->NotifyData;
+
+    // advance to the next possibly free block
+    ulCurr += sizeof(MEMPOOLBLK);
+    if (ulCurr > ulMax)
+        // start over
+        ulCurr = ((ULONG)pvMemoryPool) + sizeof(MEMPOOLHDR);
+
+    // store the new next address until someone else has already done that
+    __atomic_cmpxchg32((volatile uint32_t *)&pHdr->ulNext, ulCurr, ulNext);
+
+    // fill in parts of the allocated NOTIFYDATA
+    memset(pData, 0, sizeof(*pData));
+
+    pData->msg = pIconData->ulMsgId;
+    pData->mp1 = MPFROMSHORT(pIconData->usId);
+
+    return pData;
+}
+
+/*
+ *@@ PostNotifyMsg:
+ *      Posts WM_XST_NOTIFY to the given client window. Frees pNotifyData if
+ *      posting fails.
+ */
+
+VOID PostNotifyMsg(PSYSTRAYDATA pSysTrayData, HWND hwnd,
+                   PNOTIFYDATA pNotifyData)
+{
+    if (!WinPostMsg(hwnd, WM_XST_NOTIFY, pNotifyData, pSysTrayData->pvMemoryPool))
+        FreeNotifyDataPtr(pSysTrayData->pvMemoryPool, hwnd, pNotifyData);
+}
+
+/*
+ *@@ DrawPointer:
+ *      Draws a pointer in a presentation space.
+ */
+
+static
+BOOL DrawPointer(HPS hps, LONG lx, LONG ly, HPOINTER hptrPointer, BOOL bMini)
+{
+    return WinDrawPointer(hps, lx, ly, hptrPointer, bMini ? DP_MINI : DP_NORMAL);
+    // @todo: for icons with real alpha, do manual alpha blending
+}
+
+/* ******************************************************************
+ *
+ *   PM window class implementation
+ *
+ ********************************************************************/
+
+/*
+ *      This code has the actual PM window class.
+ *
+ */
+
+/*
+ *@@ MwgtControl:
+ *      implementation for WM_CONTROL in fnwpXSysTray.
+ *
+ *      The XCenter communicates with widgets thru
+ *      WM_CONTROL messages. At the very least, the
+ *      widget should respond to XN_QUERYSIZE because
+ *      otherwise it will be given some dumb default
+ *      size.
+ */
+
+static
+BOOL WgtControl(PXCENTERWIDGET pWidget,
+                MPARAM mp1,
+                MPARAM mp2)
+{
+    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
+    BOOL brc = FALSE;
+
+    USHORT  usID = SHORT1FROMMP(mp1),
+            usNotifyCode = SHORT2FROMMP(mp1);
+
+    // is this from the XCenter client?
+    if (usID == ID_XCENTER_CLIENT)
+    {
+        // yes:
+
+        switch (usNotifyCode)
+        {
+            /*
+             * XN_QUERYSIZE:
+             *      XCenter wants to know our size.
+             */
+
+            case XN_QUERYSIZE:
+            {
+                PSIZEL pszl = (PSIZEL)mp2;
+                LONG pad = pSysTrayData->lIconPad;
+                size_t cnt = pSysTrayData->cIcons;
+                // desired width
+                if (cnt)
+                    pszl->cx = pad + (pSysTrayData->lIconWidth + pad) * cnt;
+                else
+                    pszl->cx = 0;
+                // desired minimum height
+                pszl->cy = pSysTrayData->lIconHeight + pad * 2;
+                brc = TRUE;
+            }
+            break;
+
+        }
+    }
+    else if (usID == ID_XCENTER_TOOLTIP)
+    {
+        PICONDATA pIconData;
+        POINTL ptl;
+
+        WinQueryMsgPos(pWidget->habWidget, &ptl);
+        // make the coordinates systray-relative
+        WinMapWindowPoints(HWND_DESKTOP, pWidget->hwndWidget, &ptl, 1);
+
+        pIconData = FindIconDataAtPt(pWidget, &ptl, NULL);
+
+        switch (usNotifyCode)
+        {
+            case TTN_NEEDTEXT:
+            {
+                LOGF(("TTN_NEEDTEXT\n"));
+
+                PTOOLTIPTEXT pttt = (PTOOLTIPTEXT)mp2;
+                pttt->ulFormat = TTFMT_PSZ;
+
+                if (!pIconData || !pIconData->pszToolTip)
+                    pttt->pszText = NULL;
+                else
+                {
+                    strncpy(pSysTrayData->szToolTip, pIconData->pszToolTip,
+                            sizeof(pSysTrayData->szToolTip) - 1);
+                    // be on the safe side
+                    pSysTrayData->szToolTip[sizeof(pSysTrayData->szToolTip) - 1] = '\0';
+
+                    pttt->pszText = pSysTrayData->szToolTip;
+                }
+
+                LOGF((" pszText '%s'\n", pttt->pszText));
+            }
+            break;
+
+            case TTN_SHOW:
+                if (pIconData)
+                    pIconData->bIsToolTipShowing = TRUE;
+            break;
+
+            case TTN_POP:
+                if (pIconData)
+                    pIconData->bIsToolTipShowing = FALSE;
+            break;
+        }
+    }
+
+    return brc;
+}
+
+/*
+ *@@ WgtPaint:
+ *      implementation for WM_PAINT in fnwpXSysTray.
+ *
+ *      Draws all the icons. If the widget's center is located to the left from
+ *      the XCenter's center, icons go left to right. Otherwise, they go right
+ *      to left.
+ *
+ *      NOTE: This function must be keept in sync with FindIconDataAtPt() and
+ *      SYSTRAYCMD_QUERYRECT in terms of system tray geometry.
+ */
+/*
+        +---------------------------+  p = lIconPad
+        |     p                     |  w = lIconWidth
+        |   +-------+   +-------+   |  h = lIconHeight
+        | p |   w   | p |   w   | p |
+        |   |      h|   |      h|   |
+        |   |       |   |       |   |  If "Frame around statics" is on in XCenter
+        |   +-------+   +-------+   |  properties, then a 1 px 3D frame is drawn
+        |     p                     |  within the pad area. So, lIconPad must
+        +---------------------------+  be at least 2 px.
+ */
+
+static
+VOID WgtPaint(HWND hwnd,
+              PXCENTERWIDGET pWidget)
+{
+    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
+    HPS hps;
+    RECTL rclPaint;
+
+    if ((hps = WinBeginPaint(hwnd, NULLHANDLE, &rclPaint)))
+    {
+        SWP     swp;
+        RECTL   rcl;
+        BOOL    bLeftToRight;
+        LONG    x, y, lIconStep;
+        size_t  i;
+
+        WinQueryWindowPos(hwnd, &swp);
+        WinQueryWindowRect(pWidget->pGlobals->hwndClient, &rcl);
+
+        // correct the paint area
+        // @todo find out why it exceeds the window bounds
+        if (rclPaint.xLeft < 0)
+            rclPaint.xLeft = 0;
+        if (rclPaint.xRight > swp.cx)
+            rclPaint.xRight = swp.cx;
+        if (rclPaint.yBottom < 0)
+            rclPaint.yBottom = 0;
+        if (rclPaint.yTop > swp.cy)
+            rclPaint.yTop = swp.cy;
+
+        LOGF(("rclPaint %d,%d-%d,%d\n",
+              rclPaint.xLeft, rclPaint.yBottom, rclPaint.xRight, rclPaint.yTop));
+
+        // switch HPS to RGB mode
+        GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
+
+        // draw icons left to right if our center is closer to the left edge
+        // of XCenter and right to left otherwise
+        bLeftToRight = swp.x + swp.cx / 2 < (rcl.xRight / 2);
+
+        WinFillRect(hps, &rclPaint,
+                    WinQuerySysColor(HWND_DESKTOP, SYSCLR_DIALOGBACKGROUND, 0));
+
+        if ((pWidget->pGlobals->flDisplayStyle & XCS_SUNKBORDERS))
+        {
+            rcl.xLeft = 0;
+            rcl.yBottom = 0;
+            rcl.xRight = swp.cx - 1;
+            rcl.yTop = swp.cy - 1;
+            pgpihDraw3DFrame(hps, &rcl, 1,
+                             pWidget->pGlobals->lcol3DDark,
+                             pWidget->pGlobals->lcol3DLight);
+        }
+
+        // always center the icon vertically (we may be given more height than
+        // we requested)
+        y = (swp.cy - pSysTrayData->lIconHeight) / 2;
+
+        if (bLeftToRight)
+            x = pSysTrayData->lIconPad;
+        else
+            x = swp.cx - pSysTrayData->lIconPad - pSysTrayData->lIconWidth;
+
+        lIconStep = pSysTrayData->lIconWidth + pSysTrayData->lIconPad;
+
+        // where to start from?
+        if (bLeftToRight)
+        {
+            i = rclPaint.xLeft / lIconStep;
+            x = pSysTrayData->lIconPad + i * lIconStep;
+        }
+        else
+        {
+            i = (swp.cx - rclPaint.xRight) / lIconStep;
+            x = swp.cx - (i + 1) * lIconStep;
+            // negate the step, for convenience
+            lIconStep = -lIconStep;
+        }
+
+        // draw as many icons as we can / need
+        for (; i < pSysTrayData->cIcons; ++i)
+        {
+            if (x >= rclPaint.xRight)
+                break;
+
+            DrawPointer(hps, x, y, pSysTrayData->pIcons[i].hIcon, DP_MINI);
+            x += lIconStep;
+        }
+
+        WinEndPaint(hps);
+    }
+}
+
+/*
+ *@@ WgtMouse:
+ *      implementation for WM_BUTTONxyyy in fnwpXSysTray.
+ *
+ *      Posts a notification to the window associated with the icon and returns
+ *      TRUE if this mouse message is within the icon bounds. Otherwise returns
+ *      FALSE.
+ *
+ *      Refer to WgtPaint for more details about the widget geometry.
+ */
+
+static
+BOOL WgtMouse(HWND hwnd, ULONG msg, MRESULT mp1, MRESULT mp2,
+              PXCENTERWIDGET pWidget)
+{
+    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
+
+    POINTL  ptl;
+
+    PICONDATA   pIconData;
+    PNOTIFYDATA pNotifyData;
+
+    ptl.x = ((PPOINTS)&mp1)->x;
+    ptl.y = ((PPOINTS)&mp1)->y;
+
+    LOGF(("msg %x ptl %ld,%ld\n", msg, ptl.x, ptl.y));
+
+    pIconData = FindIconDataAtPt(pWidget, &ptl, NULL);
+    if (!pIconData)
+        return FALSE; // hit pad space
+
+    LOGF(("hwnd  %x\n", pIconData->hwnd));
+    LOGF(("usId  %d\n", pIconData->usId));
+    LOGF(("hIcon %x\n", pIconData->hIcon));
+
+    // make the coordinates global
+    WinMapWindowPoints(hwnd, HWND_DESKTOP, &ptl, 1);
+
+    // allocate a NOTIFYDATA struct
+    pNotifyData = AllocNotifyDataPtr(pSysTrayData->pvMemoryPool, pIconData);
+    if (!pNotifyData)
+        return FALSE;
+
+    switch (msg)
+    {
+        case WM_HSCROLL:
+        case WM_VSCROLL:
+            pNotifyData->mp1 += XST_IN_WHEEL << 16;
+            pNotifyData->u.WheelMsg.ulWheelMsg = msg;
+            pNotifyData->u.WheelMsg.ptsPointerPos.x = ptl.x;
+            pNotifyData->u.WheelMsg.ptsPointerPos.y = ptl.y;
+            pNotifyData->u.WheelMsg.usCmd = SHORT2FROMMP(mp2);
+            pNotifyData->mp2 = &pNotifyData->u.WheelMsg;
+        break;
+
+        case WM_CONTEXTMENU:
+            pNotifyData->mp1 += XST_IN_CONTEXT << 16;
+            pNotifyData->u.ContextMsg.ptsPointerPos.x = ptl.x;
+            pNotifyData->u.ContextMsg.ptsPointerPos.y = ptl.y;
+            pNotifyData->u.ContextMsg.fPointer = TRUE;
+            pNotifyData->mp2 = &pNotifyData->u.ContextMsg;
+        break;
+
+        default:
+            pNotifyData->mp1 += XST_IN_MOUSE << 16;
+            pNotifyData->u.MouseMsg.ulMouseMsg = msg;
+            pNotifyData->u.MouseMsg.ptsPointerPos.x = ptl.x;
+            pNotifyData->u.MouseMsg.ptsPointerPos.y = ptl.y;
+            pNotifyData->u.MouseMsg.fsHitTestRes = SHORT1FROMMP(mp2);
+            pNotifyData->u.MouseMsg.fsFlags = SHORT2FROMMP(mp2);
+            pNotifyData->mp2 = &pNotifyData->u.MouseMsg;
+        break;
+    }
+
+    PostNotifyMsg(pSysTrayData, pIconData->hwnd, pNotifyData);
+
+    return TRUE;
+}
+
+/*
+ *@@ fnwpXSysTray:
+ *      window procedure for the Extended system tray widget class.
+ *
+ *      There are a few rules which widget window procs
+ *      must follow. See XCENTERWIDGETCLASS in center.h
+ *      for details.
+ *
+ *      Other than that, this is a regular window procedure
+ *      which follows the basic rules for a PM window class.
+ */
+
+static
+MRESULT EXPENTRY fnwpXSysTray(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
+{
+    // get widget data from QWL_USER (stored there by WM_CREATE)
+    PXCENTERWIDGET pWidget = (PXCENTERWIDGET)WinQueryWindowPtr(hwnd, QWL_USER);
+                    // this ptr is valid after WM_CREATE
+
+    switch (msg)
+    {
+        /*
+         * WM_CREATE:
+         *      as with all widgets, we receive a pointer to the
+         *      XCENTERWIDGET in mp1, which was created for us.
+         *
+         *      The first thing the widget MUST do on WM_CREATE
+         *      is to store the XCENTERWIDGET pointer (from mp1)
+         *      in the QWL_USER window word by calling:
+         *
+         *          WinSetWindowPtr(hwnd, QWL_USER, mp1);
+         *
+         *      We could use XCENTERWIDGET.pUser for allocating
+         *      another private memory block for our own stuff,
+         *      for example to be able to store fonts and colors.
+         *      We ain't doing this in the minimal sample.
+         */
+
+        case WM_CREATE:
+        {
+            LOGF(("WM_CREATE\n"));
+
+            PSYSTRAYDATA pSysTrayData = NULL;
+            APIRET arc;
+
+            WinSetWindowPtr(hwnd, QWL_USER, mp1);
+            if (    (!(pWidget = (PXCENTERWIDGET)mp1))
+                 || (!pWidget->pfnwpDefWidgetProc)
+               )
+                // shouldn't happen... stop window creation!!
+                return (MRESULT)TRUE;
+
+            pSysTrayData = malloc(sizeof(*pSysTrayData));
+            if (pSysTrayData == NULL)
+                return (MRESULT)TRUE;
+
+            // initialize the SYSTRAYDATA structure
+            memset(pSysTrayData, 0, sizeof(*pSysTrayData));
+            pSysTrayData->lIconWidth = WinQuerySysValue(HWND_DESKTOP, SV_CXICON) / 2;
+            pSysTrayData->lIconHeight = WinQuerySysValue(HWND_DESKTOP, SV_CYICON) / 2;
+            pSysTrayData->lIconPad = pSysTrayData->lIconHeight / 8;
+            pSysTrayData->cIconsMax = ICONARRAY_GROW;
+            pSysTrayData->pIcons = malloc(sizeof(*pSysTrayData->pIcons) *
+                                          pSysTrayData->cIconsMax);
+            if (pSysTrayData->pIcons == NULL)
+            {
+                FreeSysTrayData(pSysTrayData);
+                return (MRESULT)TRUE;
+            }
+            pSysTrayData->cIcons = 0;
+
+            // Allocate the memory pool for NOTIFYDATA structs (we don't
+            // PAG_COMMIT all memory, AllocNotifyDataPtr() will do so as needed)
+            arc = DosAllocSharedMem((PVOID)&pSysTrayData->pvMemoryPool, NULL,
+                                    SERVER_MEMORYPOOL_SIZE,
+                                    PAG_READ | PAG_WRITE | OBJ_GIVEABLE);
+            if (arc == NO_ERROR)
+            {
+                PMEMPOOLHDR pHdr = (PMEMPOOLHDR)pSysTrayData->pvMemoryPool;
+                arc = DosSetMem(pSysTrayData->pvMemoryPool, 4096,
+                                PAG_COMMIT | PAG_READ | PAG_WRITE);
+                if (arc == NO_ERROR)
+                {
+                    pHdr->ulBeyond = (ULONG)pSysTrayData->pvMemoryPool +
+                                     SERVER_MEMORYPOOL_SIZE;
+                    pHdr->ulNeedsCommit = (ULONG)pSysTrayData->pvMemoryPool +
+                                           4096;
+                    pHdr->ulNext = (ULONG)pSysTrayData->pvMemoryPool +
+                                   sizeof(MEMPOOLHDR);
+                }
+            }
+            if (arc != NO_ERROR)
+            {
+                FreeSysTrayData(pSysTrayData);
+                return (MRESULT)TRUE;
+            }
+
+            // create the "server" window (note that we pass the XCENTERWIDGET
+            // pointer on to it)
+            pSysTrayData->hwndServer =
+                WinCreateWindow(HWND_DESKTOP, WNDCLASS_WIDGET_XSYSTRAY_SERVER,
+                                NULL, WS_MINIMIZED,
+                                0, 0, 0, 0,
+                                HWND_DESKTOP, HWND_BOTTOM,
+                                0, mp1, NULL);
+            if (pSysTrayData->hwndServer == NULLHANDLE)
+            {
+                FreeSysTrayData(pSysTrayData);
+                return (MRESULT)TRUE;
+            }
+
+            pWidget->pUser = pSysTrayData;
+
+            // inform all interested parties that we are fired up
+            WinBroadcastMsg(HWND_DESKTOP, WM_XST_CREATED,
+                            NULL, NULL, BMSG_POST);
+
+            return FALSE; // confirm success
+        }
+        break;
+
+        /*
+         * WM_DESTROY:
+         *      clean up. This _must_ be passed on to
+         *      ctrDefWidgetProc.
+         */
+
+        case WM_DESTROY:
+        {
+            LOGF(("WM_DESTROY\n"));
+
+            PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
+
+            // stop the check alive timer
+            WinStopTimer(pWidget->habWidget, pSysTrayData->hwndServer,
+                         TID_CHECKALIVE);
+
+            FreeSysTrayData(pSysTrayData);
+            pWidget->pUser = NULL;
+
+            // We _MUST_ pass this on, or the default widget proc
+            // cannot clean up, so break
+        }
+        break;
+
+        /*
+         * WM_CONTROL:
+         *      process notifications/queries from the XCenter.
+         */
+
+        case WM_CONTROL:
+            return (MPARAM)WgtControl(pWidget, mp1, mp2);
+        break;
+
+        /*
+         * WM_PAINT:
+         *      well, paint the widget.
+         */
+
+        case WM_PAINT:
+            WgtPaint(hwnd, pWidget);
+            return (MRESULT)TRUE;
+        break;
+
+        /*
+         * WM_PRESPARAMCHANGED:
+         *      A well-behaved widget would intercept
+         *      this and store fonts and colors.
+         */
+
+        /* case WM_PRESPARAMCHANGED:
+        break; */
+
+        /*
+         * All mouse click and wheel events:
+         *      Note that we hide WM_CONTEXTMENU from XCenter when it is within
+         *      the icon bounds as it's a responsibility of the application
+         *      owning the icon to show it.
+         */
+
+        case WM_BUTTON1UP:
+        case WM_BUTTON1DOWN:
+        case WM_BUTTON1CLICK:
+        case WM_BUTTON1DBLCLK:
+        case WM_BUTTON2UP:
+        case WM_BUTTON2DOWN:
+        case WM_BUTTON2CLICK:
+        case WM_BUTTON2DBLCLK:
+        case WM_BUTTON3UP:
+        case WM_BUTTON3DOWN:
+        case WM_BUTTON3CLICK:
+        case WM_BUTTON3DBLCLK:
+        case WM_CONTEXTMENU:
+        case WM_VSCROLL:
+        case WM_HSCROLL:
+        {
+            if (WgtMouse(hwnd, msg, mp1, mp2, pWidget))
+                return (MRESULT)TRUE;
+            // we didn't hit the icon, pass it on to XCenter
+        }
+        break;
+
+        default:
+            break;
+
+    } // end switch(msg)
+
+    return pWidget->pfnwpDefWidgetProc(hwnd, msg, mp1, mp2);
+}
+
+/*
+ *@@ WgtXSysTrayUpdateAfterIconAddRemove:
+ *      Name says it all.
+ */
+
+static
+VOID WgtXSysTrayUpdateAfterIconAddRemove(PXCENTERWIDGET pWidget)
+{
+    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
+
+    if (pSysTrayData->cIcons == 1)
+    {
+        // start a timer to perform "is window alive" checks
+        WinStartTimer(pWidget->habWidget, pSysTrayData->hwndServer,
+                      TID_CHECKALIVE,
+                      TID_CHECKALIVE_TIMEOUT);
+    }
+    else
+    if (pSysTrayData->cIcons == 0)
+    {
+        // stop the check alive timer
+        WinStopTimer(pWidget->habWidget, pSysTrayData->hwndServer,
+                     TID_CHECKALIVE);
+    }
+
+    // ask XCenter to take our new size into account (this will also
+    // invalidate us)
+    WinPostMsg(pWidget->pGlobals->hwndClient,
+               XCM_REFORMAT,
+               (MPARAM)XFMF_GETWIDGETSIZES,
+               0);
+}
+
+/*
+ *@@ WgtXSysTrayControl:
+ *      implementation for WM_XST_CONTROL in fnwpXSysTrayServer.
+ *
+ *      Serves as an entry point for all client-side API requests to the
+ *      Extended system tray.
+ *
+ *      Note that this message is being sent from another process which is
+ *      awaiting for an answer, so it must return as far as possible (by
+ *      rescheduling any potentially long term operation for another cycle).
+ */
+
+static
+ULONG WgtXSysTrayControl(HWND hwnd, PXCENTERWIDGET pWidget,
+                         PSYSTRAYCTLDATA pCtlData)
+{
+    BOOL    brc = FALSE;
+    ULONG   xrc = XST_FAIL;
+
+    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
+
+    switch (pCtlData->ulCommand)
+    {
+        case SYSTRAYCMD_GETVERSION:
+        {
+            LOGF(("SYSTRAYCMD_GETVERSION\n"));
+
+            pCtlData->bAcknowledged = TRUE;
+            pCtlData->u.version.ulMajor = XSYSTRAY_VERSION_MAJOR;
+            pCtlData->u.version.ulMajor = XSYSTRAY_VERSION_MINOR;
+            pCtlData->u.version.ulRevision = XSYSTRAY_VERSION_REVISION;
+            xrc = XST_OK;
+        }
+        break;
+
+        case SYSTRAYCMD_ADDICON:
+        {
+            POINTERINFO Info;
+            HPOINTER hIcon = NULLHANDLE;
+            size_t i;
+            PICONDATA pData;
+
+            LOGF(("SYSTRAYCMD_ADDICON\n"));
+            LOGF((" hwnd       %x\n", pCtlData->hwndSender));
+            LOGF((" usId       %d\n", pCtlData->u.icon.usId));
+            LOGF((" hIcon      %x\n", pCtlData->u.icon.hIcon));
+            LOGF((" szToolTip  '%s'\n", pCtlData->u.icon.szToolTip));
+
+            pCtlData->bAcknowledged = TRUE;
+
+            // make a private copy of the provided icon (it will get lost after
+            // we return from this message)
+            brc = WinQueryPointerInfo(pCtlData->u.icon.hIcon, &Info);
+            if (!brc)
+                break;
+            hIcon = WinCreatePointerIndirect(HWND_DESKTOP, &Info);
+            if (hIcon == NULLHANDLE)
+                break;
+
+            pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
+                                 pCtlData->u.icon.usId, &i);
+            if (pData)
+            {
+                LOGF((" Replacing with hIcon %x\n", hIcon));
+
+                // try update the tooltip first
+                free(pData->pszToolTip);
+                pData->pszToolTip = NULL;
+                if (pCtlData->u.icon.szToolTip[0] != '\0')
+                {
+                    pData->pszToolTip = strdup(pCtlData->u.icon.szToolTip);
+                    if (!pData->pszToolTip)
+                    {
+                        WinDestroyPointer(hIcon);
+                        break;
+                    }
+                }
+
+                if (pData->bIsToolTipShowing)
+                {
+                    if (pData->pszToolTip)
+                        // update the tooltip on screen
+                        WinSendMsg(pWidget->pGlobals->hwndTooltip,
+                                   TTM_UPDATETIPTEXT,
+                                   (MPARAM)pData->pszToolTip, 0);
+                    else
+                        // hide the tooltip
+                        WinSendMsg(pWidget->pGlobals->hwndTooltip,
+                                   TTM_SHOWTOOLTIPNOW,
+                                   (MPARAM)FALSE, 0);
+                }
+
+                // now update the icon
+                WinDestroyPointer(pData->hIcon);
+                pData->hIcon = hIcon;
+                pData->ulMsgId = pCtlData->u.icon.ulMsgId;
+
+                // we didn't change the number of icons so simply invalidate
+                WinInvalidateRect(pWidget->hwndWidget, NULL, FALSE);
+
+                xrc = XST_REPLACED;
+            }
+            else
+            {
+                LOGF((" Adding new hIcon %x\n", hIcon));
+
+                if (pSysTrayData->cIcons == pSysTrayData->cIconsMax)
+                {
+                    PICONDATA pNewIcons;
+
+                    LOGF((" Allocating more memory (new icon count is %u)!\n",
+                          pSysTrayData->cIcons + 1));
+
+                    pNewIcons = realloc(pSysTrayData->pIcons,
+                                        sizeof(*pSysTrayData->pIcons) *
+                                        pSysTrayData->cIconsMax + ICONARRAY_GROW);
+                    if (pNewIcons == NULL)
+                    {
+                        WinDestroyPointer(hIcon);
+                        break;
+                    }
+
+                    pSysTrayData->pIcons = pNewIcons;
+                    pSysTrayData->cIconsMax += ICONARRAY_GROW;
+                }
+
+                i = pSysTrayData->cIcons;
+
+                pData = &pSysTrayData->pIcons[i];
+                memset(pData, 0, sizeof(*pData));
+
+                pData->hwnd = pCtlData->hwndSender;
+                pData->usId = pCtlData->u.icon.usId;
+                pData->hIcon = hIcon;
+                pData->ulMsgId = pCtlData->u.icon.ulMsgId;
+
+                if (pCtlData->u.icon.szToolTip[0] != '\0')
+                {
+                    pData->pszToolTip = strdup(pCtlData->u.icon.szToolTip);
+                    if (!pData->pszToolTip)
+                    {
+                        WinDestroyPointer(hIcon);
+                        break;
+                    }
+                }
+
+                ++pSysTrayData->cIcons;
+
+                WgtXSysTrayUpdateAfterIconAddRemove(pWidget);
+
+                xrc = XST_OK;
+            }
+        }
+        break;
+
+        case SYSTRAYCMD_REPLACEICON:
+        {
+            POINTERINFO Info;
+            HPOINTER hIcon = NULLHANDLE;
+            size_t i;
+            PICONDATA pData;
+
+            LOGF(("SYSTRAYCMD_REPLACEICON\n"));
+            LOGF((" hwnd  %x\n", pCtlData->hwndSender));
+            LOGF((" usId  %d\n", pCtlData->u.icon.usId));
+
+            pCtlData->bAcknowledged = TRUE;
+
+            // make a private copy of the provided icon (it will get lost after
+            // we return from this message)
+            brc = WinQueryPointerInfo(pCtlData->u.icon.hIcon, &Info);
+            if (!brc)
+                break;
+            hIcon = WinCreatePointerIndirect(HWND_DESKTOP, &Info);
+            if (hIcon == NULLHANDLE)
+                break;
+
+            pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
+                                 pCtlData->u.icon.usId, &i);
+            if (pData)
+            {
+                LOGF((" Replacing with hIcon %x\n", hIcon));
+
+                WinDestroyPointer(pData->hIcon);
+                pData->hIcon = hIcon;
+                pData->ulMsgId = pCtlData->u.icon.ulMsgId;
+
+                // we didn't change the number of icons so simply invalidate
+                WinInvalidateRect(pWidget->hwndWidget, NULL, FALSE);
+
+                xrc = XST_OK;
+            }
+            else
+                LOGF((" Icon not found!\n"));
+        }
+        break;
+
+        case SYSTRAYCMD_REMOVEICON:
+        {
+            size_t i;
+            PICONDATA pData;
+
+            LOGF(("SYSTRAYCMD_REMOVEICON\n"));
+            LOGF((" hwnd  %x\n", pCtlData->hwndSender));
+            LOGF((" usId  %d\n", pCtlData->u.icon.usId));
+
+            pCtlData->bAcknowledged = TRUE;
+
+            pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
+                                 pCtlData->u.icon.usId, &i);
+            if (pData)
+            {
+                LOGF((" Removing hIcon %x\n", pData->hIcon));
+
+                FreeIconData(pData);
+
+                --pSysTrayData->cIcons;
+                if (pSysTrayData->cIcons > 0)
+                {
+                    memcpy(&pSysTrayData->pIcons[i],
+                           &pSysTrayData->pIcons[i + 1],
+                           sizeof(*pSysTrayData->pIcons) * (pSysTrayData->cIcons - i));
+                }
+
+                WgtXSysTrayUpdateAfterIconAddRemove(pWidget);
+
+                xrc = XST_OK;
+            }
+            else
+                LOGF((" Icon not found!\n"));
+        }
+        break;
+
+        case SYSTRAYCMD_SETTOOLTIP:
+        {
+            size_t i;
+            PICONDATA pData;
+
+            LOGF(("SYSTRAYCMD_SETTOOLTIP\n"));
+            LOGF((" hwnd  %x\n", pCtlData->hwndSender));
+            LOGF((" usId  %d\n", pCtlData->u.icon.usId));
+
+            pCtlData->bAcknowledged = TRUE;
+
+            pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
+                                 pCtlData->u.icon.usId, &i);
+            if (pData)
+            {
+                LOGF((" Replacing with szToolTip '%s'\n",
+                      pCtlData->u.icon.szToolTip));
+
+                free(pData->pszToolTip);
+                pData->pszToolTip = NULL;
+                if (pCtlData->u.icon.szToolTip[0] != '\0')
+                {
+                    pData->pszToolTip = strdup(pCtlData->u.icon.szToolTip);
+                    if (!pData->pszToolTip)
+                        break;
+                }
+
+                if (pData->bIsToolTipShowing)
+                {
+                    if (pData->pszToolTip)
+                        // update the tooltip on screen
+                        WinSendMsg(pWidget->pGlobals->hwndTooltip,
+                                   TTM_UPDATETIPTEXT,
+                                   (MPARAM)pData->pszToolTip, 0);
+                    else
+                        // hide the tooltip
+                        WinSendMsg(pWidget->pGlobals->hwndTooltip,
+                                   TTM_SHOWTOOLTIPNOW,
+                                   (MPARAM)FALSE, 0);
+                }
+
+                xrc = XST_OK;
+            }
+            else
+                LOGF((" Icon not found!\n"));
+        }
+        break;
+
+        case SYSTRAYCMD_QUERYRECT:
+        {
+            size_t i;
+            PICONDATA pData;
+
+            LOGF(("SYSTRAYCMD_QUERYRECT\n"));
+            LOGF((" hwnd  %x\n", pCtlData->hwndSender));
+            LOGF((" usId  %d\n", pCtlData->u.icon.usId));
+
+            pCtlData->bAcknowledged = TRUE;
+
+            pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
+                                 pCtlData->u.icon.usId, &i);
+            if (pData)
+            {
+                // Refer to FindIconDataAtPt() for details
+
+                SWP     swp;
+                RECTL   rcl;
+                BOOL    bLeftToRight;
+                LONG    y, lIconStep;
+
+                WinQueryWindowPos(pWidget->hwndWidget, &swp);
+                WinQueryWindowRect(pWidget->pGlobals->hwndClient, &rcl);
+
+                y = (swp.cy - pSysTrayData->lIconHeight) / 2;
+
+                // detect the direction
+                bLeftToRight = swp.x + swp.cx / 2 < (rcl.xRight / 2);
+
+                lIconStep = pSysTrayData->lIconWidth + pSysTrayData->lIconPad;
+
+                pCtlData->u.rect.rclIcon.yBottom = y;
+                pCtlData->u.rect.rclIcon.yTop = y + pSysTrayData->lIconHeight;
+
+                if (bLeftToRight)
+                {
+                    pCtlData->u.rect.rclIcon.xLeft =
+                        pSysTrayData->lIconPad + (lIconStep) * i;
+                    pCtlData->u.rect.rclIcon.xRight =
+                        pCtlData->u.rect.rclIcon.xLeft +
+                        pSysTrayData->lIconWidth;
+                }
+                else
+                {
+                    pCtlData->u.rect.rclIcon.xLeft =
+                        swp.cx - (lIconStep) * (i + 1);
+                    pCtlData->u.rect.rclIcon.xRight =
+                        pCtlData->u.rect.rclIcon.xLeft +
+                        pSysTrayData->lIconWidth;
+                }
+
+                // convert to screen coordinates
+                WinMapWindowPoints(pWidget->hwndWidget, HWND_DESKTOP,
+                                   (PPOINTL)&pCtlData->u.rect.rclIcon, 2);
+                xrc = XST_OK;
+            }
+            else
+                LOGF((" Icon not found!\n"));
+        }
+        break;
+
+        default:
+            break;
+    }
+
+    LOGF(("return %d (WinGetLastError is %x)\n",
+          xrc, WinGetLastError(pWidget->habWidget)));
+
+    return xrc;
+}
+
+/*
+ *@@ WgtXSysTrayTimer:
+ *      implementation for WM_TIMER in fnwpXSysTrayServer.
+ */
+
+static
+VOID WgtXSysTrayTimer(HWND hwnd, PXCENTERWIDGET pWidget,
+                      USHORT usTimerId)
+{
+    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
+    PMEMPOOLHDR pMemPoolHdr = (PMEMPOOLHDR)pSysTrayData->pvMemoryPool;
+    PMEMPOOLBLK pMemPoolBlk;
+    ULONG ulMemPoolMax;
+
+    if (usTimerId == TID_CHECKALIVE)
+    {
+        // check if windows associated with the icons are still alive
+        // and remove those icons whose windows are invalid
+        BOOL bAnyDead = FALSE;
+        size_t i;
+        for (i = 0; i < pSysTrayData->cIcons; ++i)
+        {
+            if (!WinIsWindow(pWidget->habWidget, pSysTrayData->pIcons[i].hwnd))
+            {
+                PICONDATA pData = &pSysTrayData->pIcons[i];
+
+                LOGF(("Removing icon of dead window!\n"));
+                LOGF((" hwnd  %x\n", pData->hwnd));
+                LOGF((" usId  %ld\n", pData->usId));
+                LOGF((" hIcon %x\n", pData->hIcon));
+
+                // free memory blocks from the pool allocated for this client
+                ulMemPoolMax = pMemPoolHdr->ulBeyond;
+                if (ulMemPoolMax > pMemPoolHdr->ulNeedsCommit)
+                    ulMemPoolMax = pMemPoolHdr->ulNeedsCommit;
+                ulMemPoolMax -= sizeof(MEMPOOLBLK);
+
+                pMemPoolBlk = pMemPoolHdr->aBlocks;
+                while ((ULONG)pMemPoolBlk <= ulMemPoolMax)
+                {
+                    if (pMemPoolBlk->hwnd == pData->hwnd)
+                    {
+                        LOGF((" freeing memory block %p\n", pMemPoolBlk));
+                        FreeNotifyDataPtr(pSysTrayData->pvMemoryPool,
+                                          pData->hwnd,
+                                          &pMemPoolBlk->NotifyData);
+                    }
+                    ++pMemPoolBlk;
+                }
+
+                bAnyDead = TRUE;
+                FreeIconData(pData);
+                // pData->hwnd is NULLHANDLE here
+            }
+        }
+
+        if (bAnyDead)
+        {
+            // compact the icon array
+            i = 0;
+            while (i < pSysTrayData->cIcons)
+            {
+                if (pSysTrayData->pIcons[i].hwnd == NULLHANDLE)
+                {
+                    --pSysTrayData->cIcons;
+                    if (pSysTrayData->cIcons > 0)
+                    {
+                        memcpy(&pSysTrayData->pIcons[i],
+                               &pSysTrayData->pIcons[i + 1],
+                               sizeof(*pSysTrayData->pIcons) * (pSysTrayData->cIcons - i));
+                    }
+                }
+                else
+                    ++i;
+            }
+
+            WgtXSysTrayUpdateAfterIconAddRemove(pWidget);
+        }
+    }
+}
+
+/*
+ *@@ fnwpXSysTrayServer:
+ *      window procedure for the Extended system tray server window class.
+ *
+ *      A separate "server" class is necessary because we need a CS_FRAME
+ *      top-level window for DDE (which we need to support to be backward
+ *      compatible with the System tray wdget from the SysTray/WPS package) and
+ *      also to make ourselves discoverable for the client-side implementation
+ *      of our new extended API (which queries the class of each top-level
+ *      window to find the system tray server).
+ */
+
+static
+MRESULT EXPENTRY fnwpXSysTrayServer(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
+{
+    // get widget data from QWL_USER_SERVER_DATA (stored there by WM_CREATE)
+    PXCENTERWIDGET pWidget =
+        (PXCENTERWIDGET)WinQueryWindowPtr(hwnd, QWL_USER_SERVER_DATA);
+                    // this ptr is valid after WM_CREATE
+
+    switch (msg)
+    {
+        case WM_CREATE:
+            LOGF(("WM_CREATE\n"));
+            WinSetWindowPtr(hwnd, QWL_USER_SERVER_DATA, mp1);
+            return FALSE; // confirm success
+        break;
+
+        case WM_DESTROY:
+            LOGF(("WM_DESTROY\n"));
+        break;
+
+        /*
+         * WM_XST_CONTROL:
+         *      This is the message sent to us by the clinet-side implementation
+         *      of the API to request some function. mp1 points to a
+         *      SYSTRAYCTLDATA structure.
+         */
+
+        case WM_XST_CONTROL:
+            return (MRESULT)WgtXSysTrayControl(hwnd, pWidget,
+                                               (PSYSTRAYCTLDATA)mp1);
+        break;
+
+        /*
+         * WM_TIMER:
+         *      timer event.
+         */
+
+        case WM_TIMER:
+            WgtXSysTrayTimer(hwnd, pWidget, SHORT1FROMMP(mp1));
+            return (MRESULT)TRUE;
+        break;
+
+        default:
+            break;
+    } // end switch(msg)
+
+    return WinDefWindowProc(hwnd, msg, mp1, mp2);
+}
+
+/* ******************************************************************
+ *
+ *   Exported procedures
+ *
+ ********************************************************************/
+
+/*
+ *@@ WgtInitModule:
+ *      required export with ordinal 1, which must tell
+ *      the XCenter how many widgets this DLL provides,
+ *      and give the XCenter an array of XCENTERWIDGETCLASS
+ *      structures describing the widgets.
+ *
+ *      With this call, you are given the module handle of
+ *      XFLDR.DLL. For convenience, and if you have the full
+ *      XWorkplace source code, you could resolve imports
+ *      for some useful functions which are exported thru
+ *      src\shared\xwp.def. We don't do this here.
+ *
+ *      This function must also register the PM window classes
+ *      which are specified in the XCENTERWIDGETCLASS array
+ *      entries. For this, you are given a HAB which you
+ *      should pass to WinRegisterClass. For the window
+ *      class style (4th param to WinRegisterClass),
+ *      you should specify
+ *
+ +          CS_PARENTCLIP | CS_SIZEREDRAW | CS_SYNCPAINT
+ *
+ *      Your widget window _will_ be resized by the XCenter,
+ *      even if you're not planning it to be.
+ *
+ *      This function only gets called _once_ when the widget
+ *      DLL has been successfully loaded by the XCenter. If
+ *      there are several instances of a widget running (in
+ *      the same or in several XCenters), this function does
+ *      not get called again. However, since the XCenter unloads
+ *      the widget DLLs again if they are no longer referenced
+ *      by any XCenter, this might get called again when the
+ *      DLL is re-loaded.
+ *
+ *      There will ever be only one load occurence of the DLL.
+ *      The XCenter manages sharing the DLL between several
+ *      XCenters. As a result, it doesn't matter if the DLL
+ *      has INITINSTANCE etc. set or not.
+ *
+ *      If this returns 0, this is considered an error, and the
+ *      DLL will be unloaded again immediately.
+ *
+ *      If this returns any value > 0, *ppaClasses must be
+ *      set to a static array (best placed in the DLL's
+ *      global data) of XCENTERWIDGETCLASS structures,
+ *      which must have as many entries as the return value.
+ */
+
+ULONG EXPENTRY WgtInitModule(HAB hab,               // XCenter's anchor block
+                             HMODULE hmodPlugin, // module handle of the widget DLL
+                             HMODULE hmodXFLDR,     // XFLDR.DLL module handle
+                             PCXCENTERWIDGETCLASS *ppaClasses,
+                             PSZ pszErrorMsg)       // if 0 is returned, 500 bytes of error msg
+{
+    ULONG       ulrc = 0, ul = 0;
+    CLASSINFO   ClassInfo;
+
+    LOGF(("hmodPlugin %x\n", hmodPlugin));
+
+    do
+    {
+        // resolve imports from XFLDR.DLL (this is basically
+        // a copy of the doshResolveImports code, but we can't
+        // use that before resolving...)
+        for (ul = 0;
+             ul < sizeof(G_aImports) / sizeof(G_aImports[0]);
+             ul++)
+        {
+            APIRET arc;
+            if ((arc = DosQueryProcAddr(hmodXFLDR,
+                                        0,               // ordinal, ignored
+                                        (PSZ)G_aImports[ul].pcszFunctionName,
+                                        G_aImports[ul].ppFuncAddress))
+                        != NO_ERROR)
+            {
+                snprintf(pszErrorMsg, 500,
+                         "Import %s failed with %ld.",
+                         G_aImports[ul].pcszFunctionName, arc);
+                break;
+            }
+        }
+        if (ul < sizeof(G_aImports) / sizeof(G_aImports[0]))
+            break;
+
+        // register our PM window class
+        if (!WinRegisterClass(hab,
+                             WNDCLASS_WIDGET_XSYSTRAY,
+                             fnwpXSysTray,
+                             CS_PARENTCLIP | CS_SIZEREDRAW | CS_SYNCPAINT,
+                             sizeof(PVOID))
+                                // extra memory to reserve for QWL_USER
+            )
+        {
+            snprintf(pszErrorMsg, 500,
+                     "WinRegisterClass(%s) failed with %lX.",
+                     WNDCLASS_WIDGET_XSYSTRAY, WinGetLastError(hab));
+            break;
+        }
+
+        // get the window data size for the WC_FRAME class (any window class
+        // that specifies CS_FRAME must have at least this number, otherise
+        // WinRegisterClass returns 0x1003
+        if (!WinQueryClassInfo(hab, (PSZ)WC_FRAME, &ClassInfo))
+            break;
+        QWL_USER_SERVER_DATA = ClassInfo.cbWindowData;
+
+        if (!WinRegisterClass(hab,
+                              WNDCLASS_WIDGET_XSYSTRAY_SERVER,
+                              fnwpXSysTrayServer,
+                              CS_FRAME,
+                              QWL_USER_SERVER_DATA + sizeof(PVOID))
+                                // extra memory to reserve for QWL_USER
+            )
+        {
+            // error registering class: report error then
+            snprintf(pszErrorMsg, 500,
+                     "WinRegisterClass(%s) failed with %lX",
+                     WNDCLASS_WIDGET_XSYSTRAY_SERVER, WinGetLastError(hab));
+            break;
+        }
+
+        if (WM_XST_CREATED == 0)
+            WM_XST_CREATED = WinAddAtom(WinQuerySystemAtomTable(),
+                                        WM_XST_CREATED_ATOM);
+        if (WM_XST_NOTIFY == 0)
+            WM_XST_NOTIFY = WinAddAtom(WinQuerySystemAtomTable(),
+                                       WM_XST_NOTIFY_ATOM);
+
+        // no error:
+        // return widget classes array
+        *ppaClasses = G_WidgetClasses;
+
+        // return no. of classes in this DLL (one here):
+        ulrc = sizeof(G_WidgetClasses) / sizeof(G_WidgetClasses[0]);
+    }
+    while (0);
+
+    LOGF(("pszErrorMsg '%s'\n", pszErrorMsg));
+    LOGF(("ulrc %d\n", ulrc));
+
+    return ulrc;
+}
+
+/*
+ *@@ WgtUnInitModule:
+ *      optional export with ordinal 2, which can clean
+ *      up global widget class data.
+ *
+ *      This gets called by the XCenter right before
+ *      a widget DLL gets unloaded. Note that this
+ *      gets called even if the "init module" export
+ *      returned 0 (meaning an error) and the DLL
+ *      gets unloaded right away.
+ */
+
+VOID EXPENTRY WgtUnInitModule(VOID)
+{
+    LOGF(("\n"));
+}
+
+/*
+ *@@ WgtQueryVersion:
+ *      this new export with ordinal 3 can return the
+ *      XWorkplace version number which is required
+ *      for this widget to run. For example, if this
+ *      returns 0.9.10, this widget will not run on
+ *      earlier XWorkplace versions.
+ *
+ *      NOTE: This export was mainly added because the
+ *      prototype for the "Init" export was changed
+ *      with V0.9.9. If this returns 0.9.9, it is
+ *      assumed that the INIT export understands
+ *      the new FNWGTINITMODULE_099 format (see center.h).
+ */
+
+VOID EXPENTRY WgtQueryVersion(PULONG pulMajor,
+                              PULONG pulMinor,
+                              PULONG pulRevision)
+{
+    *pulMajor = 0;
+    *pulMinor = 9;
+    *pulRevision = 9;
+}
+
Index: /trunk/src/3rdparty/os2/xsystray/plugin/w_xsystray.def
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/plugin/w_xsystray.def	(revision 835)
+++ /trunk/src/3rdparty/os2/xsystray/plugin/w_xsystray.def	(revision 835)
@@ -0,0 +1,20 @@
+;
+; module definition file for XSYSTRAY.DLL, an XCenter
+; plug-in widget DLL.
+;
+
+LIBRARY XSYSTRAY INITGLOBAL TERMGLOBAL
+Description '@#netlabs.org:0.1.0#@ Extended system tray widget DLL for XCenter'
+PROTMODE
+
+DATA              MULTIPLE READWRITE LOADONCALL NONSHARED
+;       doesn't really matter, because the widget is only ever used
+;       in the WPS process; we use MULTIPLE NONSHARED here to reduce
+;       the amount of shared memory that is needed for the DLL
+
+CODE              LOADONCALL
+
+EXPORTS
+    WgtInitModule      @1
+    WgtUnInitModule    @2
+    WgtQueryVersion    @3
Index: /trunk/src/3rdparty/os2/xsystray/plugin/w_xsystray.h
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/plugin/w_xsystray.h	(revision 835)
+++ /trunk/src/3rdparty/os2/xsystray/plugin/w_xsystray.h	(revision 835)
@@ -0,0 +1,208 @@
+
+/*
+ *@@sourcefile w_xsystray.h:
+ *      Extended system tray widget for XCenter/eCenter.
+ *
+ *      Private declarations.
+ *
+ *      Copyright (C) 2009 Dmitry A. Kuminov
+ *
+ *      This file is part of the Extended system tray widget source package.
+ *      Extended system tray widget is free software; you can redistribute it
+ *      and/or modify it under the terms of the GNU General Public License as
+ *      published by the Free Software Foundation, in version 2 as it comes in
+ *      the "COPYING" file of the Extended system tray widget distribution. This
+ *      program is distributed in the hope that it will be useful, but WITHOUT
+ *      ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *      FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ *      more details.
+ */
+
+#ifndef XSYSTRAY_HEADER_INCLUDED
+#define XSYSTRAY_HEADER_INCLUDED
+
+#include "xsystray.h"
+
+#include <sys/builtin.h>        // atomics
+
+#define XSYSTRAY_VERSION_MAJOR 0
+#define XSYSTRAY_VERSION_MINOR 1
+#define XSYSTRAY_VERSION_REVISION 0
+
+#define WNDCLASS_WIDGET_XSYSTRAY_SERVER "XWPCenterExtendedSysTrayServer"
+
+#define WNDCLASS_WIDGET_XSYSTRAY        "XWPCenterExtendedSysTray"
+#define INTCLASS_WIDGET_XSYSTRAY        "ExtendedSysTray"
+#define HUMANSTR_WIDGET_XSYSTRAY        "Extended system tray"
+
+#define WM_XST_CREATED_ATOM             "ExtendedSysTray.WM_XST_CREATED"
+#define WM_XST_NOTIFY_ATOM              "ExtendedSysTray.WM_XST_NOTIFY"
+
+#define WM_XST_CONTROL  (WM_USER + 0)
+        // message sent to the system tray server to request an action
+
+// server action commands
+typedef enum
+{
+    SYSTRAYCMD_GETVERSION,
+    SYSTRAYCMD_ADDICON,
+    SYSTRAYCMD_REPLACEICON,
+    SYSTRAYCMD_REMOVEICON,
+    SYSTRAYCMD_SETTOOLTIP,
+    SYSTRAYCMD_SHOWBALLOON,
+    SYSTRAYCMD_HIDEBALLOON,
+    SYSTRAYCMD_QUERYRECT,
+} SYSTRAYCMD;
+
+// server responses to WM_XST_CONTROL
+#define XST_OK          0   // command succeeded
+#define XST_FAIL        1   // command failed
+#define XST_REPLACED    2   // SYSTRAYCMD_ADDICON replaced the existing icon
+
+/*
+ *@@ SYSTRAYCTLDATA:
+ *      Structure holding information accompanying WM_XST_CONTROL messages sent
+ *      to the system tray server by clients (windows associated with system
+ *      tray icons).
+ *
+ *      NOTE: When you change the size of this structure, you may also need to
+ *      change CLIENT_MEMORYPOOL_SIZE value (see the comments there for
+ *      details).
+ */
+
+typedef struct
+{
+    SYSTRAYCMD  ulCommand;
+                // command to execute, must always be set
+    HWND        hwndSender;
+                // sender window, a must for SYSTRAYCMD_ADDICON, _CHANGEICON,
+                // _REMOVEICON, _SETTOOLTIP, _SHOWBALLOON, _HIDEBALLOON,
+                // _QUERYRECT
+    union
+    {
+        struct
+        {
+            ULONG ulMajor;
+            ULONG ulMinor;
+            ULONG ulRevision;
+        } version;
+          // used by SYSTRAYCMD_GETVERSION
+
+        struct
+        {
+            USHORT      usId;
+            HPOINTER    hIcon;
+            CHAR        szToolTip[512];
+            ULONG       ulMsgId;
+        } icon;
+          // used by SYSTRAYCMD_ADDICON, _CHANGEICON, _REMOVEICON, _SETTOOLTIP
+
+        struct
+        {
+            RECTL rclIcon;
+        } rect;
+          // used by SYSTRAYCMD_QUERYRECT
+    } u;
+
+    BOOL    bAcknowledged : 1;
+            // set to true by the recipient if it processes the message
+
+} SYSTRAYCTLDATA, *PSYSTRAYCTLDATA;
+
+/*
+ *@@ NOTIFYDATA:
+ *      Structure holding information acompanying notification messages
+ *      posted to clients (windows associated with system tray icons) about
+ *      icon events. This structure unions all public notification code
+ *      dependent structures defined in xsystray.h (starting with XST*).
+ *
+ *      All messages posted to the client have an ID corresponding to the
+ *      WM_XST_NOTIFY_ATOM in the system atom table. The client-side API
+ *      implementation intercepts these messages (using HK_INPUT), composes a
+ *      new message given the information in NOTIFYDATA, frees the NOTIFYDATA
+ *      pointer using FreeNotifyDataPtr() and then sends the composed message to
+ *      the appropriate window.
+ *
+ *      The layout of the XST_NOTIFY message is as follows:
+ *
+ *          param1
+ *              PNOTIFYDATA pNotifyData     pointer to the NOTIFYDATA structure
+ *
+ *          param2
+ *              PVOID       pvMemoryPool    server memory pool (for the
+ *                                          FreeNotifyDataPtr() call)
+ *
+ *      NOTE: Structures in the union should only contain values; passing
+ *      pointers to arbitrary data to the client side is not supported (yet).
+ *
+ *      NOTE: When you change the size of this structure, you may also need to
+ *      change SERVER_MEMORYPOOL_SIZE value in w_xsystray.c (see the comments
+ *      there for details).
+ */
+
+typedef struct
+{
+    ULONG   msg;
+            // ID of the message that is to be sent to the target window
+    MPARAM  mp1;
+            // message parameter (usually: USHORT usIconId, USHORT usNotifyCode)
+    MPARAM  mp2;
+            // message parameter (usually, a pointer to a struct from the union)
+    union
+    {
+        XSTMOUSEMSG     MouseMsg;
+        XSTCONTEXTMSG   ContextMsg;
+        XSTWHEELMSG     WheelMsg;
+    } u;
+
+} NOTIFYDATA, *PNOTIFYDATA;
+
+// Header of the server-side memory pool
+typedef struct
+{
+    volatile HWND   hwnd;        // owner of the block or NULLHANDLE if free
+    NOTIFYDATA      NotifyData;  // data
+
+} MEMPOOLBLK, *PMEMPOOLBLK;
+
+// allocation unit in the server-side memory pool
+typedef struct
+{
+    ULONG ulBeyond;         // address of the first byte beyond the memory pool
+
+    volatile ULONG ulNeedsCommit;   // address of the first decommitted byte
+    volatile ULONG ulNext;          // address of next possibly free block
+
+    MEMPOOLBLK aBlocks[0];          // fake array for easier addressing
+
+} MEMPOOLHDR, *PMEMPOOLHDR;
+
+/*
+ *@@ FreeNotifyDataPtr:
+ *      Frees the NOTIFYDATA structure allocated by AllocNotifyDataPtr().
+ *
+ *      See AllocNotifyDataPtr() for more details about allocating these
+ *      structures.dd
+ */
+
+inline
+VOID FreeNotifyDataPtr(PVOID pvMemoryPool,  // in: memory pool base address
+                       HWND hwndOwner,      // in: owner of the struct to free
+                       PNOTIFYDATA pData)   // in: address of the struct to free
+{
+    PMEMPOOLHDR pHdr = (PMEMPOOLHDR)pvMemoryPool;
+    PMEMPOOLBLK pBlk = (PMEMPOOLBLK)((ULONG)pData - sizeof(HWND));
+
+    ULONG ulNext = pHdr->ulNext;
+
+    __atomic_cmpxchg32((uint32_t *)&pBlk->hwnd, NULLHANDLE, hwndOwner);
+
+    // if the next possible free block is greater than we just freed,
+    // set it to us (to minimize the amount of committed pages)
+    if (ulNext > (ULONG)pBlk)
+        __atomic_cmpxchg32((uint32_t *)&pHdr->ulNext, (ULONG)pBlk, ulNext);
+}
+
+#endif // XSYSTRAY_HEADER_INCLUDED
+
+
Index: unk/src/3rdparty/os2/xsystray/xsystray.c
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/xsystray.c	(revision 834)
+++ 	(revision )
@@ -1,1859 +1,0 @@
-
-/*
- *@@sourcefile xsystray.c:
- *      Extended system tray widget for XCenter/eCenter.
- *
- *      Implementation.
- *
- *      Copyright (C) 2009 Dmitry A. Kuminov
- *
- *      This file is part of the Extended system tray widget source package.
- *      Extended system tray widget is free software; you can redistribute it
- *      and/or modify it under the terms of the GNU General Public License as
- *      published by the Free Software Foundation, in version 2 as it comes in
- *      the "COPYING" file of the Extended system tray widget distribution. This
- *      program is distributed in the hope that it will be useful, but WITHOUT
- *      ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- *      FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- *      more details.
- */
-
-#if defined(__IBMC__) || defined(__IBMCPP__)
-#pragma strings(readonly)
-#endif
-
-/*
- *  Suggested #include order:
- *  1)  os2.h
- *  2)  C library headers
- *  3)  setup.h (code generation and debugging options)
- *  4)  headers in helpers\
- *  5)  at least one SOM implementation header (*.ih)
- *  6)  dlgids.h, headers in shared\ (as needed)
- *  7)  headers in implementation dirs (e.g. filesys\, as needed)
- *  8)  #pragma hdrstop and then more SOM headers which crash with precompiled headers
- */
-
-#if defined(__GNUC__) && defined(__EMX__)
-#define OS2EMX_PLAIN_CHAR
-#endif
-
-#define INCL_BASE
-#define INCL_PM
-#include <os2.h>
-
-// C library headers
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdarg.h>
-
-// generic headers
-// If this file were part of the XWorkplace sources, we'd now include
-// the generic "setup.h" file, which has common set up code for every
-// single XWorkplace code file. But it's not, so we won't include that.
-// #include "setup.h"                      // code generation and debugging options
-
-// headers in /helpers
-// This would be the place to include headers from the "XWorkplace helpers".
-// But since we do a minimal sample here, we can't include those helpful
-// routines... again, see the src\widgets in the XWorkplace source code
-// for how these functions can be imported from XFLDR.DLL to avoid duplicate
-// code.
-// #include "helpers\dosh.h"               // Control Program helper routines
-// #include "helpers\gpih.h"               // GPI helper routines
-// #include "helpers\prfh.h"               // INI file helper routines;
-                                        // this include is required for some
-                                        // of the structures in shared\center.h
-// #include "helpers\winh.h"               // PM helper routines
-// #include "helpers\xstring.h"            // extended string helpers
-
-// XWorkplace implementation headers
-// If this file were part of the XCenter sources, we'd now include
-// "center.h" from the "include\shared" directory. Since we're not
-// part of the XCenter sources here, we include that file from the
-// "toolkit" directory in the binary release. That file is identical
-// to "include\shared\center.h" in the XWorkplace sources.
-#include "shared\center.h"              // public XCenter interfaces
-
-#include "xsystray.h"
-
-#if defined(__IBMC__) || defined(__IBMCPP__)
-#pragma hdrstop                     // VAC++ keeps crashing otherwise
-#endif
-
-// copy paste from helpers\comctl.h
-// @todo not necessary when we become part of XWorkplace
-
-#define TTN_NEEDTEXT        1000
-#define TTN_SHOW            1001
-#define TTN_POP             1002
-
-#define TTFMT_PSZ           0x01
-#define TTFMT_STRINGRES     0x02
-
-typedef struct _TOOLTIPTEXT
-{
-    HWND    hwndTooltip;
-    HWND    hwndTool;
-    ULONG   ulFormat;
-    PSZ     pszText;
-    HMODULE hmod;
-    ULONG   idResource;
-} TOOLTIPTEXT, *PTOOLTIPTEXT;
-
-#define TTM_FIRST                   (WM_USER + 1000)
-#define TTM_UPDATETIPTEXT           (TTM_FIRST + 9)
-#define TTM_SHOWTOOLTIPNOW          (TTM_FIRST + 17)
-
-// primitive debug logging to a file
-#if 0
-static void __LOG_WORKER(const char *fmt, ...)
-{
-    static FILE *f = NULL;
-    if (f == NULL)
-    {
-        f = fopen("c:\\xsystray.dbg", "a");
-        setbuf(f, NULL);
-    }
-    if (f != NULL)
-    {
-        va_list vl;
-        va_start(vl, fmt);
-        vfprintf(f, fmt, vl);
-        va_end(vl);
-    }
-}
-#define LOG(m)  do { __LOG_WORKER m; } while(0)
-#define LOGF(m) do { __LOG_WORKER("%s: ", __FUNCTION__); __LOG_WORKER m; } while(0)
-#else
-#define LOG(m)  do {} while (0)
-#define LOGF(m) do {} while (0)
-#endif
-
-/* ******************************************************************
- *
- *   Private definitions
- *
- ********************************************************************/
-
-/*
- *@@ ICONDATA:
- *      Per-icon data.
- */
-
-typedef struct
-{
-    HWND        hwnd;
-                // associated window
-    USHORT      usId;
-                // icon ID
-    HPOINTER    hIcon;
-                // icon handle
-    ULONG       ulMsgId;
-                // message ID for notifications
-    PSZ         pszToolTip;
-                // icon tooltip (NULL if none)
-    BOOL        bIsToolTipShowing;
-                // whether the tooltip is currently shown
-    BOOL        bMemoryPoolGiven;
-                // TRUE if SYSTRAYDATA::pvMemoryPool is already given to
-                // the process hwnd belongs to
-
-} ICONDATA, *PICONDATA;
-
-/*
- *@@ SYSTRAYDATA:
- *      Global system tray data.
- */
-
-typedef struct
-{
-    HWND        hwndServer;
-                // systtem tray server window handle
-    LONG        lIconWidth;
-                // system icon width in px
-    LONG        lIconHeight;
-                // system icon height in px
-    LONG        lIconPad;
-                // padding around each icon in px
-    PICONDATA   pIcons;
-                // array of icons currently shown in the system tray
-    size_t      cIcons;
-                // number of icons in the pIcons array
-    size_t      cIconsMax;
-                // maximum number of icons pIcons can fit
-    CHAR        szToolTip[sizeof(((PSYSTRAYCTLDATA)0)->u.icon.szToolTip)];
-                // "static" buffer for the tooltip (XCenter requirement)
-    PVOID       pvMemoryPool;
-                // memory pool for NOTIFYDATA structures
-
-} SYSTRAYDATA, *PSYSTRAYDATA;
-
-#define ICONARRAY_GROW  4
-        // number of element the icon array is increased by when there is no
-        // space for the newly added icon
-
-#define SERVER_MEMORYPOOL_SIZE 65536
-        // taking NOTIFYDATA size into account (<=32 B), this is enough for at
-        // least 2048 simultaneous notification messages, which (even taking
-        // slowly responsing clients into account) sounds sane since in most
-        // cases the structure is freed once it reaches the target event queue
-        // and before a message created as a copy of it is sent to the target
-        // window procedure
-
-#define TID_CHECKALIVE          1
-         // timer that checks if windows associated with icons are still alive
-#define TID_CHECKALIVE_TIMEOUT  2000 // ms
-         // how often to perform alive checks
-
-static ULONG WM_XST_CREATED = 0;
-             // identity of the WM_XST_CREATED message taken from the atom table
-static ULONG WM_XST_NOTIFY = 0;
-             // identity of the WM_XST_NOTIFY message taken from the atom table
-
-static ULONG QWL_USER_SERVER_DATA = 0;
-             // offset to the PXCENTERWIDGET pointer in the widget data array
-
-static
-VOID WgtXSysTrayUpdateAfterIconAddRemove(PXCENTERWIDGET pWidget);
-
-/* ******************************************************************
- *
- *   XCenter widget class definition
- *
- ********************************************************************/
-
-/*
- *      This contains the name of the PM window class and
- *      the XCENTERWIDGETCLASS definition(s) for the widget
- *      class(es) in this DLL.
- *
- *      The address of this structure (or an array of these
- *      structures, if there were several widget classes in
- *      this plugin) is returned by the "init" export
- *      (WgtInitModule).
- */
-
-static const XCENTERWIDGETCLASS G_WidgetClasses[] =
-{
-    {
-        WNDCLASS_WIDGET_XSYSTRAY,   // PM window class name
-        0,                          // additional flag, not used here
-        INTCLASS_WIDGET_XSYSTRAY,   // internal widget class name
-        HUMANSTR_WIDGET_XSYSTRAY,   // widget class name displayed to user
-        WGTF_UNIQUEGLOBAL |         // widget class flags
-        WGTF_TOOLTIP,
-        NULL                        // no settings dialog
-    }
-};
-
-/* ******************************************************************
- *
- *   Function imports from XFLDR.DLL
- *
- ********************************************************************/
-
-/*
- *      To reduce the size of the widget DLL, it can
- *      be compiled with the VAC subsystem libraries.
- *      In addition, instead of linking frequently
- *      used helpers against the DLL again, you can
- *      import them from XFLDR.DLL, whose module handle
- *      is given to you in the INITMODULE export.
- *
- *      Note that importing functions from XFLDR.DLL
- *      is _not_ a requirement. We can't do this in
- *      this minimal sample anyway without having access
- *      to the full XWorkplace source code.
- *
- *      If you want to know how you can import the useful
- *      functions from XFLDR.DLL to use them in your widget
- *      plugin, again, see src\widgets in the XWorkplace sources.
- *      The actual imports would then be made by WgtInitModule.
- */
-
-// @todo the function declarations should become not necessary when we move into
-// XWorkplace. Let's hope the prototypes will not change until then. Let's also
-// pray that XFLDRxxx.DLL has the _Optlink calling convention for exports and
-// not the default __cdecl (which happens if it builds with GCC according to the
-// XWPENTRY definition in the current xwphelpers sources)
-
-#define XWPENTRY _Optlink
-
-typedef VOID XWPENTRY GPIHDRAW3DFRAME(HPS hps,
-                                      PRECTL prcl,
-                                      USHORT usWidth,
-                                      LONG lColorLeft,
-                                      LONG lColorRight);
-typedef GPIHDRAW3DFRAME *PGPIHDRAW3DFRAME;
-PGPIHDRAW3DFRAME pgpihDraw3DFrame = NULL;
-
-typedef struct _RESOLVEFUNCTION
-{
-    const char  *pcszFunctionName;
-    PFN         *ppFuncAddress;
-} RESOLVEFUNCTION, *PRESOLVEFUNCTION;
-RESOLVEFUNCTION G_aImports[] =
-{
-    { "gpihDraw3DFrame", (PFN*)&pgpihDraw3DFrame },
-};
-
-/* ******************************************************************
- *
- *   Private widget instance data
- *
- ********************************************************************/
-
-// None presently. The samples in src\widgets in the XWorkplace
-// sources cleanly separate setup string data from other widget
-// instance data to allow for easier manipulation with settings
-// dialogs. We have skipped this for the minimal sample.
-
-/* ******************************************************************
- *
- *   Widget setup management
- *
- ********************************************************************/
-
-// None presently. See above.
-
-/* ******************************************************************
- *
- *   Widget settings dialog
- *
- ********************************************************************/
-
-// None currently. To see how a setup dialog can be done,
-// see the "window list" widget in the XWorkplace sources
-// (src\widgets\w_winlist.c).
-
-/* ******************************************************************
- *
- *   Callbacks stored in XCENTERWIDGETCLASS
- *
- ********************************************************************/
-
-// If you implement a settings dialog, you must write a
-// "show settings dlg" function and store its function pointer
-// in XCENTERWIDGETCLASS.
-
-/* ******************************************************************
- *
- *   Helper methods
- *
- ********************************************************************/
-
-/*
- *@@ FreeIconData:
- *      Frees all members of the ICONDATA structure and resets them to 0.
- */
-
-static
-VOID FreeIconData(PICONDATA pData)
-{
-    pData->hwnd = NULLHANDLE;
-    pData->usId = 0;
-    if (pData->hIcon != NULLHANDLE)
-    {
-        WinDestroyPointer(pData->hIcon);
-        pData->hIcon = NULLHANDLE;
-
-    }
-    pData->ulMsgId = 0;
-    if (pData->pszToolTip)
-    {
-        free(pData->pszToolTip);
-        pData->pszToolTip = NULL;
-    }
-}
-
-/*
- *@@ FindIconData:
- *      Searches for the icon with the given identity. Returns NULL if not
- *      found.
- */
-
-static
-PICONDATA FindIconData(PSYSTRAYDATA pSysTrayData,
-                       HWND hwnd,       // in: associated window handle
-                       USHORT usId,     // in: icon ID
-                       size_t *pIdx)    // out: index of the icon in the icon array
-                                        // (optional, may be NULL)
-{
-    size_t i;
-    for (i = 0; i < pSysTrayData->cIcons; ++i)
-    {
-        if (pSysTrayData->pIcons[i].hwnd == hwnd &&
-            pSysTrayData->pIcons[i].usId == usId)
-        {
-            if (pIdx)
-                *pIdx = i;
-            return &pSysTrayData->pIcons[i];
-        }
-    }
-
-    if (pIdx)
-        *pIdx = i;
-    return NULL;
-}
-
-/*
- *@@ FindIconDataAtPt:
- *      Searches for the icon under the given point.
- *      Returns NULL if no icon found (e.g. the pad space).
- *
- *      Refer to WgtPaint() for system tray geometry description.
- */
-
-static
-PICONDATA FindIconDataAtPt(PXCENTERWIDGET pWidget,
-                           PPOINTL pptl,    // point coordinates (relative to systray)
-                           size_t *pIdx)    // out: index of the icon in the icon array
-                                            // (optional, may be NULL)
-{
-    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
-
-    SWP     swp;
-    RECTL   rcl;
-    BOOL    bLeftToRight;
-    LONG    y, lIconStep;
-    size_t  i;
-
-    // start with invalid index index
-    if (pIdx)
-        *pIdx = pSysTrayData->cIcons;
-
-    WinQueryWindowPos(pWidget->hwndWidget, &swp);
-    WinQueryWindowRect(pWidget->pGlobals->hwndClient, &rcl);
-
-    y = (swp.cy - pSysTrayData->lIconHeight) / 2;
-    if (pptl->y < y || pptl->y >= y + pSysTrayData->lIconHeight)
-        return NULL; // hit pad space
-
-    // detect the direction
-    bLeftToRight = swp.x + swp.cx / 2 < (rcl.xRight / 2);
-
-    lIconStep = pSysTrayData->lIconWidth + pSysTrayData->lIconPad;
-
-    // which icon is that?
-    if (bLeftToRight)
-    {
-        i = pptl->x / lIconStep;
-        if (pptl->x % lIconStep < pSysTrayData->lIconPad)
-            return NULL; // hit pad space
-    }
-    else
-    {
-        i = (swp.cx - pptl->x - 1) / lIconStep;
-        if ((swp.cx - pptl->x - 1) % lIconStep < pSysTrayData->lIconPad)
-            return NULL; // hit pad space
-    }
-    if (i >= pSysTrayData->cIcons)
-        return NULL; // hit pad space
-
-    if (pIdx)
-        *pIdx = i;
-    return &pSysTrayData->pIcons[i];
-}
-
-static
-VOID FreeSysTrayData(PSYSTRAYDATA pSysTrayData)
-{
-    // destroy the server
-    if (pSysTrayData->hwndServer != NULLHANDLE)
-    {
-        WinDestroyWindow(pSysTrayData->hwndServer);
-        pSysTrayData->hwndServer = NULLHANDLE;
-    }
-
-    // free all system tray data
-    if (pSysTrayData->pvMemoryPool)
-    {
-        DosFreeMem(pSysTrayData->pvMemoryPool);
-    }
-    if (pSysTrayData->pIcons)
-    {
-        size_t i;
-        for (i = 0; i < pSysTrayData->cIcons; ++i)
-            FreeIconData(&pSysTrayData->pIcons[i]);
-        pSysTrayData->cIcons = 0;
-        free(pSysTrayData->pIcons);
-        pSysTrayData->pIcons = NULL;
-    }
-
-    free(pSysTrayData);
-}
-
-/*
- *@@ AllocNotifyDataPtr:
- *      Allocates a SYSTRAYCTLDATA struct in the pool of shared memory on belalf
- *      of the client window identified by pIconData->hwnd.
- *
- *      If there is no free space in the pool, it returns NULL. On success,
- *      fills in msg and mp1 fields of the returned structure using the
- *      information provided in pIconData.
- *
- *      Note that the allocated structure is supposed to be freed using
- *      FreeNotifyDataPtr by the client-side API implementation in another
- *      process.
- */
-static
-PNOTIFYDATA AllocNotifyDataPtr(PVOID pvMemoryPool,  // in: memory pool base address
-                               PICONDATA pIconData) // in: icon data
-{
-    // NOTE: we cannot use DosSubAllocMem() and friends since we want to be able
-    // to free blocks allocated to clients which death we detect but we cannot
-    // be sure about the layout of memory DosSub API uses. Therefore, we provide
-    // our own sub-allocation scheme which is rather simple because we need to
-    // allocate equally sized blocks (each is NOTIFYDATA struct). The memory
-    // pool is laid out as follows: MEMPOOLHDR followed by a number of
-    // MEMPOOLBLK.
-
-    APIRET arc;
-    PID pid;
-    TID tid;
-    PMEMPOOLHDR pHdr;
-    ULONG ulMax, ulNeedsCommit, ulNext, ulCurr;
-    PNOTIFYDATA pData;
-
-    LOGF(("pvMemoryPool  %p\n", pvMemoryPool));
-    LOGF(("hwnd          %lx\n", pIconData->hwnd));
-
-    if (!pIconData->bMemoryPoolGiven)
-    {
-        LOGF(("Giving memory pool to %lx\n", pIconData->hwnd));
-
-        arc = ERROR_INVALID_HANDLE;
-        if (WinQueryWindowProcess(pIconData->hwnd, &pid, &tid))
-            arc = DosGiveSharedMem(pvMemoryPool, pid, PAG_READ | PAG_WRITE);
-        if (arc != NO_ERROR)
-            return NULL;
-
-        pIconData->bMemoryPoolGiven = TRUE;
-    }
-
-    pHdr = (PMEMPOOLHDR)pvMemoryPool;
-
-    // maximum address that is still enough for a block
-    ulMax = pHdr->ulBeyond - sizeof(MEMPOOLBLK);
-
-    ulNeedsCommit = pHdr->ulNeedsCommit;
-    ulNext = pHdr->ulNext;
-    ulCurr = ulNext;
-
-    LOGF(("ulNeedsCommit %p\n", ulNeedsCommit));
-    LOGF(("ulNext        %p\n", ulNext));
-
-    do
-    {
-        if (ulCurr >= ulNeedsCommit)
-        {
-            // commit more memory; it's OK two or more threads will do the same
-            DosSetMem((PVOID)ulNeedsCommit, 4096,
-                      PAG_COMMIT | PAG_READ | PAG_WRITE);
-            // advance the address (only if nobody has already done so -- they
-            // could already commit more than we did)
-            __atomic_cmpxchg32((volatile uint32_t *)&pHdr->ulNeedsCommit,
-                               ulNeedsCommit + 4096, ulNeedsCommit);
-        }
-
-        if (__atomic_cmpxchg32((volatile uint32_t *)ulCurr,
-                               pIconData->hwnd, NULLHANDLE))
-            break;
-
-        ulCurr += sizeof(MEMPOOLBLK);
-        if (ulCurr > ulMax)
-            // start over
-            ulCurr = ((ULONG)pvMemoryPool) + sizeof(MEMPOOLHDR);
-
-        if (ulCurr == ulNext)
-            return NULL; // no free blocks!
-    }
-    while (1);
-
-    LOGF(("ulCurr        %p\n", ulCurr));
-
-    // memorize a pointer to the new struct
-    pData = &((PMEMPOOLBLK)ulCurr)->NotifyData;
-
-    // advance to the next possibly free block
-    ulCurr += sizeof(MEMPOOLBLK);
-    if (ulCurr > ulMax)
-        // start over
-        ulCurr = ((ULONG)pvMemoryPool) + sizeof(MEMPOOLHDR);
-
-    // store the new next address until someone else has already done that
-    __atomic_cmpxchg32((volatile uint32_t *)&pHdr->ulNext, ulCurr, ulNext);
-
-    // fill in parts of the allocated NOTIFYDATA
-    memset(pData, 0, sizeof(*pData));
-
-    pData->msg = pIconData->ulMsgId;
-    pData->mp1 = MPFROMSHORT(pIconData->usId);
-
-    return pData;
-}
-
-/*
- *@@ PostNotifyMsg:
- *      Posts WM_XST_NOTIFY to the given client window. Frees pNotifyData if
- *      posting fails.
- */
-
-VOID PostNotifyMsg(PSYSTRAYDATA pSysTrayData, HWND hwnd,
-                   PNOTIFYDATA pNotifyData)
-{
-    if (!WinPostMsg(hwnd, WM_XST_NOTIFY, pNotifyData, pSysTrayData->pvMemoryPool))
-        FreeNotifyDataPtr(pSysTrayData->pvMemoryPool, hwnd, pNotifyData);
-}
-
-/*
- *@@ DrawPointer:
- *      Draws a pointer in a presentation space.
- */
-
-static
-BOOL DrawPointer(HPS hps, LONG lx, LONG ly, HPOINTER hptrPointer, BOOL bMini)
-{
-    return WinDrawPointer(hps, lx, ly, hptrPointer, bMini ? DP_MINI : DP_NORMAL);
-    // @todo: for icons with real alpha, do manual alpha blending
-}
-
-/* ******************************************************************
- *
- *   PM window class implementation
- *
- ********************************************************************/
-
-/*
- *      This code has the actual PM window class.
- *
- */
-
-/*
- *@@ MwgtControl:
- *      implementation for WM_CONTROL in fnwpXSysTray.
- *
- *      The XCenter communicates with widgets thru
- *      WM_CONTROL messages. At the very least, the
- *      widget should respond to XN_QUERYSIZE because
- *      otherwise it will be given some dumb default
- *      size.
- */
-
-static
-BOOL WgtControl(PXCENTERWIDGET pWidget,
-                MPARAM mp1,
-                MPARAM mp2)
-{
-    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
-    BOOL brc = FALSE;
-
-    USHORT  usID = SHORT1FROMMP(mp1),
-            usNotifyCode = SHORT2FROMMP(mp1);
-
-    // is this from the XCenter client?
-    if (usID == ID_XCENTER_CLIENT)
-    {
-        // yes:
-
-        switch (usNotifyCode)
-        {
-            /*
-             * XN_QUERYSIZE:
-             *      XCenter wants to know our size.
-             */
-
-            case XN_QUERYSIZE:
-            {
-                PSIZEL pszl = (PSIZEL)mp2;
-                LONG pad = pSysTrayData->lIconPad;
-                size_t cnt = pSysTrayData->cIcons;
-                // desired width
-                if (cnt)
-                    pszl->cx = pad + (pSysTrayData->lIconWidth + pad) * cnt;
-                else
-                    pszl->cx = 0;
-                // desired minimum height
-                pszl->cy = pSysTrayData->lIconHeight + pad * 2;
-                brc = TRUE;
-            }
-            break;
-
-        }
-    }
-    else if (usID == ID_XCENTER_TOOLTIP)
-    {
-        PICONDATA pIconData;
-        POINTL ptl;
-
-        WinQueryMsgPos(pWidget->habWidget, &ptl);
-        // make the coordinates systray-relative
-        WinMapWindowPoints(HWND_DESKTOP, pWidget->hwndWidget, &ptl, 1);
-
-        pIconData = FindIconDataAtPt(pWidget, &ptl, NULL);
-
-        switch (usNotifyCode)
-        {
-            case TTN_NEEDTEXT:
-            {
-                LOGF(("TTN_NEEDTEXT\n"));
-
-                PTOOLTIPTEXT pttt = (PTOOLTIPTEXT)mp2;
-                pttt->ulFormat = TTFMT_PSZ;
-
-                if (!pIconData || !pIconData->pszToolTip)
-                    pttt->pszText = NULL;
-                else
-                {
-                    strncpy(pSysTrayData->szToolTip, pIconData->pszToolTip,
-                            sizeof(pSysTrayData->szToolTip) - 1);
-                    // be on the safe side
-                    pSysTrayData->szToolTip[sizeof(pSysTrayData->szToolTip) - 1] = '\0';
-
-                    pttt->pszText = pSysTrayData->szToolTip;
-                }
-
-                LOGF((" pszText '%s'\n", pttt->pszText));
-            }
-            break;
-
-            case TTN_SHOW:
-                if (pIconData)
-                    pIconData->bIsToolTipShowing = TRUE;
-            break;
-
-            case TTN_POP:
-                if (pIconData)
-                    pIconData->bIsToolTipShowing = FALSE;
-            break;
-        }
-    }
-
-    return brc;
-}
-
-/*
- *@@ WgtPaint:
- *      implementation for WM_PAINT in fnwpXSysTray.
- *
- *      Draws all the icons. If the widget's center is located to the left from
- *      the XCenter's center, icons go left to right. Otherwise, they go right
- *      to left.
- *
- *      NOTE: This function must be keept in sync with FindIconDataAtPt() and
- *      SYSTRAYCMD_QUERYRECT in terms of system tray geometry.
- */
-/*
-        +---------------------------+  p = lIconPad
-        |     p                     |  w = lIconWidth
-        |   +-------+   +-------+   |  h = lIconHeight
-        | p |   w   | p |   w   | p |
-        |   |      h|   |      h|   |
-        |   |       |   |       |   |  If "Frame around statics" is on in XCenter
-        |   +-------+   +-------+   |  properties, then a 1 px 3D frame is drawn
-        |     p                     |  within the pad area. So, lIconPad must
-        +---------------------------+  be at least 2 px.
- */
-
-static
-VOID WgtPaint(HWND hwnd,
-              PXCENTERWIDGET pWidget)
-{
-    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
-    HPS hps;
-    RECTL rclPaint;
-
-    if ((hps = WinBeginPaint(hwnd, NULLHANDLE, &rclPaint)))
-    {
-        SWP     swp;
-        RECTL   rcl;
-        BOOL    bLeftToRight;
-        LONG    x, y, lIconStep;
-        size_t  i;
-
-        WinQueryWindowPos(hwnd, &swp);
-        WinQueryWindowRect(pWidget->pGlobals->hwndClient, &rcl);
-
-        // correct the paint area
-        // @todo find out why it exceeds the window bounds
-        if (rclPaint.xLeft < 0)
-            rclPaint.xLeft = 0;
-        if (rclPaint.xRight > swp.cx)
-            rclPaint.xRight = swp.cx;
-        if (rclPaint.yBottom < 0)
-            rclPaint.yBottom = 0;
-        if (rclPaint.yTop > swp.cy)
-            rclPaint.yTop = swp.cy;
-
-        LOGF(("rclPaint %d,%d-%d,%d\n",
-              rclPaint.xLeft, rclPaint.yBottom, rclPaint.xRight, rclPaint.yTop));
-
-        // switch HPS to RGB mode
-        GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
-
-        // draw icons left to right if our center is closer to the left edge
-        // of XCenter and right to left otherwise
-        bLeftToRight = swp.x + swp.cx / 2 < (rcl.xRight / 2);
-
-        WinFillRect(hps, &rclPaint,
-                    WinQuerySysColor(HWND_DESKTOP, SYSCLR_DIALOGBACKGROUND, 0));
-
-        if ((pWidget->pGlobals->flDisplayStyle & XCS_SUNKBORDERS))
-        {
-            rcl.xLeft = 0;
-            rcl.yBottom = 0;
-            rcl.xRight = swp.cx - 1;
-            rcl.yTop = swp.cy - 1;
-            pgpihDraw3DFrame(hps, &rcl, 1,
-                             pWidget->pGlobals->lcol3DDark,
-                             pWidget->pGlobals->lcol3DLight);
-        }
-
-        // always center the icon vertically (we may be given more height than
-        // we requested)
-        y = (swp.cy - pSysTrayData->lIconHeight) / 2;
-
-        if (bLeftToRight)
-            x = pSysTrayData->lIconPad;
-        else
-            x = swp.cx - pSysTrayData->lIconPad - pSysTrayData->lIconWidth;
-
-        lIconStep = pSysTrayData->lIconWidth + pSysTrayData->lIconPad;
-
-        // where to start from?
-        if (bLeftToRight)
-        {
-            i = rclPaint.xLeft / lIconStep;
-            x = pSysTrayData->lIconPad + i * lIconStep;
-        }
-        else
-        {
-            i = (swp.cx - rclPaint.xRight) / lIconStep;
-            x = swp.cx - (i + 1) * lIconStep;
-            // negate the step, for convenience
-            lIconStep = -lIconStep;
-        }
-
-        // draw as many icons as we can / need
-        for (; i < pSysTrayData->cIcons; ++i)
-        {
-            if (x >= rclPaint.xRight)
-                break;
-
-            DrawPointer(hps, x, y, pSysTrayData->pIcons[i].hIcon, DP_MINI);
-            x += lIconStep;
-        }
-
-        WinEndPaint(hps);
-    }
-}
-
-/*
- *@@ WgtMouse:
- *      implementation for WM_BUTTONxyyy in fnwpXSysTray.
- *
- *      Posts a notification to the window associated with the icon and returns
- *      TRUE if this mouse message is within the icon bounds. Otherwise returns
- *      FALSE.
- *
- *      Refer to WgtPaint for more details about the widget geometry.
- */
-
-static
-BOOL WgtMouse(HWND hwnd, ULONG msg, MRESULT mp1, MRESULT mp2,
-              PXCENTERWIDGET pWidget)
-{
-    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
-
-    POINTL  ptl;
-
-    PICONDATA   pIconData;
-    PNOTIFYDATA pNotifyData;
-
-    ptl.x = ((PPOINTS)&mp1)->x;
-    ptl.y = ((PPOINTS)&mp1)->y;
-
-    LOGF(("msg %x ptl %ld,%ld\n", msg, ptl.x, ptl.y));
-
-    pIconData = FindIconDataAtPt(pWidget, &ptl, NULL);
-    if (!pIconData)
-        return FALSE; // hit pad space
-
-    LOGF(("hwnd  %x\n", pIconData->hwnd));
-    LOGF(("usId  %d\n", pIconData->usId));
-    LOGF(("hIcon %x\n", pIconData->hIcon));
-
-    // make the coordinates global
-    WinMapWindowPoints(hwnd, HWND_DESKTOP, &ptl, 1);
-
-    // allocate a NOTIFYDATA struct
-    pNotifyData = AllocNotifyDataPtr(pSysTrayData->pvMemoryPool, pIconData);
-    if (!pNotifyData)
-        return FALSE;
-
-    switch (msg)
-    {
-        case WM_HSCROLL:
-        case WM_VSCROLL:
-            pNotifyData->mp1 += XST_IN_WHEEL << 16;
-            pNotifyData->u.WheelMsg.ulWheelMsg = msg;
-            pNotifyData->u.WheelMsg.ptsPointerPos.x = ptl.x;
-            pNotifyData->u.WheelMsg.ptsPointerPos.y = ptl.y;
-            pNotifyData->u.WheelMsg.usCmd = SHORT2FROMMP(mp2);
-            pNotifyData->mp2 = &pNotifyData->u.WheelMsg;
-        break;
-
-        case WM_CONTEXTMENU:
-            pNotifyData->mp1 += XST_IN_CONTEXT << 16;
-            pNotifyData->u.ContextMsg.ptsPointerPos.x = ptl.x;
-            pNotifyData->u.ContextMsg.ptsPointerPos.y = ptl.y;
-            pNotifyData->u.ContextMsg.fPointer = TRUE;
-            pNotifyData->mp2 = &pNotifyData->u.ContextMsg;
-        break;
-
-        default:
-            pNotifyData->mp1 += XST_IN_MOUSE << 16;
-            pNotifyData->u.MouseMsg.ulMouseMsg = msg;
-            pNotifyData->u.MouseMsg.ptsPointerPos.x = ptl.x;
-            pNotifyData->u.MouseMsg.ptsPointerPos.y = ptl.y;
-            pNotifyData->u.MouseMsg.fsHitTestRes = SHORT1FROMMP(mp2);
-            pNotifyData->u.MouseMsg.fsFlags = SHORT2FROMMP(mp2);
-            pNotifyData->mp2 = &pNotifyData->u.MouseMsg;
-        break;
-    }
-
-    PostNotifyMsg(pSysTrayData, pIconData->hwnd, pNotifyData);
-
-    return TRUE;
-}
-
-/*
- *@@ fnwpXSysTray:
- *      window procedure for the Extended system tray widget class.
- *
- *      There are a few rules which widget window procs
- *      must follow. See XCENTERWIDGETCLASS in center.h
- *      for details.
- *
- *      Other than that, this is a regular window procedure
- *      which follows the basic rules for a PM window class.
- */
-
-static
-MRESULT EXPENTRY fnwpXSysTray(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
-{
-    // get widget data from QWL_USER (stored there by WM_CREATE)
-    PXCENTERWIDGET pWidget = (PXCENTERWIDGET)WinQueryWindowPtr(hwnd, QWL_USER);
-                    // this ptr is valid after WM_CREATE
-
-    switch (msg)
-    {
-        /*
-         * WM_CREATE:
-         *      as with all widgets, we receive a pointer to the
-         *      XCENTERWIDGET in mp1, which was created for us.
-         *
-         *      The first thing the widget MUST do on WM_CREATE
-         *      is to store the XCENTERWIDGET pointer (from mp1)
-         *      in the QWL_USER window word by calling:
-         *
-         *          WinSetWindowPtr(hwnd, QWL_USER, mp1);
-         *
-         *      We could use XCENTERWIDGET.pUser for allocating
-         *      another private memory block for our own stuff,
-         *      for example to be able to store fonts and colors.
-         *      We ain't doing this in the minimal sample.
-         */
-
-        case WM_CREATE:
-        {
-            LOGF(("WM_CREATE\n"));
-
-            PSYSTRAYDATA pSysTrayData = NULL;
-            APIRET arc;
-
-            WinSetWindowPtr(hwnd, QWL_USER, mp1);
-            if (    (!(pWidget = (PXCENTERWIDGET)mp1))
-                 || (!pWidget->pfnwpDefWidgetProc)
-               )
-                // shouldn't happen... stop window creation!!
-                return (MRESULT)TRUE;
-
-            pSysTrayData = malloc(sizeof(*pSysTrayData));
-            if (pSysTrayData == NULL)
-                return (MRESULT)TRUE;
-
-            // initialize the SYSTRAYDATA structure
-            memset(pSysTrayData, 0, sizeof(*pSysTrayData));
-            pSysTrayData->lIconWidth = WinQuerySysValue(HWND_DESKTOP, SV_CXICON) / 2;
-            pSysTrayData->lIconHeight = WinQuerySysValue(HWND_DESKTOP, SV_CYICON) / 2;
-            pSysTrayData->lIconPad = pSysTrayData->lIconHeight / 8;
-            pSysTrayData->cIconsMax = ICONARRAY_GROW;
-            pSysTrayData->pIcons = malloc(sizeof(*pSysTrayData->pIcons) *
-                                          pSysTrayData->cIconsMax);
-            if (pSysTrayData->pIcons == NULL)
-            {
-                FreeSysTrayData(pSysTrayData);
-                return (MRESULT)TRUE;
-            }
-            pSysTrayData->cIcons = 0;
-
-            // Allocate the memory pool for NOTIFYDATA structs (we don't
-            // PAG_COMMIT all memory, AllocNotifyDataPtr() will do so as needed)
-            arc = DosAllocSharedMem((PVOID)&pSysTrayData->pvMemoryPool, NULL,
-                                    SERVER_MEMORYPOOL_SIZE,
-                                    PAG_READ | PAG_WRITE | OBJ_GIVEABLE);
-            if (arc == NO_ERROR)
-            {
-                PMEMPOOLHDR pHdr = (PMEMPOOLHDR)pSysTrayData->pvMemoryPool;
-                arc = DosSetMem(pSysTrayData->pvMemoryPool, 4096,
-                                PAG_COMMIT | PAG_READ | PAG_WRITE);
-                if (arc == NO_ERROR)
-                {
-                    pHdr->ulBeyond = (ULONG)pSysTrayData->pvMemoryPool +
-                                     SERVER_MEMORYPOOL_SIZE;
-                    pHdr->ulNeedsCommit = (ULONG)pSysTrayData->pvMemoryPool +
-                                           4096;
-                    pHdr->ulNext = (ULONG)pSysTrayData->pvMemoryPool +
-                                   sizeof(MEMPOOLHDR);
-                }
-            }
-            if (arc != NO_ERROR)
-            {
-                FreeSysTrayData(pSysTrayData);
-                return (MRESULT)TRUE;
-            }
-
-            // create the "server" window (note that we pass the XCENTERWIDGET
-            // pointer on to it)
-            pSysTrayData->hwndServer =
-                WinCreateWindow(HWND_DESKTOP, WNDCLASS_WIDGET_XSYSTRAY_SERVER,
-                                NULL, WS_MINIMIZED,
-                                0, 0, 0, 0,
-                                HWND_DESKTOP, HWND_BOTTOM,
-                                0, mp1, NULL);
-            if (pSysTrayData->hwndServer == NULLHANDLE)
-            {
-                FreeSysTrayData(pSysTrayData);
-                return (MRESULT)TRUE;
-            }
-
-            pWidget->pUser = pSysTrayData;
-
-            // inform all interested parties that we are fired up
-            WinBroadcastMsg(HWND_DESKTOP, WM_XST_CREATED,
-                            NULL, NULL, BMSG_POST);
-
-            return FALSE; // confirm success
-        }
-        break;
-
-        /*
-         * WM_DESTROY:
-         *      clean up. This _must_ be passed on to
-         *      ctrDefWidgetProc.
-         */
-
-        case WM_DESTROY:
-        {
-            LOGF(("WM_DESTROY\n"));
-
-            PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
-
-            // stop the check alive timer
-            WinStopTimer(pWidget->habWidget, pSysTrayData->hwndServer,
-                         TID_CHECKALIVE);
-
-            FreeSysTrayData(pSysTrayData);
-            pWidget->pUser = NULL;
-
-            // We _MUST_ pass this on, or the default widget proc
-            // cannot clean up, so break
-        }
-        break;
-
-        /*
-         * WM_CONTROL:
-         *      process notifications/queries from the XCenter.
-         */
-
-        case WM_CONTROL:
-            return (MPARAM)WgtControl(pWidget, mp1, mp2);
-        break;
-
-        /*
-         * WM_PAINT:
-         *      well, paint the widget.
-         */
-
-        case WM_PAINT:
-            WgtPaint(hwnd, pWidget);
-            return (MRESULT)TRUE;
-        break;
-
-        /*
-         * WM_PRESPARAMCHANGED:
-         *      A well-behaved widget would intercept
-         *      this and store fonts and colors.
-         */
-
-        /* case WM_PRESPARAMCHANGED:
-        break; */
-
-        /*
-         * All mouse click and wheel events:
-         *      Note that we hide WM_CONTEXTMENU from XCenter when it is within
-         *      the icon bounds as it's a responsibility of the application
-         *      owning the icon to show it.
-         */
-
-        case WM_BUTTON1UP:
-        case WM_BUTTON1DOWN:
-        case WM_BUTTON1CLICK:
-        case WM_BUTTON1DBLCLK:
-        case WM_BUTTON2UP:
-        case WM_BUTTON2DOWN:
-        case WM_BUTTON2CLICK:
-        case WM_BUTTON2DBLCLK:
-        case WM_BUTTON3UP:
-        case WM_BUTTON3DOWN:
-        case WM_BUTTON3CLICK:
-        case WM_BUTTON3DBLCLK:
-        case WM_CONTEXTMENU:
-        case WM_VSCROLL:
-        case WM_HSCROLL:
-        {
-            if (WgtMouse(hwnd, msg, mp1, mp2, pWidget))
-                return (MRESULT)TRUE;
-            // we didn't hit the icon, pass it on to XCenter
-        }
-        break;
-
-        default:
-            break;
-
-    } // end switch(msg)
-
-    return pWidget->pfnwpDefWidgetProc(hwnd, msg, mp1, mp2);
-}
-
-/*
- *@@ WgtXSysTrayUpdateAfterIconAddRemove:
- *      Name says it all.
- */
-
-static
-VOID WgtXSysTrayUpdateAfterIconAddRemove(PXCENTERWIDGET pWidget)
-{
-    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
-
-    if (pSysTrayData->cIcons == 1)
-    {
-        // start a timer to perform "is window alive" checks
-        WinStartTimer(pWidget->habWidget, pSysTrayData->hwndServer,
-                      TID_CHECKALIVE,
-                      TID_CHECKALIVE_TIMEOUT);
-    }
-    else
-    if (pSysTrayData->cIcons == 0)
-    {
-        // stop the check alive timer
-        WinStopTimer(pWidget->habWidget, pSysTrayData->hwndServer,
-                     TID_CHECKALIVE);
-    }
-
-    // ask XCenter to take our new size into account (this will also
-    // invalidate us)
-    WinPostMsg(pWidget->pGlobals->hwndClient,
-               XCM_REFORMAT,
-               (MPARAM)XFMF_GETWIDGETSIZES,
-               0);
-}
-
-/*
- *@@ WgtXSysTrayControl:
- *      implementation for WM_XST_CONTROL in fnwpXSysTrayServer.
- *
- *      Serves as an entry point for all client-side API requests to the
- *      Extended system tray.
- *
- *      Note that this message is being sent from another process which is
- *      awaiting for an answer, so it must return as far as possible (by
- *      rescheduling any potentially long term operation for another cycle).
- */
-
-static
-ULONG WgtXSysTrayControl(HWND hwnd, PXCENTERWIDGET pWidget,
-                         PSYSTRAYCTLDATA pCtlData)
-{
-    BOOL    brc = FALSE;
-    ULONG   xrc = XST_FAIL;
-
-    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
-
-    switch (pCtlData->ulCommand)
-    {
-        case SYSTRAYCMD_GETVERSION:
-        {
-            LOGF(("SYSTRAYCMD_GETVERSION\n"));
-
-            pCtlData->bAcknowledged = TRUE;
-            pCtlData->u.version.ulMajor = XSYSTRAY_VERSION_MAJOR;
-            pCtlData->u.version.ulMajor = XSYSTRAY_VERSION_MINOR;
-            pCtlData->u.version.ulRevision = XSYSTRAY_VERSION_REVISION;
-            xrc = XST_OK;
-        }
-        break;
-
-        case SYSTRAYCMD_ADDICON:
-        {
-            POINTERINFO Info;
-            HPOINTER hIcon = NULLHANDLE;
-            size_t i;
-            PICONDATA pData;
-
-            LOGF(("SYSTRAYCMD_ADDICON\n"));
-            LOGF((" hwnd       %x\n", pCtlData->hwndSender));
-            LOGF((" usId       %d\n", pCtlData->u.icon.usId));
-            LOGF((" hIcon      %x\n", pCtlData->u.icon.hIcon));
-            LOGF((" szToolTip  '%s'\n", pCtlData->u.icon.szToolTip));
-
-            pCtlData->bAcknowledged = TRUE;
-
-            // make a private copy of the provided icon (it will get lost after
-            // we return from this message)
-            brc = WinQueryPointerInfo(pCtlData->u.icon.hIcon, &Info);
-            if (!brc)
-                break;
-            hIcon = WinCreatePointerIndirect(HWND_DESKTOP, &Info);
-            if (hIcon == NULLHANDLE)
-                break;
-
-            pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
-                                 pCtlData->u.icon.usId, &i);
-            if (pData)
-            {
-                LOGF((" Replacing with hIcon %x\n", hIcon));
-
-                // try update the tooltip first
-                free(pData->pszToolTip);
-                pData->pszToolTip = NULL;
-                if (pCtlData->u.icon.szToolTip[0] != '\0')
-                {
-                    pData->pszToolTip = strdup(pCtlData->u.icon.szToolTip);
-                    if (!pData->pszToolTip)
-                    {
-                        WinDestroyPointer(hIcon);
-                        break;
-                    }
-                }
-
-                if (pData->bIsToolTipShowing)
-                {
-                    if (pData->pszToolTip)
-                        // update the tooltip on screen
-                        WinSendMsg(pWidget->pGlobals->hwndTooltip,
-                                   TTM_UPDATETIPTEXT,
-                                   (MPARAM)pData->pszToolTip, 0);
-                    else
-                        // hide the tooltip
-                        WinSendMsg(pWidget->pGlobals->hwndTooltip,
-                                   TTM_SHOWTOOLTIPNOW,
-                                   (MPARAM)FALSE, 0);
-                }
-
-                // now update the icon
-                WinDestroyPointer(pData->hIcon);
-                pData->hIcon = hIcon;
-                pData->ulMsgId = pCtlData->u.icon.ulMsgId;
-
-                // we didn't change the number of icons so simply invalidate
-                WinInvalidateRect(pWidget->hwndWidget, NULL, FALSE);
-
-                xrc = XST_REPLACED;
-            }
-            else
-            {
-                LOGF((" Adding new hIcon %x\n", hIcon));
-
-                if (pSysTrayData->cIcons == pSysTrayData->cIconsMax)
-                {
-                    PICONDATA pNewIcons;
-
-                    LOGF((" Allocating more memory (new icon count is %u)!\n",
-                          pSysTrayData->cIcons + 1));
-
-                    pNewIcons = realloc(pSysTrayData->pIcons,
-                                        sizeof(*pSysTrayData->pIcons) *
-                                        pSysTrayData->cIconsMax + ICONARRAY_GROW);
-                    if (pNewIcons == NULL)
-                    {
-                        WinDestroyPointer(hIcon);
-                        break;
-                    }
-
-                    pSysTrayData->pIcons = pNewIcons;
-                    pSysTrayData->cIconsMax += ICONARRAY_GROW;
-                }
-
-                i = pSysTrayData->cIcons;
-
-                pData = &pSysTrayData->pIcons[i];
-                memset(pData, 0, sizeof(*pData));
-
-                pData->hwnd = pCtlData->hwndSender;
-                pData->usId = pCtlData->u.icon.usId;
-                pData->hIcon = hIcon;
-                pData->ulMsgId = pCtlData->u.icon.ulMsgId;
-
-                if (pCtlData->u.icon.szToolTip[0] != '\0')
-                {
-                    pData->pszToolTip = strdup(pCtlData->u.icon.szToolTip);
-                    if (!pData->pszToolTip)
-                    {
-                        WinDestroyPointer(hIcon);
-                        break;
-                    }
-                }
-
-                ++pSysTrayData->cIcons;
-
-                WgtXSysTrayUpdateAfterIconAddRemove(pWidget);
-
-                xrc = XST_OK;
-            }
-        }
-        break;
-
-        case SYSTRAYCMD_REPLACEICON:
-        {
-            POINTERINFO Info;
-            HPOINTER hIcon = NULLHANDLE;
-            size_t i;
-            PICONDATA pData;
-
-            LOGF(("SYSTRAYCMD_REPLACEICON\n"));
-            LOGF((" hwnd  %x\n", pCtlData->hwndSender));
-            LOGF((" usId  %d\n", pCtlData->u.icon.usId));
-
-            pCtlData->bAcknowledged = TRUE;
-
-            // make a private copy of the provided icon (it will get lost after
-            // we return from this message)
-            brc = WinQueryPointerInfo(pCtlData->u.icon.hIcon, &Info);
-            if (!brc)
-                break;
-            hIcon = WinCreatePointerIndirect(HWND_DESKTOP, &Info);
-            if (hIcon == NULLHANDLE)
-                break;
-
-            pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
-                                 pCtlData->u.icon.usId, &i);
-            if (pData)
-            {
-                LOGF((" Replacing with hIcon %x\n", hIcon));
-
-                WinDestroyPointer(pData->hIcon);
-                pData->hIcon = hIcon;
-                pData->ulMsgId = pCtlData->u.icon.ulMsgId;
-
-                // we didn't change the number of icons so simply invalidate
-                WinInvalidateRect(pWidget->hwndWidget, NULL, FALSE);
-
-                xrc = XST_OK;
-            }
-            else
-                LOGF((" Icon not found!\n"));
-        }
-        break;
-
-        case SYSTRAYCMD_REMOVEICON:
-        {
-            size_t i;
-            PICONDATA pData;
-
-            LOGF(("SYSTRAYCMD_REMOVEICON\n"));
-            LOGF((" hwnd  %x\n", pCtlData->hwndSender));
-            LOGF((" usId  %d\n", pCtlData->u.icon.usId));
-
-            pCtlData->bAcknowledged = TRUE;
-
-            pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
-                                 pCtlData->u.icon.usId, &i);
-            if (pData)
-            {
-                LOGF((" Removing hIcon %x\n", pData->hIcon));
-
-                FreeIconData(pData);
-
-                --pSysTrayData->cIcons;
-                if (pSysTrayData->cIcons > 0)
-                {
-                    memcpy(&pSysTrayData->pIcons[i],
-                           &pSysTrayData->pIcons[i + 1],
-                           sizeof(*pSysTrayData->pIcons) * (pSysTrayData->cIcons - i));
-                }
-
-                WgtXSysTrayUpdateAfterIconAddRemove(pWidget);
-
-                xrc = XST_OK;
-            }
-            else
-                LOGF((" Icon not found!\n"));
-        }
-        break;
-
-        case SYSTRAYCMD_SETTOOLTIP:
-        {
-            size_t i;
-            PICONDATA pData;
-
-            LOGF(("SYSTRAYCMD_SETTOOLTIP\n"));
-            LOGF((" hwnd  %x\n", pCtlData->hwndSender));
-            LOGF((" usId  %d\n", pCtlData->u.icon.usId));
-
-            pCtlData->bAcknowledged = TRUE;
-
-            pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
-                                 pCtlData->u.icon.usId, &i);
-            if (pData)
-            {
-                LOGF((" Replacing with szToolTip '%s'\n",
-                      pCtlData->u.icon.szToolTip));
-
-                free(pData->pszToolTip);
-                pData->pszToolTip = NULL;
-                if (pCtlData->u.icon.szToolTip[0] != '\0')
-                {
-                    pData->pszToolTip = strdup(pCtlData->u.icon.szToolTip);
-                    if (!pData->pszToolTip)
-                        break;
-                }
-
-                if (pData->bIsToolTipShowing)
-                {
-                    if (pData->pszToolTip)
-                        // update the tooltip on screen
-                        WinSendMsg(pWidget->pGlobals->hwndTooltip,
-                                   TTM_UPDATETIPTEXT,
-                                   (MPARAM)pData->pszToolTip, 0);
-                    else
-                        // hide the tooltip
-                        WinSendMsg(pWidget->pGlobals->hwndTooltip,
-                                   TTM_SHOWTOOLTIPNOW,
-                                   (MPARAM)FALSE, 0);
-                }
-
-                xrc = XST_OK;
-            }
-            else
-                LOGF((" Icon not found!\n"));
-        }
-        break;
-
-        case SYSTRAYCMD_QUERYRECT:
-        {
-            size_t i;
-            PICONDATA pData;
-
-            LOGF(("SYSTRAYCMD_QUERYRECT\n"));
-            LOGF((" hwnd  %x\n", pCtlData->hwndSender));
-            LOGF((" usId  %d\n", pCtlData->u.icon.usId));
-
-            pCtlData->bAcknowledged = TRUE;
-
-            pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
-                                 pCtlData->u.icon.usId, &i);
-            if (pData)
-            {
-                // Refer to FindIconDataAtPt() for details
-
-                SWP     swp;
-                RECTL   rcl;
-                BOOL    bLeftToRight;
-                LONG    y, lIconStep;
-
-                WinQueryWindowPos(pWidget->hwndWidget, &swp);
-                WinQueryWindowRect(pWidget->pGlobals->hwndClient, &rcl);
-
-                y = (swp.cy - pSysTrayData->lIconHeight) / 2;
-
-                // detect the direction
-                bLeftToRight = swp.x + swp.cx / 2 < (rcl.xRight / 2);
-
-                lIconStep = pSysTrayData->lIconWidth + pSysTrayData->lIconPad;
-
-                pCtlData->u.rect.rclIcon.yBottom = y;
-                pCtlData->u.rect.rclIcon.yTop = y + pSysTrayData->lIconHeight;
-
-                if (bLeftToRight)
-                {
-                    pCtlData->u.rect.rclIcon.xLeft =
-                        pSysTrayData->lIconPad + (lIconStep) * i;
-                    pCtlData->u.rect.rclIcon.xRight =
-                        pCtlData->u.rect.rclIcon.xLeft +
-                        pSysTrayData->lIconWidth;
-                }
-                else
-                {
-                    pCtlData->u.rect.rclIcon.xLeft =
-                        swp.cx - (lIconStep) * (i + 1);
-                    pCtlData->u.rect.rclIcon.xRight =
-                        pCtlData->u.rect.rclIcon.xLeft +
-                        pSysTrayData->lIconWidth;
-                }
-
-                // convert to screen coordinates
-                WinMapWindowPoints(pWidget->hwndWidget, HWND_DESKTOP,
-                                   (PPOINTL)&pCtlData->u.rect.rclIcon, 2);
-                xrc = XST_OK;
-            }
-            else
-                LOGF((" Icon not found!\n"));
-        }
-        break;
-
-        default:
-            break;
-    }
-
-    LOGF(("return %d (WinGetLastError is %x)\n",
-          xrc, WinGetLastError(pWidget->habWidget)));
-
-    return xrc;
-}
-
-/*
- *@@ WgtXSysTrayTimer:
- *      implementation for WM_TIMER in fnwpXSysTrayServer.
- */
-
-static
-VOID WgtXSysTrayTimer(HWND hwnd, PXCENTERWIDGET pWidget,
-                      USHORT usTimerId)
-{
-    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
-    PMEMPOOLHDR pMemPoolHdr = (PMEMPOOLHDR)pSysTrayData->pvMemoryPool;
-    PMEMPOOLBLK pMemPoolBlk;
-    ULONG ulMemPoolMax;
-
-    if (usTimerId == TID_CHECKALIVE)
-    {
-        // check if windows associated with the icons are still alive
-        // and remove those icons whose windows are invalid
-        BOOL bAnyDead = FALSE;
-        size_t i;
-        for (i = 0; i < pSysTrayData->cIcons; ++i)
-        {
-            if (!WinIsWindow(pWidget->habWidget, pSysTrayData->pIcons[i].hwnd))
-            {
-                PICONDATA pData = &pSysTrayData->pIcons[i];
-
-                LOGF(("Removing icon of dead window!\n"));
-                LOGF((" hwnd  %x\n", pData->hwnd));
-                LOGF((" usId  %ld\n", pData->usId));
-                LOGF((" hIcon %x\n", pData->hIcon));
-
-                // free memory blocks from the pool allocated for this client
-                ulMemPoolMax = pMemPoolHdr->ulBeyond;
-                if (ulMemPoolMax > pMemPoolHdr->ulNeedsCommit)
-                    ulMemPoolMax = pMemPoolHdr->ulNeedsCommit;
-                ulMemPoolMax -= sizeof(MEMPOOLBLK);
-
-                pMemPoolBlk = pMemPoolHdr->aBlocks;
-                while ((ULONG)pMemPoolBlk <= ulMemPoolMax)
-                {
-                    if (pMemPoolBlk->hwnd == pData->hwnd)
-                    {
-                        LOGF((" freeing memory block %p\n", pMemPoolBlk));
-                        FreeNotifyDataPtr(pSysTrayData->pvMemoryPool,
-                                          pData->hwnd,
-                                          &pMemPoolBlk->NotifyData);
-                    }
-                    ++pMemPoolBlk;
-                }
-
-                bAnyDead = TRUE;
-                FreeIconData(pData);
-                // pData->hwnd is NULLHANDLE here
-            }
-        }
-
-        if (bAnyDead)
-        {
-            // compact the icon array
-            i = 0;
-            while (i < pSysTrayData->cIcons)
-            {
-                if (pSysTrayData->pIcons[i].hwnd == NULLHANDLE)
-                {
-                    --pSysTrayData->cIcons;
-                    if (pSysTrayData->cIcons > 0)
-                    {
-                        memcpy(&pSysTrayData->pIcons[i],
-                               &pSysTrayData->pIcons[i + 1],
-                               sizeof(*pSysTrayData->pIcons) * (pSysTrayData->cIcons - i));
-                    }
-                }
-                else
-                    ++i;
-            }
-
-            WgtXSysTrayUpdateAfterIconAddRemove(pWidget);
-        }
-    }
-}
-
-/*
- *@@ fnwpXSysTrayServer:
- *      window procedure for the Extended system tray server window class.
- *
- *      A separate "server" class is necessary because we need a CS_FRAME
- *      top-level window for DDE (which we need to support to be backward
- *      compatible with the System tray wdget from the SysTray/WPS package) and
- *      also to make ourselves discoverable for the client-side implementation
- *      of our new extended API (which queries the class of each top-level
- *      window to find the system tray server).
- */
-
-static
-MRESULT EXPENTRY fnwpXSysTrayServer(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
-{
-    // get widget data from QWL_USER_SERVER_DATA (stored there by WM_CREATE)
-    PXCENTERWIDGET pWidget =
-        (PXCENTERWIDGET)WinQueryWindowPtr(hwnd, QWL_USER_SERVER_DATA);
-                    // this ptr is valid after WM_CREATE
-
-    switch (msg)
-    {
-        case WM_CREATE:
-            LOGF(("WM_CREATE\n"));
-            WinSetWindowPtr(hwnd, QWL_USER_SERVER_DATA, mp1);
-            return FALSE; // confirm success
-        break;
-
-        case WM_DESTROY:
-            LOGF(("WM_DESTROY\n"));
-        break;
-
-        /*
-         * WM_XST_CONTROL:
-         *      This is the message sent to us by the clinet-side implementation
-         *      of the API to request some function. mp1 points to a
-         *      SYSTRAYCTLDATA structure.
-         */
-
-        case WM_XST_CONTROL:
-            return (MRESULT)WgtXSysTrayControl(hwnd, pWidget,
-                                               (PSYSTRAYCTLDATA)mp1);
-        break;
-
-        /*
-         * WM_TIMER:
-         *      timer event.
-         */
-
-        case WM_TIMER:
-            WgtXSysTrayTimer(hwnd, pWidget, SHORT1FROMMP(mp1));
-            return (MRESULT)TRUE;
-        break;
-
-        default:
-            break;
-    } // end switch(msg)
-
-    return WinDefWindowProc(hwnd, msg, mp1, mp2);
-}
-
-/* ******************************************************************
- *
- *   Exported procedures
- *
- ********************************************************************/
-
-/*
- *@@ WgtInitModule:
- *      required export with ordinal 1, which must tell
- *      the XCenter how many widgets this DLL provides,
- *      and give the XCenter an array of XCENTERWIDGETCLASS
- *      structures describing the widgets.
- *
- *      With this call, you are given the module handle of
- *      XFLDR.DLL. For convenience, and if you have the full
- *      XWorkplace source code, you could resolve imports
- *      for some useful functions which are exported thru
- *      src\shared\xwp.def. We don't do this here.
- *
- *      This function must also register the PM window classes
- *      which are specified in the XCENTERWIDGETCLASS array
- *      entries. For this, you are given a HAB which you
- *      should pass to WinRegisterClass. For the window
- *      class style (4th param to WinRegisterClass),
- *      you should specify
- *
- +          CS_PARENTCLIP | CS_SIZEREDRAW | CS_SYNCPAINT
- *
- *      Your widget window _will_ be resized by the XCenter,
- *      even if you're not planning it to be.
- *
- *      This function only gets called _once_ when the widget
- *      DLL has been successfully loaded by the XCenter. If
- *      there are several instances of a widget running (in
- *      the same or in several XCenters), this function does
- *      not get called again. However, since the XCenter unloads
- *      the widget DLLs again if they are no longer referenced
- *      by any XCenter, this might get called again when the
- *      DLL is re-loaded.
- *
- *      There will ever be only one load occurence of the DLL.
- *      The XCenter manages sharing the DLL between several
- *      XCenters. As a result, it doesn't matter if the DLL
- *      has INITINSTANCE etc. set or not.
- *
- *      If this returns 0, this is considered an error, and the
- *      DLL will be unloaded again immediately.
- *
- *      If this returns any value > 0, *ppaClasses must be
- *      set to a static array (best placed in the DLL's
- *      global data) of XCENTERWIDGETCLASS structures,
- *      which must have as many entries as the return value.
- */
-
-ULONG EXPENTRY WgtInitModule(HAB hab,               // XCenter's anchor block
-                             HMODULE hmodPlugin, // module handle of the widget DLL
-                             HMODULE hmodXFLDR,     // XFLDR.DLL module handle
-                             PCXCENTERWIDGETCLASS *ppaClasses,
-                             PSZ pszErrorMsg)       // if 0 is returned, 500 bytes of error msg
-{
-    ULONG       ulrc = 0, ul = 0;
-    CLASSINFO   ClassInfo;
-
-    LOGF(("hmodPlugin %x\n", hmodPlugin));
-
-    do
-    {
-        // resolve imports from XFLDR.DLL (this is basically
-        // a copy of the doshResolveImports code, but we can't
-        // use that before resolving...)
-        for (ul = 0;
-             ul < sizeof(G_aImports) / sizeof(G_aImports[0]);
-             ul++)
-        {
-            APIRET arc;
-            if ((arc = DosQueryProcAddr(hmodXFLDR,
-                                        0,               // ordinal, ignored
-                                        (PSZ)G_aImports[ul].pcszFunctionName,
-                                        G_aImports[ul].ppFuncAddress))
-                        != NO_ERROR)
-            {
-                snprintf(pszErrorMsg, 500,
-                         "Import %s failed with %ld.",
-                         G_aImports[ul].pcszFunctionName, arc);
-                break;
-            }
-        }
-        if (ul < sizeof(G_aImports) / sizeof(G_aImports[0]))
-            break;
-
-        // register our PM window class
-        if (!WinRegisterClass(hab,
-                             WNDCLASS_WIDGET_XSYSTRAY,
-                             fnwpXSysTray,
-                             CS_PARENTCLIP | CS_SIZEREDRAW | CS_SYNCPAINT,
-                             sizeof(PVOID))
-                                // extra memory to reserve for QWL_USER
-            )
-        {
-            snprintf(pszErrorMsg, 500,
-                     "WinRegisterClass(%s) failed with %lX.",
-                     WNDCLASS_WIDGET_XSYSTRAY, WinGetLastError(hab));
-            break;
-        }
-
-        // get the window data size for the WC_FRAME class (any window class
-        // that specifies CS_FRAME must have at least this number, otherise
-        // WinRegisterClass returns 0x1003
-        if (!WinQueryClassInfo(hab, (PSZ)WC_FRAME, &ClassInfo))
-            break;
-        QWL_USER_SERVER_DATA = ClassInfo.cbWindowData;
-
-        if (!WinRegisterClass(hab,
-                              WNDCLASS_WIDGET_XSYSTRAY_SERVER,
-                              fnwpXSysTrayServer,
-                              CS_FRAME,
-                              QWL_USER_SERVER_DATA + sizeof(PVOID))
-                                // extra memory to reserve for QWL_USER
-            )
-        {
-            // error registering class: report error then
-            snprintf(pszErrorMsg, 500,
-                     "WinRegisterClass(%s) failed with %lX",
-                     WNDCLASS_WIDGET_XSYSTRAY_SERVER, WinGetLastError(hab));
-            break;
-        }
-
-        if (WM_XST_CREATED == 0)
-            WM_XST_CREATED = WinAddAtom(WinQuerySystemAtomTable(),
-                                        WM_XST_CREATED_ATOM);
-        if (WM_XST_NOTIFY == 0)
-            WM_XST_NOTIFY = WinAddAtom(WinQuerySystemAtomTable(),
-                                       WM_XST_NOTIFY_ATOM);
-
-        // no error:
-        // return widget classes array
-        *ppaClasses = G_WidgetClasses;
-
-        // return no. of classes in this DLL (one here):
-        ulrc = sizeof(G_WidgetClasses) / sizeof(G_WidgetClasses[0]);
-    }
-    while (0);
-
-    LOGF(("pszErrorMsg '%s'\n", pszErrorMsg));
-    LOGF(("ulrc %d\n", ulrc));
-
-    return ulrc;
-}
-
-/*
- *@@ WgtUnInitModule:
- *      optional export with ordinal 2, which can clean
- *      up global widget class data.
- *
- *      This gets called by the XCenter right before
- *      a widget DLL gets unloaded. Note that this
- *      gets called even if the "init module" export
- *      returned 0 (meaning an error) and the DLL
- *      gets unloaded right away.
- */
-
-VOID EXPENTRY WgtUnInitModule(VOID)
-{
-    LOGF(("\n"));
-}
-
-/*
- *@@ WgtQueryVersion:
- *      this new export with ordinal 3 can return the
- *      XWorkplace version number which is required
- *      for this widget to run. For example, if this
- *      returns 0.9.10, this widget will not run on
- *      earlier XWorkplace versions.
- *
- *      NOTE: This export was mainly added because the
- *      prototype for the "Init" export was changed
- *      with V0.9.9. If this returns 0.9.9, it is
- *      assumed that the INIT export understands
- *      the new FNWGTINITMODULE_099 format (see center.h).
- */
-
-VOID EXPENTRY WgtQueryVersion(PULONG pulMajor,
-                              PULONG pulMinor,
-                              PULONG pulRevision)
-{
-    *pulMajor = 0;
-    *pulMinor = 9;
-    *pulRevision = 9;
-}
-
Index: unk/src/3rdparty/os2/xsystray/xsystray.def
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/xsystray.def	(revision 834)
+++ 	(revision )
@@ -1,20 +1,0 @@
-;
-; module definition file for XSYSTRAY.DLL, an XCenter
-; plug-in widget DLL.
-;
-
-LIBRARY XSYSTRAY INITGLOBAL TERMGLOBAL
-Description '@#netlabs.org:0.1.0#@ Extended system tray widget DLL for XCenter'
-PROTMODE
-
-DATA              MULTIPLE READWRITE LOADONCALL NONSHARED
-;       doesn't really matter, because the widget is only ever used
-;       in the WPS process; we use MULTIPLE NONSHARED here to reduce
-;       the amount of shared memory that is needed for the DLL
-
-CODE              LOADONCALL
-
-EXPORTS
-    WgtInitModule      @1
-    WgtUnInitModule    @2
-    WgtQueryVersion    @3
Index: unk/src/3rdparty/os2/xsystray/xsystray.h
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/xsystray.h	(revision 834)
+++ 	(revision )
@@ -1,208 +1,0 @@
-
-/*
- *@@sourcefile xsystray.h:
- *      Extended system tray widget for XCenter/eCenter.
- *
- *      Private declarations.
- *
- *      Copyright (C) 2009 Dmitry A. Kuminov
- *
- *      This file is part of the Extended system tray widget source package.
- *      Extended system tray widget is free software; you can redistribute it
- *      and/or modify it under the terms of the GNU General Public License as
- *      published by the Free Software Foundation, in version 2 as it comes in
- *      the "COPYING" file of the Extended system tray widget distribution. This
- *      program is distributed in the hope that it will be useful, but WITHOUT
- *      ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- *      FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- *      more details.
- */
-
-#ifndef XSYSTRAY_HEADER_INCLUDED
-#define XSYSTRAY_HEADER_INCLUDED
-
-#include "xsystray_api.h"
-
-#include <sys/builtin.h>        // atomics
-
-#define XSYSTRAY_VERSION_MAJOR 0
-#define XSYSTRAY_VERSION_MINOR 1
-#define XSYSTRAY_VERSION_REVISION 0
-
-#define WNDCLASS_WIDGET_XSYSTRAY_SERVER "XWPCenterExtendedSysTrayServer"
-
-#define WNDCLASS_WIDGET_XSYSTRAY        "XWPCenterExtendedSysTray"
-#define INTCLASS_WIDGET_XSYSTRAY        "ExtendedSysTray"
-#define HUMANSTR_WIDGET_XSYSTRAY        "Extended system tray"
-
-#define WM_XST_CREATED_ATOM             "ExtendedSysTray.WM_XST_CREATED"
-#define WM_XST_NOTIFY_ATOM              "ExtendedSysTray.WM_XST_NOTIFY"
-
-#define WM_XST_CONTROL  (WM_USER + 0)
-        // message sent to the system tray server to request an action
-
-// server action commands
-typedef enum
-{
-    SYSTRAYCMD_GETVERSION,
-    SYSTRAYCMD_ADDICON,
-    SYSTRAYCMD_REPLACEICON,
-    SYSTRAYCMD_REMOVEICON,
-    SYSTRAYCMD_SETTOOLTIP,
-    SYSTRAYCMD_SHOWBALLOON,
-    SYSTRAYCMD_HIDEBALLOON,
-    SYSTRAYCMD_QUERYRECT,
-} SYSTRAYCMD;
-
-// server responses to WM_XST_CONTROL
-#define XST_OK          0   // command succeeded
-#define XST_FAIL        1   // command failed
-#define XST_REPLACED    2   // SYSTRAYCMD_ADDICON replaced the existing icon
-
-/*
- *@@ SYSTRAYCTLDATA:
- *      Structure holding information accompanying WM_XST_CONTROL messages sent
- *      to the system tray server by clients (windows associated with system
- *      tray icons).
- *
- *      NOTE: When you change the size of this structure, you may also need to
- *      change CLIENT_MEMORYPOOL_SIZE value (see the comments there for
- *      details).
- */
-
-typedef struct
-{
-    SYSTRAYCMD  ulCommand;
-                // command to execute, must always be set
-    HWND        hwndSender;
-                // sender window, a must for SYSTRAYCMD_ADDICON, _CHANGEICON,
-                // _REMOVEICON, _SETTOOLTIP, _SHOWBALLOON, _HIDEBALLOON,
-                // _QUERYRECT
-    union
-    {
-        struct
-        {
-            ULONG ulMajor;
-            ULONG ulMinor;
-            ULONG ulRevision;
-        } version;
-          // used by SYSTRAYCMD_GETVERSION
-
-        struct
-        {
-            USHORT      usId;
-            HPOINTER    hIcon;
-            CHAR        szToolTip[512];
-            ULONG       ulMsgId;
-        } icon;
-          // used by SYSTRAYCMD_ADDICON, _CHANGEICON, _REMOVEICON, _SETTOOLTIP
-
-        struct
-        {
-            RECTL rclIcon;
-        } rect;
-          // used by SYSTRAYCMD_QUERYRECT
-    } u;
-
-    BOOL    bAcknowledged : 1;
-            // set to true by the recipient if it processes the message
-
-} SYSTRAYCTLDATA, *PSYSTRAYCTLDATA;
-
-/*
- *@@ NOTIFYDATA:
- *      Structure holding information acompanying notification messages
- *      posted to clients (windows associated with system tray icons) about
- *      icon events. This structure unions all public notification code
- *      dependent structures defined in xsystray_api.h (starting with XST*).
- *
- *      All messages posted to the client have an ID corresponding to the
- *      WM_XST_NOTIFY_ATOM in the system atom table. The client-side API
- *      implementation intercepts these messages (using HK_INPUT), composes a
- *      new message given the information in NOTIFYDATA, frees the NOTIFYDATA
- *      pointer using FreeNotifyDataPtr() and then sends the composed message to
- *      the appropriate window.
- *
- *      The layout of the XST_NOTIFY message is as follows:
- *
- *          param1
- *              PNOTIFYDATA pNotifyData     pointer to the NOTIFYDATA structure
- *
- *          param2
- *              PVOID       pvMemoryPool    server memory pool (for the
- *                                          FreeNotifyDataPtr() call)
- *
- *      NOTE: Structures in the union should only contain values; passing
- *      pointers to arbitrary data to the client side is not supported (yet).
- *
- *      NOTE: When you change the size of this structure, you may also need to
- *      change SERVER_MEMORYPOOL_SIZE value in xsystray.c (see the comments
- *      there for details).
- */
-
-typedef struct
-{
-    ULONG   msg;
-            // ID of the message that is to be sent to the target window
-    MPARAM  mp1;
-            // message parameter (usually: USHORT usIconId, USHORT usNotifyCode)
-    MPARAM  mp2;
-            // message parameter (usually, a pointer to a struct from the union)
-    union
-    {
-        XSTMOUSEMSG     MouseMsg;
-        XSTCONTEXTMSG   ContextMsg;
-        XSTWHEELMSG     WheelMsg;
-    } u;
-
-} NOTIFYDATA, *PNOTIFYDATA;
-
-// Header of the server-side memory pool
-typedef struct
-{
-    volatile HWND   hwnd;        // owner of the block or NULLHANDLE if free
-    NOTIFYDATA      NotifyData;  // data
-
-} MEMPOOLBLK, *PMEMPOOLBLK;
-
-// allocation unit in the server-side memory pool
-typedef struct
-{
-    ULONG ulBeyond;         // address of the first byte beyond the memory pool
-
-    volatile ULONG ulNeedsCommit;   // address of the first decommitted byte
-    volatile ULONG ulNext;          // address of next possibly free block
-
-    MEMPOOLBLK aBlocks[0];          // fake array for easier addressing
-
-} MEMPOOLHDR, *PMEMPOOLHDR;
-
-/*
- *@@ FreeNotifyDataPtr:
- *      Frees the NOTIFYDATA structure allocated by AllocNotifyDataPtr().
- *
- *      See AllocNotifyDataPtr() for more details about allocating these
- *      structures.dd
- */
-
-inline
-VOID FreeNotifyDataPtr(PVOID pvMemoryPool,  // in: memory pool base address
-                       HWND hwndOwner,      // in: owner of the struct to free
-                       PNOTIFYDATA pData)   // in: address of the struct to free
-{
-    PMEMPOOLHDR pHdr = (PMEMPOOLHDR)pvMemoryPool;
-    PMEMPOOLBLK pBlk = (PMEMPOOLBLK)((ULONG)pData - sizeof(HWND));
-
-    ULONG ulNext = pHdr->ulNext;
-
-    __atomic_cmpxchg32((uint32_t *)&pBlk->hwnd, NULLHANDLE, hwndOwner);
-
-    // if the next possible free block is greater than we just freed,
-    // set it to us (to minimize the amount of committed pages)
-    if (ulNext > (ULONG)pBlk)
-        __atomic_cmpxchg32((uint32_t *)&pHdr->ulNext, (ULONG)pBlk, ulNext);
-}
-
-#endif // XSYSTRAY_HEADER_INCLUDED
-
-
Index: /trunk/src/3rdparty/os2/xsystray/xsystray.pro
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/xsystray.pro	(revision 834)
+++ /trunk/src/3rdparty/os2/xsystray/xsystray.pro	(revision 835)
@@ -1,10 +1,3 @@
-TEMPLATE = lib
+TEMPLATE = subdirs
+SUBDIRS = plugin apilib
 
-CONFIG += dll
-QT =
-
-SOURCES = xsystray.c
-
-DEF_FILE = xsystray.def
-
-DESTDIR = $$[QT_INSTALL_PLUGINS]/xcenter
Index: unk/src/3rdparty/os2/xsystray/xsystray_api.c
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/xsystray_api.c	(revision 834)
+++ 	(revision )
@@ -1,674 +1,0 @@
-
-/*
- *@@sourcefile xsystray_api.c:
- *      Extended system tray widget for XCenter/eCenter.
- *
- *      Implementation of the public API.
- *
- *      Copyright (C) 2009 Dmitry A. Kuminov
- *
- *      This file is part of the Extended system tray widget source package.
- *      Extended system tray widget is free software; you can redistribute it
- *      and/or modify it under the terms of the GNU General Public License as
- *      published by the Free Software Foundation, in version 2 as it comes in
- *      the "COPYING" file of the Extended system tray widget distribution. This
- *      program is distributed in the hope that it will be useful, but WITHOUT
- *      ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- *      FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- *      more details.
- */
-
-#define OS2EMX_PLAIN_CHAR
-#define INCL_DOSERRORS
-#define INCL_DOSPROCESS
-#define INCL_WINWINDOWMGR
-#define INCL_WINERRORS
-#define INCL_WINATOM
-#define INCL_WINPOINTERS
-#define INCL_WINHOOKS
-#define INCL_WINWINDOWMGR
-#include <os2.h>
-
-#include "xsystray_api.h"
-#include "xsystray.h"
-
-#include <string.h>
-#include <sys/builtin.h>        // atomics
-#include <sys/fmutex.h>         // fast mutex
-#include <sys/smutex.h>         // simple mutex
-
-static ULONG WM_XST_CREATED = 0;
-             // identity of the WM_XST_CREATED message taken from the atom table
-static ULONG WM_XST_NOTIFY = 0;
-             // identity of the WM_XST_NOTIFY message taken from the atom table
-
-static
-volatile HWND G_hwndSysTray = NULLHANDLE;
-              // window handle of the system tray server
-
-static
-volatile PVOID G_pvMemoryPool = NULL;
-               // shared memory pool for SYSTRAYCTLDATA structs used by
-               // WM_XST_CONTROL messages. Note that once allocated, this memory
-               // is never freed: it is intentional since the memory is assumed
-               // to be always in need and that the system will free it when the
-               // application terminates
-
-#define CLIENT_MEMORYPOOL_SIZE 65536
-        // taking SYSTRAYCTLDATA size into account (<=1024 B), this is enough
-        // for at least 64 threads sending WM_XST_CONTROL simultaneously, which
-        // sounds sane
-
-typedef struct
-{
-    HMQ         hmq;
-    unsigned    cRefs;
-} HMQREFS, *PHMQREFS;
-
-static PHMQREFS G_pHmqRefs = NULL;
-                // array of references to each HMQ that we make
-static size_t   G_cHmqRefs = 0;
-                // number of elements in G_pHmqRefs
-static size_t   G_cHmqRefsMax = 0;
-                // maximum number of elements in G_pHmqRefs
-static _fmutex  G_fmtx;
-                // fast mutex to protect
-static _smutex  G_smtx = 0;
-                // simple mutex for xstAddSysTrayIcon()
-#define         HMQREFS_GROW 4
-                // how fast G_pHmqRefs grows when more space is necessary
-
-// @todo to be on the safe side with casting in __atomic_cmpxchg32() we need
-// compile-time assertions like this:
-// AssertCompile(sizeof(uint32_t) == sizeof(HWND));
-// AssertCompile(sizeof(uint32_t) == sizeof(PVOID));
-
-static HWND FindSysTrayServerWindow()
-{
-    char buf[sizeof(WNDCLASS_WIDGET_XSYSTRAY_SERVER) + 1];
-    HWND hwnd;
-    HENUM henum = WinBeginEnumWindows(HWND_DESKTOP);
-    while ((hwnd = WinGetNextWindow(henum)) != NULLHANDLE)
-    {
-        LONG len = WinQueryClassName(hwnd, sizeof(buf), buf);
-        buf[len] = '\0';
-        if (strcmp(WNDCLASS_WIDGET_XSYSTRAY_SERVER, buf) == 0)
-            break;
-    }
-    WinEndEnumWindows(henum);
-
-    return hwnd;
-}
-
-static ULONG SendSysTrayCtlMsg(PSYSTRAYCTLDATA pData)
-{
-    APIRET arc;
-    PID pid;
-    TID tid;
-    MRESULT mrc;
-
-    BOOL bTriedFind = FALSE;
-
-    do
-    {
-        if (G_hwndSysTray == NULLHANDLE)
-        {
-            bTriedFind = TRUE;
-            HWND hwnd = FindSysTrayServerWindow();
-            __atomic_cmpxchg32((volatile uint32_t *)&G_hwndSysTray,
-                               hwnd, NULLHANDLE);
-            if (G_hwndSysTray == NULLHANDLE)
-                break;
-        }
-
-        if (bTriedFind)
-        {
-            arc = ERROR_INVALID_HANDLE;
-            if (WinQueryWindowProcess(G_hwndSysTray, &pid, &tid))
-                arc = DosGiveSharedMem(G_pvMemoryPool,
-                                       pid, PAG_READ | PAG_WRITE);
-            if (arc != NO_ERROR)
-                break;
-        }
-
-        pData->bAcknowledged = FALSE;
-
-        mrc = WinSendMsg(G_hwndSysTray, WM_XST_CONTROL, pData, NULL);
-        if (pData->bAcknowledged)
-            return (ULONG)mrc;
-
-        // if we failed to send the message, it may mean that XCenter was restarted
-        // or the system tray was re-enabled. Try to get a new handle (only if we
-        // didn't already do it in this call)
-        if (!bTriedFind)
-        {
-            G_hwndSysTray = NULLHANDLE;
-            continue;
-        }
-
-        break;
-    }
-    while (1);
-
-    return XST_FAIL;
-}
-
-/*
- *@@ AllocSysTrayCtlDataPtr:
- *      Allocates a SYSTRAYCTLDATA struct in the pool of shared memory.
- *
- *      If there is no free space in the pool, it returns NULL. The allocated
- *      memory must be freed by FreeSysTrayCtlDataPtr() when not needed.
- */
-
-static PSYSTRAYCTLDATA AllocSysTrayCtlDataPtr()
-{
-    APIRET arc;
-    PVOID pvPool;
-    PSYSTRAYCTLDATA pData;
-
-    if (!G_pvMemoryPool)
-    {
-        // Note: we don't PAG_COMMIT, DosSubAllocMem will do so when needed
-        arc = DosAllocSharedMem((PVOID)&pvPool, NULL, CLIENT_MEMORYPOOL_SIZE,
-                                PAG_READ | PAG_WRITE | OBJ_GIVEABLE);
-        if (arc == NO_ERROR)
-            arc = DosSubSetMem(pvPool,
-                               DOSSUB_INIT | DOSSUB_SPARSE_OBJ,
-                               CLIENT_MEMORYPOOL_SIZE);
-        if (!__atomic_cmpxchg32((volatile uint32_t *)&G_pvMemoryPool,
-                                (uint32_t)pvPool, (uint32_t)NULL))
-        {
-            // another thread has already got an entry, discard our try
-            if (pvPool)
-                DosFreeMem(pvPool);
-        }
-        else
-        {
-            // we could fail to allocate while being the first... give up
-            if (arc != NO_ERROR)
-                return NULL;
-        }
-    }
-
-    arc = DosSubAllocMem(G_pvMemoryPool, (PVOID)&pData, sizeof(*pData));
-    if (arc != NO_ERROR)
-        return NULL;
-
-    return pData;
-}
-
-static VOID FreeSysTrayCtlDataPtr(PSYSTRAYCTLDATA pData)
-{
-    DosSubFreeMem(G_pvMemoryPool, pData, sizeof(*pData));
-}
-
-/*
- *@@ InputHook:
- *      This is used to intercept posted WM_XST_NOTIFY messages and apply
- *      special processing to them (compose a client window-specific
- *      notification message, free the NOTIFYDATA structure and post the
- *      composed message to the target window).
- */
-
-static BOOL EXPENTRY InputHook(HAB hab, PQMSG pQmsg, ULONG fs)
-{
-    if (pQmsg->msg == WM_XST_NOTIFY)
-    {
-        PNOTIFYDATA pNotifyData = (PNOTIFYDATA)pQmsg->mp1;
-        PVOID pvMemoryPool = (PVOID)pQmsg->mp2;
-
-        // copy NOTIFYDATA and free it
-        NOTIFYDATA NotifyData = *pNotifyData;
-        FreeNotifyDataPtr(pvMemoryPool, pQmsg->hwnd, pNotifyData);
-
-        // start with a copy of the message and change the fields we need
-        QMSG newMsg = *pQmsg;
-        newMsg.msg = NotifyData.msg;
-        newMsg.mp1 = NotifyData.mp1;
-        newMsg.mp2 = NotifyData.mp2;
-
-        // deliver the message
-        WinDispatchMsg(hab, &newMsg);
-
-        return TRUE;
-    }
-
-    return FALSE;
-}
-
-/*
- *@@ xstQuerySysTrayVersion:
- *
- *      Returns the version of the Extended system tray in the variables pointed
- *      to by arguments. Any argument may be NULL in which case the
- *      corresponding component of the version is not returned.
- *
- *      Returns TRUE on success and FALSE if the Extended system tray is not
- *      installed or not operational.
- *
- *      NOTE: When the Extended system tray is started up or gets enabled after
- *      being temporarily disabled, it sends a message with the ID returned by
- *      xstGetSysTrayCreatedMsgId() to all top-level WC_FRAME windows on the
- *      desktop to let them add tray icons if they need.
- */
-
-BOOL xstQuerySysTrayVersion(PULONG pulMajor,    // out: major version number
-                            PULONG pulMinor,    // out: minor version number
-                            PULONG pulRevision) // out: revision number
-{
-    BOOL brc;
-    PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
-    if (!pData)
-        return FALSE;
-
-    pData->ulCommand = SYSTRAYCMD_GETVERSION;
-    pData->hwndSender = NULLHANDLE;
-
-    brc = SendSysTrayCtlMsg(pData) == XST_OK;
-    if (brc)
-    {
-        if (pulMajor)
-            *pulMajor = pData->u.version.ulMajor;
-        if (pulMinor)
-            *pulMinor = pData->u.version.ulMinor;
-        if (pulRevision)
-            *pulRevision = pData->u.version.ulRevision;
-    }
-
-    FreeSysTrayCtlDataPtr(pData);
-
-    return brc;
-}
-
-/*
- *@@ xstAddSysTrayIcon:
- *
- *      Adds an icon for the given window handle to the system tray. The icon ID
- *      is used to distinguish between several icons for the same window handle.
- *      If the icon with the specified ID already exists in the system tray, it
- *      will be replaced.
- *
- *      Returns TRUE on success and FALSE otherwise.
- *
- *      The specified window handle receives notification messages about icon
- *      events using the message ID specified by the ulMsgId parameter. The
- *      layout of the message parameters is as follows:
- *
- *          param1
- *              USHORT  usIconID        icon ID
- *              USHORT  usNotifyCode    notify code, one of XST_IN_ constants
- *
- *          param2
- *              PVOID   pData           notify code specific data (see below)
- *
- *      The following notify codes are currently recognized:
- *
- *          XST_IN_MOUSE:
- *              Mouse event in the icon area. Currently, only mouse click
- *              messages are recognized. param2 is a pointer to the XSTMOUSEMSG
- *              structure containing full mouse message details.
- *
- *          XST_IN_CONTEXT:
- *              Context menu event in the icon area. param2 is a pointer to the
- *              XSTCONTEXTMSG structure containing full message details.
- *
- *          XST_IN_WHEEL:
- *              Mouse wheel event in the icon area. param2 is a pointer to the
- *              XSTWHEELTMSG structure containing full message details.
- *
- *      NOTE: The maximum tooltip text length (including terminating null) is
- *      limited to a value returned by xstGetSysTrayMaxTextLen(). If the
- *      supplied string is longer, it will be truncated.
- */
-
-BOOL xstAddSysTrayIcon(HWND hwnd,       // in: window handle associated with the icon
-                       USHORT usId,     // in: icon ID to add
-                       HPOINTER hIcon,  // in: icon handle
-                       PCSZ pcszToolTip,// in: tooltip text
-                       ULONG ulMsgId,   // in: message ID for notifications
-                       ULONG ulFlags)   // in: flags (not currently used, must be 0)
-{
-    BOOL    brc;
-    ULONG   xrc = XST_FAIL;
-    PPIB    ppib;
-    HAB     hab;
-    HMQ     hmq;
-    size_t  i;
-
-    PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
-    if (!pData)
-        return FALSE;
-
-    if (WM_XST_NOTIFY == 0)
-        WM_XST_NOTIFY = WinAddAtom(WinQuerySystemAtomTable(),
-                                   WM_XST_NOTIFY_ATOM);
-
-    hab = WinQueryAnchorBlock(hwnd);
-    hmq = WinQueryWindowULong(hwnd, QWL_HMQ);
-    if (hmq == NULLHANDLE)
-        return FALSE;
-
-    // initialize the HMQ refs array
-    // @todo remove _smutex usage when we get into the DLL and initialize
-    // _fmutex + array in the DLL init routine
-    _smutex_request(&G_smtx);
-    if (!G_pHmqRefs)
-    {
-        if (_fmutex_create(&G_fmtx, 0))
-            return FALSE;
-        G_pHmqRefs = malloc(sizeof(*G_pHmqRefs) * HMQREFS_GROW);
-        if (!G_pHmqRefs)
-            return FALSE;
-        G_cHmqRefs = 0;
-        G_cHmqRefsMax = HMQREFS_GROW;
-    }
-    _smutex_release(&G_smtx);
-
-    // give all processes temporary access to hIcon
-    brc = WinSetPointerOwner(hIcon, 0, FALSE);
-    if (brc)
-    {
-        pData->ulCommand = SYSTRAYCMD_ADDICON;
-        pData->hwndSender = hwnd;
-
-        pData->u.icon.usId = usId;
-        pData->u.icon.hIcon = hIcon;
-        pData->u.icon.ulMsgId = ulMsgId;
-
-        if (!pcszToolTip)
-            pData->u.icon.szToolTip[0] = '\0';
-        else
-        {
-            strncpy(pData->u.icon.szToolTip, pcszToolTip,
-                    sizeof(pData->u.icon.szToolTip) - 1);
-            // be on the safe side
-            pData->u.icon.szToolTip[sizeof(pData->u.icon.szToolTip) - 1] = '\0';
-        }
-
-        xrc = SendSysTrayCtlMsg(pData);
-        brc = xrc == XST_OK || xrc == XST_REPLACED;
-
-        // revoke temporary access to hIcon
-        DosGetInfoBlocks(NULL, &ppib);
-        WinSetPointerOwner(hIcon, ppib->pib_ulpid, TRUE);
-    }
-
-    FreeSysTrayCtlDataPtr(pData);
-
-    if (xrc == XST_OK)
-    {
-        // install the message hook for the new icon to intercept WM_XST_NOTIFY
-        // messages or increase the reference count if already done so
-        brc = FALSE;
-        _fmutex_request(&G_fmtx, _FMR_IGNINT);
-        do
-        {
-            for (i = 0; i < G_cHmqRefs; ++i)
-                if (G_pHmqRefs[i].hmq == hmq)
-                    break;
-            if (i < G_cHmqRefs)
-                ++G_pHmqRefs[i].cRefs;
-            else
-            {
-                if (i == G_cHmqRefsMax)
-                {
-                    PHMQREFS pNewRefs = realloc(G_pHmqRefs,
-                                                sizeof(*G_pHmqRefs) *
-                                                (G_cHmqRefsMax + HMQREFS_GROW));
-                    if (!pNewRefs)
-                        break;
-                    G_pHmqRefs = pNewRefs;
-                    G_cHmqRefsMax += HMQREFS_GROW;
-                }
-                brc = WinSetHook(hab, hmq, HK_INPUT, (PFN)InputHook, NULLHANDLE);
-                if (!brc)
-                    break;
-                ++G_cHmqRefs;
-                G_pHmqRefs[i].hmq = hmq;
-                G_pHmqRefs[i].cRefs = 1;
-            }
-            brc = TRUE;
-        }
-        while (0);
-        _fmutex_release(&G_fmtx);
-
-        if (!brc)
-            xstRemoveSysTrayIcon(hwnd, usId);
-    }
-
-    return brc;
-}
-
-/*
- *@@ xstReplaceSysTrayIcon:
- *
- *      Replaces the existing icon previously added by xstAddSysTrayIcon() with
- *      a new icon.
- *
- *      Returns TRUE on success and FALSE otherwise.
- */
-
-BOOL xstReplaceSysTrayIcon(HWND hwnd,        // in: window handle associated with the icon
-                           USHORT usId,      // in: icon ID to change
-                           HPOINTER hIcon)   // in: new icon handle
-{
-    BOOL    brc;
-    PPIB    ppib;
-
-    PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
-    if (!pData)
-        return FALSE;
-
-    // give all processes temporary access to hIcon
-    brc = WinSetPointerOwner(hIcon, 0, FALSE);
-    if (brc)
-    {
-        pData->ulCommand = SYSTRAYCMD_REPLACEICON;
-        pData->hwndSender = hwnd;
-
-        pData->u.icon.usId = usId;
-        pData->u.icon.hIcon = hIcon;
-
-        brc = SendSysTrayCtlMsg(pData) == XST_OK;
-
-        // revoke temporary access to hIcon
-        DosGetInfoBlocks(NULL, &ppib);
-        WinSetPointerOwner(hIcon, ppib->pib_ulpid, TRUE);
-    }
-
-    FreeSysTrayCtlDataPtr(pData);
-
-    return brc;
-}
-
-/*
- *@@ xstRemoveSysTrayIcon:
- *
- *      Removes the icon previously added by xstAddSysTrayIcon() from the system
- *      tray.
- *
- *      Returns TRUE on success and FALSE otherwise.
- */
-
-BOOL xstRemoveSysTrayIcon(HWND hwnd,    // in: window handle associated with the icon
-                          USHORT usId)  // in: icon ID to remove
-{
-    BOOL brc;
-    HAB     hab;
-    HMQ     hmq;
-    size_t  i;
-
-    PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
-    if (!pData)
-        return FALSE;
-
-    hab = WinQueryAnchorBlock(hwnd);
-    hmq = WinQueryWindowULong(hwnd, QWL_HMQ);
-    if (hmq == NULLHANDLE)
-        return FALSE;
-
-    pData->ulCommand = SYSTRAYCMD_REMOVEICON;
-    pData->hwndSender = hwnd;
-    pData->u.icon.usId = usId;
-
-    brc = SendSysTrayCtlMsg(pData) == XST_OK;
-
-    FreeSysTrayCtlDataPtr(pData);
-
-    if (brc)
-    {
-        // remove the message hook if it's the last reference to the HMQ
-        _fmutex_request(&G_fmtx, _FMR_IGNINT);
-        do
-        {
-            for (i = 0; i < G_cHmqRefs; ++i)
-                if (G_pHmqRefs[i].hmq == hmq)
-                    break;
-            if (i == G_cHmqRefs)
-                // unknown HMQ??
-                break;
-
-            if (--G_pHmqRefs[i].cRefs == 0)
-                WinReleaseHook(hab, hmq, HK_INPUT, (PFN)InputHook, NULLHANDLE);
-        }
-        while (0);
-        _fmutex_release(&G_fmtx);
-    }
-
-    return brc;
-}
-
-/*
- *@@ xstSetSysTrayIconToolTip:
- *
- *      Sets the tooltip text for the given icon in the system tray. This text
- *      is shown when the mouse pointer is held still over the icon area for
- *      some time.
- *
- *      If pszText is NULL, the tooltip text is reset and will not be shown next
- *      time. The old tooltip is hidden if it is being currently shown.
- *
- *      Returns TRUE on success and FALSE otherwise.
- *
- *      NOTE: The maximum tooltip text length (including terminating null) is
- *      limited to a value returned by xstGetSysTrayMaxTextLen(). If the
- *      supplied string is longer, it will be truncated.
- */
-
-BOOL xstSetSysTrayIconToolTip(HWND hwnd,        // in: window handle associated with the icon
-                              USHORT usId,      // in: icon ID to set the tooltip for
-                              PCSZ pcszToolTip) // in: tooltip text
-{
-    BOOL brc;
-    PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
-    if (!pData)
-        return FALSE;
-
-    pData->ulCommand = SYSTRAYCMD_SETTOOLTIP;
-    pData->hwndSender = hwnd;
-    pData->u.icon.usId = usId;
-
-    if (!pcszToolTip)
-        pData->u.icon.szToolTip[0] = '\0';
-    else
-    {
-        strncpy(pData->u.icon.szToolTip, pcszToolTip,
-                sizeof(pData->u.icon.szToolTip) - 1);
-        // be on the safe side
-        pData->u.icon.szToolTip[sizeof(pData->u.icon.szToolTip) - 1] = '\0';
-    }
-
-    brc = SendSysTrayCtlMsg(pData) == XST_OK;
-
-    FreeSysTrayCtlDataPtr(pData);
-
-    return brc;
-}
-
-BOOL xstShowSysTrayIconBalloon(HWND hwnd, USHORT usId, PCSZ pcszTitle,
-                               PCSZ pcszText, ULONG ulFlags, ULONG ulTimeout)
-{
-    // @todo implement
-    return FALSE;
-}
-
-BOOL xstHideSysTrayIconBalloon(HWND hwnd, USHORT usId)
-{
-    // @todo implement
-    return FALSE;
-}
-
-/*
- *@@ xstQuerySysTrayIconRect:
- *
- *      Obtains a rectangle occupied by the given icon (in screen coordinates,
- *      top right corner exclusive).
- *
- *      Returns TRUE on success and FALSE otherwise.
- */
-BOOL xstQuerySysTrayIconRect(HWND hwnd, USHORT usId, PRECTL prclRect)
-{
-    BOOL brc;
-    PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
-    if (!pData)
-        return FALSE;
-
-    pData->ulCommand = SYSTRAYCMD_QUERYRECT;
-    pData->hwndSender = hwnd;
-    pData->u.icon.usId = usId;
-
-    brc = SendSysTrayCtlMsg(pData) == XST_OK;
-    if (brc)
-    {
-        *prclRect = pData->u.rect.rclIcon;
-    }
-
-    FreeSysTrayCtlDataPtr(pData);
-
-    return brc;
-}
-
-/*
- *@@ xstGetSysTrayCreatedMsgId:
- *
- *      Returns the ID of the message that is sent by the Extended system tray
- *      to all top-level WC_FRAME windows on the desktop to let them add tray
- *      icons if they need.
- *
- *      NOTE: The returned value never changes until reboot so it is a good
- *      practice to cache it instead of calling this function each time from the
- *      window procedure of every involved window.
- */
-
-ULONG xstGetSysTrayCreatedMsgId()
-{
-    if (WM_XST_CREATED == 0)
-        WM_XST_CREATED = WinAddAtom(WinQuerySystemAtomTable(),
-                                    WM_XST_CREATED_ATOM);
-    return WM_XST_CREATED;
-}
-
-/*
- *@@ xstGetSysTrayMaxTextLen:
- *
- *      Returns the maximum length of the text (in bytes, including the
- *      terminating null) that can be shown in the tooltop of the icon in the
- *      system tray. You can use the returned value to determine the maximum
- *      length of the string passed as pszText to xstSetSysTrayIconToolTip().
- *
- *      The returned value also defines the maximum length of both the title and
- *      the text (including terminating nulls) of the icon's balloon for the
- *      xstShowSysTrayIconBalloon() call.
- *
- *      Returns TRUE on success and FALSE otherwise.
- *
- *      NOTE: The returned value never changes until reboot so it is a good
- *      practice to cache it instead of calling this function each time.
- */
-
-ULONG xstGetSysTrayMaxTextLen()
-{
-    return sizeof(((PSYSTRAYCTLDATA)0)->u.icon.szToolTip);
-}
-
Index: unk/src/3rdparty/os2/xsystray/xsystray_api.h
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/xsystray_api.h	(revision 834)
+++ 	(revision )
@@ -1,98 +1,0 @@
-
-/*
- *@@sourcefile xsystray_api.h:
- *      Extended system tray widget for XCenter/eCenter.
- *
- *      Public API.
- *
- *      This file contains the public API to the Extended system tray widget.
- *      This API is used by applications to add and remove icons from the
- *      special notification area on the desktop provided by the Extended
- *      system tray widget.
- *
- *      Refer to the "API" file for more details about the public API.
- *
- *      Copyright (C) 2009 Dmitry A. Kuminov
- *
- *      This file is part of the Extended system tray widget source package.
- *      Extended system tray widget is free software; you can redistribute it
- *      and/or modify it under the terms of the GNU General Public License as
- *      published by the Free Software Foundation, in version 2 as it comes in
- *      the "COPYING" file of the Extended system tray widget distribution. This
- *      program is distributed in the hope that it will be useful, but WITHOUT
- *      ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- *      FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- *      more details.
- */
-
-#ifndef XSYSTRAY_API_HEADER_INCLUDED
-#define XSYSTRAY_API_HEADER_INCLUDED
-
-#if __cplusplus
-extern "C" {
-#endif
-
-// notification code constants for the notification messages sent by the system
-// tray (refer to xstAddSysTrayIcon() for details)
-#define XST_IN_MOUSE    0x0001
-#define XST_IN_CONTEXT  0x0002
-#define XST_IN_WHEEL    0x0003
-
-// structure for XST_IN_MOUSE
-typedef struct
-{
-    ULONG  ulMouseMsg;
-           // mouse message (one of WM_BUTTONxyyy)
-    POINTS ptsPointerPos;
-           // global pointer position at the time of the mouse event
-    USHORT fsHitTestRes;
-           // hit-test result (see WM_BUTTONxyyy description in PM)
-    USHORT fsFlags;
-           // keyboard control codes (see WM_BUTTONxyyy description in PM)
-
-} XSTMOUSEMSG, *PXSTMOUSEMSG;
-
-// structure for XST_IN_CONTEXT
-typedef struct
-{
-    POINTS ptsPointerPos;
-           // global pointer position at the time of the mouse event
-    USHORT fPointer;
-           // input device flag (see WM_CONTEXTMENU description in PM)
-
-} XSTCONTEXTMSG, *PXSTCONTEXTMSG;
-
-// structure for XST_IN_WHEEL
-typedef struct
-{
-    ULONG  ulWheelMsg;
-           // mouse message (one of WM_HSCROLL or WM_VSCROLL)
-    POINTS ptsPointerPos;
-           // global pointer position at the time of the mouse event
-    USHORT usCmd;
-           // command (see WM_HSCROLL/WM_VSCROLL description in PM)
-
-} XSTWHEELMSG, *PXSTWHEELMSG;
-
-BOOL xstQuerySysTrayVersion(PULONG pulMajor, PULONG pulMinor, PULONG pulRevision);
-BOOL xstAddSysTrayIcon(HWND hwnd, USHORT usId, HPOINTER hIcon, PCSZ pcszToolTip,
-                       ULONG ulMsgId, ULONG ulFlags);
-BOOL xstReplaceSysTrayIcon(HWND hwnd, USHORT usId, HPOINTER hIcon);
-BOOL xstRemoveSysTrayIcon(HWND hwnd, USHORT usId);
-BOOL xstSetSysTrayIconToolTip(HWND hwnd, USHORT usId, PCSZ pcszToolTip);
-BOOL xstShowSysTrayIconBalloon(HWND hwnd, USHORT usId, PCSZ pcszTitle,
-                               PCSZ pcszText, ULONG ulFlags, ULONG ulTimeout);
-BOOL xstHideSysTrayIconBalloon(HWND hwnd, USHORT usId);
-
-BOOL xstQuerySysTrayIconRect(HWND hwnd, USHORT usId, PRECTL prclRect);
-
-ULONG xstGetSysTrayCreatedMsgId();
-
-ULONG xstGetSysTrayMaxTextLen();
-
-#if __cplusplus
-}
-#endif
-
-#endif // XSYSTRAY_API_HEADER_INCLUDED
-
Index: /trunk/src/gui/util/qsystemtrayicon_pm.cpp
===================================================================
--- /trunk/src/gui/util/qsystemtrayicon_pm.cpp	(revision 834)
+++ /trunk/src/gui/util/qsystemtrayicon_pm.cpp	(revision 835)
@@ -48,7 +48,18 @@
 #include "qapplication.h"
 #include "qdesktopwidget.h"
+#include "qlibrary.h"
 #include "qt_os2.h"
 
-#include "xsystray_api.h"
+// for dynamic linking to xsystray DLL
+#define xstQuerySysTrayVersion (*_xstQuerySysTrayVersion)
+#define xstAddSysTrayIcon (*_xstAddSysTrayIcon)
+#define xstReplaceSysTrayIcon (*_xstReplaceSysTrayIcon)
+#define xstRemoveSysTrayIcon (*_xstRemoveSysTrayIcon)
+#define xstSetSysTrayIconToolTip (*_xstSetSysTrayIconToolTip)
+#define xstQuerySysTrayIconRect (*_xstQuerySysTrayIconRect)
+#define xstGetSysTrayCreatedMsgId (*_xstGetSysTrayCreatedMsgId)
+#define xstGetSysTrayMaxTextLen (*_xstGetSysTrayMaxTextLen)
+
+#include "xsystray.h"
 
 QT_BEGIN_NAMESPACE
@@ -56,6 +67,6 @@
 #define WM_XST_MYNOTIFY     (WM_USER + 1000)
 
-static ULONG WM_XST_CREATED = xstGetSysTrayCreatedMsgId();
-static ULONG MaxTextLen = xstGetSysTrayMaxTextLen();
+static ULONG WM_XST_CREATED = 0;
+static ULONG MaxTextLen = 0;
 
 class QSystemTrayIconSys : QWidget
@@ -307,4 +318,29 @@
 bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys()
 {
+    static bool tried = false;
+    if (!tried) {
+        tried = true;
+
+        // link to the xsystray DLL at runtime
+        QLibrary xsystray(QLatin1String("xsystray"));
+
+        #define R(f) if ((*((void **)&_##f) = xsystray.resolve(#f)) == NULL) return false
+
+        R(xstQuerySysTrayVersion);
+        R(xstAddSysTrayIcon);
+        R(xstReplaceSysTrayIcon);
+        R(xstRemoveSysTrayIcon);
+        R(xstSetSysTrayIconToolTip);
+        R(xstQuerySysTrayIconRect);
+        R(xstGetSysTrayCreatedMsgId);
+        R(xstGetSysTrayMaxTextLen);
+
+        #undef R
+
+        // initialize some constants
+        WM_XST_CREATED = xstGetSysTrayCreatedMsgId();
+        MaxTextLen = xstGetSysTrayMaxTextLen();
+    }
+
     return xstQuerySysTrayVersion(0, 0, 0);
 }
Index: /trunk/src/gui/util/util.pri
===================================================================
--- /trunk/src/gui/util/util.pri	(revision 834)
+++ /trunk/src/gui/util/util.pri	(revision 835)
@@ -31,8 +31,9 @@
 os2 {
 		SOURCES += \
-				util/qsystemtrayicon_pm.cpp \
-				../3rdparty/os2/xsystray/xsystray_api.c
+				util/qsystemtrayicon_pm.cpp
+		# @todo this should be required to present extenrally in the
+		# environment when it gets moved to XWP (see #99 for details)
 		INCLUDEPATH += \
-				../3rdparty/os2/xsystray
+				../3rdparty/os2/xsystray/apilib
 }
 
