| 1 | /*
|
|---|
| 2 | * Extended system tray widget for XCenter/eCenter
|
|---|
| 3 | *
|
|---|
| 4 | * Public API implementation
|
|---|
| 5 | *
|
|---|
| 6 | * Made by netlabs.org
|
|---|
| 7 | *
|
|---|
| 8 | * Author: Dmitry A. Kuminov
|
|---|
| 9 | *
|
|---|
| 10 | * This software is public domain.
|
|---|
| 11 | *
|
|---|
| 12 | * WITHOUT ANY WARRANTY..., AT YOUR OWN RISC... ETC.
|
|---|
| 13 | *
|
|---|
| 14 | */
|
|---|
| 15 |
|
|---|
| 16 | #define INCL_DOSERRORS
|
|---|
| 17 | #define INCL_DOSPROCESS
|
|---|
| 18 | #define INCL_WINWINDOWMGR
|
|---|
| 19 | #define INCL_WINATOM
|
|---|
| 20 | #define INCL_WINPOINTERS
|
|---|
| 21 | #include <os2.h>
|
|---|
| 22 |
|
|---|
| 23 | #include "xsystray_api.h"
|
|---|
| 24 | #include "xsystray.h"
|
|---|
| 25 |
|
|---|
| 26 | #include <string.h>
|
|---|
| 27 | #include <sys/builtin.h> // atomics
|
|---|
| 28 | #include <InnotekLIBC/thread.h> // TLS
|
|---|
| 29 |
|
|---|
| 30 | static HWND G_hwndSysTray = NULLHANDLE;
|
|---|
| 31 | static int G_itlsSysTrayCtlData = -1;
|
|---|
| 32 |
|
|---|
| 33 | static HWND FindSysTrayWindow()
|
|---|
| 34 | {
|
|---|
| 35 | char buf[sizeof(WNDCLASS_WIDGET_XSYSTRAY_SERVER) + 1];
|
|---|
| 36 | HWND hwnd;
|
|---|
| 37 | HENUM henum = WinBeginEnumWindows(HWND_DESKTOP);
|
|---|
| 38 | while ((hwnd = WinGetNextWindow(henum)) != NULLHANDLE)
|
|---|
| 39 | {
|
|---|
| 40 | LONG len = WinQueryClassName(hwnd, sizeof(buf), buf);
|
|---|
| 41 | buf[len] = '\0';
|
|---|
| 42 | if (strcmp(WNDCLASS_WIDGET_XSYSTRAY_SERVER, buf) == 0)
|
|---|
| 43 | break;
|
|---|
| 44 | }
|
|---|
| 45 | WinEndEnumWindows(henum);
|
|---|
| 46 |
|
|---|
| 47 | return hwnd;
|
|---|
| 48 | }
|
|---|
| 49 |
|
|---|
| 50 | static BOOL SendSysTrayCtlMsg(PSYSTRAYCTLDATA pData)
|
|---|
| 51 | {
|
|---|
| 52 | APIRET arc;
|
|---|
| 53 | PID pid;
|
|---|
| 54 | TID tid;
|
|---|
| 55 | MRESULT mrc;
|
|---|
| 56 |
|
|---|
| 57 | BOOL bTriedFind = FALSE;
|
|---|
| 58 |
|
|---|
| 59 | do
|
|---|
| 60 | {
|
|---|
| 61 | if (G_hwndSysTray == NULLHANDLE)
|
|---|
| 62 | {
|
|---|
| 63 | bTriedFind = TRUE;
|
|---|
| 64 | HWND hwnd = FindSysTrayWindow();
|
|---|
| 65 | __atomic_cmpxchg32((uint32_t *)&G_hwndSysTray, hwnd, NULLHANDLE);
|
|---|
| 66 | if (G_hwndSysTray == NULLHANDLE)
|
|---|
| 67 | break;
|
|---|
| 68 | }
|
|---|
| 69 |
|
|---|
| 70 | if (bTriedFind)
|
|---|
| 71 | {
|
|---|
| 72 | arc = ERROR_INVALID_HANDLE;
|
|---|
| 73 | if (WinQueryWindowProcess(G_hwndSysTray, &pid, &tid))
|
|---|
| 74 | arc = DosGiveSharedMem(__libc_TLSGet(G_itlsSysTrayCtlData),
|
|---|
| 75 | pid, PAG_READ | PAG_WRITE);
|
|---|
| 76 | if (arc != NO_ERROR)
|
|---|
| 77 | break;
|
|---|
| 78 | }
|
|---|
| 79 |
|
|---|
| 80 | pData->bAcknowledged = FALSE;
|
|---|
| 81 |
|
|---|
| 82 | mrc = WinSendMsg(G_hwndSysTray, WM_XST_CONTROL, pData, NULL);
|
|---|
| 83 | if (mrc == (MRESULT)TRUE && pData->bAcknowledged)
|
|---|
| 84 | return TRUE;
|
|---|
| 85 |
|
|---|
| 86 | // if we failed to send the message, it may mean that XCenter was restarted
|
|---|
| 87 | // or the system tray was re-enabled. Try to get a new handle (only if we
|
|---|
| 88 | // didn't already do it in this call)
|
|---|
| 89 | if (!bTriedFind)
|
|---|
| 90 | {
|
|---|
| 91 | G_hwndSysTray = NULLHANDLE;
|
|---|
| 92 | continue;
|
|---|
| 93 | }
|
|---|
| 94 |
|
|---|
| 95 | break;
|
|---|
| 96 | }
|
|---|
| 97 | while (TRUE);
|
|---|
| 98 |
|
|---|
| 99 | return FALSE;
|
|---|
| 100 | }
|
|---|
| 101 |
|
|---|
| 102 | // This function returns a per-thread SYSTRAYCTLDATA pointer. We communicate
|
|---|
| 103 | // to the server thread using WinSendMsg() which allows us to reuse a single
|
|---|
| 104 | // memory block for all calls (WinSendMsg() doesn't return until the server is
|
|---|
| 105 | // done with processing the message).
|
|---|
| 106 | static PSYSTRAYCTLDATA GetSysTrayCtlDataPtr()
|
|---|
| 107 | {
|
|---|
| 108 | APIRET arc;
|
|---|
| 109 |
|
|---|
| 110 | // allocate a thread local storage entry if not done so
|
|---|
| 111 | if (G_itlsSysTrayCtlData == -1)
|
|---|
| 112 | {
|
|---|
| 113 | // @todo does XWorkplace have its own TLS? Or is it built with GCC? Not?
|
|---|
| 114 | // Use DosAllocThreadLocalMemory() directly then (though it's not nice
|
|---|
| 115 | // due to the too limited amount of memory space in that area)
|
|---|
| 116 | int itls = __libc_TLSAlloc();
|
|---|
| 117 | if (!__atomic_cmpxchg32(&G_itlsSysTrayCtlData, itls, -1))
|
|---|
| 118 | {
|
|---|
| 119 | // another thread has already got an entry, discard our try
|
|---|
| 120 | if (itls != -1)
|
|---|
| 121 | __libc_TLSFree(itls);
|
|---|
| 122 | }
|
|---|
| 123 |
|
|---|
| 124 | if (G_itlsSysTrayCtlData == -1)
|
|---|
| 125 | return NULL;
|
|---|
| 126 | }
|
|---|
| 127 |
|
|---|
| 128 | // allocate a SYSTRAYCTLDATA struct for this thread if not done so
|
|---|
| 129 | PSYSTRAYCTLDATA pData = __libc_TLSGet(G_itlsSysTrayCtlData);
|
|---|
| 130 | if (!pData)
|
|---|
| 131 | {
|
|---|
| 132 | arc = DosAllocSharedMem((PVOID)&pData, NULL, sizeof(SYSTRAYCTLDATA),
|
|---|
| 133 | PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_GIVEABLE);
|
|---|
| 134 | if (arc != NO_ERROR)
|
|---|
| 135 | return NULL;
|
|---|
| 136 |
|
|---|
| 137 | __libc_TLSSet(G_itlsSysTrayCtlData, pData);
|
|---|
| 138 |
|
|---|
| 139 | // note that we don't ever free the allocated block since our API doesn't
|
|---|
| 140 | // have a concept of initialization/termination and therefore it's fine
|
|---|
| 141 | // if the memory stays allocated until application termination
|
|---|
| 142 | }
|
|---|
| 143 |
|
|---|
| 144 | return pData;
|
|---|
| 145 | }
|
|---|
| 146 |
|
|---|
| 147 | /*
|
|---|
| 148 | *@@ xstQuerySysTrayVersion:
|
|---|
| 149 | *
|
|---|
| 150 | * Returns the version of the Extended system tray in the variables pointed
|
|---|
| 151 | * to by arguments. Any argument may be NULL in which case the
|
|---|
| 152 | * corresponding component of the version is not returned.
|
|---|
| 153 | *
|
|---|
| 154 | * Returns TRUE on success and FALSE if the Extended system tray is not
|
|---|
| 155 | * installed or not operational.
|
|---|
| 156 | *
|
|---|
| 157 | * NOTE: When the Extended system tray is started up or gets enabled after
|
|---|
| 158 | * being temporarily disabled, it sends a message with the ID returned by
|
|---|
| 159 | * xstGetSysTrayCreatedMsgId() to all top-level WC_FRAME windows on the
|
|---|
| 160 | * desktop to let them add tray icons if they need.
|
|---|
| 161 | */
|
|---|
| 162 |
|
|---|
| 163 | BOOL xstQuerySysTrayVersion(PULONG pulMajor, // out: major version number
|
|---|
| 164 | PULONG pulMinor, // out: minor version number
|
|---|
| 165 | PULONG pulRevision) // out: revision number
|
|---|
| 166 | {
|
|---|
| 167 | BOOL brc;
|
|---|
| 168 | PSYSTRAYCTLDATA pData = GetSysTrayCtlDataPtr();
|
|---|
| 169 | if (!pData)
|
|---|
| 170 | return FALSE;
|
|---|
| 171 |
|
|---|
| 172 | pData->ulCommand = SYSTRAYCMD_GETVERSION;
|
|---|
| 173 | pData->hwndSender = NULLHANDLE;
|
|---|
| 174 |
|
|---|
| 175 | brc = SendSysTrayCtlMsg(pData);
|
|---|
| 176 | if (brc)
|
|---|
| 177 | {
|
|---|
| 178 | if (pulMajor)
|
|---|
| 179 | *pulMajor = pData->u.version.ulMajor;
|
|---|
| 180 | if (pulMinor)
|
|---|
| 181 | *pulMinor = pData->u.version.ulMinor;
|
|---|
| 182 | if (pulRevision)
|
|---|
| 183 | *pulRevision = pData->u.version.ulRevision;
|
|---|
| 184 | }
|
|---|
| 185 |
|
|---|
| 186 | return brc;
|
|---|
| 187 | }
|
|---|
| 188 |
|
|---|
| 189 | /*
|
|---|
| 190 | *@@ xstAddSysTrayIcon:
|
|---|
| 191 | *
|
|---|
| 192 | * Adds an icon for the given window handle to the system tray. The icon ID
|
|---|
| 193 | * is used to distinguish between several icons for the same window handle.
|
|---|
| 194 | * If the icon with the specified ID already exists in the system tray, it
|
|---|
| 195 | * will be replaced.
|
|---|
| 196 | *
|
|---|
| 197 | * Returns TRUE on success and FALSE otherwise.
|
|---|
| 198 | *
|
|---|
| 199 | * The specified window handle receives notification messages about icon
|
|---|
| 200 | * events using the message ID specified by the ulMsgId parameter. The
|
|---|
| 201 | * layout of the message parameters is as follows:
|
|---|
| 202 | *
|
|---|
| 203 | * param1
|
|---|
| 204 | * USHORT usID icon ID
|
|---|
| 205 | * USHORT usCode notify code, one of XST_IN_ constants
|
|---|
| 206 | *
|
|---|
| 207 | * param2
|
|---|
| 208 | * PVOID pData notify code specific data (see below)
|
|---|
| 209 | *
|
|---|
| 210 | * The following notify codes are currently recognized:
|
|---|
| 211 | *
|
|---|
| 212 | * XST_IN_MOUSE:
|
|---|
| 213 | * Mouse event in the icon area. Currently, only mouse click
|
|---|
| 214 | * messages are recognized. param2 is a pointer to the XSTMOUSEMSG
|
|---|
| 215 | * structure containing full mouse message details.
|
|---|
| 216 | */
|
|---|
| 217 |
|
|---|
| 218 | BOOL xstAddSysTrayIcon(HWND hwnd, // in: window handle associated with the icon
|
|---|
| 219 | ULONG ulId, // in: icon ID to add
|
|---|
| 220 | HPOINTER hIcon, // in: icon handle
|
|---|
| 221 | ULONG ulMsgId, // in: message ID for notifications
|
|---|
| 222 | ULONG ulFlags) // in: flags (not currently used, must be 0)
|
|---|
| 223 | {
|
|---|
| 224 | BOOL brc;
|
|---|
| 225 | PPIB ppib;
|
|---|
| 226 | PSYSTRAYCTLDATA pData = GetSysTrayCtlDataPtr();
|
|---|
| 227 | if (!pData)
|
|---|
| 228 | return FALSE;
|
|---|
| 229 |
|
|---|
| 230 | // give all processes temporary access to hIcon
|
|---|
| 231 | brc = WinSetPointerOwner(hIcon, 0, FALSE);
|
|---|
| 232 | if (!brc)
|
|---|
| 233 | return FALSE;
|
|---|
| 234 |
|
|---|
| 235 | pData->ulCommand = SYSTRAYCMD_ADDICON;
|
|---|
| 236 | pData->hwndSender = hwnd;
|
|---|
| 237 | pData->u.icon.ulId = ulId;
|
|---|
| 238 | pData->u.icon.hIcon = hIcon;
|
|---|
| 239 | pData->u.icon.ulMsgId = ulMsgId;
|
|---|
| 240 |
|
|---|
| 241 | brc = SendSysTrayCtlMsg(pData);
|
|---|
| 242 |
|
|---|
| 243 | // revoke temporary access to hIcon
|
|---|
| 244 | DosGetInfoBlocks(NULL, &ppib);
|
|---|
| 245 | WinSetPointerOwner(hIcon, ppib->pib_ulpid, TRUE);
|
|---|
| 246 |
|
|---|
| 247 | return brc;
|
|---|
| 248 | }
|
|---|
| 249 |
|
|---|
| 250 | /*
|
|---|
| 251 | *@@ xstRemoveSysTrayIcon:
|
|---|
| 252 | *
|
|---|
| 253 | * Removes the icon previously added by xstAddSysTrayIcon() from the system
|
|---|
| 254 | * tray.
|
|---|
| 255 | *
|
|---|
| 256 | * Returns TRUE on success and FALSE otherwise.
|
|---|
| 257 | */
|
|---|
| 258 |
|
|---|
| 259 | BOOL xstRemoveSysTrayIcon(HWND hwnd, // in: window handle associated with the icon
|
|---|
| 260 | ULONG ulId) // in: icon ID to remove
|
|---|
| 261 | {
|
|---|
| 262 | PSYSTRAYCTLDATA pData = GetSysTrayCtlDataPtr();
|
|---|
| 263 | if (!pData)
|
|---|
| 264 | return FALSE;
|
|---|
| 265 |
|
|---|
| 266 | pData->ulCommand = SYSTRAYCMD_REMOVEICON;
|
|---|
| 267 | pData->hwndSender = hwnd;
|
|---|
| 268 | pData->u.icon.ulId = ulId;
|
|---|
| 269 |
|
|---|
| 270 | return SendSysTrayCtlMsg(pData);
|
|---|
| 271 | }
|
|---|
| 272 |
|
|---|
| 273 | /*
|
|---|
| 274 | *@@ xstSetSysTrayIconToolTip:
|
|---|
| 275 | *
|
|---|
| 276 | * Sets the tooltip text for the given icon in the system tray. This text
|
|---|
| 277 | * is shown when the mouse pointer is held still over the icon area for
|
|---|
| 278 | * some time.
|
|---|
| 279 | *
|
|---|
| 280 | * If pszText is NULL, the tooltip text is reset and will not be shown next
|
|---|
| 281 | * time. The old tooltip is hidden if it is being currently shown.
|
|---|
| 282 | *
|
|---|
| 283 | * Returns TRUE on success and FALSE otherwise.
|
|---|
| 284 | *
|
|---|
| 285 | * NOTE: The maximum tooltip text length (including terminating null) is
|
|---|
| 286 | * limited to a value returned by xstGetSysTrayMaxTextLen(). If the
|
|---|
| 287 | * supplied string is longer, it will be truncated.
|
|---|
| 288 | */
|
|---|
| 289 |
|
|---|
| 290 | BOOL xstSetSysTrayIconToolTip(HWND hwnd, // in: window handle associated with the icon
|
|---|
| 291 | ULONG ulId, // in: icon ID to set the tooltip for
|
|---|
| 292 | PSZ pszText) // in: tooltip text
|
|---|
| 293 | {
|
|---|
| 294 | PSYSTRAYCTLDATA pData = GetSysTrayCtlDataPtr();
|
|---|
| 295 | if (!pData)
|
|---|
| 296 | return FALSE;
|
|---|
| 297 |
|
|---|
| 298 | pData->ulCommand = SYSTRAYCMD_SETTOOLTIP;
|
|---|
| 299 | pData->hwndSender = hwnd;
|
|---|
| 300 | pData->u.tooltip.ulId = ulId;
|
|---|
| 301 |
|
|---|
| 302 | if (pszText == NULL)
|
|---|
| 303 | pData->u.tooltip.szText[0] = '\0';
|
|---|
| 304 | else
|
|---|
| 305 | {
|
|---|
| 306 | strncpy(pData->u.tooltip.szText, pszText,
|
|---|
| 307 | sizeof(pData->u.tooltip.szText) - 1);
|
|---|
| 308 | // be on the safe side
|
|---|
| 309 | pData->u.tooltip.szText[sizeof(pData->u.tooltip.szText) - 1] = '\0';
|
|---|
| 310 | }
|
|---|
| 311 |
|
|---|
| 312 | return SendSysTrayCtlMsg(pData);
|
|---|
| 313 | }
|
|---|
| 314 |
|
|---|
| 315 | BOOL xstShowSysTrayIconBalloon(HWND hwnd, ULONG ulId, PSZ pszTitle, PSZ pszText,
|
|---|
| 316 | ULONG ulFlags, ULONG ulTimeout)
|
|---|
| 317 | {
|
|---|
| 318 | // @todo implement
|
|---|
| 319 | return FALSE;
|
|---|
| 320 | }
|
|---|
| 321 |
|
|---|
| 322 | BOOL xstHideSysTrayIconBalloon(HWND hwnd, ULONG ulId)
|
|---|
| 323 | {
|
|---|
| 324 | // @todo implement
|
|---|
| 325 | return FALSE;
|
|---|
| 326 | }
|
|---|
| 327 |
|
|---|
| 328 | /*
|
|---|
| 329 | *@@ xstQuerySysTrayIconRect:
|
|---|
| 330 | *
|
|---|
| 331 | * Obtains a rectangle occupied by the given icon (in screen coordinates,
|
|---|
| 332 | * top right corner exclusive).
|
|---|
| 333 | *
|
|---|
| 334 | * Returns TRUE on success and FALSE otherwise.
|
|---|
| 335 | */
|
|---|
| 336 | BOOL xstQuerySysTrayIconRect(HWND hwnd, ULONG ulId, PRECTL prclRect)
|
|---|
| 337 | {
|
|---|
| 338 | // @todo implement
|
|---|
| 339 | return FALSE;
|
|---|
| 340 | }
|
|---|
| 341 |
|
|---|
| 342 | /*
|
|---|
| 343 | *@@ xstGetSysTrayCreatedMsgId:
|
|---|
| 344 | *
|
|---|
| 345 | * Returns the ID of the message that is sent by the Extended system tray
|
|---|
| 346 | * to all top-level WC_FRAME windows on the desktop to let them add tray
|
|---|
| 347 | * icons if they need.
|
|---|
| 348 | *
|
|---|
| 349 | * NOTE: The returned value never changes until reboot so it is a good
|
|---|
| 350 | * practice to cache it instead of calling this function each time from the
|
|---|
| 351 | * window procedure of every involved window.
|
|---|
| 352 | */
|
|---|
| 353 |
|
|---|
| 354 | ULONG xstGetSysTrayCreatedMsgId()
|
|---|
| 355 | {
|
|---|
| 356 | static ULONG WM_XST_CREATED = 0;
|
|---|
| 357 | if (WM_XST_CREATED == 0)
|
|---|
| 358 | WM_XST_CREATED = WinAddAtom(WinQuerySystemAtomTable(),
|
|---|
| 359 | "ExtendedSysTray.WM_XST_CREATED");
|
|---|
| 360 | return WM_XST_CREATED;
|
|---|
| 361 | }
|
|---|
| 362 |
|
|---|
| 363 | /*
|
|---|
| 364 | *@@ xstGetSysTrayMaxTextLen:
|
|---|
| 365 | *
|
|---|
| 366 | * Returns the maximum length of the text (in bytes, including the
|
|---|
| 367 | * terminating null) that can be shown in the tooltop of the icon in the
|
|---|
| 368 | * system tray. You can use the returned value to determine the maximum
|
|---|
| 369 | * length of the string passed as pszText to xstSetSysTrayIconToolTip().
|
|---|
| 370 | *
|
|---|
| 371 | * The returned value also defines the maximum length of both the title and
|
|---|
| 372 | * the text (including terminating nulls) of the icon's balloon for the
|
|---|
| 373 | * xstShowSysTrayIconBalloon() call.
|
|---|
| 374 | *
|
|---|
| 375 | * Returns TRUE on success and FALSE otherwise.
|
|---|
| 376 | *
|
|---|
| 377 | * NOTE: The returned value never changes until reboot so it is a good
|
|---|
| 378 | * practice to cache it instead of calling this function each time.
|
|---|
| 379 | */
|
|---|
| 380 |
|
|---|
| 381 | ULONG xstGetSysTrayMaxTextLen()
|
|---|
| 382 | {
|
|---|
| 383 | return sizeof(((PSYSTRAYCTLDATA)0)->u.tooltip.szText);
|
|---|
| 384 | }
|
|---|
| 385 |
|
|---|