source: trunk/src/3rdparty/os2/xsystray/xsystray_api.c@ 280

Last change on this file since 280 was 280, checked in by Dmitry A. Kuminov, 16 years ago

3rdparty: os2/xsystray: Relicensed under GPL v2 to ease future integration with xworkplace.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 15.1 KB
Line 
1
2/*
3 *@@sourcefile xsystray_api.c:
4 * Extended system tray widget for XCenter/eCenter.
5 *
6 * Implementation of the public API.
7 *
8 * Copyright (C) 2009 Dmitry A. 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 INCL_DOSERRORS
22#define INCL_DOSPROCESS
23#define INCL_WINWINDOWMGR
24#define INCL_WINERRORS
25#define INCL_WINATOM
26#define INCL_WINPOINTERS
27#define INCL_WINHOOKS
28#include <os2.h>
29
30#include "xsystray_api.h"
31#include "xsystray.h"
32
33#include <string.h>
34#include <sys/builtin.h> // atomics
35
36static ULONG WM_XST_CREATED = 0;
37 // identity of the WM_XST_CREATED message taken from the atom table
38static ULONG WM_XST_NOTIFY = 0;
39 // identity of the WM_XST_NOTIFY message taken from the atom table
40
41static
42volatile HWND G_hwndSysTray = NULLHANDLE;
43 // window handle of the system tray server
44
45static
46volatile PVOID G_pvMemoryPool = NULL;
47 // shared memory pool for SYSTRAYCTLDATA structs used by
48 // WM_XST_CONTROL messages. Note that once allocated, this memory
49 // is never freed: it is intentional since the memory is assumed
50 // to be always in need and that the system will free it when the
51 // application terminates
52
53#define CLIENT_MEMORYPOOL_SIZE 65536
54 // taking SYSTRAYCTLDATA size into account (<=1024 B), this is enough
55 // for at least 64 threads sending WM_XST_CONTROL simultaneously, which
56 // sounds sane
57
58// @todo to be on the safe side with casting in __atomic_cmpxchg32() we need
59// compile-time assertions like this:
60// AssertCompile(sizeof(uint32_t) == sizeof(HWND));
61// AssertCompile(sizeof(uint32_t) == sizeof(PVOID));
62
63static HWND FindSysTrayServerWindow()
64{
65 char buf[sizeof(WNDCLASS_WIDGET_XSYSTRAY_SERVER) + 1];
66 HWND hwnd;
67 HENUM henum = WinBeginEnumWindows(HWND_DESKTOP);
68 while ((hwnd = WinGetNextWindow(henum)) != NULLHANDLE)
69 {
70 LONG len = WinQueryClassName(hwnd, sizeof(buf), buf);
71 buf[len] = '\0';
72 if (strcmp(WNDCLASS_WIDGET_XSYSTRAY_SERVER, buf) == 0)
73 break;
74 }
75 WinEndEnumWindows(henum);
76
77 return hwnd;
78}
79
80static BOOL SendSysTrayCtlMsg(PSYSTRAYCTLDATA pData)
81{
82 APIRET arc;
83 PID pid;
84 TID tid;
85 MRESULT mrc;
86
87 BOOL bTriedFind = FALSE;
88
89 do
90 {
91 if (G_hwndSysTray == NULLHANDLE)
92 {
93 bTriedFind = TRUE;
94 HWND hwnd = FindSysTrayServerWindow();
95 __atomic_cmpxchg32((volatile uint32_t *)&G_hwndSysTray,
96 hwnd, NULLHANDLE);
97 if (G_hwndSysTray == NULLHANDLE)
98 break;
99 }
100
101 if (bTriedFind)
102 {
103 arc = ERROR_INVALID_HANDLE;
104 if (WinQueryWindowProcess(G_hwndSysTray, &pid, &tid))
105 arc = DosGiveSharedMem(G_pvMemoryPool,
106 pid, PAG_READ | PAG_WRITE);
107 if (arc != NO_ERROR)
108 break;
109 }
110
111 pData->bAcknowledged = FALSE;
112
113 mrc = WinSendMsg(G_hwndSysTray, WM_XST_CONTROL, pData, NULL);
114 if (mrc == (MRESULT)TRUE && pData->bAcknowledged)
115 return TRUE;
116
117 // if we failed to send the message, it may mean that XCenter was restarted
118 // or the system tray was re-enabled. Try to get a new handle (only if we
119 // didn't already do it in this call)
120 if (!bTriedFind)
121 {
122 G_hwndSysTray = NULLHANDLE;
123 continue;
124 }
125
126 break;
127 }
128 while (TRUE);
129
130 return FALSE;
131}
132
133/*
134 *@@ AllocSysTrayCtlDataPtr:
135 * Allocates a SYSTRAYCTLDATA struct in the pool of shared memory.
136 *
137 * If there is no free space in the pool, it returns NULL. The allocated
138 * memory must be freed by FreeSysTrayCtlDataPtr() when not needed.
139 */
140
141static PSYSTRAYCTLDATA AllocSysTrayCtlDataPtr()
142{
143 APIRET arc;
144 PVOID pvPool;
145 PSYSTRAYCTLDATA pData;
146
147 if (!G_pvMemoryPool)
148 {
149 // Note: we don't PAG_COMMIT, DosSubAllocMem will do so when needed
150 arc = DosAllocSharedMem((PVOID)&pvPool, NULL, CLIENT_MEMORYPOOL_SIZE,
151 PAG_READ | PAG_WRITE | OBJ_GIVEABLE);
152 if (arc == NO_ERROR)
153 arc = DosSubSetMem(pvPool,
154 DOSSUB_INIT | DOSSUB_SPARSE_OBJ,
155 CLIENT_MEMORYPOOL_SIZE);
156 if (!__atomic_cmpxchg32((volatile uint32_t *)&G_pvMemoryPool,
157 (uint32_t)pvPool, (uint32_t)NULL))
158 {
159 // another thread has already got an entry, discard our try
160 if (pvPool)
161 DosFreeMem(pvPool);
162 }
163 else
164 {
165 // we could fail to allocate while being the first... give up
166 if (arc != NO_ERROR)
167 return NULL;
168 }
169 }
170
171 arc = DosSubAllocMem(G_pvMemoryPool, (PVOID)&pData, sizeof(*pData));
172 if (arc != NO_ERROR)
173 return NULL;
174
175 return pData;
176}
177
178static VOID FreeSysTrayCtlDataPtr(PSYSTRAYCTLDATA pData)
179{
180 DosSubFreeMem(G_pvMemoryPool, pData, sizeof(*pData));
181}
182
183/*
184 *@@ InputHook:
185 * This is used to intercept posted XST_NOTIFY messages and apply special
186 * processing to them (compose a client window-specific notification
187 * message, free the NOTIFYDATA structure and post the composed message to
188 * the target window).
189 */
190
191static BOOL EXPENTRY InputHook(HAB hab, PQMSG pQmsg, ULONG fs)
192{
193 if (pQmsg->msg == WM_XST_NOTIFY)
194 {
195 PNOTIFYDATA pNotifyData = (PNOTIFYDATA)pQmsg->mp1;
196 PVOID pvMemoryPool = (PVOID)pQmsg->mp2;
197
198 // copy NOTIFYDATA and free it
199 NOTIFYDATA NotifyData = *pNotifyData;
200 FreeNotifyDataPtr(pvMemoryPool, pQmsg->hwnd, pNotifyData);
201
202 // start with a copy of the message and change the fields we need
203 QMSG newMsg = *pQmsg;
204 newMsg.msg = NotifyData.msg;
205 newMsg.mp1 = NotifyData.mp1;
206 newMsg.mp2 = NotifyData.mp2;
207
208 // deliver the message
209 WinDispatchMsg(hab, &newMsg);
210
211 return TRUE;
212 }
213
214 return FALSE;
215}
216
217/*
218 *@@ xstQuerySysTrayVersion:
219 *
220 * Returns the version of the Extended system tray in the variables pointed
221 * to by arguments. Any argument may be NULL in which case the
222 * corresponding component of the version is not returned.
223 *
224 * Returns TRUE on success and FALSE if the Extended system tray is not
225 * installed or not operational.
226 *
227 * NOTE: When the Extended system tray is started up or gets enabled after
228 * being temporarily disabled, it sends a message with the ID returned by
229 * xstGetSysTrayCreatedMsgId() to all top-level WC_FRAME windows on the
230 * desktop to let them add tray icons if they need.
231 */
232
233BOOL xstQuerySysTrayVersion(PULONG pulMajor, // out: major version number
234 PULONG pulMinor, // out: minor version number
235 PULONG pulRevision) // out: revision number
236{
237 BOOL brc;
238 PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
239 if (!pData)
240 return FALSE;
241
242 pData->ulCommand = SYSTRAYCMD_GETVERSION;
243 pData->hwndSender = NULLHANDLE;
244
245 brc = SendSysTrayCtlMsg(pData);
246 if (brc)
247 {
248 if (pulMajor)
249 *pulMajor = pData->u.version.ulMajor;
250 if (pulMinor)
251 *pulMinor = pData->u.version.ulMinor;
252 if (pulRevision)
253 *pulRevision = pData->u.version.ulRevision;
254 }
255
256 FreeSysTrayCtlDataPtr(pData);
257
258 return brc;
259}
260
261/*
262 *@@ xstAddSysTrayIcon:
263 *
264 * Adds an icon for the given window handle to the system tray. The icon ID
265 * is used to distinguish between several icons for the same window handle.
266 * If the icon with the specified ID already exists in the system tray, it
267 * will be replaced.
268 *
269 * Returns TRUE on success and FALSE otherwise.
270 *
271 * The specified window handle receives notification messages about icon
272 * events using the message ID specified by the ulMsgId parameter. The
273 * layout of the message parameters is as follows:
274 *
275 * param1
276 * USHORT usIconID icon ID
277 * USHORT usNotifyCode notify code, one of XST_IN_ constants
278 *
279 * param2
280 * PVOID pData notify code specific data (see below)
281 *
282 * The following notify codes are currently recognized:
283 *
284 * XST_IN_MOUSE:
285 * Mouse event in the icon area. Currently, only mouse click
286 * messages are recognized. param2 is a pointer to the XSTMOUSEMSG
287 * structure containing full mouse message details.
288 *
289 * XST_IN_CONTEXT:
290 * Context menu event in the icon area. param2 is a pointer to the
291 * XSTCONTEXTMSG structure containing full message details.
292 *
293 * XST_IN_WHEEL:
294 * Mouse wheel event in the icon area. param2 is a pointer to the
295 * XSTWHEELTMSG structure containing full message details.
296 */
297
298BOOL xstAddSysTrayIcon(HWND hwnd, // in: window handle associated with the icon
299 USHORT usId, // in: icon ID to add
300 HPOINTER hIcon, // in: icon handle
301 ULONG ulMsgId, // in: message ID for notifications
302 ULONG ulFlags) // in: flags (not currently used, must be 0)
303{
304 BOOL brc;
305 PPIB ppib;
306 PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
307 if (!pData)
308 return FALSE;
309
310 // install the message hook if not already done so
311 // @todo the code is temporary and incorrect, it's just for testing
312 // (we should use an array of HMQ to check if we already installed the filter)
313 if (WM_XST_NOTIFY == 0)
314 {
315 WM_XST_NOTIFY = WinAddAtom(WinQuerySystemAtomTable(),
316 WM_XST_NOTIFY_ATOM);
317
318 brc = WinSetHook(WinQueryAnchorBlock(hwnd),
319 HMQ_CURRENT, HK_INPUT, (PFN)InputHook, NULLHANDLE);
320 if (!brc)
321 return FALSE;
322 }
323
324 // give all processes temporary access to hIcon
325 brc = WinSetPointerOwner(hIcon, 0, FALSE);
326 if (brc)
327 {
328 pData->ulCommand = SYSTRAYCMD_ADDICON;
329 pData->hwndSender = hwnd;
330 pData->u.icon.usId = usId;
331 pData->u.icon.hIcon = hIcon;
332 pData->u.icon.ulMsgId = ulMsgId;
333
334 brc = SendSysTrayCtlMsg(pData);
335
336 // revoke temporary access to hIcon
337 DosGetInfoBlocks(NULL, &ppib);
338 WinSetPointerOwner(hIcon, ppib->pib_ulpid, TRUE);
339 }
340
341 FreeSysTrayCtlDataPtr(pData);
342
343 return brc;
344}
345
346/*
347 *@@ xstRemoveSysTrayIcon:
348 *
349 * Removes the icon previously added by xstAddSysTrayIcon() from the system
350 * tray.
351 *
352 * Returns TRUE on success and FALSE otherwise.
353 */
354
355BOOL xstRemoveSysTrayIcon(HWND hwnd, // in: window handle associated with the icon
356 USHORT usId) // in: icon ID to remove
357{
358 BOOL brc;
359 PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
360 if (!pData)
361 return FALSE;
362
363 pData->ulCommand = SYSTRAYCMD_REMOVEICON;
364 pData->hwndSender = hwnd;
365 pData->u.icon.usId = usId;
366
367 brc = SendSysTrayCtlMsg(pData);
368
369 FreeSysTrayCtlDataPtr(pData);
370
371 return brc;
372}
373
374/*
375 *@@ xstSetSysTrayIconToolTip:
376 *
377 * Sets the tooltip text for the given icon in the system tray. This text
378 * is shown when the mouse pointer is held still over the icon area for
379 * some time.
380 *
381 * If pszText is NULL, the tooltip text is reset and will not be shown next
382 * time. The old tooltip is hidden if it is being currently shown.
383 *
384 * Returns TRUE on success and FALSE otherwise.
385 *
386 * NOTE: The maximum tooltip text length (including terminating null) is
387 * limited to a value returned by xstGetSysTrayMaxTextLen(). If the
388 * supplied string is longer, it will be truncated.
389 */
390
391BOOL xstSetSysTrayIconToolTip(HWND hwnd, // in: window handle associated with the icon
392 USHORT usId, // in: icon ID to set the tooltip for
393 PSZ pszText) // in: tooltip text
394{
395 BOOL brc;
396 PSYSTRAYCTLDATA pData = AllocSysTrayCtlDataPtr();
397 if (!pData)
398 return FALSE;
399
400 pData->ulCommand = SYSTRAYCMD_SETTOOLTIP;
401 pData->hwndSender = hwnd;
402 pData->u.tooltip.usId = usId;
403
404 if (pszText == NULL)
405 pData->u.tooltip.szText[0] = '\0';
406 else
407 {
408 strncpy(pData->u.tooltip.szText, pszText,
409 sizeof(pData->u.tooltip.szText) - 1);
410 // be on the safe side
411 pData->u.tooltip.szText[sizeof(pData->u.tooltip.szText) - 1] = '\0';
412 }
413
414 brc = SendSysTrayCtlMsg(pData);
415
416 FreeSysTrayCtlDataPtr(pData);
417
418 return brc;
419}
420
421BOOL xstShowSysTrayIconBalloon(HWND hwnd, USHORT usId, PSZ pszTitle, PSZ pszText,
422 ULONG ulFlags, ULONG ulTimeout)
423{
424 // @todo implement
425 return FALSE;
426}
427
428BOOL xstHideSysTrayIconBalloon(HWND hwnd, USHORT usId)
429{
430 // @todo implement
431 return FALSE;
432}
433
434/*
435 *@@ xstQuerySysTrayIconRect:
436 *
437 * Obtains a rectangle occupied by the given icon (in screen coordinates,
438 * top right corner exclusive).
439 *
440 * Returns TRUE on success and FALSE otherwise.
441 */
442BOOL xstQuerySysTrayIconRect(HWND hwnd, USHORT usId, PRECTL prclRect)
443{
444 // @todo implement
445 return FALSE;
446}
447
448/*
449 *@@ xstGetSysTrayCreatedMsgId:
450 *
451 * Returns the ID of the message that is sent by the Extended system tray
452 * to all top-level WC_FRAME windows on the desktop to let them add tray
453 * icons if they need.
454 *
455 * NOTE: The returned value never changes until reboot so it is a good
456 * practice to cache it instead of calling this function each time from the
457 * window procedure of every involved window.
458 */
459
460ULONG xstGetSysTrayCreatedMsgId()
461{
462 if (WM_XST_CREATED == 0)
463 WM_XST_CREATED = WinAddAtom(WinQuerySystemAtomTable(),
464 WM_XST_CREATED_ATOM);
465 return WM_XST_CREATED;
466}
467
468/*
469 *@@ xstGetSysTrayMaxTextLen:
470 *
471 * Returns the maximum length of the text (in bytes, including the
472 * terminating null) that can be shown in the tooltop of the icon in the
473 * system tray. You can use the returned value to determine the maximum
474 * length of the string passed as pszText to xstSetSysTrayIconToolTip().
475 *
476 * The returned value also defines the maximum length of both the title and
477 * the text (including terminating nulls) of the icon's balloon for the
478 * xstShowSysTrayIconBalloon() call.
479 *
480 * Returns TRUE on success and FALSE otherwise.
481 *
482 * NOTE: The returned value never changes until reboot so it is a good
483 * practice to cache it instead of calling this function each time.
484 */
485
486ULONG xstGetSysTrayMaxTextLen()
487{
488 return sizeof(((PSYSTRAYCTLDATA)0)->u.tooltip.szText);
489}
490
Note: See TracBrowser for help on using the repository browser.