Index: /trunk/src/3rdparty/os2/xsystray/xsystray.c
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/xsystray.c	(revision 264)
+++ /trunk/src/3rdparty/os2/xsystray/xsystray.c	(revision 265)
@@ -76,5 +76,5 @@
 
 // primitive debug logging to a file
-#if 1
+#if 0
 static void __LOG_WORKER(const char *fmt, ...)
 {
@@ -133,7 +133,12 @@
 typedef struct
 {
+    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
-                // (left to right)
     size_t      cIcons;
                 // number of icons in the pIcons array
@@ -143,5 +148,11 @@
 } SYSTRAYDATA, *PSYSTRAYDATA;
 
-ULONG QWL_USER_SERVER_DATA = 0;
+static ULONG QWL_USER_SERVER_DATA = 0;
+             // offset to the PXCENTERWIDGET pointer in the widget data array
+
+#define TID_CHECKALIVE          1
+         // check alive timer
+#define TID_CHECKALIVE_TIMEOUT  1000 // ms
+         // how often to check if windows associated with icons are still alive
 
 /* ******************************************************************
@@ -240,4 +251,28 @@
 /* ******************************************************************
  *
+ *   Helper methods
+ *
+ ********************************************************************/
+
+VOID FreeIconData(PICONDATA pData)
+{
+    pData->hwnd = NULLHANDLE;
+    pData->ulId = 0;
+    if (pData->hIcon != NULLHANDLE)
+    {
+        WinDestroyPointer(pData->hIcon);
+        pData->hIcon = NULLHANDLE;
+
+    }
+    pData->ulMsgId = 0;
+    if (pData->pszToolTip)
+    {
+        free(pData->pszToolTip);
+        pData->pszToolTip = NULL;
+    }
+}
+
+/* ******************************************************************
+ *
  *   PM window class implementation
  *
@@ -265,4 +300,5 @@
                 MPARAM mp2)
 {
+    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
     BOOL brc = FALSE;
 
@@ -285,6 +321,7 @@
             {
                 PSIZEL pszl = (PSIZEL)mp2;
-                pszl->cx = 30;      // desired width
-                pszl->cy = 20;      // desired minimum height
+                LONG pad2 = pSysTrayData->lIconPad * 2;
+                pszl->cx = (pSysTrayData->lIconWidth + pad2) * pSysTrayData->cIcons; // desired width
+                pszl->cy = pSysTrayData->lIconHeight + pad2; // desired minimum height
                 brc = TRUE;
             }
@@ -297,4 +334,14 @@
 }
 
+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:
+    // 1) for icons w/o real alpha, draw them manually by correctly selecting
+    //    the normal or mini HBITMAP (WinDrawPointer fails to do so);
+    // 2) for icons with real alpha, do manual alpha blending
+}
+
 /*
  *@@ WgtPaint:
@@ -309,28 +356,75 @@
               PXCENTERWIDGET pWidget)
 {
+    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
     HPS hps;
-    if ((hps = WinBeginPaint(hwnd, NULLHANDLE, NULL)))
-    {
-        RECTL   rclWin;
+    RECTL rclPaint;
+
+    if ((hps = WinBeginPaint(hwnd, NULLHANDLE, &rclPaint)))
+    {
+        SWP     swp;
+        RECTL   rcl;
+        BOOL    bLeftToRight;
+        LONG    x, lTotalWidth;
+        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.xRight, rclPaint.yBottom, rclPaint.yTop));
 
         // switch HPS to RGB mode
         GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
 
-        // now paint
-        WinQueryWindowRect(hwnd,
-                           &rclWin);        // exclusive
-
-        WinFillRect(hps,
-                    &rclWin,                // exclusive
+        // 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));
 
-        // print question mark
-        WinDrawText(hps,
-                    1,
-                    "?",
-                    &rclWin,                // exclusive
-                    WinQuerySysColor(HWND_DESKTOP, SYSCLR_WINDOWSTATICTEXT, 0),
-                    WinQuerySysColor(HWND_DESKTOP, SYSCLR_DIALOGBACKGROUND, 0),
-                    DT_CENTER | DT_VCENTER);
+        if (bLeftToRight)
+            x = pSysTrayData->lIconPad;
+        else
+            x = swp.cx - pSysTrayData->lIconPad - pSysTrayData->lIconWidth;
+
+        lTotalWidth = pSysTrayData->lIconWidth + pSysTrayData->lIconPad * 2;
+
+        // where to start from?
+        if (bLeftToRight)
+        {
+            i = rclPaint.xLeft / lTotalWidth;
+            x = pSysTrayData->lIconPad + i * lTotalWidth;
+        }
+        else
+        {
+            i = (swp.cx - rclPaint.xRight) / lTotalWidth;
+            x = swp.cx - (i + 1) * lTotalWidth + pSysTrayData->lIconPad;
+        }
+
+        // draw as many icons as we can / need
+        for (i = 0; i < pSysTrayData->cIcons; ++i)
+        {
+            if (x >= rclPaint.xRight)
+                break;
+
+            DrawPointer(hps, x, pSysTrayData->lIconPad,
+                        pSysTrayData->pIcons[i].hIcon, DP_MINI);
+            if (bLeftToRight)
+                x += lTotalWidth;
+            else
+                x -= lTotalWidth;
+        }
 
         WinEndPaint(hps);
@@ -394,4 +488,7 @@
 
             // initialize the SYSTRAYDATA structure
+            pSysTrayData->lIconWidth = WinQuerySysValue(HWND_DESKTOP, SV_CXICON) / 2;
+            pSysTrayData->lIconHeight = WinQuerySysValue(HWND_DESKTOP, SV_CYICON) / 2;
+            pSysTrayData->lIconPad = pSysTrayData->lIconHeight / 8;
             pSysTrayData->cIconsMax = 4;
             pSysTrayData->pIcons = malloc(sizeof(*pSysTrayData->pIcons) *
@@ -459,8 +556,5 @@
             size_t i;
             for (i = 0; i < pSysTrayData->cIcons; ++i)
-            {
-                if (pSysTrayData->pIcons[i].pszToolTip)
-                    free(pSysTrayData->pIcons[i].pszToolTip);
-            }
+                FreeIconData(&pSysTrayData->pIcons[i]);
             free(pSysTrayData->pIcons);
             free(pSysTrayData);
@@ -484,6 +578,6 @@
  *      implementation for WM_XST_CONTROL in fnwpXSysTrayServer.
  *
- *      Serves as an entry point for all client-side requests to the Extended
- *      system tray.
+ *      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
@@ -497,4 +591,5 @@
 {
     BOOL brc = FALSE;
+    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
 
     switch (pCtlData->ulCommand)
@@ -512,9 +607,125 @@
         break;
 
+        case SYSTRAYCMD_ADDICON:
+        {
+            POINTERINFO Info;
+            HPOINTER hIcon = NULLHANDLE;
+
+            LOGF(("SYSTRAYCMD_ADDICON\n"));
+            LOGF(("hIcon %x\n", pCtlData->u.icon.hIcon));
+
+            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)
+            {
+                brc = FALSE;
+                break;
+            }
+
+            // @todo just testing....
+            if (pSysTrayData->pIcons[0].hIcon != NULLHANDLE)
+                WinDestroyPointer(pSysTrayData->pIcons[0].hIcon);
+            pSysTrayData->cIcons = 1;
+            pSysTrayData->pIcons[0].hwnd = pCtlData->hwndSender;
+            pSysTrayData->pIcons[0].ulId = pCtlData->u.icon.ulId;
+            pSysTrayData->pIcons[0].hIcon = hIcon;
+            pSysTrayData->pIcons[0].ulMsgId = pCtlData->u.icon.ulMsgId;
+
+            // ask XCenter to take our new size into account
+            // (this will also invalidate us)
+            WinPostMsg(pWidget->pGlobals->hwndClient,
+                       XCM_REFORMAT,
+                       (MPARAM)XFMF_GETWIDGETSIZES,
+                       0);
+
+            if (pSysTrayData->cIcons == 1)
+            {
+                // start a timer to perform "is window alive" checks
+                WinStartTimer(pWidget->habWidget, hwnd,
+                              TID_CHECKALIVE,
+                              TID_CHECKALIVE_TIMEOUT);
+            }
+
+            brc = TRUE;
+        }
+
         default:
             break;
     }
 
+    LOGF(("return %d (WinGetLastError is %x)\n",
+          brc, WinGetLastError(pWidget->habWidget)));
+
     return brc;
+}
+
+/*
+ *@@ WgtXSysTrayTimer:
+ *      implementation for WM_TIMER in fnwpXSysTrayServer.
+ */
+
+static
+VOID WgtXSysTrayTimer(HWND hwnd, PXCENTERWIDGET pWidget,
+                      USHORT usTimerId)
+{
+    PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
+
+    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))
+            {
+                bAnyDead = TRUE;
+                FreeIconData(&pSysTrayData->pIcons[i]);
+                // 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;
+            }
+
+            if (pSysTrayData->cIcons == 0)
+            {
+                // stop the check alive timer
+                WinStopTimer(pWidget->habWidget, hwnd,
+                              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);
+        }
+    }
 }
 
@@ -556,4 +767,13 @@
             mrc = (MRESULT)WgtXSysTrayControl(hwnd, pWidget,
                                               (PSYSTRAYCTLDATA)mp1);
+        break;
+
+        /*
+         * WM_TIMER:
+         *      timer event.
+         */
+
+        case WM_TIMER:
+            WgtXSysTrayTimer(hwnd, pWidget, SHORT1FROMMP(mp1));
         break;
 
Index: /trunk/src/3rdparty/os2/xsystray/xsystray_api.c
===================================================================
--- /trunk/src/3rdparty/os2/xsystray/xsystray_api.c	(revision 264)
+++ /trunk/src/3rdparty/os2/xsystray/xsystray_api.c	(revision 265)
@@ -15,6 +15,8 @@
 
 #define INCL_DOSERRORS
+#define INCL_DOSPROCESS
 #define INCL_WINWINDOWMGR
 #define INCL_WINATOM
+#define INCL_WINPOINTERS
 #include <os2.h>
 
@@ -98,4 +100,8 @@
 }
 
+// This function returns a per-thread SYSTRAYCTLDATA pointer. We communicate
+// to the server thread using WinSendMsg() which allows us to reuse a single
+// memory block for all calls (WinSendMsg() doesn't return until the server is
+// done with processing the message).
 static PSYSTRAYCTLDATA GetSysTrayCtlDataPtr()
 {
@@ -105,7 +111,7 @@
     if (G_itlsSysTrayCtlData == -1)
     {
-        // @todo does XWorkplace have its own TLS? Not? Use
-        // DosAllocThreadLocalMemory() directly then (though it's not nice due
-        // to the lack of space in that area)
+        // @todo does XWorkplace have its own TLS? Or is it built with GCC? Not?
+        // Use DosAllocThreadLocalMemory() directly then (though it's not nice
+        // due to the too limited amount of memory space in that area)
         int itls = __libc_TLSAlloc();
         if (!__atomic_cmpxchg32(&G_itlsSysTrayCtlData, itls, -1))
@@ -159,4 +165,5 @@
                             PULONG pulRevision) // out: revision number
 {
+    BOOL brc;
     PSYSTRAYCTLDATA pData = GetSysTrayCtlDataPtr();
     if (!pData)
@@ -166,5 +173,5 @@
     pData->hwndSender = NULLHANDLE;
 
-    BOOL brc = SendSysTrayCtlMsg(pData);
+    brc = SendSysTrayCtlMsg(pData);
     if (brc)
     {
@@ -215,6 +222,13 @@
                        ULONG ulFlags)   // in: flags (not currently used, must be 0)
 {
+    BOOL brc;
+    PPIB ppib;
     PSYSTRAYCTLDATA pData = GetSysTrayCtlDataPtr();
     if (!pData)
+        return FALSE;
+
+    // give all processes temporary access to hIcon
+    brc = WinSetPointerOwner(hIcon, 0, FALSE);
+    if (!brc)
         return FALSE;
 
@@ -225,5 +239,11 @@
     pData->u.icon.ulMsgId = ulMsgId;
 
-    return SendSysTrayCtlMsg(pData);
+    brc = SendSysTrayCtlMsg(pData);
+
+    // revoke temporary access to hIcon
+    DosGetInfoBlocks(NULL, &ppib);
+    WinSetPointerOwner(hIcon, ppib->pib_ulpid, TRUE);
+
+    return brc;
 }
 
