source: trunk/src/3rdparty/os2/xsystray/apilib/xsystray.c

Last change on this file was 1126, checked in by Dmitry A. Kuminov, 12 years ago

xsystray: Fix build break in GCC 4.4.6.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 15.8 KB
Line 
1
2/*
3 *@@sourcefile xsystray.c:
4 * Extended system tray widget for XCenter/eCenter.
5 *
6 * Implementation of the public API.
7 *
8 * Copyright (C) 2009-2011 Dmitriy Kuminov
9 *
10 * This file is part of the Extended system tray widget source package.
11 * Extended system tray widget is free software; you can redistribute it
12 * and/or modify it under the terms of the GNU General Public License as
13 * published by the Free Software Foundation, in version 2 as it comes in
14 * the "COPYING" file of the Extended system tray widget distribution. This
15 * program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * more details.
19 */
20
21#define OS2EMX_PLAIN_CHAR
22#define INCL_DOSERRORS
23#define INCL_DOSPROCESS
24#define INCL_WINWINDOWMGR
25#define INCL_WINERRORS
26#define INCL_WINATOM
27#define INCL_WINPOINTERS
28#define INCL_WINHOOKS
29#define INCL_WINWINDOWMGR
30#include <os2.h>
31
32#define XSTAPI_IMPL
33#include "xsystray.h"
34
35//#define ENABLE_LOG_TO "c:\\xsystray_api.dbg"
36
37#include "w_xsystray.h"
38
39#include <string.h>
40#include <sys/builtin.h> // atomics
41#include <sys/fmutex.h> // fast mutex
42#include <sys/smutex.h> // simple mutex
43
44static ULONG WM_XST_CREATED = 0;
45 // identity of the WM_XST_CREATED message taken from the atom table
46static ULONG WM_XST_NOTIFY = 0;
47 // identity of the WM_XST_NOTIFY message taken from the atom table
48
49static
50volatile HWND G_hwndSysTray = NULLHANDLE;
51 // window handle of the system tray server
52
53static
54volatile PVOID G_pvMemoryPool = NULL;
55 // shared memory pool for SYSTRAYCTLDATA structs used by
56 // WM_XST_CONTROL messages. Note that once allocated, this memory
57 // is never freed: it is intentional since the memory is assumed
58 // to be always in need and that the system will free it when the
59 // application terminates
60
61#define CLIENT_MEMORYPOOL_SIZE 65536
62 // taking SYSTRAYCTLDATA size into account (<=1024 B), this is enough
63 // for at least 64 threads sending WM_XST_CONTROL simultaneously, which
64 // sounds sane
65
66typedef struct
67{
68 HMQ hmq;
69 unsigned cRefs;
70} HMQREFS, *PHMQREFS;
71
72static PHMQREFS G_pHmqRefs = NULL;
73 // array of references to each HMQ that we make
74static size_t G_cHmqRefs = 0;
75 // number of elements in G_pHmqRefs
76static size_t G_cHmqRefsMax = 0;
77 // maximum number of elements in G_pHmqRefs
78static _fmutex G_fmtx;
79 // fast mutex to protect
80static _smutex G_smtx = 0;
81 // simple mutex for xstAddSysTrayIcon()
82#define HMQREFS_GROW 4
83 // how fast G_pHmqRefs grows when more space is necessary
84
85// @todo to be on the safe side with casting in __atomic_cmpxchg32() we need
86// compile-time assertions like this:
87// AssertCompile(sizeof(uint32_t) == sizeof(HWND));
88// AssertCompile(sizeof(uint32_t) == sizeof(PVOID));
89
90static HWND FindSysTrayServerWindow()
91{
92 char buf[sizeof(WNDCLASS_WIDGET_XSYSTRAY_SERVER) + 1];
93 HWND hwnd;
94 HENUM henum = WinBeginEnumWindows(HWND_DESKTOP);
95 while ((hwnd = WinGetNextWindow(henum)) != NULLHANDLE)
96 {
97 LONG len = WinQueryClassName(hwnd, sizeof(buf), buf);
98 buf[len] = '\0';
99 if (strcmp(WNDCLASS_WIDGET_XSYSTRAY_SERVER, buf) == 0)
100 break;
101 }
102 WinEndEnumWindows(henum);
103
104 return hwnd;
105}
106
107static ULONG SendSysTrayCtlMsg(PSYSTRAYCTLDATA pData)
108{
109 APIRET arc;
110 PID pid;
111 TID tid;
112 MRESULT mrc;
113
114 BOOL bTriedFind = FALSE;
115
116 do
117 {
118 if (G_hwndSysTray == NULLHANDLE)
119 {
120 bTriedFind = TRUE;
121 HWND hwnd = FindSysTrayServerWindow();
122 __atomic_cmpxchg32((volatile uint32_t *)&G_hwndSysTray,
123 hwnd, NULLHANDLE);
124 if (G_hwndSysTray == NULLHANDLE)
125 break;
126 }
127
128 if (bTriedFind)
129 {
130 arc = ERROR_INVALID_HANDLE;
131 if (WinQueryWindowProcess(G_hwndSysTray, &pid, &tid))
132 arc = DosGiveSharedMem(G_pvMemoryPool,
133 pid, PAG_READ | PAG_WRITE);
134 if (arc != NO_ERROR)
135 break;
136 }
137
138 pData->bAcknowledged = FALSE;
139
140 mrc = WinSendMsg(G_hwndSysTray, WM_XST_CONTROL, pData, NULL);
141 if (pData->bAcknowledged)
142 return (ULONG)mrc;
143
144 // if we failed to send the message, it may mean that XCenter was restarted
145 // or the system tray was re-enabled. Try to get a new handle (only if we
146 // didn't already do it in this call)
147 if (!bTriedFind)
148 {
149 G_hwndSysTray = NULLHANDLE;
150 continue;
151 }
152
153 break;
154 }
155 while (1);
156
157 return XST_FAIL;
158}
159
160/*
161 *@@ AllocSysTrayCtlDataPtr:
162 * Allocates a SYSTRAYCTLDATA struct in the pool of shared memory.
163 *
164 * If there is no free space in the pool, it returns NULL. The allocated
165 * memory must be freed by FreeSysTrayCtlDataPtr() when not needed.
166 */
167
168static PSYSTRAYCTLDATA AllocSysTrayCtlDataPtr()
169{
170 APIRET arc;
171 PVOID pvPool;
172 PSYSTRAYCTLDATA pData;
173
174 if (!G_pvMemoryPool)
175 {
176 // Note: we don't PAG_COMMIT, DosSubAllocMem will do so when needed
177 arc = DosAllocSharedMem((PVOID)&pvPool, NULL, CLIENT_MEMORYPOOL_SIZE,
178 PAG_READ | PAG_WRITE | OBJ_GIVEABLE);
179 if (arc == NO_ERROR)
180 arc = DosSubSetMem(pvPool,
181 DOSSUB_INIT | DOSSUB_SPARSE_OBJ,
182 CLIENT_MEMORYPOOL_SIZE);
183 if (!__atomic_cmpxchg32((volatile uint32_t *)&G_pvMemoryPool,
184 (uint32_t)pvPool, (uint32_t)NULL))
185 {
186 // another thread has already got an entry, discard our try
187 if (pvPool)
188 DosFreeMem(pvPool);
189 }
190 else
191 {
192 // we could fail to allocate while being the first... give up
193 if (arc != NO_ERROR)
194 return NULL;
195 }
196 }
197
198 arc = DosSubAllocMem(G_pvMemoryPool, (PVOID)&pData, sizeof(*pData));
199 if (arc != NO_ERROR)
200 return NULL;
201
202 return pData;
203}
204
205static VOID FreeSysTrayCtlDataPtr(PSYSTRAYCTLDATA pData)
206{
207 DosSubFreeMem(G_pvMemoryPool, pData, sizeof(*pData));
208}
209
210/*
211 *@@ InputHook:
212 * This is used to intercept posted WM_XST_NOTIFY messages and apply
213 * special processing to them (compose a client window-specific
214 * notification message, free the NOTIFYDATA structure and post the
215 * composed message to the target window).
216 */
217
218static BOOL EXPENTRY InputHook(HAB hab, PQMSG pQmsg, ULONG fs)
219{
220 if (pQmsg->msg == WM_XST_NOTIFY)
221 {
222 PNOTIFYDATA pNotifyData = (PNOTIFYDATA)pQmsg->mp1;
223 PVOID pvMemoryPool = (PVOID)pQmsg->mp2;
224
225 // 1) create a local copy of NOTIFYDATA
226 NOTIFYDATA NotifyData = *pNotifyData;
227 // 2) fix the mp2 pointer in it (which is always to one of u's structs)
228 NotifyData.mp2 -= (ULONG)pNotifyData;
229 NotifyData.mp2 += (ULONG)&NotifyData;
230 // 3) free the original to let it be reused by other processes ASAP
231 FreeNotifyDataPtr(pvMemoryPool, pQmsg->hwnd, pNotifyData);
232
233 // start with a copy of the message and change the fields we need
234 QMSG newMsg = *pQmsg;
235 newMsg.msg = NotifyData.msg;
236 newMsg.mp1 = NotifyData.mp1;
237 newMsg.mp2 = NotifyData.mp2;
238
239 LOGF(("Dispatching msg %08lx to hwnd %lx\n", newMsg.msg, newMsg.hwnd));
240
241 // deliver the message
242 WinDispatchMsg(hab, &newMsg);
243
244 return TRUE;
245 }
246
247 return FALSE;
248}
249
250XSTAPI(BOOL, xstQuerySysTrayVersion)(PULONG pulMajor,
251 PULONG pulMinor,
252 PULONG pulRevision)
253{
254 BOOL brc;
255 PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
256 if (!pData)
257 return FALSE;
258
259 pData->ulCommand = SYSTRAYCMD_GETVERSION;
260 pData->hwndSender = NULLHANDLE;
261
262 brc = SendSysTrayCtlMsg(pData) == XST_OK;
263 if (brc)
264 {
265 if (pulMajor)
266 *pulMajor = pData->u.version.ulMajor;
267 if (pulMinor)
268 *pulMinor = pData->u.version.ulMinor;
269 if (pulRevision)
270 *pulRevision = pData->u.version.ulRevision;
271 }
272
273 FreeSysTrayCtlDataPtr(pData);
274
275 return brc;
276}
277
278XSTAPI(BOOL, xstAddSysTrayIcon)(HWND hwnd,
279 USHORT usId,
280 HPOINTER hIcon,
281 PCSZ pcszToolTip,
282 ULONG ulMsgId,
283 ULONG ulFlags)
284{
285 BOOL brc;
286 ULONG xrc = XST_FAIL;
287 PPIB ppib;
288 HAB hab;
289 HMQ hmq;
290 size_t i;
291
292 PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
293 if (!pData)
294 return FALSE;
295
296 if (WM_XST_NOTIFY == 0)
297 WM_XST_NOTIFY = WinAddAtom(WinQuerySystemAtomTable(),
298 WM_XST_NOTIFY_ATOM);
299
300 hab = WinQueryAnchorBlock(hwnd);
301 hmq = WinQueryWindowULong(hwnd, QWL_HMQ);
302 if (hmq == NULLHANDLE)
303 return FALSE;
304
305 // initialize the HMQ refs array
306 // @todo remove _smutex usage when we get into the DLL and initialize
307 // _fmutex + array in the DLL init routine
308 _smutex_request(&G_smtx);
309 if (!G_pHmqRefs)
310 {
311 if (_fmutex_create(&G_fmtx, 0))
312 return FALSE;
313 G_pHmqRefs = malloc(sizeof(*G_pHmqRefs) * HMQREFS_GROW);
314 if (!G_pHmqRefs)
315 return FALSE;
316 G_cHmqRefs = 0;
317 G_cHmqRefsMax = HMQREFS_GROW;
318 }
319 _smutex_release(&G_smtx);
320
321 // give all processes temporary access to hIcon
322 brc = hIcon != NULLHANDLE ? WinSetPointerOwner(hIcon, 0, FALSE) : TRUE;
323 if (brc)
324 {
325 pData->ulCommand = SYSTRAYCMD_ADDICON;
326 pData->hwndSender = hwnd;
327
328 pData->u.icon.usId = usId;
329 pData->u.icon.hIcon = hIcon;
330 pData->u.icon.ulMsgId = ulMsgId;
331
332 if (!pcszToolTip)
333 pData->u.icon.szToolTip[0] = '\0';
334 else
335 {
336 strncpy(pData->u.icon.szToolTip, pcszToolTip,
337 sizeof(pData->u.icon.szToolTip) - 1);
338 // be on the safe side
339 pData->u.icon.szToolTip[sizeof(pData->u.icon.szToolTip) - 1] = '\0';
340 }
341
342 xrc = SendSysTrayCtlMsg(pData);
343 brc = xrc == XST_OK || xrc == XST_REPLACED;
344
345 // revoke temporary access to hIcon
346 if (hIcon != NULLHANDLE)
347 {
348 DosGetInfoBlocks(NULL, &ppib);
349 WinSetPointerOwner(hIcon, ppib->pib_ulpid, TRUE);
350 }
351 }
352
353 FreeSysTrayCtlDataPtr(pData);
354
355 if (xrc == XST_OK)
356 {
357 // install the message hook for the new icon to intercept WM_XST_NOTIFY
358 // messages or increase the reference count if already done so
359 brc = FALSE;
360 _fmutex_request(&G_fmtx, _FMR_IGNINT);
361 do
362 {
363 for (i = 0; i < G_cHmqRefs; ++i)
364 if (G_pHmqRefs[i].hmq == hmq)
365 break;
366 if (i < G_cHmqRefs)
367 ++G_pHmqRefs[i].cRefs;
368 else
369 {
370 if (i == G_cHmqRefsMax)
371 {
372 PHMQREFS pNewRefs = realloc(G_pHmqRefs,
373 sizeof(*G_pHmqRefs) *
374 (G_cHmqRefsMax + HMQREFS_GROW));
375 if (!pNewRefs)
376 break;
377 G_pHmqRefs = pNewRefs;
378 G_cHmqRefsMax += HMQREFS_GROW;
379 }
380 brc = WinSetHook(hab, hmq, HK_INPUT, (PFN)InputHook, NULLHANDLE);
381 if (!brc)
382 break;
383 ++G_cHmqRefs;
384 G_pHmqRefs[i].hmq = hmq;
385 G_pHmqRefs[i].cRefs = 1;
386 }
387 brc = TRUE;
388 }
389 while (0);
390 _fmutex_release(&G_fmtx);
391
392 if (!brc)
393 xstRemoveSysTrayIcon(hwnd, usId);
394 }
395
396 return brc;
397}
398
399XSTAPI(BOOL, xstReplaceSysTrayIcon)(HWND hwnd,
400 USHORT usId,
401 HPOINTER hIcon)
402{
403 BOOL brc;
404 PPIB ppib;
405
406 PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
407 if (!pData)
408 return FALSE;
409
410 // give all processes temporary access to hIcon
411 brc = hIcon != NULLHANDLE ? WinSetPointerOwner(hIcon, 0, FALSE) : TRUE;
412 if (brc)
413 {
414 pData->ulCommand = SYSTRAYCMD_REPLACEICON;
415 pData->hwndSender = hwnd;
416
417 pData->u.icon.usId = usId;
418 pData->u.icon.hIcon = hIcon;
419
420 brc = SendSysTrayCtlMsg(pData) == XST_OK;
421
422 // revoke temporary access to hIcon
423 if (hIcon != NULLHANDLE)
424 {
425 DosGetInfoBlocks(NULL, &ppib);
426 WinSetPointerOwner(hIcon, ppib->pib_ulpid, TRUE);
427 }
428 }
429
430 FreeSysTrayCtlDataPtr(pData);
431
432 return brc;
433}
434
435XSTAPI(BOOL, xstRemoveSysTrayIcon)(HWND hwnd,
436 USHORT usId)
437{
438 BOOL brc;
439 HAB hab;
440 HMQ hmq;
441 size_t i;
442
443 PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
444 if (!pData)
445 return FALSE;
446
447 hab = WinQueryAnchorBlock(hwnd);
448 hmq = WinQueryWindowULong(hwnd, QWL_HMQ);
449 if (hmq == NULLHANDLE)
450 return FALSE;
451
452 pData->ulCommand = SYSTRAYCMD_REMOVEICON;
453 pData->hwndSender = hwnd;
454 pData->u.icon.usId = usId;
455
456 brc = SendSysTrayCtlMsg(pData) == XST_OK;
457
458 FreeSysTrayCtlDataPtr(pData);
459
460 if (brc)
461 {
462 // remove the message hook if it's the last reference to the HMQ
463 _fmutex_request(&G_fmtx, _FMR_IGNINT);
464 do
465 {
466 for (i = 0; i < G_cHmqRefs; ++i)
467 if (G_pHmqRefs[i].hmq == hmq)
468 break;
469 if (i == G_cHmqRefs)
470 // unknown HMQ??
471 break;
472
473 if (--G_pHmqRefs[i].cRefs == 0)
474 WinReleaseHook(hab, hmq, HK_INPUT, (PFN)InputHook, NULLHANDLE);
475 }
476 while (0);
477 _fmutex_release(&G_fmtx);
478 }
479
480 return brc;
481}
482
483XSTAPI(BOOL, xstSetSysTrayIconToolTip)(HWND hwnd,
484 USHORT usId,
485 PCSZ pcszToolTip)
486{
487 BOOL brc;
488 PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
489 if (!pData)
490 return FALSE;
491
492 pData->ulCommand = SYSTRAYCMD_SETTOOLTIP;
493 pData->hwndSender = hwnd;
494 pData->u.icon.usId = usId;
495
496 if (!pcszToolTip)
497 pData->u.icon.szToolTip[0] = '\0';
498 else
499 {
500 strncpy(pData->u.icon.szToolTip, pcszToolTip,
501 sizeof(pData->u.icon.szToolTip) - 1);
502 // be on the safe side
503 pData->u.icon.szToolTip[sizeof(pData->u.icon.szToolTip) - 1] = '\0';
504 }
505
506 brc = SendSysTrayCtlMsg(pData) == XST_OK;
507
508 FreeSysTrayCtlDataPtr(pData);
509
510 return brc;
511}
512
513XSTAPI(BOOL, xstShowSysTrayIconBalloon)(HWND hwnd, USHORT usId, PCSZ pcszTitle,
514 PCSZ pcszText, ULONG ulFlags, ULONG ulTimeout)
515{
516 // @todo implement
517 return FALSE;
518}
519
520XSTAPI(BOOL, xstHideSysTrayIconBalloon)(HWND hwnd, USHORT usId)
521{
522 // @todo implement
523 return FALSE;
524}
525
526XSTAPI(BOOL, xstQuerySysTrayIconRect)(HWND hwnd, USHORT usId, PRECTL prclRect)
527{
528 BOOL brc;
529 PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
530 if (!pData)
531 return FALSE;
532
533 pData->ulCommand = SYSTRAYCMD_QUERYRECT;
534 pData->hwndSender = hwnd;
535 pData->u.icon.usId = usId;
536
537 brc = SendSysTrayCtlMsg(pData) == XST_OK;
538 if (brc)
539 {
540 *prclRect = pData->u.rect.rclIcon;
541 }
542
543 FreeSysTrayCtlDataPtr(pData);
544
545 return brc;
546}
547
548XSTAPI(ULONG, xstGetSysTrayCreatedMsgId)()
549{
550 if (WM_XST_CREATED == 0)
551 WM_XST_CREATED = WinAddAtom(WinQuerySystemAtomTable(),
552 WM_XST_CREATED_ATOM);
553 return WM_XST_CREATED;
554}
555
556XSTAPI(ULONG, xstGetSysTrayMaxTextLen)()
557{
558 return sizeof(((PSYSTRAYCTLDATA)0)->u.icon.szToolTip);
559}
560
Note: See TracBrowser for help on using the repository browser.