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

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

3rdparty: os2/xsystray: Implemented adding an icon to the system tray (one icon so far) and automatically removing it if the associated window gets destroyed w/o doing so (for example, as a result of unexpected application termination).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 11.5 KB
Line 
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
30static HWND G_hwndSysTray = NULLHANDLE;
31static int G_itlsSysTrayCtlData = -1;
32
33static 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
50static 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).
106static 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
163BOOL 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
218BOOL 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
259BOOL 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
290BOOL 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
315BOOL xstShowSysTrayIconBalloon(HWND hwnd, ULONG ulId, PSZ pszTitle, PSZ pszText,
316 ULONG ulFlags, ULONG ulTimeout)
317{
318 // @todo implement
319 return FALSE;
320}
321
322BOOL 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 */
336BOOL 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
354ULONG 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
381ULONG xstGetSysTrayMaxTextLen()
382{
383 return sizeof(((PSYSTRAYCTLDATA)0)->u.tooltip.szText);
384}
385
Note: See TracBrowser for help on using the repository browser.